From d42b821c0067a0065e49a68cce406fa1f866281c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 3 Jan 2026 16:59:35 -0800 Subject: [PATCH 001/181] rel 3.0 Signed-off-by: James Cherry --- CMakeLists.txt | 20 +- app/Main.cc | 43 +- app/StaMain.cc | 37 +- dcalc/ArcDcalcWaveforms.cc | 10 +- dcalc/ArcDcalcWaveforms.hh | 6 +- dcalc/ArcDelayCalc.cc | 5 +- dcalc/Arnoldi.hh | 2 +- dcalc/ArnoldiDelayCalc.cc | 287 +- dcalc/ArnoldiReduce.cc | 43 +- dcalc/ArnoldiReduce.hh | 16 +- dcalc/CcsCeffDelayCalc.cc | 40 +- dcalc/CcsCeffDelayCalc.hh | 13 +- dcalc/DcalcAnalysisPt.cc | 68 - dcalc/DelayCalc.cc | 14 +- dcalc/DelayCalc.i | 14 +- dcalc/DelayCalc.tcl | 166 +- dcalc/DelayCalcBase.cc | 63 +- dcalc/DelayCalcBase.hh | 29 +- dcalc/DmpCeff.cc | 429 +- dcalc/DmpCeff.hh | 25 +- dcalc/DmpDelayCalc.cc | 139 +- dcalc/FindRoot.cc | 20 +- dcalc/FindRoot.hh | 26 +- dcalc/GraphDelayCalc.cc | 577 +- dcalc/LumpedCapDelayCalc.cc | 71 +- dcalc/LumpedCapDelayCalc.hh | 15 +- dcalc/NetCaps.cc | 12 +- dcalc/NetCaps.hh | 12 +- dcalc/ParallelDelayCalc.cc | 19 +- dcalc/ParallelDelayCalc.hh | 6 +- dcalc/PrimaDelayCalc.cc | 95 +- dcalc/PrimaDelayCalc.hh | 41 +- dcalc/UnitDelayCalc.cc | 70 +- dcalc/UnitDelayCalc.hh | 32 +- doc/ApiChanges.txt | 39 + doc/ChangeLog.txt | 179 + doc/OpenSTA.fodt | 5752 +++++++++-------- doc/OpenSTA.pdf | Bin 1424501 -> 1424807 bytes examples/asap7_small_ff.lib.gz | Bin 0 -> 72203 bytes examples/asap7_small_ss.lib.gz | Bin 0 -> 72452 bytes examples/mcmm2_mode1.sdc | 2 + examples/mcmm2_mode2.sdc | 2 + examples/mcmm3.tcl | 18 + examples/multi_corner.tcl | 21 +- examples/reg1_asap7.spef | 135 + examples/reg1_asap7.v | 11 + examples/reg1_asap7_ss.spef | 135 + graph/Delay.cc | 2 +- graph/DelayFloat.cc | 60 +- graph/DelayNormal1.cc | 130 +- graph/DelayNormal2.cc | 146 +- graph/Graph.cc | 440 +- graph/Graph.i | 249 +- graph/Graph.tcl | 58 +- graph/GraphCmp.cc | 13 +- include/sta/ArcDelayCalc.hh | 73 +- include/sta/Bdd.hh | 4 +- include/sta/Bfs.hh | 26 +- include/sta/BoundedHeap.hh | 256 + include/sta/ClkNetwork.hh | 25 +- include/sta/Clock.hh | 126 +- include/sta/ClockGroups.hh | 10 +- include/sta/ClockInsertion.hh | 12 +- include/sta/ClockLatency.hh | 20 +- include/sta/ConcreteLibrary.hh | 71 +- include/sta/ConcreteNetwork.hh | 45 +- include/sta/ContainerHelpers.hh | 381 ++ include/sta/Corner.hh | 139 - include/sta/CycleAccting.hh | 47 +- include/sta/DataCheck.hh | 34 +- include/sta/DcalcAnalysisPt.hh | 81 - include/sta/Debug.hh | 19 +- include/sta/Delay.hh | 10 +- include/sta/DelayCalc.hh | 6 +- include/sta/DelayFloat.hh | 78 +- include/sta/DelayNormal1.hh | 84 +- include/sta/DelayNormal2.hh | 86 +- include/sta/DeratingFactors.hh | 74 +- include/sta/DisabledPorts.hh | 24 +- include/sta/EnumNameMap.hh | 16 +- include/sta/EquivCells.hh | 24 +- include/sta/Error.hh | 6 +- include/sta/ExceptionPath.hh | 277 +- include/sta/FuncExpr.hh | 59 +- include/sta/Fuzzy.hh | 10 +- include/sta/Graph.hh | 226 +- include/sta/GraphClass.hh | 38 +- include/sta/GraphCmp.hh | 12 +- include/sta/GraphDelayCalc.hh | 86 +- include/sta/Hash.hh | 4 +- include/sta/HpinDrvrLoad.hh | 15 +- include/sta/InputDrive.hh | 58 +- include/sta/InternalPower.hh | 36 +- include/sta/Iterator.hh | 76 + include/sta/LeakagePower.hh | 2 +- include/sta/Liberty.hh | 464 +- include/sta/LibertyClass.hh | 39 +- include/sta/LinearModel.hh | 4 +- include/sta/Machine.hh | 2 +- include/sta/Map.hh | 191 - include/sta/MinMax.hh | 25 +- include/sta/MinMaxValues.hh | 50 +- include/sta/Mode.hh | 95 + include/sta/Mutex.hh | 2 +- include/sta/Network.hh | 226 +- include/sta/NetworkClass.hh | 82 +- include/sta/NetworkCmp.hh | 19 +- include/sta/ObjectId.hh | 6 +- include/sta/ObjectTable.hh | 18 +- include/sta/Parasitics.hh | 198 +- include/sta/ParasiticsClass.hh | 1 - include/sta/ParseBus.hh | 38 +- include/sta/Path.hh | 69 +- include/sta/PathAnalysisPt.hh | 69 - include/sta/PathEnd.hh | 358 +- include/sta/PathExpanded.hh | 16 +- include/sta/PathGroup.hh | 181 +- include/sta/PatternMatch.hh | 22 +- include/sta/PinPair.hh | 11 +- include/sta/PortDelay.hh | 20 +- include/sta/PortDirection.hh | 2 +- include/sta/PortExtCap.hh | 51 +- include/sta/PowerClass.hh | 14 +- include/sta/Property.hh | 83 +- include/sta/Report.hh | 2 +- include/sta/ReportTcl.hh | 4 +- include/sta/RiseFallMinMax.hh | 44 +- ...teParasitics.hh => RiseFallMinMaxDelay.hh} | 24 +- include/sta/RiseFallValues.hh | 4 +- include/sta/Scene.hh | 99 + include/sta/Sdc.hh | 1288 ++-- include/sta/SdcClass.hh | 53 +- include/sta/SdcNetwork.hh | 36 +- include/sta/Search.hh | 799 ++- include/sta/SearchClass.hh | 67 +- include/sta/SearchPred.hh | 114 +- include/sta/Sequential.hh | 20 +- include/sta/Set.hh | 220 - include/sta/Sta.hh | 1710 ++--- include/sta/StaMain.hh | 30 +- include/sta/StaState.hh | 44 +- include/sta/Stats.hh | 4 +- include/sta/StringSeq.hh | 6 +- include/sta/StringSet.hh | 6 +- include/sta/StringUtil.hh | 44 +- include/sta/TableModel.hh | 132 +- include/sta/TclTypeHelpers.hh | 20 +- include/sta/TimingArc.hh | 66 +- include/sta/TimingModel.hh | 12 +- include/sta/TimingRole.hh | 26 +- include/sta/TokenParser.hh | 2 +- include/sta/Transition.hh | 10 +- include/sta/Units.hh | 2 +- include/sta/UnorderedMap.hh | 203 - include/sta/UnorderedSet.hh | 139 - include/sta/Variables.hh | 5 - include/sta/Vector.hh | 146 - include/sta/VectorMap.hh | 494 ++ include/sta/VerilogReader.hh | 183 +- include/sta/VerilogWriter.hh | 6 +- include/sta/VertexId.hh | 2 +- include/sta/VisitPathEnds.hh | 167 +- include/sta/Wireload.hh | 36 +- include/sta/WriteSdc.hh | 20 +- include/sta/Zlib.hh | 2 +- liberty/EquivCells.cc | 105 +- liberty/FuncExpr.cc | 201 +- liberty/InternalPower.cc | 46 +- liberty/LeakagePower.cc | 2 +- liberty/LibExprReader.cc | 18 +- liberty/LibExprReader.hh | 6 +- liberty/LibExprReaderPvt.hh | 14 +- liberty/Liberty.cc | 833 ++- liberty/Liberty.i | 45 +- liberty/Liberty.tcl | 18 +- liberty/LibertyBuilder.cc | 297 +- liberty/LibertyBuilder.hh | 151 +- liberty/LibertyExt.cc | 40 +- liberty/LibertyParser.cc | 77 +- liberty/LibertyParser.hh | 108 +- liberty/LibertyReader.cc | 1475 +++-- liberty/LibertyReader.hh | 4 +- liberty/LibertyReaderPvt.hh | 275 +- liberty/LibertyWriter.cc | 19 +- liberty/LinearModel.cc | 42 +- liberty/Sequential.cc | 16 +- liberty/TableModel.cc | 379 +- liberty/TimingArc.cc | 194 +- liberty/TimingRole.cc | 14 +- liberty/Units.cc | 6 +- liberty/Wireload.cc | 54 +- network/ConcreteLibrary.cc | 119 +- network/ConcreteNetwork.cc | 384 +- network/HpinDrvrLoad.cc | 232 +- network/Network.cc | 636 +- network/Network.i | 154 +- network/Network.tcl | 85 +- network/NetworkCmp.cc | 19 +- network/NetworkEdit.i | 12 +- network/NetworkEdit.tcl | 18 +- network/ParseBus.cc | 68 +- network/PortDirection.cc | 2 +- network/SdcNetwork.cc | 283 +- network/VerilogNamespace.cc | 28 +- parasitics/ConcreteParasitics.cc | 520 +- parasitics/ConcreteParasitics.hh | 65 +- parasitics/ConcreteParasiticsPvt.hh | 84 +- parasitics/EstimateParasitics.cc | 133 +- parasitics/EstimateParasitics.hh | 96 +- parasitics/Parasitics.cc | 107 +- parasitics/Parasitics.i | 54 +- parasitics/Parasitics.tcl | 32 +- parasitics/ReduceParasitics.cc | 269 +- parasitics/ReduceParasitics.hh | 24 +- parasitics/ReportParasiticAnnotation.cc | 40 +- parasitics/ReportParasiticAnnotation.hh | 8 +- parasitics/SpefNamespace.cc | 46 +- parasitics/SpefNamespace.hh | 4 +- parasitics/SpefParse.yy | 2 +- parasitics/SpefReader.cc | 138 +- parasitics/SpefReader.hh | 27 +- parasitics/SpefReaderPvt.hh | 71 +- power/Power.cc | 550 +- power/Power.hh | 91 +- power/Power.i | 93 +- power/Power.tcl | 264 +- power/ReportPower.cc | 247 + power/ReportPower.hh | 91 + power/SaifReader.cc | 4 +- power/SaifReaderPvt.hh | 2 +- power/VcdParse.hh | 4 +- power/VcdReader.cc | 63 +- power/VcdReader.hh | 7 +- sdc/Clock.cc | 170 +- sdc/ClockGroups.cc | 13 +- sdc/ClockInsertion.cc | 28 +- sdc/ClockLatency.cc | 20 +- sdc/CycleAccting.cc | 225 +- sdc/DataCheck.cc | 40 +- sdc/DeratingFactors.cc | 74 +- sdc/DisabledPorts.cc | 28 +- sdc/ExceptionPath.cc | 805 +-- sdc/InputDrive.cc | 70 +- sdc/PinPair.cc | 6 +- sdc/PortDelay.cc | 12 +- sdc/PortExtCap.cc | 48 +- sdc/Sdc.cc | 2761 ++++---- sdc/Sdc.i | 1203 ++-- sdc/Sdc.tcl | 478 +- sdc/SdcGraph.cc | 396 -- sdc/Variables.cc | 7 - sdc/WriteSdc.cc | 940 ++- sdc/WriteSdcPvt.hh | 163 +- sdf/ReportAnnotation.cc | 362 +- sdf/ReportAnnotation.hh | 47 +- sdf/Sdf.i | 86 +- sdf/Sdf.tcl | 50 +- sdf/SdfReader.cc | 288 +- sdf/SdfReader.hh | 4 +- sdf/SdfReaderPvt.hh | 135 +- sdf/SdfWriter.cc | 313 +- sdf/SdfWriter.hh | 16 +- search/Bdd.cc | 14 +- search/Bfs.cc | 59 +- search/CheckCapacitanceLimits.cc | 356 - search/CheckCapacitanceLimits.hh | 105 - search/CheckCapacitances.cc | 369 ++ search/CheckCapacitances.hh | 128 + search/CheckFanoutLimits.cc | 332 - search/CheckFanoutLimits.hh | 82 - search/CheckFanouts.cc | 334 + search/CheckFanouts.hh | 126 + search/CheckMaxSkews.cc | 235 +- search/CheckMaxSkews.hh | 63 +- search/CheckMinPeriods.cc | 232 +- search/CheckMinPeriods.hh | 64 +- search/CheckMinPulseWidths.cc | 362 +- search/CheckMinPulseWidths.hh | 73 +- search/CheckSlewLimits.cc | 422 -- search/CheckSlewLimits.hh | 129 - search/CheckSlews.cc | 420 ++ search/CheckSlews.hh | 168 + search/CheckTiming.cc | 178 +- search/CheckTiming.hh | 40 +- search/ClkInfo.cc | 85 +- search/ClkInfo.hh | 50 +- search/ClkLatency.cc | 36 +- search/ClkLatency.hh | 8 +- search/ClkNetwork.cc | 111 +- search/ClkSkew.cc | 368 +- search/ClkSkew.hh | 40 +- search/Corner.cc | 461 -- search/Crpr.cc | 133 +- search/Crpr.hh | 62 +- search/FindRegister.cc | 460 +- search/FindRegister.hh | 40 +- search/GatedClk.cc | 211 +- search/GatedClk.hh | 32 +- search/Genclks.cc | 657 +- search/Genclks.hh | 87 +- search/Latches.cc | 420 +- search/Latches.hh | 113 +- search/Levelize.cc | 332 +- search/Levelize.hh | 13 +- search/MakeTimingModel.cc | 52 +- search/MakeTimingModel.hh | 4 +- search/MakeTimingModelPvt.hh | 12 +- search/Mode.cc | 146 + search/Path.cc | 199 +- search/PathAnalysisPt.cc | 73 - search/PathEnd.cc | 586 +- search/PathEnum.cc | 294 +- search/PathEnum.hh | 42 +- search/PathExpanded.cc | 95 +- search/PathGroup.cc | 620 +- search/Property.cc | 505 +- search/Property.i | 24 +- search/ReportPath.cc | 1289 ++-- search/ReportPath.hh | 443 +- search/Scene.cc | 187 + search/Search.cc | 2831 ++++---- search/Search.i | 632 +- search/Search.tcl | 493 +- search/SearchPred.cc | 236 +- search/Sim.cc | 660 +- search/Sim.hh | 171 +- search/Sta.cc | 2920 +++++---- search/StaState.cc | 57 +- search/Tag.cc | 323 +- search/Tag.hh | 68 +- search/TagGroup.cc | 69 +- search/TagGroup.hh | 27 +- search/VisitPathEnds.cc | 641 +- search/WorstSlack.cc | 93 +- search/WorstSlack.hh | 38 +- spice/WritePathSpice.cc | 117 +- spice/WritePathSpice.hh | 20 +- spice/WriteSpice.cc | 63 +- spice/WriteSpice.hh | 82 +- spice/WriteSpice.i | 12 +- spice/WriteSpice.tcl | 30 +- spice/Xyce.hh | 4 +- tcl/CmdArgs.tcl | 313 +- tcl/CmdUtil.tcl | 34 +- tcl/Sta.tcl | 150 +- tcl/StaTclTypes.i | 238 +- tcl/TclTypeHelpers.cc | 71 +- tcl/Util.tcl | 84 +- tcl/Variables.tcl | 10 +- test/disconnect_mcp_pin.ok | 8 +- test/liberty_arcs_one2one_1.ok | 2 +- test/liberty_arcs_one2one_2.ok | 2 +- test/mcmm3.ok | 208 + test/power.ok | 12 +- test/power_json.tcl | 2 +- test/power_vcd.ok | 2 +- test/prima3.ok | 10 - test/regression.tcl | 105 +- test/regression_vars.tcl | 5 +- test/report_json1.ok | 10 +- test/suppress_msg.ok | 12 +- util/Debug.cc | 50 +- util/Error.cc | 4 +- util/Fuzzy.cc | 10 +- util/MachineLinux.cc | 14 +- util/MinMax.cc | 30 +- util/PatternMatch.cc | 26 +- util/Report.cc | 28 +- util/RiseFallMinMax.cc | 76 +- util/RiseFallMinMaxDelay.cc | 77 + util/RiseFallValues.cc | 6 +- util/StringSeq.cc | 5 +- util/StringSet.cc | 5 +- util/StringUtil.cc | 38 +- util/TokenParser.cc | 18 +- util/Transition.cc | 14 +- util/Util.i | 28 +- verilog/Verilog.i | 4 +- verilog/Verilog.tcl | 2 +- verilog/VerilogParse.yy | 5 +- verilog/VerilogReader.cc | 719 +-- verilog/VerilogReader.hh | 296 - verilog/VerilogReaderPvt.hh | 68 +- verilog/VerilogWriter.cc | 81 +- 384 files changed, 32753 insertions(+), 31880 deletions(-) create mode 100644 examples/asap7_small_ff.lib.gz create mode 100644 examples/asap7_small_ss.lib.gz create mode 100644 examples/mcmm2_mode1.sdc create mode 100644 examples/mcmm2_mode2.sdc create mode 100644 examples/mcmm3.tcl create mode 100644 examples/reg1_asap7.spef create mode 100644 examples/reg1_asap7.v create mode 100644 examples/reg1_asap7_ss.spef create mode 100644 include/sta/BoundedHeap.hh create mode 100644 include/sta/ContainerHelpers.hh delete mode 100644 include/sta/Corner.hh delete mode 100644 include/sta/DcalcAnalysisPt.hh delete mode 100644 include/sta/Map.hh create mode 100644 include/sta/Mode.hh delete mode 100644 include/sta/PathAnalysisPt.hh rename include/sta/{MakeConcreteParasitics.hh => RiseFallMinMaxDelay.hh} (65%) create mode 100644 include/sta/Scene.hh delete mode 100644 include/sta/Set.hh delete mode 100644 include/sta/UnorderedMap.hh delete mode 100644 include/sta/UnorderedSet.hh delete mode 100644 include/sta/Vector.hh create mode 100644 include/sta/VectorMap.hh create mode 100644 power/ReportPower.cc create mode 100644 power/ReportPower.hh delete mode 100644 sdc/SdcGraph.cc delete mode 100644 search/CheckCapacitanceLimits.cc delete mode 100644 search/CheckCapacitanceLimits.hh create mode 100644 search/CheckCapacitances.cc create mode 100644 search/CheckCapacitances.hh delete mode 100644 search/CheckFanoutLimits.cc delete mode 100644 search/CheckFanoutLimits.hh create mode 100644 search/CheckFanouts.cc create mode 100644 search/CheckFanouts.hh delete mode 100644 search/CheckSlewLimits.cc delete mode 100644 search/CheckSlewLimits.hh create mode 100644 search/CheckSlews.cc create mode 100644 search/CheckSlews.hh create mode 100644 search/Mode.cc create mode 100644 search/Scene.cc create mode 100644 test/mcmm3.ok create mode 100644 util/RiseFallMinMaxDelay.cc delete mode 100644 verilog/VerilogReader.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index f41fcac08..dda0ca9db 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 2.7.0 +project(STA VERSION 3.0.0 LANGUAGES CXX ) @@ -77,7 +77,6 @@ set(STA_SOURCE dcalc/ArnoldiDelayCalc.cc dcalc/ArnoldiReduce.cc dcalc/CcsCeffDelayCalc.cc - dcalc/DcalcAnalysisPt.cc dcalc/DelayCalc.cc dcalc/DelayCalcBase.cc dcalc/DmpCeff.cc @@ -135,6 +134,7 @@ set(STA_SOURCE parasitics/SpefReaderPvt.hh power/Power.cc + power/ReportPower.cc power/VcdReader.cc power/SaifReader.cc power/VcdParse.cc @@ -154,7 +154,6 @@ set(STA_SOURCE sdc/PortDelay.cc sdc/PortExtCap.cc sdc/Sdc.cc - sdc/SdcGraph.cc sdc/SdcCmdComment.cc sdc/Variables.cc sdc/WriteSdc.cc @@ -168,15 +167,14 @@ set(STA_SOURCE search/CheckMaxSkews.cc search/CheckMinPeriods.cc search/CheckMinPulseWidths.cc - search/CheckCapacitanceLimits.cc - search/CheckFanoutLimits.cc - search/CheckSlewLimits.cc + search/CheckCapacitances.cc + search/CheckFanouts.cc + search/CheckSlews.cc search/CheckTiming.cc search/ClkInfo.cc search/ClkLatency.cc search/ClkNetwork.cc search/ClkSkew.cc - search/Corner.cc search/Crpr.cc search/FindRegister.cc search/GatedClk.cc @@ -184,8 +182,8 @@ set(STA_SOURCE search/Latches.cc search/Levelize.cc search/MakeTimingModel.cc + search/Mode.cc search/Path.cc - search/PathAnalysisPt.cc search/Path.cc search/PathEnd.cc search/PathEnum.cc @@ -195,6 +193,7 @@ set(STA_SOURCE search/ReportPath.cc search/Search.cc search/SearchPred.cc + search/Scene.cc search/Sim.cc search/Sta.cc search/StaState.cc @@ -222,6 +221,7 @@ set(STA_SOURCE util/ReportStd.cc util/ReportTcl.cc util/RiseFallMinMax.cc + util/RiseFallMinMaxDelay.cc util/RiseFallValues.cc util/Stats.cc util/StringSeq.cc @@ -549,14 +549,14 @@ endif() target_compile_options(OpenSTA PRIVATE - $<$:${CXX_FLAGS}> + $<$:${CXX_FLAGS} -Wno-format-zero-length> $<$:${CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments> $<$:${CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments> ) # Disable compiler specific extensions like gnu++11. set_target_properties(OpenSTA PROPERTIES CXX_EXTENSIONS OFF) -target_compile_features(OpenSTA PUBLIC cxx_std_17) +target_compile_features(OpenSTA PUBLIC cxx_std_20) message(STATUS "STA library: ${CMAKE_BINARY_DIR}/libOpenSTA.a") diff --git a/app/Main.cc b/app/Main.cc index 4c7e56e98..baa1c0ea6 100644 --- a/app/Main.cc +++ b/app/Main.cc @@ -27,6 +27,7 @@ #include #include // exit +#include #include #if TCL_READLINE #include @@ -47,7 +48,6 @@ using sta::evalTclInit; using sta::sourceTclFile; using sta::parseThreadsArg; using sta::tcl_inits; -using sta::is_regular_file; // Swig uses C linkage for init functions. extern "C" { @@ -60,18 +60,18 @@ static const char *init_filename = ".sta"; static void showUsage(const char *prog, - const char *init_filename); + const char *init_filename); static int tclAppInit(Tcl_Interp *interp); static int staTclAppInit(int argc, - char *argv[], - const char *init_filename, - Tcl_Interp *interp); + char *argv[], + const char *init_filename, + Tcl_Interp *interp); static void initStaApp(int &argc, - char *argv[], - Tcl_Interp *interp); + char *argv[], + Tcl_Interp *interp); int main(int argc, @@ -87,20 +87,11 @@ main(int argc, } else { // Set argc to 1 so Tcl_Main doesn't source any files. - // Tcl_Main never returns. -#if 0 - // It should be possible to pass argc/argv to staTclAppInit with - // a closure but I couldn't get the signature to match Tcl_AppInitProc. - Tcl_Main(1, argv, [=](Tcl_Interp *interp) - { sta::staTclAppInit(argc, argv, interp); - return 1; - }); -#else - // Workaround. + // Store argc and argv in static variables for tclAppInit. cmd_argc = argc; cmd_argv = argv; + // Tcl_Main never returns. Tcl_Main(1, argv, tclAppInit); -#endif return 0; } } @@ -114,9 +105,9 @@ tclAppInit(Tcl_Interp *interp) // Tcl init executed inside Tcl_Main. static int staTclAppInit(int argc, - char *argv[], - const char *init_filename, - Tcl_Interp *interp) + char *argv[], + const char *init_filename, + Tcl_Interp *interp) { // source init.tcl if (Tcl_Init(interp) == TCL_ERROR) @@ -141,7 +132,7 @@ staTclAppInit(int argc, string init_path = home; init_path += "/"; init_path += init_filename; - if (is_regular_file(init_path.c_str())) + if (std::filesystem::is_regular_file(init_path.c_str())) sourceTclFile(init_path.c_str(), true, true, interp); } } @@ -157,7 +148,7 @@ staTclAppInit(int argc, if (argc == 2) { char *cmd_file = argv[1]; if (cmd_file) { - int result = sourceTclFile(cmd_file, false, false, interp); + int result = sourceTclFile(cmd_file, false, false, interp); if (exit_after_cmd_file) { int exit_code = (result == TCL_OK) ? EXIT_SUCCESS : EXIT_FAILURE; exit(exit_code); @@ -174,8 +165,8 @@ staTclAppInit(int argc, static void initStaApp(int &argc, - char *argv[], - Tcl_Interp *interp) + char *argv[], + Tcl_Interp *interp) { initSta(); Sta *sta = new Sta; @@ -194,7 +185,7 @@ initStaApp(int &argc, static void showUsage(const char *prog, - const char *init_filename) + const char *init_filename) { printf("Usage: %s [-help] [-version] [-no_init] [-exit] cmd_file\n", prog); printf(" -help show help and exit\n"); diff --git a/app/StaMain.cc b/app/StaMain.cc index 97040fd7b..5be334ea7 100644 --- a/app/StaMain.cc +++ b/app/StaMain.cc @@ -30,14 +30,13 @@ #include "Machine.hh" #include "StringUtil.hh" -#include "Vector.hh" #include "Sta.hh" namespace sta { int parseThreadsArg(int &argc, - char *argv[]) + char *argv[]) { char *thread_arg = findCmdLineKey(argc, argv, "-threads"); if (thread_arg) { @@ -53,15 +52,15 @@ parseThreadsArg(int &argc, bool findCmdLineFlag(int &argc, - char *argv[], - const char *flag) + char *argv[], + const char *flag) { for (int i = 1; i < argc; i++) { char *arg = argv[i]; if (stringEq(arg, flag)) { // Remove flag from argv. for (int j = i + 1; j < argc; j++, i++) - argv[i] = argv[j]; + argv[i] = argv[j]; argc--; argv[argc] = nullptr; return true; @@ -72,8 +71,8 @@ findCmdLineFlag(int &argc, char * findCmdLineKey(int &argc, - char *argv[], - const char *key) + char *argv[], + const char *key) { for (int i = 1; i < argc; i++) { char *arg = argv[i]; @@ -81,7 +80,7 @@ findCmdLineKey(int &argc, char *value = argv[i + 1]; // Remove key and value from argv. for (int j = i + 2; j < argc; j++, i++) - argv[i] = argv[j]; + argv[i] = argv[j]; argc -= 2; argv[argc] = nullptr; return value; @@ -93,15 +92,15 @@ findCmdLineKey(int &argc, // Use overridden version of source to echo cmds and results. int sourceTclFile(const char *filename, - bool echo, - bool verbose, - Tcl_Interp *interp) + bool echo, + bool verbose, + Tcl_Interp *interp) { std::string cmd; stringPrint(cmd, "sta::include_file %s %s %s", - filename, - echo ? "1" : "0", - verbose ? "1" : "0"); + 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') @@ -111,7 +110,7 @@ sourceTclFile(const char *filename, void evalTclInit(Tcl_Interp *interp, - const char *inits[]) + const char *inits[]) { char *unencoded = unencode(inits); if (Tcl_Eval(interp, unencoded) != TCL_OK) { @@ -148,12 +147,4 @@ unencode(const char *inits[]) return unencoded; } -// Hack until c++17 filesystem is better supported. -bool -is_regular_file(const char *filename) -{ - struct stat sb; - return stat(filename, &sb) == 0 && S_ISREG(sb.st_mode); -} - } // namespace diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index 4b8d340d8..16595a28e 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -31,7 +31,6 @@ #include "Network.hh" #include "Graph.hh" #include "ArcDelayCalc.hh" -#include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" namespace sta { @@ -40,7 +39,8 @@ using std::make_shared; Waveform ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const StaState *sta) { const Network *network = sta->network(); @@ -55,7 +55,8 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, const Vertex *in_vertex = graph->pinLoadVertex(in_pin); GraphDelayCalc *graph_dcalc = sta->graphDelayCalc(); Slew in_slew = graph_dcalc->edgeFromSlew(in_vertex, in_rf, - dcalc_arg.arc()->role(), dcalc_ap); + dcalc_arg.arc()->role(), + scene, min_max); LibertyLibrary *library = port->libertyLibrary(); float vdd; bool vdd_exists; @@ -67,7 +68,8 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, FloatSeq *time_values = new FloatSeq; for (float time : *in_waveform.axis1()->values()) time_values->push_back(time + dcalc_arg.inputDelay()); - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, time_values); + TableAxisPtr time_axis = make_shared(TableAxisVariable::time, + time_values); // Scale the waveform from 0:vdd. FloatSeq *scaled_values = new FloatSeq; for (float value : *in_waveform.values()) { diff --git a/dcalc/ArcDcalcWaveforms.hh b/dcalc/ArcDcalcWaveforms.hh index 2cbcf3b5c..6babfce68 100644 --- a/dcalc/ArcDcalcWaveforms.hh +++ b/dcalc/ArcDcalcWaveforms.hh @@ -31,8 +31,7 @@ namespace sta { class StaState; -class Corner; -class DcalcAnalysisPt; +class Scene; class ArcDcalcArg; // Abstract class for delay calculation waveforms for ploting. @@ -47,7 +46,8 @@ public: protected: Waveform inputWaveform(ArcDcalcArg &dcalc_arg, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const StaState *sta); }; diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index d20ca928d..2f6fcbfd9 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -46,14 +46,15 @@ ArcDelayCalc::gateDelay(const TimingArc *arc, const Parasitic *parasitic, float, const Pvt *, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. ArcDelay &gate_delay, Slew &drvr_slew) { LoadPinIndexMap load_pin_index_map(network_); ArcDcalcResult dcalc_result = gateDelay(nullptr, arc, in_slew, load_cap, parasitic, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); gate_delay = dcalc_result.gateDelay(); drvr_slew = dcalc_result.drvrSlew(); } diff --git a/dcalc/Arnoldi.hh b/dcalc/Arnoldi.hh index 05a11b71d..775124137 100644 --- a/dcalc/Arnoldi.hh +++ b/dcalc/Arnoldi.hh @@ -66,7 +66,7 @@ public: // n is the number of terms // The vectors U[j] are of size n class rcmodel : public ConcreteParasitic, - public arnoldi1 + public arnoldi1 { public: rcmodel(); diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 62f973c93..ac6705d81 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -43,7 +43,6 @@ #include "Graph.hh" #include "Parasitics.hh" #include "Sdc.hh" -#include "DcalcAnalysisPt.hh" #include "DelayCalc.hh" #include "ArcDelayCalc.hh" #include "LumpedCapDelayCalc.hh" @@ -78,7 +77,7 @@ static void delay_work_destroy(delay_work *D); static double * delay_work_get_residues(delay_work *D, - int term_index); + int term_index); static bool tridiagEV(int n,double *d,double *e,double *p,double **v); @@ -129,17 +128,20 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc const char *name() const override { return "arnoldi"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + 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 DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -147,21 +149,23 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; void finishDrvrPin() override; void delay_work_set_thresholds(delay_work *D, - double lo, - double hi, - bool rising, - double derate); + double lo, + double hi, + bool rising, + double derate); private: ArcDcalcResult gateDelaySlew(const LibertyCell *drvr_cell, @@ -173,61 +177,61 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc void ar1_ceff_delay(delay_work *D, timing_table *tab, arnoldi1 *mod, - double *delays, + double *delays, double *slews); double ra_rdelay_1(timing_table *tab, - double ctot); + double ctot); double ra_get_r(delay_work *D, - timing_table *tab, - double rdelay, - double ctot); + timing_table *tab, + double rdelay, + double ctot); double ra_get_s(delay_work *D, - timing_table *tab, - double r, - double c); + timing_table *tab, + double r, + double c); void ra_solve_for_s(delay_work *D, - double p, - double tlohi, - double &s); + double p, + double tlohi, + double &s); // from poles and residues, solve for t20,t50,t80 void pr_solve1(double s, - int order, - double *p, - double *rr, - double v1, - double *t1); + int order, + double *p, + double *rr, + double v1, + double *t1); void pr_solve3(double s, - int order, - double *p, - double *rr, - double vhi, - double *thi, - double vmid, - double *tmid, - double vlo, - double *tlo); + int order, + double *p, + double *rr, + double vhi, + double *thi, + double vmid, + double *tmid, + double vlo, + double *tlo); // // routines for linear drive model and ceff // double pr_ceff(double s, - double rdrive, - int order, - double *p, - double *rr, - double ceff_time); + double rdrive, + int order, + double *p, + double *rr, + double ceff_time); double ra_solve_for_t(double p, - double s, - double v); + double s, + double v); void ra_solve_for_pt(double ps, - double v, - double *pt, - double *d); + double v, + double *pt, + double *d); void ra_calc_c(double lo, - double hi, - double *c_smin, - double *c_x1, - double *c_y1); + double hi, + double *c_smin, + double *c_x1, + double *c_y1); rcmodel *rcmodel_; int _pinNmax; @@ -272,36 +276,35 @@ ArnoldiDelayCalc::~ArnoldiDelayCalc() Parasitic * ArnoldiDelayCalc::findParasitic(const Pin *drvr_pin, - const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap) + const RiseFall *drvr_rf, + const Scene *scene, + const MinMax *min_max) { Parasitic *parasitic = nullptr; - const Corner *corner = dcalc_ap->corner(); - // set_load net has precedence over parasitics. - if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + const Sdc *sdc = scene->sdc(); + Parasitics *parasitics = scene->parasitics(min_max); + if (parasitics == nullptr + // set_load net has precedence over parasitics. || network_->direction(drvr_pin)->isInternal()) return nullptr; - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); Parasitic *parasitic_network = - parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); - const MinMax *min_max = dcalc_ap->constraintMinMax(); + parasitics->findParasiticNetwork(drvr_pin); if (parasitic_network == nullptr) { - Wireload *wireload = sdc_->wireload(min_max); + Wireload *wireload = sdc->wireload(min_max); if (wireload) { float pin_cap, wire_cap, fanout; bool has_wire_cap; - graph_delay_calc_->netCaps(drvr_pin, drvr_rf, dcalc_ap, + graph_delay_calc_->netCaps(drvr_pin, drvr_rf, scene, min_max, pin_cap, wire_cap, fanout, has_wire_cap); - parasitic_network = parasitics_->makeWireloadNetwork(drvr_pin, wireload, - fanout, min_max, - parasitic_ap); + parasitic_network = parasitics->makeWireloadNetwork(drvr_pin, wireload, + fanout, scene, min_max); } } if (parasitic_network) { rcmodel *rcmodel = reduce_->reduceToArnoldi(parasitic_network, drvr_pin, - parasitic_ap->couplingCapFactor(), - drvr_rf, corner, min_max, parasitic_ap); + parasitics->couplingCapFactor(), + drvr_rf, scene, min_max); // Arnoldi parasitics are their own class that are not saved in the parasitic db. unsaved_parasitics_.push_back(rcmodel); parasitic = rcmodel; @@ -313,7 +316,8 @@ Parasitic * ArnoldiDelayCalc::reduceParasitic(const Parasitic *, const Pin *, const RiseFall *, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { // Decline because reduced arnoldi parasitics are not stored in the parasitics db. return nullptr; @@ -333,7 +337,8 @@ ArnoldiDelayCalc::inputPortDelay(const Pin *, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { rcmodel_ = nullptr; _delayV[0] = 0.0; @@ -389,28 +394,29 @@ ArnoldiDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { const LibertyCell *drvr_cell = arc->from()->libertyCell(); ConcreteParasitic *cparasitic = reinterpret_cast(const_cast(parasitic)); rcmodel_ = dynamic_cast(cparasitic); pocv_enabled_ = variables_->pocvEnabled(); - GateTableModel *table_model = arc->gateTableModel(dcalc_ap); + GateTableModel *table_model = arc->gateTableModel(scene, min_max); if (table_model && rcmodel_) { - const Pvt *pvt = pinPvt(drvr_pin, dcalc_ap); + const Pvt *pvt = pinPvt(drvr_pin, scene, min_max); return gateDelaySlew(drvr_cell, arc, table_model, in_slew, load_pin_index_map, pvt); } else return LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, - parasitic, load_pin_index_map, dcalc_ap); + parasitic, load_pin_index_map, scene, min_max); } ArcDcalcResult ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, const TimingArc *arc, - const GateTableModel *table_model, - const Slew &in_slew, + const GateTableModel *table_model, + const Slew &in_slew, const LoadPinIndexMap &load_pin_index_map, const Pvt *pvt) { @@ -432,7 +438,7 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, double hi_thresh = drvr_library->slewUpperThreshold(rf); bool rising = (rf == RiseFall::rise()); delay_work_set_thresholds(delay_work_, lo_thresh, hi_thresh, rising, - slew_derate); + slew_derate); if (rcmodel_->order > 0) { timing_table tab; tab.table = table_model; @@ -440,7 +446,7 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, tab.pvt = pvt; tab.in_slew = delayAsFloat(in_slew); ar1_ceff_delay(delay_work_, &tab, rcmodel_, - _delayV, _slewV); + _delayV, _slewV); } dcalc_result.setGateDelay(_delayV[0]); dcalc_result.setDrvrSlew(_slewV[0]); @@ -470,12 +476,13 @@ ArnoldiDelayCalc::reportGateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) { return LumpedCapDelayCalc::reportGateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, - dcalc_ap, digits); + scene, min_max, digits); } //////////////////////////////////////////////////////////////// @@ -560,10 +567,10 @@ delay_work_alloc(delay_work *D,int n) void ArnoldiDelayCalc::delay_work_set_thresholds(delay_work *D, - double lo, - double hi, - bool rising, - double derate) + double lo, + double hi, + bool rising, + double derate) { double mid = 0.5; // 0.0:1.0 int i = rising?1:0; @@ -582,7 +589,7 @@ ArnoldiDelayCalc::delay_work_set_thresholds(delay_work *D, D->c->vmid = mid; D->c->vlg = log(hi/lo); ra_calc_c(lo,hi, - &(D->c->smin), &(D->c->x1),&(D->c->y1)); + &(D->c->smin), &(D->c->x1),&(D->c->y1)); } D->lo_thresh = D->c->vlo; D->hi_thresh = D->c->vhi; @@ -790,7 +797,7 @@ get_dv(double t, double s, int order, double *p, double *rr, static double solve_t_bracketed(double s,int order,double *p,double *rr, - double val,double x1,double x2,double v1,double v2) + double val,double x1,double x2,double v1,double v2) { int j; double df,dx,dxold,f,f2,f1; @@ -859,17 +866,17 @@ solve_t_bracketed(double s,int order,double *p,double *rr, void ArnoldiDelayCalc::pr_solve1(double s, - int order, - double *p, - double *rr, - double v1, - double *t1) + int order, + double *p, + double *rr, + double v1, + double *t1) { double tmin = 0.0,tmax = 0.0,vmin = 0.0,vmax = 0.0; int h, h0 = 0; while (order>1 - && rr[order-1]<1e-8 // 1e-8V - && rr[order-1]>-1e-8) + && rr[order-1]<1e-8 // 1e-8V + && rr[order-1]>-1e-8) order--; if (rr[0]<0.5) { for (h=1;h0.3 && rr[h]>rr[0]) { h0 = h; break; } @@ -893,7 +900,7 @@ ArnoldiDelayCalc::pr_solve1(double s, // ignoring a typical error at drive node, that comes // from slight inaccuracies in rr if (!(rr[order-1]>1.0 && p[order-1]>500.0 && va>v1-0.002)) - debugPrint(debug_, "arnoldi", 1, "err, pr_solve1, vav1) - debugPrint(debug_, "arnoldi", 1, "err, pr_solve1, va>v1"); + debugPrint(debug_, "arnoldi", 1, "err, pr_solve1, va>v1"); tmax = ta; vmax = va; } } else { @@ -925,15 +932,15 @@ ArnoldiDelayCalc::pr_solve1(double s, void ArnoldiDelayCalc::pr_solve3(double s, - int order, - double *p, - double *rr, - double vhi, - double *thi, - double vmid, - double *tmid, - double vlo, - double *tlo) + int order, + double *p, + double *rr, + double vhi, + double *thi, + double vmid, + double *tmid, + double vlo, + double *tlo) { // falling, thi1 - && rr[order-1]<1e-8 // 1e-8V - && rr[order-1]>-1e-8) + && rr[order-1]<1e-8 // 1e-8V + && rr[order-1]>-1e-8) order--; if (rr[0]<0.5) { for (h=1;h0.3 && rr[h]>rr[0]) { h0 = h; break; } @@ -1111,11 +1118,11 @@ calc_integ(double p,double s,double t) double ArnoldiDelayCalc::pr_ceff(double s, - double rdrive, - int order, - double *p, - double *rr, - double ceff_time) + double rdrive, + int order, + double *p, + double *rr, + double ceff_time) { double integi = 0.0; double ceff, v0; @@ -1133,7 +1140,7 @@ ArnoldiDelayCalc::pr_ceff(double s, static double ra_hinv(double y, - Debug *debug) + Debug *debug) { double x; if (y<1.0) { @@ -1160,8 +1167,8 @@ ra_hinv(double y, double ArnoldiDelayCalc::ra_solve_for_t(double p, - double s, - double v) + double s, + double v) { double t; double ps = p*s; @@ -1180,9 +1187,9 @@ ArnoldiDelayCalc::ra_solve_for_t(double p, void ArnoldiDelayCalc::ra_solve_for_pt(double ps, - double v, - double *pt, - double *d) + double v, + double *pt, + double *d) { if (ps>30.0) { *pt = 1.0+ps*(1.0-v); @@ -1201,10 +1208,10 @@ ArnoldiDelayCalc::ra_solve_for_pt(double ps, void ArnoldiDelayCalc::ra_calc_c(double vlo, - double vhi, - double *c_smin, - double *c_x1, - double *c_y1) + double vhi, + double *c_smin, + double *c_x1, + double *c_y1) { double a = log(1.0/vhi); *c_smin = a + ra_hinv((1.0-vhi)/vhi - a, debug_); @@ -1224,9 +1231,9 @@ ArnoldiDelayCalc::ra_calc_c(double vlo, void ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, - double p, - double tlohi, - double &s) + double p, + double tlohi, + double &s) { delay_c *c = D->c; double vhi = c->vhi; @@ -1258,28 +1265,28 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, f = (ptlo-pthi)/p - tlohi; df = dlo-dhi; s = s - f/df; - if (abs(f)<.001e-12) return; // .001ps + if (abs(f)<.001e-12) return; // .001ps ra_solve_for_pt(p*s,vlo,&ptlo,&dlo); ra_solve_for_pt(p*s,vhi,&pthi,&dhi); f = (ptlo-pthi)/p - tlohi; df = dlo-dhi; s = s - f/df; - if (abs(f)<.001e-12) return; // .001ps + if (abs(f)<.001e-12) return; // .001ps ra_solve_for_pt(p*s,vlo,&ptlo,&dlo); ra_solve_for_pt(p*s,vhi,&pthi,&dhi); f = (ptlo-pthi)/p - tlohi; df = dlo-dhi; s = s - f/df; - if (abs(f)<.001e-12) return; // .001ps + if (abs(f)<.001e-12) return; // .001ps ra_solve_for_pt(p*s,vlo,&ptlo,&dlo); ra_solve_for_pt(p*s,vhi,&pthi,&dhi); f = (ptlo-pthi)/p - tlohi; df = dlo-dhi; s = s - f/df; - if (abs(f)<.001e-12) return; // .001ps + if (abs(f)<.001e-12) return; // .001ps ra_solve_for_pt(p*s,vlo,&ptlo,&dlo); ra_solve_for_pt(p*s,vhi,&pthi,&dhi); @@ -1308,9 +1315,9 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, // Rough translation of ra_get_r(sy_table) used by ar1_ceff_delay. double ArnoldiDelayCalc::ra_get_r(delay_work *D, - timing_table *tab, - double rdelay, - double ctot) + timing_table *tab, + double rdelay, + double ctot) { // find the maximum r that allows a solution for s of // (s,r,ctot)-> output_slew @@ -1333,9 +1340,9 @@ ArnoldiDelayCalc::ra_get_r(delay_work *D, double ArnoldiDelayCalc::ra_get_s(delay_work *D, - timing_table *tab, - double r, - double c) + timing_table *tab, + double r, + double c) { delay_c *con = D->c; double slew_derate = con->slew_derate; @@ -1367,7 +1374,7 @@ ArnoldiDelayCalc::ra_get_s(delay_work *D, double ArnoldiDelayCalc::ra_rdelay_1(timing_table *tab, - double ctot) + double ctot) { // determine the drive resistance from change in delay versus ctot float c1 = ctot; @@ -1387,10 +1394,10 @@ ArnoldiDelayCalc::ra_rdelay_1(timing_table *tab, void ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, - timing_table *tab, - arnoldi1 *mod, - double *delays, - double *slews) + timing_table *tab, + arnoldi1 *mod, + double *delays, + double *slews) { delay_c *con = D->c; double slew_derate = con->slew_derate; @@ -1411,13 +1418,13 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, r = rdelay; r = ra_get_r(D,tab,rdelay,ctot); if (! (r>0.0 - && r<100e+3)) // 100khom + && r<100e+3)) // 100khom rdelay = 1e+3; // 1kohm bool bad = (r0.0 - && s<100e-9)) // 100ns + && s<100e-9)) // 100ns s = 0.5e-9; // .5ns if (debug_->check("arnoldi", 1)) { @@ -1464,9 +1471,9 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, ceff = pr_ceff(s,r,mod->order,p,rr,ceff_time); if ((ceff-1e-20) < 0.0) { // 1e-8pf - debugPrint(debug_, "arnoldi", 1, + debugPrint(debug_, "arnoldi", 1, "Invalid effective capacitance, using total capacitance"); - ceff = ctot; + ceff = ctot; } // new mvs at ceff diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc index 43d57f464..f05f8abcd 100644 --- a/dcalc/ArnoldiReduce.cc +++ b/dcalc/ArnoldiReduce.cc @@ -151,20 +151,20 @@ ArnoldiReduce::~ArnoldiReduce() rcmodel * ArnoldiReduce::reduceToArnoldi(Parasitic *parasitic, - const Pin *drvr_pin, - float coupling_cap_factor, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap) + const Pin *drvr_pin, + float coupling_cap_factor, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { - parasitic_network_ = reinterpret_cast(parasitic); drvr_pin_ = drvr_pin; coupling_cap_factor_ = coupling_cap_factor; rf_ = rf; - corner_ = corner; + scene_ = scene; min_max_ = min_max; - ap_ = ap; + parasitics_ = scene->parasitics(min_max); + parasitic_network_ = reinterpret_cast(parasitic); + loadWork(); return makeRcmodelDrv(); } @@ -426,17 +426,17 @@ ArnoldiReduce::getRC() if (p->node_) { ParasiticNode *node = p->node_; double cap = parasitics_->nodeGndCap(node) - + pinCapacitance(node); + + pinCapacitance(node); if (cap > 0.0) { - p->c = cap; - ctot_ += cap; + p->c = cap; + ctot_ += cap; } else - p->c = 0.0; + 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, + debugPrint(debug_, "arnoldi", 1, "R value %g out of range, drvr pin %s", p->r, network_->pathName(drvr_pin_)); @@ -444,7 +444,7 @@ ArnoldiReduce::getRC() } } for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) { - float cap = parasitics_->value(capacitor) * ap_->couplingCapFactor(); + float cap = parasitics_->value(capacitor) * parasitics_->couplingCapFactor(); ParasiticNode *node1 = parasitics_->node1(capacitor); if (!parasitics_->isExternal(node1)) { ts_point *pt = findPt(node1); @@ -466,10 +466,11 @@ ArnoldiReduce::pinCapacitance(ParasiticNode *node) if (pin) { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(port); + const Sdc *sdc = scene_->sdc(); if (lib_port) - pin_cap = sdc_->pinCapacitance(pin,rf_, corner_, min_max_); + pin_cap = sdc->pinCapacitance(pin,rf_, scene_, min_max_); else if (network_->isTopLevelPort(pin)) - pin_cap = sdc_->portExtCap(port, rf_, corner_, min_max_); + pin_cap = sdc->portExtCap(port, rf_, min_max_); } return pin_cap; } @@ -510,7 +511,7 @@ ArnoldiReduce::makeRcmodelFromTs() if (p->is_term) debugPrint(debug_, "arnoldi", 1, " term %d", p->tindex); if (p->in_edge) - debugPrint(debug_, "arnoldi", 1, " from T%d,P%ld r=%s", + debugPrint(debug_, "arnoldi", 1, " from T%d,P%ld r=%s", p->in_edge->from->ts, p->in_edge->from-p0, units_->resistanceUnit()->asString(p->r)); @@ -610,19 +611,19 @@ ArnoldiReduce::makeRcmodelFromTs() report_->reportLine("order %d n %d",order,n); for (h=0;hreportLine(" d[%d] %s e[%d] %s", + report_->reportLine(" d[%d] %s e[%d] %s", h, units_->timeUnit()->asString(d[h]), h, units_->timeUnit()->asString(e[h])); else - report_->reportLine(" d[%d] %s", + report_->reportLine(" d[%d] %s", h, units_->timeUnit()->asString(d[h])); string line = stdstrPrint("U[%d]",h); for (i=0;ireportLineString(line); } } diff --git a/dcalc/ArnoldiReduce.hh b/dcalc/ArnoldiReduce.hh index 14e5d68ab..96297bb77 100644 --- a/dcalc/ArnoldiReduce.hh +++ b/dcalc/ArnoldiReduce.hh @@ -28,7 +28,8 @@ #pragma once -#include "Map.hh" +#include + #include "Transition.hh" #include "NetworkClass.hh" #include "ParasiticsClass.hh" @@ -39,13 +40,13 @@ namespace sta { class ConcreteParasiticNetwork; class ConcreteParasiticNode; -class Corner; +class Scene; class rcmodel; struct ts_edge; struct ts_point; -typedef Map ArnolidPtMap; +using ArnolidPtMap = std::map; class ArnoldiReduce : public StaState { @@ -56,9 +57,8 @@ public: const Pin *drvr_pin, float coupling_cap_factor, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); protected: void loadWork(); @@ -73,13 +73,13 @@ protected: void makeRcmodelFromTs(); rcmodel *makeRcmodelFromW(); + Parasitics *parasitics_; ConcreteParasiticNetwork *parasitic_network_; const Pin *drvr_pin_; float coupling_cap_factor_; const RiseFall *rf_; - const Corner *corner_; + const Scene *scene_; const MinMax *min_max_; - const ParasiticAnalysisPt *ap_; // ParasiticNode -> ts_point index. ArnolidPtMap pt_map_; diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index f5462661a..e068a73da 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -30,8 +30,7 @@ #include "TimingArc.hh" #include "Network.hh" #include "Graph.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" +#include "Scene.hh" #include "Parasitics.hh" #include "GraphDelayCalc.hh" #include "DmpDelayCalc.hh" @@ -86,17 +85,20 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { in_slew_ = delayAsFloat(in_slew); load_cap_ = load_cap; + parasitics_ = scene->parasitics(min_max); parasitic_ = parasitic; output_waveforms_ = nullptr; - GateTableModel *table_model = arc->gateTableModel(dcalc_ap); + GateTableModel *table_model = arc->gateTableModel(scene, min_max); if (table_model && parasitic) { OutputWaveforms *output_waveforms = table_model->outputWaveforms(); - parasitics_->piModel(parasitic, c2_, rpi_, c1_); + Parasitics *parasitics = scene->parasitics(min_max); + parasitics->piModel(parasitic, c2_, rpi_, c1_); if (output_waveforms && rpi_ > 0.0 && c1_ > 0.0 // Bounds check because extrapolating waveforms does not work for shit. @@ -114,8 +116,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_; - const DcalcAnalysisPtSeq &dcalc_aps = corners_->dcalcAnalysisPts(); - drvr_cell->ensureVoltageWaveforms(dcalc_aps); + drvr_cell->ensureVoltageWaveforms(scenes_); in_slew_ = delayAsFloat(in_slew); output_waveforms_ = output_waveforms; ref_time_ = output_waveforms_->referenceTime(in_slew_); @@ -129,7 +130,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, } } return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); } void @@ -574,7 +575,7 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin, const Pin *drvr_pin, const RiseFall *drvr_rf, const Pin *load_pin, - const Corner *corner, + const Scene *scene, const MinMax *min_max) { bool elmore_exists = false; @@ -582,7 +583,7 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin, if (parasitic_) { parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists); bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin, - drvr_rf, corner, min_max); + drvr_rf, scene, min_max); if (dcalc_success && elmore_exists) { FloatSeq *load_times = new FloatSeq; @@ -617,7 +618,7 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin, const RiseFall *in_rf, const Pin *drvr_pin, const RiseFall *drvr_rf, - const Corner *corner, + const Scene *scene, const MinMax *min_max) { Vertex *in_vertex = graph_->pinLoadVertex(in_pin); @@ -641,15 +642,15 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin, } } if (arc) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - const Slew &in_slew = graph_->slew(in_vertex, in_rf, dcalc_ap->index()); - parasitic_ = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, dcalc_ap); + DcalcAPIndex slew_index = scene->dcalcAnalysisPtIndex(min_max); + const Slew &in_slew = graph_->slew(in_vertex, in_rf, slew_index); + parasitic_ = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, scene, min_max); 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, dcalc_ap); + load_pin_index_map, scene, min_max); return true; } } @@ -666,20 +667,19 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) { Parasitic *pi_elmore = nullptr; const RiseFall *rf = arc->toEdge()->asRiseFall(); if (parasitic && !parasitics_->isPiElmore(parasitic)) { - const ParasiticAnalysisPt *ap = dcalc_ap->parasiticAnalysisPt(); pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, - dcalc_ap->corner(), - dcalc_ap->constraintMinMax(), ap); + scene, min_max); } string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, pi_elmore, load_pin_index_map, - dcalc_ap, digits); + scene, min_max, digits); parasitics_->deleteDrvrReducedParasitics(drvr_pin); return report; } diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 389a7776d..27c5159a8 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -29,7 +29,7 @@ namespace sta { -typedef std::map WatchPinValuesMap; +using WatchPinValuesMap = std::map; ArcDelayCalc * makeCcsCeffDelayCalc(StaState *sta); @@ -49,14 +49,16 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; // Record waveform for drvr/load pin. @@ -100,7 +102,7 @@ protected: const RiseFall *in_rf, const Pin *drvr_pin, const RiseFall *drvr_rf, - const Corner *corner, + const Scene *scene, const MinMax *min_max); Waveform drvrWaveform(); Waveform loadWaveform(const Pin *load_pin); @@ -109,7 +111,7 @@ protected: const Pin *drvr_pin, const RiseFall *drvr_rf, const Pin *load_pin, - const Corner *corner, + const Scene *scene, const MinMax *min_max); void vl(double t, double elmore, @@ -124,6 +126,7 @@ protected: const RiseFall *drvr_rf_; double in_slew_; double load_cap_; + Parasitics *parasitics_; const Parasitic *parasitic_; OutputWaveforms *output_waveforms_; diff --git a/dcalc/DcalcAnalysisPt.cc b/dcalc/DcalcAnalysisPt.cc index 1c5b89eda..e69de29bb 100644 --- a/dcalc/DcalcAnalysisPt.cc +++ b/dcalc/DcalcAnalysisPt.cc @@ -1,68 +0,0 @@ -// 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 "StringUtil.hh" -#include "DcalcAnalysisPt.hh" -#include "Corner.hh" - -namespace sta { - -DcalcAnalysisPt::DcalcAnalysisPt(Corner *corner, - DcalcAPIndex index, - const OperatingConditions *op_cond, - const MinMax *min_max, - const MinMax *check_clk_slew_min_max) : - corner_(corner), - index_(index), - op_cond_(op_cond), - min_max_(min_max), - check_clk_slew_min_max_(check_clk_slew_min_max) -{ -} - -void -DcalcAnalysisPt::setOperatingConditions(const OperatingConditions *op_cond) -{ - op_cond_ = op_cond; -} - -ParasiticAnalysisPt * -DcalcAnalysisPt::parasiticAnalysisPt() const -{ - return corner_->findParasiticAnalysisPt(min_max_); -} - -void -DcalcAnalysisPt::setCheckClkSlewIndex(DcalcAPIndex index) -{ - check_clk_slew_index_ = index; -} - -int -DcalcAnalysisPt::libertyIndex() const -{ - return corner_->libertyIndex(min_max_); -} - -} // namespace diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index 77fc7ed1b..bde9248c9 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -24,7 +24,9 @@ #include "DelayCalc.hh" -#include "Map.hh" +#include + +#include "ContainerHelpers.hh" #include "StringUtil.hh" #include "UnitDelayCalc.hh" #include "LumpedCapDelayCalc.hh" @@ -35,7 +37,7 @@ namespace sta { -typedef Map DelayCalcMap; +typedef std::map DelayCalcMap; static DelayCalcMap *delay_calcs = nullptr; @@ -53,7 +55,7 @@ registerDelayCalcs() void registerDelayCalc(const char *name, - MakeArcDelayCalc maker) + MakeArcDelayCalc maker) { if (delay_calcs == nullptr) delay_calcs = new DelayCalcMap; @@ -69,9 +71,9 @@ deleteDelayCalcs() ArcDelayCalc * makeDelayCalc(const char *name, - StaState *sta) + StaState *sta) { - MakeArcDelayCalc maker = delay_calcs->findKey(name); + MakeArcDelayCalc maker = findKey(delay_calcs, name); if (maker) return maker(sta); else @@ -81,7 +83,7 @@ makeDelayCalc(const char *name, bool isDelayCalcName(const char *name) { - return delay_calcs->hasKey(name); + return delay_calcs->contains(name); } StringSeq diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index e3494286f..a0bbaea66 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -32,8 +32,6 @@ #include "dcalc/PrimaDelayCalc.hh" #include "Sta.hh" -using std::string; - %} %inline %{ @@ -62,15 +60,15 @@ set_delay_calc_incremental_tolerance(float tol) Sta::sta()->setIncrementalDelayTolerance(tol); } -string +std::string report_delay_calc_cmd(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMax *min_max, - int digits) + TimingArc *arc, + const Scene *scene, + const MinMax *min_max, + int digits) { Sta *sta = Sta::sta(); - return sta->reportDelayCalc(edge, arc, corner, min_max, digits); + return sta->reportDelayCalc(edge, arc, scene, min_max, digits); } void diff --git a/dcalc/DelayCalc.tcl b/dcalc/DelayCalc.tcl index b920e4b0e..e195fa234 100644 --- a/dcalc/DelayCalc.tcl +++ b/dcalc/DelayCalc.tcl @@ -25,7 +25,7 @@ namespace eval sta { define_cmd_args "report_dcalc" \ - {[-from from_pin] [-to to_pin] [-corner corner] [-min] [-max] [-digits digits]} + {[-from from_pin] [-to to_pin] [-scene scene] [-min] [-max] [-digits digits]} proc_redirect report_dcalc { report_dcalc_cmd "report_dcalc" $args "-digits" @@ -36,9 +36,9 @@ proc report_dcalc_cmd { cmd cmd_args digits_key } { global sta_report_default_digits parse_key_args $cmd cmd_args \ - keys "$digits_key -from -to -corner" \ + keys "$digits_key -from -to -scene -corner" \ flags {-min -max} - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_flags flags] check_argc_eq0 $cmd $cmd_args @@ -52,14 +52,14 @@ proc report_dcalc_cmd { cmd cmd_args digits_key } { set to_pin [get_port_pin_error "to_pin" $keys(-to)] foreach from_vertex [$from_pin vertices] { foreach to_vertex [$to_pin vertices] { - set iter [$from_vertex out_edge_iterator] - while {[$iter has_next]} { - set edge [$iter next] - if { [$edge to] == $to_vertex } { - report_edge_dcalc $edge $corner $min_max $digits - } - } - $iter finish + set iter [$from_vertex out_edge_iterator] + while {[$iter has_next]} { + set edge [$iter next] + if { [$edge to] == $to_vertex } { + report_edge_dcalc $edge $scene $min_max $digits + } + } + $iter finish } } } elseif [info exists keys(-from)] { @@ -67,8 +67,8 @@ proc report_dcalc_cmd { cmd cmd_args digits_key } { foreach from_vertex [$from_pin vertices] { set iter [$from_vertex out_edge_iterator] while {[$iter has_next]} { - set edge [$iter next] - report_edge_dcalc $edge $corner $min_max $digits + set edge [$iter next] + report_edge_dcalc $edge $scene $min_max $digits } $iter finish } @@ -77,15 +77,15 @@ proc report_dcalc_cmd { cmd cmd_args digits_key } { foreach to_vertex [$to_pin vertices] { set iter [$to_vertex in_edge_iterator] while {[$iter has_next]} { - set edge [$iter next] - report_edge_dcalc $edge $corner $min_max $digits + set edge [$iter next] + report_edge_dcalc $edge $scene $min_max $digits } $iter finish } } } -proc report_edge_dcalc { edge corner min_max digits } { +proc report_edge_dcalc { edge scene min_max digits } { set role [$edge role] if { $role != "wire" } { set from_vertex [$edge from] @@ -96,7 +96,7 @@ proc report_edge_dcalc { edge corner min_max digits } { set library [$cell library] # Filter timing checks based on min_max. if {!(($min_max == "max" && $role == "hold") \ - || ($min_max=="min" && $role=="setup"))} { + || ($min_max=="min" && $role=="setup"))} { report_line "Library: [get_name $library]" report_line "Cell: [get_name $cell]" set sense [$edge sense] @@ -106,18 +106,18 @@ proc report_edge_dcalc { edge corner min_max digits } { report_line "Arc type: $role" foreach arc [$edge timing_arcs] { - set from [get_name [$from_pin port]] - set from_rf [$arc from_edge] - set to [get_name [$to_pin port]] - set to_rf [$arc to_edge] - report_line "$from $from_rf -> $to $to_rf" - report_line [report_delay_calc_cmd $edge $arc $corner $min_max $digits] - if { [$edge delay_annotated $arc $corner $min_max] } { - set delay [$edge arc_delay $arc $corner $min_max] - report_line "Annotated value = [format_time $delay $digits]" - } - report_line "............................................." - report_line "" + set from [get_name [$from_pin port]] + set from_rf [$arc from_edge] + set to [get_name [$to_pin port]] + set to_rf [$arc to_edge] + report_line "$from $from_rf -> $to $to_rf" + report_line [report_delay_calc_cmd $edge $arc $scene $min_max $digits] + if { [$edge delay_annotated $arc $scene $min_max] } { + set delay [$edge arc_delay $arc $scene $min_max] + report_line "Annotated value = [format_time $delay $digits]" + } + report_line "............................................." + report_line "" } } } @@ -140,16 +140,16 @@ define_cmd_args "set_pocv_sigma_factor" { factor } ################################################################ define_cmd_args "set_assigned_delay" \ - {-cell|-net [-rise] [-fall] [-corner corner] [-min] [-max]\ + {-cell|-net [-rise] [-fall] [-scene scene] [-min] [-max]\ [-from from_pins] [-to to_pins] delay} # Change the delay for timing arcs between from_pins and to_pins matching # on cell (instance) or net. proc set_assigned_delay { args } { - parse_key_args "set_assigned_delay" args keys {-corner -from -to} \ + parse_key_args "set_assigned_delay" args keys {-scene -corner -from -to} \ flags {-cell -net -rise -fall -max -min} check_argc_eq1 "set_assigned_delay" $args - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_all_check_flags flags] set to_rf [parse_rise_fall_flags flags] @@ -176,14 +176,14 @@ proc set_assigned_delay { args } { 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]." - } + if {[$pin instance] != $inst} { + sta_error 185 "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]" - } + if {[$pin instance] != $inst} { + sta_error 186 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]" + } } } } elseif {![info exists flags(-net)]} { @@ -192,40 +192,40 @@ proc set_assigned_delay { args } { foreach from_pin $from_pins { set from_vertices [$from_pin vertices] set_assigned_delay1 [lindex $from_vertices 0] \ - $to_pins $to_rf $corner $min_max $delay + $to_pins $to_rf $scene $min_max $delay if { [llength $from_vertices] == 2 } { set_assigned_delay1 [lindex $from_vertices 1] \ - $to_pins $to_rf $corner $min_max $delay + $to_pins $to_rf $scene $min_max $delay } } } -proc set_assigned_delay1 { from_vertex to_pins to_rf corner min_max delay } { +proc set_assigned_delay1 { from_vertex to_pins to_rf scene min_max delay } { foreach to_pin $to_pins { set to_vertices [$to_pin vertices] set_assigned_delay2 $from_vertex [lindex $to_vertices 0] \ - $to_rf $corner $min_max $delay + $to_rf $scene $min_max $delay if { [llength $to_vertices] == 2 } { # Bidirect driver. set_assigned_delay2 $from_vertex [lindex $to_vertices 1] \ - $to_rf $corner $min_max $delay + $to_rf $scene $min_max $delay } } } -proc set_assigned_delay2 {from_vertex to_vertex to_rf corner min_max delay} { +proc set_assigned_delay2 {from_vertex to_vertex to_rf scene min_max delay} { set matched 0 set edge_iter [$from_vertex out_edge_iterator] while {[$edge_iter has_next]} { set edge [$edge_iter next] if { [$edge to] == $to_vertex \ - && ![timing_role_is_check [$edge role]] } { + && ![timing_role_is_check [$edge role]] } { foreach arc [$edge timing_arcs] { - if { $to_rf == "rise_fall" \ - || $to_rf eq [$arc to_edge_name] } { - set_arc_delay $edge $arc $corner $min_max $delay + if { $to_rf == "rise_fall" \ + || $to_rf eq [$arc to_edge_name] } { + set_arc_delay $edge $arc $scene $min_max $delay set matched 1 - } + } } } } @@ -239,13 +239,13 @@ proc set_assigned_delay2 {from_vertex to_vertex to_rf corner min_max delay} { define_cmd_args "set_assigned_check" \ {-setup|-hold|-recovery|-removal [-rise] [-fall]\ - [-corner corner] [-min] [-max]\ + [-scene scene] [-min] [-max]\ [-from from_pins] [-to to_pins] [-clock rise|fall]\ [-cond sdf_cond] check_value} proc set_assigned_check { args } { parse_key_args "set_assigned_check" args \ - keys {-from -to -corner -clock -cond} \ + keys {-from -to -scene -corner -clock -cond} \ flags {-setup -hold -recovery -removal -rise -fall -max -min} check_argc_eq1 "set_assigned_check" $args @@ -258,7 +258,7 @@ proc set_assigned_check { args } { if { [info exists keys(-clock)] } { set clk_arg $keys(-clock) if { $clk_arg eq "rise" \ - || $clk_arg eq "fall" } { + || $clk_arg eq "fall" } { set from_rf $clk_arg } else { sta_error 189 "set_assigned_check -clock must be rise or fall." @@ -271,7 +271,7 @@ proc set_assigned_check { args } { sta_error 190 "set_assigned_check missing -to argument." } set to_rf [parse_rise_fall_flags flags] - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_all_check_flags flags] if { [info exists flags(-setup)] } { @@ -298,46 +298,46 @@ proc set_assigned_check { args } { foreach from_pin $from_pins { set from_vertices [$from_pin vertices] set_assigned_check1 [lindex $from_vertices 0] $from_rf \ - $to_pins $to_rf $role $corner $min_max $cond $check_value + $to_pins $to_rf $role $scene $min_max $cond $check_value if { [llength $from_vertices] == 2 } { set_assigned_check1 [lindex $from_vertices 1] $from_rf \ - $to_pins $to_rf $role $corner $min_max $cond $check_value + $to_pins $to_rf $role $scene $min_max $cond $check_value } } } proc set_assigned_check1 { from_vertex from_rf to_pins to_rf \ - role corner min_max cond check_value } { + role scene min_max cond check_value } { foreach to_pin $to_pins { set to_vertices [$to_pin vertices] set_assigned_check2 $from_vertex $from_rf [lindex $to_vertices 0] \ - $to_rf $role $corner $min_max $cond $check_value + $to_rf $role $scene $min_max $cond $check_value if { [llength $to_vertices] == 2 } { # Bidirect driver. set_assigned_check2 $from_vertex $from_rf \ - [lindex $to_vertices 1] $to_rf $role $corner $min_max \ - $cond $check_value + [lindex $to_vertices 1] $to_rf $role $scene $min_max \ + $cond $check_value } } } proc set_assigned_check2 { from_vertex from_rf to_vertex to_rf \ - role corner min_max cond check_value } { + role scene min_max cond check_value } { set edge_iter [$from_vertex out_edge_iterator] set matched 0 while {[$edge_iter has_next]} { set edge [$edge_iter next] if { [$edge to] == $to_vertex } { foreach arc [$edge timing_arcs] { - if { ($from_rf eq "rise_fall" \ - || $from_rf eq [$arc from_edge_name]) \ - && ($to_rf eq "rise_fall" \ - || $to_rf eq [$arc to_edge_name]) \ - && [$arc role] eq $role \ - && ($cond eq "" || [$arc sdf_cond] eq $cond) } { - set_arc_delay $edge $arc $corner $min_max $check_value + if { ($from_rf eq "rise_fall" \ + || $from_rf eq [$arc from_edge_name]) \ + && ($to_rf eq "rise_fall" \ + || $to_rf eq [$arc to_edge_name]) \ + && [$arc role] eq $role \ + && ($cond eq "" || [$arc sdf_cond] eq $cond) } { + set_arc_delay $edge $arc $scene $min_max $check_value set matched 1 - } + } } } } @@ -350,14 +350,14 @@ proc set_assigned_check2 { from_vertex from_rf to_vertex to_rf \ ################################################################a define_cmd_args "set_assigned_transition" \ - {[-rise] [-fall] [-corner corner] [-min] [-max] slew pins} + {[-rise] [-fall] [-scene scene] [-min] [-max] slew pins} # Change the slew on a list of ports. proc set_assigned_transition { args } { - parse_key_args "set_assigned_transition" args keys {-corner} \ + parse_key_args "set_assigned_transition" args keys {-scene -corner} \ flags {-rise -fall -max -min} - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_all_check_flags flags] set tr [parse_rise_fall_flags flags] check_argc_eq2 "set_assigned_transition" $args @@ -371,7 +371,7 @@ proc set_assigned_transition { args } { foreach pin $pins { set vertices [$pin vertices] set vertex [lindex $vertices 0] - set_annotated_slew $vertex $corner $min_max $tr $slew + set_annotated_slew $vertex $scene $min_max $tr $slew if { [llength $vertices] == 2 } { # Bidirect driver. set vertex [lindex $vertices 1] @@ -380,5 +380,27 @@ proc set_assigned_transition { args } { } } +################################################################ + +define_cmd_args "report_slews" {[-scenes scenes] pin} + +proc report_slews { args } { + global sta_report_default_digits + + parse_key_args "report_slews" args keys {-corner -scenes} flags {} + 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 + 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] + report_line "[vertex_path_name $vertex] [rise_short_name] $rise_min:$rise_max [fall_short_name] $fall_min:$fall_max" + } +} + # sta namespace end } diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index 8c15bfdb9..ecaa16d4f 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -32,8 +32,7 @@ #include "Parasitics.hh" #include "Graph.hh" #include "Sdc.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" +#include "Scene.hh" #include "GraphDelayCalc.hh" #include "Variables.hh" @@ -50,7 +49,7 @@ DelayCalcBase::DelayCalcBase(StaState *sta) : void DelayCalcBase::reduceParasitic(const Parasitic *parasitic_network, const Net *net, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max) { NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); @@ -59,16 +58,12 @@ DelayCalcBase::reduceParasitic(const Parasitic *parasitic_network, if (network_->isDriver(pin)) { for (const RiseFall *rf : RiseFall::range()) { for (const MinMax *min_max : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner1 : *corners_) { - DcalcAnalysisPt *dcalc_ap = corner1->findDcalcAnalysisPt(min_max); - reduceParasitic(parasitic_network, pin, rf, dcalc_ap); - } - } - else { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - reduceParasitic(parasitic_network, pin, rf, dcalc_ap); + if (scene == nullptr) { + for (const Scene *scene1 : scenes_) + reduceParasitic(parasitic_network, pin, rf, scene1, min_max); } + else + reduceParasitic(parasitic_network, pin, rf, scene, min_max); } } } @@ -136,7 +131,7 @@ DelayCalcBase::thresholdAdjust(const Pin *load_pin, float drvr_slew_derate = drvr_library->slewDerateFromLibrary(); float load_slew_derate = load_library->slewDerateFromLibrary(); load_slew = load_slew * ((load_slew_delta / load_slew_derate) - / (drvr_slew_delta / drvr_slew_derate)); + / (drvr_slew_delta / drvr_slew_derate)); } } @@ -162,13 +157,15 @@ DelayCalcBase::checkDelay(const Pin *check_pin, const Slew &from_slew, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - CheckTimingModel *model = arc->checkModel(dcalc_ap); + CheckTimingModel *model = arc->checkModel(scene, min_max); if (model) { float from_slew1 = delayAsFloat(from_slew); float to_slew1 = delayAsFloat(to_slew); - return model->checkDelay(pinPvt(check_pin, dcalc_ap), from_slew1, to_slew1, + return model->checkDelay(pinPvt(check_pin, scene, min_max), + from_slew1, to_slew1, related_out_cap, variables_->pocvEnabled()); } @@ -183,40 +180,46 @@ DelayCalcBase::reportCheckDelay(const Pin *check_pin, const char *from_slew_annotation, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) { - CheckTimingModel *model = arc->checkModel(dcalc_ap); + CheckTimingModel *model = arc->checkModel(scene, min_max); if (model) { float from_slew1 = delayAsFloat(from_slew); float to_slew1 = delayAsFloat(to_slew); - return model->reportCheckDelay(pinPvt(check_pin, dcalc_ap), from_slew1, - from_slew_annotation, to_slew1, - related_out_cap, false, digits); + return model->reportCheckDelay(pinPvt(check_pin, scene, min_max), + from_slew1, from_slew_annotation, + to_slew1, related_out_cap, false, + digits); } return ""; } const Pvt * DelayCalcBase::pinPvt(const Pin *pin, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { const Instance *drvr_inst = network_->instance(pin); - const Pvt *pvt = sdc_->pvt(drvr_inst, dcalc_ap->constraintMinMax()); + const Sdc *sdc = scene->sdc(); + const Pvt *pvt = sdc->pvt(drvr_inst, min_max); if (pvt == nullptr) - pvt = dcalc_ap->operatingConditions(); + pvt = sdc->operatingConditions(min_max); return pvt; } void DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArg &gate, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { const Pin *drvr_pin = gate.drvrPin(); if (drvr_pin) { const Parasitic *parasitic; float load_cap; - graph_delay_calc_->parasiticLoad(drvr_pin, gate.drvrEdge(), dcalc_ap, + graph_delay_calc_->parasiticLoad(drvr_pin, gate.drvrEdge(), + scene, min_max, nullptr, this, load_cap, parasitic); gate.setLoadCap(load_cap); @@ -224,17 +227,19 @@ DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArg &gate, const Pin *in_pin = gate.inPin(); const Vertex *in_vertex = graph_->pinLoadVertex(in_pin); const Slew &in_slew = graph_delay_calc_->edgeFromSlew(in_vertex, gate.inEdge(), - gate.edge(), dcalc_ap); + gate.edge(), + scene, min_max); gate.setInSlew(in_slew); } } void DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { for (ArcDcalcArg &gate : gates) - setDcalcArgParasiticSlew(gate, dcalc_ap); + setDcalcArgParasiticSlew(gate, scene, min_max); } } // namespace diff --git a/dcalc/DelayCalcBase.hh b/dcalc/DelayCalcBase.hh index 82597f7ef..6caa4f6c6 100644 --- a/dcalc/DelayCalcBase.hh +++ b/dcalc/DelayCalcBase.hh @@ -34,23 +34,26 @@ class GateTableModel; class DelayCalcBase : public ArcDelayCalc { public: - explicit DelayCalcBase(StaState *sta); + DelayCalcBase(StaState *sta); void finishDrvrPin() override; void reduceParasitic(const Parasitic *parasitic_network, const Net *net, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max) override; void setDcalcArgParasiticSlew(ArcDcalcArg &gate, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDelay checkDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportCheckDelay(const Pin *check_pin, const TimingArc *arc, @@ -58,7 +61,8 @@ public: const char *from_slew_annotation, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; protected: @@ -68,18 +72,19 @@ protected: void thresholdAdjust(const Pin *load_pin, const LibertyLibrary *drvr_library, const RiseFall *rf, - ArcDelay &load_delay, - Slew &load_slew); + ArcDelay &load_delay, + Slew &load_slew); // Helper function for input ports driving dspf parasitic. void dspfWireDelaySlew(const Pin *load_pin, - const RiseFall *rf, + const RiseFall *rf, Slew drvr_slew, float elmore, // Return values. - ArcDelay &wire_delay, - Slew &load_slew); + ArcDelay &wire_delay, + Slew &load_slew); const Pvt *pinPvt(const Pin *pin, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); using ArcDelayCalc::reduceParasitic; }; diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index e5b8f85ee..d759fc40b 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -44,7 +44,6 @@ #include "Network.hh" #include "Sdc.hh" #include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" #include "ArcDelayCalc.hh" #include "FindRoot.hh" #include "Variables.hh" @@ -95,36 +94,36 @@ class DmpError : public Exception static double gateModelRd(const LibertyCell *cell, - const GateTableModel *gate_model, - const RiseFall *rf, - double in_slew, - double c2, - double c1, - const Pvt *pvt, - bool pocv_enabled); + const GateTableModel *gate_model, + const RiseFall *rf, + double in_slew, + double c2, + double c1, + const Pvt *pvt, + bool pocv_enabled); static void newtonRaphson(const int max_iter, - double x[], - const int n, - const double x_tol, - // eval(state) is called to fill fvec and fjac. - function eval, - // Temporaries supplied by caller. - double *fvec, - double **fjac, - int *index, - double *p, - double *scale); + double x[], + const int n, + const double x_tol, + // eval(state) is called to fill fvec and fjac. + function eval, + // Temporaries supplied by caller. + double *fvec, + double **fjac, + int *index, + double *p, + double *scale); static void luSolve(double **a, - const int size, - const int *index, - double b[]); + const int size, + const int *index, + double b[]); static void luDecomp(double **a, - const int size, - int *index, - double *scale); + const int size, + int *index, + double *scale); //////////////////////////////////////////////////////////////// @@ -138,23 +137,23 @@ class DmpAlg : public StaState virtual const char *name() = 0; // Set driver model and pi model parameters for delay calculation. virtual void init(const LibertyLibrary *library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - double c2, - double rpi, - double c1); + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + double c2, + double rpi, + double c1); virtual void gateDelaySlew(// Return values. double &delay, - double &slew) = 0; + double &slew) = 0; virtual void loadDelaySlew(const Pin *load_pin, - double elmore, + double elmore, // Return values. - ArcDelay &delay, - Slew &slew); + ArcDelay &delay, + Slew &slew); double ceff() { return ceff_; } // Given x_ as a vector of input parameters, fill fvec_ with the @@ -176,32 +175,32 @@ class DmpAlg : public StaState void findDriverParams(double ceff); void gateCapDelaySlew(double cl, // Return values. - double &delay, - double &slew); + double &delay, + double &slew); void gateDelays(double ceff, // Return values. - double &t_vth, - double &t_vl, - double &slew); + double &t_vth, + double &t_vl, + double &slew); // Partial derivatives of y(t) (jacobian). void dy(double t, - double t0, - double dt, - double cl, + double t0, + double dt, + double cl, // Return values. - double &dydt0, - double &dyddt, - double &dydcl); + double &dydt0, + double &dyddt, + double &dydcl); double y0dt(double t, - double cl); + double cl); double y0dcl(double t, - double cl); + double cl); void showX(); void showFvec(); void showJacobian(); void findDriverDelaySlew(// Return values. double &delay, - double &slew); + double &slew); double findVoCrossing(double vth, double lower_bound, double upper_bound); @@ -214,12 +213,12 @@ class DmpAlg : public StaState // Output response to vs(t) ramp driving capacitive load. double y(double t, - double t0, - double dt, - double cl); + double t0, + double dt, + double cl); // Output response to unit ramp driving capacitive load. double y0(double t, - double cl); + double cl); // Output response to unit ramp driving pi model load. virtual void V0(double t, // Return values. @@ -285,7 +284,7 @@ class DmpAlg : public StaState }; DmpAlg::DmpAlg(int nr_order, - StaState *sta): + StaState *sta): StaState(sta), c2_(0.0), rpi_(0.0), @@ -301,16 +300,16 @@ DmpAlg::~DmpAlg() = default; void DmpAlg::init(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - // Pi model. - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + // Pi model. + double c2, + double rpi, + double c1) { drvr_library_ = drvr_library; drvr_cell_ = drvr_cell; @@ -343,7 +342,7 @@ DmpAlg::findDriverParams(double ceff) x_[DmpParam::t0] = t0; newtonRaphson(100, x_, nr_order_, driver_param_tol, [this] () { evalDmpEqns(); }, - fvec_, fjac_, index_, p_, scale_); + fvec_, fjac_, index_, p_, scale_); t0_ = x_[DmpParam::t0]; dt_ = x_[DmpParam::dt]; debugPrint(debug_, "dmp_ceff", 3, " t0 = %s dt = %s ceff = %s", @@ -356,9 +355,9 @@ DmpAlg::findDriverParams(double ceff) void DmpAlg::gateCapDelaySlew(double ceff, - // Return values. + // Return values. double &delay, - double &slew) + double &slew) { ArcDelay model_delay; Slew model_slew; @@ -371,10 +370,10 @@ DmpAlg::gateCapDelaySlew(double ceff, void DmpAlg::gateDelays(double ceff, - // Return values. + // Return values. double &t_vth, - double &t_vl, - double &slew) + double &t_vl, + double &slew) { double table_slew; gateCapDelaySlew(ceff, t_vth, table_slew); @@ -385,9 +384,9 @@ DmpAlg::gateDelays(double ceff, double DmpAlg::y(double t, - double t0, - double dt, - double cl) + double t0, + double dt, + double cl) { double t1 = t - t0; if (t1 <= 0.0) @@ -400,20 +399,20 @@ DmpAlg::y(double t, double DmpAlg::y0(double t, - double cl) + double cl) { return t - rd_ * cl * (1.0 - exp2(-t / (rd_ * cl))); } void DmpAlg::dy(double t, - double t0, - double dt, - double cl, - // Return values. - double &dydt0, - double &dyddt, - double &dydcl) + double t0, + double dt, + double cl, + // Return values. + double &dydt0, + double &dyddt, + double &dydcl) { double t1 = t - t0; if (t1 <= 0.0) @@ -433,14 +432,14 @@ DmpAlg::dy(double t, double DmpAlg::y0dt(double t, - double cl) + double cl) { return 1.0 - exp2(-t / (rd_ * cl)); } double DmpAlg::y0dcl(double t, - double cl) + double cl) { return rd_ * ((1.0 + t / (rd_ * cl)) * exp2(-t / (rd_ * cl)) - 1); } @@ -478,7 +477,7 @@ DmpAlg::showJacobian() void DmpAlg::findDriverDelaySlew(// Return values. double &delay, - double &slew) + double &slew) { double t_upper = voCrossingUpperBound(); delay = findVoCrossing(vth_, t0_, t_upper); @@ -554,9 +553,9 @@ DmpAlg::showVo() void DmpAlg::loadDelaySlew(const Pin *, - double elmore, - ArcDelay &delay, - Slew &slew) + double elmore, + ArcDelay &delay, + Slew &slew) { if (!driver_valid_ || elmore == 0.0 @@ -570,7 +569,7 @@ DmpAlg::loadDelaySlew(const Pin *, // convert the delay and slew to the load's thresholds. try { if (debug_->check("dmp_ceff", 4)) - showVl(); + showVl(); elmore_ = elmore; p3_ = 1.0 / elmore; double t_lower = t0_; @@ -583,17 +582,17 @@ DmpAlg::loadDelaySlew(const Pin *, // Convert measured slew to reported/table slew. double slew1 = (th - tl) / slew_derate_; if (delay1 < 0.0) { - // Only report a problem if the difference is significant. - if (-delay1 > vth_time_tol * vo_delay_) - fail("load delay less than zero"); - // Use elmore delay. - delay1 = elmore; + // Only report a problem if the difference is significant. + if (-delay1 > vth_time_tol * vo_delay_) + fail("load delay less than zero"); + // Use elmore delay. + delay1 = elmore; } if (slew1 < drvr_slew_) { - // Only report a problem if the difference is significant. - if ((drvr_slew_ - slew1) > vth_time_tol * drvr_slew_) - fail("load slew less than driver slew"); - slew1 = drvr_slew_; + // Only report a problem if the difference is significant. + if ((drvr_slew_ - slew1) > vth_time_tol * drvr_slew_) + fail("load slew less than driver slew"); + slew1 = drvr_slew_; } delay = delay1; slew = slew1; @@ -735,26 +734,26 @@ DmpCap::DmpCap(StaState *sta): void DmpCap::init(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + double c2, + double rpi, + 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); + rd, in_slew, c2, rpi, c1); ceff_ = c1 + c2; } void DmpCap::gateDelaySlew(// Return values. double &delay, - double &slew) + double &slew) { debugPrint(debug_, "dmp_ceff", 3, " ceff = %s", units_->capacitanceUnit()->asString(ceff_)); @@ -764,9 +763,9 @@ DmpCap::gateDelaySlew(// Return values. void DmpCap::loadDelaySlew(const Pin *, - double elmore, - ArcDelay &delay, - Slew &slew) + double elmore, + ArcDelay &delay, + Slew &slew) { delay = elmore; slew = drvr_slew_; @@ -830,9 +829,9 @@ class DmpPi : public DmpAlg private: void findDriverParamsPi(); double ipiIceff(double t0, - double dt, - double ceff_time, - double ceff); + double dt, + double ceff_time, + double ceff); void V0(double t, // Return values. double &vo, @@ -876,19 +875,19 @@ DmpPi::DmpPi(StaState *sta) : void DmpPi::init(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + double c2, + double rpi, + 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); + in_slew, c2, rpi, c1); // Find poles/zeros. z1_ = 1.0 / (rpi_ * c1_); @@ -914,7 +913,7 @@ DmpPi::init(const LibertyLibrary *drvr_library, void DmpPi::gateDelaySlew(// Return values. double &delay, - double &slew) + double &slew) { driver_valid_ = false; try { @@ -1000,7 +999,7 @@ DmpPi::evalDmpEqns() (-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))) + - 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))) @@ -1027,17 +1026,17 @@ DmpPi::evalDmpEqns() // Eqn 13, Eqn 14. double DmpPi::ipiIceff(double, double dt, - double ceff_time, - double ceff) + double ceff_time, + double ceff) { double exp_p1_dt = exp2(-p1_ * ceff_time); 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)) + + (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)) + * (1.0 - exp_dt_rd_ceff)) / (rd_ * ceff_time * dt); return ipi - iceff; } @@ -1064,7 +1063,7 @@ DmpPi::Vl0(double t, 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_)); + + p3_ * k4_ / (p2_ - p3_)); double exp_p1 = exp2(-p1_ * t); double exp_p2 = exp2(-p2_ * t); double exp_p3 = exp2(-p3_ * t); @@ -1194,19 +1193,19 @@ DmpZeroC2::DmpZeroC2(StaState *sta) : void DmpZeroC2::init(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double rd, - double in_slew, - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double rd, + double in_slew, + double c2, + double rpi, + 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); + in_slew, c2, rpi, c1); ceff_ = c1; z1_ = 1.0 / (rpi_ * c1_); @@ -1221,7 +1220,7 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library, void DmpZeroC2::gateDelaySlew(// Return values. double &delay, - double &slew) + double &slew) { try { findDriverParams(c1_); @@ -1280,16 +1279,16 @@ DmpZeroC2::voCrossingUpperBound() // Return error msg on failure. static void newtonRaphson(const int max_iter, - double x[], - const int size, - const double x_tol, - function eval, - // Temporaries supplied by caller. - double *fvec, - double **fjac, - int *index, - double *p, - double *scale) + double x[], + const int size, + const double x_tol, + function eval, + // Temporaries supplied by caller. + double *fvec, + double **fjac, + int *index, + double *p, + double *scale) { for (int k = 0; k < max_iter; k++) { eval(); @@ -1302,7 +1301,7 @@ newtonRaphson(const int max_iter, bool all_under_x_tol = true; for (int i = 0; i < size; i++) { if (abs(p[i]) > abs(x[i]) * x_tol) - all_under_x_tol = false; + all_under_x_tol = false; x[i] += p[i]; } if (all_under_x_tol) @@ -1325,11 +1324,11 @@ newtonRaphson(const int max_iter, // Return error msg on failure. void luDecomp(double **a, - const int size, - int *index, - // Temporary supplied by caller. - // scale stores the implicit scaling of each row. - double *scale) + const int size, + int *index, + // Temporary supplied by caller. + // scale stores the implicit scaling of each row. + double *scale) { // Find implicit scaling factors. for (int i = 0; i < size; i++) { @@ -1337,7 +1336,7 @@ luDecomp(double **a, for (int j = 0; j < size; j++) { double temp = abs(a[i][j]); if (temp > big) - big = temp; + big = temp; } if (big == 0.0) throw DmpError("LU decomposition: no non-zero row element"); @@ -1349,7 +1348,7 @@ luDecomp(double **a, for (int i = 0; i < j; i++) { double sum = a[i][j]; for (int k = 0; k < i; k++) - sum -= a[i][k] * a[k][j]; + sum -= a[i][k] * a[k][j]; a[i][j] = sum; } // Run down jth subdiag to form the residuals after the elimination @@ -1362,21 +1361,21 @@ luDecomp(double **a, for (int i = j; i < size; i++) { double sum = a[i][j]; for (int k = 0; k < j; k++) - sum -= a[i][k] * a[k][j]; + sum -= a[i][k] * a[k][j]; a[i][j] = sum; double dum = scale[i] * abs(sum); if (dum >= big) { - big = dum; - imax = i; + big = dum; + imax = i; } } // Permute current row with imax. if (j != imax) { // Yes, do so... for (int k = 0; k < size; k++) { - double dum = a[imax][k]; - a[imax][k] = a[j][k]; - a[j][k] = dum; + double dum = a[imax][k]; + a[imax][k] = a[j][k]; + a[j][k] = dum; } scale[imax] = scale[j]; } @@ -1387,7 +1386,7 @@ luDecomp(double **a, if (j != size_1) { double pivot = 1.0 / a[j][j]; for (int i = j + 1; i < size; i++) - a[i][j] *= pivot; + a[i][j] *= pivot; } } } @@ -1399,9 +1398,9 @@ luDecomp(double **a, // a and index are not modified. void luSolve(double **a, - const int size, - const int *index, - double b[]) + const int size, + const int *index, + double b[]) { // Transform b allowing for leading zeros. int non_zero = -1; @@ -1411,11 +1410,11 @@ luSolve(double **a, b[iperm] = b[i]; if (non_zero != -1) { for (int j = non_zero; j <= i - 1; j++) - sum -= a[i][j] * b[j]; + sum -= a[i][j] * b[j]; } else { if (sum != 0.0) - non_zero = i; + non_zero = i; } b[i] = sum; } @@ -1495,20 +1494,22 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { + parasitics_ = scene->parasitics(min_max); const RiseFall *rf = arc->toEdge()->asRiseFall(); const LibertyCell *drvr_cell = arc->from()->libertyCell(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); - GateTableModel *table_model = arc->gateTableModel(dcalc_ap); + GateTableModel *table_model = arc->gateTableModel(scene, min_max); if (table_model && parasitic) { float in_slew1 = delayAsFloat(in_slew); float c2, rpi, c1; parasitics_->piModel(parasitic, c2, rpi, c1); if (isnan(c2) || isnan(c1) || isnan(rpi)) report_->error(1040, "parasitic Pi model has NaNs."); - setCeffAlgorithm(drvr_library, drvr_cell, pinPvt(drvr_pin, dcalc_ap), + setCeffAlgorithm(drvr_library, drvr_cell, pinPvt(drvr_pin, scene, min_max), table_model, rf, in_slew1, c2, rpi, c1); double gate_delay, drvr_slew; gateDelaySlew(gate_delay, drvr_slew); @@ -1529,12 +1530,12 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, else { ArcDcalcResult dcalc_result = LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); if (parasitic - && !unsuppored_model_warned_) { + && !unsuppored_model_warned_) { unsuppored_model_warned_ = true; report_->warn(1041, "cell %s delay model not supported on SPF parasitics by DMP delay calculator", - drvr_cell->name()); + drvr_cell->name()); } return dcalc_result; } @@ -1542,25 +1543,25 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, void DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, - const LibertyCell *drvr_cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double in_slew, - double c2, - double rpi, - double c1) + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double in_slew, + double c2, + double rpi, + double c1) { double rd = 0.0; if (gate_model) { rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1, - pvt, variables_->pocvEnabled()); + pvt, variables_->pocvEnabled()); // 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. - || rpi < rd * 1e-3 - // c1/Rpi can be ignored. - || (c1 == 0.0 || c1 < c2 * 1e-3 || rpi == 0.0)) + // Rpi is small compared to Rd, which makes the load capacitive. + || rpi < rd * 1e-3 + // c1/Rpi can be ignored. + || (c1 == 0.0 || c1 < c2 * 1e-3 || rpi == 0.0)) dmp_alg_ = dmp_cap_; else if (c2 < c1 * 1e-3) dmp_alg_ = dmp_zero_c2_; @@ -1571,7 +1572,7 @@ 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); + 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)", units_->timeUnit()->asString(in_slew), @@ -1585,16 +1586,17 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, string DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *parasitic, + const Slew &in_slew, + float load_cap, + const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, - int digits) + const Scene *scene, + const MinMax *min_max, + int digits) { ArcDcalcResult dcalc_result = gateDelay(drvr_pin, arc, in_slew, load_cap, - parasitic, load_pin_index_map, dcalc_ap); - GateTableModel *model = arc->gateTableModel(dcalc_ap); + parasitic, load_pin_index_map, scene, min_max); + GateTableModel *model = arc->gateTableModel(scene, min_max); float c_eff = 0.0; string result; const LibertyCell *drvr_cell = arc->to()->libertyCell(); @@ -1603,9 +1605,11 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, const Unit *cap_unit = units->capacitanceUnit(); const Unit *res_unit = units->resistanceUnit(); if (parasitic && dmp_alg_) { + Parasitics *parasitics = scene->parasitics(min_max); + c_eff = dmp_alg_->ceff(); float c2, rpi, c1; - parasitics_->piModel(parasitic, c2, rpi, c1); + parasitics->piModel(parasitic, c2, rpi, c1); result += "Pi model C2="; result += cap_unit->asString(c2, digits); result += " Rpi="; @@ -1621,7 +1625,8 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, if (model) { const Unit *time_unit = units->timeUnit(); float in_slew1 = delayAsFloat(in_slew); - result += model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, c_eff, + result += model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), + in_slew1, c_eff, variables_->pocvEnabled(), digits); result += "Driver waveform slew = "; float drvr_slew = delayAsFloat(dcalc_result.drvrSlew()); @@ -1633,13 +1638,13 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, static double gateModelRd(const LibertyCell *cell, - const GateTableModel *gate_model, - const RiseFall *rf, - double in_slew, - double c2, - double c1, - const Pvt *pvt, - bool pocv_enabled) + const GateTableModel *gate_model, + const RiseFall *rf, + double in_slew, + double c2, + double c1, + const Pvt *pvt, + bool pocv_enabled) { float cap1 = c1 + c2; float cap2 = cap1 + 1e-15; @@ -1655,7 +1660,7 @@ gateModelRd(const LibertyCell *cell, void DmpCeffDelayCalc::gateDelaySlew(// Return values. double &delay, - double &slew) + double &slew) { dmp_alg_->gateDelaySlew(delay, slew); } diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index e85151515..fc3fbb2b9 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -49,14 +49,16 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; void copyState(const StaState *sta) override; @@ -71,22 +73,23 @@ protected: Slew &load_slew) = 0; void gateDelaySlew(// Return values. double &delay, - double &slew); + double &slew); void loadDelaySlewElmore(const Pin *load_pin, double elmore, ArcDelay &delay, Slew &slew); // Select the appropriate special case Dartu/Menezes/Pileggi algorithm. void setCeffAlgorithm(const LibertyLibrary *library, - const LibertyCell *cell, - const Pvt *pvt, - const GateTableModel *gate_model, - const RiseFall *rf, - double in_slew, - double c2, - double rpi, - double c1); + const LibertyCell *cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const RiseFall *rf, + double in_slew, + double c2, + double rpi, + double c1); + const Parasitics *parasitics_; static bool unsuppored_model_warned_; private: diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 9081a65dd..5e8b37b01 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -31,7 +31,6 @@ #include "Network.hh" #include "Sdc.hh" #include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" #include "DmpCeff.hh" @@ -50,7 +49,8 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; protected: void loadDelaySlew(const Pin *load_pin, @@ -86,8 +86,10 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *scene, + const MinMax *min_max) { + Parasitics *parasitics = scene->parasitics(min_max); ArcDcalcResult dcalc_result(load_pin_index_map.size()); LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); for (auto [load_pin, load_idx] : load_pin_index_map) { @@ -96,7 +98,7 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *, bool elmore_exists = false; float elmore = 0.0; if (parasitic) - parasitics_->findElmore(parasitic, load_pin, elmore, elmore_exists); + parasitics->findElmore(parasitic, load_pin, elmore, elmore_exists); if (elmore_exists) // Input port with no external driver. dspfWireDelaySlew(load_pin, rf, in_slew, elmore, wire_delay, load_slew); @@ -140,20 +142,23 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc const char *name() const override { return "dmp_ceff_two_pole"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + 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 DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; private: void loadDelaySlew(const Pin *load_pin, @@ -166,20 +171,20 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc Slew &load_slew) override; void loadDelay(double drvr_slew, Parasitic *pole_residue, - double p1, - double k1, - ArcDelay &wire_delay, - Slew &load_slew); + double p1, + double k1, + ArcDelay &wire_delay, + Slew &load_slew); float loadDelay(double vth, - double p1, - double p2, - double k1, - double k2, - double B, - double k1_p1_2, - double k2_p2_2, - double tt, - double y_tt); + double p1, + double p2, + double k1, + double k2, + double B, + double k1_p1_2, + double k2_p2_2, + double tt, + double y_tt); bool parasitic_is_pole_residue_; float vth_; @@ -212,43 +217,41 @@ DmpCeffTwoPoleDelayCalc::copy() Parasitic * DmpCeffTwoPoleDelayCalc::findParasitic(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { Parasitic *parasitic = nullptr; - const Corner *corner = dcalc_ap->corner(); - // set_load net has precedence over parasitics. - if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + const Sdc *sdc = scene->sdc(); + Parasitics *parasitics = scene->parasitics(min_max); + if (parasitics == nullptr + // set_load net has precedence over parasitics. || network_->direction(drvr_pin)->isInternal()) return nullptr; - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); // Prefer PiPoleResidue. - parasitic = parasitics_->findPiPoleResidue(drvr_pin, rf, parasitic_ap); + parasitic = parasitics->findPiPoleResidue(drvr_pin, rf, min_max); if (parasitic) return parasitic; - parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap); + parasitic = parasitics->findPiElmore(drvr_pin, rf, min_max); if (parasitic) return parasitic; Parasitic *parasitic_network = - parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + parasitics->findParasiticNetwork(drvr_pin); if (parasitic_network) { - parasitic = parasitics_->reduceToPiPoleResidue2(parasitic_network, drvr_pin, rf, - corner, - dcalc_ap->constraintMinMax(), - parasitic_ap); + parasitic = parasitics->reduceToPiPoleResidue2(parasitic_network, drvr_pin, rf, + scene, min_max); if (parasitic) return parasitic; } - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - Wireload *wireload = sdc_->wireload(cnst_min_max); + Wireload *wireload = sdc->wireload(min_max); if (wireload) { float pin_cap, wire_cap, fanout; bool has_wire_cap; - graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap, + graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_wire_cap); - parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload, - fanout, pin_cap, corner, - cnst_min_max); + parasitic = parasitics->estimatePiElmore(drvr_pin, rf, wireload, + fanout, pin_cap, + scene, min_max); } return parasitic; } @@ -259,21 +262,23 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *scene, + const MinMax *min_max) { + 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; LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); for (const auto [load_pin, load_idx] : load_pin_index_map) { - if (parasitics_->isPiPoleResidue(parasitic)) { - const Parasitic *pole_residue = parasitics_->findPoleResidue(parasitic, load_pin); + if (parasitics->isPiPoleResidue(parasitic)) { + const Parasitic *pole_residue = parasitics->findPoleResidue(parasitic, load_pin); if (pole_residue) { - size_t pole_count = parasitics_->poleResidueCount(pole_residue); + size_t pole_count = parasitics->poleResidueCount(pole_residue); if (pole_count >= 1) { ComplexFloat pole1, residue1; // Find the 1st (elmore) pole. - parasitics_->poleResidue(pole_residue, 0, pole1, residue1); + parasitics->poleResidue(pole_residue, 0, pole1, residue1); if (pole1.imag() == 0.0 && residue1.imag() == 0.0) { float p1 = pole1.real(); @@ -297,8 +302,10 @@ DmpCeffTwoPoleDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { + parasitics_ = scene->parasitics(min_max); const LibertyLibrary *drvr_library = arc->to()->libertyLibrary(); const RiseFall *rf = arc->toEdge()->asRiseFall(); vth_ = drvr_library->outputThreshold(rf); @@ -306,7 +313,7 @@ DmpCeffTwoPoleDelayCalc::gateDelay(const Pin *drvr_pin, vh_ = drvr_library->slewUpperThreshold(rf); slew_derate_ = drvr_library->slewDerateFromLibrary(); return DmpCeffDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, - load_pin_index_map, dcalc_ap) ; + load_pin_index_map, scene, min_max) ; } void @@ -333,9 +340,9 @@ DmpCeffTwoPoleDelayCalc::loadDelaySlew(const Pin *load_pin, // Find the 1st (elmore) pole. parasitics_->poleResidue(pole_residue, 0, pole1, residue1); if (pole1.imag() == 0.0 - && residue1.imag() == 0.0) { - float p1 = pole1.real(); - float k1 = residue1.real(); + && residue1.imag() == 0.0) { + float p1 = pole1.real(); + float k1 = residue1.real(); if (pole_count >= 2) loadDelay(drvr_slew, pole_residue, p1, k1, wire_delay, load_slew); else { @@ -352,11 +359,11 @@ DmpCeffTwoPoleDelayCalc::loadDelaySlew(const Pin *load_pin, void DmpCeffTwoPoleDelayCalc::loadDelay(double drvr_slew, Parasitic *pole_residue, - double p1, + double p1, double k1, - // Return values. + // Return values. ArcDelay &wire_delay, - Slew &load_slew) + Slew &load_slew) { ComplexFloat pole2, residue2; parasitics_->poleResidue(pole_residue, 1, pole2, residue2); @@ -371,7 +378,7 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double drvr_slew, // Convert tt to 0:1 range. float tt = delayAsFloat(drvr_slew) * slew_derate_ / (vh_ - vl_); double y_tt = (tt - B + k1_p1_2 * exp(-p1 * tt) - + k2_p2_2 * exp(-p2 * tt)) / tt; + + k2_p2_2 * exp(-p2 * tt)) / tt; wire_delay = loadDelay(vth_, p1, p2, k1, k2, B, k1_p1_2, k2_p2_2, tt, y_tt) - tt * vth_; @@ -383,15 +390,15 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double drvr_slew, float DmpCeffTwoPoleDelayCalc::loadDelay(double vth, - double p1, - double p2, - double k1, - double k2, - double B, - double k1_p1_2, - double k2_p2_2, - double tt, - double y_tt) + double p1, + double p2, + double k1, + double k2, + double B, + double k1_p1_2, + double k2_p2_2, + double tt, + double y_tt) { if (y_tt < vth) { // t1 > tt @@ -403,9 +410,9 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double vth, double exp_p1_t1_tt = exp(-p1 * (t1 - tt)); double exp_p2_t1_tt = exp(-p2 * (t1 - tt)); double y_t1 = (tt - k1_p1_2 * (exp_p1_t1_tt - exp_p1_t1) - - k2_p2_2 * (exp_p2_t1_tt - exp_p2_t1)) / tt; + - k2_p2_2 * (exp_p2_t1_tt - exp_p2_t1)) / tt; double yp_t1 = (k1 / p1 * (exp_p1_t1_tt - exp_p1_t1) - - k2 / p2 * (exp_p2_t1_tt - exp_p2_t1)) / tt; + - k2 / p2 * (exp_p2_t1_tt - exp_p2_t1)) / tt; double delay = t1 - (y_t1 - vth) / yp_t1; return static_cast(delay); } @@ -417,9 +424,9 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double vth, double exp_p1_t1 = exp(-p1 * t1); double exp_p2_t1 = exp(-p2 * t1); double y_t1 = (t1 - B + k1_p1_2 * exp_p1_t1 - + k2_p2_2 * exp_p1_t1) / tt; + + k2_p2_2 * exp_p1_t1) / tt; double yp_t1 = (1 - k1 / p1 * exp_p1_t1 - - k2 / p2 * exp_p2_t1) / tt; + - k2 / p2 * exp_p2_t1) / tt; double delay = t1 - (y_t1 - vth) / yp_t1; return static_cast(delay); } diff --git a/dcalc/FindRoot.cc b/dcalc/FindRoot.cc index ded4bec07..b74d7d16b 100644 --- a/dcalc/FindRoot.cc +++ b/dcalc/FindRoot.cc @@ -32,10 +32,10 @@ using std::abs; double findRoot(FindRootFunc func, - double x1, - double x2, - double x_tol, - int max_iter, + double x1, + double x2, + double x_tol, + int max_iter, // Return value. bool &fail) { @@ -47,12 +47,12 @@ findRoot(FindRootFunc func, double findRoot(FindRootFunc func, - double x1, - double y1, + double x1, + double y1, double x2, - double y2, + double y2, double x_tol, - int max_iter, + int max_iter, // Return value. bool &fail) { @@ -83,8 +83,8 @@ findRoot(FindRootFunc func, for (int iter = 0; iter < max_iter; iter++) { // Newton/raphson out of range. if ((((root - x2) * dy - y) * ((root - x1) * dy - y) > 0.0) - // Not decreasing fast enough. - || (abs(2.0 * y) > abs(dx_prev * dy))) { + // Not decreasing fast enough. + || (abs(2.0 * y) > abs(dx_prev * dy))) { // Bisect x1/x2 interval. dx_prev = dx; dx = (x2 - x1) * 0.5; diff --git a/dcalc/FindRoot.hh b/dcalc/FindRoot.hh index e4d4d8542..7224671dd 100644 --- a/dcalc/FindRoot.hh +++ b/dcalc/FindRoot.hh @@ -28,28 +28,28 @@ namespace sta { -typedef const std::function FindRootFunc; +using FindRootFunc = const std::function; double findRoot(FindRootFunc func, - double x1, - double x2, - double x_tol, - int max_iter, + double x1, + double x2, + double x_tol, + int max_iter, // Return value. bool &fail); double findRoot(FindRootFunc func, - double x1, - double y1, + double x1, + double y1, double x2, - double y2, - double x_tol, - int max_iter, + double y2, + double x_tol, + int max_iter, // Return value. bool &fail); diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 6fcaa8702..f75883c9a 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -24,6 +24,9 @@ #include "GraphDelayCalc.hh" +#include + +#include "ContainerHelpers.hh" #include "Debug.hh" #include "Stats.hh" #include "MinMax.hh" @@ -35,17 +38,18 @@ #include "Network.hh" #include "InputDrive.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Graph.hh" #include "Parasitics.hh" #include "search/Levelize.hh" -#include "Corner.hh" +#include "Scene.hh" #include "SearchPred.hh" #include "Bfs.hh" #include "ArcDelayCalc.hh" -#include "DcalcAnalysisPt.hh" #include "NetCaps.hh" #include "ClkNetwork.hh" #include "Variables.hh" +#include "search/Latches.hh" namespace sta { @@ -59,16 +63,94 @@ static bool isLeafDriver(const Pin *pin, const Network *network); +//////////////////////////////////////////////////////////////// + +class DcalcPred : public SearchPred +{ +public: + DcalcPred(const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; +}; + +DcalcPred::DcalcPred(const StaState *sta) : + SearchPred(sta) +{ +} + +bool +DcalcPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const +{ + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); + const Network *network = sta_->network(); + Net *net = network->net(from_pin); + return !(sdc->isDisabledConstraint(from_pin) + || (net && (network->isPower(net) + || network->isGround(net)))); +} + +bool +DcalcPred::searchThru(Edge *edge, + const Mode *mode) const +{ + const Sdc *sdc = mode->sdc(); + const Variables *variables = sta_->variables(); + const TimingRole *role = edge->role(); + return !(role->isTimingCheck() + || (role == TimingRole::regSetClr() + && !variables->presetClrArcsEnabled()) + || edge->isDisabledLoop() + || sdc->isDisabledConstraint(edge) + || sdc->isDisabledCondDefault(edge) + || (edge->isBidirectInstPath() + && !variables->bidirectInstPathsEnabled())); +} + +bool +DcalcPred::searchTo(const Vertex *, + const Mode *) const +{ + return true; +} + +class DcalcNonLatchPred : public DcalcPred +{ +public: + DcalcNonLatchPred(const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; +}; + +DcalcNonLatchPred::DcalcNonLatchPred(const StaState *sta) : + DcalcPred(sta) +{ +} + +bool +DcalcNonLatchPred::searchThru(Edge *edge, + const Mode *mode) const +{ + return DcalcPred::searchThru(edge, mode) + && !edge->role()->isLatchDtoQ(); +} + +//////////////////////////////////////////////////////////////// + GraphDelayCalc::GraphDelayCalc(StaState *sta) : StaState(sta), observer_(nullptr), delays_seeded_(false), incremental_(false), delays_exist_(false), - invalid_delays_(new VertexSet(graph_)), - search_pred_(new SearchPred1(sta)), - search_non_latch_pred_(new SearchPredNonLatch2(sta)), - clk_pred_(new ClkTreeSearchPred(sta)), + invalid_delays_(makeVertexSet(this)), + search_pred_(new DcalcPred(sta)), + search_non_latch_pred_(new DcalcNonLatchPred(sta)), iter_(new BfsFwdIterator(BfsIndex::dcalc, search_non_latch_pred_, sta)), incremental_delay_tolerance_(0.0) { @@ -77,9 +159,7 @@ GraphDelayCalc::GraphDelayCalc(StaState *sta) : GraphDelayCalc::~GraphDelayCalc() { delete search_pred_; - delete invalid_delays_; delete search_non_latch_pred_; - delete clk_pred_; delete iter_; deleteMultiDrvrNets(); delete observer_; @@ -88,16 +168,14 @@ GraphDelayCalc::~GraphDelayCalc() void GraphDelayCalc::deleteMultiDrvrNets() { - Set drvr_nets; - MultiDrvrNetMap::Iterator multi_iter(multi_drvr_net_map_); - while (multi_iter.hasNext()) { - MultiDrvrNet *multi_drvr = multi_iter.next(); + std::set drvr_nets; + for (auto [vertex, multi_drvr] : multi_drvr_net_map_) { // Multiple drvr pins point to the same drvr PinSet, // so collect them into a set. drvr_nets.insert(multi_drvr); } multi_drvr_net_map_.clear(); - drvr_nets.deleteContents(); + deleteContents(drvr_nets); } void @@ -106,6 +184,8 @@ GraphDelayCalc::copyState(const StaState *sta) StaState::copyState(sta); // Notify sub-components. iter_->copyState(sta); + search_pred_->copyState(sta); + search_non_latch_pred_->copyState(sta); } void @@ -143,7 +223,7 @@ GraphDelayCalc::delaysInvalid() incremental_ = false; iter_->clear(); // No need to keep track of incremental updates any more. - invalid_delays_->clear(); + invalid_delays_.clear(); invalid_check_edges_.clear(); invalid_latch_edges_.clear(); } @@ -176,11 +256,11 @@ GraphDelayCalc::delayInvalid(Vertex *vertex) debugPrint(debug_, "delay_calc", 2, "delay invalid %s", vertex->to_string(this).c_str()); if (graph_ && incremental_) { - invalid_delays_->insert(vertex); + invalid_delays_.insert(vertex); // Invalidate driver that triggers dcalc for multi-driver nets. MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); if (multi_drvr) - invalid_delays_->insert(multi_drvr->dcalcDrvr()); + invalid_delays_.insert(multi_drvr->dcalcDrvr()); } } @@ -202,7 +282,7 @@ GraphDelayCalc::deleteVertexBefore(Vertex *vertex) { iter_->deleteVertexBefore(vertex); if (incremental_) - invalid_delays_->erase(vertex); + invalid_delays_.erase(vertex); MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); if (multi_drvr) { // Don't bother incrementally updating MultiDrvrNet. @@ -298,15 +378,9 @@ GraphDelayCalc::findDelays(Level level) void GraphDelayCalc::seedInvalidDelays() { - for (Vertex *vertex : *invalid_delays_) { - if (vertex->isRoot()) - seedRootSlew(vertex, arc_delay_calc_); - else { - if (search_non_latch_pred_->searchFrom(vertex)) + for (Vertex *vertex : invalid_delays_) iter_->enqueue(vertex); - } - } - invalid_delays_->clear(); + invalid_delays_.clear(); } void @@ -334,32 +408,34 @@ GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex, const Pin *drvr_pin = drvr_vertex->pin(); debugPrint(debug_, "delay_calc", 2, "seed driver slew %s", drvr_vertex->to_string(this).c_str()); - InputDrive *drive = 0; + for (const Scene *scene : scenes_) { + const Sdc *sdc = scene->sdc(); + for (const MinMax *min_max : MinMax::range()) { + for (const RiseFall *rf : RiseFall::range()) { + InputDrive *drive = nullptr; if (network_->isTopLevelPort(drvr_pin)) { Port *port = network_->port(drvr_pin); - drive = sdc_->findInputDrive(port); + drive = sdc->findInputDrive(port); } - for (const RiseFall *rf : RiseFall::range()) { - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { if (drive) { - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); const LibertyCell *drvr_cell; const LibertyPort *from_port, *to_port; float *from_slews; - drive->driveCell(rf, cnst_min_max, drvr_cell, from_port, + drive->driveCell(rf, min_max, drvr_cell, from_port, from_slews, to_port); if (drvr_cell) { if (from_port == nullptr) from_port = driveCellDefaultFromPort(drvr_cell, to_port); findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf, - from_port, from_slews, to_port, dcalc_ap); + from_port, from_slews, to_port, scene, min_max); } else - seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, dcalc_ap, + seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, scene, min_max, arc_delay_calc); } else - seedNoDrvrSlew(drvr_vertex, drvr_pin, rf, dcalc_ap, arc_delay_calc); + seedNoDrvrSlew(drvr_vertex, drvr_pin, rf, scene, min_max, arc_delay_calc); + } } } } @@ -369,15 +445,15 @@ GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex, const Pin *drvr_pin, const RiseFall *rf, const InputDrive *drive, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); Slew slew = default_slew; float drive_slew; bool exists; - drive->slew(rf, cnst_min_max, drive_slew, exists); + drive->slew(rf, min_max, drive_slew, exists); if (exists) slew = drive_slew; else { @@ -390,24 +466,23 @@ GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex, } Delay drive_delay = delay_zero; float drive_res; - drive->driveResistance(rf, cnst_min_max, drive_res, exists); + drive->driveResistance(rf, min_max, drive_res, exists); const Parasitic *parasitic; float load_cap; - parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc, + parasiticLoad(drvr_pin, rf, scene, min_max, nullptr, arc_delay_calc, load_cap, parasitic); if (exists) { drive_delay = load_cap * drive_res; slew = load_cap * drive_res; } - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) + if (!drvr_vertex->slewAnnotated(rf, min_max)) graph_->setSlew(drvr_vertex, rf, ap_index, slew); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult dcalc_result = arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), rf, parasitic, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); annotateLoadDelays(drvr_vertex, rf, dcalc_result, load_pin_index_map, - drive_delay, false, dcalc_ap); + drive_delay, false, scene, min_max); arc_delay_calc->finishDrvrPin(); } @@ -426,11 +501,11 @@ void GraphDelayCalc::seedNoDrvrSlew(Vertex *drvr_vertex, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - DcalcAPIndex ap_index = dcalc_ap->index(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); Slew slew(default_slew); // Top level bidirect driver uses load slew unless // bidirect instance paths are disabled. @@ -438,15 +513,15 @@ GraphDelayCalc::seedNoDrvrSlew(Vertex *drvr_vertex, Vertex *load_vertex = graph_->pinLoadVertex(drvr_pin); slew = graph_->slew(load_vertex, rf, ap_index); } - if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) + if (!drvr_vertex->slewAnnotated(rf, min_max)) graph_->setSlew(drvr_vertex, rf, ap_index, slew); - Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); + Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, scene, min_max); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult dcalc_result = arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), rf, parasitic, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); annotateLoadDelays(drvr_vertex, rf, dcalc_result, load_pin_index_map, delay_zero, - false, dcalc_ap); + false, scene, min_max); arc_delay_calc->finishDrvrPin(); } @@ -456,28 +531,28 @@ GraphDelayCalc::seedLoadSlew(Vertex *vertex) const Pin *pin = vertex->pin(); debugPrint(debug_, "delay_calc", 2, "seed load slew %s", vertex->to_string(this).c_str()); - ClockSet *clks = sdc_->findLeafPinClocks(pin); initSlew(vertex); + for (const Scene *scene : scenes_) { + 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 DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (!vertex->slewAnnotated(rf, slew_min_max)) { + ClockSet *clks = sdc->findLeafPinClocks(pin); + if (!vertex->slewAnnotated(rf, min_max)) { float slew = 0.0; if (clks) { - slew = slew_min_max->initValue(); - ClockSet::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); - float clk_slew = clk->slew(rf, slew_min_max); - if (slew_min_max->compare(clk_slew, slew)) + 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; } } - DcalcAPIndex ap_index = dcalc_ap->index(); graph_->setSlew(vertex, rf, ap_index, slew); } } } + } } // If a driving cell does not specify a -from_pin, the first port @@ -526,7 +601,8 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, const LibertyPort *from_port, float *from_slews, const LibertyPort *to_port, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { debugPrint(debug_, "delay_calc", 2, " driver cell %s %s", drvr_cell->name(), @@ -535,7 +611,7 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, for (TimingArc *arc : arc_set->arcs()) { if (arc->toEdge()->asRiseFall() == rf) { float from_slew = from_slews[arc->fromEdge()->index()]; - findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, dcalc_ap); + findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, scene, min_max); } } } @@ -550,7 +626,8 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, Vertex *drvr_vertex, const TimingArc *arc, float from_slew, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { debugPrint(debug_, "delay_calc", 3, " %s %s -> %s %s (%s)", arc->from()->name(), @@ -560,23 +637,24 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, arc->role()->to_string().c_str()); const RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); if (drvr_rf) { - DcalcAPIndex ap_index = dcalc_ap->index(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + const Parasitic *parasitic; float load_cap; - parasiticLoad(drvr_pin, drvr_rf, dcalc_ap, nullptr, arc_delay_calc_, + parasiticLoad(drvr_pin, drvr_rf, scene, min_max, nullptr, arc_delay_calc_, load_cap, parasitic); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult intrinsic_result = arc_delay_calc_->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); 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, - dcalc_ap); + scene, min_max); ArcDelay gate_delay = gate_result.gateDelay(); Slew gate_slew = gate_result.drvrSlew(); @@ -588,7 +666,7 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, delayAsString(gate_slew, this)); graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); annotateLoadDelays(drvr_vertex, drvr_rf, gate_result, load_pin_index_map, - load_delay, false, dcalc_ap); + load_delay, false, scene, min_max); arc_delay_calc_->finishDrvrPin(); } } @@ -608,11 +686,8 @@ GraphDelayCalc::findVertexDelay(Vertex *vertex, debugPrint(debug_, "delay_calc", 2, "find delays %s (%s)", vertex->to_string(this).c_str(), network_->cellName(network_->instance(pin))); - if (vertex->isRoot()) { + if (vertex->isRoot()) seedRootSlew(vertex, arc_delay_calc); - if (propagate) - iter_->enqueueAdjacentVertices(vertex); - } else { if (network_->isLeaf(pin)) { if (vertex->isDriver(network_)) { @@ -793,7 +868,7 @@ isLeafDriver(const Pin *pin, MultiDrvrNet * GraphDelayCalc::multiDrvrNet(const Vertex *drvr_vertex) const { - return multi_drvr_net_map_.findKey(drvr_vertex); + return findKey(multi_drvr_net_map_, drvr_vertex); } MultiDrvrNet * @@ -827,7 +902,7 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) } } multi_drvr->setDcalcDrvr(max_drvr); - multi_drvr->findCaps(sdc_); + multi_drvr->findCaps(this); return multi_drvr; } report_->critical(1101, "mult_drvr missing load."); @@ -842,17 +917,19 @@ GraphDelayCalc::initLoadSlews(Vertex *drvr_vertex) Edge *wire_edge = edge_iter.next(); if (wire_edge->isWire()) { Vertex *load_vertex = wire_edge->to(graph_); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - Slew slew_init_value(slew_min_max->initValue()); - DcalcAPIndex ap_index = dcalc_ap->index(); + + 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()) { - if (!load_vertex->slewAnnotated(rf, slew_min_max)) + if (!load_vertex->slewAnnotated(rf, min_max)) graph_->setSlew(load_vertex, rf, ap_index, slew_init_value); } } } } + } } bool @@ -868,11 +945,7 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, VertexInEdgeIterator edge_iter(drvr_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - // Don't let disabled edges set slews that influence downstream delays. - if (search_pred_->searchFrom(from_vertex) - && search_pred_->searchThru(edge) - && !edge->role()->isLatchDtoQ()) + if (!edge->role()->isLatchDtoQ()) delay_changed |= findDriverEdgeDelays(drvr_vertex, multi_drvr, edge, arc_delay_calc, load_pin_index_map, delay_exists); @@ -891,14 +964,15 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, void GraphDelayCalc::initRootSlews(Vertex *vertex) { - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - DcalcAPIndex ap_index = dcalc_ap->index(); + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); for (const RiseFall *rf : RiseFall::range()) { - if (!vertex->slewAnnotated(rf, slew_min_max)) + if (!vertex->slewAnnotated(rf, min_max)) graph_->setSlew(vertex, rf, ap_index, default_slew); } } + } } void @@ -930,13 +1004,20 @@ GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex, Vertex *from_vertex = edge->from(graph_); const TimingArcSet *arc_set = edge->timingArcSet(); bool delay_changed = false; - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { + + for (Scene *scene : scenes_) { + const Mode *mode = scene->mode(); + if (search_pred_->searchFrom(from_vertex, mode) + && search_pred_->searchThru(edge, mode)) { + for (const MinMax *min_max : MinMax::range()) { for (const TimingArc *arc : arc_set->arcs()) { delay_changed |= findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc, - dcalc_ap, arc_delay_calc, + scene, min_max, arc_delay_calc, load_pin_index_map); delay_exists[arc->toEdge()->asRiseFall()->index()] = true; } + } + } } if (delay_changed && observer_) { observer_->delayChangedFrom(from_vertex); @@ -950,12 +1031,13 @@ void GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) { MultiDrvrNet *multi_drvr = multiDrvrNet(drvr_vertex); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); - findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc, dcalc_ap, + findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc, scene, min_max, arc_delay_calc, load_pin_index_map); } @@ -964,7 +1046,8 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map) { @@ -975,33 +1058,33 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, const Pin *drvr_pin = drvr_vertex->pin(); const Parasitic *parasitic; float load_cap; - parasiticLoad(drvr_pin, drvr_rf, dcalc_ap, multi_drvr, arc_delay_calc, + parasiticLoad(drvr_pin, drvr_rf, scene, min_max, multi_drvr, arc_delay_calc, load_cap, parasitic); if (multi_drvr && multi_drvr->parallelGates(network_)) { ArcDcalcArgSeq dcalc_args = makeArcDcalcArgs(drvr_vertex, multi_drvr, - edge, arc, dcalc_ap, + edge, arc, scene, min_max, arc_delay_calc); ArcDcalcResultSeq dcalc_results = - arc_delay_calc->gateDelays(dcalc_args, load_pin_index_map, dcalc_ap); + arc_delay_calc->gateDelays(dcalc_args, load_pin_index_map, scene, min_max); for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) { ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx]; delay_changed |= annotateDelaysSlews(dcalc_arg.edge(), dcalc_arg.arc(), dcalc_result, load_pin_index_map, - dcalc_ap); + scene, min_max); } } else { Vertex *from_vertex = edge->from(graph_); - const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); + const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge, scene, min_max); ArcDcalcResult dcalc_result = arc_delay_calc->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, - dcalc_ap); + scene, min_max); delay_changed |= annotateDelaysSlews(edge, arc, dcalc_result, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); } arc_delay_calc->finishDrvrPin(); } @@ -1013,7 +1096,8 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) { ArcDcalcArgSeq dcalc_args; @@ -1032,11 +1116,13 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex, const Pin *from_pin = from_vertex->pin(); const RiseFall *from_rf = arc1->fromEdge()->asRiseFall(); const RiseFall *drvr_rf = arc1->toEdge()->asRiseFall(); - const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, dcalc_ap); + Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, scene, min_max); + in_slew = edgeFromSlew(from_vertex, from_rf, edge1, scene, min_max); + const Pin *drvr_pin1 = drvr_vertex1->pin(); float load_cap; const Parasitic *parasitic; - parasiticLoad(drvr_pin1, drvr_rf, dcalc_ap, multi_drvr, arc_delay_calc, + parasiticLoad(drvr_pin1, drvr_rf, scene, min_max, multi_drvr, arc_delay_calc, load_cap, parasitic); dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew, load_cap, parasitic); @@ -1062,7 +1148,8 @@ GraphDelayCalc::findParallelEdge(Vertex *vertex, VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { edge = edge_iter.next(); - if (edge->timingArcSet() == arc->set()) + if (edge->timingArcSet() == arc->set() + && !edge->isBidirectInstPath()) return; } } @@ -1072,7 +1159,8 @@ GraphDelayCalc::findParallelEdge(Vertex *vertex, edge = edge_iter.next(); for (TimingArc *arc1 : edge->timingArcSet()->arcs()) { if (arc1->fromEdge() == drvr_arc->fromEdge() - && arc1->toEdge() == drvr_arc->toEdge()) { + && arc1->toEdge() == drvr_arc->toEdge() + && !edge->isBidirectInstPath()) { arc = arc1; return; } @@ -1088,17 +1176,18 @@ GraphDelayCalc::annotateDelaysSlews(Edge *edge, const TimingArc *arc, ArcDcalcResult &dcalc_result, LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { bool delay_changed = annotateDelaySlew(edge, arc, dcalc_result.gateDelay(), - dcalc_result.drvrSlew(), dcalc_ap); + dcalc_result.drvrSlew(), scene, min_max); if (!edge->role()->isLatchDtoQ()) { Vertex *drvr_vertex = edge->to(graph_); delay_changed |= annotateLoadDelays(drvr_vertex, arc->toEdge()->asRiseFall(), dcalc_result, load_pin_index_map, delay_zero, true, - dcalc_ap); + scene, min_max); } return delay_changed; } @@ -1111,30 +1200,30 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge, const TimingArc *arc, ArcDelay &gate_delay, Slew &gate_slew, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - bool delay_changed = false; - DcalcAPIndex ap_index = dcalc_ap->index(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); debugPrint(debug_, "delay_calc", 3, - " %s %s -> %s %s (%s) corner:%s/%s", + " %s %s -> %s %s (%s) scene:%s/%s", arc->from()->name(), arc->fromEdge()->to_string().c_str(), arc->to()->name(), arc->toEdge()->to_string().c_str(), arc->role()->to_string().c_str(), - dcalc_ap->corner()->name(), - dcalc_ap->delayMinMax()->to_string().c_str()); + scene->name().c_str(), + min_max->to_string().c_str()); debugPrint(debug_, "delay_calc", 3, " gate delay = %s slew = %s", delayAsString(gate_delay, this), delayAsString(gate_slew, this)); + bool delay_changed = false; 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 MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (delayGreater(gate_slew, drvr_slew, slew_min_max, this) - && !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max) + 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)) { @@ -1160,11 +1249,11 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, LoadPinIndexMap &load_pin_index_map, const ArcDelay &extra_delay, bool merge, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); bool changed = false; - DcalcAPIndex ap_index = dcalc_ap->index(); - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); while (edge_iter.hasNext()) { Edge *wire_edge = edge_iter.next(); @@ -1180,8 +1269,8 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, delayAsString(wire_delay, this), delayAsString(load_slew, this)); bool load_changed = false; - if (!load_vertex->slewAnnotated(drvr_rf, slew_min_max)) { - if (drvr_vertex->slewAnnotated(drvr_rf, slew_min_max)) { + 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); graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew); @@ -1190,7 +1279,7 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, else { const Slew &slew = graph_->slew(load_vertex, drvr_rf, ap_index); if (!merge - || delayGreater(load_slew, slew, slew_min_max, this)) { + || delayGreater(load_slew, slew, min_max, this)) { graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); load_changed = true; } @@ -1202,9 +1291,8 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, // rather than set. const ArcDelay &delay = graph_->wireArcDelay(wire_edge, drvr_rf, ap_index); Delay wire_delay_extra = extra_delay + wire_delay; - const MinMax *delay_min_max = dcalc_ap->delayMinMax(); if (!merge - || delayGreater(wire_delay_extra, delay, delay_min_max, this)) { + || delayGreater(wire_delay_extra, delay, min_max, this)) { graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, wire_delay_extra); load_changed = true; } @@ -1241,12 +1329,12 @@ GraphDelayCalc::makeLoadPinIndexMap(Vertex *drvr_vertex) // External float GraphDelayCalc::loadCap(const Pin *drvr_pin, - const DcalcAnalysisPt *dcalc_ap) const + const Scene *scene, + const MinMax *min_max) const { - const MinMax *min_max = dcalc_ap->constraintMinMax(); float load_cap = min_max->initValue(); for (const RiseFall *drvr_rf : RiseFall::range()) { - float cap = loadCap(drvr_pin, drvr_rf, dcalc_ap); + float cap = loadCap(drvr_pin, drvr_rf, scene, min_max); load_cap = min_max->minMax(cap, load_cap); } arc_delay_calc_->finishDrvrPin(); @@ -1257,10 +1345,11 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, float GraphDelayCalc::loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) const + const Scene *scene, + const MinMax *min_max) const { float pin_cap, wire_cap; - loadCap(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap); + loadCap(drvr_pin, rf, scene, min_max, pin_cap, wire_cap); return pin_cap + wire_cap; } @@ -1268,7 +1357,8 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, void GraphDelayCalc::loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, float &pin_cap, float &wire_cap) const { @@ -1278,7 +1368,7 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, multi_drvr = multiDrvrNet(drvr_vertex); } const Parasitic *parasitic; - parasiticLoad(drvr_pin, rf, dcalc_ap, multi_drvr, arc_delay_calc_, + parasiticLoad(drvr_pin, rf, scene, min_max, multi_drvr, arc_delay_calc_, pin_cap, wire_cap, parasitic); arc_delay_calc_->finishDrvrPin(); } @@ -1286,12 +1376,13 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, float GraphDelayCalc::loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) const { const Parasitic *parasitic; float pin_cap, wire_cap; - parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc, + parasiticLoad(drvr_pin, rf, scene, min_max, nullptr, arc_delay_calc, pin_cap, wire_cap, parasitic); return pin_cap + wire_cap; } @@ -1299,7 +1390,8 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, void GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, // Return values. @@ -1307,7 +1399,7 @@ GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, const Parasitic *¶sitic) const { float pin_cap, wire_cap; - parasiticLoad(drvr_pin, rf, dcalc_ap, multi_drvr, arc_delay_calc, + parasiticLoad(drvr_pin, rf, scene, min_max, multi_drvr, arc_delay_calc, pin_cap, wire_cap, parasitic); load_cap = pin_cap + wire_cap; } @@ -1315,7 +1407,8 @@ GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, void GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, // Return values. @@ -1323,19 +1416,20 @@ GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, float &wire_cap, const Parasitic *¶sitic) const { + Parasitics *parasitics = scene->parasitics(min_max); bool has_net_load; float fanout; - netCaps(drvr_pin, rf, dcalc_ap, multi_drvr, + netCaps(drvr_pin, rf, scene, min_max, multi_drvr, pin_cap, wire_cap, fanout, has_net_load); - parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); + parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, scene, min_max); // set_load net has precedence over parasitics. if (!has_net_load && parasitic) { - if (parasitics_->isParasiticNetwork(parasitic)) - wire_cap += parasitics_->capacitance(parasitic); + if (parasitics->isParasiticNetwork(parasitic)) + wire_cap += parasitics->capacitance(parasitic); else { // PiModel includes both pin and external caps. - float parasitic_cap = parasitics_->capacitance(parasitic); + float parasitic_cap = parasitics->capacitance(parasitic); if (parasitic_cap >= pin_cap) wire_cap = parasitic_cap - pin_cap; else { @@ -1350,7 +1444,8 @@ GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, void GraphDelayCalc::netCaps(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, @@ -1362,14 +1457,15 @@ GraphDelayCalc::netCaps(const Pin *drvr_pin, Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); multi_drvr = multiDrvrNet(drvr_vertex); } - netCaps(drvr_pin, rf, dcalc_ap, multi_drvr, + netCaps(drvr_pin, rf, scene, min_max, multi_drvr, pin_cap, wire_cap, fanout, has_net_load); } void GraphDelayCalc::netCaps(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, // Return values. float &pin_cap, @@ -1378,13 +1474,12 @@ GraphDelayCalc::netCaps(const Pin *drvr_pin, bool &has_net_load) const { if (multi_drvr) - multi_drvr->netCaps(rf, dcalc_ap, + multi_drvr->netCaps(rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); else { - const Corner *corner = dcalc_ap->corner(); - const MinMax *min_max = dcalc_ap->constraintMinMax(); + const Sdc *sdc = scene->sdc(); // Find pin and external pin/wire capacitance. - sdc_->connectedCap(drvr_pin, rf, corner, min_max, + sdc->connectedCap(drvr_pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); } } @@ -1392,12 +1487,12 @@ GraphDelayCalc::netCaps(const Pin *drvr_pin, void GraphDelayCalc::initSlew(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 DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (!vertex->slewAnnotated(rf, slew_min_max)) { - DcalcAPIndex ap_index = dcalc_ap->index(); - graph_->setSlew(vertex, rf, ap_index, slew_min_max->initValue()); + if (!vertex->slewAnnotated(rf, min_max)) + graph_->setSlew(vertex, rf, ap_index, min_max->initValue()); } } } @@ -1407,26 +1502,25 @@ void GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex, const RiseFall *rf) { - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - // Init drvr slew. - if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) { - DcalcAPIndex ap_index = dcalc_ap->index(); - graph_->setSlew(drvr_vertex, rf, ap_index, slew_min_max->initValue()); - } + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + // Init drvr slew. + if (!drvr_vertex->slewAnnotated(rf, min_max)) + graph_->setSlew(drvr_vertex, rf, ap_index, min_max->initValue()); - // Init wire delays and slews. - VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *wire_edge = edge_iter.next(); - if (wire_edge->isWire()) { - Vertex *load_vertex = wire_edge->to(graph_); - if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) - graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0); - // Init load vertex slew. - if (!load_vertex->slewAnnotated(rf, slew_min_max)) - graph_->setSlew(load_vertex, rf, ap_index, 0.0); + // Init wire delays and slews. + VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *wire_edge = edge_iter.next(); + if (wire_edge->isWire()) { + Vertex *load_vertex = wire_edge->to(graph_); + if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) + graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0); + // Init load vertex slew. + if (!load_vertex->slewAnnotated(rf, min_max)) + graph_->setSlew(load_vertex, rf, ap_index, 0.0); + } } } } @@ -1439,10 +1533,11 @@ GraphDelayCalc::initWireDelays(Vertex *drvr_vertex) while (edge_iter.hasNext()) { Edge *wire_edge = edge_iter.next(); if (wire_edge->isWire()) { - for (const DcalcAnalysisPt * dcalc_ap : corners_->dcalcAnalysisPts()) { - const MinMax *delay_min_max = dcalc_ap->delayMinMax(); - Delay delay_init_value(delay_min_max->initValue()); - DcalcAPIndex ap_index = dcalc_ap->index(); + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + + Delay delay_init_value(min_max->initValue()); for (const RiseFall *rf : RiseFall::range()) { if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) graph_->setWireArcDelay(wire_edge, rf, ap_index, delay_init_value); @@ -1450,15 +1545,17 @@ GraphDelayCalc::initWireDelays(Vertex *drvr_vertex) } } } + } } Slew GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex, const RiseFall *from_rf, const Edge *edge, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - return edgeFromSlew(from_vertex, from_rf, edge->role(), dcalc_ap); + return edgeFromSlew(from_vertex, from_rf, edge->role(), scene, min_max); } // Use clock slew for register/latch clk->q edges. @@ -1466,15 +1563,17 @@ Slew GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex, const RiseFall *from_rf, const TimingRole *role, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - + const ClkNetwork *clk_network = scene->mode()->clkNetwork(); if (role->genericRole() == TimingRole::regClkToQ() - && clk_network_->isIdealClock(from_vertex->pin())) - return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, - dcalc_ap->slewMinMax()); - else - return graph_->slew(from_vertex, from_rf, dcalc_ap->index()); + && clk_network->isIdealClock(from_vertex)) + return clk_network->idealClkSlew(from_vertex->pin(), from_rf, min_max); + else { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + return graph_->slew(from_vertex, from_rf, ap_index); + } } void @@ -1499,32 +1598,34 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, const Pin *related_out_pin = 0; if (related_out_port) related_out_pin = network_->findPin(inst, related_out_port); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); + + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, - dcalc_ap); - int slew_index = dcalc_ap->checkDataSlewIndex(); - const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index); + 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) corner:%s/%s", + " %s %s -> %s %s (%s) scene:%s/%s", arc_set->from()->name(), arc->fromEdge()->to_string().c_str(), arc_set->to()->name(), arc->toEdge()->to_string().c_str(), arc_set->role()->to_string().c_str(), - dcalc_ap->corner()->name(), - dcalc_ap->delayMinMax()->to_string().c_str()); + scene->name().c_str(), + min_max->to_string().c_str()); debugPrint(debug_, "delay_calc", 3, " from_slew = %s to_slew = %s", delayAsString(from_slew, this), delayAsString(to_slew, this)); float related_out_cap = 0.0; if (related_out_pin) - related_out_cap = loadCap(related_out_pin, to_rf,dcalc_ap,arc_delay_calc); + related_out_cap = loadCap(related_out_pin, to_rf,scene,min_max, + arc_delay_calc); ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew, to_slew, related_out_cap, - dcalc_ap); + scene, min_max); debugPrint(debug_, "delay_calc", 3, " check_delay = %s", delayAsString(check_delay, this)); @@ -1535,6 +1636,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, } } } + } if (delay_changed && observer_) observer_->checkDelayChangedTo(to_vertex); @@ -1544,13 +1646,16 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, Slew GraphDelayCalc::checkEdgeClkSlew(const Vertex *from_vertex, const RiseFall *from_rf, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - if (clk_network_->isIdealClock(from_vertex->pin())) - return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, - dcalc_ap->checkClkSlewMinMax()); + const ClkNetwork *clk_network = scene->mode()->clkNetwork(); + if (clk_network->isIdealClock(from_vertex)) + return clk_network->idealClkSlew(from_vertex->pin(), from_rf, + scene->checkClkSlewMinMax(min_max)); else - return graph_->slew(from_vertex, from_rf, dcalc_ap->checkClkSlewIndex()); + return graph_->slew(from_vertex, from_rf, + scene->checkClkSlewIndex(min_max)); } //////////////////////////////////////////////////////////////// @@ -1558,7 +1663,7 @@ GraphDelayCalc::checkEdgeClkSlew(const Vertex *from_vertex, string GraphDelayCalc::reportDelayCalc(const Edge *edge, const TimingArc *arc, - const Corner *corner, + const Scene *scene, const MinMax *min_max, int digits) { @@ -1569,7 +1674,6 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, const Instance *inst = network_->instance(to_pin); const TimingArcSet *arc_set = edge->timingArcSet(); string result; - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); const RiseFall *to_rf = arc->toEdge()->asRiseFall(); if (from_rf && to_rf) { @@ -1579,27 +1683,28 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, related_out_pin = network_->findPin(inst, related_out_port); float related_out_cap = 0.0; if (related_out_pin) - related_out_cap = loadCap(related_out_pin, to_rf, dcalc_ap, arc_delay_calc_); + related_out_cap = loadCap(related_out_pin, to_rf, scene, min_max, arc_delay_calc_); if (role->isTimingCheck()) { - const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, dcalc_ap); - int slew_index = dcalc_ap->checkDataSlewIndex(); + 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); - bool from_ideal_clk = clk_network_->isIdealClock(from_vertex->pin()); + 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; result = arc_delay_calc_->reportCheckDelay(to_pin, arc, from_slew, from_slew_annotation, to_slew, - related_out_cap, dcalc_ap, digits); + related_out_cap, scene, min_max, digits); } else { - const Slew &from_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); + const Slew &from_slew = edgeFromSlew(from_vertex, from_rf, edge, scene, min_max); const Parasitic *to_parasitic; float load_cap; - parasiticLoad(to_pin, to_rf, dcalc_ap, nullptr, arc_delay_calc_, + parasiticLoad(to_pin, to_rf, scene, min_max, nullptr, arc_delay_calc_, load_cap, to_parasitic); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(to_vertex); result = arc_delay_calc_->reportGateDelay(to_pin, arc, from_slew, load_cap, to_parasitic, load_pin_index_map, - dcalc_ap, digits); + scene, min_max, digits); } arc_delay_calc_->finishDrvrPin(); } @@ -1610,19 +1715,18 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, void GraphDelayCalc::minPeriod(const Pin *pin, - const Corner *corner, + const Scene *scene, // Return values. float &min_period, bool &exists) { exists = false; const MinMax *min_max = MinMax::max(); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + const DcalcAPIndex dcalc_ap_index = scene->dcalcAnalysisPtIndex(min_max); // Sdf annotation. float min_period1 = 0.0; bool exists1 = false; - graph_->periodCheckAnnotation(pin, dcalc_ap->index(), - min_period, exists); + graph_->periodCheckAnnotation(pin, dcalc_ap_index, min_period, exists); if (exists1 && (!exists || min_period1 < min_period)) { min_period = min_period1; @@ -1636,7 +1740,7 @@ GraphDelayCalc::minPeriod(const Pin *pin, graph_->minPeriodArc(vertex, RiseFall::rise(), edge, arc); if (edge) { exists = true; - min_period = delayAsFloat(graph_->arcDelay(edge, arc, dcalc_ap->index())); + min_period = delayAsFloat(graph_->arcDelay(edge, arc, dcalc_ap_index)); } } if (!exists) { @@ -1644,9 +1748,19 @@ GraphDelayCalc::minPeriod(const Pin *pin, LibertyPort *port = network_->libertyPort(pin); if (port) { Instance *inst = network_->instance(pin); - OperatingConditions *op_cond = sdc_->operatingConditions(min_max); - const Pvt *pvt = inst ? sdc_->pvt(inst, min_max) : nullptr; - port->minPeriod(op_cond, pvt, min_period, exists); + for (const Mode *mode : modes_) { + const Sdc *sdc = mode->sdc(); + OperatingConditions *op_cond = sdc->operatingConditions(min_max); + const Pvt *pvt = inst ? sdc->pvt(inst, min_max) : nullptr; + float min_period1 = 0.0; + bool exists1 = false; + port->minPeriod(op_cond, pvt, min_period1, exists1); + if (exists1 + && (!exists || min_period1 < min_period)) { + min_period = min_period1; + exists = true; + } + } } } } @@ -1660,14 +1774,15 @@ MultiDrvrNet::MultiDrvrNet() : void MultiDrvrNet::netCaps(const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, float &fanout, bool &has_net_load) const { - int index = dcalc_ap->index() * RiseFall::index_count + int index = scene->dcalcAnalysisPtIndex(min_max) * RiseFall::index_count + drvr_rf->index(); const NetCaps &net_caps = net_caps_[index]; pin_cap = net_caps.pinCap(); @@ -1677,16 +1792,15 @@ MultiDrvrNet::netCaps(const RiseFall *drvr_rf, } void -MultiDrvrNet::findCaps(const Sdc *sdc) +MultiDrvrNet::findCaps(const StaState *sta) { - Corners *corners = sdc->corners(); - int count = RiseFall::index_count * corners->dcalcAnalysisPtCount(); + int count = RiseFall::index_count * sta->dcalcAnalysisPtCount(); net_caps_.resize(count); const Pin *drvr_pin = dcalc_drvr_->pin(); - for (const DcalcAnalysisPt *dcalc_ap : corners->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); - const Corner *corner = dcalc_ap->corner(); - const MinMax *min_max = dcalc_ap->constraintMinMax(); + for (Scene *scene : sta->scenes()) { + const Sdc *sdc = scene->sdc(); + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); for (const RiseFall *drvr_rf : RiseFall::range()) { int drvr_rf_index = drvr_rf->index(); int index = ap_index * RiseFall::index_count + drvr_rf_index; @@ -1694,11 +1808,12 @@ MultiDrvrNet::findCaps(const Sdc *sdc) float pin_cap, wire_cap, fanout; bool has_net_load; // Find pin and external pin/wire capacitance. - sdc->connectedCap(drvr_pin, drvr_rf, corner, min_max, + sdc->connectedCap(drvr_pin, drvr_rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); net_caps.init(pin_cap, wire_cap, fanout, has_net_load); } } + } } void diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 334366627..948e277f2 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -35,7 +35,6 @@ #include "Network.hh" #include "Sdc.hh" #include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" #include "Variables.hh" @@ -63,36 +62,38 @@ LumpedCapDelayCalc::copy() Parasitic * LumpedCapDelayCalc::findParasitic(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { Parasitic *parasitic = nullptr; - const Corner *corner = dcalc_ap->corner(); - // set_load net has precedence over parasitics. - if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + Parasitics *parasitics = scene->parasitics(min_max); + const Sdc *sdc = scene->sdc(); + if (parasitics == nullptr + // set_load net has precedence over parasitics. + || sdc->drvrPinHasWireCap(drvr_pin) || network_->direction(drvr_pin)->isInternal()) - return nullptr; - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + return nullptr; + // Prefer PiElmore. - parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap); + parasitic = parasitics->findPiElmore(drvr_pin, rf, min_max); if (parasitic) return parasitic; - Parasitic *parasitic_network = parasitics_->findParasiticNetwork(drvr_pin, - parasitic_ap); + Parasitic *parasitic_network = parasitics->findParasiticNetwork(drvr_pin); if (parasitic_network) { - parasitic = reduceParasitic(parasitic_network, drvr_pin, rf, dcalc_ap); + parasitic = reduceParasitic(parasitic_network, drvr_pin, rf, scene, min_max); if (parasitic) return parasitic; } - const MinMax *min_max = dcalc_ap->constraintMinMax(); - Wireload *wireload = sdc_->wireload(min_max); + + Wireload *wireload = sdc->wireload(min_max); if (wireload) { float pin_cap, wire_cap, fanout; bool has_net_load; - graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, + graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); - parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload, fanout, - pin_cap, corner, min_max); + parasitic = parasitics->estimatePiElmore(drvr_pin, rf, wireload, fanout, + pin_cap, scene, min_max); } return parasitic; } @@ -101,14 +102,13 @@ Parasitic * LumpedCapDelayCalc::reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - const Corner *corner = dcalc_ap->corner(); - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); - return parasitics_->reduceToPiElmore(parasitic_network, drvr_pin, rf, - corner, dcalc_ap->constraintMinMax(), - parasitic_ap); + Parasitics *parasitics = scene->parasitics(min_max); + return parasitics->reduceToPiElmore(parasitic_network, drvr_pin, rf, + scene, min_max); } ArcDcalcResult @@ -117,7 +117,8 @@ LumpedCapDelayCalc::inputPortDelay(const Pin *, const RiseFall *rf, const Parasitic *, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { const LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); return makeResult(drvr_library,rf, 0.0, in_slew, load_pin_index_map); @@ -126,13 +127,14 @@ LumpedCapDelayCalc::inputPortDelay(const Pin *, ArcDcalcResult LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *, + const Slew &in_slew, + float load_cap, + const Parasitic *, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - GateTimingModel *model = arc->gateModel(dcalc_ap); + GateTimingModel *model = arc->gateModel(scene, min_max); debugPrint(debug_, "delay_calc", 3, " in_slew = %s load_cap = %s lumped", delayAsString(in_slew, this), @@ -146,7 +148,7 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, // NaNs cause seg faults during table lookup. if (isnan(load_cap) || isnan(delayAsFloat(in_slew))) report_->error(1350, "gate delay input variable is NaN"); - model->gateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap, + 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); @@ -182,14 +184,15 @@ LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin, float load_cap, const Parasitic *, const LoadPinIndexMap &, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) { - GateTimingModel *model = arc->gateModel(dcalc_ap); + GateTimingModel *model = arc->gateModel(scene, min_max); if (model) { float in_slew1 = delayAsFloat(in_slew); - return model->reportGateDelay(pinPvt(check_pin, dcalc_ap), in_slew1, load_cap, - false, digits); + return model->reportGateDelay(pinPvt(check_pin, scene, min_max), + in_slew1, load_cap, false, digits); } return ""; } diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index 5170d7a56..5cf829bd8 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -38,32 +38,37 @@ public: const char *name() const override { return "lumped_cap"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; bool reduceSupported() const override { return true; } Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + 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 DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; protected: diff --git a/dcalc/NetCaps.cc b/dcalc/NetCaps.cc index 5aa200ed5..0500b57dc 100644 --- a/dcalc/NetCaps.cc +++ b/dcalc/NetCaps.cc @@ -31,9 +31,9 @@ NetCaps::NetCaps() } NetCaps::NetCaps(float pin_cap, - float wire_cap, - float fanout, - bool has_net_load) : + float wire_cap, + float fanout, + bool has_net_load) : pin_cap_(pin_cap), wire_cap_(wire_cap), fanout_(fanout), @@ -43,9 +43,9 @@ NetCaps::NetCaps(float pin_cap, void NetCaps::init(float pin_cap, - float wire_cap, - float fanout, - bool has_net_load) + float wire_cap, + float fanout, + bool has_net_load) { pin_cap_ = pin_cap; wire_cap_ = wire_cap; diff --git a/dcalc/NetCaps.hh b/dcalc/NetCaps.hh index 5679d7aed..aeb1d9ff8 100644 --- a/dcalc/NetCaps.hh +++ b/dcalc/NetCaps.hh @@ -32,13 +32,13 @@ class NetCaps public: NetCaps(); NetCaps(float pin_cap, - float wire_cap, - float fanout, - bool has_net_load); + float wire_cap, + float fanout, + bool has_net_load); void init(float pin_cap, - float wire_cap, - float fanout, - bool has_net_load); + float wire_cap, + float fanout, + bool has_net_load); float pinCap() const { return pin_cap_; } float wireCap() const{ return wire_cap_; } float fanout() const{ return fanout_; } diff --git a/dcalc/ParallelDelayCalc.cc b/dcalc/ParallelDelayCalc.cc index 8bb5b18c3..f18c785d1 100644 --- a/dcalc/ParallelDelayCalc.cc +++ b/dcalc/ParallelDelayCalc.cc @@ -25,7 +25,7 @@ #include "ParallelDelayCalc.hh" #include "TimingArc.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Network.hh" #include "Graph.hh" #include "Sdc.hh" @@ -44,25 +44,28 @@ ParallelDelayCalc::ParallelDelayCalc(StaState *sta): ArcDcalcResultSeq ParallelDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { if (dcalc_args.size() == 1) { ArcDcalcArg &dcalc_arg = dcalc_args[0]; ArcDcalcResult dcalc_result = gateDelay(dcalc_arg.drvrPin(), dcalc_arg.arc(), dcalc_arg.inSlew(), dcalc_arg.loadCap(), dcalc_arg.parasitic(), - load_pin_index_map, dcalc_ap); + load_pin_index_map, + scene, min_max); ArcDcalcResultSeq dcalc_results; dcalc_results.push_back(dcalc_result); return dcalc_results; } - return gateDelaysParallel(dcalc_args, load_pin_index_map, dcalc_ap); + return gateDelaysParallel(dcalc_args, load_pin_index_map, scene, min_max); } ArcDcalcResultSeq ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { size_t drvr_count = dcalc_args.size(); ArcDcalcResultSeq dcalc_results(drvr_count); @@ -75,16 +78,16 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx]; const Pin *drvr_pin = dcalc_arg.drvrPin(); const TimingArc *arc = dcalc_arg.arc(); - Slew in_slew = dcalc_arg.inSlew(); + const Slew &in_slew = dcalc_arg.inSlew(); ArcDcalcResult intrinsic_result = gateDelay(drvr_pin, arc, in_slew, 0.0, nullptr, - load_pin_index_map, dcalc_ap); + load_pin_index_map, scene, min_max); ArcDelay intrinsic_delay = intrinsic_result.gateDelay(); intrinsic_delays[drvr_idx] = intrinsic_result.gateDelay(); ArcDcalcResult gate_result = gateDelay(drvr_pin, arc, in_slew, dcalc_arg.loadCap(), dcalc_arg.parasitic(), - load_pin_index_map, dcalc_ap); + 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; diff --git a/dcalc/ParallelDelayCalc.hh b/dcalc/ParallelDelayCalc.hh index 40c8bc140..45f868147 100644 --- a/dcalc/ParallelDelayCalc.hh +++ b/dcalc/ParallelDelayCalc.hh @@ -38,11 +38,13 @@ public: ParallelDelayCalc(StaState *sta); ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; protected: ArcDcalcResultSeq gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); }; } // namespace diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index bce55d364..6379b85ef 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -33,8 +33,7 @@ #include "PortDirection.hh" #include "Network.hh" #include "Sdc.hh" -#include "DcalcAnalysisPt.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Graph.hh" #include "Parasitics.hh" #include "GraphDelayCalc.hh" @@ -64,9 +63,12 @@ makePrimaDelayCalc(StaState *sta) PrimaDelayCalc::PrimaDelayCalc(StaState *sta) : DelayCalcBase(sta), dcalc_args_(nullptr), + scene_(nullptr), + min_max_(nullptr), + parasitics_(nullptr), + parasitic_network_(nullptr), load_pin_index_map_(nullptr), pin_node_map_(network_), - node_index_map_(ParasiticNodeLess(parasitics_, network_)), prima_order_(3), make_waveforms_(false), waveform_drvr_pin_(nullptr), @@ -81,7 +83,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) : dcalc_args_(nullptr), load_pin_index_map_(nullptr), pin_node_map_(network_), - node_index_map_(ParasiticNodeLess(parasitics_, network_)), + node_index_map_(dcalc.node_index_map_), prima_order_(dcalc.prima_order_), make_waveforms_(false), waveform_drvr_pin_(nullptr), @@ -113,27 +115,26 @@ PrimaDelayCalc::copyState(const StaState *sta) Parasitic * PrimaDelayCalc::findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - const Corner *corner = dcalc_ap->corner(); - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); - // set_load net has precidence over parasitics. - if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + const Sdc *sdc = scene->sdc(); + Parasitics *parasitics = scene->parasitics(min_max); + if (parasitics == nullptr + // set_load net has precedence over parasitics. || network_->direction(drvr_pin)->isInternal()) return nullptr; - Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + Parasitic *parasitic = parasitics->findParasiticNetwork(drvr_pin); if (parasitic) return parasitic; - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - Wireload *wireload = sdc_->wireload(cnst_min_max); + Wireload *wireload = sdc->wireload(min_max); if (wireload) { float pin_cap, wire_cap, fanout; bool has_wire_cap; - graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, pin_cap, 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, cnst_min_max, - parasitic_ap); + parasitic = parasitics->makeWireloadNetwork(drvr_pin, wireload, + fanout, scene, min_max); } return parasitic; } @@ -142,7 +143,8 @@ Parasitic * PrimaDelayCalc::reduceParasitic(const Parasitic *, const Pin *, const RiseFall *, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { return nullptr; } @@ -153,18 +155,16 @@ PrimaDelayCalc::inputPortDelay(const Pin *drvr_pin, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { + Parasitics *parasitics = scene->parasitics(min_max); ArcDcalcResult dcalc_result(load_pin_index_map.size()); LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); - const Parasitic *pi_elmore = nullptr; - if (parasitic && parasitics_->isParasiticNetwork(parasitic)) { - const ParasiticAnalysisPt *ap = dcalc_ap->parasiticAnalysisPt(); - pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin, rf, - dcalc_ap->corner(), - dcalc_ap->constraintMinMax(), ap); - } + if (parasitic && parasitics->isParasiticNetwork(parasitic)) + 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; @@ -174,7 +174,7 @@ PrimaDelayCalc::inputPortDelay(const Pin *drvr_pin, bool elmore_exists = false; float elmore = 0.0; if (pi_elmore) - parasitics_->findElmore(pi_elmore, load_pin, elmore, elmore_exists); + parasitics->findElmore(pi_elmore, load_pin, elmore, elmore_exists); if (elmore_exists) // Input port with no external driver. dspfWireDelaySlew(load_pin, rf, in_slew, elmore, wire_delay, load_slew); @@ -192,33 +192,37 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + 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, dcalc_ap); + ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, scene, min_max); return dcalc_results[0]; } ArcDcalcResultSeq PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { dcalc_args_ = &dcalc_args; load_pin_index_map_ = &load_pin_index_map; drvr_count_ = dcalc_args.size(); - dcalc_ap_ = dcalc_ap; + scene_ = scene; + min_max_ = min_max; drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall(); parasitic_network_ = dcalc_args[0].parasitic(); load_cap_ = dcalc_args[0].loadCap(); + parasitics_ = scene->parasitics(min_max); + node_index_map_ = NodeIndexMap(ParasiticNodeLess(parasitics_, network_)); bool failed = false; output_waveforms_.resize(drvr_count_); - const DcalcAnalysisPtSeq &dcalc_aps = corners_->dcalcAnalysisPts(); for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; - GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(dcalc_ap); + GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(scene, min_max); if (table_model && dcalc_arg.parasitic()) { OutputWaveforms *output_waveforms = table_model->outputWaveforms(); float in_slew = dcalc_arg.inSlewFlt(); @@ -236,7 +240,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); if (!vdd_exists) report_->error(1720, "VDD not defined in library %s", drvr_library->name()); - drvr_cell->ensureVoltageWaveforms(dcalc_aps); + drvr_cell->ensureVoltageWaveforms(scenes_); if (drvr_idx == 0) { vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; @@ -266,11 +270,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, dcalc_ap_); + 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_, dcalc_ap_); + return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, + scene_, min_max_); } void @@ -388,8 +394,7 @@ PrimaDelayCalc::driverResistance() { const Pin *drvr_pin = (*dcalc_args_)[0].drvrPin(); LibertyPort *drvr_port = network_->libertyPort(drvr_pin); - const MinMax *min_max = dcalc_ap_->delayMinMax(); - return drvr_port->driveResistance(drvr_rf_, min_max); + return drvr_port->driveResistance(drvr_rf_, min_max_); } void @@ -458,17 +463,16 @@ PrimaDelayCalc::pinCapacitance(ParasiticNode *node) { const Pin *pin = parasitics_->pin(node); float pin_cap = 0.0; + const Sdc *sdc = scene_->sdc(); if (pin) { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(port); - const Corner *corner = dcalc_ap_->corner(); - const MinMax *cnst_min_max = dcalc_ap_->constraintMinMax(); if (lib_port) { if (!includes_pin_caps_) - pin_cap = sdc_->pinCapacitance(pin, drvr_rf_, corner, cnst_min_max); + pin_cap = sdc->pinCapacitance(pin, drvr_rf_, scene_, min_max_); } else if (network_->isTopLevelPort(pin)) - pin_cap = sdc_->portExtCap(port, drvr_rf_, corner, cnst_min_max); + pin_cap = sdc->portExtCap(port, drvr_rf_, min_max_); } return pin_cap; } @@ -910,14 +914,15 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, float load_cap, const Parasitic *, const LoadPinIndexMap &, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) { - GateTimingModel *model = arc->gateModel(dcalc_ap); + GateTimingModel *model = arc->gateModel(scene, min_max); if (model) { float in_slew1 = delayAsFloat(in_slew); - return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap, - false, digits); + return model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), + in_slew1, load_cap, false, digits); } return ""; } diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index 454fdc987..20e84c241 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -29,7 +29,6 @@ #include #include -#include "Map.hh" #include "LumpedCapDelayCalc.hh" #include "ArcDcalcWaveforms.hh" #include "Parasitics.hh" @@ -38,16 +37,16 @@ namespace sta { class ArcDelayCalc; class StaState; -class Corner; +class Scene; -typedef Map PinNodeMap; -typedef std::map NodeIndexMap; -typedef Map PortIndexMap; -typedef Eigen::SparseMatrix MatrixSd; -typedef Map PinLMap; -typedef std::map WatchPinValuesMap; +using PinNodeMap = std::map; +using NodeIndexMap = std::map; +using PortIndexMap = std::map; +using MatrixSd = Eigen::SparseMatrix; +using PinLMap = std::map; +using WatchPinValuesMap = std::map; -typedef Table1 Waveform; +using Waveform = Table1; ArcDelayCalc * makePrimaDelayCalc(StaState *sta); @@ -65,35 +64,41 @@ public: void setPrimaReduceOrder(size_t order); Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; bool reduceSupported() const override { return false; } Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult inputPortDelay(const Pin *drvr_pin, float in_slew, const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; // Record waveform for drvr/load pin. @@ -147,7 +152,7 @@ protected: const Pin *drvr_pin, const RiseFall *drvr_rf, const Pin *load_pin, - const Corner *corner, + const Scene *scene, const MinMax *min_max); void primaReduce(); void primaReduce2(); @@ -168,7 +173,9 @@ protected: ArcDcalcArgSeq *dcalc_args_; size_t drvr_count_; float load_cap_; - const DcalcAnalysisPt *dcalc_ap_; + const Scene *scene_; + const MinMax *min_max_; + Parasitics *parasitics_; const Parasitic *parasitic_network_; const RiseFall *drvr_rf_; const LoadPinIndexMap *load_pin_index_map_; diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc index 63f88a4b3..c32197fb7 100644 --- a/dcalc/UnitDelayCalc.cc +++ b/dcalc/UnitDelayCalc.cc @@ -49,8 +49,9 @@ UnitDelayCalc::copy() Parasitic * UnitDelayCalc::findParasitic(const Pin *, - const RiseFall *, - const DcalcAnalysisPt *) + const RiseFall *, + const Scene *, + const MinMax *) { return nullptr; } @@ -59,7 +60,8 @@ Parasitic * UnitDelayCalc::reduceParasitic(const Parasitic *, const Pin *, const RiseFall *, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { return nullptr; } @@ -67,30 +69,33 @@ UnitDelayCalc::reduceParasitic(const Parasitic *, void UnitDelayCalc::reduceParasitic(const Parasitic *, const Net *, - const Corner *, + const Scene *, const MinMaxAll *) { } void UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArg &, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { } void UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArgSeq &, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { } ArcDcalcResult UnitDelayCalc::inputPortDelay(const Pin *, - float, - const RiseFall *, - const Parasitic *, + float, + const RiseFall *, + const Parasitic *, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { return unitDelayResult(load_pin_index_map); } @@ -98,11 +103,12 @@ UnitDelayCalc::inputPortDelay(const Pin *, ArcDcalcResult UnitDelayCalc::gateDelay(const Pin *, const TimingArc *, - const Slew &, - float, - const Parasitic *, + const Slew &, + float, + const Parasitic *, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { return unitDelayResult(load_pin_index_map); } @@ -110,7 +116,8 @@ UnitDelayCalc::gateDelay(const Pin *, ArcDcalcResultSeq UnitDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *) + const Scene *, + const MinMax *) { size_t drvr_count = dcalc_args.size(); ArcDcalcResultSeq dcalc_results(drvr_count); @@ -138,12 +145,13 @@ UnitDelayCalc::unitDelayResult(const LoadPinIndexMap &load_pin_index_map) string UnitDelayCalc::reportGateDelay(const Pin *, const TimingArc *, - const Slew &, - float, - const Parasitic *, + const Slew &, + float, + const Parasitic *, const LoadPinIndexMap &, - const DcalcAnalysisPt *, - int) + const Scene *, + const MinMax *, + int) { string result("Delay = 1.0\n"); result += "Slew = 0.0\n"; @@ -153,10 +161,11 @@ UnitDelayCalc::reportGateDelay(const Pin *, ArcDelay UnitDelayCalc::checkDelay(const Pin *, const TimingArc *, - const Slew &, - const Slew &, - float, - const DcalcAnalysisPt *) + const Slew &, + const Slew &, + float, + const Scene *, + const MinMax *) { return units_->timeUnit()->scale(); } @@ -164,12 +173,13 @@ UnitDelayCalc::checkDelay(const Pin *, string UnitDelayCalc::reportCheckDelay(const Pin *, const TimingArc *, - const Slew &, - const char *, - const Slew &, - float, - const DcalcAnalysisPt *, - int) + const Slew &, + const char *, + const Slew &, + float, + const Scene *, + const MinMax *, + int) { return "Check = 1.0\n"; } diff --git a/dcalc/UnitDelayCalc.hh b/dcalc/UnitDelayCalc.hh index b0491c0b9..094626cd1 100644 --- a/dcalc/UnitDelayCalc.hh +++ b/dcalc/UnitDelayCalc.hh @@ -37,26 +37,31 @@ public: const char *name() const override { return "unit"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; bool reduceSupported() const override { return false; } Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; void reduceParasitic(const Parasitic *parasitic_network, const Net *net, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max) override; void setDcalcArgParasiticSlew(ArcDcalcArg &gate, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + 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 DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -64,23 +69,27 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; ArcDelay checkDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap) override; + const Scene *scene, + const MinMax *min_max) override; std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; std::string reportCheckDelay(const Pin *check_pin, const TimingArc *arc, @@ -88,7 +97,8 @@ public: const char *from_slew_annotation, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) override; void finishDrvrPin() override; diff --git a/doc/ApiChanges.txt b/doc/ApiChanges.txt index d2ad43f88..553db6b3f 100644 --- a/doc/ApiChanges.txt +++ b/doc/ApiChanges.txt @@ -24,6 +24,45 @@ This file summarizes STA API changes for each release. +Release 3.0.0 2025/11/26 +------------------------ + +OpenSTA now requires c++ 20. + +Corner replaced by Scene + mode() + parasitics(min_max) +DcalcAnalysisPt replaced by scene/min_min +DcalcAnalysisPt replaced by scene/min_min +PathAnalysisPt replaced by scene/min_min +StaState::sdc_ moved to Mode +StaState::sim_ moved to Mode +StaState::clk_network__ moved to Mode +StaState::parasitics_ moved to Scene + +Sta::findPathEnds group_paths arg has been changed from PathGroupNameSet* +to StdStringSeq&. + +Sta::isClock has been removed. Use mode->clkNetwork()->isClock instead. + +Sta::vertexSlew renamed to slew +Sta::vertexSlack renamed to slack +Sta::vertexSlacks renamed to slacks +Sta::vertexArrival renamed to arrival +Sta::vertexRequired renamed to required +Sta::pinSlack renamed to slack +Sta::pinArrival renamed to arrival + +FuncExpr::Operator::op_* renamed to FuncExpr::Op::* +FuncExprPortIterator has been removed. Use FuncExpr::ports(). + +Sdc::clocks() now returns ClockSeq&. +Sdc::clks() has been removed. + +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. + Release 2.6.2 2025/03/30 ------------------------ diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index 932339f8c..9c8c82209 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -3,6 +3,180 @@ OpenSTA Timing Analyzer Release Notes This file summarizes user visible changes for each release. +Release 3.0.0 2025/11/26 +------------------------ + +This release adds multi-corner multi-mode (mcmm) support. The SDC +constraints in each mode describe a different operating mode, such as +mission mode or scan mode. + +A "scene" is the combination of a mode and corner. Each scene can have +separate min/max liberty and spef files. + +THe basic structure of a multi-corner/multi-mode command file is + read_liberty + read_verilog + link_design + read_sdc -mode... or set_mode followed by sdc commands + read_spef -name... + define_scene... + report_checks [-scenes] + +This is an example script with 2 corners, 2 modes and 3 scenes. + +read_liberty bc.lib +read_liberty wc.lib + +read_verilog design.v +link_design top + +read_sdc -mode run design.sdc +read_sdc -mode scan design_scan.sdc + +read_spef -name bc bc.spef +read_spef -name wc wc.spef + +define_scene bc \ + -mode run \ + -liberty bc \ + -spef bc +define_scene wc \ + -mode run \ + -liberty wc \ + -spef wc +define_scene scan \ + -mode scan \ + -liberty wc \ + -spef wc + +report_checks +report_checks -scenes bc +report_checks -scenes wc +report_checks -scenes scan + +................ + +Alternatively, the set_mode command can be used to define commands +for each mode at the command level instead of using SDC files. + +set_mode run +create_clock -period 10 clock +set_input_delay 0 -clock clock [all_inputs -no_clocks] +set_output_delay 0 -clock clock [all_outputs] + +set_mode scan +create_clock -period 100 scan_clock +set_input_delay 0 -clock scan_clock scan_in +set_output_delay 0 -clock scan_clock scan_out + +................ + +The define_corners command is supported for compatiblity but should +not be used with mcmm flows. Similarly, the -min/-max arguemnts to +read_liberty and read_spaf are supported for compabibility but should +not be used with mcmm flows. + +................ + +An initial mode and scene named "default" are defined for single mode, +single corner analysis. SDC commands defined interactively and read +with read_sdc without a -mode argument are defined in the "default" +mode. + +Use the set_mode command to define a mode or set the command +interpreter to add following commands to mode mode_name. + + set_mode mode_name + +If mode_name does not exist it is created. When modes are created the +default mode is deleted. + +The read_sdc command has a -mode argument to assign the commands in the file +to a mode. + + read_sdc [-mode mode_name] + +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 define_scene command defines a scene for a mode (SDC), liberty files +and spef parasitics. + + 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] + +Use get_scenes to find defined scenes. + + get_scenes [-modes mode_names] scene_name + +................ + +Use the read_spef -name argument to append multiple parasitics files +to annotate hierarchical blocks. Scene definitions use the spef_name +to specify which parasitices to use for each scene. + + read_spef -name spef_name + report_parasitic_annotation [-name spef_name] + +If -name is omitted the base name of the file name is used. + +The read_spef -corner/-min/-max arguments are supported for comppatibility +but will be removed in a future release. + +The read_spef -reduce options don't work because sdc, liberty ap isn't known + +................ + +The report_checks and report_check_typescommands support a -scenes +argument to report timing checks/paths from multiple scenes. + + report_checks -scenes + report_check_types -scenes + report_slews -scenes + report_clock_latency -scenes + +................ + +To annotate delays with SDF when there are multiple scenes, use +the -scene argument. + + read_sdf -scene + report_annotated_delay -scene + report_annotated_check -scene + +SDF annotation for mcmm analysis must follow the scene definitions. + +................ + +VCD annotation with read_vcd now supports a -mode arguement. + + read_vcd [-mode mode_name] + +................ + +The -corner args has been removed from the following commands because they are no +longer necessary. + set_load -corner + set_port_fanout_number -corner + +................ + +The report_pulse_width_checks command is no longer supported. Use +report_check_types -min_pulse_width. + +................ + +Delay calculation slew values now propagate through set_case_analysis +and set_logic_zero, set_logic_one, set_logic_dc constraints. + +Power analysis now ignores set_case_analysis and set_logic_zero, +set_logic_one, set_logic_dc. + Release 2.7.0 2025/05/19 ------------------------- @@ -19,6 +193,9 @@ to remove paths through identical pins and rise/fall edges. Instances now have pins for verilog netlist power/ground connections, +Sta::findPathEnds group_paths arg has been changed from PathGroupNameSet* +to StdStringSeq&. + Release 2.6.1 2025/03/30 ------------------------- @@ -93,6 +270,8 @@ timing groups. report_clock_skew -include_internal_latency report_clock_latency -include_internal_latency +The report_clock_skew requires a -scene argument if multiple scenes are defined. + The all_inputs command now supports the -no_clocks argument to exclude clocks from the list. diff --git a/doc/OpenSTA.fodt b/doc/OpenSTA.fodt index f7a17c627..39de2a674 100644 --- a/doc/OpenSTA.fodt +++ b/doc/OpenSTA.fodt @@ -1,24 +1,24 @@ - Parallax STA documentationJames Cherry4832025-03-17T12:59:52.4638705382010-07-31T21:07:002025-11-04T12:25:14.489956000P117DT14H41M24SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse + Parallax STA documentationJames Cherry5112025-03-17T12:59:52.4638705382010-07-31T21:07:002025-12-25T18:12:25.212818000P122DT13H54M10SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse - 981699 + 2475563 0 - 30224 + 19290 17736 true false view2 - 16529 - 988826 + 3041 + 2477826 0 - 981699 - 30222 - 999434 + 2475563 + 19288 + 2493298 0 1 false @@ -89,7 +89,7 @@ false true false - 25757696 + 26291860 0 false @@ -198,7 +198,7 @@ - + @@ -364,7 +364,7 @@ - + @@ -452,6 +452,7 @@ + @@ -534,6 +535,12 @@ + + + + + + @@ -1373,22 +1380,22 @@ - + - + - + - + - + - + @@ -1790,6 +1797,24 @@ + + + + + + + + + + + + + + + + + + @@ -1980,14 +2005,29 @@ - + + + + + + + + + + + + + + + + @@ -2348,24 +2388,6 @@ - - - - - - - - - - - - - - - - - - @@ -3149,6 +3171,21 @@ + + + + + + + + + + + + + + + @@ -3857,21 +3894,6 @@ - - - - - - - - - - - - - - - @@ -4116,1053 +4138,1149 @@ + + + + - + - + - + - + - + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + - + + + + + + + + + + + + + + + - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - + - + - + + + + - + - + + + + + - + - + - + - + - + - - - - + + - + - + - + - + - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + - + - + - + + + + + - + - + - + - + - + - + - + - + + + + + + + + - + - + + + + + - + + + + + - + - + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - + - - - - - + - + - + - + - + - + - + - + - + + + + + - + - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -5181,1029 +5299,1056 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - - - - + - - - - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - + - + - + - + - + - + - + - + - + + + + - + @@ -6324,92 +6469,100 @@ Table of Contents - Command Line Arguments1 - Example Command Scripts1 - Timing Analysis using SDF2 - Timing Analysis with Multiple Process Corners2 - Power Analysis2 - TCL Interpreter3 - Debugging Timing4 - No paths found4 - No path reported an endpoint5 - Commands6 - Filter Expressions80 - Variables80 + Command Line Arguments1 + Example Command Scripts1 + Timing Analysis using SDF2 + Timing Analysis with Multiple Process Corners2 + Timing Analysis with Multiple Modes3 + Power Analysis3 + TCL Interpreter5 + Debugging Timing6 + No paths found6 + No path reported an endpoint7 + Commands7 + Filter Expressions84 + 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. - 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 - An example command script using three process corners and +/-10% min/max derating is shown below. - define_corners wc typ bcread_liberty -corner wc example1_slow.libread_liberty -corner typ example1_typ.libread_liberty -corner bc example1_fast.libread_verilog example1.vlink_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}report_checks -path_delay min_maxreport_checks -corner typ - This example can be found in examples/spef_parasitics.tcl. Other examples can be found in the examples directory. - 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. + 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 + 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 + 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 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. 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] [-corner corner] [-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] [-corner corner] [-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: + 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_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. + 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. + 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 - + @@ -6419,15 +6572,16 @@ - all_inputs + all_inputs - [-no_clocks] + [-no_clocks] + - -no_clocks + -no_clocks Exclude inputs defined as clock sources. @@ -6440,10 +6594,10 @@ - all_outputs + all_outputs - + @@ -6453,92 +6607,93 @@ - 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. @@ -6546,7 +6701,7 @@ - -unconstrained_endpoints + -unconstrained_endpoints Check path endpoints for timing constraints (timing check or set_output_delay). @@ -6554,7 +6709,7 @@ - -multiple_clock + -multiple_clock Check register/latch clock pins for multiple clocks. @@ -6562,7 +6717,7 @@ - -no_clock + -no_clock Check register/latch clock pins for a clock. @@ -6570,7 +6725,7 @@ - -no_input_delay + -no_input_delay Check for inputs that do not have a set_input_delay command. @@ -6578,7 +6733,7 @@ - -loops + -loops Check for combinational logic loops. @@ -6586,28 +6741,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. @@ -6615,7 +6770,7 @@ - port + port A port to connect to net. @@ -6623,29 +6778,29 @@ - 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. @@ -6653,7 +6808,7 @@ - -name clock_name + -name clock_name The name of the clock. @@ -6661,7 +6816,7 @@ - -waveform edge_list + -waveform edge_list A list of edge rise and fall time. @@ -6669,15 +6824,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. @@ -6685,29 +6840,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 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. @@ -6715,39 +6869,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). @@ -6755,7 +6909,7 @@ - -invert + -invert Invert the master clock. @@ -6763,15 +6917,15 @@ - -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. @@ -6779,29 +6933,29 @@ - -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 -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 -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. @@ -6815,10 +6969,10 @@ - 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 @@ -6828,75 +6982,90 @@ - current_design + current_design - [design] + [design] - + - current_instance + current_instance - [instance] + [instance] - - instance + instance - Not supported. + Not supported. - - - - - - - define_corners + + + + + + + define_scene - - corner1 [corner2]... + + -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 - - - corner + + + mode_name - - The name of a delay calculation corner. + + The SDC mode to use. + + + + + liberty_files + + + List of Liberty files to use. + + + + + spef_file + + + The SPEF parasitics file to use. - Use the define_corners command to define the names of multiple process/temperature/voltage corners. The define_corners command must follow set_operating_conditions -analysis_type and precede any reference to the corner names and can only appear once in a command file. There is no support for re-defining corners. - For analysis type single, each corner has one delay calculation result and early/late path arrivals. For analysis type best_case/worst_case and on_chip_variation, each corner has min/max delay calculation results and early/late path arrivals. + 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. + 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. @@ -6906,26 +7075,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. @@ -6935,18 +7104,19 @@ - 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. @@ -6956,18 +7126,18 @@ - delete_instance + delete_instance - instance + instance - instance + instance - Instance to delete. + Instance to delete. @@ -6975,47 +7145,46 @@ - - delete_net + delete_net - net + net - net + net - Net to delete. + Net to delete. - The network editing command delete_net removes a net from the design. + The network editing command delete_net removes a net from the design. - 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. @@ -7023,7 +7192,7 @@ - pin + pin A pin to connect to net. @@ -7031,10 +7200,10 @@ - -all + -all - Disconnect all pins from the net. + Disconnect all pins from the net. @@ -7044,10 +7213,10 @@ - elapsed_run_time + elapsed_run_time - + @@ -7058,104 +7227,104 @@ - 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][-corner corner][-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. + Return rising paths through a list of instances, pins or nets. - -fall_through through_list + -fall_through through_list - Return falling paths through a list of instances, pins or nets. + Return falling paths through a list of instances, pins or nets. - -to to_list + -to to_list - Return paths to a list of clocks, instances, ports or pins. + Return paths to a list of clocks, instances, ports or pins. - -rise_to to_list + -rise_to to_list - Return rising paths to a list of clocks, instances, ports or pins. + Return rising paths to a list of clocks, instances, ports or pins. - -fall_to to_list + -fall_to to_list - Return falling paths to a list of clocks, instances, ports or pins. + Return falling paths to a list of clocks, instances, ports or pins. - -unconstrained + -unconstrained - Report unconstrained paths also. + Report unconstrained paths also. + - -path_delay min + -path_delay min Return min path (hold) checks. - - -path_delay min_rise + -path_delay min_rise Return min path (hold) checks for rising endpoints. @@ -7163,7 +7332,7 @@ - -path_delay min_fall + -path_delay min_fall Return min path (hold) checks for falling endpoints. @@ -7171,7 +7340,7 @@ - -path_delay max + -path_delay max Return max path (setup) checks. @@ -7179,7 +7348,7 @@ - -path_delay max_rise + -path_delay max_rise Return max path (setup) checks for rising endpoints. @@ -7187,7 +7356,7 @@ - -path_delay max_fall + -path_delay max_fall Return max path (setup) checks for falling endpoints. @@ -7195,7 +7364,7 @@ - -path_delay min_max + -path_delay min_max Return max and max path (setup and hold) checks. @@ -7203,23 +7372,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. @@ -7227,7 +7396,7 @@ - -corner corner + -scene scene Return paths for one process corner. @@ -7235,109 +7404,109 @@ - -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. @@ -7350,47 +7519,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. @@ -7401,18 +7571,17 @@ - - 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. @@ -7420,15 +7589,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. @@ -7436,7 +7605,7 @@ - -startpoints_only + -startpoints_only Only return pins that are startpoints. @@ -7444,7 +7613,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7452,7 +7621,7 @@ - -pin_levels pin_count + -pin_levels pin_count Only return pins within pin_count pin traversals. @@ -7460,44 +7629,45 @@ - -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. - The get_fanin command returns traverses the design from sink_list pins, ports or nets backwards and return the fanin pins or instances. + The get_fanin command returns traverses the design from sink_list pins, ports or nets backwards and return the fanin pins or instances. + - 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. @@ -7505,24 +7675,23 @@ - -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. - - -endpoints_only + -endpoints_only Only return pins that are endpoints. @@ -7530,7 +7699,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7538,7 +7707,7 @@ - -pin_levels pin_count + -pin_levels pin_count Only return pins within pin_count pin traversals. @@ -7546,114 +7715,114 @@ - -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. - The get_fanout command returns traverses the design from source_list pins, ports or nets backwards and return the fanout pins or instances. + The get_fanout command returns traverses the design from source_list pins, ports or nets backwards and return the fanout pins or instances. - 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. @@ -7666,63 +7835,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. @@ -7735,117 +7905,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. @@ -7853,97 +8023,98 @@ - patterns + patterns A list of net name patterns. - The get_nets command returns a list of all nets that match patterns. + The get_nets command returns a list of all nets that match patterns. - get_name + get_name - object + object - object + object A library, cell, port, instance, pin or timing arc object. - Return the name of object. Equivalent to [get_property object name]. + Return the name of object. Equivalent to [get_property object name]. + - 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. @@ -7951,63 +8122,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. @@ -8020,31 +8191,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. @@ -8052,89 +8223,119 @@ 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 + 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)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)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 + + + [-mode mode_name]scene_name + + + + + mode_name + + + Get the scenes for mode_name. + + + + + scene_name + + + A scene name pattern. + + + + 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. @@ -8142,7 +8343,7 @@ - -weight weight + -weight weight Not supported. @@ -8150,7 +8351,7 @@ - -critical_range range + -critical_range range Not supported. @@ -8158,216 +8359,215 @@ - -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. - The group_path command is used to group paths reported by the report_checks command. See set_false_path for a description of allowed from_list, through_list and to_list objects. + The group_path command is used to group paths reported by the report_checks command. See set_false_path for a description of allowed from_list, through_list and to_list objects. - 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. - - make_instance + make_instance - inst_pathlib_cell + inst_pathlib_cell - inst_path + inst_path A hierarchical instance name. + - 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. @@ -8380,48 +8580,48 @@ - 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: - 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 ; } }} + 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. @@ -8429,247 +8629,232 @@ - 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 - [-echo]filename + [-mode mode_name][-echo]filename + + + + + mode_name + + + 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. + 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. - - read_sdf + read_sdf - [-corner corner][-unescaped_dividers]filename + [-scene scene][-unescaped_dividers]filename - -corner corner + scene - Process corner 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. + 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. - 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 corners are defined -corner must be specified. + 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 - [-min][-max][-path path][-corner corner][-keep_capacitive_coupling][-coupling_reduction_factor factor][-reduce]filename - - - - - -min - - - Annotate parasitics for min delays. - - - - - -max - - - Annotate parasitics for max delays. - - - - - path - - - Hierarchical block instance path to annotate with parasitics. + [-name name][-keep_capacitive_coupling][-coupling_reduction_factor factor][-reduce][-path path]filename - + - -corner corner + name - Annotate parasitics for one process corner. + The name of the SPEF parasitics to use for defining scenes. The default is the base name of filename. - - + - ‑keep_capacitive_coupling + path - Keep coupling capacitors in parasitic networks rather than converting them to grounded capacitors. + Hierarchical block instance path to annotate with parasitics. - + - ‑coupling_reduction_factorfactor + ‑keep_capacitive_coupling - Factor to multiply coupling capacitance by when reducing parasitic networks. The default value is 1.0. + Keep coupling capacitors in parasitic networks rather than converting them to grounded capacitors. - + - -reduce + ‑coupling_reduction_factorfactor - Reduce detailed parasitics and do not save the detailed parastic network. + 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 parasitics can be annotated for corners and min and max paths using the -corner, –min and -max arguments. To use the same parastiics for every corner and for min/max delay calculation read the SPEF without -corner, -min, and -max options. - read_spef spef1 - To use separate parastics for min/max delay, use the -min, and -max options for each SPEF file. - read_spef -min spef1read_spef -max spef2 - To use separate parastics for each corner, use the -corner option for each SPEF file. - read_spef -corner ss spef1read_spef -corner tt spef2read_spef -corner ff spef3 - To use separate parastics for each corner and separate min/max delay calculation, use the -corner option along with the -min, and -max options. - read_spef -corner ss -min spef1read_spef -corner ss -max spef2read_spef -corner ff -min spef3read_spef -corner ff -max spef4 - With the -reduce option, the current delay calculator reduces the parastic network to the appropriate type and deletes the parasitic network. This substantially reduces the memory required to store the parasitics. - 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. + *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. - read_vcd + read_vcd - [-scope scope]filename + [-scope scope][-mode mode_name]filename + + + + + 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 ‘/’. - scope + mode_name - 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 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. @@ -8677,699 +8862,707 @@ - 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][-corner corner][-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. - -corner corner + 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. + - -slack_max max_slack + max_slack - Only report paths with less slack than max_slack. + Only report paths with less slack than max_slack. - - -slack_min 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. - -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 + 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 - -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. - -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 - [-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][-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] - -violators + scenes - Report all violated timing and design rule constraints. + Report checks for some scens. The default value is all scenes. - -verbose + -violators - Use a verbose output format. + Report all violated timing and design rule constraints. - -format slack_only + -verbose - Report the minimum slack for each timing check. + Use a verbose output format. - -format end + -format slack_only - Report the endpoint for each check. + Report the minimum slack for each timing check. - -max_delay + -format end - Report setup and max delay path delay constraints. + Report the endpoint for each check. - -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_pulse_width + + + Report min pulse width design rule checks. - - -min_period + -min_period - Report min period design rule checks. + Report min period design rule checks. - -min_slew + -min_slew - Report min slew design rule checks. + Report min slew design rule checks. - -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. - -no_split_lines + -no_split_lines - Do not split long lines into multiple lines. + Do not split long lines into multiple lines. @@ -9379,84 +9572,93 @@ - report_clock_latency + report_clock_latency - [-clock clocks][-include_internal_latency][-digits digits] + [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] + + + + + clocks + + + The clocks to report. The default value is all c - -clock clocks + scenes - The clocks to report. + 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 + 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][-include_port_paths] + [-clocks clocks][-scenes scenes][-include_port_paths] - -clocks 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. @@ -9464,114 +9666,122 @@ - - report_clock_skew + report_clock_skew - [-setup|-hold][-clock clocks][-include_internal_latency][-digits digits] + [-setup|-hold][-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] + + + + + -setup + + + Report skew for setup checks. - -setup + -hold - Report skew for setup checks. + Report skew for hold checks. - -hold + clocks - Report skew for hold checks. + The clocks to report. The default value is all clocks in scenes modes. - -clock clocks + scenes - The clocks to report. + 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][-corner corner][-min][-max][-digits digits][> filename][>> filename] + [-from from_pin][-to to_pin][-scene scene][-min][-max][-digits digits][> filename][>> filename] - -from 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 to_pin + to_pin - Report delay calculations for timing arcs to instance output pin to_pin. + Report delay calculations for timing arcs to instance output pin to_pin. - -corner corner + scene - Report paths for process corner. The -corner keyword is required if more than one process corner is defined. + Report paths for process scene. The -scene keyword is required if more than one process corner is defined. + - -min + -min - Report delay calculation for min delays. + Report delay calculation for min delays. - -max + -max - Report delay calculation for max delays. + Report delay calculation for max delays. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default is sta_report_default_digits. + The number of digits after the decimal point to report. The default is sta_report_default_digits. @@ -9579,64 +9789,63 @@ - - 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] - -from from_pin + -from from_pin - Report edges/timing arcs from pin from_pin. + Report edges/timing arcs from pin from_pin. - -to to_pin + -to to_pin - Report edges/timing arcs to pin to_pin. + Report edges/timing arcs to pin to_pin. - 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. @@ -9646,307 +9855,262 @@ - 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 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 + -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. + 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. - -highest_power_instances count + -highest_power_instances count - Report the power for the count highest power instances. + Report the power for the count highest power instances. - -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 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_pulse_width_checks - - - [-verbose][-digits digits][-no_line_splits][pins][> filename][>> filename] - - - - - -verbose - - - Use a verbose output format. - - - - - -digits digits - - - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - - - - - -no_line_splits - - - - - - - - pins - - - List of pins or ports to report. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - The report_pulse_width_checks command reports min pulse width checks for pins in the clock network. If pins is not specified all clock network pins are reported. + 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 - [-corner corner]pin + [-scenes scenes]pin - -corner corner + scenes - Report paths for process corner. The -corner keyword is required if more than one process corner is defined. + Report slews for process for scenes process corners.. - pin + 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 + -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. @@ -9957,114 +10121,114 @@ - set_assigned_check + set_assigned_check - -setup|-hold|-recovery|-removal[-rise][-fall][-corner corner][-min][-max][-from from_pins][-to to_pins][-clock rise|fall][-cond sdf_cond][-worst]margin + -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 + -setup - Annotate setup timing checks. + Annotate setup timing checks. - -hold + -hold - Annotate hold timing checks. + Annotate hold timing checks. - -recovery + -recovery - Annotate recovery timing checks. + Annotate recovery timing checks. - -removal + -removal - Annotate removal timing checks. + Annotate removal timing checks. - -rise + -rise - Annotate rising delays. + Annotate rising delays. - -fall + -fall - Annotate falling delays. + Annotate falling delays. - -corner corner + scene - The name of a process corner. The -corner keyword is required if more than one process corner is defined. + The name of a scene. The -scene keyword is required if more than one scene is defined. - -min + -min - Annotate the minimum value of the process corner. + Annotate the minimum value of the process corner. - -max + -max - Annotate the maximum value of the process corner. + Annotate the maximum value of the process corner. - -from from_pins + from_pins - A list of pins for the clock. + A list of pins for the clock. - -to to_pins + to_pins - A list of pins for the data. + A list of pins for the data. - -clock rise|fall + -clock rise|fall - The timing check clock pin transition. + The timing check clock pin transition. - margin + margin - The timing check margin. + The timing check margin. @@ -10075,161 +10239,160 @@ - set_assigned_delay + set_assigned_delay - -cell|-net[-rise][-fall][-corner corner][-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. - -corner corner + scene - The name of a process corner. The -corner keyword is required if more than one process corner 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 from_pins + from_pins - A list of pins. + A list of pins. - -to 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. The set_assigned_delay command is used to annotate the delays between two pins on an instance or net. The annotated delay overrides the calculated delay. This command is an interactive way to back-annotate delays like an SDF file. - Use the -corner keyword to specify a process corner. The -corner keyword is required if more than one process corner is defined. - set_assigned_transition + set_assigned_transition - [-rise][-fall][-corner corner][-min][-max]slewpin_list + [-rise][-fall][-scene scene][-min][-max]slewpin_list - - -rise + -rise - Annotate the rising transition. + Annotate the rising transition. + - -fall + -fall - Annotate the falling transition. + Annotate the falling transition. - -corner corner + scene - Annotate delays for process corner. + Annotate delays for scene. - -min + -min - Annotate the minimum transition time. + Annotate the minimum transition time. - -max + -max - Annotate the maximum transition time. + Annotate the maximum transition time. - slew + slew - The pin transition time. + The pin transition time. - pin_list + pin_list - A list of pins. + A list of pins. @@ -10239,89 +10402,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][-hold hold_time][-rise][-fall][-high][-low][objects] - -setup setup_time + -setup setup_time - Clock enable setup margin. + Clock enable setup margin. - -hold hold_time + -hold hold_time - Clock enable hold margin. + Clock enable hold margin. - -rise + -rise - The setup/hold margin is for the rising edge of the clock enable. + The setup/hold margin is for the rising edge of the clock enable. - -fall + -fall - The setup/hold margin is for the falling edge of the clock enable. + The setup/hold margin is for the falling edge of the clock enable. - - -high + -high - The gating clock is active high (pin and instance objects only). + The gating clock is active high (pin and instance objects only). + - -low + -low - The gating clock is active low (pin and instance objects only). + The gating clock is active low (pin and instance objects only). - objects + objects - A list of clocks, instances, pins or ports. + A list of clocks, instances, pins or ports. @@ -10335,58 +10498,58 @@ - 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 + -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. + 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. - -asynchronous + -asynchronous - The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. + The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. - -allow_paths + -allow_paths - + - clocks + clocks - A list of clocks in the group. + A list of clocks in the group. @@ -10396,74 +10559,74 @@ - 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. @@ -10473,59 +10636,59 @@ - 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. + Set the transition time for the rising edge of the clock. - -fall + -fall - Set the transition time for the falling edge of the clock. + Set the transition time for the falling edge of the clock. - -min + -min - Set the min transition time. + Set the min transition time. - -max + -max - Set the min transition time. + Set the min transition time. - transition + transition - Clock transition time (slew). + Clock transition time (slew). - clocks + clocks - A list of clocks. + A list of clocks. @@ -10535,214 +10698,213 @@ - 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 + -hold - uncertainty is for hold checks. + uncertainty is for hold checks. - uncertainty + uncertainty - Clock uncertainty. + Clock uncertainty. - objects + 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 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 + set_cmd_units -time ns -capacitance pF -current mA -voltage V -resistance kOhm -distance um - - 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. @@ -10752,61 +10914,61 @@ - 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. - set_disable_timing u2 + 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. + 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. @@ -10818,157 +10980,156 @@ - set_drive + set_drive - [-rise][-fall][-max][-min]resistanceports + [-rise][-fall][-max][-min]resistanceports - -rise + -rise - Set the drive rise resistance. + Set the drive rise resistance. - -fall + -fall - Set the drive fall resistance. + Set the drive fall resistance. - -max + -max - Set the maximum resistance. + Set the maximum resistance. - -min + -min - Set the minimum resistance. + Set the minimum resistance. - resistance + resistance - The external drive resistance. + The external drive resistance. - ports + ports A list of ports. - The set_drive command describes the resistance of an input port external driver. + The set_drive command describes the resistance of an input port external driver. - - 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. + Set the driving cell for a rising edge. - -fall + -fall - Set the driving cell for a falling edge. + Set the driving cell for a falling edge. - -max + -max - Set the driving cell for max delays. + Set the driving cell for max delays. - -min + -min - Set the driving cell for min delays. + Set the driving cell for min delays. - -pin pin + -pin pin - The output port of the driving cell. + The output port of the driving cell. - -from_pin from_pin + -from_pin from_pin - Use timing arcs from from_pin to the output pin. + Use timing arcs from from_pin to the output pin. - -input_transition_rise trans_rise + -input_transition_rise trans_rise - The transition time for a rising input at from_pin. + The transition time for a rising input at from_pin. - -input_transition_fall trans_fall + -input_transition_fall trans_fall - The transition time for a falling input at from_pin. + The transition time for a falling input at from_pin. - ports + ports A list of ports. @@ -10982,47 +11143,47 @@ - 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. @@ -11030,7 +11191,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11038,7 +11199,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11046,7 +11207,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11063,10 +11224,10 @@ - set_fanout_load + set_fanout_load - fanoutport_list + fanoutport_list @@ -11076,18 +11237,18 @@ - set_hierarchy_separator + set_hierarchy_separator - separator + separator - separator + separator - Character used to separate hierarchical names. + Character used to separate hierarchical names. @@ -11097,10 +11258,10 @@ - set_ideal_latency + set_ideal_latency - [-rise] [-fall] [-min] [-max] delay objects + [-rise] [-fall] [-min] [-max] delay objects @@ -11110,10 +11271,10 @@ - set_ideal_network + set_ideal_network - [-no_propagation] objects + [-no_propagation] objects @@ -11123,10 +11284,10 @@ - set_ideal_transition + set_ideal_transition - [-rise] [-fall] [-min] [-max] transition_time objects + [-rise] [-fall] [-min] [-max] transition_time objects @@ -11136,175 +11297,175 @@ - 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. @@ -11314,10 +11475,10 @@ - set_level_shifter_strategy + set_level_shifter_strategy - [-rule rule_type] + [-rule rule_type] @@ -11327,10 +11488,10 @@ - set_level_shifter_threshold + set_level_shifter_threshold - [-voltage voltage] + [-voltage voltage] @@ -11340,56 +11501,56 @@ - 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). @@ -11397,7 +11558,7 @@ - -wire_load + -wire_load capacitance is external wire capacitance (ports only). @@ -11405,7 +11566,7 @@ - capacitance + capacitance The capacitance, in library capacitance units. @@ -11413,34 +11574,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. @@ -11450,61 +11611,61 @@ - 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 - + @@ -11514,26 +11675,26 @@ - set_max_capacitance + set_max_capacitance - capacitanceobjects + capacitanceobjects - capacitance + capacitance - + - objects + objects - List of ports or cells. + List of ports or cells. @@ -11543,32 +11704,32 @@ - 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. @@ -11576,7 +11737,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11584,7 +11745,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11592,7 +11753,7 @@ - -ignore_clock_latency + -ignore_clock_latency Ignore clock latency at the source and target registers. @@ -11600,15 +11761,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. @@ -11616,7 +11777,7 @@ - delay + delay The maximum delay. @@ -11624,16 +11785,16 @@ The set_max_delay command constrains the maximum delay through combinational logic paths. See set_false_path for a description of allowed from_list, through_list and to_list objects. If the to_list ends at a timing check the setup/hold time is included in the path delay. - When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. + When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. - set_max_dynamic_power + set_max_dynamic_power - power [unit] + power [unit] @@ -11643,26 +11804,26 @@ - set_max_fanout + set_max_fanout - fanoutobjects + fanoutobjects - fanout + fanout - + - objects + objects - List of ports or cells. + List of ports or cells. @@ -11672,10 +11833,10 @@ - set_max_leakage_power + set_max_leakage_power - power [unit] + power [unit] @@ -11685,92 +11846,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. @@ -11778,26 +11939,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. @@ -11808,31 +11969,31 @@ - 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. @@ -11840,7 +12001,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11848,7 +12009,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11856,7 +12017,7 @@ - -ignore_clock_latency + -ignore_clock_latency Ignore clock latency at the source and target registers. @@ -11864,15 +12025,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. @@ -11880,107 +12041,121 @@ - delay + delay - The minimum delay. + The minimum delay. The set_min_delay command constrains the minimum delay through combinational logic. See set_false_path for a description of allowed from_list, through_list and to_list objects. If the to_list ends at a timing check the setup/hold time is included in the path delay. - When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. + When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. - 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. If -low and -high are not specified the minimum width applies to both high and low pulses. + + + + + + set_mode + + + 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. - 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. @@ -11988,16 +12163,15 @@ - -end + -end Multiply the target clock period by period_multiplier. - - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -12005,7 +12179,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12013,7 +12187,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12021,7 +12195,7 @@ - -reset_path + -reset_path Remove any matching set_false_path, set_multicycle_path, set_max_delay, set_min_delay exceptions first. @@ -12029,7 +12203,7 @@ - path_multiplier + path_multiplier The number of clock periods to add to the path required time. @@ -12042,321 +12216,323 @@ - 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. - The set_operating_conditions command is used to specify the type of analysis performed and the operating conditions used to derate library data. + The set_operating_conditions command is used to specify the type of analysis performed and the operating conditions used to derate library data. - 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. @@ -12366,59 +12542,60 @@ - 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. @@ -12426,226 +12603,225 @@ - - 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. The set_timing_derate command is used to derate delay calculation results used by the STA. If the –early and –late flags are omitted the both min and max paths are derated. If the –clock and –data flags are not used the derating both clock and data paths are derated. - Use the unset_timing_derate command to remove all derating factors. + Use the unset_timing_derate command to remove all derating factors. - 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. @@ -12655,75 +12831,75 @@ - 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 @@ -12733,34 +12909,34 @@ - set_wire_load_mode + set_wire_load_mode - top|enclosed|segmented + top|enclosed|segmented - top + top - + - enclosed + enclosed - + - segmented + segmented - + @@ -12770,50 +12946,50 @@ - 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. @@ -12823,51 +12999,51 @@ - 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. @@ -12877,68 +13053,68 @@ - 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. - The unset_case_analysis command removes the constant values defined by the set_case_analysis command. + The unset_case_analysis command removes the constant values defined by the set_case_analysis command. - 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. @@ -12948,18 +13124,18 @@ - unset_clock_transition + unset_clock_transition - clocks + clocks - clocks + clocks - A list of clocks. + A list of clocks. @@ -12970,74 +13146,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. @@ -13047,50 +13223,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. @@ -13100,52 +13276,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. @@ -13158,67 +13334,67 @@ - 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. @@ -13228,66 +13404,66 @@ - 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. @@ -13297,48 +13473,48 @@ - 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. @@ -13346,7 +13522,7 @@ - -through through + -through through A list of instances, pins or nets. @@ -13354,7 +13530,7 @@ - -to to + -to to A list of clocks, instances, ports or pins. @@ -13362,74 +13538,74 @@ 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. @@ -13442,45 +13618,45 @@ - 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 - + @@ -13490,145 +13666,145 @@ - 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. + 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. - 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. @@ -13638,295 +13814,281 @@ - write_sdf + write_sdf - [-corner corner][-divider /|.][-include_typ][-digits digits][-gzip][-no_timestamp][-no_version]filename + [-scene scene][-divider /|.][-include_typ][-digits digits][-gzip][-no_timestamp][-no_version]filename - corner + scene - Write delays for corner. + 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] - [-corner corner]filename + [-library_name lib_name][-cell_name cell_name] + [-scene scene]filename - -library_name 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 + 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. - -corner corner + scene - The process corner 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. Instances are always sorted 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. The hierarchy_separator separates instance names in a hierarchical instance, net, or pin name. The default value is '/'. - - - - - - sta_bidirect_net_paths_enabled - - - 0|1 - - - - When set to 0, paths from bidirectional (inout) ports back through nets are disabled. When set to 1, paths from bidirectional paths from the net back into the instance are enabled. The default value is 0. - 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 @@ -13936,49 +14098,49 @@ - sta_crpr_enabled + sta_crpr_enabled - 0|1 + 0|1 - During min/max timing analysis for on_chip_variation the data and clock paths may overlap. For a setup check the maximum path delays are used for the data and the minimum path delays are used for the clock. Because the gates cannot simultaneously have minimum and maximum delays the timing check slack is pessimistic. This pessimism is known as Common Reconvergent Pessimism Removal, or “CRPR”. Enabling CRPR slows down the analysis. The default value is 1. + During min/max timing analysis for on_chip_variation the data and clock paths may overlap. For a setup check the maximum path delays are used for the data and the minimum path delays are used for the clock. Because the gates cannot simultaneously have minimum and maximum delays the timing check slack is pessimistic. This pessimism is known as Common Reconvergent Pessimism Removal, or “CRPR”. Enabling CRPR slows down the analysis. The default value is 1. - sta_dynamic_loop_breaking + sta_dynamic_loop_breaking - 0|1 + 0|1 - When sta_dynamic_loop_breaking is 0, combinational logic loops are disabled by disabling a timing arc that closes the loop. When sta_dynamic_loop_breaking is 1, all paths around the loop are reported. The default value is 0. + When sta_dynamic_loop_breaking is 0, combinational logic loops are disabled by disabling a timing arc that closes the loop. When sta_dynamic_loop_breaking is 1, all paths around the loop are reported. The default value is 0. - 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 @@ -13988,10 +14150,10 @@ - sta_internal_bidirect_instance_paths_enabled + sta_internal_bidirect_instance_paths_enabled - 0|1 + 0|1 @@ -14001,10 +14163,10 @@ - sta_pocv_enabled + sta_pocv_enabled - 0|1 + 0|1 @@ -14014,14 +14176,14 @@ - 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. @@ -14029,37 +14191,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 @@ -14067,12 +14228,13 @@ + - sta_preset_clear_arcs_enabled + sta_preset_clear_arcs_enabled - 0|1 + 0|1 @@ -14102,184 +14264,186 @@ - Alphabetical Index + Alphabetical Index - all_clocks6 - all_inputs6 - all_outputs6 - all_registers6 - check_setup7 - Command Line Arguments1 - Commands6 - connect_pin7 - create_generated_clock9 - create_voltage_area10 - current_design10 - current_instance10 - define_corners11 - delete_clock11 - delete_from_list11 - delete_generated_clock11 - delete_instance11 - delete_net12 - disconnect_pin12 - elapsed_run_time12 - Example Command Scripts1 - Filter Expressions80 - find_timing_paths13 - get_cells14 - get_clocks15 - get_fanin16 - get_fanout16 - get_full_name17 - get_lib_pins18 - get_libs18 - get_name20 - get_nets19 - get_pins20 - get_ports21 - get_property21 - get_timing_edges24 - group_path25 - hierarchy_separator80 - include26 - link_design26 - make_instance26 - make_net27 - Power Analysis2 - read_liberty27 - read_saif28 - read_sdc28 - read_sdf28 - read_spef29 - read_vcd31 - read_verilog31 - redirection4 - replace_activity_annotation31 - replace_cell31 - report_annotated_check32 - report_annotated_delay33 - report_check_types36 - report_checks34 - report_clock_latency37 - report_clock_min_period38 - report_clock_properties38 - report_clock_skew38 - report_dcalc39 - report_disabled_edges39 - report_edges39 - report_instance40 - report_lib_cell40 - report_net40 - report_parasitic_annotation40 - report_power41 - report_pulse_width_checks41 - report_slews42 - report_tns42 - report_units42 - report_wns43 - report_worst_slack43 - set_assigned_check43 - set_assigned_delay44 - set_assigned_transition45 - set_case_analysis46 - set_clock_gating_check46 - set_clock_groups47 - set_clock_latency47 - set_clock_transition48 - set_clock_uncertainty49 - set_cmd_units50 - set_data_check51 - set_disable_inferred_clock_gating51 - set_disable_timing51 - set_drive52 - set_driving_cell53 - set_false_path54 - set_fanout_load55 - set_hierarchy_separator55 - set_ideal_latency55 - set_ideal_network55 - set_ideal_transition55 - set_input_delay55 - set_input_transition57 - set_level_shifter_strategy57 - set_level_shifter_threshold57 - set_load57 - set_logic_dc58 - set_logic_one58 - set_logic_zero59 - set_max_area59 - set_max_capacitance59 - set_max_delay59 - set_max_dynamic_power60 - set_max_fanout60 - set_max_leakage_power60 - set_max_time_borrow60 - set_max_transition61 - set_min_capacitance61 - set_min_delay62 - set_min_pulse_width62 - set_multicycle_path63 - set_operating_conditions64 - set_output_delay65 - set_port_fanout_number66 - set_power_activity66 - set_propagated_clock67 - set_pvt67 - set_resistance69 - set_sense68 - set_timing_derate69 - set_units70 - set_wire_load_min_block_size71 - set_wire_load_mode71 - set_wire_load_model71 - set_wire_load_selection_group71 - SPEF30 - sta_bidirect_net_paths_enabled80 - sta_cond_default_arcs_enabled81 - sta_continue_on_error80 - sta_crpr_enabled81 - sta_crpr_mode81 - sta_dynamic_loop_breaking81 - sta_gated_clock_checks_enabled81 - sta_input_port_default_clock81 - sta_internal_bidirect_instance_paths_enabled81 - sta_pocv_enabled82 - sta_preset_clear_arcs_enabled82 - sta_propagate_all_clocks82 - sta_propagate_gated_clock_enable82 - sta_recovery_removal_checks_enabled82 - sta_report_default_digits82 - suppress_msg72 - TCL Interpreter3 - Timing Analysis using SDF2 - Timing Analysis with Multiple Process Corners2 - unset_case_analysis72 - unset_clock_latency72 - unset_clock_transition72 - unset_clock_uncertainty73 - unset_data_check73 - unset_disable_inferred_clock_gating74 - unset_disable_timing74 - unset_input_delay74 - unset_output_delay75 - unset_path_exceptions75 - unset_propagated_clock76 - unset_timing_derate76 - unsuppress_msg76 - user_run_time76 - Variables80 - verilog netlist31 - with_output_to_variable76 - write_path_spice77 - write_sdc77 - write_sdf78 - write_timing_model78 - write_verilog79 + 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_clock13 + 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 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 2.6.0, Sep 23, 2024Copyright (c) 2024, Parallax Software, Inc. + + Version 2.6.0, Sep 23, 2024Copyright (c) 2024, 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 62964a595afb18703d31b2406b24f4ae686d2196..bb81eb068165936bdfdd8c5814ea5bef9d593e59 100644 GIT binary patch literal 1424807 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

_Ig5&cCU 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

R|bp0sW|?xGF*Aa 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 O^LKyu>p%SV&Hn@C@x)gE literal 1424501 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 z zPH{a>kyfr0J+-h})N z|EpDqhGyl)DB{m5gkW_2R5bO_*7RFaWCK_<@VKtkxi+v@F}p{-08@#}MWPpiff z*4PN0zurx{q^zpzlB({eu*Tny^BWVl*Sp=#{Tbbi3HX%(zJJ6ja*6exLg|&xUOL&( zUY`{&&ZxSxsmJ^K`~A|^UPGgwt;PENLMdJ8Lgud;-!7H|ubzz$-ka;g&)qw_6O6Dk zv2E@y>n*8<{33TZSHc9R0|}cVGT`(bRHCFmF*gzmw;wkX`d;iGCm=E3K3-ps`-*Xb z3=~lnDHA1ufm9smBp$64h)Dr*qMk3FBtXL0{U|yN1rVX%pt~;7{BUH)p?4Y(p$1Ep zRMHyCIzG;HF;~S@p62uNK*vkMRacaC+7zhw*2=4BtVMsU;KuWcN)xn3nbGY$f83qx zBXj^kd^|neJ-+TAkJmRIZ&?5Ys#3+ww&`rBt_r@K^|zBz9w*@y_wm^${G9h=k6uM& zjD5#;5HDF#N5$`pRV|f$!oPh8y*Gds4cO{4pPpP3)!EW@+n&Ehd_7-$$~i!Gp*j&t zJ4o)Ky){`}%9Lm%%H^YF%uhq|ARX=b!{p;Wg}JO<BsDmT55-j!KdmQEPAlV1e;eKSnrn#Hfdo@OuzAA(o+oXD^VFzvR zQT~<_#aaTq0^I+7Wp!b7)q^DNBx;KGi<%>KQ$ys#O^Y6p?%c;pWkUoK3y@mEh)(mC zMlN8!TL_YLpolVS%POw@au`x#6_2dpXN~5Y<%KFUyLw<(&|&L`6f#U0n-B~FcQWf) z-@nXgy%wK>zUn9G=*`&9lAvz{u_JmuEQJTi41~WrZYW7ZeF8Jh=xMAT^Q3|mn2d0P zqyD7aPzRWwd_-gAqu<(kW5)%<3Pwlm`}*T=$ZL1~rSp}pq*@D3tT=%H&Jw=V=ND|N z1b|ijroayXa4B`zsMD_m1){K?FbBPJCIR+0RkM&xln5zVIh*~nGwL@NmmM%JUSW4@ zWJHr4U3G&}e7rJ^HO6=|)44Fjt1+B^E7KGcatiKf?q?eni=kUJfpkXPIaLZ4TVlL_ zcNqm}{{|FqI_oMZEL*@+aMq)f)luRivgM9htq>A7rBBYB9b<;>an`&dWGe0Hl$#bP zwzXY~hcco6)X825;a>%k#C2r&Cpp#zYQ-l)h@qDeMyLrTrs=>%g<@215(Px_PaL(`jBp+nL92HX`cOiNHjQc{DqQle}tMD}Xis1QV6glj4cALFwcWVu?8{EcP?andsYm4QV zYqIqy#!BIw+fEY{mL8ZqWHJwA{=E=LKyt^0=?=F+=Ua$t{rvfg*Zb?KXsKTKp4XoI zDZ0Ee(TDE#<$D3jrSY35!~0a0-+PZ(V@RT>VgVy@8g!0SFqXaO;T(;%Fu5MNfA_v{ zVyz;Y1gEFHe=(dqx#^uNw|hrCF>~;+?H;2o=%0Tf#EfStqN$&upw>%+n$hcnEi@8^ z>`dFz8u~dcI!S{2nVafab(CQ<2e4HIQ5Ja@_sFvzcetS2jY1cZ@5I?*59miYd2Qf> z8;cP*5rl>Yg)P6S__}#`z3(o(uZCAYkGacQE<$hQl?V0mcccV#KDu0M+~tIeqlGdW z0Z0pwCX|`TvdQc`tVIeEnmjhx(a;6n!5uG6RE3iGBMHt*ujF^g4<_82Snt4Mh`Am! zHatuzHK=lBrbVq(>jKWei9bTQG6#5A&4%rziQ<2MB=GrEoPHD(pRnM0@VEo(j01EKDxH z?NptvwS`8wL2sz(s7++Uf!mQ)-0~WFBMrX|4f!gPVpxwW@>`{5H3xH+`W_XzDvfn2 z)~H*_Frmr8ydt}c`Zgbtg2d|Z@EuISa_~=Hj%l&C8P1u}W6gEBGzD4LTg)HsV6=Ow zITxlVP~wJMrh}bo4cTE}iI_=s*6lw}2O;m`Tq=s87S3~*nrmb+15Z)b@^`4(wpm0> z{pV_u`8d@6UiK94-rD&WRO+f?YD!p+BGw2^DFh4x@fk-*65UJa zA$eD=-troXX<%z{%N*fv2+xtwx4UPLu$^zy1MmkD8vFe#O*gt7pfZh25+vJ{?|nHjvvqNdg>BHM!QP$jE9TPe0L z0XMNM7kYFvWDv?=UHSg{mj-Jk86dI9GkK7uWRy72YvC6SGzv~xbOy4lBwoSIe80W9 zgUXqM2UnU{wK^=Tg;`(*lb~VEFfmVx`g9WbHO8ogZJT7m0@=gejSpRAM)wH&30`svqChE`}V`)S_x` zS<|^ROY$L3GgRi_%w?GZGalXOSk;77Ovlg69amWh*{@aZ%quh=1+axArNEQQlvRwO zhC9)c_8)=NGw>T(0>M|^q&Acj%b{qMs{qYUD{D)SIx)O4cySXQ#8E9iZfzLBnip@Z zccihxZJl&*^8PCYSZ1qn`F?ml3 z;gECp+IAuD_p#dngKHo|a33yY^=Dhb#S%RXWKOA1ieeXce$(VQJK6B5tMT~jgNgB( z-w#X@9EcK4R%E7AvQwb$$xWS>mWORHVEAJ(XGqE}%%dfkJDR2P-HIB+)BKD+K}6Y< z!`6nKwS=J3n|&5M^mU^+Ru;IL+u5DW%Z#}IS`I;|TCld(LE0;cPMM&Zwf?1ogHXSb zqrmh0C~VZB#1q;0kHdFMM_1@LD{YJIe9+yR!XAV!Y=#KZmnog!^&s7G#yBK%}y5R7W-9oF=K4)kRagz(1%a|dx#-y&ky zURqfte7q}FE`@3o*8Z{j;!SV(jn!4p}zXtpyJ8?;Kv6=)-$_GD(%H{gaJ!|*O0&2r7x^#VTU`*?Ia z1JRwMs|{E!U)=0!=7kU))s&C_SFWH_!6^*`K?@izO0vfW+g}#%;#K zbmg|NtSJ_P7jF;(aX$pYpI|upRhej`p->MM_fLgr8?C*M#hM??slttd03+Yc0&sN7 z34(b1Y^`~$Tr4*rxz1DA69X$D>HHm>$u2`AV#b8l7zVe4h7r^)?~ppx1V?$@h7Zb@ z4_k{cU(Y1@jL7oX1%AFhcsQp3(&?m&eyj#PJsk}HsSJDwVuxYyG9Yd z72K%PK(o1fjLtZynU$G~Hw14Z9GWmR{zCK)y^6djG+#FL9A^^39-^Rx^^3dTT~bOB z_G_-EF1VE&8n?{c*r!Lst%2u3B?J2C1zB-Fcfu5n3G$`be4I4(g10CjOjq`M@r-S! zNL+%tl(|_mA^m0&V`LV@y}CF@340JIvWdM3S?axA6B^3brvBd(1j+~>VO%2*EF{|+ z5dpaBj!v8~MegNNA#h6uzHCKhJTgN^ zzYz>9ys5m{Txh^u<~5ZrrG~}L3Gujo;ks9GEr8%C96%HrF}XPK4w47%_z=?Z(snAO zbU~lNfuBpVRJlBgBR!YJGP*o|4^w^t7iK67Wse9~gH4iGrVv+6NJgEl@ zHteIvO~<7H4x*gKTSc;9B32e#hCt5!`$Jv=)Y2=I%FBbY&^iGR%5e>|t;8* zfw`ULSC?hjKYc2#a-SdEOFgs(j_T0Hce7EdVE();i} z`-(Q;P)KjmdCQd2L+}eD*!TuPN{(6?IUpD(_2PBR)H1Nq0Mwzm6~MttTinxwsd5yDK<^xU31T7hUK?=44SE z#W~D+JQ2WE*F?OCUY!9-&Kv(Su)6CX^hwf!rNRsaJvIcjZ<(KZQb4Z`v0-F)piSHgl`R+NNT&K8}+3?(=chjt)j=?*IbV91np>RU_ zcc&G&9*^9;?wyfE??GgNy)A1Wdc^!MBnqCet*E0rSprfoN05L9N;pYM0Or`-*q6d@ z9NWNk#~C9#YgU+$11*(3SC zGxJI8ysNP3u=zwJZ<24~kU*Z#7JLkKQgam2IDYHc6PTNM9D}_wlrDq7$vQ|fT1ys> zjHB7CoV;FQ+0aCM7=$`HRj2Hvg_6>=eZQaLNRxWUcAuSj2SS6JJJFChPOUR4Q*($1 zKA3=iy{7Vt8(K)Ctds_m;S`4h=kn<=aqcg}=Nn5o;Uu+myxwB7(Ys>7<#OBRxkpw8 zrKfR0J^E=$6yCzMUT5T5bXJ>u`2m8XxuVu^H3^x7)Fkn^T7+_p@KSR$8lDL0YD`b< z@wu9rhM~+o&bW88Y=hzbPKrvRwk5;rKsIK%d%#(@gulYz^tRI+&1FW|$C;QAey0q@ zDN9c%|7{UW5tVVuax4Oq?xY2;rb_LsCNEQsQ*O)~C$u6hrch>X@&rZ5(d|--&qYOo zjJzOl@b}W+n3>Jm=4LXIDfAbrJN;f@Q7d9-cZ0w!;+(F&(}OznWSG`rq;s{W`kNL` zV(>@s!{l@+G`;yR>mz3AZb>uC=oYQFWHLf(ZSEIj#P$zmGD53rebJZ>pMt(+xZ40m zW~d2S;nlz4&~X7ty?eqz)pHg*xD?+R$cQb==-_gRhgHbSmKf-(UvB1lS_9SwT71mk zKv_TW(Er9!|De(Ta1Od*FQYYAJQZ zHwSC}IaKk5@x$Gxm4n-g8Td8BX=>p7Xi1$li5cG3Rd^nl16c@TE(vLl@1p?|k5%_4 z;)OJoF#%eoP)`V!BuNG|=cjMxo@p1;-qIjZm;^Y@h%7BrLjNy0TBAj8p?T;ZE0l~+NPVL zob-|YxuzAB3cN+U194Y3 zT?Fz)0?<>Y47xDg0GQre!}`jqK+QVL3Dm+{G9_CAR`77ZtKxDcrp5t`hEKwRrluoz z2+#W-XxX;_dv9e#>wh7j1?-tcLU{r7Fx66NV!;e+1JIzNk*(wLcRUNDL(i*mT+lT# zl8ADJj-DqR)#PR=u@6zAkr$y@Z*rI+W(@fL%JuR7CdI-YvKv81VXMwy0E4SmfA>p7oSbFZU}YVSC5eAhk(%H19|ZTqf!-=Oeb zeI0mbiG^hdj*kCrMHFv$o%2ynG>1UJZ=B>1qmM&-amC7U&FamX#U?7vUK^btN#M6E z^b07dsR{;=yvtJzA76{@fK>-T2!hy}0VJ;j+!GOe*)VK`9HNb+i>%=}1gP*hrsbD5 zbJjpd`T8mXQGf7;8XWumnKBeiG7cwk9>;G|`*OX!Vof=~Cjl?P*ykb=p^4@=iwY}{ zhlFMHxxrb7ivn@1zZr}f_)(l@f_Vqkysp*SbsZ*BR|pIWrCBBvD+(JgRd;2GK&B4{ zTd2foIn;KN(NCv?u-4Uxotw6?TazOLI2>Uy_3&>{sN%Q_{P&4H~+t7I&IK0Q$b3{rs z96VFI`{&!ce{6kyzFa#l9ZnOHTBTC1u4_!KU}%)JDLjYQHb5b}_io5f1XUZI-l!n| zc1d*rF-i@r4H>{V#wu38@1R0s{e={gsvOlW+jWRKk3x<#myli{tgI?VmJ`ZH4b*JG zCre?RQy_5d4$gXGXnwlFU;J9oD#Q-fuL+KJ8I(+|i>HG(4MHShJwwEv@jHqmh{Jv| z?M6K&zXXjTBQk2}3SL z&coPA7m97P&OTG+pNmQyqSC5^;Dvxh1GE#SZZLQlw%>vo$%?3r-ImCIK^c?Yk?3Y6}DmqMX^GZP+WC|SC8djtWLpL*s*`vtd32)Ov(vg-9IEG%7Bq#Iq2ryej zdm2+n!e1l0fr0HIxxY9);kRa1Xqzqf!DMFO(CwN8U0*Zu@Wg%7uUohY_>yqhJlKdi62W{Qbtn=_}*Rl5;O&}JJN zrXBY|daLlhXbxcBro1lwblkZhfgKy>B{k&__na7vOX+wMtK}Cxqjl`b85W2XCWFC4 zOY?dFgmTz=&ce^pRaPL0u{sOVCcSOyi<>&*#ElN%C| z=DIa@c6ZQ@Qg4UZ95H;Gckb7#nU#OPLMVcxHgp_ZYi?qcCl`AczqEF<*fG#MuO}{N zQF%S?xE{2gC9CIHdYYas9}XwJ#xk9v4RW`O3{~9AjXOklN9Y!)hTDB}e@dD4i)$=n zF&V^$J*ZO9OB$dl3XWh?BXeqm_e(1u3DC^9?Bd4eDU3? z{>_zVyq&qhi_0Rr*Lm~=Mzf$4Cb-Sw_)e9jg`EsAIFs9WK0a(?&Kl$#LV^V@m!{`Y z1h_J3(1@u?ntSLLU)}KABJRhu=olrR37znhUIjmWt(XY1R!W81o!Mo>VxhH=- zYkv5AC>@PBD6A#6$P+PBmX~!hmM$6l=a$*1y1l>DO|wiY-=5W`2x@*M!|2aeW$XD( zOr4`FU)~BAt#zRUMAzP$bJyHLiBis<|0{12ZQZ*)ViHze)JqXP9=a4>>T-y^Dz>7( z_%OJ|LV0Ms-dyLJbf~L)1ll;0FTtTjbLovKMcQyJw;{*GVV$N=Uu9%0iB4;8_a0XB zZVGONib=%^FBeO|R1iuqeHOYrS_$Raw8b*L=S${X+SoUdXw+W_y&?@(S!~D;nHailwr#L{T{d5U-Pby-f5WkV2X|U-S;>N2hI;SJ#@! zEPrH6iU=fed65?8b0PXbAF(q+_1M}T%7o_GkM}F-@4Lf+&JkZPbD8bjYad&+0gYkl zLO_jmfd%#NuREvbd$y_4PQY9b$G4BCUfaG)Drz%4ncCJoWu#r$*Cg2pocn5y30I# zb@%dZ##{U6sJD*g?Ct8`9rl*m0fxiMcxiyJTM#P#(Cx zTyTBIbcOTAZKt**jnA@}-&*~&WxZdApTA~ga|572ivID9Pai+4Dk{ML)D)G|0seYJ+Z6Bq-Em3;lpJnrZY}kt)IatvV=e31(C6K@PN7e zI~s#S)z*vA&gh`BeUo%r#`w;#5vwIYNK5TU)jCr4GT3`aNTDLV7ju$a$3yfxlQxz` z0Dhd+#W*hPtIVFcOY+hJAwKsy)=<0phoPh~Jl?x;ZigMEV`;kk}!C0#H-sRv^d%eU`%*lSFq86u1SunEFh*?KqvdQle2F4C3nQH2>lgB^knz zOMQp1j>cs*6Ofd{^7F9AVWxGH01dVF` zsgxf-nLXVN8eJcWnt5V}!L3TPUgL~IdYEcA?ZIFa4JM}zS%wu!AG^s~B!)$l%_Lcf zA9p8p$HuIUQjAoz0Cny~Gzy9d7oy~1+KAha812D1xO=#|jg8f{d-L`5`teZQy{z-y z-ak4vL}`$B#uJb+Y2({4)^)a<{5Ynkb?f67qMGw?ECrtJKVW_Qw^EbhKkg+GKz$Xv3>g+1t!T2ec%MIkU&t6#!4j7+6 z<(c+o#vm8WVe9Day@DnFIm;&qI42<#y}qP!hL$B|a*uUA>XNUoaZQP75LhC9lN>VV zQIy82EgdYb_5GqA6E5g$>Doacz;}JnH)1KbTWhc&GO10KizK#c9G7WT}}ZHkM+Q*CeS|1O>G*m=95vlTBgvx(ucY}cob`1)1)OE zr=hA;pO4aj@Q#)cq%=yoWjEW-O2MjwI`DR$t>navN9|I>j~X~%kPa4Y1YhIxU}Xt4 z?NhHM4Y>orcMwSemHXlgpx1AIS{|0spVP7@N{w$SiOr*y$Gy@c^3KvxSB&(Jpq?Fe z7W9wZBsjr!=`2!U`C7U6)G*Ea8-PVlW{=A=gGjT^;?6x{#X#G#`$_}JI{{Lu4%jwM zg#eoCa8sme)RG~yZaT-+YEeTf0VSQ&ml#&6&C_JKbfQZZr1pIfT1E~T$54ofv?Mo% zZ7D|c=l+&hLBB1C*p3+Fr4W&{EMA=7sK!O|LCGkr$ja0-GVd8TI!7FZ+zss#Z_|Hc zu9A+geBm@ECefJ>?tQcCL5I$m^h%)L+AzxDCl-BX?*ut`eq81-GkL@pRiz}!|0x$J z4)LRDpk_i(J#8t7;WgVIeQHZ9hav7xYq=WJptS@zzf-B(8K2wiN|VG=^^~r()E;wc z)6)R)(OM>1?&FU0`&sullLT>&>tZ+PNxS)l1%I9pE9&Rky+pb`x21Ch^dm^X=;BJZ z)os9G1``jBBYa+9Ax**_%o^P#>|iA)Deq*tP`PRbPbw>%S~W#$LS;+}7yh=d+=R0` zlCM{7_dIGIuEd5p5ezh4cjKlS4aJChHOY(aPk~O$wv_JxKZdF%&0$SW8D#RCivy^Q zyvI%spuOI+v%a(59awR(V@Y4-AGhkF!QFrrt*JNP>hIV$)vZ_WWG}gef-5?$G8V2J z6Q_mqxxlR|Y;V8R&>*6gg_}#ZlpZW%NHPm46-&)>PrqNQn%s;eObp~={$p!tCw2KA zT4bMsc|0n@LmJ%gBuXya7~w=z;wMH}DX=j|Uv=p4$)Q>GONgL;)%6`R{ehwi)`Qqy z4UjG^h77keZB~-3@`{XYLRLlon2L3>xg~Qr*}4;zr)SAZb~;$u8`xyN;8<*r@q&EO zJ5X2)um&-Gn!`2bwP=P)N}`~K-|5Z-kf%-OB~6RXA%XoCnGmQ z&= z7a6M-qU##F+qCL-(E+WN!?rYzhxquiq?T=ZJ)QA3Xd0~MyCQHfWRN>^LdRA1gJF36 zcR38C{TDd{$NTqv;h^So5RVhhs&49#FLewR$$d>Xt&m}tz^EGe`=e*wU?#L-{mPYBs zji+WIAVw&eHc1x<0@obkV>*!1s|=iAF-=;fe6C%JzgXXVULKKnk0=O#63ju z&j9@CHOc}L7m9NbB#{oF6AQS$wOK3WuyaM*QeS+A<0o_CPqcs2KfDd?sB~=*C533m zarQSoQJt@;@BOkEWLyawR&9^!{+00!SWeu7`)>^R57zx7ZLc>lNiedup zBc}M`ntm&7)y2EQ+lkQx;)ss3$0*RuMZI|FJb|>)Jqd+ zbTu8U%y>zO-OvO){4!_I@4 z{0zV)7ioSWezqGXH8SJ;JKx7z5$A$fDZFNsMK}+&>rBb9wYlx{dV^_wyKuR1xn8<1 z>$|sI8k;U%0XWeijg10Xd(uj?ey!9gGToh`2}9GpFE-j~`LTAbHvz}Bix*-USJuDT z6JiJ6Kf(D7)GE7Sh+NxV5A@lL3|*@IQq+5{yCYt<`ss7;uvucsT)@fWnr)=eQfV!T z91&NX0b(^M5_(kM$J?LYB%1hpBDLs8Wsw3VHU;4%T%oeQ10B+sUcm}ofe_iHRf6Qk za6x7!79*fv7W7*vA~HRryPpDuGQWcyE`FN8nbj`J({LroZC6hy11XEnI#Z#T0Z>uR zCZdEJIGakiAZa2;mHF|7;7|*dZ8ooZY>n=HG+}dyt{-0^Z)kwLiS(ptRP=F?waZG> z=U4AZ;71kIw!n$=L2b&j$ec$L0G+PoG9s0=xcD_~3%0lf;}UpjqRpUF_o|RlSRYXU z7aBW>@7W*AI;A)pcSIuCi5EOxk=}zaGW^-sLq_QF%S}JdvxE>uhLeyKA02Cfmb=Qi zFu22NjPgz^X6E?IsaGeN(Ya|<|p| zvzvhl;ecjI10hUfAgeMGzi$JLdKkw!mOhpp;)A4{uEA5Nf7MZST@_vWnBDe_omZHe zeKvYbz^+z@%V!tpo2%*j|+q8QMb{pqPGb&G+p zY)9;`?IX>;JV2QV_1Iw>2uS+X|ICHEb>IZE`&9=TVCpM(4ta3@2tMTa=5(89s zYuVD=QmWbgdtENY?>;8n6POxD$+)!{K>eS6rKPM%ao7O zP_a46WW8`iMSbm|CSpfWlLu+Z0M3+I@b}DWJXNs_$NGBwO?JcnL%aBi0i3aNm2TER z%2Ac&keUQnv0Y@Uu^e%jLF5oZ6Bi`vYVJ(GTgc(r3GUt(tDZ5 z5QaH>xsIi&8NOA}HB4S!_ci`|yw-J$lPc6-b*6pVv8lf@bVc*uV{N~L-Zy(a-?%Vi zi`-1P=gy+5LVa-ci7j1%PuwHXs+3=HwLXC4&w`h9SEHvY^;OB>7Q7Dkn2s29qfR!t z7b)PR9;KE|-DtR5<~Y37JPmOdOF2h*V8$3sIw9?ic$%q4?Gl6=Exh^*m1g7Bg`E5l zCLA1i7KWNtno9B3vIVXG8~?0BeCrRJ72mU3LZ}3r*F_(jo(^|H?h*Wacq_{yMBq zm*vnjib2b_BFiE>sN6!gy!uO`Tvr&p42Q7;;{Dl`V#Ji+Npjh8;ZJ0N2yn9eldfj& z?A-m(#B=dC%+ceu?YSc*xY50_40JB^$3bf)7rLZ;EZeP*abW3PyFY zCV+46Wx5;wXp?mnEO7N^Bfb#o1c56}N9KH%2uCIZmb0>JmhcPImF#4Va~#SEgc3M> z$x|gY$EujO9o>V#vlb3412Zr*C}v^p(1Lv>GvEbjlUd$i-&v*=y2@aLcNGeIsW(%; znBl#$gqO`r8+J-nin`2x6Vr%`2*}Gj)e?;~vP4Y;tB;py7=}kl*bZAkmUWc}`HtbU zcmRQK?(#nW^6W9eiJU7u_`|DPli+N+Wac}r2lmvmVN~Z*^XIGpj7Ddt$JI&WWX zZF2b8u$IGIt=}hb#KZaqoO%H>A!nH@3vZkT-M!@LFxV@8o3JrGn6Q>6S=;WRrv;|4 z=b?E+M6h!2d*BfR=mn=q=v_(c4A7)-jXwsIZP+Z+Sq8ZaH-drP zV3#?^h@bNWs}~B93mW%vUsJ;7vR~0(*7C*d=5URS^z=2K?uxKt3{1Sh_4nVWx?fLM zw1f3ZSQf9&5{K3CCh}LX^`-EFh=ecSoBP;Ng`HwMH0f}hk$-G>vAEy%ChPx}mNT$1{mc4{jp@HxpH-)= zTVOLFgj|23KzCCK=LEVGgBsqv0ni*2=$(k1ij*ZL%$HPKn!P{anp?N3n`=!#iZ6$< zzp-RwU*|$%RQbdVH(p+ST^$Y7oYfo`UZX1M`FSnjXDsU<{{6D|pxJ+4JVl`7pd=uPsn=8NfgECZEyY%oz&J(p(DSwaM(U8KqbLsM#ApuU$B7MkCh6Kcu*pQ&!G;ci+`kC-(hSx z(|#;dvwe2%$w&`V60NQ=cAA`paYkK+i@}Bve~?!M(M=5Y_||W=HUd;(ZJi9U}ZcBW)TMu1MC&6oiI9DlXc~W!&b(9)@R##We z<#1|Lwu{!!7<9f<*TYaM@-9b*u(YeMZGEH8BQX*J+a_Bih{_D8LePaofQ#8ug3^z% ztLbLP$yzlO)IpqY1PpmdJ+ZxM>w6Kyni(uYZ5b&p-`bCKt`{!2dYar-m*2L})kEiw zzICMEZd%+ZEKoI2Uc8r}AbY)Rj|gM_SD;rM2#^G*lC|$OZdb?17JHsaQTCgvkSfa8QtexMB`{b6f$&@ znl0Bs6Ot?k1jlPS>NHqYEIu{S=&bk~W)WY{iTBZt*pqiw(j%)_nPrmr2b(A8fiWfe5>l6^gG>5Yhf^LkoT_6;` z+vilWeDlUp{)UiyO^Ohc4Mi`eryC)op7Jk%qpqXAfBRtk^9}gV2ZMv{|NLGs|ED|n zKjZ)7ij<9+4EUp_vj%e=`I>B zzHGg|kXU|g;*wFhijVAS!TZWc-`t+0UR3++ZnKRkm5qJhmQj9p>3(SpfU-cSVGL_- zRr`M!d#51Lnsi%qmCaSQvC6h>+qP}nwzbN(ZQHi(s#|~ejo3TR>4?)$GiOFVe>r00 zfZDbX4qSxJmZD4qm6lWYr^)y5@^lC;@XU5eNl5?v=}nF1R=3u7OQ~+7IZJ=R$@Xo3 z{w5V?YL?IzWhR|wXf{jWyPQc%nj z_}=(nyiU+15OIGc`l&^2nIh4@0(f)?M1Ebf-)tn}+AC{oiKqEwvdU~m4+Jz(!GcXh z>vJVQDtLu%zoSZp@@yh38C5JNOk;$f(uwUFI^xt9zmTmQRV*coMhBt=j|OJ%N&cvv zFGms@_cEZRA>dY0!V%rg(8(|86D;JzSqlPK0A`UVwDDsDG-kLbO8RdVI^B?fF2cAa z(?+Z#Zc@wu=%9|q$Akze+ySaRfg5h=4rvPEu&=daKv_}BO)(6k3%`LL;lW``xnDh6 zPB4@-(4r+xl z#^6;7S(SVhiNmtUjiU(5-^|QHS}-Au%{6@9-CVzPPzqR%fMZOyvL=4sZz;Gc>qBJ2 zI__r?`5|L3K=z-&rmXz*IetED4&AuQDJqgW2}$@tQje=QvTk_W#w;1*=3=h5)LQ}}B`X9&9Q&$Rf$>C^9o2G$CXU>F+ zV==+}YCXNUsTvT8FNxx3(F$cjW{oe20MzJDq~3m#Lm`+IncH0{;i`T+AzXYXo9L&8 zrkbQo?_vZB>$vs&#h+Qd%b6sy>(Q||lV=GzMwya7DW72o2})x5SIqitL1|Ajn@|3< z0VjuSQWA|mB41mn%*@SGD$zI*t~)vO9wY{dfGU;l=#wAFEl_lzkTn_`CV0OB?T$6> zmswi6Tn4(xpv@upar~NHBG7?K$yhvbm9(CrRec0Qp`p432!-UWB0?f!NxQ7wx7dJk z1EGtRSu1J_ITV(nIRUd+gdeNkGR0AYZXS}?lSdr#bQ9PbFbT{uT+(P7-^13|t09)Q zp;)NPUM$p5U-5Js{Ja3IPX|7tRA4jO7QSc5B?ErOmmh3@B>ioCardD#bTxUllY0R( zsvqTH(ViQ5pcL~f?ULMOMU#S27#lFdV)|?qS#~s_Q(H``Dno&XEGH7kUqh%{d3$S> zVprk(l+$GGGPZt6oFp`PpSnj*T06*!bB!cUG0##eXfMj84u zs`ia^Qqn&e2h$nmJd@8JWKzZnVd|>j^8x9rvHK&uzcF5D*(40zGIM8Iqi`dGM1R+r zhr#N2;g3yhjejrKTj0^RC8o~*u`TWC|d4ai!sZsw%kdS;U@G!kdP8>qP)9YK}4 zEc%Ez8}t6hS@^H{_Rr9xqhtHu!;ty^4)^?#-~YF~)6X#cX;Xmz;hp9=>Rou@b1@@8 z!Dk9V-((;7{$?@qT@(iYQ(UyF6`wz%Cf^Xqnk*^i;B0I%m8mA4DEm?0qQ%qg`DXia zU$oM(N&Q7Fh%fF}Mfgl0s{j>Wgok<7Bo0pF#C;JLFTN~#p zWqiGPJ;xMCMg92GCT9ZmwE4*~-APtW4!@!*&B z?G-fq4(~51P>{%2eK>GTcgMa--o6eF{=~aKTO$ZI!FZJncz*yJ7vub!Pcvn+A-Za!&_?$F< zSD#ce31Z<>(M5Y}fg@31VyZq@#3SA`l6tFmyf54=5Ryh25W_TiG8JMhV?XoxNGUcO zNZuIuY=o05H&|RzTwtjC48(DJpxe<5UP;?;qj89 zvx&Mj;#)aK>jS7KP)ZDF#uZMcr~ibPh+hck??SE3#QE)2IzQ&5#k zIEy{*k0;w4_-y_5P#;1TI0lAD%n=4O&orf?qY?K-VeBubgQsR~>cM=LO& zEnHp4X&Yz77(vG6;ps-kexKZS=AO?tyEmWXP?xVXA;=?#CmUbz+b~8+}bg&kJ zUX51PZ0;f+FY7+^ThK!<}F7vYpJ*HNgXS1m$ z77^*^dldL#ZK)@O$6apOF+;TNN<~jttWb*i*e4fm!y^h9J7c+LT7BT>R|d-UKL1A3 zVCVF)chaQh1;$(z!wp8pNc^e?#^o(j{S9CP|ALdImON1iMS51bZuPy%479#cSW6j4 z&gyq6)C{Y2GmyCtA)y-{IsnZjw!dUaqdF$9?u?&`s@sIz4EjN6M-TMx)FSinF5Jhp-bXl|RuXo-zHeFHln9^?qh0BNVT7gN^b;3F8 zeB5!k^|Pv|vDL+6*HI;oN;_8_eu$!tixuO*qsS3r_38u$_8t!m(bVb9=jJb?1&>pho zS4NUWn!m%F`T^q!6uYdnrBWbhoYm9KnR3{qyOlWj9F@z~^zhf4tXJ}04?JS%`ikJ^ z2u=4TZ@)_5pKoBq+z+Jy22JGogBEJ#c;epqn&o(53V<#Mj#2*8DRPaI{J!o z;^7cH0h-oB=^|EwWREX%Sq`_&wt-`*{*UUq|Z>Z87CS$HS{X^xAJ=x?A6kav@s@V!5rY zrcmp2G#$nRpM|zGA9KdO^B-~9+uVq&RxhI-taWCO{l20vKUJD{y?rQ5PPv0(`HsX5 zt7xsQ7-lT0m)tVY(`qVs`9P$RX24ZLPefa|193a&0;OJRVQ4S%OY#GVeH+j73xEWz zqf_0XTMs3z`>*q}1*)H8ALdHH?Fl;q9#5?#bzUv<{GZ_C_yD&>GTLaPmVyUiBhLKO zTTC-uy5^GZS)%ve%UB3n4^+6Q1x+sQ*c)Dv3@oWi#x22aQEO!lBJxrHtWEEs2Jl7& zhlK+#((0|nY1KbCX4b7qeGbb?W6I`;DK&Q&1W`|pHHUxezALBB?; z84qRFU&IQq7nu*U8-Z;Ewm$j%6vVteDapjU0WJksTZt>oyV>HcYcv%R$3F0oMyRQA z8|w8wsy7kBa0yej6B{z(7mdZ^Z-*;O5}(6Dh~e*(b)hL7^it(nk7LjZk*sSl)N2V_ zLRS{+r616^`l8R(lzvPB<&kMKKA&KX#p_K;BsY8NB!ek53h;}C0@G2IxZc)Uk*iap zI#PK>(o|DPs*heO>U1|Dg;|at&V~1RCYh5oqoAfnU{m+$X*Ol|nZk^tfv2YBH!7^Q zO=ID1bDyQgbZzoShUy#NIcKMZ+xWvyYYnJ3dhc*LnU@`F@`S66{puIwY2qdkP*i@4 zTb=$}n(gq}!BR{deaQ2rAHSkbVsB-&FlmruS9cCcUmv0CD9(` zt=3n9+(aXwrGiXmj}w`I)FxGcbeBqRh(sCJ+#PWX@u-}UIYI{AMr~}mNeD|VIpm&p z7v=!Q;ghjLLS{CvxMUaRzM&s;x89d!YAmoh{oZ}-eG(DFwYuEcKE@ROT}fYdLM-j@ zMi0j-90O@eo?(7QqnoGKTgg4S$#Y6>-ejik^M%~p7-Om)_vkOuACqr>=s%eS5OBfI z5r###p|&Eap{2oMyG3_OmGp{`ldQRvFh{wyY-VYYMt_iv2}~4fA3?^h7SG>#w!Cx0 zZ@Qmjo^5cheLlmyxjTGZEZcS{qedw1MkpH=+O`BQ07trBW4pv*Fz7C_XSScQW5q)7 zoCIOL%8gO2VJ0hNjjjcz2^j@gsT)UoC-*aWaI|4E104}ib<&v1H9YZJ1{Z54A*dJJ zL5O21LxD)tlgqmfZB6N{3T;gVir8Sm^4?)b@~-=XrWCqm^_;-C;`>{`STsf;*cxIj z_s-;kSga zC3^Wco0kN5x@}rz)Nc|!+f2NziMg@{cCPdoID$N$-{r^W8Ruzur%H7m|?Sn9b*&Hz!^)_3zW z6|O1I7;^zdr@mCjNVtd2+VW<7LMn)B@SDX^xpzdIL^r+RVc<0k1hRK)B~qC>H41&r zzg`l=J5wr*$xBp(4=lws1}}^rDqb9psHc>%i6K)}6Q9=;Ozz0XKq`ea@=I|Sd-RhEk3DQwT)?G!?X>_Rla<#(jCt0A|gy^Ki_e=2PN z1OqY~R+dmR4-3d!`2)*L42u)mi3OHH00zZ$4Wwz_Z^`h0$vh!?%P>>TI@K5Wf@A5- zfpKL_&yI0LDxN*n&)nayx11!hGk>I)v8z`o%l-rp>)1vu?3xyUAmb`~OTP=VURJ7d=Ck3FF{o#n zxkXpw>i_rGq8N67naqwjv@iw~mmePv?28MnSqjPNTw&H$?D?du0Q<2JC~! znnT{XKjYjX$HHpT7hS4Ra1{z({SL^A z+G~+EbU|94U(w9?CX3C~49@t==$|*NBt)yO-;8|S2I<}%vrpn%Q3@J&dMg1G<_=)$PP3CAK(A$$<;F?+!;${NchYTbaV0?Q2YO?ZP z9STe4UE*pxv@iA}BMh`V13oO62Ci&!KyRxK-L6py!22E#0fxTb-}Gi3EXZ<#Ye#GfGjj<$quf{*gm&Z6*-+SC%_eT90I-vm=RzWd2Axh;;Tr_is z>qi=hkk&4NCO!@=q0R1}IxOE{f(Dm?{|Soy8(sYazvvkl{}(yJM*ANkEZJ!Pn+VIz zpEN1LVT2!_9NI_*-&$1{9C+`EC?p-4{xL5OKTdSL2@A7_b5;VA-IrHcdkM|F0vCfi z2Yg?jni!#yii(%hYgq<%?)&o9MKR`$kr`+sbEw0X7G%H3y9C35;Vg+;=zk z_1A~evz^PJO=s)Z-NW5{$npN|BnzBRi{phOyUq~qe)sZo`~CjZKdA%TTwRx28;W2R z9-c0S;aaJF48La>#$Qfvhc_SXo~>fvUp~f-^#;`EUczpwIp)sqzMb zeAy@N4a8Wo_9CFQXFa_H?_)mMt_i$kqCdlYgIkw;C+{Q@2g-$b>xWvJu>#cqFp4&?53B6@T=HD@);t(4 zLiwv}&?!`AWy+?qS|%~NUFUdsSgv$y@&g)#cozpoQ{3**phrgIEUIWdsyiHHbSu94vR9^Nju@~rwb4nflD7@U<-d>o)ml999P_S=*k-TI*_NYw<3iK^p zC%y%%11T!_*f7-eD)xnFzMNZ~S5`RuYV{SLb%M!bR@^S-Yd{m+O1OGG5x*9D&J4mm zCW80wt*@z(Ze(|{*A*=@e7S;{QU0oR?70h&YY6ex>k`BT*zcJQ#b783!VGZYd0bGJ zuXV5Vzz#x#H_MY!ocI9uUG&O>CYO~L9Ss$A74FcHS1X92o2PWgvvTS>&qljGO z6YLp{>`dhK%_{|TeS@YC384$SHEg6z^cXS9OLfvTzil1n3OR?iFXXeU?>~Rf(aGjH z`ivj+)o`9U^meKO@f|vl%#UMI-dP7th-cFQXVW4`OWf|WX0h>`IOeYdBD05Mu}vxm z0|9){;=p&P0oJZmRH~+f&n&opgiT8$PM2nE3Z7~;5inFv7FM|{hjEh>Z^kEAh%p@Y zA2wdBFu(JaG{Mhw^U>W;C%GDZt4EIv&=Oqa|g4 zS81Fg2Y{2lsS|C(WvVDTzguF%9@ab09jR)rPqr$aZN<1+UI01UqS&d{;*iqt$mw0{rzP%)ehjs6H zxMaEHxmt$j6I*AS8`-!Rh~}46I2*)3VAhDw(RK5}VsvH4^|WQ7*LxikilPbD+IJit zFuEsYU6|ZKny(MJ>nw7TkQwbn%bnwr1g3e(BJdWC(W(E1+Avca#<;QZ`|EtJw6WrJ__A9SyyFQ-fy${=s^ zJ-BfY#AzK|NW{u09x4PIz8OI%W0hbOgvnkH(G!v_u8O}~ITgsdh*fQ8X@S)I#N5Vs zop?%YYyDzVx^U^#-eplPyyz@M>r6pZO26=Ygir}bmUwdCyoJ{%Oj4oq4@(?hyM@7-GUmKM)aF4t^2|Sk+wFVMlg2XWT z#h$El$IY){3P;W4M6%V@gG-;JbDSp_ z;7ifiQa&Hq2TF%fOQpt}19uA$t6$(L3M~CfD+dkm$o{I;>vBNe7I7y4LCED z1CWUl+q`CvzBvw}s}QiPBTzRfK`lkb8txsW+ppwY5w$1^2*i)dgt~Z5u^Ys%L~HCe z2H%!e6aMoi^q_fM|@!Z^|+%F zn4}i0-G=9D{`;J2yx0&`51t1G+;nl|7`v$|1absRlFk)F)BERbAhF^9)B`Q#(^z&DLHvP zshipEdBYKmpLWfLPyDcEB2T&iYY3nf1QHS9p#6ae(;m7tidgS>8KMP#J z#uiay^DF0Wab)mSSelr}F(i^2gn@36VfSH3?+&PPkjU96d&-I2+i5URUj;;rw< zc|TRef>o-5HOZgc(UsvBfQ1_Zk z>6&j}i5dh1aOzK2$dTFd8)m2;r8pBL&B8+l4-<5A9J*q7-6K2Pm7A;2TO=S%0u?#% zAnpuK<|1xs#3ihwq~=r}ida;l#7qV=0oNG%&Ef#h=V_bbikvMEq z^|+BAPrY^Ci<5#}k8qYHY3dk4{oXwdC74j5bgeaM6su;bCvVDkB%5X0s!`1lW0g+~ z@N_8T)gluieUVhsh5u^FH}h_qNR~C1gzCO{?l4z}gBUvuNCFaU){f!F0g=w0;xD!C zp(qDt8rJ$qgvDNMs}g=9cVarRP^#3RIlI*%mEXr_6kBQN&%er+CL)PFIX>6xxpm{aLC`T-@fKVoX*V zxkO8tLudUd5(a6(J)yb&SR~7EUEyflaY!w;0|Xn@98%@J9qU;#pUR6?X*}CtLn323azvD*Ss3riBDWO`~}rCG*=9HMG47n&D_aQ^U4*q^?U_K#y&|9zt{ zyY#--Z)*dli5{DnLsYA|1Tz`3%Lh`cy4VT#^fXg#D2;kCCsYFqJg@anurM5Ilm7%? z|BXoh1z(x}S64ATE$#o!B>rsvH%wxu`nvhS&)Z&3^^agxx3&F^03sM*Iufa_CRPgC z4cv{kwsY0!a+6g%{==*6x>8w;fnK_%Rz$wM?0w--(Fv~H?Q!|&?(Fon|8vr2dhzR0 z?_Ol^k1~TP#iZLNQ-?YG^Y^Ls{eAP|e(|te%I7uNkjK#UM(sJ|S;f!LY((ohfqh@1?eU40b1 zN~cQYsSj5dpZmx8dzK#W7PmIHZ^!#Xmk-XL&Og|-j!18pk%Le zYlh%63&Hb@Dt`+egHRd$l8z5Jh>bw)o%5uu!DEo7_q&&n%reiOWdHC`J)(^V_v9We z5+Qp62S5b);>i`!uVeagrQtowjwERABnLoP#?wwKy0Saj(<_8DinC2-5hC8H{f_5| z>0r?=Peln%WYA(xB=6Ha=n$_`pkgqFY@O1{9&ySfLNoyxvf%Rb&oIK;+t^g!fGknN zt1UHH#_t0PK{Kgjvdax72YR=g&SW*f8K zE@6H~W+rj`3&0U3D(hDz===dhf7~lAT68MQCM*IuPI8&zXhv8y`NYWzeGpbR=jE#` z5*4DdpT9baFsRo2T%U3v<_s}fP3^(_taJJ@FuE_!rKHNlAwN3`p~W)8!z{ud#`Gk4 z)VF}t2%X&cXf0ysc{%pq8Upzdb%7HnWXfsLE{a5$BOJev)TU6z<;;iZvuCHM_(9Ux z0;yv?kxB9ELkww67@;DgB^-R!{iQDTEE{eecyJCS0;p>{*#nFRJsEx~eS(cF+XuQa zB8nb}7|Rdsdn!)yOvbR+1&csOw-@v5ADAQs*8{LUumhe<6De%kRAaKip7;rnWm$k% z@;4AUfG&#aKp#J7WH~MlFiN^c?6i*;pa5j*`~x!arXq}ElBXvlEDs&qv(c3?kDBS>~WPbYti)%{{&KT-M_0t8e; z9|Gu9n?qA`MOl%%2@d=#Eo0)LiFS2W_ZyVS?d)xF7DlbY0%@291Y^)oi_jWNiB>tp zQr^e{vlAtq?K5*JQa=9l2dDVB*7kNoWD8&h=(bzo$ez{n>9#heo zncFwNK%831A=zG;lPRB!otB9Dwk|h7*uzh3Axl1OE)2h%H>kyE+wU0e|3*4r6cq5J zkYy3B`{_g*#=ipGF0!G;y@9n~Z|sFjAptR00dZL1s>dJD2y!SZDrTjzz(S-&lDw(j z8HTX5PVe!k#QedSdLc=xoD|=KJVM9JEnOwbW0qSDWzQ_QVL%)ky}Y(XJc5;z7Xunx z!c4@}ImN1Y6n{gTb~I5|j0~qH3w+Fgjyhpw;J{?-38%(zB(J}TGw|>C-{cS=l`M8+ z%SIn~lRg$B;}{etc0#ERnaeEIG$C_7C@i47)M9hW^-kwhLQk_~k{;YA*hVQLZ(f5r zphe8RAeG!zkKw;A zmul`4F3)Dm46cn&QNpaVYmNzHT({IsFm1yd@2PBiAPhwoH^;+Y7m}HhRt3At?qbH#!|q!4#yzWV}aG93m+0<$=(+7HS5dvBMg#NzLcR% znyS}&PgC>NL!!jI$}gas9z zX+nlL1c21P^6zI|N_}V=Nv;sP#unsx4Ni^IJR$jlRLuPgSBbqhFRsGG37*>Qsjj6x z;GMZP@AJ2?)wpu$3fA;@Jwn_o}Rd3R15iUCQh|po^2iupTi1` z?{?j9CA!cN#AOQe z!mbYR6SID{4~Xfw3Tk07isc%|Bc;|c>!jjb!W^+_Hs01yCWZeR{p}} z2qRaQ48kGXUUPMm-y497g4<1HktEw(w7(z2!QGHM69@{+AB+afa*{B#9RzzaR?Ms7 zgMdUv7|2H>k$)vg#|1l2#_Ztcgf-9v<(OSiA*gL%W{dMa&=Cf=9(KolK z3qtN+G7e6~IjD>3i=faO`mwv}zqz1&NhFYW_Dl4ss60DK`z1@Wz&>H7w?z5P6X%b^ zVKN<(Y0+TsQT9Hv7OvJ=(zUOjXNx1NXuT{hF+MzT^J?%<(#utq=xkaS0X9~5mNnzE z{yfqA68=C5Y=HSRmh5+NnzOiU*e^R(@&td!2r5GK?^R+B*l}5HK)d!EidORC*n_#T z_$&j43cBY+%Ax@Ei-C`WPmICzur^R3Uz3Z{cQjNsy-t~GA7E{P{aW%@?g&dMEx^gb zt!7S?BLlB^Hnw$mHgwr7akstOhU1HuqKlwU|I9!m4WuiB(VDtMaQTT0uZF8e$NtqT z93cVIlhy@ZTm7SYk+LZWl6Av)1mML%8p9~g zP>&-$XO#G03+52pdRx=S{W}o!+ws%&^Xc|&Kj-D;0iPjr(1H9=Rfr!I4#3L{PQoIX z9c8reZj-IL$}Zg*R=fx-;M2ayGE8E=t;=5tqX*7CmCsj=g1rum#*_N9AD}!ev;3wX zpk}DX1`*O7A-PZlkUelb^pSo9tELES5>E|GQBpq)nQH_m?1}yha*Qa?+*;Zv>Eae$ zZebkx2{CWdbl`v&U+^GsEzXY1JcjJsp*~n>UKMO+ffi!NEa5K=o6wn|@J^hlX73 z&Xho-if|tllQ0}-Ks%z2#rScqr__P5hkt*kptZNwDTif4z)SM> zg2BE#Qql`?-D8hjOPE+MTFJQHmktO*`KSER%FYWtnda+tVq735eUe5(<7W^mhd`(u zlo84ZhoOe8w$GuKlPAvBW67jMm#NxF{`WX}%0-mNl}~rm4!5yoDlg5|Cb~-g zw~m2>^}TCT6M-;71&Tyxe7k8nQ;al_Rl9}&b9>@dGFs^qzKMErUEvou9Lppd)KpV1 zD~@t>5zcpssgoeB-RD>C1_w<>>o+iAo;fC+|Z(Rap^R_ zg=;D)RX4~4v(qYXK7?t#(hk|*dFN2f)8MFGd3N5}5RPuc`az^$J*S=qfK_L*F$B+#U;~`O2wWYQag&<(b17onT2__BmPl5qSr8NVqJ}B z8{YUf(Cx6rT3K0Clw9Fe=r~5&O*K_}&24KnUFnv}s&PiNL)mt1MwtwYbExbu86O6# z^Li*6nE|Q0aN~P530gJYvkU0Yv0=^AEUmzbpV6)}7b9{xJA+mXqT?<{rwMZV&i1%B zqEzNfT+mkyuKYLn%Fy09I(jD>dRY$5aFn1iCdG{KCq{X2d2kZ-86<`*r_6bm-PKc{`m6h5ckqbL zLr=AZgr}l8h4@rfU#nc0Iow^X-Jkmg^*(f$U*@6$>V0p*a%*SN%vsJkGA`}gT{bu_ zBC3xUFV~m-AucY<%hTGDJUAHi(C-00=wDo&TbDM@*g$XCry5il87hQ_cklF?w>y zf-DjPCBf7U9(I#4~8Gc7N(k>PVh8g`OCk)KUaT6Tqx@&=yxD9aN$OFIVys4sew2G z-Ft*!E>QZ~@YyZl(3^>H2ni3SrIacdDr!nEQzJg5!);l|wK!AI;t!cptfi?uJ)Ivc zk-tMieQ-gbz$_E9Co`G&Y3s(n`#(2sy+%521W57`>*cv9AZ)}uSvNZd9u^fKHTCtk z*CWq5Xt0GaLjvwPo$g74mJ7k(sFF(+XBiERJe*4LLwT9FH^g+*=9^W3XB$<~)jOfP zVIuTWRi~=pfd8qgug@UYG}P83e+jU_sZ${GdmpHqR9u1105xmcficcfC!WXMXhV+3 zTb)yrG!IjyJWbL_o;}npS2(;r3d9VHr0n2^-rdE&QYqvm6UStbGc$%PiF9h57k;h* zXqriGCc3?Ik4d=y%tXV z+?n>+3Hs|i=uQzU_=Hrejq?bMK46)lvZXndY{J-E$13P=#xPjj^0XSj6X7Q$SvjAk zm07Bu{PpWPiX4@sG^?p|K1ddW^IA5YNkIU|{jsMe)r>!-z|_F_==zD!F>a@qXpnam4;=pUF1b z#3WRvVXr%Wxiq<7C?!*38TAs!6XebP*V>J5YllSwISL`4>%`zAB8fs+2O2 z$D2_VWwTa)A?93}s#1iU&SHM`mrxpCSv=Nm@wvIUwp{LJ*t{oaH{$!I#i$d%) zw<1EufCL(d17oKZqdC-dy!(Ytl zv&7?HsI3XOQ5!(v3voz1DrnCMZcorUQ&vDWr1Yssnhravp83xJa!F&CvwO=h<<_{w zK)AnA_C>Um)Uyx0)vx;x`r{9QR%Fh}u7Jmo0*wm7b61aqPSrDH6+w0=k8`Nqu6YDiq2^3uH16FfXBUEc zSSw_qt+rHfN9!X?x2N*~^HRXjb;WOmn!dWVT?@s|Kfhh5GX8*tgA%OqzxqpHDli6-+AAMwJrOSHlI8Naq!!MzY z8|wsWdSnZo8(tm)GE|Vt>}|*u0S^#*04%iF>nMevhjoTy#zeYjN>Z!cn-{8{_B$>P zt63(TRXh8x>(W7Z5MHoXy7HA*Kav98l(sfD&G$zKwc%#2DL_@ zzr|>NyE?OIm_5R+WZKZd);qm0uHIgtiq3P zf;X?;MWk%EtCeH+DihdsdQB|XHl&L6myG!^#+so+3rnAt@Ut?#@g>=kQI8&pToJU| zuo2*VR^;u{X+9qxcvDUDWx-4mSr3u7U_!VtiJD#mB2;Q_sIjuUT%&XHEn)%ISJf1o zg}&=~(QsI6tgBgx)70uqsGXYBDus<4vDCJC<_VH`JEG{X5jv z4K(+2ha)m-sO-2J>Fi3xWjFPY$bTaVTx0)Xag*pOz)oGUBgJ#(WzvOnsCiI&Et3by%LcQstXU7+9Yh zA8^vfycz!q!2BDN{2Rb9(*ASepEV>sJ^ep4%m2Z>(9{289s7S4uV`#W5Vj)tw7ZlXU+3^}f4#n6ee@q**>KeU_P`Ix0$GWHb+(TD_VI3eKW%QWc2W6Q zYCq?}?f$sCpWnT?3K_q~ao%&~*ze7&9o!kd@pohr7w*hHFo3m)M>qQU3~xgY zCl=upSLgG$XEd(5C*{z7=fhi149*Ac@%Da-(+3+jM0N0K1j{!j`d{s6QPBb1osuwd zBHB-SFB(LI#1U4igFTv07NG4fftKKFkYJi&4_30@%JK8-ys+J;J;a*7 zGVQooEveQIFE>citB`fIM8n5aUUjL!@PqgFhzSfq!zB|j&^nUiS_NDtA=H22h1iij zI^#i%GvUDj&h7#Dj{xDSZD;f9xj<3Ye+#i9PqgBoo&+)z=mek;j*sO&>#Az&KA4l` zq9SK>>a^9#EY~eJk6?8Pj?5pvHs=3kWY=SN1qe{R!?Mf|4jpf%(ZT8V=Jk`F&hh@d zQw;QXrfibGHo}gYEzNTErL#N|RsmvGm&qVs+){*G7(36$J9O$BRJa51C;7r{|&rdlCDZeojsrZ3to*v)pv zeCIb7!(bqxBhjsB-2U^BWJy_^nh6TWwBd5mfsHb0I`iw?SATX23|b9!0zPCic?xvO z^wgV8m3L5oJ-lcLo}W7&fr}`L4QWV6?+nKpxHd$P^%#e!fWxRS86@Mr8Sb0k%kw8+ z^*KM*nUiJc!s-$|iC>j*ykQldd#S+zdj~-hKUgfMPWQ+s`7Fp2rc6ZZd&P^J>YG-J|P4x|WS6ju7 ze~@kIMrM@Cn{HAIVMftR03Z5Lsy&Q_xR@)U8TzJ0mIKn>B5L@>>VBE5%=jKVGMS=8 z+0>rh628tX$ht3Gkw?Q?X?$sE3!J$WvF$(#0d(<-ew?xQ1pzP}id4lAkTx|@ID8Ip<5s7dsFjlT$L_fZsg;zBa%>+w} zKDW(cat_5u1^!DB^WMtf#RuPL<(IRylTT9&cXV*Y1nv2XAR+7)RO886193t7R$NFZ-u@$ zm%kp+jSlH=Br{>s*b93oZ=qDTy)SVo7IA0sZ zOm3ZpF5)j5SLCDOCF2COI91g!qlU+Tc0Et(m z8MCN*ahr8S#e=yWTP4?fUQ0RMd~Y8#TY*y)BmLr?oclcKH~4BA_2Tk3YS-P+-p~Fd z4zZ}q=Z-0@#>3#GY=8&C-ib3}P9ZTIy2xugySYT8!`vRjofl0J%ZlCZjTaHO&VY@i#U+4#azV%UqA60+R) z(4RS2-$WVy7lH!lWFDgwB_`FdXtbCGCK-2t%FT)R9D|HxNw8hjsU^QHD|S|-_Xdmv zF#fyqkV&R~YPmx|Kg$|L<)>R`e}-_xpp)t~d&jN@2@s>I&gqRDYg%_Ep%r!do}cO; z`fDR=w%1CPum176%T;!k(YYE|GZPK4_>U1iqF*ZP5W3C~pL2Rr>^`Dln3M zfi2nU8${a0$a+^)CvHmEzuNL{Azmmzq+TgO^v}!O%bxc77?z|WYq?h366k^fn}wFe zt64D{>mq(%&=E4H-_3nJ8;>RdL}`D_mb9XW1b2>8O{V7CPIO=QCuiTNsTX&aA{XwC z!_EIf*aXaeBZjJ@tmF$Ah&tGW<|Tu5p3ILoYWvEL17(`oa&K$p1znR=J=u)+Wdn2Q zSQ4hHfBmHt-6^;o`n9CjZKqpYo}~Q$G4@VDnzUQDcG=Zs+qP|^%eHOXwr$(Cp0aJD z%Uxa8-|t!x|M$h-D`Kysydxtc&obu7Ip?^>Q02+OYzwCTIlgsT1E^4h*LkJZbR#Eb z>hVN>Y#6Tos2F^ysah4F5s0a>+^lmIKBsVH^l1H*ro$o~wvD1XCHJY=nqc~U1N-$G z(^Z9qr+*F*%Bvsg?U6l1;oddLHNkkn;vem{Y+{GDh#g#=!7e5bAg*>h_(;|oTphpA zKxNyv8pQvQ!BkGr?=M-hs&c)&^-ymu)0)9rr~CT+Sb!S z`sGni@6Ee%l!=IRx1~%jU(c)o-U^kqlS0+R-~V2jZQP#-RdQ&WivnFAD`+Y)T4yd( z*?!02r;4d*Rn{iXHOodj^7B!XcWdJIh2Q*od=Txsh+eA8BduW7c!B7ir^+1{w&o~Q z=4Sf)CIhpk>d=E|Dhdz75?jdFM!X$EhfYK);M+o3lm!0Y)^D&Rx6eTQdJCg#vet7Z zRx0XMtYcEmv!{`(RM5~-yOnTDi%{ST zG(2}p8tMdYfy5isG{bM<))ttdp)RrzKcq|7QLFSGO#z4M+eGi{pNf*a$%#>B+RzWk zuS^-!*W6b}9OOKio}a8kLLj#jWZ){*44GW*)@eEz&ac>jNM8zbi)<1S3Z;_FGXvnM zq)Dan_z%i2jvG2Uia|Jxg+9+k@o*<(xyUsfg;o=2n?ZCB$6|_Y=9v-d0%)>=;GEXr zpf8ZCd1=k|^m<0JQn|8$NJZ$Ty4_ME9@#dfw@&o7pE}@z`b*HuaU=c46SQ5|Fi9hq zmjqe)dBS-n7t4iv1Evr_gDTSMA&To$Qy%xP{UIJjUA=3KU2>}}3r3r`&m2upKIre# zmkzBAFx1=AINT;y>;faV-qIlN(#JvK4?J3o6ypjD1Yyz5nVy>o+`l2HP@9wgljQ%mivO47=VIgh zAIZN zrgRH-^XvJ)rQ$|J=RSjg;@j@pXBBlKyk9A2jgW*T>iEuax_6;p};T`Y4~0 z$Bi$SAq@TA<=@W5zwrv()+*V^*co2B*)efby29TjSk|;I92Jvhk|H8^Osk!5La99$ zb@zFBwe#tCw*%GIG^ZKSo9Xxh-w+PB@t&G*RQ9O7yCnsxpOXioV%1TIkFwLsosv0BrsMYxym(m)w{NTaQB`f-Ww*zWYL(EP5`e3HT%ou- z=;UgRACmk4aZ<(o-nPR*=3Z>QovSy$Y;osDHGJ`~)K4Na(`Yl;#M*!WtG18_B*7`@ z-4h;cajzu%q$Drli5UFpATWW>AcTLrB1GMjV(zaGy|JD?o}3HxKDv$|A!Fga(JkYR zk;}${e0J3Pj<)9_zq1YbSdQNBbM5)4NhhB|A7PY~v&&iR`(xW_PQq1z9HtiH zgZgLB=<>w96`$#~9*3fK!P#bUe>Vc<6_+Uwn`ZFFA6vKLU2jQLj)b=Krs$vBkcBz6 zggrk-)Y6)+QZ$)_hp~iOgBpRYy88mP^5P6_FIw5eLCKwUo0K$eRc1NloWPo{H)~99 z@#E5*)t=MJh1oGT?JZHMcfo@or}!e*Mhh zF#h3*nHP5TTd3%mL)nj0l-lU4r;^fNXh;gNkwloarj0?Y;~?b6M_GyGSoY~9!KpaZ z;D)sFb64I2cd_9fi=8=^>T`?9VCq8I;h-2}RWzmM%dvZy!Fd)ck}`b^y&D^>^F3m_ zXJ!Tnp%iix;<%IrH<$-7Qj20V1{1Mdh%N23l!~TFFk!7QIupE62G_*HS$aBPjUq&D zcq?3AACZ&n7i|RahDKM4Pmnx@p@J1`IxX*FHdbRM4z@0{Rg+iA`GVb!8M=*Lkl!G2 zt?9OGmYk}hoLqCtZp2vO9SX5*havpqi@i#=!5=oVRQ{2hTT1>y95&E$uHLDKcYwZt zuTbFk$IH{p!S#K5{y+Kd;E-;6pn?;`xARU*x!0v~RQMXN1LU}~tWfSGk+@fFetDH#d$;M{&MA(1d4Voi#az zNs&IM=OM!JI56YnWELEX%-zcB$Dqte9fE2ld`g!wpj%VeLV8Itvo{Ksyty!F#mT^O zHV0-BBjp??$rao_!>>0`ew|d@9Pe(Ypt9G5{B+n&!bZ_)Z!pn^7-@xXi>WZ>b8Wu8 z_gr$jqDG!;8ko6Szp5t%9`7s@w4WhNQ{3 zwmf9myE?uxcS+3^GA(%3yP=I?yuU2pK|lpN;@UjQHn|6! z0m|d+36x}FZwpDcW=li|Q`OT`dXjB2!Cr zGw&ow)*St;Y@1*3osYam0Z&t;*FCbY_1%K}a?}GH&`^1qeWiQKkR(j|!eCUaP?ff9 z)5f?2O5R%kOhqXuL!9Q6_gZC`4n@3Z(+OsulD0FXV*k2?c*3)<^ zw~iBZ@?rH>DH--+ytH>A+Ils?3&gxow@Gj!3#DwdmmxyggTg~%N4=i*oJ#9*bYf|5 zVQSY8i%2^O_~sx(B9xN|%CR_W8<7)@lvi7qgZ%C&xJ2+)MXN@niVtvRtBrT;`^9$U&J+*mR5M+11& zP7yvk-F5HQb0#7g9-2H|bc)_JietuXt$wWw`^&tpf4)LWPZp?CelWH}&v2QHa$n0) z!Hy^6wpII0Aw+stss#O@(E};0j-yTmPma(->cwNDBPl@r=>rLIQ(jxK`BWPme@rRt z+m=OaO*^b=fn(@F*xakOs%Y6+W>j%aQd`QOZ5+ifsNcSK6`yGOJTWvgNF+8y=1o`-XH2dVknA zBs-i%T}+nx56U_TW!(!u?5&IgbPNrT#`o>$064CV;;cghlVFn%u6)J!u;-k4Zl(+; zdBH4u*gpyKKgNaPwAHM#XxG>nLMAR`k`XG+d#)!h2F7uUWJ6g8DjIK!7`xg}7=>})>Met3#0k~}N9b8P-Cry$jpZ^Sru^>qLhE zPPHVO7YcS5J9Kubm>C*}o7T+=;O;^sXT^97tXxxRx;1j-p{aym&B2?&McUr{iReSa z7He3$q8K7oWski*vfKW1d|W=g&Er<7avM3jHCSNdA_*Sk_bhi94q0nyAzcNz3HjeI zZY^`goU)-HQlzQ9?)+^+XjtN@LDKjTjOsdK-DY$1ej~!ttXOzEa59W!^mYE!X3{2x zL9#h4qNg4qv_Abt>sK%TG<9LD|M zC36LsH*`8bx%*hAP4asUpYVG1sN1j^>skJcjxCxqKO6)fYrNo>8Nm z+z4}wVRC%^Fz4#dO1StVueW#K>4ifL@9*+7rLT0V+P*Ta9Fdas!7bnDf!z3gnZOdC~T>+PVn zs*Plkkx3(su$%*^+L*>u#C4!)FF&vp3LW@T=?#V1etH7_de*s0;Dfu~4xJ(+B; z&b=XBEj!OVDNO3yYbv*hJM_#p?Jy(sR=kM7FPFkmfG2>GV(3K=yzSfwSoXE5{3kK} zZ+-kPG0pM6C~M3toc}jv?eES1hO*YH^)KY)*Nr~WdHc}Cldrk~TpL+B1c;pXJVGIX zY_bfBlayEQ;?th{lWG!McM|;|uKCoe7yflK%iMIFsFl`dbLWl#e{a9Q_m>Ku9zKV^ zS_v_mVVP3OYHV4m-N*aU)=d4LdOkZ|OxN`npMcNni(7ZEujfZ&?YN!0#c=$=?fuTF zL;C{Um9^0?aW!}Qs;%TY%F3IY_7XC@4HR)oB!>}qcLMgEol`WcETb|i60WV)(?m>6 z!|_Dwp@VFQFH#?wYb;Kyu?WB%0>ar(dt%~Y#7u)$ETYMPacyEMGHk}DyIIO&_E1M>#g^PJ%ao`;Pj;%2zg8IKIgqI;GsUr zZ=-&br#ll!=;v3|YortYtv&Ch*k*GCszR6Nt(|DzRO+8(9BjQXsa=9(YpovmsYT_? zF-7-1=Ln#I%U|bZ6bay~NFWkLB0cAbyNuMSiYtFf}GsqN16r!@YEWj8s>SzUg)`NiTmw7XL@!; z&-m9wJMWJTA1u8uMwC_4DqQ6`dNURWHAnA{f11n7Q58raBh$}&1)uwv{$!zAd)6C1 zrDUH4c@|m*VwG!K>M`x>bT}>wS0GtQ()>8u7e<s}W;Ms9SMGKN{K_F7!ztWjk^Fd0ZC`-weAoHHUSqo$Hm5Wr&os^ zqtF-*>2$lKj$)e7sIBYtB>T!%{zd8m)h^Ktu3Gc?H+-{?jZneWfz_#`en}moT8j3> z5q7jr>X7OO{Aj})HjHBAZ&WiL)nOP5w$xIqAc1~{M_TIM)Lv)NBfp`If^CG@K#gDF zj?orkkIPG0*IJKadsa#^sz3Uj!mR|WZrj21avKlOsTR7{H6OHP8dFlJKUQKdl%$QqbU4UdeQAV7w^2?0WDYVsSVl^P zm6i_rnXzS-E}IVtjUoF#?pZH z>m9W4>MGj$vHSFSo<_tS-EY(-KWm`#M9`n^4LWY#=$iosK+? zQzEG*>h5A}k?2y$KC3m>27A3LS|~Z(f~+6|_FeIqomRk<8AmOanTgY$ z;h<|8cN8jnbvj~Bec1T8uW>Rt$US$ev3$m&?Ixk@2~U0I`{`~pga&Lk2n>d*RP)p6 zCJ6OX+dfzH-T;Q;#-WP7Ffm8)5J-+!p&=6rZ3jXggJ0#bQ!gPw2tPLDn3Hxg<|C~g-eI~zd*Bbl#5}bK}JtG)B}aiM#@ynreGexE3Ylc_og^y$j7S7>VetXRn1w10?+S-t@deTk8MInPfN z3JW$~lf%9ictlw4srrIU`r?ZGkCpUa#qpm?`j`_ne_y#ptCW<>s6;JoU1?cl|M2b5{QPa} z+Rr_toBkf}?tb=u`S|u5@QDx}Wy9Vr^1%4v?a{ocTgwXSBWL64s;ir4A|~bP+D)E> z-9+Xs3{8Uj#SG(56{>%6`s?!_69AS8uASYSzn{lPSYrMx!WqXu)$`!};^@;DRfefC zrYcJY(Z&j2^nvj0QR@y~JNY`St}+B+h#kX6?g{6G)0TceDeN+30PoJbL#!kI^OV?+ z4;<_{r>O;y_}(;rnErC@09nKl9>Fwp(~7@a<9I$Uby0 z<$cCW_R!woCYbs=V9U0HWZ+F^%M7|ocdc-kQ3$>a=|&B%qr73ut+-q~Z}-FN(P*Kv zF;Q7r?P=C#rro!A{IAs0Xj6k$v-V1{u(vY^P)SCyNbpo%Hj)5yOARifB-<(tqiQg9 zobsNsnn}9d_o13W?R@w6L>SoZ^L{BFO%)SRR$3;9EKAGV*0deIf4;|&3yQe4pIy7X z*%Kv#K{0YBQmkCAoC%t=xHC~W!%z9u$JF6e07okohz(y;T0UkL{N8>RPN`K4m=LuL z&Mh5+=zLb%eXY96_H!s6Y1cg4syqt~cQxJv0L^%h4+6KBJS^_5v9WsRlJ z*if6d(dz|P%rD^L7AwvzA(~^Nd)cS^5L{k2KofZYfVN=01Gli`Tf#En!53af{kwak zADnY$)_u|#Ryvnm|FFPCeC9KQ320768|M2NiOiT(OP(?a&`A)vXjH(!eQqv@Cfc=8 zb=a;9qDI_?ZV7@-1j!;hvI&jnT0pdar8=-flZSShYS7TUl9h#~F;{?YVNG8lODYW| zBXAal;iaOGZq~y8S&T2QT{t(-TwI2?fX+mnMAB7iA_(S0mPUhMcwBH7gXP=^BjMDX zEYe>9V~n@nkH4<7_v_Y4xpTtkiU<&S2U4m&bAruKb@sYnS#=7Mur%Vxo5c+)B{f!SR1jpqO7_Q_dmq~CnIM(+IPN#B@Brv ze}W;3e`Vn{wTdx!V0J&nevZ+L-=YZ`TO`{as-amVoQ^1TQusBtF9Yn)9qn2g1i1p3vl0)m~k?Tr;E z<+1a0N`(#$fJZhT8PZgswld2kxgSzlMBY}(C4mK}VgTabgG0lv#lC~nuq3n{ z^$*LBQY?n5`wg{vi8p}!VUw_T<%)F=C92rPAZxN^jA=dBB8NEik&MP>VR!X+pT#7w zOJ~qg2(jtgHZ+dSh+zB(wb=v;O>Mmh+Z=Y45Q0_6C{r*l>RwmCR?qRDq>!t&6#8{K z6;j?BI%W;t1=ut~PeS*Om)Txa`K6|@ond#qgU;*cvW1UXE8z}P73$)uzs`_%S7&R7 z6EU&s=Y7Zl-jqw;?b;Hh0mkfazY4wBU-da^89;5Hnr4=)S++LhCSSDhTrYn^H2^)F zV`Y{O^ixun(c~9W@MjiU&sTanB+jf00kF=j7)SyKK#i!boO0pQJkd|$ty`?ltS75e z=9nptdOqivGD>LX*V!RwqQR6aHq$eXaAkY-j6y`lUg1?hc(6sm$gP%RgUZkVi zT3DiMw)Tx#lFW>&Iw`BQOr+|rr>3_y< zZ2!N7;D5Os*|`3jO}iECt+*|AMBjhs0D5!vpfIA}wf;>3?4_L#FG7fBm%<^n>{>7S z{P-aym3DHV;N-T19=)DMJD00Yd>;*?Y8%Y`HjCz+vW~SvCxO& zhbHU%EaU9#0v~aD^Qd(-Dr6Hf_?)XC96Vr5SL4v`ZjTY(J=$J^QjjBQsb(;{ov|9b z2@9$X)Cf$&YOR%2R*RF}rV9o!13`q;DzfeEXfi_s(Fy+s!ll$CMIQ$e0?Ne%BiE@Y z9J`}~6?#x~SA;r>9=;AMzy&iU#^m5^iWVHsBegw7P0R&3z#21N52-^WWJZ=>>3j;kat%f|d%TGt%Rk4+^T&frI;HoEU66?-H zB`jPQ6-j?zAkC!Ogt4BLmOi;$c3U@XQ_u^1d0FP5N=HE zXoDu+AoB5^#bHO!o_0f=fRnl`NX)a200q9@V6am3bG_xZX|7=@({0*A)I`ob0&WFw z-hZMVIsEGrSCO9u8mSycTp4YQa8j1^yS1@UwfK?dVgglX)j~zd6T3VEhe^Und*Hp~ z9T;tlzguh2%oU>*=Sb!kU0bUc(TZp&EQ0c7=n6u?C3v%2cqdMSkx}k=b%#<(T}$uM zymLJb{)XbXhM=xco>H|yJfB!qteZkin)y#ht6~vDG^7nK>cHEZk(_{<(HCx`_OQSu z!s`o7_YmOJAKl3Ad=T=kEhKOI5kzJ%qPe4M(slFLhePXr`7}%fKo5h)-s4(j7*Go0 zdWJ!l6fIOOcL`>|2mtvlE1!8jF$ZG?J_6<$2kse8$F!p}AF-++W`&p_b1`QckXl%3 z=UM6L;mN2}y`}aV3t>eQrKHLli>6H;wF*k=nub&w*d5T4DxsNBzI|LDqsr6RT185i zGYZ{X^RZ3Jgw{>^&TzjsE`?Sa45FtV(=N4#D`{~B!;o54MTJ`1V*ypmT~fi^ky(@I z=@CD#;3LB=q@lTv&E|Wp4>54q_CM-*zmB4HmBPtpM-`2Y!57SnX9*TDZ*^1ebRz1O0mMdmz*DOQ1*dkcwN3^X zlS_s+kJLYCchb+~14eWX0&4nPWQ4hUnT>a{y~+oYRKr=`#_yJQEA+GSeNyS06|cE4Pn zjq<$hXyRQMaJ%O6Tz~t-002-D`|dKGY4Z^k*U52^RO9#?pwxH@=?5`VLJPl1f4FJk6`89#u3BlpWIwv#laye7Gf zUhPYnF0LO`3ExdvkAyZ0hB%E4lmSvPB?qV2gN^Ri2uW9Jb-R)goo_^S=o-FLOZrK5ZJ!f^@!Dj0c$~clzjmaZ1CzNKcxY`a#jvhi z@w-?+b8r3T;YnO@eTrszl*C-O#puBvJ6(63b<0)2M#`5w^{?c_2A?x$8B7^Qe0P5$ z9s1>jZzw7o%~3Wo(s2BYP9d0E=Y0=? zNZ%0%hH9U&nche_37&07IV!b+Z!yA+&zFq#6^eC+RV&-?rvvm60{H2HE-Luj@ge2o zi4YO<<>Nui0VTKoKjTF-;|C9ze+kE*@%4%QVv*!w7PxIJfF>E(;Q=aFf6@$Pn9oKp zyXvF&*~!FL6ZbgR0vHBO?}hEfkdVSdm95PzU#Wlj89gR+z}}N=X~d zYYkiE>nPeu>G$JH;1w1Bc{jf0id0xeF|;Q)zqRy!Rwl(R8xet@jCM0=ki3h*x5^Y2o^&^BaYaOg3wEuUvqIL@y#(2r9w&>3yo zzA>SQ(q})z8nGE$ggv&sV#PxzAdTF*S&7%fi2bcof(uKT<^QAs{$1Ap)#Kn|`(GM> zo%uhk-mtU$H>)?-+M3b063Bn;f(s4yVN|Baiki?OB60)xzQ7JN&Ic6v@<(S1{YL$z} zG$%$N(&&1LS%0o-2@2!{xxC>SBl;)k2~o%@kdFl(?i##u>RPfXl9Z&*-E$;arjDIS zCEhzfB|N20aG@1O2CN2iMHAunom4EicwOwW=4a;)#h?PMEL00BUk0+r4X=kYaH`1B zj8JBBC;%ypC>cvJ0s||qg$opDoF4h(t`z7oe*uH@A^>4N@1wKx+GTP)b3Mm4KlCs_ zfe11sBF!Ik(JzMz0VRKvw#v_m9^AV|%sb4+BHjZJg778`@L&U^_gT{))?rWui8jSx zvLQAxs3>u}B{h+kHm+p@#$!$3C_B1rVj?|+J_;$JTxhBIm4S*BP(c!g3H6wPsG@X< zfH1b}#|KcsS<-_Vk2;enZ%2{d>SuaT0qv0pAnLbnH?D)^zHh(Kp+b4Z%L9)XWtL(BsNOu)P#$$tQ@KkzM-$J1h5%*9*n)D5mRllSqko zs`XWPn%bO979eJUc=pT$4V4|SEOCyN*Gg?jh-jpv_6QdzG;v&24x-rO7_-lHnwX-J zag@#K$VFlM1-IaB6lcaT7HY?o2FX6FHZXj!6pj#2;2*bqCsaPlMQ@$5qzc~LELC+0 zQ%OSvK`~^=sPqWc0AN{KP(gKM^V3A4NhusW-x--)} zp^)Y)V!&m;?&M$EkD@il#(DY^E85O?ztPf6p?n2r@eeP6H+TTAqzA^l|GtM zYwHQSMza6blf%+RTd1AYiw_LGd*zAO?5IfSqsl!iQoOFu38aJNHOkZ$kMh6^IbeM# zT7v(c7CCfc z)>LV@!qPS)_4)Da&?FErFWIe_|KxDQkx@f7*$^e$XOx8Xqi)4VxF($>hJ*$^;f5br zano%f%FM`+9OF&X`-}39EXEYz(Yrm5&7DyV5rX2q5wQJ?Km^|R^<+nsfYqbb(nZW} zxjLBwcy4BEa3#R-#|e(GQG_x@>S80Pq^6+=rg1F`pp%<>D6q@jZqF;cfnEZ`lpD6! zt6gCXJ7aZTQ2sq8J@24a5a+dpuz)AJng>uWz|}Ixx=dV;^WFO0&(Y zrvKHPj0g3^rSXeRp2pxkl} zCN#4vDWHb5a_v+5ApN(Ttq_>Ox z009$C^SNJ4C{47<;vBX9z>liJndV=wxzP%GMKdHiQ zbLzTE`SosoU~HWu*vWW_XVQ2y>lZTvL-iOV*KW;+vDIyrpH7xsGpU=;xUu=nUY2aU zyir8HiP;X=SGOd~7$DL1MNJ#|=}Dd4)^t2p-s5+E+d!q$t z?!|9A^-L~E8b5Nb#v^DJS_pp8LZY1=S+g5?F#YUi-S@Vy)i_Wpv=K9`c53Rek;wj7 z;SkKM^4P+Np}r>7QW;!j&7S$eYxVb;b}DVfQCy`nm*Y+%~4ez!PAS^7$#;fHAUG3GkImL__ zxPE>{5TxgYbaD@$QPzVI+LoZs)KY@2hgs-a5mx#1lp~Gt7{7q+y=pG@BF%tzV1;LdsJlNkvOYz>$LeHdc4y$e4bS(cZ#^LLK*v&I)rIF+}5C_4ua61`@1>|H4`qIuw19+TjCoBWOQo@ z)G(Ag>>L%2Pjh}Crq=uYadK@EFJ#fSU&E*s!$-J!|;0D=`o? zDsZ4INn~1Zc~{`njluecaIRNVxb<{^{eiA)Nx4Qg$My^s0DL)BXON>zjaS_@wZ?$9 zj(nzWH#jiTlqE~1eC^$rzOo7xCtwb_s0fy_^S)*B}bNgZV3`yFa zwf3L1;lJhHzxqS0od1&!#LU6=e-Dbl@%4Lm zykC3v`g>}8cA8nQGVa!~2`*u2WY2zoU-&&fJYOPIqx3x<{yDk1UwVI^IUSuEMgXvF z**yd9j6ZHKuC4sLnQ^aiR%`9|YV>nfu-D(742MxcSDAt7asq?R%-sx}yx#%8T)Mr) zv>+$BBiK3Wy&rNc+su>jGKGc*+(ecD@8R8ZxB2}?SLNO1-R(c!^ZovgG6)2CpNk!> zSZ7s1t!(Ehv|f=y3GN@m^4oCBTSMW`ui^Iw;;_8W8hQiagS%YGyFZAZe3Ti-l6%yzj?@WTCwfgIor`U6F=58WnnR!9@ESZmP*qF~TAJij>}I^R_Q-ts5F!D0~h2IOYpjM0Gw zA}~1Lg%HZaVLo!aM%WZv8}O&`ru!$-<8IrHUML}0Qn5EG^-4<1 znAYXftRWQ%?N8(FWugmxK5`w;->^csW4A%}6f8htwTUC;_R>G~8G)qUg0P$d)(pLN zsd(kxeWp7weDLIYBVa_re{jFU8(IR;O5d&Rh*@4D&O6SBtBHQ`VbNxn`ODDf zlg~V3mm&Vj7++FEi*)5SbPfSY9T^l`N{a#U&gN9u(9lbyf2Gr}UC9}QkK_9jinZrLDDB?a6fI72F0vY zgbYsL4-o8>&oH9Oe|a@vZ7PwOl|*L2em}vK_@&#;TI{66@p!OubVnTahdseZ=@~@~ zfZM@ERmIx@PJp2!;-NmuOo5ij0dH%WrA4JNhvnvU_`OfPJW7?7>{RMrr%>E<^WFL& zSBt0g_;_$~bVskt<2&M*xxTq`>_?HLkE8N7|H0T=s%c*8Uhz^)BRwI?we0VjG{r?7 zKMYOg&8z&KE<({l-!wU7I8@ypReNrmUA#a*H831OWh=t=Bcrw1Wpqun69fbqEdWrO zmqv)BST6I(UXvQ}yn##$VVYJ_4qj`s<=9<9X%}F*N2Yx>l~t087ZcSRz0R_&x?tU{ zdCa9oC&gB61kJpam@4{0jfpC3z894ZJJbS~Qk7lH2+TeI1aq641|hsmTCjZBCH)X3 z2(LSfdw@_8&%{N7ZGMV%eCN_izY06RP62%c~U03$FSth{5pdT!wBD{cA z>_J^7b5d8JIf+zC&XDZU%~(RmklJdYNrV2xv{pWfgzhqiVI@0h*Ifmx(}m4VL#(a zt=|x;R@R>gTj=W8|J=!(Q7D3J0C)lMJ>A`0)VnBIybn2B5(bxi5 zIR!b9^Tl~v9OxYd<%8)1;j^B#pvnNh)Z#_fjfIGCF>LF%!W4_5~DO;|TgQ|NN}hJ=#9No)m>$2lT3Cvc%=idAp>q>02Hq0(Gvd!JOH zxx?Io*bzODtV_!zCf{e%nm$uUOH|%th7AsLCi?g_lvu=cUh0IAVtvkpy^p4qWw z4uLzb$gm$QLp7ek4=<3|mX3qVlPR|TPrdGvVX>S7UmsSt-T7e9-kY`7PD?4rZWPQNQeslp^i z{n8&8`wpV{KMAORYodP%C}#Hm5@)YflW4t>x-|)w>o_!@DoJu=FaMR zdf#50-}hN~y96Bm=AatdwpM{4<>9-M6thx74&0L%f`Sa{=SuYO1&jP? zRrFNOkp0&3iWm}%hzrg&h(d>79U&M4L1#TuPy$9c@QV~8)_6P!z=#T@5&>AoKkXb*~wJ-{JM&bF+89ICzo$Ntkm25XGHf{j=;!ocAY3oda0ku_|= z0Bk#s2nCZC8S)%!e!Tc$1vf~Va9$D>6U-6`bAjc_b%E@bQN{AXivE;PgG5UK4;~7`g?t%}Yg&l(S5QKt$GOpsc!a1XA~7-| zo@ij*!kCnY?|?U266F~XimULQWQd0#%2C_Rj2DF>G8lX~SY7|1f<**TeECdxbs#ay zLz6O;IO)E>h|;D*1K9rbdY8F{kP7$t5OkD{?c6g%{jL)uur2Ip==A7_Lq#E$i0U)K+3nAIsK=>SC%>MY z{?Ev@nKfV&;i68&PCKT|;g6QT-Aeb8?cpB}Sbwp9?KTo|-X9iE+CC-I&UQCOdP>iM z9QR1WMkam=--rMqj=2odMW6M-Yh9ke>-Kp)t?4QPQ+Ozj721&^ZJJUsB0H^qd6_zuUwv@d`&31U4_%;o3DrLon@jxbg}PegeHRAu@P?ZRYao5-Af*90l zbwm{yI14*})E9d1HJMt{vuWtCXMj9ZT`C8IKNiZxCOCFK>flyI%Hb;WrrZAvbI^2| zaENW-#+nSok0onLuUAox3G1UulsO_+WPWuez}ehmDC26ZX3cO>r-=2*Ap zx^3;XCGh!dME$+Ww&=`3DQqfJ3rctj_4d)Q}szhssTzDal8W)=uQSirnffb?+H zX<9brT?k+?py3%{XQ?Q+o!=nCWhb=RoX|}sxYt}p@QL+Ir$@U1Kz@!aCZP9lwd*rZumP@=IE@Y7TSP z>Ro^W2(D?|1c=DK`0mF{IhYs@kzrr3Qq~uM|0v~&2^%cdodZFD&po_8o}=Hr#+ZjZY}TO<$9wm&`1}3O+v)pU z_cMn%=HubVU!1u8t=`^F@5|fENu}}o=K(5ynEy|E`ybC7y+E&gINk5+JJHcMoR>Fy z+e|a?-3=rdHYR*$cYa3YpP%&{XYbkey?>T2|0c-wd4I_^*yHEn@AC6~JhAlj`*?ig zQXDUb1mSQTH6;7VUz~j$$WQck7%%1RIl?o@UfG}E@!TAoH1&t(d%bsW?+w`(`&RD& zz1R91Od`bG02}tRJ(arW#}a^}G%y~T^e=dZc)!?4U_N_F3)~0lFCGOi=Q#F#-+$4R zBueiURsM};%eA!1X%2k6a;R-H)!r&2a6h70W*6B!IP2}u>L-L2{gC*qYV-RWtfAFk z%1@)2mBPkqlDi)IY(QsC(@MFf;50Z^YJdVRW5)Rbxu8za<~R6}b?aj4$8Y8exy8w% zdF5RVo9ZXPjrv!%WkmEfb%Ksh0FU&#LV+N5II6|n1v9)VaFG_+n@>#`VD~7*i=`r% zbH2K%HO?`fU=8Yd{Q*M?YVQH_+bqO`sd$)k0+ntmrr&7q_{{jDS@LNpdv-(uu6NV9 zx!-}nUF51@P@Y=dBk`l{(UYT%#q`aQwi!ARLvvjDja7B$e*iTioH z#b59_fV4g~NXyiyLS?64vISf*Hufw?2ViV;r>#;J+1c{|_1k)MxKO8DxBN5xMo zi>Aze)t*A3#Z8apGA-9T_oIlivoivthwy{@B79o4DZ@fI2pKscMmD(|TC=zzc`TWp zoIQo9#XSIQ#}*11jmG&G+mPJDgh7=eG=Hhhk%0`Y#$ku!ohe6y$n_*fSf4~_@!d9)GVsZF*e;9M;tR(I`erC^OhXkBbFNoMVK3`3S6oi zsXz?8o6cfV2Y5bOiKd{}Ei&fhi{>%Hf{L@1%`L#m#6OvFDWID->L0n3pOR#kuom!C z=9L|{>_1jYV?9}@>aBw+2ELyQvOSYjOC3GEqNm=-?&~W9FHpfBORbC;{Tm`BTQe0V z+F1M}3TH&sMF@q&%_n&n_!6$th;+NKlrgsII9yg%$bTZ(Ve9 zqz>eRlQ3=dIW@=mdhOcZZgoHm^phWRAC4+m-;)$)jZgB+9$zALN>FE-#m(oT|QheRS7El$ejKla&!7kRzb zsFWdSj`7>kU6&yIC`-!%#fb<5ETKF6K~l^?WM|rln`T~@owhz%GnHSorslBV94XV6 zTq(zve&a^jI!7bXF29%cTcOqOqp-1qYJ`S6_g__14gMK1P?murf;;@>iN{!>_r`D*Ak{oZU~=^D_P?2Yv`)qS(F<<8~RPu-zo zOR}ZqhljV<0V$ff+oeN+Mv*?(9uJ${E|9WfF&-XW##1qk4XMN}5cc;qS$g6>QYmnpP(B45for*O$iBUPF50ox<0ZVCUvZ2{+XM1>I z+jGFVIpvbR!CI+-`4O^9rZgT4p^$|G90F~vwq{=z}KP4qCKD^!2(n1N@YET>Q zLYz8$)rnus+Pbqs6x5-MYK6Vr7S zPFNN75PD;+A_3K>0W2LTWq&R@{1_JzsHES8$xWEBoL1pb1rer_C@)qSfiW0GN9$P_ zKrfvG^hfm=q8r8gX;qil3*w2qeyev>uFPCRo zC3XLm6rL=)c9BWPXAaqdGTOX!ey_L!f!bfDAcEjC6l~Cp^?Zy)ZEQt`bKkj3x?h8J zyDUo&I#)G~(Po!zJb`F_fcBNjTB;JS68t6D0Y)*aajy+YVNjnl!^>H7M}6p^=;kZy z{t!g|QprzK1$8)=R7h4wH73J!m~t$`q*GcW=gfJ+Iv|?wrE5v0@owz7?9ftLFA7Ra z$!B8;T)*y=79(6LDSmw8a2iRT597-b6?hJvgsv|7hIL zJVBsZxKqG=%45YcFW4s2y@-mTqj@OU%?vu8A%fEhCC9ke6wOsc2-IGvJnx>Nj0rEW zNl`j$KrDw-aiFP@nGN;@PnFSnp_>RWngu1F_gplV0CHJv!e$!+3Nno*chy3))z&&g zBI6niqdl8Y-O~2wU<=&HR5U#I65n@%7Kj@?L<7?Bks%^5fsY zeiS+lDMvV`by20ZnxW536|tCwd^| zZf784Y0qQHA#sPX;Y`WVdxM*+3AXn5-DV=X($ur~_5=k&9o z>*-uf0YiSA{f3ovp%~11KXe|D#PL)OT%|_zDi}vZcWI#H=~On}qc?Yx2c4ZkJ&8tq z&02GGnaIH93+K=ES{OtbkwsFdR{H4nl4QPF$*?PEgGHh(&S!XH~c-0 zE4OehuD9;BF*W+FP?L}YbO%%0-a})~2J{+PZY9$YpfD>R6HgCNz;TPadBDwnj;CP5 zZRqUjWZZU@8>4h^n`2S}?(5qtvO2!7%>kLlwQ-k+dWvAIR~S|iXUO8kK(Rrm?!4N1 z2~cF}+LeuvPDBcT$_I0YeFvY;H#yvT+-e5vnGK-{kte@Y4Tz?3o&?{g`#j%5l^h@J zuof2@PxBN!3ZzQeTRK$WPy@}b@=+=`j9&?W+eVfRe68@dmiLIW6wh}zNp=U6U1dBB z)M5<4u09Y@EqVE`{e_78_PT9X`i=Es7dZo9KBH@AszSOTO*QF8Ex;*~V)DI`6o!gK zF;n)wM<;N=>$6pSGjNj>tEn%Un-V?}Fda>t^c3Zy?=* zhOG&RX+(y1BlZ&Xg~(&XiKeg?DMEGc9Ob&mo60Dr96WU%PmV+$1DG-hkND05R*>J# z_(3fgNB#Hw+6-6h%2xZ4y4JZ~$nS`1dd7-_tNsT9o=)SyX%Ao1AP7>*UFNIWt`7UQ zA)GvwmVLbN>@R~f7x(68BsGwTKky7}qKaujpQ{l7wLLe^gos?r%3DQ7)+qXke8Od6 zd6-l!PJ&wRZ8O(zvJ%^=nh47PTmBjMG1I8MOp!FD(vSsS&Ede~m)8`W^O_OvP$75S z`8$HZtajoEKLix8*2y26A7Ts#b#d4azP)R-tE}vHbl$r4w62A%@EtBvmGST9llrIb zs&`ZRTrx_Ui6Sy<)euJbRk1~2p;&v?)f}}a7%(S_3kJRL!QNyaRVD{xl4fCWBnQw8 zEDs6J`p(Vd$hAh??7Fl;RW#!2a|ON!8BNdM0ZQd-0jV@|UtXLn?p+Rgs2qI4T* zQ1HpNqfRqIM`h;OD*~praAOIpZayWaOZN#;XEas{&S<&;VZ%%w{In4lfS=Qc>?t%! zZ7W!11qs>5KVz!leZh3K^~ZxUEcu;d>XKIOg4U=OysA*a?+oPutCVB|GAJLq^>tKW)q^}R1*g?#>V*m+B`_Z~U?l0YF-Cl+?N!o;=$d`_YcwOzxIlRyfNT>51@3BX=?LUd4 z?b%mRgu0B%52qd1Q-y?Yb04AQ09xmR9E#ASzxv~zb}!0P^UCN|kH6U|oZV6m;?aK( zg(+`(iA(B_(y6_-c141{04T>euR}c;fMnslF7IrY%F_`{wcMt>8*7%$w(Xvjbm*2K zlqC}wUoq7Aml0(kKA#1kOrzWX^^p^P!TF}e9-ok)Va3@zb^S_X=j-^a!L(;L;N)}E z1b>2t5jR0G^oKJ=?)?g%gg(64c;Y-ulB|u?pZtp%8s7e}Gfa)SP zf0t)$50RK5ju_~DtYmuzmZ+fb4nRE2?g@xrG9^IrZ8nCmN?7raSDf`86bNR%1SSrv zO>L{dwWbVEmA`63T6aVQ#QwD8Yc|_ERegp{0oJ)m#rTG+h7v*GK0?=|G)yz$9>)F7 z?z0w^J1sD;H@rC`#?;3fimwH)v&UCD+{VlB0bplbdioEl>HiZ#|5Qzk zEdO3-VPg0vQ1JhrtNkthH+H8cjSZWPzY(=7stBi=fD>GvB!B_{&QzHA{0o?;LUZ5+ z_VXZh3GHy}-`=Klo7&aEQX0;4N{A#5w5F!hlhf%{d2g1^f59l;Zmu62iVdD!x?gj( z!BrTxSg^I%FwDgdH!-e%!!-k%)T^nHr8|?S zVJaMq@yiFit8zUy|ls$pc3v$0Y)%*AE*$-Fuf? zAtQV_V6J-eI8__Y7hS)$Gjv&J`^=%SmN(EEzMhViO&-1Q0xSe zVJ_5SZm`+&oee_au(5T(KZm<9Beu}V#z_Pyj%Q_G1OME@Hb!dZ3_eqKOMV2^)2(-S z4zXGFpe%n;)d9XFcv{?C^Qm)CZ=T?ueX`AOPgk+ezDD_YA&g%IJVSaq&R+6Tx~>px z9~H8H&5E)}kh}76Wx&m8vD;)FQsGW=k;r8(&pDWU3(SUuy9M?kJ8;8%>FI#@N5WYY z9Xj1obZ89;Mlpawm$BgMoiC5+1av)LM_8+g1zsKkI5dL^sJ0P@94N;#-?Q4VFHj6sFPuif-i8WT1N3i=$Y|~K1yk7{FUFGZUTY#oT5zHl zsY@xB^uJ}!C{WW1su|;=U2{A6y7NrC=FF%wmiK|g0+u6u)o;{psSKw^M#d#i9XLsv zWtY&ybFd^hqqN`gDnkbq`=TjSC1DEiEkc=C4yh;gT29O6-gWS#zESFvS1E)kT(rs} zpwpB>VQ?)yl!q&F;p7U!TVX&`G6FPXH)pQ10t*Nfm zSsI@I_GPFg2lBOoFs2%T(qRTconwmqDunl`uHB)z9OoipzbMYuUQ_UjgtED8tMAp) zNENEOfRdbh&Bni&W|xx9RY!t$yOWYbwSkBvC+=~gK<~h6-ZsPj{6L_~5`BL5?1}DE#iWC~w6>DC)9`{n2yr}`- zklgaJt)gDS=9ks@xuG^@+BYC@9y!eH^q*-T#-}#wLeU?&(=WoEF|Q+7m+c3GG82ir{OPQkDTQ_T^88n&}Mu(ZZv8?UwLHf(p}+)ogluKNHTnsm5bd zShIV$GS;eF@#8KG^{_C#U&KBT+MKrvbA;luap*L}dd=g@^Gee2N5r)L)D7l<1rYJS zIeAby{ZPf9|E7KTlPrFQpUK$C;+Qb!cs|p3~8}&R62gIh2V-t5- z3_6yx^r0i+TAF>tq#Q>UOSJjNHs&FDQk-ydYwoWM34p{K~Ac9wuot2HX4rY@;`v!2_7TZF0)Nb5aeCIH5`df5-I0JNG?ar^&2qo9YTN6@>`6oVZ-a zy2wInS}LY(2grjAH%!``)^$}_wv9%VZp}uXLSdQTz%(cI@z~*S<$&uM*vvrXr2x`1 zblR3s^M1d?L86-)V0wku2Gv0r~yYRqmk1bEWo#FYKaPAjbFsNK4N_+b7Z;7++EV_az@*L~gKxZ|Oy zi+QRCF!0PfDI1Fz35$B!8--uJUE{glMAz8<%{cjbvvz%d_Ig>t*{I_Q_*25M1CO)g z9Psh;^mZN7v)#3+ZNK<8<0Pm1`|;>)Z*S`_k4D?&(vDsGbK=$g%gNQF^8(<^)oPXJ z&uSOeQgp-C!3cQqYEV?CArbI!8aMa1ovU}}M|9x?MLV{dtQvT~zj-vD_^Se97q^es z`$tNPF5t)4!o@<#N=eSQ|IP@W?lur!?km7bvRn^Zbb?*xHs7sZ4ET3%^s%aID|q6# zS6903zTFHS|L}>aV^o110`~O~@M_I9jWrm|@w)+TTTOxcV4CO9EFjzJHaxQk{+{T{ z!rWf^OLS>gQqgb|j((}5ahqUfP?Evp436{d%vKZ9Fk-VIfrMGG!}ZP)W|EQUTi6zK z4G>NZ!FY(rfMq5c@$oG_e$I6h;;i(_dFXOq4sOqy5kS5*fJZD6@U5fc1^p_=YS1|8 zB^e@fm?O>sds+NCX8T~CjgppxEhr;;`ibEpuBa$ddn*d;497qag|Y%ix)o>b^54Hq zj;jv0OFCcL)XBJOX=5?6zG)r4slEohXLmcca@lwh^=4~c(|vCHi%@t<^Jvs;&BjiD zdydoiXm;dLrK)!!2Jbt_!Ta;`y@fp0#_|{Er}j9>5a9SzOg{--?iW^3<8uBc-9xtP z^W51vZ)+9CuP@!qlXK!gv($#f%X&%uVUjavRgSKuBkrop^(dON1q& zoxMK@lnr0UP>QCI72!n^&m2A=gfyjvG&KOIcB*mpd5xMYkyyDKW8>=n08Bw)ax#%V zyvXWDrx`d(*?Mtg2v4XzjSLLy>NIz@o0Ab8nIhLmaVi^JYWKm}H}NtZsrMj7MT%sjGS@I_3NxD3`KAef#6Cbgl)@=mFpn}ggrvUM4SvjpDf&0a8$rs zM@qtFQ z73Y<&r<0QV$TA|C+9)hpDDAeLbQSQJPpJ$D_Y2YBxnQ#nkP_fTLqqv`vTMu#;P=eb zmSd=u_xYhtGL+VMWw{{>!cD3s=Qu%M`+6$%|ws4HG0n?A{sP!b!;A0M9r|Bz_d-fOy^l% z$2u8%hwf1xfiveKr8N47Z)<5aHgI|vj{|9&9<2<6&e$;?#!%5ybVDFAKKS-*3j53#1OmTYk5jmqmprtRsy?YAzaVkH${G3<(-nQvL`WkK zf_x!~GEc85gD&MP9L9dyxVAI(vC9NUG)}+r0&8%2ML8S>$%c)o;M4Kal#+ALWqK6Z zg#Bk{_H$=@R*5OYioUn|`89@hmOT6;tJRu&5v9TI&tc8V72;oZhb z34D$&uL!MCA%+Dl0|ogC2`Ot)^eF6a##TET&HF+f8-VP|xq)Tl$FzzFHuv`QeDO={ z5SRsEVR05ge#&1m$r~_vFE@%iy+e6<(w7hF0x)_QQX@cw)9kz{=&co(iF0G0G#T)? zNJ!YE3NAhDzVL+nOG&PYKz8sH)QiEQwBB5#0f)mVD##+JmpIZJPP^&#(F~wCwqR=@ zbQ06NgFvz7NTkMTe>BOUv7>~CE~#FzS%wI4SRJn+m57@JU9St7huF(f2+tRvG}|_g ztSalZ#l!p4bQUBvykFe&E|pCM)t}>|AFF_Z9Ex4aOoX#&USllRD|IH_*8mp?Z?aF; zYZoB8UG;54#x-J=3m1vqBd?(okLZcco=e;MkFPhEWBy zQMXR)sQeR%9ZT(*xm@RK8MqYf!_E#E`jqRsBs(uNrQbJyybwgnMYDCrNYk1%=_D4W z4Ql{hnM^?&bk(`M%hD3-1Zbrtjfpb)*-e?o5wlg*v^(Gd`S;8~A7$&QhhMF{6ckw+ zK(}nfZ}YBgFghvc{aMxD8wGp7{VzY-tXil1r9Xl#jz!XmR)YpKAWxA*XY*%fOX;N7 zg(XRCQoo_3t>rA+SXsM0(k-+w~E+K9@eV5nrz;?z^*OjpWdBcJd4HM2`rP|Nwg-y zL5Lt_RG#W2`viFfo<`Tb&aNDT%#mpJ!au#BW1Waq#`46b3vFvVCG4XZ3cW)~q?7;@ zDs}h(nA9v5kFex_)-Uk#qZj@g)T4k6Dd7UNJ(H~r<~t5i;TnS6RAufs~MrACA5Vs@$^J%VBH2j!v&EG`T|ft>+)SdE>7A+C}@DB{H(r@y~Ym4bhjaK8>TsP(e402hiMw00QM4&(G1IgiRC$2PBrYN zo*iJOkYZy83@e~q=m?1>Y2QK>??2tE2ewbNsNh`T!mB2N8;uxysd7)GOgLqYyw0zS zjJ)mk%+TRw>uzToBJ#xEdDCxbX@#F}hSn)NxjQ-;={dXsck!@TY%`D@d3xH?my|L* zU0r3rq`BHZ&NWKDhVqphGZZ4qeKF#7cvYEd4iY7xlHnGwdUtu5X=Uk|WoNN;GN5Z4?h&iv_hud` z^iD;KwNe43_E3Fl`xzGPURTG--CA`HwlGoVPPqZ*(;6HX6}?zSH463A7^C2D<{w`R z*VoSDCS=GM4b{>LXBxA*#x_;@V}2kVANAHLNjmu52Z`+2L0iTSK-% zJk*Lvp%k@Mz%e7fX9H3tuJhE4mT_KG(R_)p*&}H7XUVwk|tY z&~-YtFDw-A?wwt$Iyy@Jt?K#aPWJA~ot)=D!uIYDHYATJZ|Zc55H(LTDK&I(DQLDi zdDxLuWP^yF?PFD|5ESx>f-*nipqM22wSrRoIMw;Z75EtytJ_o}j!S33vGM{QoCg&ds?q*Woggr-L4-%?3p@AKwO$P*QK?VoEkf7D{x_ zKJE?%6c3Z7=&kk2fcUH=VauDG-0Rc_!dGdCQF2~Uj9a@k4GWN7j}>4(@QmH1qR7GTXyWybJv5l7MN~ zq`2*G&>I!MuGXjpT_&Z!qb~Sk#KxJR#TexvnIgO?B-dx5yjsM;sX? z{yb7e?6_P+urW@@MqKs;PlQuGj&v*wb$4cWW?T0gY6;HbQIYOhzKw}Cf}R$-@|0IG zeLuuDQ_ZHfc&C##*G;Y+byxv%VW>+EZV8(YnU7Vt1+V6t&}V0p=NlI%`d~;%qOFk= zKH16%DaN)&PSq8@B@UWz_POm8T_x+a+RG>)CbWtQ!iY~uUA}!9RFeI0p<`dBk20o=qELV7| zHr_f;l2TevIR%`|T04!CFbajxz?ks&2-POm`LxN_smCb`GcnR2rG&R{iZ$};lY-?m z5wMkQf%Svi$J=+i2m?0WB*jHKFC9F%6--@IKBIQ~76eqX^`zEFaKj2iI*9PCWaVvj zSEUd>WLVp|foPdUK}(hKfDJ*g%KRj|UkRs*(M0wO zd@|#;*Xxu7YExX5E`UX3BMJ3iK-VOlW^9gU@8hUTGffdVz3OPOhv6X_tjgnPMl*bA z=;?A*)P3L@p{Vs!>gJof%pF&d9+)EBAxv5l+1LygFLI)tJiV#MZ|c#Hv!DXj0g>>a zMEdT+!V+!ftw4%y5?B+J)tH5jl_dAQVe`u^OAg>QL(Zhc$c*Fy#HEken$o4tNR~)CSp6FF+ zKQzC;)1IIkY>#a=%hdCbStmq47;JGA)#ZELgGPypd zd|)i1Sg6YmymA0Tshmm*GvzLhgaL{?#9j+0C3+U$U<(SRT`4eLRY6=?n;pQH4K1~U zc3ukCym9IWN@XkUVz4MQJmIGT$RtFYI!UEh0q1G%li!$;Pq0a*@9=&oNs_y~6So{} z$aF=GGwa2hfM(N%4OT%!Pcx^y%%ARDglkTQjFTxo3 zq6RqMp+y+tTAs6;fOwX%H+#J(jWCR`n{V%*=S8;|G8ryfMllWQ&%qdrqT-E4_#loY zgR)tKxgW@h)Z^^v`e9p5Jrurf=NU=x^wg_&JKi4H6@DPz+*=jr0Z@Ma^h|!hcTI9Z zuUZv971)34dU2V$m#z%6+{HbmcygG&^=UaBVGN`?lCqY4{#=@oHovdRTG>YQ02bgE zbNwg%@GnX5uel_{zj07ZOpL7m%X{#*$o5}&53ba$Ee}Ky{uLhKpR~1xX5NR6-IIum z{iq?tm-vTX#$hV%u-Zm)!)Ip==Ss3(G#({WUa~E-YQ^2e)g*h(l*S?Li=#sWC+Fq{ zu7}%n$M*XhYH;i8?&V}?|NQHH zLxwbY;zGt4@CS!)y85L72%#`Gw!+FMLHkOHRHV7*!#r;Z5!Ik65!(D#sMnzy)u3xI zX3HVE1OI?epFaD}=%`#9S&1OXz^wEc4WNH9U#_wF^~J3z|%l zn=<)B4mMN;0wjo^9DIIt-ekw;V9u-dcDh5_BnN@6VcM;6B(08h4YChrK-lt*x$GEeqc zyuxpHkfzL$K&HdVPid`zJ^%AC7=b_mYoI~aHvTqi1Ls1Hb579hel0jrcIoCk1x&oa zBe1;02^=nWXzFGa0;Q&4le?Smv*YKpw`WpJI`)HUv|vyH7nyo0DC#DZKfoqQ1MwS> z2#T}gf0%zCFE;`Pi9T?d|56FyZ>5m0MjLM>WHgNmge)rjfzj zK7L?nuvx~&<_6A(o7*tV%at9M7Z+al&(OivNr?yG;Ic8x!m+{yo=>-R4Nq*(KGhYg zNAQf5dq4m-dXD)o1eFF2lqE`YbLJ%(4fc_v8>bsJ4SDhMqO;bOmD>b{WGHgT z+zqIT@~lh{;K$s);p2yKa{&7$HJZBHUnt09wEFU9myfBn+(n6cAtM*LXSvj26i7}I zXs*;KNnvCh zD?%$rt^hc1!qb`ib3=`usvFrQzRSkz{_>_dLzsCpTa@CQA*syTJ9sd1EzfHh(!Bn1 z_o93m0kEpM#(c*)paP|qU}0mZOE94(q!x8(NP?4; zM=d+ATLezImDdkF^gM%5OK_j=rleT4VaRLr4~nInIqT2zS6zgpgVWV|lw0yRJ>Htt zD%rHk1gKxy0pubeyd^rE$hzq#;EMIm*N3R8X&>#-Gl!J6Noo?U7%?7^_c*U&YZnn* zV?V#*y!nTo)xyiE=fMDLLg-Jve*a#cv169pMddx<#@<}L^wkLn)j1bohRwr|hAY-B!`YcX%0O-F9ybtuBS<-N zn}J*CfWBMjME|vc+v+jlOokI&Rkw)Z1ZR2W)y(pFLnv^{ETb!UmP$Z55Uu3&$Nx?D z{Bxu*dT&Ee91t0vVoOKl0{xFqXrnCB*VSOOeupTDODber==yJjKd0dqC7e}D(;=65 z)9)S z9p(T4J=AmIMerg`*kod5=C8o4f4YSfkexB+=dTGx{1TI`viBy_n1meEA%}J{E{^x^ zwzl40Mht%5Yu_65Kⅆ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

V=zF6OD_1` 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ÿUm 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 SFfD`O(=UGU5C8P@r~d;6?8bKh diff --git a/examples/asap7_small_ff.lib.gz b/examples/asap7_small_ff.lib.gz new file mode 100644 index 0000000000000000000000000000000000000000..3bd6e823f6a151dd914df3c93ebbe25ecd3dcc95 GIT binary patch literal 72203 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 literal 0 HcmV?d00001 diff --git a/examples/asap7_small_ss.lib.gz b/examples/asap7_small_ss.lib.gz new file mode 100644 index 0000000000000000000000000000000000000000..8041fb4aefbb18e83cab4adb6d0f88bc08c431e6 GIT binary patch literal 72452 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 literal 0 HcmV?d00001 diff --git a/examples/mcmm2_mode1.sdc b/examples/mcmm2_mode1.sdc new file mode 100644 index 000000000..d84bb5793 --- /dev/null +++ b/examples/mcmm2_mode1.sdc @@ -0,0 +1,2 @@ +create_clock -name m1_clk -period 1000 {clk1 clk2 clk3} +set_input_delay -clock m1_clk 100 {in1 in2} diff --git a/examples/mcmm2_mode2.sdc b/examples/mcmm2_mode2.sdc new file mode 100644 index 000000000..1eee9b1fd --- /dev/null +++ b/examples/mcmm2_mode2.sdc @@ -0,0 +1,2 @@ +create_clock -name m2_clk -period 500 {clk1 clk3} +set_output_delay -clock m2_clk 100 out diff --git a/examples/mcmm3.tcl b/examples/mcmm3.tcl new file mode 100644 index 000000000..a846c2e22 --- /dev/null +++ b/examples/mcmm3.tcl @@ -0,0 +1,18 @@ +# mmcm reg1 parasitics +read_liberty asap7_small_ff.lib.gz +read_liberty asap7_small_ss.lib.gz +read_verilog reg1_asap7.v +link_design top + +read_sdc -mode mode1 mcmm2_mode1.sdc +read_sdc -mode mode2 mcmm2_mode2.sdc + +read_spef -name reg1_ff reg1_asap7.spef +read_spef -name reg1_ss reg1_asap7_ss.spef + +define_scene scene1 -mode mode1 -liberty asap7_small_ff -spef reg1_ff +define_scene scene2 -mode mode2 -liberty asap7_small_ss -spef reg1_ss + +report_checks -scenes scene1 +report_checks -scenes scene2 +report_checks -group_path_count 4 diff --git a/examples/multi_corner.tcl b/examples/multi_corner.tcl index 589ca9b71..f75c15283 100644 --- a/examples/multi_corner.tcl +++ b/examples/multi_corner.tcl @@ -1,15 +1,20 @@ -# 3 corners with +/- 10% derating example -define_corners ss tt ff -read_liberty -corner ss nangate45_slow.lib.gz -read_liberty -corner tt nangate45_typ.lib.gz -read_liberty -corner ff nangate45_fast.lib.gz +# 3 liberty corners with +/- 10% derating example +read_liberty nangate45_slow.lib.gz +read_liberty nangate45_typ.lib.gz +read_liberty nangate45_fast.lib.gz read_verilog example1.v link_design top set_timing_derate -early 0.9 set_timing_derate -late 1.1 create_clock -name clk -period 10 {clk1 clk2 clk3} set_input_delay -clock clk 0 {in1 in2} -# report all corners + +define_scene ss -liberty nangate45_slow +define_scene tt -liberty nangate45_typ +define_scene ff -liberty nangate45_fast + +# report all scenes report_checks -path_delay min_max -# report typical corner -report_checks -corner tt +# report typical scene +report_checks -scene tt + diff --git a/examples/reg1_asap7.spef b/examples/reg1_asap7.spef new file mode 100644 index 000000000..14bc4d6b9 --- /dev/null +++ b/examples/reg1_asap7.spef @@ -0,0 +1,135 @@ +*SPEF "IEEE 1481-1998" +*DESIGN "reg1" +*DATE "Fri Nov 20 13:23:00 2002" +*VENDOR "Parallax Software, Inc" +*PROGRAM "Handjob" +*VERSION "1.0.1c" +*DESIGN_FLOW "MISSING_NETS" +*DIVIDER / +*DELIMITER : +*BUS_DELIMITER [ ] +*T_UNIT 1.0 PS +*C_UNIT 1.0 FF +*R_UNIT 1.0 KOHM +*L_UNIT 1.0 UH + +*POWER_NETS VDD +*GROUND_NETS VSS + +*PORTS +in1 I +in2 I +clk1 I +clk2 I +clk3 I +out O + +*D_NET in1 13.4 +*CONN +*P in1 I +*I r1:D I *L .0036 +*CAP +1 in1 6.7 +2 r1:D 6.7 +*RES +3 in1 r1:D 2.42 +*END + +*D_NET in2 13.4 +*CONN +*P in2 I +*I r2:D I *L .0036 +*CAP +1 in2 6.7 +2 r2:D 6.7 +*RES +3 in2 r2:D 2.42 +*END + +*D_NET clk1 13.4 +*CONN +*P clk1 I +*I r1:CLK I *L .0036 +*CAP +1 clk1 6.7 +2 r1:CLK 6.7 +*RES +3 clk1 r1:CLK 2.42 +*END + +*D_NET clk2 13.4 +*CONN +*P clk2 I +*I r2:CLK I *L .0036 +*CAP +1 clk2 6.7 +2 r2:CLK 6.7 +*RES +3 clk2 r2:CLK 2.42 +*END + +*D_NET clk3 13.4 +*CONN +*P clk3 I +*I r3:CLK I *L .0036 +*CAP +1 clk3 6.7 +2 r3:CLK 6.7 +*RES +3 clk3 r3:CLK 2.42 +*END + +*D_NET r1q 13.4 +*CONN +*I r1:Q O +*I u2:A I *L .0086 +*CAP +1 r1:Q 6.7 +2 u2:A 6.7 +*RES +3 r1:Q u2:A 2.42 +*END + +*D_NET r2q 13.4 +*CONN +*I r2:Q O +*I u1:A I *L .0086 +*CAP +1 r2:Q 6.7 +2 u1:A 6.7 +*RES +3 r2:Q u1:A 2.42 +*END + +*D_NET u1z 13.4 +*CONN +*I u1:Y O +*I u2:B I *L .0086 +*CAP +1 u1:Y 6.7 +2 u2:B 6.7 +*RES +3 u1:Y u2:B 2.42 +*END + +*D_NET u2z 13.4 +*CONN +*I u2:Y O +*I r3:D I *L .0086 +*CAP +1 u2:Y 6.7 +2 r3:D 6.7 +*RES +3 u2:Y r3:D 2.42 +*END + +*D_NET out 13.4 +*CONN +*I r3:Q O +*P out O +*CAP +1 r3:Q 6.7 +2 out 6.7 +*RES +3 r3:Q out 2.42 +*END diff --git a/examples/reg1_asap7.v b/examples/reg1_asap7.v new file mode 100644 index 000000000..5eb10b466 --- /dev/null +++ b/examples/reg1_asap7.v @@ -0,0 +1,11 @@ +module top (in1, in2, clk1, clk2, clk3, out); + input in1, in2, clk1, clk2, clk3; + output out; + wire r1q, r2q, u1z, u2z; + + DFFHQx4_ASAP7_75t_R r1 (.D(in1), .CLK(clk1), .Q(r1q)); + DFFHQx4_ASAP7_75t_R r2 (.D(in2), .CLK(clk2), .Q(r2q)); + BUFx2_ASAP7_75t_R u1 (.A(r2q), .Y(u1z)); + AND2x2_ASAP7_75t_R u2 (.A(r1q), .B(u1z), .Y(u2z)); + DFFHQx4_ASAP7_75t_R r3 (.D(u2z), .CLK(clk3), .Q(out)); +endmodule // top diff --git a/examples/reg1_asap7_ss.spef b/examples/reg1_asap7_ss.spef new file mode 100644 index 000000000..fe49a5e5a --- /dev/null +++ b/examples/reg1_asap7_ss.spef @@ -0,0 +1,135 @@ +*SPEF "IEEE 1481-1998" +*DESIGN "reg1" +*DATE "Fri Nov 20 13:23:00 2002" +*VENDOR "Parallax Software, Inc" +*PROGRAM "Handjob" +*VERSION "1.0.1c" +*DESIGN_FLOW "MISSING_NETS" +*DIVIDER / +*DELIMITER : +*BUS_DELIMITER [ ] +*T_UNIT 1.0 PS +*C_UNIT 1.0 FF +*R_UNIT 1.0 KOHM +*L_UNIT 1.0 UH + +*POWER_NETS VDD +*GROUND_NETS VSS + +*PORTS +in1 I +in2 I +clk1 I +clk2 I +clk3 I +out O + +*D_NET in1 13.4 +*CONN +*P in1 I +*I r1:D I *L .0036 +*CAP +1 in1 8.1 +2 r1:D 8.1 +*RES +3 in1 r1:D 2.7 +*END + +*D_NET in2 13.4 +*CONN +*P in2 I +*I r2:D I *L .0036 +*CAP +1 in2 8.1 +2 r2:D 8.1 +*RES +3 in2 r2:D 2.7 +*END + +*D_NET clk1 13.4 +*CONN +*P clk1 I +*I r1:CLK I *L .0036 +*CAP +1 clk1 8.1 +2 r1:CLK 8.1 +*RES +3 clk1 r1:CLK 2.7 +*END + +*D_NET clk2 13.4 +*CONN +*P clk2 I +*I r2:CLK I *L .0036 +*CAP +1 clk2 8.1 +2 r2:CLK 8.1 +*RES +3 clk2 r2:CLK 2.7 +*END + +*D_NET clk3 13.4 +*CONN +*P clk3 I +*I r3:CLK I *L .0036 +*CAP +1 clk3 8.1 +2 r3:CLK 8.1 +*RES +3 clk3 r3:CLK 2.7 +*END + +*D_NET r1q 13.4 +*CONN +*I r1:Q O +*I u2:A I *L .0086 +*CAP +1 r1:Q 8.1 +2 u2:A 8.1 +*RES +3 r1:Q u2:A 2.7 +*END + +*D_NET r2q 13.4 +*CONN +*I r2:Q O +*I u1:A I *L .0086 +*CAP +1 r2:Q 8.1 +2 u1:A 8.1 +*RES +3 r2:Q u1:A 2.7 +*END + +*D_NET u1z 13.4 +*CONN +*I u1:Y O +*I u2:B I *L .0086 +*CAP +1 u1:Y 8.1 +2 u2:B 8.1 +*RES +3 u1:Y u2:B 2.7 +*END + +*D_NET u2z 13.4 +*CONN +*I u2:Y O +*I r3:D I *L .0086 +*CAP +1 u2:Y 8.1 +2 r3:D 8.1 +*RES +3 u2:Y r3:D 2.7 +*END + +*D_NET out 13.4 +*CONN +*I r3:Q O +*P out O +*CAP +1 r3:Q 8.1 +2 out 8.1 +*RES +3 r3:Q out 2.7 +*END diff --git a/graph/Delay.cc b/graph/Delay.cc index cfe32cffb..2f538a302 100644 --- a/graph/Delay.cc +++ b/graph/Delay.cc @@ -32,7 +32,7 @@ namespace sta { const char * delayAsString(const Delay &delay, - const StaState *sta) + const StaState *sta) { return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); } diff --git a/graph/DelayFloat.cc b/graph/DelayFloat.cc index a9a90dcf9..73b877dff 100644 --- a/graph/DelayFloat.cc +++ b/graph/DelayFloat.cc @@ -45,24 +45,24 @@ initDelayConstants() const char * delayAsString(const Delay &delay, - const StaState *sta) + const StaState *sta) { return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); } const char * delayAsString(const Delay &delay, - const StaState *sta, - int digits) + 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 EarlyLate *, + const StaState *sta, + int digits) { const Unit *unit = sta->units()->timeUnit(); return unit->asString(delay, digits); @@ -76,7 +76,7 @@ delayInitValue(const MinMax *min_max) bool delayIsInitValue(const Delay &delay, - const MinMax *min_max) + const MinMax *min_max) { return fuzzyEqual(delay, min_max->initValue()); } @@ -95,24 +95,24 @@ delayInf(const Delay &delay) bool delayEqual(const Delay &delay1, - const Delay &delay2) + const Delay &delay2) { return fuzzyEqual(delay1, delay2); } bool delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *) + const Delay &delay2, + const StaState *) { return fuzzyLess(delay1, delay2); } bool delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) + const Delay &delay2, + const MinMax *min_max, + const StaState *) { if (min_max == MinMax::max()) return fuzzyLess(delay1, delay2); @@ -122,17 +122,17 @@ delayLess(const Delay &delay1, bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *) + const Delay &delay2, + const StaState *) { return fuzzyLessEqual(delay1, delay2); } bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) + const Delay &delay2, + const MinMax *min_max, + const StaState *) { if (min_max == MinMax::max()) return fuzzyLessEqual(delay1, delay2); @@ -142,17 +142,17 @@ delayLessEqual(const Delay &delay1, bool delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *) + const Delay &delay2, + const StaState *) { return fuzzyGreater(delay1, delay2); } bool delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) + const Delay &delay2, + const MinMax *min_max, + const StaState *) { if (min_max == MinMax::max()) return fuzzyGreater(delay1, delay2); @@ -162,17 +162,17 @@ delayGreater(const Delay &delay1, bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *) + const Delay &delay2, + const StaState *) { return fuzzyGreaterEqual(delay1, delay2); } bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *) + const Delay &delay2, + const MinMax *min_max, + const StaState *) { if (min_max == MinMax::max()) return fuzzyGreaterEqual(delay1, delay2); @@ -182,14 +182,14 @@ delayGreaterEqual(const Delay &delay1, Delay delayRemove(const Delay &delay1, - const Delay &delay2) + const Delay &delay2) { return delay1 - delay2; } float delayRatio(const Delay &delay1, - const Delay &delay2) + const Delay &delay2) { return delay1 / delay2; } diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc index d50db11f5..2e855586f 100644 --- a/graph/DelayNormal1.cc +++ b/graph/DelayNormal1.cc @@ -85,7 +85,7 @@ Delay::Delay(float mean) : } Delay::Delay(float mean, - float sigma2) : + float sigma2) : mean_(mean), sigma2_(sigma2) { @@ -139,7 +139,7 @@ Delay Delay::operator+(const Delay &delay) const { return Delay(mean_ + delay.mean_, - sigma2_ + delay.sigma2_); + sigma2_ + delay.sigma2_); } Delay @@ -152,7 +152,7 @@ Delay Delay::operator-(const Delay &delay) const { return Delay(mean_ - delay.mean_, - sigma2_ + delay.sigma2_); + sigma2_ + delay.sigma2_); } Delay @@ -219,24 +219,24 @@ DelayDbl::operator-=(const Delay &delay) Delay makeDelay(float delay, - float sigma, - float) + float sigma, + float) { return Delay(delay, square(sigma)); } Delay makeDelay2(float delay, - float sigma2, - float ) + float sigma2, + float ) { return Delay(delay, sigma2); } float delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta) + const EarlyLate *early_late, + const StaState *sta) { if (sta->variables()->pocvEnabled()) { if (early_late == EarlyLate::early()) @@ -251,29 +251,29 @@ delayAsFloat(const Delay &delay, float delaySigma2(const Delay &delay, - const EarlyLate *) + const EarlyLate *) { return delay.sigma2(); } const char * delayAsString(const Delay &delay, - const StaState *sta) + const StaState *sta) { return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); } const char * delayAsString(const Delay &delay, - const StaState *sta, - int digits) + 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)); + unit->asString(delay.mean(), digits), + unit->asString(sigma, digits)); } else return unit->asString(delay.mean(), digits); @@ -281,9 +281,9 @@ delayAsString(const Delay &delay, const char * delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits) + 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); @@ -291,7 +291,7 @@ delayAsString(const Delay &delay, bool delayIsInitValue(const Delay &delay, - const MinMax *min_max) + const MinMax *min_max) { return fuzzyEqual(delay.mean(), min_max->initValue()) && delay.sigma2() == 0.0; @@ -312,7 +312,7 @@ delayInf(const Delay &delay) bool delayEqual(const Delay &delay1, - const Delay &delay2) + const Delay &delay2) { return fuzzyEqual(delay1.mean(), delay2.mean()) && fuzzyEqual(delay1.sigma2(), delay2.sigma2()); @@ -320,27 +320,27 @@ delayEqual(const Delay &delay1, bool delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta) + const Delay &delay2, + const StaState *sta) { return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); + delayAsFloat(delay2, EarlyLate::early(), sta)); } bool delayLess(const Delay &delay1, - float delay2, - const StaState *sta) + float delay2, + const StaState *sta) { return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); + delay2); } bool delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) return delayLess(delay1, delay2, sta); @@ -350,27 +350,27 @@ delayLess(const Delay &delay1, bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta) + const Delay &delay2, + const StaState *sta) { return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); + delayAsFloat(delay2, EarlyLate::early(), sta)); } bool delayLessEqual(const Delay &delay1, - float delay2, - const StaState *sta) + float delay2, + const StaState *sta) { return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); + delay2); } bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) return delayLessEqual(delay1, delay2, sta); @@ -380,46 +380,46 @@ delayLessEqual(const Delay &delay1, bool delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta) + const Delay &delay2, + const StaState *sta) { return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool delayGreater(const Delay &delay1, - float delay2, - const StaState *sta) + float delay2, + const StaState *sta) { return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), - delay2); + delay2); } bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta) + const Delay &delay2, + const StaState *sta) { return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool delayGreaterEqual(const Delay &delay1, - float delay2, - const StaState *sta) + float delay2, + const StaState *sta) { return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delay2); + delay2); } bool delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) return delayGreater(delay1, delay2, sta); @@ -429,9 +429,9 @@ delayGreater(const Delay &delay1, bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) return delayGreaterEqual(delay1, delay2, sta); @@ -441,41 +441,41 @@ delayGreaterEqual(const Delay &delay1, Delay delayRemove(const Delay &delay1, - const Delay &delay2) + const Delay &delay2) { return Delay(delay1.mean() - delay2.mean(), - delay1.sigma2() - delay2.sigma2()); + delay1.sigma2() - delay2.sigma2()); } float delayRatio(const Delay &delay1, - const Delay &delay2) + const Delay &delay2) { return delay1.mean() / delay2.mean(); } Delay operator+(float delay1, - const Delay &delay2) + const Delay &delay2) { return Delay(delay1 + delay2.mean(), - delay2.sigma2()); + delay2.sigma2()); } Delay operator/(float delay1, - const Delay &delay2) + const Delay &delay2) { return Delay(delay1 / delay2.mean(), - delay2.sigma2()); + delay2.sigma2()); } Delay operator*(const Delay &delay1, - float delay2) + float delay2) { return Delay(delay1.mean() * delay2, - delay1.sigma2() * delay2 * delay2); + delay1.sigma2() * delay2 * delay2); } } // namespace diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index 9d445559d..b6d74df6b 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -86,8 +86,8 @@ Delay::Delay(float mean) : } Delay::Delay(float mean, - float sigma2_early, - float sigma2_late) : + float sigma2_early, + float sigma2_late) : mean_(mean), sigma2_{sigma2_early, sigma2_late} { @@ -157,8 +157,8 @@ 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]); + sigma2_[early_index] + delay.sigma2_[early_index], + sigma2_[late_index] + delay.sigma2_[late_index]); } Delay @@ -171,8 +171,8 @@ 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]); + sigma2_[early_index] + delay.sigma2_[late_index], + sigma2_[late_index] + delay.sigma2_[early_index]); } Delay @@ -245,23 +245,23 @@ DelayDbl::operator-=(const Delay &delay) Delay makeDelay(float delay, - float sigma_early, - float sigma_late) + 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) + float sigma2_early, + float sigma2_late) { return Delay(delay, sigma2_early, sigma2_late); } bool delayIsInitValue(const Delay &delay, - const MinMax *min_max) + const MinMax *min_max) { return fuzzyEqual(delay.mean(), min_max->initValue()) && fuzzyZero(delay.sigma2Early()) @@ -284,7 +284,7 @@ delayInf(const Delay &delay) bool delayEqual(const Delay &delay1, - const Delay &delay2) + const Delay &delay2) { return fuzzyEqual(delay1.mean(), delay2.mean()) && fuzzyEqual(delay1.sigma2Early(), delay2.sigma2Early()) @@ -293,27 +293,27 @@ delayEqual(const Delay &delay1, bool delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta) + const Delay &delay2, + const StaState *sta) { return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); + delayAsFloat(delay2, EarlyLate::early(), sta)); } bool delayLess(const Delay &delay1, - float delay2, - const StaState *sta) + float delay2, + const StaState *sta) { return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); + delay2); } bool delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) return delayLess(delay1, delay2, sta); @@ -323,27 +323,27 @@ delayLess(const Delay &delay1, bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta) + const Delay &delay2, + const StaState *sta) { return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delayAsFloat(delay2, EarlyLate::early(), sta)); + delayAsFloat(delay2, EarlyLate::early(), sta)); } bool delayLessEqual(const Delay &delay1, - float delay2, - const StaState *sta) + float delay2, + const StaState *sta) { return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta), - delay2); + delay2); } bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) return delayLessEqual(delay1, delay2, sta); @@ -353,45 +353,45 @@ delayLessEqual(const Delay &delay1, bool delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta) + const Delay &delay2, + const StaState *sta) { return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool delayGreater(const Delay &delay1, - float delay2, - const StaState *sta) + float delay2, + const StaState *sta) { return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta) + const Delay &delay2, + const StaState *sta) { return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool delayGreaterEqual(const Delay &delay1, - float delay2, - const StaState *sta) + float delay2, + const StaState *sta) { return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delay2); + delay2); } bool delayGreater(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) return delayGreater(delay1, delay2, sta); @@ -401,9 +401,9 @@ delayGreater(const Delay &delay1, bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta) + const Delay &delay2, + const MinMax *min_max, + const StaState *sta) { if (min_max == MinMax::max()) return delayGreaterEqual(delay1, delay2, sta); @@ -413,8 +413,8 @@ delayGreaterEqual(const Delay &delay1, float delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta) + const EarlyLate *early_late, + const StaState *sta) { if (sta->pocvEnabled()) { if (early_late == EarlyLate::early()) @@ -429,31 +429,31 @@ delayAsFloat(const Delay &delay, float delaySigma2(const Delay &delay, - const EarlyLate *early_late) + const EarlyLate *early_late) { return delay.sigma2(early_late); } const char * delayAsString(const Delay &delay, - const StaState *sta) + const StaState *sta) { return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); } const char * delayAsString(const Delay &delay, - const StaState *sta, - int digits) + 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)); + unit->asString(delay.mean(), digits), + unit->asString(sigma_early, digits), + unit->asString(sigma_late, digits)); } else return unit->asString(delay.mean(), digits); @@ -461,9 +461,9 @@ delayAsString(const Delay &delay, const char * delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits) + 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); @@ -471,45 +471,45 @@ delayAsString(const Delay &delay, Delay delayRemove(const Delay &delay1, - const Delay &delay2) + const Delay &delay2) { return Delay(delay1.mean() - delay2.mean(), - delay1.sigma2Early() - delay2.sigma2Early(), - delay1.sigma2Late() - delay2.sigma2Late()); + delay1.sigma2Early() - delay2.sigma2Early(), + delay1.sigma2Late() - delay2.sigma2Late()); } float delayRatio(const Delay &delay1, - const Delay &delay2) + const Delay &delay2) { return delay1.mean() / delay2.mean(); } Delay operator+(float delay1, - const Delay &delay2) + const Delay &delay2) { return Delay(delay1 + delay2.mean(), - delay2.sigma2Early(), - delay2.sigma2Late()); + delay2.sigma2Early(), + delay2.sigma2Late()); } Delay operator/(float delay1, - const Delay &delay2) + const Delay &delay2) { return Delay(delay1 / delay2.mean(), - delay2.sigma2Early(), - delay2.sigma2Late()); + delay2.sigma2Early(), + delay2.sigma2Late()); } Delay operator*(const Delay &delay1, - float delay2) + float delay2) { return Delay(delay1.mean() * delay2, - delay1.sigma2Early() * delay2 * delay2, - delay1.sigma2Late() * delay2 * delay2); + delay1.sigma2Early() * delay2 * delay2, + delay1.sigma2Late() * delay2 * delay2); } } // namespace diff --git a/graph/Graph.cc b/graph/Graph.cc index 713fa2e56..2fe4d9344 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -24,6 +24,7 @@ #include "Graph.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" #include "Stats.hh" #include "MinMax.hh" @@ -34,7 +35,6 @@ #include "Liberty.hh" #include "PortDirection.hh" #include "Network.hh" -#include "DcalcAnalysisPt.hh" #include "FuncExpr.hh" namespace sta { @@ -48,15 +48,15 @@ using std::string; //////////////////////////////////////////////////////////////// Graph::Graph(StaState *sta, - int slew_rf_count, - DcalcAPIndex ap_count) : + 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_(new VertexSet(graph_)) + reg_clk_vertices_(makeVertexSet(this)) { // For the benifit of reg_clk_vertices_ that references graph_. graph_ = this; @@ -68,7 +68,6 @@ Graph::~Graph() delete edges_; vertices_->clear(); delete vertices_; - delete reg_clk_vertices_; removePeriodCheckAnnotations(); } @@ -105,11 +104,11 @@ class FindNetDrvrLoadCounts : public PinVisitor { public: FindNetDrvrLoadCounts(Pin *drvr_pin, - PinSet &visited_drvrs, - int &drvr_count, - int &bidirect_count, - int &load_count, - const Network *network); + PinSet &visited_drvrs, + int &drvr_count, + int &bidirect_count, + int &load_count, + const Network *network); virtual void operator()(const Pin *pin); protected: @@ -122,11 +121,11 @@ class FindNetDrvrLoadCounts : public PinVisitor }; FindNetDrvrLoadCounts::FindNetDrvrLoadCounts(Pin *drvr_pin, - PinSet &visited_drvrs, - int &drvr_count, - int &bidirect_count, - int &load_count, - const Network *network) : + PinSet &visited_drvrs, + int &drvr_count, + int &bidirect_count, + int &load_count, + const Network *network) : drvr_pin_(drvr_pin), visited_drvrs_(visited_drvrs), drvr_count_(drvr_count), @@ -186,48 +185,48 @@ Graph::makePinInstanceEdges(const Pin *pin) void Graph::makePortInstanceEdges(const Instance *inst, - LibertyCell *cell, - LibertyPort *from_to_port) + LibertyCell *cell, + LibertyPort *from_to_port) { for (TimingArcSet *arc_set : cell->timingArcSets()) { LibertyPort *from_port = arc_set->from(); LibertyPort *to_port = arc_set->to(); if ((from_to_port == nullptr - || from_port == from_to_port - || to_port == from_to_port) - && from_port) { + || from_port == from_to_port + || to_port == from_to_port) + && from_port) { Pin *from_pin = network_->findPin(inst, from_port); Pin *to_pin = network_->findPin(inst, to_port); if (from_pin && to_pin) { - Vertex *from_vertex, *from_bidirect_drvr_vertex; - Vertex *to_vertex, *to_bidirect_drvr_vertex; - pinVertices(from_pin, from_vertex, from_bidirect_drvr_vertex); - pinVertices(to_pin, to_vertex, to_bidirect_drvr_vertex); - // From pin and/or to pin can be bidirect. - // For combinational arcs edge is to driver. - // For timing checks edge is to load. - // Vertices can be missing from the graph if the pins - // are power or ground. - if (from_vertex) { + Vertex *from_vertex, *from_bidirect_drvr_vertex; + Vertex *to_vertex, *to_bidirect_drvr_vertex; + pinVertices(from_pin, from_vertex, from_bidirect_drvr_vertex); + pinVertices(to_pin, to_vertex, to_bidirect_drvr_vertex); + // From pin and/or to pin can be bidirect. + // For combinational arcs edge is to driver. + // For timing checks edge is to load. + // Vertices can be missing from the graph if the pins + // are power or ground. + if (from_vertex) { const TimingRole *role = arc_set->role(); - bool is_check = role->isTimingCheckBetween(); - if (to_bidirect_drvr_vertex && !is_check) - makeEdge(from_vertex, to_bidirect_drvr_vertex, arc_set); - else if (to_vertex) { - makeEdge(from_vertex, to_vertex, arc_set); - if (is_check) { - to_vertex->setHasChecks(true); - from_vertex->setIsCheckClk(true); - } - } - if (from_bidirect_drvr_vertex && to_vertex) { - // Internal path from bidirect output back into the - // instance. - Edge *edge = makeEdge(from_bidirect_drvr_vertex, to_vertex, - arc_set); - edge->setIsBidirectInstPath(true); - } - } + bool is_check = role->isTimingCheckBetween(); + if (to_bidirect_drvr_vertex && !is_check) + makeEdge(from_vertex, to_bidirect_drvr_vertex, arc_set); + else if (to_vertex) { + makeEdge(from_vertex, to_vertex, arc_set); + if (is_check) { + to_vertex->setHasChecks(true); + from_vertex->setIsCheckClk(true); + } + } + if (from_bidirect_drvr_vertex && to_vertex) { + // Internal path from bidirect output back into the + // instance. + Edge *edge = makeEdge(from_bidirect_drvr_vertex, to_vertex, + arc_set); + edge->setIsBidirectInstPath(true); + } + } } } } @@ -248,13 +247,13 @@ Graph::makeWireEdges() void Graph::makeInstDrvrWireEdges(const Instance *inst, - PinSet &visited_drvrs) + PinSet &visited_drvrs) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); if (network_->isDriver(pin) - && !visited_drvrs.hasKey(pin)) + && !visited_drvrs.contains(pin)) makeWireEdgesFromPin(pin, visited_drvrs); } delete pin_iter; @@ -276,7 +275,7 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin) void Graph::makeWireEdgesFromPin(const Pin *drvr_pin, - PinSet &visited_drvrs) + PinSet &visited_drvrs) { // Find all drivers and loads on the net to avoid N*M run time // for large fanin/fanout nets. @@ -296,7 +295,7 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin, for (auto drvr_pin : drvrs) { for (auto load_pin : loads) { if (drvr_pin != load_pin) - makeWireEdge(drvr_pin, load_pin); + makeWireEdge(drvr_pin, load_pin); } } } @@ -337,7 +336,7 @@ Graph::makeWireEdgesToPin(const Pin *to_pin) if (drvrs) { for (auto drvr : *drvrs) { if (drvr != to_pin) - makeWireEdge(drvr, to_pin); + makeWireEdge(drvr, to_pin); } } } @@ -349,7 +348,7 @@ class MakeEdgesThruHierPin : public HierPinThruVisitor private: virtual void visit(const Pin *drvr, - const Pin *load); + const Pin *load); Graph *graph_; }; @@ -362,7 +361,7 @@ MakeEdgesThruHierPin::MakeEdgesThruHierPin(Graph *graph) : void MakeEdgesThruHierPin::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { graph_->makeWireEdge(drvr, load); } @@ -376,7 +375,7 @@ Graph::makeWireEdgesThruPin(const Pin *hpin) void Graph::makeWireEdge(const Pin *from_pin, - const Pin *to_pin) + const Pin *to_pin) { TimingArcSet *arc_set = TimingArcSet::wireTimingArcSet(); Vertex *from_vertex, *from_bidirect_drvr_vertex; @@ -391,6 +390,13 @@ Graph::makeWireEdge(const Pin *from_pin, } } +void +Graph::makeSceneAfter() +{ + ap_count_ = dcalcAnalysisPtCount(); + initSlews(); +} + //////////////////////////////////////////////////////////////// Vertex * @@ -414,8 +420,8 @@ Graph::makePinVertices(Pin *pin) void Graph::makePinVertices(Pin *pin, - Vertex *&vertex, - Vertex *&bidir_drvr_vertex) + Vertex *&vertex, + Vertex *&bidir_drvr_vertex) { PortDirection *dir = network_->direction(pin); if (!dir->isPowerGround()) { @@ -433,26 +439,26 @@ Graph::makePinVertices(Pin *pin, Vertex * Graph::makeVertex(Pin *pin, - bool is_bidirect_drvr, - bool is_reg_clk) + bool is_bidirect_drvr, + bool is_reg_clk) { Vertex *vertex = vertices_->make(); vertex->init(pin, is_bidirect_drvr, is_reg_clk); initSlews(vertex); if (is_reg_clk) - reg_clk_vertices_->insert(vertex); + reg_clk_vertices_.insert(vertex); return vertex; } void Graph::pinVertices(const Pin *pin, - // Return values. - Vertex *&vertex, - Vertex *&bidirect_drvr_vertex) const + // Return values. + Vertex *&vertex, + Vertex *&bidirect_drvr_vertex) const { vertex = Graph::vertex(network_->vertexId(pin)); if (network_->direction(pin)->isBidirect()) - bidirect_drvr_vertex = pin_bidirect_drvr_vertex_map_.findKey(pin); + bidirect_drvr_vertex = findKey(pin_bidirect_drvr_vertex_map_, pin); else bidirect_drvr_vertex = nullptr; } @@ -461,7 +467,7 @@ Vertex * Graph::pinDrvrVertex(const Pin *pin) const { if (network_->direction(pin)->isBidirect()) - return pin_bidirect_drvr_vertex_map_.findKey(pin); + return findKey(pin_bidirect_drvr_vertex_map_, pin); else return Graph::vertex(network_->vertexId(pin)); } @@ -476,11 +482,11 @@ void Graph::deleteVertex(Vertex *vertex) { if (vertex->isRegClk()) - reg_clk_vertices_->erase(vertex); + reg_clk_vertices_.erase(vertex); Pin *pin = vertex->pin_; if (vertex->isBidirectDriver()) pin_bidirect_drvr_vertex_map_.erase(pin_bidirect_drvr_vertex_map_ - .find(pin)); + .find(pin)); else network_->setVertexId(pin, vertex_id_null); // Delete edges to vertex. @@ -513,7 +519,7 @@ Graph::hasFaninOne(Vertex *vertex) const void Graph::deleteInEdge(Vertex *vertex, - Edge *edge) + Edge *edge) { EdgeId edge_id = id(edge); EdgeId prev = 0; @@ -529,7 +535,7 @@ Graph::deleteInEdge(Vertex *vertex, void Graph::deleteOutEdge(Vertex *vertex, - Edge *edge) + Edge *edge) { EdgeId next = edge->vertex_out_next_; EdgeId prev = edge->vertex_out_prev_; @@ -574,30 +580,6 @@ Graph::gateEdgeArc(const Pin *in_pin, //////////////////////////////////////////////////////////////// -Path * -Graph::makePaths(Vertex *vertex, - uint32_t count) -{ - Path *paths = new Path[count]; - vertex->setPaths(paths); - return paths; -} - -Path * -Graph::paths(const Vertex *vertex) const -{ - return vertex->paths(); -} - -void -Graph::deletePaths(Vertex *vertex) -{ - vertex->setPaths(nullptr); - vertex->tag_group_index_ = tag_group_index_max; -} - -//////////////////////////////////////////////////////////////// - const Slew & Graph::slew(const Vertex *vertex, const RiseFall *rf, @@ -652,8 +634,8 @@ Graph::id(const Edge *edge) const Edge * Graph::makeEdge(Vertex *from, - Vertex *to, - TimingArcSet *arc_set) + Vertex *to, + TimingArcSet *arc_set) { Edge *edge = edges_->make(); edge->init(id(from), id(to), arc_set); @@ -687,8 +669,8 @@ Graph::deleteEdge(Edge *edge) ArcDelay Graph::arcDelay(const Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index) const + const TimingArc *arc, + DcalcAPIndex ap_index) const { ArcDelay *delays = edge->arcDelays(); size_t index = arc->index() * ap_count_ + ap_index; @@ -697,9 +679,9 @@ Graph::arcDelay(const Edge *edge, void Graph::setArcDelay(Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index, - ArcDelay delay) + const TimingArc *arc, + DcalcAPIndex ap_index, + ArcDelay delay) { ArcDelay *arc_delays = edge->arcDelays(); size_t index = arc->index() * ap_count_ + ap_index; @@ -708,8 +690,8 @@ Graph::setArcDelay(Edge *edge, const ArcDelay & Graph::wireArcDelay(const Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index) + const RiseFall *rf, + DcalcAPIndex ap_index) { ArcDelay *delays = edge->arcDelays(); size_t index = rf->index() * ap_count_ + ap_index; @@ -718,9 +700,9 @@ Graph::wireArcDelay(const Edge *edge, void Graph::setWireArcDelay(Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index, - const ArcDelay &delay) + const RiseFall *rf, + DcalcAPIndex ap_index, + const ArcDelay &delay) { ArcDelay *delays = edge->arcDelays(); size_t index = rf->index() * ap_count_ + ap_index; @@ -731,25 +713,25 @@ Graph::setWireArcDelay(Edge *edge, bool Graph::arcDelayAnnotated(const Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index) const + const TimingArc *arc, + DcalcAPIndex ap_index) const { return edge->arcDelayAnnotated(arc, ap_index, ap_count_); } void Graph::setArcDelayAnnotated(Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index, - bool annotated) + const TimingArc *arc, + DcalcAPIndex ap_index, + bool annotated) { return edge->setArcDelayAnnotated(arc, ap_index, ap_count_, annotated); } bool Graph::wireDelayAnnotated(const Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index) const + const RiseFall *rf, + DcalcAPIndex ap_index) const { int arc_index = TimingArcSet::wireArcIndex(rf); TimingArc *arc = TimingArcSet::wireTimingArcSet()->findTimingArc(arc_index); @@ -758,9 +740,9 @@ Graph::wireDelayAnnotated(const Edge *edge, void Graph::setWireDelayAnnotated(Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index, - bool annotated) + const RiseFall *rf, + DcalcAPIndex ap_index, + bool annotated) { int arc_index = TimingArcSet::wireArcIndex(rf); TimingArc *arc = TimingArcSet::wireTimingArcSet()->findTimingArc(arc_index); @@ -831,19 +813,6 @@ Graph::initArcDelays(Edge *edge) arc_delays[i] = 0.0; } -bool -Graph::delayAnnotated(Edge *edge) -{ - TimingArcSet *arc_set = edge->timingArcSet(); - for (TimingArc *arc : arc_set->arcs()) { - for (DcalcAPIndex ap_index = 0; ap_index < ap_count_; ap_index++) { - if (!arcDelayAnnotated(edge, arc, ap_index)) - return false; - } - } - return true; -} - //////////////////////////////////////////////////////////////// void @@ -873,10 +842,10 @@ Graph::minPulseWidthArc(Vertex *vertex, void Graph::minPeriodArc(Vertex *vertex, - const RiseFall *rf, - // Return values. - Edge *&edge, - TimingArc *&arc) + const RiseFall *rf, + // Return values. + Edge *&edge, + TimingArc *&arc) { VertexOutEdgeIterator edge_iter(vertex, this); while (edge_iter.hasNext()) { @@ -899,30 +868,30 @@ Graph::minPeriodArc(Vertex *vertex, void Graph::periodCheckAnnotation(const Pin *pin, - DcalcAPIndex ap_index, - // Return values. - float &period, - bool &exists) + DcalcAPIndex ap_index, + // Return values. + float &period, + bool &exists) { exists = false; if (period_check_annotations_) { - float *periods = period_check_annotations_->findKey(pin); + float *periods = findKey(period_check_annotations_, pin); if (periods) { period = periods[ap_index]; if (period >= 0.0) - exists = true; + exists = true; } } } void Graph::setPeriodCheckAnnotation(const Pin *pin, - DcalcAPIndex ap_index, - float period) + DcalcAPIndex ap_index, + float period) { if (period_check_annotations_ == nullptr) period_check_annotations_ = new PeriodCheckAnnotations(network_); - float *periods = period_check_annotations_->findKey(pin); + float *periods = findKey(period_check_annotations_, pin); if (periods == nullptr) { periods = new float[ap_count_]; // Use negative (illegal) period values to indicate unannotated checks. @@ -974,8 +943,8 @@ Vertex::Vertex() void Vertex::init(Pin *pin, - bool is_bidirect_drvr, - bool is_reg_clk) + bool is_bidirect_drvr, + bool is_reg_clk) { pin_ = pin; is_reg_clk_ = is_reg_clk; @@ -986,16 +955,13 @@ Vertex::init(Pin *pin, paths_ = nullptr; tag_group_index_ = tag_group_index_max; slew_annotated_ = false; - sim_value_ = unsigned(LogicValue::unknown); - is_disabled_constraint_ = false; - is_gated_clk_enable_ = false; has_checks_ = false; is_check_clk_ = false; - is_constrained_ = false; has_downstream_clk_pin_ = false; level_ = 0; visited1_ = false; visited2_ = false; + has_sim_value_ = false; bfs_in_queue_ = 0; } @@ -1046,15 +1012,15 @@ Vertex::isDriver(const Network *network) const PortDirection *dir = network->direction(pin_); bool top_level_port = network->isTopLevelPort(pin_); return ((top_level_port - && (dir->isInput() - || (dir->isBidirect() - && is_bidirect_drvr_))) - || (!top_level_port - && (dir->isOutput() - || dir->isTristate() - || (dir->isBidirect() - && is_bidirect_drvr_) - || dir->isInternal()))); + && (dir->isInput() + || (dir->isBidirect() + && is_bidirect_drvr_))) + || (!top_level_port + && (dir->isOutput() + || dir->isTristate() + || (dir->isBidirect() + && is_bidirect_drvr_) + || dir->isInternal()))); } void @@ -1082,11 +1048,17 @@ Vertex::setSlews(Slew *slews) slews_ = slews; } +void +Vertex::setHasSimValue(bool has_sim) +{ + has_sim_value_ = has_sim; +} + bool Vertex::slewAnnotated(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { - int index = min_max->index() * transitionCount() + rf->index(); + int index = min_max->index() * RiseFall::index_count+ rf->index(); return ((1 << index) & slew_annotated_) != 0; } @@ -1098,14 +1070,14 @@ Vertex::slewAnnotated() const void Vertex::setSlewAnnotated(bool annotated, - const RiseFall *rf, - DcalcAPIndex ap_index) + const RiseFall *rf, + DcalcAPIndex ap_index) { // Track rise/fall/min/max annotations separately, but after that // only rise/fall. if (ap_index > 1) ap_index = 0; - int index = ap_index * transitionCount() + rf->index(); + int index = ap_index * RiseFall::index_count + rf->index(); if (annotated) slew_annotated_ |= (1 << index); else @@ -1130,37 +1102,28 @@ Vertex::setTagGroupIndex(TagGroupIndex tag_index) tag_group_index_ = tag_index; } -void -Vertex::setPaths(Path *paths) +Path * +Vertex::makePaths(uint32_t count) { delete [] paths_; + Path *paths = new Path[count]; paths_ = paths; -} - -LogicValue -Vertex::simValue() const -{ - return static_cast(sim_value_); + return paths; } void -Vertex::setSimValue(LogicValue value) -{ - sim_value_ = unsigned(value); -} - -bool -Vertex::isConstant() const +Vertex::setPaths(Path *paths) { - LogicValue value = static_cast(sim_value_); - return value == LogicValue::zero - || value == LogicValue::one; + delete [] paths_; + paths_ = paths; } void -Vertex::setIsDisabledConstraint(bool disabled) +Vertex::deletePaths() { - is_disabled_constraint_ = disabled; + delete [] paths_; + paths_ = nullptr; + tag_group_index_ = tag_group_index_max; } bool @@ -1187,18 +1150,6 @@ Vertex::setIsCheckClk(bool is_check_clk) is_check_clk_ = is_check_clk; } -void -Vertex::setIsGatedClkEnable(bool enable) -{ - is_gated_clk_enable_ = enable; -} - -void -Vertex::setIsConstrained(bool constrained) -{ - is_constrained_ = constrained; -} - void Vertex::setHasDownstreamClkPin(bool has_clk_pin) { @@ -1213,7 +1164,7 @@ Vertex::bfsInQueue(BfsIndex index) const void Vertex::setBfsInQueue(BfsIndex index, - bool value) + bool value) { if (value) bfs_in_queue_ |= 1 << int(index); @@ -1235,8 +1186,8 @@ Edge::Edge() void Edge::init(VertexId from, - VertexId to, - TimingArcSet *arc_set) + VertexId to, + TimingArcSet *arc_set) { from_ = from; to_ = to; @@ -1251,10 +1202,9 @@ Edge::init(VertexId from, arc_delay_annotated_is_bits_ = true; arc_delay_annotated_.bits_ = 0; delay_annotation_is_incremental_ = false; - sim_timing_sense_ = unsigned(TimingSense::unknown); - is_disabled_constraint_ = false; - is_disabled_cond_ = false; is_disabled_loop_ = false; + has_sim_sense_ = false; + has_disabled_cond_ = false; } Edge::~Edge() @@ -1377,71 +1327,41 @@ Edge::isWire() const { return arc_set_->role()->isWire(); } - + TimingSense Edge::sense() const { return arc_set_->sense(); } - -TimingSense -Edge::simTimingSense() const -{ - return static_cast(sim_timing_sense_); -} - -void -Edge::setSimTimingSense(TimingSense sense) -{ - sim_timing_sense_ = unsigned(sense); -} - -bool -Edge::isDisabledConstraint() const -{ - const TimingRole *role = arc_set_->role(); - bool is_wire = role->isWire(); - return is_disabled_constraint_ - || arc_set_->isDisabledConstraint() - // set_disable_timing cell does not disable timing checks. - || (!(role->isTimingCheck() || is_wire) - && arc_set_->libertyCell()->isDisabledConstraint()) - || (!is_wire - && arc_set_->from()->isDisabledConstraint()) - || (!is_wire - && arc_set_->to()->isDisabledConstraint()); -} - - void -Edge::setIsDisabledConstraint(bool disabled) +Edge::setIsDisabledLoop(bool disabled) { - is_disabled_constraint_ = disabled; + is_disabled_loop_ = disabled; } void -Edge::setIsDisabledCond(bool disabled) +Edge::setIsBidirectInstPath(bool is_bidir) { - is_disabled_cond_ = disabled; + is_bidirect_inst_path_ = is_bidir; } void -Edge::setIsDisabledLoop(bool disabled) +Edge::setIsBidirectNetPath(bool is_bidir) { - is_disabled_loop_ = disabled; + is_bidirect_net_path_ = is_bidir; } void -Edge::setIsBidirectInstPath(bool is_bidir) +Edge::setHasSimSense(bool has_sense) { - is_bidirect_inst_path_ = is_bidir; + has_sim_sense_ = has_sense; } void -Edge::setIsBidirectNetPath(bool is_bidir) +Edge::setHasDisabledCond(bool has_disabled) { - is_bidirect_net_path_ = is_bidir; + has_disabled_cond_ = has_disabled; } //////////////////////////////////////////////////////////////// @@ -1483,7 +1403,7 @@ VertexIterator::findNextPin() Pin *pin = pin_iter_->next(); vertex_ = graph_->vertex(network_->vertexId(pin)); bidir_vertex_ = network_->direction(pin)->isBidirect() - ? graph_->pin_bidirect_drvr_vertex_map_.findKey(pin) + ? findKey(graph_->pin_bidirect_drvr_vertex_map_, pin) : nullptr; if (vertex_ || bidir_vertex_) return true; @@ -1518,14 +1438,14 @@ VertexIterator::findNext() } VertexInEdgeIterator::VertexInEdgeIterator(Vertex *vertex, - const Graph *graph) : + const Graph *graph) : next_(graph->edge(vertex->in_edges_)), graph_(graph) { } VertexInEdgeIterator::VertexInEdgeIterator(VertexId vertex_id, - const Graph *graph) : + const Graph *graph) : next_(graph->edge(graph->vertex(vertex_id)->in_edges_)), graph_(graph) { @@ -1541,7 +1461,7 @@ VertexInEdgeIterator::next() } VertexOutEdgeIterator::VertexOutEdgeIterator(Vertex *vertex, - const Graph *graph) : + const Graph *graph) : next_(graph->edge(vertex->out_edges_)), graph_(graph) { @@ -1562,9 +1482,9 @@ class FindEdgesThruHierPinVisitor : public HierPinThruVisitor { public: FindEdgesThruHierPinVisitor(EdgeSet &edges, - Graph *graph); + Graph *graph); virtual void visit(const Pin *drvr, - const Pin *load); + const Pin *load); protected: EdgeSet &edges_; @@ -1572,7 +1492,7 @@ class FindEdgesThruHierPinVisitor : public HierPinThruVisitor }; FindEdgesThruHierPinVisitor::FindEdgesThruHierPinVisitor(EdgeSet &edges, - Graph *graph) : + Graph *graph) : HierPinThruVisitor(), edges_(edges), graph_(graph) @@ -1581,7 +1501,7 @@ FindEdgesThruHierPinVisitor::FindEdgesThruHierPinVisitor(EdgeSet &edges, void FindEdgesThruHierPinVisitor::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr); Vertex *load_vertex = graph_->pinLoadVertex(load); @@ -1595,12 +1515,24 @@ FindEdgesThruHierPinVisitor::visit(const Pin *drvr, } EdgesThruHierPinIterator::EdgesThruHierPinIterator(const Pin *hpin, - Network *network, - Graph *graph) + Network *network, + Graph *graph) { FindEdgesThruHierPinVisitor visitor(edges_, graph); visitDrvrLoadsThruHierPin(hpin, network, &visitor); - edge_iter_.init(edges_); + edge_iter_ = edges_.begin(); +} + +bool +EdgesThruHierPinIterator::hasNext() +{ + return edge_iter_ != edges_.end(); +} + +Edge * +EdgesThruHierPinIterator::next() +{ + return *edge_iter_++; } //////////////////////////////////////////////////////////////// @@ -1617,9 +1549,5 @@ VertexIdLess::operator()(const Vertex *vertex1, return graph_->id(vertex1) < graph_->id(vertex2); } -VertexSet::VertexSet(Graph *&graph) : - Set(VertexIdLess(graph)) -{ -} } // namespace diff --git a/graph/Graph.i b/graph/Graph.i index 032c056a6..da7be0eef 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -31,8 +31,9 @@ #include "Liberty.hh" #include "Network.hh" #include "Clock.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Search.hh" +#include "Sdc.hh" #include "Sta.hh" using namespace sta; @@ -92,22 +93,22 @@ vertex_iterator() void set_arc_delay(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMaxAll *min_max, - float delay) + TimingArc *arc, + const Scene *scene, + const MinMaxAll *min_max, + float delay) { - Sta::sta()->setArcDelay(edge, arc, corner, min_max, delay); + Sta::sta()->setArcDelay(edge, arc, scene, min_max, delay); } void set_annotated_slew(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - const RiseFallBoth *rf, - float slew) + const Scene *scene, + const MinMaxAll *min_max, + const RiseFallBoth *rf, + float slew) { - Sta::sta()->setAnnotatedSlew(vertex, corner, min_max, rf, slew); + Sta::sta()->setAnnotatedSlew(vertex, scene, min_max, rf, slew); } // Remove all delay and slew annotations. @@ -132,20 +133,20 @@ int level() { return Sta::sta()->vertexLevel(self); } int tag_group_index() { return self->tagGroupIndex(); } Slew -slew(const RiseFall *rf, +slew(const RiseFallBoth *rf, const MinMax *min_max) { Sta *sta = Sta::sta(); - return sta->vertexSlew(self, rf, min_max); + return sta->slew(self, rf, sta->scenes(), min_max); } Slew -slew_corner(const RiseFall *rf, - const Corner *corner, +slew_scenes(const RiseFallBoth *rf, + const SceneSeq scenes, const MinMax *min_max) { Sta *sta = Sta::sta(); - return sta->vertexSlew(self, rf, corner, min_max); + return sta->slew(self, rf, scenes, min_max); } VertexOutEdgeIterator * @@ -160,162 +161,13 @@ in_edge_iterator() return new VertexInEdgeIterator(self, Sta::sta()->graph()); } -FloatSeq -arrivals_clk(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf) -{ - Sta *sta = Sta::sta(); - FloatSeq arrivals; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - arrivals.push_back(delayAsFloat(sta->vertexArrival(self, rf, clk_edge, - path_ap, nullptr))); - } - return arrivals; -} - -float -arrival(const MinMax *min_max) -{ - Sta *sta = Sta::sta(); - return delayAsFloat(sta->vertexArrival(self, min_max)); -} - -StringSeq -arrivals_clk_delays(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf, - int digits) -{ - Sta *sta = Sta::sta(); - StringSeq arrivals; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - arrivals.push_back(delayAsString(sta->vertexArrival(self, rf, clk_edge, - path_ap, nullptr), - sta, digits)); - } - return arrivals; -} - -FloatSeq -requireds_clk(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf) -{ - Sta *sta = Sta::sta(); - FloatSeq reqs; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - reqs.push_back(delayAsFloat(sta->vertexRequired(self, rf, clk_edge, - path_ap))); - } - return reqs; -} - -StringSeq -requireds_clk_delays(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf, - int digits) -{ - Sta *sta = Sta::sta(); - StringSeq reqs; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - reqs.push_back(delayAsString(sta->vertexRequired(self, rf, clk_edge, path_ap), - sta, digits)); - } - return reqs; -} - -Slack -slack(MinMax *min_max) -{ - Sta *sta = Sta::sta(); - return sta->vertexSlack(self, min_max); -} - -FloatSeq -slacks(RiseFall *rf) -{ - Sta *sta = Sta::sta(); - FloatSeq slacks; - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, path_ap))); - } - return slacks; -} - -// Slack with respect to a clock rise/fall edge. -FloatSeq -slacks_clk(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf) -{ - Sta *sta = Sta::sta(); - FloatSeq slacks; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, clk_edge, - path_ap))); - } - return slacks; -} - -StringSeq -slacks_clk_delays(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf, - int digits) -{ - Sta *sta = Sta::sta(); - StringSeq slacks; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - slacks.push_back(delayAsString(sta->vertexSlack(self, rf, clk_edge, - path_ap), - sta, digits)); - } - return slacks; -} - VertexPathIterator * path_iterator(const RiseFall *rf, - const MinMax *min_max) -{ - return Sta::sta()->vertexPathIterator(self, rf, min_max); -} - -bool -has_downstream_clk_pin() + const MinMax *min_max) { - return self->hasDownstreamClkPin(); + return new VertexPathIterator(self, rf, min_max, Sta::sta()); } -bool -is_clock() -{ - Sta *sta = Sta::sta(); - Search *search = sta->search(); - return search->isClock(self); -} - -bool is_disabled_constraint() { return self->isDisabledConstraint(); } - } // Vertex methods %extend Edge { @@ -328,62 +180,85 @@ const char *sense() { return to_string(self->sense()); } TimingArcSeq & timing_arcs() { return self->timingArcSet()->arcs(); } bool is_disabled_loop() { return Sta::sta()->isDisabledLoop(self); } -bool is_disabled_constraint() { return Sta::sta()->isDisabledConstraint(self);} -bool is_disabled_constant() { return Sta::sta()->isDisabledConstant(self); } + +bool is_disabled_constraint() +{ + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->isDisabledConstraint(self, sdc); +} + +bool is_disabled_constant() +{ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return sta->isDisabledConstant(self, mode); +} + bool is_disabled_cond_default() { return Sta::sta()->isDisabledCondDefault(self); } + PinSet -disabled_constant_pins() { return Sta::sta()->disabledConstantPins(self); } +disabled_constant_pins() +{ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return sta->disabledConstantPins(self, mode); +} + bool is_disabled_bidirect_inst_path() { return Sta::sta()->isDisabledBidirectInstPath(self); } -bool is_disabled_bidirect_net_path() -{ return Sta::sta()->isDisabledBidirectNetPath(self); } bool is_disabled_preset_clear() { return Sta::sta()->isDisabledPresetClr(self); } const char * -sim_timing_sense(){return to_string(Sta::sta()->simTimingSense(self));} +sim_timing_sense(){ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return to_string(sta->simTimingSense(self, mode)); +} FloatSeq arc_delays(TimingArc *arc) { Sta *sta = Sta::sta(); FloatSeq delays; - for (auto dcalc_ap : sta->corners()->dcalcAnalysisPts()) - delays.push_back(delayAsFloat(sta->arcDelay(self, arc, dcalc_ap))); + 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))); return delays; } StringSeq arc_delay_strings(TimingArc *arc, - int digits) + int digits) { Sta *sta = Sta::sta(); StringSeq delays; - for (auto dcalc_ap : sta->corners()->dcalcAnalysisPts()) - delays.push_back(delayAsString(sta->arcDelay(self, arc, dcalc_ap), + 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)); return delays; } bool delay_annotated(TimingArc *arc, - const Corner *corner, - const MinMax *min_max) + const Scene *scene, + const MinMax *min_max) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - return Sta::sta()->arcDelayAnnotated(self, arc, dcalc_ap); + return Sta::sta()->arcDelayAnnotated(self, arc, scene, min_max); } float arc_delay(TimingArc *arc, - const Corner *corner, - const MinMax *min_max) + const Scene *scene, + const MinMax *min_max) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - return delayAsFloat(Sta::sta()->arcDelay(self, arc, dcalc_ap)); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + return delayAsFloat(Sta::sta()->arcDelay(self, arc, ap_index)); } -string +std::string cond() { FuncExpr *cond = self->timingArcSet()->cond(); diff --git a/graph/Graph.tcl b/graph/Graph.tcl index 331fcc731..72c72e429 100644 --- a/graph/Graph.tcl +++ b/graph/Graph.tcl @@ -37,20 +37,20 @@ proc report_edges { args } { 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 } } } 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 } } 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 } } } @@ -61,9 +61,9 @@ proc report_edges_between_ { from_vertex to_vertex } { 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 } else { - report_edge_ $edge vertex_port_name vertex_port_name + report_edge_ $edge vertex_port_name vertex_port_name } } } @@ -78,11 +78,11 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } { set edge [$iter next] if { [$edge role] != "wire" } { if { !$device_header } { - set pin [$vertex pin] - if { ![$pin is_top_level_port] } { - set inst [$pin instance] - } - set device_header 1 + set pin [$vertex pin] + if { ![$pin is_top_level_port] } { + set inst [$pin instance] + } + set device_header 1 } report_edge_ $edge vertex_port_name vertex_port_name } @@ -180,10 +180,6 @@ proc edge_disable_reason { edge } { if { $disables != "" } { append disables ", " } append disables "bidirect instance path" } - if [$edge is_disabled_bidirect_net_path] { - if { $disables != "" } { append disables ", " } - append disables "bidirect net path" - } if { [$edge is_disabled_preset_clear] } { if { $disables != "" } { append disables ", " } append disables "sta_preset_clear_arcs_enabled" @@ -252,7 +248,7 @@ proc_redirect report_disabled_edges { set to_port_name [get_name [$to_pin port]] set cond [$edge cond] if { $cond != "" } { - set when " when: $cond" + set when " when: $cond" } else { set when "" } @@ -295,28 +291,6 @@ proc edge_disable_reason_verbose { edge } { return $disables } -################################################################ - -define_cmd_args "report_slews" {[-corner corner] pin} - -proc report_slews { args } { - global sta_report_default_digits - - parse_key_args "report_slews" args keys {-corner} flags {} - check_argc_eq1 "report_slews" $args - - set corner [parse_corner_or_all keys] - set pin [get_port_pin_error "pin" [lindex $args 0]] - set digits $sta_report_default_digits - foreach vertex [$pin vertices] { - if { $corner == "NULL" } { - report_line "[vertex_path_name $vertex] [rise_short_name] [format_time [$vertex slew rise min] $digits]:[format_time [$vertex slew rise max] $digits] [fall_short_name] [format_time [$vertex slew fall min] $digits]:[format_time [$vertex slew fall max] $digits]" - } else { - report_line "[vertex_path_name $vertex] [rise_short_name] [format_time [$vertex slew_corner rise $corner min] $digits]:[format_time [$vertex slew_corner rise $corner max] $digits] [fall_short_name] [format_time [$vertex slew_corner fall $corner min] $digits]:[format_time [$vertex slew_corner fall $corner max] $digits]" - } - } -} - proc vertex_path_name { vertex } { set pin [$vertex pin] set pin_name [get_full_name $pin] @@ -348,8 +322,8 @@ proc hier_pins_crossed_by_edge { edge } { set to_pins [hier_pins_above [[$edge to] pin]] foreach p $to_pins { report_line [$p path_name] } while { [llength $from_pins] > 0 \ - && [llength $to_pins] > 0 \ - && [lindex $from_pins 0] == [lindex $to_pins 0] } { + && [llength $to_pins] > 0 \ + && [lindex $from_pins 0] == [lindex $to_pins 0] } { set from_pins [lrange $from_pins 1 end] set to_pins [lrange $to_pins 1 end] } @@ -367,9 +341,9 @@ proc hier_pins_above { pin } { while {[$parent_pin_iter has_next]} { set parent_pin [$parent_pin_iter next] if {[$parent_pin net] == $net} { - set pins_above [concat [list $parent_pin] $pins_above] - set found 1 - break + set pins_above [concat [list $parent_pin] $pins_above] + set found 1 + break } } $parent_pin_iter finish diff --git a/graph/GraphCmp.cc b/graph/GraphCmp.cc index 4001fb821..92aa24d06 100644 --- a/graph/GraphCmp.cc +++ b/graph/GraphCmp.cc @@ -22,6 +22,7 @@ // // This notice may not be removed or altered from any source distribution. +#include "ContainerHelpers.hh" #include "StringUtil.hh" #include "Network.hh" #include "NetworkCmp.hh" @@ -37,7 +38,7 @@ VertexNameLess::VertexNameLess(Network *network) : bool VertexNameLess::operator()(const Vertex *vertex1, - const Vertex *vertex2) + const Vertex *vertex2) { return network_->pathNameLess(vertex1->pin(), vertex2->pin()); } @@ -45,7 +46,7 @@ VertexNameLess::operator()(const Vertex *vertex1, //////////////////////////////////////////////////////////////// EdgeLess::EdgeLess(const Network *network, - Graph *&graph) : + Graph *&graph) : pin_less_(network), graph_(graph) { @@ -53,7 +54,7 @@ EdgeLess::EdgeLess(const Network *network, bool EdgeLess::operator()(const Edge *edge1, - const Edge *edge2) const + const Edge *edge2) const { const Pin *from1 = edge1->from(graph_)->pin(); const Pin *from2 = edge2->from(graph_)->pin(); @@ -61,13 +62,13 @@ EdgeLess::operator()(const Edge *edge1, const Pin *to2 = edge2->to(graph_)->pin(); return pin_less_(from1, from2) || (from1 == from2 - && pin_less_(to1, to2)); + && pin_less_(to1, to2)); } void sortEdges(EdgeSeq *edges, - Network *network, - Graph *graph) + Network *network, + Graph *graph) { sort(edges, EdgeLess(network, graph)); } diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index aaf0bf056..59e431fc6 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -40,17 +40,16 @@ namespace sta { -class Corner; +class Scene; class Parasitic; -class DcalcAnalysisPt; class MultiDrvrNet; class ArcDcalcArg; -typedef std::vector ArcDcalcArgPtrSeq; -typedef std::vector ArcDcalcArgSeq; +using ArcDcalcArgPtrSeq = std::vector; +using ArcDcalcArgSeq = std::vector; // Driver load pin -> index in driver loads. -typedef std::map LoadPinIndexMap; +using LoadPinIndexMap = std::map; // Arguments for gate delay calculation delay/slew at one driver pin // through one timing arc at one delay calc analysis point. @@ -81,7 +80,7 @@ public: const Net *drvrNet(const Network *network) const; Edge *edge() const { return edge_; } const TimingArc *arc() const { return arc_; } - Slew inSlew() const { return in_slew_; } + const Slew &inSlew() const { return in_slew_; } float inSlewFlt() const; void setInSlew(Slew in_slew); const Parasitic *parasitic() const { return parasitic_; } @@ -138,8 +137,7 @@ protected: std::vector load_slews_; }; -typedef std::vector ArcDcalcArgSeq; -typedef std::vector ArcDcalcResultSeq; +using ArcDcalcResultSeq = std::vector; // Delay calculator class hierarchy. // ArcDelayCalc @@ -160,7 +158,7 @@ typedef std::vector ArcDcalcResultSeq; class ArcDelayCalc : public StaState { public: - explicit ArcDelayCalc(StaState *sta); + ArcDelayCalc(StaState *sta); virtual ~ArcDelayCalc() {} virtual ArcDelayCalc *copy() = 0; virtual const char *name() const = 0; @@ -168,26 +166,30 @@ public: // Find the parasitic for drvr_pin that is acceptable to the delay // calculator by probing parasitics_. virtual Parasitic *findParasitic(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) = 0; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) = 0; virtual bool reduceSupported() const = 0; // Reduce parasitic_network to a representation acceptable to the delay calculator. virtual Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Reduce parasitic_network to a representation acceptable to the delay calculator - // for one or more corners and min/max rise/fall. - // Null corner means reduce all corners. + // for one or more scenes and min/max rise/fall. + // Null scene means reduce all scenes. virtual void reduceParasitic(const Parasitic *parasitic_network, const Net *net, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max) = 0; // Set the in_slew, load_cap, parasitic for gates. virtual void setDcalcArgParasiticSlew(ArcDcalcArg &gate, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; virtual void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Find the wire delays and slews for an input port without a driving cell. // This call primarily initializes the load delay/slew iterator. virtual ArcDcalcResult inputPortDelay(const Pin *port_pin, @@ -195,7 +197,8 @@ public: const RiseFall *rf, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Find the delay and slew for arc driving drvr_pin. virtual ArcDcalcResult gateDelay(const Pin *drvr_pin, @@ -205,23 +208,26 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // deprecated 2024-02-27 virtual void gateDelay(const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) __attribute__ ((deprecated)); + const Slew &in_slew, + float load_cap, + const Parasitic *parasitic, + float related_out_cap, + const Pvt *pvt, + const Scene *scene, + const MinMax *min_max, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) __attribute__ ((deprecated)); // Find gate delays and slews for parallel gates. virtual ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Find the delay for a timing check arc given the arc's // from/clock, to/data slews and related output pin parasitic. @@ -230,7 +236,8 @@ public: const Slew &from_slew, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap) = 0; + const Scene *scene, + const MinMax *min_max) = 0; // Report delay and slew calculation. virtual std::string reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, @@ -238,7 +245,8 @@ public: float load_cap, const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) = 0; // Report timing check delay calculation. virtual std::string reportCheckDelay(const Pin *check_pin, @@ -247,7 +255,8 @@ public: const char *from_slew_annotation, const Slew &to_slew, float related_out_cap, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, int digits) = 0; virtual void finishDrvrPin() = 0; }; diff --git a/include/sta/Bdd.hh b/include/sta/Bdd.hh index b0497d259..ee2249759 100644 --- a/include/sta/Bdd.hh +++ b/include/sta/Bdd.hh @@ -34,8 +34,8 @@ struct DdManager; namespace sta { -typedef std::map BddPortVarMap; -typedef std::map BddVarIdxPortMap; +using BddPortVarMap = std::map; +using BddVarIdxPortMap = std::map; class Bdd : public StaState { diff --git a/include/sta/Bfs.hh b/include/sta/Bfs.hh index c95fc5cb0..9b6396248 100644 --- a/include/sta/Bfs.hh +++ b/include/sta/Bfs.hh @@ -25,9 +25,9 @@ #pragma once #include +#include #include "Iterator.hh" -#include "Set.hh" #include "GraphClass.hh" #include "VertexVisitor.hh" #include "StaState.hh" @@ -39,7 +39,7 @@ class BfsFwdIterator; class BfsBkwdIterator; // LevelQueue is a vector of vertex vectors indexed by logic level. -typedef Vector LevelQueue; +using LevelQueue = std::vector; // Abstract base class for forward and backward breadth first search iterators. // Visit all of the vertices at a level before moving to the next. @@ -58,19 +58,19 @@ public: void ensureSize(); // Reset to virgin state. void clear(); - bool empty() const; + [[nodiscard]] bool empty() const; // Enqueue a vertex to search from. void enqueue(Vertex *vertex); // Enqueue vertices adjacent to a vertex. void enqueueAdjacentVertices(Vertex *vertex); - void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred); - void enqueueAdjacentVertices(Vertex *vertex, - Level to_level); + virtual void enqueueAdjacentVertices(Vertex *vertex, + const Mode *mode); virtual void enqueueAdjacentVertices(Vertex *vertex, SearchPred *search_pred, - Level to_level) = 0; - bool inQueue(Vertex *vertex); + const Mode *mode) = 0; + virtual void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred) = 0; + [[nodiscard]] bool inQueue(Vertex *vertex); void checkInQueue(Vertex *vertex); // Notify iterator that vertex will be deleted. void deleteVertexBefore(Vertex *vertex); @@ -131,9 +131,11 @@ public: SearchPred *search_pred, StaState *sta); virtual ~BfsFwdIterator(); + virtual void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred); virtual void enqueueAdjacentVertices(Vertex *vertex, SearchPred *search_pred, - Level to_level); + const Mode *mode); using BfsIterator::enqueueAdjacentVertices; protected: @@ -151,9 +153,11 @@ public: SearchPred *search_pred, StaState *sta); virtual ~BfsBkwdIterator(); + virtual void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred); virtual void enqueueAdjacentVertices(Vertex *vertex, SearchPred *search_pred, - Level to_level); + const Mode *mode); using BfsIterator::enqueueAdjacentVertices; protected: diff --git a/include/sta/BoundedHeap.hh b/include/sta/BoundedHeap.hh new file mode 100644 index 000000000..45bfdeed3 --- /dev/null +++ b/include/sta/BoundedHeap.hh @@ -0,0 +1,256 @@ +// 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 +#include +#include + +namespace sta { + +// BoundedHeap: A container that maintains the top N elements using a min-heap. +// This provides O(log n) insertion when the heap is full, O(1) when not full, +// and O(n log n) extraction of all elements. Useful for maintaining top K +// elements without storing all elements. +// +// The heap maintains the "worst" (minimum according to Compare) element at +// the root, so new elements that are better than the worst can replace it. +// For example, with Compare = std::greater, this maintains the N largest +// values (greater values are "better"). +// +// Template parameters: +// T: The element type +// Compare: Comparison function object type (default: std::less) +// For top N largest, use std::greater +// For top N smallest, use std::less +template > +class BoundedHeap { +public: + using value_type = T; + using size_type = size_t; + using const_reference = const T&; + using compare_type = Compare; + + // Constructors + explicit BoundedHeap(size_type max_size, + const Compare& comp = Compare()) : + max_size_(max_size), + comp_(comp), + min_heap_comp_(comp) + { + heap_.reserve(max_size); + } + + // Copy constructor + BoundedHeap(const BoundedHeap& other) : + heap_(other.heap_), + max_size_(other.max_size_), + comp_(other.comp_), + min_heap_comp_(other.comp_) + {} + + // Assignment operator + BoundedHeap& operator=(const BoundedHeap& other) + { + if (this != &other) { + heap_ = other.heap_; + max_size_ = other.max_size_; + comp_ = other.comp_; + min_heap_comp_ = MinHeapCompare(other.comp_); + } + return *this; + } + + // Move constructor + BoundedHeap(BoundedHeap&& other) noexcept : + heap_(std::move(other.heap_)), + max_size_(other.max_size_), + comp_(std::move(other.comp_)), + min_heap_comp_(comp_) + {} + + // Move assignment operator + BoundedHeap& operator=(BoundedHeap&& other) noexcept + { + if (this != &other) { + heap_ = std::move(other.heap_); + max_size_ = other.max_size_; + comp_ = std::move(other.comp_); + min_heap_comp_ = MinHeapCompare(comp_); + } + return *this; + } + + void + setMaxSize(size_t max_size) + { + max_size_ = max_size; + heap_.reserve(max_size); + } + + // Insert an element into the heap. + // If the heap is not full, the element is added. + // If the heap is full and the new element is better than the worst element, + // the worst element is replaced. Otherwise, the element is ignored. + // Returns true if the element was inserted, false if it was ignored. + bool + insert(const T& value) { + if (heap_.size() < max_size_) { + heap_.push_back(value); + std::push_heap(heap_.begin(), heap_.end(), min_heap_comp_); + return true; + } + else if (!heap_.empty()) { + // When keeping N worst (smallest) values: if new value is smaller than worst, + // we should keep it and remove the largest element to make room. + // If new value is larger than worst, we reject it (already have worse values). + // comp_(value, worst) is true when value < worst (value is smaller/worse) + if (comp_(value, heap_.front())) { + // New value is smaller than worst - find and replace the largest element + auto max_it = std::max_element(heap_.begin(), heap_.end(), comp_); + *max_it = value; + // Rebuild heap since we modified an internal element + std::make_heap(heap_.begin(), heap_.end(), min_heap_comp_); + return true; + } + // Otherwise, new value is >= worst, so we already have worse values - reject it + } + return false; + } + + // Insert an element using move semantics + bool insert(T&& value) + { + if (heap_.size() < max_size_) { + heap_.push_back(std::move(value)); + std::push_heap(heap_.begin(), heap_.end(), min_heap_comp_); + return true; + } + else if (!heap_.empty()) { + // When keeping N worst (smallest) values: if new value is smaller than worst, + // we should keep it and remove the largest element to make room. + // If new value is larger than worst, we reject it (already have worse values). + // comp_(value, worst) is true when value < worst (value is smaller/worse) + if (comp_(value, heap_.front())) { + // New value is smaller than worst - find and replace the largest element + auto max_it = std::max_element(heap_.begin(), heap_.end(), comp_); + *max_it = std::move(value); + // Rebuild heap since we modified an internal element + std::make_heap(heap_.begin(), heap_.end(), min_heap_comp_); + return true; + } + // Otherwise, new value is >= worst, so we already have worse values - reject it + } + return false; + } + + // Extract all elements sorted from best to worst. + // This destroys the heap structure but preserves the elements. + std::vector extract() + { + // Convert heap to sorted vector (best to worst) + std::sort_heap(heap_.begin(), heap_.end(), min_heap_comp_); + // Reverse to get best first (according to user's comparison) + std::reverse(heap_.begin(), heap_.end()); + std::vector result = std::move(heap_); + heap_.clear(); + return result; + } + + // Extract all elements sorted from best to worst (const version). + // Creates a copy since we can't modify the heap. + std::vector extract() const + { + std::vector temp_heap = heap_; + std::sort_heap(temp_heap.begin(), temp_heap.end(), min_heap_comp_); + std::reverse(temp_heap.begin(), temp_heap.end()); + return temp_heap; + } + + // Get the worst element (the one that would be replaced next). + // Requires !empty() + const_reference worst() const + { + return heap_.front(); + } + + // Check if the heap is empty + bool empty() const + { + return heap_.empty(); + } + + // Get the current number of elements in the heap + size_type size() const + { + return heap_.size(); + } + + // Get the maximum size of the heap + size_type max_size() const + { + return max_size_; + } + + // Check if the heap is full + bool full() const + { + return heap_.size() >= max_size_; + } + + // Clear all elements from the heap + void clear() + { + heap_.clear(); + } + + // Get the comparison function + Compare compare() const + { + return comp_; + } + +private: + std::vector heap_; + size_type max_size_; + Compare comp_; + + // Helper comparator for min-heap: we want the worst element at root + // so we can easily remove it when adding better elements. + // This is the inverse of the user's comparison. + struct MinHeapCompare + { + Compare comp_; + explicit MinHeapCompare(const Compare& c) : comp_(c) {} + bool operator()(const T& a, const T& b) const { + return comp_(b, a); // Inverted: worst is at root + } + }; + + MinHeapCompare min_heap_comp_; +}; + +} // namespace sta + diff --git a/include/sta/ClkNetwork.hh b/include/sta/ClkNetwork.hh index 62d89a781..8b4692fda 100644 --- a/include/sta/ClkNetwork.hh +++ b/include/sta/ClkNetwork.hh @@ -24,8 +24,8 @@ #pragma once -#include "Map.hh" -#include "Set.hh" +#include + #include "StaState.hh" #include "NetworkClass.hh" #include "GraphClass.hh" @@ -33,31 +33,34 @@ namespace sta { -typedef Map PinClksMap; -typedef Map ClkPinsMap; +using PinClksMap = std::map; +using ClkPinsMap = std::map; class Sta; // Find clock network pins. -// This is not as reliable as Search::isClock but is much cheaper. class ClkNetwork : public StaState { public: - ClkNetwork(StaState *sta); + ClkNetwork(Mode *mode, + StaState *sta); ~ClkNetwork(); void ensureClkNetwork(); void clear(); bool isClock(const Pin *pin) const; + bool isClock(const Vertex *vertex) const; bool isClock(const Net *net) const; bool isIdealClock(const Pin *pin) const; + bool isIdealClock(const Vertex *vertex) const; bool isPropagatedClock(const Pin *pin) const; - const ClockSet *clocks(const Pin *pin); - const ClockSet *idealClocks(const Pin *pin); + const ClockSet *clocks(const Pin *pin) const; + const ClockSet *clocks(const Vertex *vertex) const; + const ClockSet *idealClocks(const Pin *pin) const; const PinSet *pins(const Clock *clk); void clkPinsInvalid(); float idealClkSlew(const Pin *pin, const RiseFall *rf, - const MinMax *min_max); + const MinMax *min_max) const; protected: void deletePinBefore(const Pin *pin); @@ -66,9 +69,11 @@ protected: friend class Sta; private: + Mode *mode_; + void findClkPins(); void findClkPins(bool ideal_only, - PinClksMap &clk_pin_map); + PinClksMap &clk_pin_map); bool clk_pins_valid_; // pin -> clks diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index d372aab2f..25dd92443 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "MinMax.hh" #include "RiseFallMinMax.hh" #include "SdcClass.hh" @@ -32,7 +34,7 @@ namespace sta { -typedef Map ClkHpinEdgeMap; +using ClkHpinEdgeMap = std::map; class Clock : public SdcCmdComment { @@ -63,40 +65,40 @@ public: bool isIdeal() const { return !is_propagated_; } // Ideal clock slew. void slew(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &slew, - bool &exists) const; + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const; // Return zero (default) if no slew exists. float slew(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; void setSlew(const RiseFall *rf, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew); void setSlew(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); + const MinMaxAll *min_max, + float slew); void removeSlew(); const RiseFallMinMax &slews() const { return slews_; } void setSlewLimit(const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew); + const PathClkOrData clk_data, + const MinMax *min_max, + float slew); void slewLimit(const RiseFall *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - // Return values. - float &slew, - bool &exists) const; + const PathClkOrData clk_data, + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const; ClockUncertainties *uncertainties() const { return uncertainties_; } void uncertainty(const SetupHold *setup_hold, - // Return values. - float &uncertainty, - bool &exists) const; + // Return values. + float &uncertainty, + bool &exists) const; void setUncertainty(const SetupHoldAll *setup_hold, - float uncertainty); + float uncertainty); void setUncertainty(const SetupHold *setup_hold, - float uncertainty); + float uncertainty); void removeUncertainty(const SetupHoldAll *setup_hold); void setPeriod(float period); @@ -124,8 +126,8 @@ public: bool isDivideByOneCombinational() const; bool generatedUpToDate() const; void srcPinVertices(VertexSet &src_vertices, - const Network *network, - Graph *graph); + const Network *network, + Graph *graph); // True if the generated clock waveform is up to date. bool waveformValid() const { return waveform_valid_; } void waveformInvalid(); @@ -133,36 +135,36 @@ public: protected: // Private to Sdc::makeClock. Clock(const char *name, - int index, + int index, const Network *network); void initClk(PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - const char *comment, - const Network *network); + bool add_to_pins, + float period, + FloatSeq *waveform, + const char *comment, + const Network *network); void initGeneratedClk(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, - bool is_propagated, - const char *comment, - const Network *network); + 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, + bool is_propagated, + const char *comment, + const Network *network); void setPins(PinSet *pins, - const Network *network); + const Network *network); void setMasterClk(Clock *master); void makeClkEdges(); void setClkEdgeTimes(); void setClkEdgeTime(const RiseFall *rf); void generateScaledClk(const Clock *src_clk, - float scale); + float scale); void generateEdgesClk(const Clock *src_clk); const char *name_; @@ -229,10 +231,10 @@ clkCmp(const Clock *clk1, const Clock *clk2); int clkEdgeCmp(const ClockEdge *clk_edge1, - const ClockEdge *clk_edge2); + const ClockEdge *clk_edge2); bool clkEdgeLess(const ClockEdge *clk_edge1, - const ClockEdge *clk_edge2); + const ClockEdge *clk_edge2); class ClockNameLess { @@ -247,24 +249,24 @@ class InterClockUncertainty { public: InterClockUncertainty(const Clock *src, - const Clock *target); + const Clock *target); const Clock *src() const { return src_; } const Clock *target() const { return target_; } void uncertainty(const RiseFall *src_rf, - const RiseFall *tgt_rf, - const SetupHold *setup_hold, - // Return values. - float &uncertainty, - bool &exists) const; + const RiseFall *tgt_rf, + const SetupHold *setup_hold, + // Return values. + float &uncertainty, + bool &exists) const; void setUncertainty(const RiseFallBoth *src_rf, - const RiseFallBoth *tgt_rf, - const SetupHoldAll *setup_hold, - float uncertainty); + const RiseFallBoth *tgt_rf, + const SetupHoldAll *setup_hold, + float uncertainty); void removeUncertainty(const RiseFallBoth *src_rf, - const RiseFallBoth *tgt_rf, - const SetupHoldAll *setup_hold); + const RiseFallBoth *tgt_rf, + const SetupHoldAll *setup_hold); const RiseFallMinMax *uncertainties(const RiseFall *src_rf) const; - bool empty() const; + [[nodiscard]] bool empty() const; private: const Clock *src_; @@ -276,14 +278,14 @@ class InterClockUncertaintyLess { public: bool operator()(const InterClockUncertainty *inter1, - const InterClockUncertainty *inter2) const; + const InterClockUncertainty *inter2) const; }; class ClkNameLess { public: bool operator()(const Clock *clk1, - const Clock *clk2) const + const Clock *clk2) const { return stringLess(clk1->name(), clk2->name()); } diff --git a/include/sta/ClockGroups.hh b/include/sta/ClockGroups.hh index 509440344..38e905c11 100644 --- a/include/sta/ClockGroups.hh +++ b/include/sta/ClockGroups.hh @@ -33,11 +33,11 @@ class ClockGroups : public SdcCmdComment { public: ClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment); + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + const char *comment); ~ClockGroups(); void makeClockGroup(ClockSet *clks); const char *name() const { return name_; } diff --git a/include/sta/ClockInsertion.hh b/include/sta/ClockInsertion.hh index 1496dedae..43fc48999 100644 --- a/include/sta/ClockInsertion.hh +++ b/include/sta/ClockInsertion.hh @@ -39,16 +39,16 @@ public: const Clock *clock() const { return clk_; } const Pin *pin() const { return pin_; } float delay(const RiseFall *rf, const MinMax *min_max, - const EarlyLate *early_late); + const EarlyLate *early_late); void delay(const RiseFall *rf, const MinMax *min_max, - const EarlyLate *early_late, - // Return values. - float &insertion, bool &exists); + const EarlyLate *early_late, + // Return values. + float &insertion, bool &exists); RiseFallMinMax *delays(const EarlyLate *early_late); void setDelay(const RiseFall *rf, const MinMax *min_max, - const EarlyLate *early_late, float delay); + const EarlyLate *early_late, float delay); void setDelay(const RiseFallBoth *rf, const MinMaxAll *min_max, - const EarlyLateAll *early_late, float delay); + const EarlyLateAll *early_late, float delay); void setDelays(RiseFallMinMax *delays); private: diff --git a/include/sta/ClockLatency.hh b/include/sta/ClockLatency.hh index 9e05680b0..9972bdc40 100644 --- a/include/sta/ClockLatency.hh +++ b/include/sta/ClockLatency.hh @@ -36,23 +36,23 @@ class ClockLatency { public: ClockLatency(const Clock *clk, - const Pin *pin); + const Pin *pin); const Clock *clock() const { return clk_; } const Pin *pin() const { return pin_; } float delay(const RiseFall *rf, - const MinMax *min_max); + const MinMax *min_max); void delay(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists); + const MinMax *min_max, + // Return values. + float &latency, + bool &exists); RiseFallMinMax *delays(); void setDelay(const RiseFall *rf, - const MinMax *min_max, - float delay); + const MinMax *min_max, + float delay); void setDelay(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay); + const MinMaxAll *min_max, + float delay); void setDelays(RiseFallMinMax *delays); private: diff --git a/include/sta/ConcreteLibrary.hh b/include/sta/ConcreteLibrary.hh index d4eb6ff89..48c4db06b 100644 --- a/include/sta/ConcreteLibrary.hh +++ b/include/sta/ConcreteLibrary.hh @@ -25,9 +25,9 @@ #pragma once #include +#include +#include -#include "Vector.hh" -#include "Map.hh" #include "StringUtil.hh" #include "NetworkClass.hh" @@ -45,19 +45,19 @@ class PatternMatch; class LibertyCell; class LibertyPort; -typedef Map ConcreteCellMap; -typedef Vector ConcretePortSeq; -typedef Map ConcretePortMap; -typedef ConcreteCellMap::ConstIterator ConcreteLibraryCellIterator; -typedef ConcretePortSeq::ConstIterator ConcreteCellPortIterator; -typedef ConcretePortSeq::ConstIterator ConcretePortMemberIterator; +using ConcreteCellMap = std::map; +using ConcretePortSeq = std::vector; +using ConcretePortMap = std::map; +using ConcreteLibraryCellIterator = MapIterator; +using ConcreteCellPortIterator = VectorIterator; +using ConcretePortMemberIterator = VectorIterator; class ConcreteLibrary { public: - explicit ConcreteLibrary(const char *name, - const char *filename, - bool is_liberty); + ConcreteLibrary(const char *name, + const char *filename, + bool is_liberty); virtual ~ConcreteLibrary(); const char *name() const { return name_.c_str(); } void setName(const char *name); @@ -66,8 +66,8 @@ public: const char *filename() const { return filename_.c_str(); } void addCell(ConcreteCell *cell); ConcreteCell *makeCell(const char *name, - bool is_leaf, - const char *filename); + bool is_leaf, + const char *filename); void deleteCell(ConcreteCell *cell); ConcreteLibraryCellIterator *cellIterator() const; ConcreteCell *findCell(const char *name) const; @@ -75,11 +75,11 @@ public: char busBrktLeft() const { return bus_brkt_left_; } char busBrktRight() const { return bus_brkt_right_; } void setBusBrkts(char left, - char right); + char right); protected: void renameCell(ConcreteCell *cell, - const char *cell_name); + const char *cell_name); std::string name_; ObjectId id_; @@ -122,14 +122,14 @@ public: ConcretePort *makePort(const char *name); // Bus port. ConcretePort *makeBusPort(const char *name, - int from_index, - int to_index); + int from_index, + int to_index); // Bundle port. ConcretePort *makeBundlePort(const char *name, - ConcretePortSeq *members); + ConcretePortSeq *members); // Group previously defined bus bit ports together. void groupBusPorts(const char bus_brkt_left, - const char bus_brkt_right, + const char bus_brkt_right, std::function port_msb_first); size_t portCount() const; void setName(const char *name); @@ -138,23 +138,23 @@ public: protected: ConcreteCell(const char *name, - const char *filename, + const char *filename, bool is_leaf, ConcreteLibrary *library); ConcretePort *makeBusPort(const char *name, - int from_index, - int to_index, - ConcretePortSeq *members); + int from_index, + int to_index, + ConcretePortSeq *members); void makeBusPortBits(ConcretePort *bus_port, - const char *name, - int from_index, - int to_index); + const char *name, + int from_index, + int to_index); // Bus port bit (internal to makeBusPortBits). ConcretePort *makePort(const char *bit_name, - int bit_index); + int bit_index); void makeBusPortBit(ConcretePort *bus_port, - const char *name, - int index); + const char *name, + int index); std::string name_; ObjectId id_; @@ -233,10 +233,10 @@ protected: // Constructors for factory in cell class. ConcretePort(const char *name, bool is_bus, - int from_index, - int to_index, - bool is_bundle, - ConcretePortSeq *member_ports, + int from_index, + int to_index, + bool is_bundle, + ConcretePortSeq *member_ports, ConcreteCell *cell); std::string name_; @@ -263,14 +263,15 @@ private: class ConcreteCellPortBitIterator : public Iterator { public: - explicit ConcreteCellPortBitIterator(const ConcreteCell *cell); + ConcreteCellPortBitIterator(const ConcreteCell *cell); virtual bool hasNext(); virtual ConcretePort *next(); private: void findNext(); - ConcretePortSeq::ConstIterator port_iter_; + const ConcretePortSeq &ports_; + ConcretePortSeq::const_iterator port_iter_; ConcretePortMemberIterator *member_iter_; ConcretePort *next_; }; diff --git a/include/sta/ConcreteNetwork.hh b/include/sta/ConcreteNetwork.hh index 162598735..fe2b76c31 100644 --- a/include/sta/ConcreteNetwork.hh +++ b/include/sta/ConcreteNetwork.hh @@ -25,9 +25,10 @@ #pragma once #include +#include +#include +#include -#include "Map.hh" -#include "Set.hh" #include "StringUtil.hh" #include "Network.hh" #include "LibertyClass.hh" @@ -45,16 +46,14 @@ class ConcretePort; class ConcreteBindingTbl; class ConcreteLibertyLibraryIterator; -typedef Vector ConcreteLibrarySeq; -typedef Map ConcreteLibraryMap; -typedef ConcreteLibrarySeq::ConstIterator ConcreteLibraryIterator; -typedef Map ConcreteInstanceChildMap; -typedef Map ConcreteInstanceNetMap; -typedef Vector ConcreteNetSeq; -typedef Vector ConcretePinSeq; -typedef Map CellNetworkViewMap; -typedef Set ConcreteNetSet; +using ConcreteLibrarySeq = std::vector; +using ConcreteLibraryMap = std::map; +using ConcreteInstanceChildMap = std::map; +using ConcreteInstanceNetMap = std::map; +using ConcreteNetSeq = std::vector; +using ConcretePinSeq = std::vector; +using CellNetworkViewMap = std::map; +using ConcreteNetSet = std::set; // This adapter implements the network api for the concrete network. // A superset of the Network api methods are implemented in the interface. @@ -172,7 +171,7 @@ public: ConstantPinIterator *constantPinIterator() override; void addConstantNet(Net *net, - LogicValue value) override; + LogicValue value) override; // Edit methods. Library *makeLibrary(const char *name, @@ -270,12 +269,12 @@ protected: PinVisitor &visitor, NetSet &visited_nets) const override; Instance *makeConcreteInstance(ConcreteCell *cell, - const char *name, - Instance *parent); + const char *name, + Instance *parent); void disconnectNetPin(ConcreteNet *cnet, - ConcretePin *cpin); + ConcretePin *cpin); void connectNetPin(ConcreteNet *cnet, - ConcretePin *cpin); + ConcretePin *cpin); // Cell lookup search order sequence. ConcreteLibrarySeq library_seq_; @@ -315,14 +314,14 @@ public: void deletePin(ConcretePin *pin); void addNet(ConcreteNet *net); void addNet(const char *name, - ConcreteNet *net); + ConcreteNet *net); void deleteNet(ConcreteNet *net); void setCell(ConcreteCell *cell); void initPins(); protected: ConcreteInstance(const char *name, - ConcreteCell *cell, + ConcreteCell *cell, ConcreteInstance *parent); ~ConcreteInstance(); @@ -356,8 +355,8 @@ public: protected: ~ConcretePin() {} ConcretePin(ConcreteInstance *instance, - ConcretePort *port, - ConcreteNet *net); + ConcretePort *port, + ConcreteNet *net); ConcreteInstance *instance_; ConcretePort *port_; @@ -386,7 +385,7 @@ public: protected: ~ConcreteTerm() {} ConcreteTerm(ConcretePin *pin, - ConcreteNet *net); + ConcreteNet *net); ConcretePin *pin_; ConcreteNet *net_; @@ -415,7 +414,7 @@ public: protected: ConcreteNet(const char *name, - ConcreteInstance *instance); + ConcreteInstance *instance); ~ConcreteNet(); const char *name_; ObjectId id_; diff --git a/include/sta/ContainerHelpers.hh b/include/sta/ContainerHelpers.hh new file mode 100644 index 000000000..49268ce88 --- /dev/null +++ b/include/sta/ContainerHelpers.hh @@ -0,0 +1,381 @@ +// 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 +#include // for std::declval +#include +#include +#include +#include +#include +#include + +namespace sta { + +// C++ kung foo courtesy of chat gtp. + +// ------------------------------------------------------------ +// 1. Sequence containers (vector, list, deque, …) +// ------------------------------------------------------------ +template +std::enable_if_t> +deleteContents(Container& c) +{ + for (auto ptr : c) + delete ptr; + c.clear(); +} + +template +std::enable_if_t> +deleteContents(Container *c) +{ + for (auto ptr : *c) + delete ptr; + c->clear(); +} + +// ------------------------------------------------------------ +// 2. Maps (map, unordered_map) +// ------------------------------------------------------------ +template +std::enable_if_t +> +deleteContents(Map& m) +{ + for (auto& kv : m) + delete kv.second; + m.clear(); +} + +template +std::enable_if_t +> +deleteContents(Map *m) +{ + for (auto& kv : *m) + delete kv.second; + m->clear(); +} + +// ------------------------------------------------------------ +// 3. Sets (set, unordered_set) +// ------------------------------------------------------------ +template +std::enable_if_t< + std::is_pointer_v && + !std::is_same_v +> +deleteContents(Set& s) +{ + for (auto ptr : s) + delete ptr; + s.clear(); +} + +//////////////////////////////////////////////////////////////// + +// detect whether container has mapped_type +template +struct has_mapped_type : std::false_type {}; + +template +struct has_mapped_type> + : std::true_type {}; + +// handle pointer types +template +struct has_mapped_type : has_mapped_type {}; + +// return-type chooser: use struct, NOT alias template +template::value> +struct find_return; + +// pointer to map +template +struct find_return +{ + using type = typename C::mapped_type; +}; + +// pointer to set +template +struct find_return +{ + using type = typename C::key_type; +}; + +// map ref +template +struct find_return +{ + using type = typename C::mapped_type; +}; + +// set ref +template +struct find_return +{ + using type = typename C::key_type; +}; + + +// Find an value in a contaiiner of pointers. +// return nullptr if not found. +template +auto +findKey(const AssocContainer& c, + typename AssocContainer::key_type key) + -> typename find_return::type +{ + using ReturnType = typename find_return::type; + + static_assert(std::is_pointer_v, + "findKey requires pointer types"); + + auto it = c.find(key); + if (it == c.end()) + return nullptr; + + if constexpr (has_mapped_type::value) + return it->second; // map + else + return *it; // set +} + +// Find an value in a contaiiner of pointers. +// return nullptr if not found. +template +auto +findKey(const AssocContainer* c, + typename AssocContainer::key_type key) + -> typename find_return::type +{ + using ReturnType = typename find_return::type; + + static_assert(std::is_pointer_v, + "findKey requires pointer types"); + + auto it = c->find(key); + if (it == c->end()) + return nullptr; + + if constexpr (has_mapped_type::value) + // map + return it->second; + else + // set + return *it; +} + +template +void +findKeyValue(const AssocContainer& c, + typename AssocContainer::key_type key, + typename find_return::type &value, + bool &exists) +{ + auto it = c.find(key); + if (it == c.end()) { + exists = false; + return; + } + + if constexpr (has_mapped_type::value) { + // map + value = it->second; + exists = true; + } + else { + // set + value = *it; + exists = true; + } +} + +template +void +findKeyValue(const AssocContainer *c, + typename AssocContainer::key_type key, + typename find_return::type &value, + bool &exists) +{ + auto it = c->find(key); + if (it == c->end()) { + exists = false; + return; + } + + if constexpr (has_mapped_type::value) { + // map + value = it->second; + exists = true; + } + else { + // set + value = *it; + exists = true; + } +} + +template +auto +findKeyValuePtr(AssocContainer& c, + typename AssocContainer::key_type key) + -> typename find_return::type* +{ + auto it = c.find(key); + if (it == c.end()) + return nullptr; + + if constexpr (has_mapped_type::value) + // map + return &it->second; + else + // set + return *it; +} + +//////////////////////////////////////////////////////////////// + +// Determine if two std::set's intersect. +// Returns true if there is at least one common element. +template +bool +intersects(const Set &set1, + const Set &set2, + typename Set::key_compare key_less) +{ + auto iter1 = set1.begin(); + auto end1 = set1.end(); + auto iter2 = set2.begin(); + auto end2 = set2.end(); + + while (iter1 != end1 && iter2 != end2) { + if (key_less(*iter1, *iter2)) + iter1++; + else if (key_less(*iter2, *iter1)) + iter2++; + else + return true; + } + return false; +} + +// Determine if two std::set's intersect (pointer version). +// Returns true if there is at least one common element. +template +bool +intersects(const Set *set1, + const Set *set2, + typename Set::key_compare key_less) +{ + if (set1 && set2) { + auto iter1 = set1->begin(); + auto end1 = set1->end(); + auto iter2 = set2->begin(); + auto end2 = set2->end(); + + while (iter1 != end1 && iter2 != end2) { + if (key_less(*iter1, *iter2)) + iter1++; + else if (key_less(*iter2, *iter1)) + iter2++; + else + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////// + +// Compare set contents. +template +int +compare(const Set *set1, + const Set *set2, + typename Set::key_compare key_less) +{ + size_t size1 = set1 ? set1->size() : 0; + size_t size2 = set2 ? set2->size() : 0; + if (size1 == size2) { + if (set1 == nullptr || set2 == nullptr) { + // Both are null or empty, so they're equal + return 0; + } + auto iter1 = set1->begin(); + auto iter2 = set2->begin(); + auto end1 = set1->end(); + auto end2 = set2->end(); + while (iter1 != end1 && iter2 != end2) { + if (key_less(*iter1, *iter2)) + return -1; + else if (key_less(*iter2, *iter1)) + return 1; + ++iter1; + ++iter2; + } + // Sets are equal. + return 0; + } + else + return (size1 > size2) ? 1 : -1; +} + +//////////////////////////////////////////////////////////////// + +// Sort functions that do not require begin()/end() range. + +// reference arg +template> +requires std::predicate, + std::ranges::range_reference_t> +void +sort(Range& r, + Comp comp = Comp{}) +{ + std::sort(std::ranges::begin(r), std::ranges::end(r), comp); +} + + +// pointer arg +template> +requires std::ranges::random_access_range && + std::predicate, + std::ranges::range_reference_t> +void +sort(Range* r, + Comp comp = Comp{}) +{ + std::sort(std::ranges::begin(*r), std::ranges::end(*r), comp); +} + +} // namespace diff --git a/include/sta/Corner.hh b/include/sta/Corner.hh deleted file mode 100644 index bee765d00..000000000 --- a/include/sta/Corner.hh +++ /dev/null @@ -1,139 +0,0 @@ -// 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 "MinMax.hh" -#include "Vector.hh" -#include "StringSet.hh" -#include "GraphClass.hh" -#include "SearchClass.hh" -#include "StaState.hh" - -namespace sta { - -class ParasiticAnalysisPt; -class DcalcAnalysisPt; -class PathAnalysisPt; -class Corner; -class Corners; -class LibertyLibrary; - -typedef Vector CornerSeq; -typedef Map CornerMap; -typedef Vector ParasiticAnalysisPtSeq; -typedef Vector DcalcAnalysisPtSeq; -typedef Vector PathAnalysisPtSeq; -typedef Vector LibertySeq; - -class Corners : public StaState -{ -public: - explicit Corners(StaState *sta); - ~Corners(); - void clear(); - int count() const; - void copy(Corners *corners); - bool multiCorner() const; - Corner *findCorner(const char *corner); - Corner *findCorner(int corner_index); - void makeCorners(StringSet *corner_names); - void analysisTypeChanged(); - void operatingConditionsChanged(); - - // Make one parasitic analysis points. - void makeParasiticAnalysisPts(bool per_corner); - int parasiticAnalysisPtCount() const; - ParasiticAnalysisPtSeq ¶siticAnalysisPts(); - - DcalcAPIndex dcalcAnalysisPtCount() const; - DcalcAnalysisPtSeq &dcalcAnalysisPts(); - const DcalcAnalysisPtSeq &dcalcAnalysisPts() const; - - PathAPIndex pathAnalysisPtCount() const; - PathAnalysisPt *findPathAnalysisPt(PathAPIndex path_index) const; - PathAnalysisPtSeq &pathAnalysisPts(); - const PathAnalysisPtSeq &pathAnalysisPts() const; - CornerSeq &corners() { return corners_; } - // Iterators for range iteration. - // for (auto corner : *sta->corners()) {} - CornerSeq::iterator begin() { return corners_.begin(); } - CornerSeq::iterator end() { return corners_.end(); } - -protected: - void makeAnalysisPts(); - void makeDcalcAnalysisPts(Corner *corner); - DcalcAnalysisPt *makeDcalcAnalysisPt(Corner *corner, - const MinMax *min_max, - const MinMax *check_clk_slew_min_max); - void makePathAnalysisPts(Corner *corner); - void makePathAnalysisPts(Corner *corner, - bool swap_clk_min_max, - DcalcAnalysisPt *dcalc_ap_min, - DcalcAnalysisPt *dcalc_ap_max); - -private: - CornerMap corner_map_; - CornerSeq corners_; - ParasiticAnalysisPtSeq parasitic_analysis_pts_; - DcalcAnalysisPtSeq dcalc_analysis_pts_; - PathAnalysisPtSeq path_analysis_pts_; -}; - -class Corner -{ -public: - Corner(const char *name, - int index); - const char *name() const { return name_.c_str(); } - int index() const { return index_; } - ParasiticAnalysisPt *findParasiticAnalysisPt(const MinMax *min_max) const; - int parasiticAnalysisPtcount(); - DcalcAnalysisPt *findDcalcAnalysisPt(const MinMax *min_max) const; - PathAnalysisPt *findPathAnalysisPt(const MinMax *min_max) const; - void addLiberty(LibertyLibrary *lib, - const MinMax *min_max); - const LibertySeq &libertyLibraries(const MinMax *min_max) const; - int libertyIndex(const MinMax *min_max) const; - -protected: - void setParasiticAnalysisPtcount(int ap_count); - void setParasiticAP(ParasiticAnalysisPt *path_ap, - int mm_index); - void setDcalcAnalysisPtcount(DcalcAPIndex ap_count); - void addDcalcAP(DcalcAnalysisPt *dcalc_ap); - void addPathAP(PathAnalysisPt *path_ap); - -private: - std::string name_; - int index_; - ParasiticAnalysisPtSeq parasitic_analysis_pts_; - DcalcAnalysisPtSeq dcalc_analysis_pts_; - PathAnalysisPtSeq path_analysis_pts_; - LibertySeq liberty_[MinMax::index_count]; - - friend class Corners; -}; - -} // namespace diff --git a/include/sta/CycleAccting.hh b/include/sta/CycleAccting.hh index 3cc5a0452..867d620c9 100644 --- a/include/sta/CycleAccting.hh +++ b/include/sta/CycleAccting.hh @@ -24,7 +24,8 @@ #pragma once -#include "UnorderedSet.hh" +#include + #include "MinMax.hh" #include "TimingRole.hh" #include "StaState.hh" @@ -42,17 +43,19 @@ class CycleAcctingEqual { public: bool operator()(const CycleAccting *acct1, - const CycleAccting *acct2) const; + const CycleAccting *acct2) const; }; class CycleAcctingLess { public: bool operator()(const CycleAccting *acct1, - const CycleAccting *acct2) const; + const CycleAccting *acct2) const; }; -typedef UnorderedSet CycleAcctingSet; +using CycleAcctingSet = std::unordered_set; class CycleAcctings { @@ -63,7 +66,7 @@ public: // Find the cycle accounting info for paths that start at src clock // edge and end at target clock edge. CycleAccting *cycleAccting(const ClockEdge *src, - const ClockEdge *tgt); + const ClockEdge *tgt); void reportClkToClkMaxCycleWarnings(Report *report); private: @@ -75,7 +78,7 @@ class CycleAccting { public: CycleAccting(const ClockEdge *src, - const ClockEdge *tgt); + const ClockEdge *tgt); // Fill in required times. void findDelays(StaState *sta); // Find delays when source clk edge is the default arrival clock edge @@ -92,26 +95,26 @@ public: private: void setHoldAccting(int src_cycle, - int tgt_cycle, - float delay, - float req); + int tgt_cycle, + float delay, + float req); void setAccting(const TimingRole *role, - int src_cycle, - int tgt_cycle, - float delay, - float req); + int src_cycle, + int tgt_cycle, + float delay, + float req); void setSetupAccting(int src_cycle, - int tgt_cycle, - float delay, - float req); + int tgt_cycle, + float delay, + float req); void setDefaultSetupAccting(int src_cycle, - int tgt_cycle, - float delay, - float req); + int tgt_cycle, + float delay, + float req); void setDefaultHoldAccting(int src_cycle, - int tgt_cycle, - float delay, - float req); + int tgt_cycle, + float delay, + float req); int firstCycle(const ClockEdge *clk_edge) const; const ClockEdge *src_; diff --git a/include/sta/DataCheck.hh b/include/sta/DataCheck.hh index 3e55f4a86..23ed758b9 100644 --- a/include/sta/DataCheck.hh +++ b/include/sta/DataCheck.hh @@ -37,29 +37,29 @@ class DataCheck { public: DataCheck(Pin *from, - Pin *to, - Clock *clk); + Pin *to, + Clock *clk); Pin *from() const { return from_; } Pin *to() const { return to_; } Clock *clk() const { return clk_; } void margin(const RiseFall *from_rf, - const RiseFall *to_rf, - const SetupHold *setup_hold, - // Return values. - float &margin, - bool &exists) const; + const RiseFall *to_rf, + const SetupHold *setup_hold, + // Return values. + float &margin, + bool &exists) const; void setMargin(const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, - float margin); + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + float margin); void removeMargin(const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold); - bool empty() const; + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold); + [[nodiscard]] bool empty() const; void marginIsOneValue(const SetupHold *setup_hold, - // Return values. - float &value, - bool &one_value) const; + // Return values. + float &value, + bool &one_value) const; private: Pin *from_; @@ -73,7 +73,7 @@ class DataCheckLess public: DataCheckLess(const Network *network); bool operator()(const DataCheck *check1, - const DataCheck *check2) const; + const DataCheck *check2) const; private: const Network *network_; diff --git a/include/sta/DcalcAnalysisPt.hh b/include/sta/DcalcAnalysisPt.hh deleted file mode 100644 index 76af5fe8b..000000000 --- a/include/sta/DcalcAnalysisPt.hh +++ /dev/null @@ -1,81 +0,0 @@ -// 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 "Iterator.hh" -#include "MinMax.hh" -#include "LibertyClass.hh" -#include "SdcClass.hh" -#include "ParasiticsClass.hh" -#include "GraphClass.hh" -#include "StaState.hh" - -namespace sta { - -class Corner; - -// Delay calculation analysis point. -// This collects all of the parameters used to find one set of -// delay calculation results. -class DcalcAnalysisPt -{ -public: - DcalcAnalysisPt(Corner *corner, - DcalcAPIndex index, - const OperatingConditions *op_cond, - const MinMax *min_max, - const MinMax *check_clk_slew_min_max); - Corner *corner() const { return corner_; } - // Which of the delay_count results this analysis point corresponds to. - DcalcAPIndex index() const { return index_; } - // Slew index of timing check data. - DcalcAPIndex checkDataSlewIndex() const { return index_; } - // Slew index of timing check clock. - DcalcAPIndex checkClkSlewIndex() const { return check_clk_slew_index_; } - // Slew min/max of timing check clock. - const MinMax *checkClkSlewMinMax() const { return check_clk_slew_min_max_; } - // Constraint min/max values to use. - const MinMax *constraintMinMax() const { return min_max_; } - // Constraints::operatingCondition(cnst_min_max_) - const OperatingConditions *operatingConditions() const { return op_cond_; } - void setOperatingConditions(const OperatingConditions *op_cond); - // Delay merging min/max operator (for wires). - const MinMax *delayMinMax() const { return min_max_; } - // Merge min/max slews across timing arcs. - const MinMax *slewMinMax() const { return min_max_; } - ParasiticAnalysisPt *parasiticAnalysisPt() const; - void setCheckClkSlewIndex(DcalcAPIndex index); - int libertyIndex() const; - -private: - Corner *corner_; - DcalcAPIndex index_; - DcalcAPIndex check_clk_slew_index_; - const OperatingConditions *op_cond_; - const MinMax *min_max_; - const MinMax *check_clk_slew_min_max_; -}; - -} // namespace diff --git a/include/sta/Debug.hh b/include/sta/Debug.hh index 0f3923aed..4c958065d 100644 --- a/include/sta/Debug.hh +++ b/include/sta/Debug.hh @@ -24,10 +24,11 @@ #pragma once +#include #include +#include #include -#include "Map.hh" #include "StringUtil.hh" namespace sta { @@ -35,18 +36,17 @@ namespace sta { class Report; class Pin; -typedef Map DebugMap; +using DebugMap = std::map; class Debug { public: - explicit Debug(Report *report); - ~Debug(); + Debug(Report *report); int level(const char *what); void setLevel(const char *what, - int level); + int level); bool check(const char *what, - int level) const; + int level) const; int statsLevel() const { return stats_level_; } void reportLine(const char *what, const char *fmt, @@ -57,18 +57,15 @@ protected: Report *report_; std::mutex buffer_lock_; bool debug_on_; - DebugMap *debug_map_; + DebugMap debug_map_; int stats_level_; }; // Inlining a varargs function would eval the args, which can // be expensive, so use a macro. -// Note that "##__VA_ARGS__" is a gcc extension to support zero arguments (no comma). -// clang -Wno-gnu-zero-variadic-macro-arguments suppresses the warning. -// c++20 has "__VA_OPT__" to deal with the zero arg case so this is temporary. #define debugPrint(debug, what, level, ...) \ if (debug->check(what, level)) { \ - debug->reportLine(what, ##__VA_ARGS__); \ + debug->reportLine(what __VA_OPT__(,) __VA_ARGS__); \ } } // namespace diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index 236158c92..1711f78ee 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -41,10 +41,10 @@ namespace sta { -typedef Delay ArcDelay; -typedef Delay Slew; -typedef Delay Arrival; -typedef Delay Required; -typedef Delay Slack; +using ArcDelay = Delay; +using Slew = Delay; +using Arrival = Delay; +using Required = Delay; +using Slack = Delay; } // namespace diff --git a/include/sta/DelayCalc.hh b/include/sta/DelayCalc.hh index ff1c96295..dbadf0b6c 100644 --- a/include/sta/DelayCalc.hh +++ b/include/sta/DelayCalc.hh @@ -31,7 +31,7 @@ namespace sta { class ArcDelayCalc; class StaState; -typedef ArcDelayCalc *(*MakeArcDelayCalc)(StaState *sta); +using MakeArcDelayCalc = ArcDelayCalc *(*)(StaState *sta); // Register builtin delay calculators. void @@ -39,7 +39,7 @@ registerDelayCalcs(); // Register a delay calculator for the set_delay_calc command. void registerDelayCalc(const char *name, - MakeArcDelayCalc maker); + MakeArcDelayCalc maker); bool isDelayCalcName(const char *name); StringSeq @@ -50,6 +50,6 @@ deleteDelayCalcs(); // Make a registered delay calculator by name. ArcDelayCalc * makeDelayCalc(const char *name, - StaState *sta); + StaState *sta); } // namespace diff --git a/include/sta/DelayFloat.hh b/include/sta/DelayFloat.hh index 88aeca964..a834bb578 100644 --- a/include/sta/DelayFloat.hh +++ b/include/sta/DelayFloat.hh @@ -32,9 +32,9 @@ namespace sta { class StaState; -typedef float Delay; +using Delay = float; // Delay double for accumulating Delays. -typedef double DelayDbl; +using DelayDbl = double; const Delay delay_zero = 0.0; @@ -43,29 +43,29 @@ initDelayConstants(); const char * delayAsString(const Delay &delay, - const StaState *sta); + const StaState *sta); const char * delayAsString(const Delay &delay, - const StaState *sta, - int digits); + const StaState *sta, + int digits); const char * delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits); + const EarlyLate *early_late, + const StaState *sta, + int digits); inline Delay makeDelay(float delay, - float, - float) + float, + float) { return delay; } inline Delay makeDelay2(float delay, - float, - float) + float, + float) { return delay; } @@ -79,15 +79,15 @@ delayAsFloat(const Delay &delay) // mean late+/early- sigma inline float delayAsFloat(const Delay &delay, - const EarlyLate *, - const StaState *) + const EarlyLate *, + const StaState *) { return delay; } inline float delaySigma2(const Delay &, - const EarlyLate *) + const EarlyLate *) { return 0.0; } @@ -96,57 +96,57 @@ const Delay & delayInitValue(const MinMax *min_max); bool delayIsInitValue(const Delay &delay, - const MinMax *min_max); + const MinMax *min_max); bool delayZero(const Delay &delay); bool delayInf(const Delay &delay); bool delayEqual(const Delay &delay1, - const Delay &delay2); + const Delay &delay2); bool delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); bool delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); + 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); + 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); + const Delay &delay2); float delayRatio(const Delay &delay1, - const Delay &delay2); + const Delay &delay2); } // namespace diff --git a/include/sta/DelayNormal1.hh b/include/sta/DelayNormal1.hh index e722bc4ef..87beef239 100644 --- a/include/sta/DelayNormal1.hh +++ b/include/sta/DelayNormal1.hh @@ -41,7 +41,7 @@ public: Delay(const DelayDbl &delay); Delay(float mean); Delay(float mean, - float sigma2); + float sigma2); float mean() const { return mean_; } float sigma() const; // sigma^2 @@ -95,27 +95,27 @@ initDelayConstants(); const char * delayAsString(const Delay &delay, - const StaState *sta); + const StaState *sta); const char * delayAsString(const Delay &delay, - const StaState *sta, - int digits); + const StaState *sta, + int digits); const char * delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits); + const EarlyLate *early_late, + const StaState *sta, + int digits); Delay makeDelay(float delay, - float sigma_early, - float sigma_late); + float sigma_early, + float sigma_late); Delay makeDelay2(float delay, - // sigma^2 - float sigma_early, - float sigma_late); + // sigma^2 + float sigma_early, + float sigma_late); inline float delayAsFloat(const Delay &delay) @@ -126,78 +126,78 @@ delayAsFloat(const Delay &delay) // mean late+/early- sigma float delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta); + const EarlyLate *early_late, + const StaState *sta); float delaySigma2(const Delay &delay, - const EarlyLate *early_late); + const EarlyLate *early_late); const Delay & delayInitValue(const MinMax *min_max); bool delayIsInitValue(const Delay &delay, - const MinMax *min_max); + const MinMax *min_max); bool delayZero(const Delay &delay); bool delayInf(const Delay &delay); bool delayEqual(const Delay &delay1, - const Delay &delay2); + const Delay &delay2); bool delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); bool delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); + 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); + 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); + const Delay &delay2); float delayRatio(const Delay &delay1, - const Delay &delay2); + 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); + const Delay &delay2); // Used for parallel gate delay calc. Delay operator/(float delay1, - const Delay &delay2); + const Delay &delay2); // Used for parallel gate delay calc. Delay operator*(const Delay &delay1, - float delay2); + float delay2); } // namespace diff --git a/include/sta/DelayNormal2.hh b/include/sta/DelayNormal2.hh index 10e7a9655..f19a64119 100644 --- a/include/sta/DelayNormal2.hh +++ b/include/sta/DelayNormal2.hh @@ -41,8 +41,8 @@ public: Delay(const DelayDbl &delay); Delay(float mean); Delay(float mean, - float sigma2_early, - float sigma2_late); + float sigma2_early, + float sigma2_late); float mean() const { return mean_; } float sigma(const EarlyLate *early_late) const; // sigma^2 @@ -106,27 +106,27 @@ initDelayConstants(); const char * delayAsString(const Delay &delay, - const StaState *sta); + const StaState *sta); const char * delayAsString(const Delay &delay, - const StaState *sta, - int digits); + const StaState *sta, + int digits); const char * delayAsString(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta, - int digits); + const EarlyLate *early_late, + const StaState *sta, + int digits); Delay makeDelay(float delay, - float sigma_early, - float sigma_late); + float sigma_early, + float sigma_late); Delay makeDelay2(float delay, - // sigma^2 - float sigma_early, - float sigma_late); + // sigma^2 + float sigma_early, + float sigma_late); inline float delayAsFloat(const Delay &delay) @@ -137,78 +137,78 @@ delayAsFloat(const Delay &delay) // mean late+/early- sigma float delayAsFloat(const Delay &delay, - const EarlyLate *early_late, - const StaState *sta); + const EarlyLate *early_late, + const StaState *sta); float delaySigma2(const Delay &delay, - const EarlyLate *early_late); + const EarlyLate *early_late); const Delay & delayInitValue(const MinMax *min_max); bool delayIsInitValue(const Delay &delay, - const MinMax *min_max); + const MinMax *min_max); bool delayZero(const Delay &delay); bool delayInf(const Delay &delay); bool delayEqual(const Delay &delay1, - const Delay &delay2); + const Delay &delay2); bool delayLess(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayLess(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayLessEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); + const Delay &delay2, + const MinMax *min_max, + const StaState *sta); bool delayGreater(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const StaState *sta); + const Delay &delay2, + const StaState *sta); bool delayGreaterEqual(const Delay &delay1, - const Delay &delay2, - const MinMax *min_max, - const StaState *sta); + 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); + 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); + const Delay &delay2); float delayRatio(const Delay &delay1, - const Delay &delay2); + 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); + const Delay &delay2); // Used for parallel gate delay calc. Delay operator/(float delay1, - const Delay &delay2); + const Delay &delay2); // Used for parallel gate delay calc. Delay operator*(const Delay &delay1, - float delay2); + float delay2); } // namespace diff --git a/include/sta/DeratingFactors.hh b/include/sta/DeratingFactors.hh index aa4808a0a..dcc6b344e 100644 --- a/include/sta/DeratingFactors.hh +++ b/include/sta/DeratingFactors.hh @@ -36,22 +36,22 @@ class DeratingFactors public: DeratingFactors(); void setFactor(PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor); + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor); void factor(PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const; + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const; void clear(); void isOneValue(const EarlyLate *early_late, - bool &is_one_value, - float &value) const; + bool &is_one_value, + float &value) const; void isOneValue(PathClkOrData clk_data, - const EarlyLate *early_late, - bool &is_one_value, - float &value) const; + const EarlyLate *early_late, + bool &is_one_value, + float &value) const; bool hasValue() const; private: @@ -63,22 +63,22 @@ class DeratingFactorsGlobal public: DeratingFactorsGlobal(); void setFactor(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor); void factor(TimingDerateType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const; + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const; void factor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const; + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const; DeratingFactors *factors(TimingDerateType type); void clear(); @@ -91,21 +91,21 @@ class DeratingFactorsCell public: DeratingFactorsCell(); void setFactor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor); void factor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const; + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const; DeratingFactors *factors(TimingDerateCellType type); void clear(); void isOneValue(const EarlyLate *early_late, - bool &is_one_value, - float &value) const; + bool &is_one_value, + float &value) const; private: DeratingFactors factors_[timing_derate_cell_type_count]; diff --git a/include/sta/DisabledPorts.hh b/include/sta/DisabledPorts.hh index 6168247c5..d41661d46 100644 --- a/include/sta/DisabledPorts.hh +++ b/include/sta/DisabledPorts.hh @@ -24,7 +24,9 @@ #pragma once -#include "Map.hh" +#include +#include + #include "NetworkClass.hh" #include "LibertyClass.hh" #include "SdcClass.hh" @@ -35,10 +37,10 @@ class TimingRole; class DisabledCellPorts; class DisabledInstancePorts; -typedef Vector DisabledInstancePortsSeq; -typedef Vector DisabledCellPortsSeq; -typedef Vector LibertyPortPairSeq; -typedef Set TimingArcSetSet; +using DisabledInstancePortsSeq = std::vector; +using DisabledCellPortsSeq = std::vector; +using LibertyPortPairSeq = std::vector; +using TimingArcSetSet = std::set; // Base class for disabled cell and instance ports. class DisabledPorts @@ -56,13 +58,13 @@ public: LibertyPort *to); void removeDisabledFromTo(LibertyPort *from, LibertyPort *to); - bool isDisabled(LibertyPort *from, - LibertyPort *to, - const TimingRole *role); + [[nodiscard]] bool isDisabled(LibertyPort *from, + LibertyPort *to, + const TimingRole *role); LibertyPortPairSet *fromTo() const { return from_to_; } LibertyPortSet *from() const { return from_; } LibertyPortSet *to() const { return to_; } - bool all() const { return all_; } + [[nodiscard]] bool all() const { return all_; } private: bool all_; @@ -80,7 +82,7 @@ public: LibertyCell *cell() const { return cell_; } void setDisabled(TimingArcSet *arc_set); void removeDisabled(TimingArcSet *arc_set); - bool isDisabled(TimingArcSet *arc_set) const; + [[nodiscard]] bool isDisabled(TimingArcSet *arc_set) const; TimingArcSetSet *timingArcSets() const { return arc_sets_; } using DisabledPorts::isDisabled; @@ -102,7 +104,7 @@ private: }; DisabledCellPortsSeq -sortByName(DisabledCellPortsMap *cell_map); +sortByName(const DisabledCellPortsMap *cell_map); DisabledInstancePortsSeq sortByPathName(const DisabledInstancePortsMap *inst_map, const Network *network); diff --git a/include/sta/EnumNameMap.hh b/include/sta/EnumNameMap.hh index d7c60c382..50ef13e64 100644 --- a/include/sta/EnumNameMap.hh +++ b/include/sta/EnumNameMap.hh @@ -37,11 +37,11 @@ public: EnumNameMap(std::initializer_list> enum_names); const char *find(ENUM key) const; ENUM find(std::string name, - ENUM unknown_key) const; + ENUM unknown_key) const; void find(std::string name, - // Return values. - ENUM &key, - bool &exists) const; + // Return values. + ENUM &key, + bool &exists) const; private: std::map enum_map_; @@ -70,9 +70,9 @@ EnumNameMap::find(ENUM key) const template void EnumNameMap::find(std::string name, - // Return values. - ENUM &key, - bool &exists) const + // Return values. + ENUM &key, + bool &exists) const { auto find_iter = name_map_.find(name); if (find_iter != name_map_.end()) { @@ -86,7 +86,7 @@ EnumNameMap::find(std::string name, template ENUM EnumNameMap::find(std::string name, - ENUM unknown_key) const + ENUM unknown_key) const { auto find_iter = name_map_.find(name); if (find_iter != name_map_.end()) diff --git a/include/sta/EquivCells.hh b/include/sta/EquivCells.hh index 4a337b0c5..3ceadeb78 100644 --- a/include/sta/EquivCells.hh +++ b/include/sta/EquivCells.hh @@ -24,15 +24,15 @@ #pragma once -#include "Vector.hh" -#include "Map.hh" -#include "UnorderedMap.hh" +#include +#include + #include "LibertyClass.hh" namespace sta { -typedef Map EquivCellMap; -typedef UnorderedMap LibertyCellHashMap; +using EquivCellMap = std::map; +using LibertyCellHashMap = std::unordered_map; class EquivCells { @@ -40,16 +40,16 @@ public: // Find equivalent cells in equiv_libs. // Optionally add mappings for cells in map_libs. EquivCells(LibertyLibrarySeq *equiv_libs, - LibertyLibrarySeq *map_libs); + LibertyLibrarySeq *map_libs); ~EquivCells(); // Find equivalents for cell (member of from_libs) in to_libs. LibertyCellSeq *equivs(LibertyCell *cell); protected: void findEquivCells(const LibertyLibrary *library, - LibertyCellHashMap &hash_matches); + LibertyCellHashMap &hash_matches); void mapEquivCells(const LibertyLibrary *library, - LibertyCellHashMap &hash_matches); + LibertyCellHashMap &hash_matches); EquivCellMap equiv_cells_; // Unique cell for each equiv cell group. @@ -60,7 +60,7 @@ protected: // functions or timing arcs match. bool equivCells(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); // Predicate that is true when the ports, functions, sequentials and // timing arcs match. @@ -71,7 +71,7 @@ equivCellsArcs(const LibertyCell *cell1, // Predicate that is true when the ports match. bool equivCellPorts(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); // Predicate that is true cell functions match. bool @@ -81,10 +81,10 @@ equivCellFuncs(const LibertyCell *cell1, // Predicate that is true when the timing arc sets match. bool equivCellTimingArcSets(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); bool equivCellSequentials(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); } // namespace diff --git a/include/sta/Error.hh b/include/sta/Error.hh index e5dc3b8f6..2230f45f7 100644 --- a/include/sta/Error.hh +++ b/include/sta/Error.hh @@ -56,7 +56,7 @@ class ExceptionLine : public Exception { public: ExceptionLine(const char *filename, - int line); + int line); protected: const char *filename_; @@ -67,7 +67,7 @@ protected: class FileNotReadable : public Exception { public: - explicit FileNotReadable(const char *filename); + FileNotReadable(const char *filename); virtual const char *what() const noexcept; protected: @@ -78,7 +78,7 @@ protected: class FileNotWritable : public Exception { public: - explicit FileNotWritable(const char *filename); + FileNotWritable(const char *filename); virtual const char *what() const noexcept; protected: diff --git a/include/sta/ExceptionPath.hh b/include/sta/ExceptionPath.hh index c1c099781..f5b89f14f 100644 --- a/include/sta/ExceptionPath.hh +++ b/include/sta/ExceptionPath.hh @@ -24,8 +24,9 @@ #pragma once +#include + #include "Error.hh" -#include "Set.hh" #include "SdcCmdComment.hh" #include "SdcClass.hh" @@ -44,18 +45,18 @@ class ExceptionThru; class ExceptionTo; class ExceptionState; -typedef Vector ExceptionPathSeq; +using ExceptionPathSeq = std::vector; class ExceptionPath : public SdcCmdComment { public: ExceptionPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - int priority, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + const char *comment); virtual ~ExceptionPath(); size_t id() const { return id_; } void setId(size_t id); @@ -75,14 +76,14 @@ public: const Network *network) const; const MinMaxAll *minMax() const { return min_max_; } virtual bool matches(const MinMax *min_max, - bool exact) const; + bool exact) const; bool matchesFirstPt(const RiseFall *to_rf, - const MinMax *min_max); + const MinMax *min_max); ExceptionState *firstState(); virtual bool resetMatch(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, const Network *network); // The priority remains the same even though pin/clock/net/inst objects // are added to the exceptions points during exception merging because @@ -103,22 +104,22 @@ public: // they cannot be coded into the priority. virtual bool tighterThan(ExceptionPath *exception) const = 0; static int fromThruToPriority(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to); + ExceptionThruSeq *thrus, + ExceptionTo *to); size_t hash() const; size_t hash(ExceptionPt *missing_pt) const; // Mergeable properties (independent of exception points). virtual bool mergeable(ExceptionPath *exception) const; bool mergeablePts(ExceptionPath *exception) const; bool mergeablePts(ExceptionPath *exception2, - ExceptionPt *missing_pt2, - ExceptionPt *&missing_pt) const; + ExceptionPt *missing_pt2, + ExceptionPt *&missing_pt) const; // Overrides properties (independent of exception points). virtual bool overrides(ExceptionPath *exception) const = 0; virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) = 0; + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) = 0; void deleteInstance(const Instance *inst, const Network *network); @@ -151,22 +152,22 @@ class FalsePath : public ExceptionPath { public: FalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + const char *comment); FalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - int priority, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + const char *comment); virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts); virtual bool isFalse() const { return true; } virtual ExceptionPathType type() const { return ExceptionPathType::false_path; } virtual const char *typeString() const; @@ -182,7 +183,7 @@ class LoopPath : public FalsePath { public: LoopPath(ExceptionThruSeq *thrus, - bool own_pts); + bool own_pts); virtual bool isLoop() const { return true; } virtual ExceptionPathType type() const { return ExceptionPathType::loop; } virtual const char *typeString() const; @@ -194,18 +195,18 @@ class PathDelay : public ExceptionPath { public: PathDelay(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, - bool own_pts, - const char *comment); + float delay, + bool own_pts, + const char *comment); virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts); virtual bool isPathDelay() const { return true; } virtual ExceptionPathType type() const { return ExceptionPathType::path_delay; } virtual const char *asString(const Network *network) const; @@ -229,21 +230,21 @@ class MultiCyclePath : public ExceptionPath { public: MultiCyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - bool own_pts, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + bool own_pts, + const char *comment); virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts); virtual bool isMultiCycle() const { return true; } virtual ExceptionPathType type() const { return ExceptionPathType::multi_cycle; } virtual bool matches(const MinMax *min_max, - bool exactly) const; + bool exactly) const; virtual const char *asString(const Network *network) const; virtual const char *typeString() const; virtual bool mergeable(ExceptionPath *exception) const; @@ -267,22 +268,22 @@ class FilterPath : public ExceptionPath { public: FilterPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts); virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts); virtual bool isFilter() const { return true; } virtual ExceptionPathType type() const { return ExceptionPathType::filter; } virtual const char *typeString() const; virtual bool mergeable(ExceptionPath *exception) const; virtual bool overrides(ExceptionPath *exception) const; virtual bool resetMatch(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, const Network *network); virtual int typePriority() const; virtual bool tighterThan(ExceptionPath *exception) const; @@ -292,17 +293,17 @@ class GroupPath : public ExceptionPath { public: GroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts, - const char *comment); + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts, + const char *comment); virtual ~GroupPath(); virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts); virtual bool isGroupPath() const { return true; } virtual ExceptionPathType type() const { return ExceptionPathType::group_path; } virtual const char *typeString() const; @@ -323,7 +324,7 @@ class ExceptionPt { public: ExceptionPt(const RiseFallBoth *rf, - bool own_pts); + bool own_pts); virtual ~ExceptionPt() {}; virtual bool isFrom() const { return false; } virtual bool isThru() const { return false; } @@ -379,9 +380,9 @@ class ExceptionFromTo : public ExceptionPt public: ExceptionFromTo(PinSet *pins, ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, const Network *network); ~ExceptionFromTo(); virtual PinSet *pins() { return pins_; } @@ -398,7 +399,7 @@ public: virtual PinSet allPins(const Network *network); bool equal(ExceptionFromTo *from_to) const; virtual int compare(ExceptionPt *pt, - const Network *network) const; + const Network *network) const; virtual void mergeInto(ExceptionPt *pt, const Network *network); virtual const char *asString(const Network *network) const; @@ -436,10 +437,10 @@ class ExceptionFrom : public ExceptionFromTo { public: ExceptionFrom(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, const Network *network); ExceptionFrom *clone(const Network *network); virtual bool isFrom() const { return true; } @@ -456,13 +457,13 @@ class ExceptionTo : public ExceptionFromTo { public: ExceptionTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - // -to|-rise_to|-fall_to - const RiseFallBoth *rf, - // -rise|-fall endpoint transition. - const RiseFallBoth *end_rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + // -to|-rise_to|-fall_to + const RiseFallBoth *rf, + // -rise|-fall endpoint transition. + const RiseFallBoth *end_rf, + bool own_pts, const Network *network); ExceptionTo *clone(const Network *network); virtual bool isTo() const { return true; } @@ -472,28 +473,28 @@ public: const Network *network) const; virtual int typePriority() const { return 1; } bool matches(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - const Network *network) const; + const ClockEdge *clk_edge, + const RiseFall *end_rf, + const Network *network) const; bool matches(const Pin *pin, - const RiseFall *end_rf) const; + const RiseFall *end_rf) const; bool matches(const Clock *clk) const; bool matches(const Pin *pin, const RiseFall *end_rf, const Network *network) const; bool matchesFilter(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - const Network *network) const; + const ClockEdge *clk_edge, + const RiseFall *end_rf, + const Network *network) const; virtual int compare(ExceptionPt *pt, const Network *network) const; protected: bool matches(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - bool inst_matches_reg_clk_pin, - const Network *network) const; + const ClockEdge *clk_edge, + const RiseFall *end_rf, + bool inst_matches_reg_clk_pin, + const Network *network) const; virtual const char *cmdKeyword() const; // -rise|-fall endpoint transition. @@ -504,11 +505,11 @@ class ExceptionThru : public ExceptionPt { public: ExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, - const Network *network); + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, + const Network *network); ~ExceptionThru(); ExceptionThru *clone(const Network *network); virtual const char *asString(const Network *network) const; @@ -523,12 +524,12 @@ public: const Network *network); virtual PinSet allPins(const Network *network); bool matches(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const Network *network); + const Pin *to_pin, + const RiseFall *to_rf, + const Network *network); bool equal(ExceptionThru *thru) const; virtual int compare(ExceptionPt *pt, - const Network *network) const; + const Network *network) const; virtual void mergeInto(ExceptionPt *pt, const Network *network); bool intersectsPts(ExceptionThru *thru, @@ -563,19 +564,19 @@ protected: void makeNetEdges(const Network *network); void makeInstEdges(const Network *network); void makeHpinEdges(const Pin *pin, - const Network *network); + const Network *network); void makePinEdges(const Pin *pin, - const Network *network); + const Network *network); void makeNetEdges(const Net *net, - const Network *network); + const Network *network); void makeInstEdges(Instance *inst, - Network *network); + Network *network); void deletePinEdges(const Pin *pin, - Network *network); + Network *network); void deleteNetEdges(Net *net, - const Network *network); + const Network *network); void deleteInstEdges(Instance *inst, - Network *network); + Network *network); // Leaf/port pins. PinSet *pins_; @@ -587,20 +588,20 @@ protected: ExceptionThruSeq * exceptionThrusClone(ExceptionThruSeq *thrus, - const Network *network); + const Network *network); // Iterate uniformly across exception from/thru/to's. class ExceptionPtIterator { public: - explicit ExceptionPtIterator(const ExceptionPath *exception); + ExceptionPtIterator(const ExceptionPath *exception); bool hasNext(); ExceptionPt *next(); private: const ExceptionPath *exception_; bool from_done_; - ExceptionThruSeq::Iterator thru_iter_; + ExceptionThruSeq::iterator thru_iter_; bool to_done_; }; @@ -616,13 +617,13 @@ class ExpandedExceptionVisitor { public: ExpandedExceptionVisitor(ExceptionPath *exception, - const Network *network); + const Network *network); virtual ~ExpandedExceptionVisitor() {} void visitExpansions(); // From/thrus/to have a single exception point (pin/instance/net/clock). virtual void visit(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) = 0; + ExceptionThruSeq *thrus, + ExceptionTo *to) = 0; protected: ExceptionPath *exception_; @@ -632,10 +633,10 @@ private: void expandFrom(); void expandThrus(ExceptionFrom *expanded_from); void expandThru(ExceptionFrom *expanded_from, - size_t next_thru_idx, - ExceptionThruSeq *expanded_thrus); + size_t next_thru_idx, + ExceptionThruSeq *expanded_thrus); void expandTo(ExceptionFrom *expanded_from, - ExceptionThruSeq *expanded_thrus); + ExceptionThruSeq *expanded_thrus); }; // States used by tags to know what exception points have been seen @@ -644,15 +645,15 @@ class ExceptionState { public: ExceptionState(ExceptionPath *exception, - ExceptionThru *next_thru, - int index); + ExceptionThru *next_thru, + int index); ExceptionPath *exception() { return exception_; } const ExceptionPath *exception() const { return exception_; } bool matchesNextThru(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - const Network *network) const; + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + const Network *network) const; bool isComplete() const; ExceptionThru *nextThru() const { return next_thru_; } ExceptionState *nextState() const { return next_state_; } @@ -667,9 +668,9 @@ private: int index_; }; -bool -exceptionStateLess(const ExceptionState *state1, - const ExceptionState *state2); +int +exceptionStateCmp(const ExceptionState *state1, + const ExceptionState *state2); // Exception thrown by check. class EmptyExpceptionPt : public Exception @@ -683,7 +684,7 @@ class ExceptionPathLess public: ExceptionPathLess(const Network *network); bool operator()(const ExceptionPath *except1, - const ExceptionPath *except2) const; + const ExceptionPath *except2) const; private: const Network *network_; @@ -692,7 +693,7 @@ private: // Throws EmptyExpceptionPt it finds an empty exception point. void checkFromThrusTo(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to); + ExceptionThruSeq *thrus, + ExceptionTo *to); } // namespace diff --git a/include/sta/FuncExpr.hh b/include/sta/FuncExpr.hh index ae7a0bc10..3f537f521 100644 --- a/include/sta/FuncExpr.hh +++ b/include/sta/FuncExpr.hh @@ -26,7 +26,6 @@ #include -#include "Set.hh" #include "NetworkClass.hh" #include "LibertyClass.hh" @@ -35,46 +34,47 @@ namespace sta { class FuncExpr { public: - enum Operator {op_port, - op_not, - op_or, - op_and, - op_xor, - op_one, - op_zero}; + enum class Op {port, + not_, + or_, + and_, + xor_, + one, + zero}; // Constructors. - FuncExpr(Operator op, - FuncExpr *left, - FuncExpr *right, - LibertyPort *port); + FuncExpr(Op op, + FuncExpr *left, + FuncExpr *right, + LibertyPort *port); static FuncExpr *makePort(LibertyPort *port); static FuncExpr *makeNot(FuncExpr *expr); static FuncExpr *makeAnd(FuncExpr *left, - FuncExpr *right); + FuncExpr *right); static FuncExpr *makeOr(FuncExpr *left, - FuncExpr *right); + FuncExpr *right); static FuncExpr *makeXor(FuncExpr *left, - FuncExpr *right); + FuncExpr *right); static FuncExpr *makeZero(); static FuncExpr *makeOne(); static bool equiv(const FuncExpr *expr1, - const FuncExpr *expr2); + const FuncExpr *expr2); static bool less(const FuncExpr *expr1, - const FuncExpr *expr2); + const FuncExpr *expr2); // Deep copy. FuncExpr *copy(); // Delete expression and all of its subexpressions. void deleteSubexprs(); - // op == op_port + // op == port LibertyPort *port() const; - Operator op() const { return op_; } + Op op() const { return op_; } // When operator is NOT left is the only operand. FuncExpr *left() const { return left_; } - // nullptr when op == op_not + // nullptr when op == not_ FuncExpr *right() const { return right_; } TimingSense portTimingSense(const LibertyPort *port) const; + LibertyPortSet ports() const; // Return true if expression has port as an input. bool hasPort(const LibertyPort *port) const; std::string to_string() const; @@ -86,11 +86,14 @@ public: bool checkSize(LibertyPort *port); private: + void findPorts(const FuncExpr *expr, + LibertyPortSet &ports) const; + std::string to_string(bool with_parens) const; std::string to_string(bool with_parens, char op) const; - Operator op_; + Op op_; FuncExpr *left_; FuncExpr *right_; LibertyPort *port_; @@ -100,18 +103,4 @@ private: FuncExpr * funcExprNot(FuncExpr *expr); -class FuncExprPortIterator : public Iterator -{ -public: - explicit FuncExprPortIterator(const FuncExpr *expr); - virtual bool hasNext() { return iter_.hasNext(); } - virtual LibertyPort *next() { return iter_.next(); } - -private: - void findPorts(const FuncExpr *expr); - - LibertyPortSet ports_; - LibertyPortSet::ConstIterator iter_; -}; - } // namespace diff --git a/include/sta/Fuzzy.hh b/include/sta/Fuzzy.hh index ac4280f02..0ae490aa5 100644 --- a/include/sta/Fuzzy.hh +++ b/include/sta/Fuzzy.hh @@ -30,21 +30,21 @@ namespace sta { bool fuzzyEqual(float v1, - float v2); + float v2); bool fuzzyZero(float v); bool fuzzyLess(float v1, - float v2); + float v2); bool fuzzyLessEqual(float v1, - float v2); + float v2); bool fuzzyGreater(float v1, - float v2); + float v2); bool fuzzyGreaterEqual(float v1, - float v2); + float v2); bool fuzzyInf(float value); diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 6bf86cfe6..286258542 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -26,10 +26,9 @@ #include #include +#include #include "Iterator.hh" -#include "Map.hh" -#include "Vector.hh" #include "ObjectTable.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" @@ -44,12 +43,12 @@ namespace sta { class MinMax; class Sdc; -typedef ObjectTable VertexTable; -typedef ObjectTable EdgeTable; -typedef Map PinVertexMap; -typedef Iterator VertexEdgeIterator; -typedef Map PeriodCheckAnnotations; -typedef ObjectId EdgeId; +using VertexTable = ObjectTable; +using EdgeTable = ObjectTable; +using PinVertexMap = std::map; +using VertexEdgeIterator = Iterator; +using PeriodCheckAnnotations = std::map; +using EdgeId = ObjectId; static constexpr EdgeId edge_id_null = object_id_null; static constexpr ObjectIdx edge_idx_null = object_id_null; @@ -65,8 +64,8 @@ public: // 2 rise/fall slews // ap_count is the dcalc analysis point count. Graph(StaState *sta, - int slew_rf_count, - DcalcAPIndex ap_count); + int slew_rf_count, + DcalcAPIndex ap_count); void makeGraph(); ~Graph(); @@ -80,13 +79,13 @@ public: VertexId id(const Vertex *vertex) const; void makePinVertices(Pin *pin); void makePinVertices(Pin *pin, - Vertex *&vertex, - Vertex *&bidir_drvr_vertex); + Vertex *&vertex, + Vertex *&bidir_drvr_vertex); // Both vertices for bidirects. void pinVertices(const Pin *pin, - // Return values. - Vertex *&vertex, - Vertex *&bidirect_drvr_vertex) const; + // Return values. + Vertex *&vertex, + Vertex *&bidirect_drvr_vertex) const; // Driver vertex for bidirects. Vertex *pinDrvrVertex(const Pin *pin) const; // Load vertex for bidirects. @@ -94,10 +93,6 @@ public: void deleteVertex(Vertex *vertex); bool hasFaninOne(Vertex *vertex) const; VertexId vertexCount() { return vertices_->size(); } - Path *makePaths(Vertex *vertex, - uint32_t count); - Path *paths(const Vertex *vertex) const; - void deletePaths(Vertex *vertex); // Reported slew are the same as those in the liberty tables. // reported_slews = measured_slews / slew_derate_from_library @@ -150,21 +145,19 @@ public: const ArcDelay &delay); // Is timing arc delay annotated. bool arcDelayAnnotated(const Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index) const; + const TimingArc *arc, + DcalcAPIndex ap_index) const; void setArcDelayAnnotated(Edge *edge, - const TimingArc *arc, - DcalcAPIndex ap_index, - bool annotated); + const TimingArc *arc, + DcalcAPIndex ap_index, + bool annotated); bool wireDelayAnnotated(const Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index) const; + const RiseFall *rf, + DcalcAPIndex ap_index) const; void setWireDelayAnnotated(Edge *edge, - const RiseFall *rf, - DcalcAPIndex ap_index, - bool annotated); - // True if any edge arc is annotated. - bool delayAnnotated(Edge *edge); + const RiseFall *rf, + DcalcAPIndex ap_index, + bool annotated); void minPulseWidthArc(Vertex *vertex, const RiseFall *hi_low, @@ -172,23 +165,24 @@ public: Edge *&edge, TimingArc *&arc); void minPeriodArc(Vertex *vertex, - const RiseFall *rf, - // Return values. - Edge *&edge, - TimingArc *&arc); + const RiseFall *rf, + // Return values. + Edge *&edge, + TimingArc *&arc); // Sdf period check annotation. void periodCheckAnnotation(const Pin *pin, - DcalcAPIndex ap_index, - // Return values. - float &period, - bool &exists); + DcalcAPIndex ap_index, + // Return values. + float &period, + bool &exists); void setPeriodCheckAnnotation(const Pin *pin, - DcalcAPIndex ap_index, - float period); + DcalcAPIndex ap_index, + float period); // Remove all delay and slew annotations. void removeDelaySlewAnnotations(); - VertexSet *regClkVertices() { return reg_clk_vertices_; } + VertexSet ®ClkVertices() { return reg_clk_vertices_; } + void makeSceneAfter(); static constexpr int vertex_level_bits = 24; static constexpr int vertex_level_max = (1< { public: - explicit VertexIterator(Graph *graph); + VertexIterator(Graph *graph); virtual bool hasNext() { return vertex_ || bidir_vertex_; } virtual Vertex *next(); @@ -477,9 +447,9 @@ class VertexInEdgeIterator : public VertexEdgeIterator { public: VertexInEdgeIterator(Vertex *vertex, - const Graph *graph); + const Graph *graph); VertexInEdgeIterator(VertexId vertex_id, - const Graph *graph); + const Graph *graph); bool hasNext() { return (next_ != nullptr); } Edge *next(); @@ -492,7 +462,7 @@ class VertexOutEdgeIterator : public VertexEdgeIterator { public: VertexOutEdgeIterator(Vertex *vertex, - const Graph *graph); + const Graph *graph); bool hasNext() { return (next_ != nullptr); } Edge *next(); @@ -506,31 +476,21 @@ class EdgesThruHierPinIterator : public Iterator { public: EdgesThruHierPinIterator(const Pin *hpin, - Network *network, - Graph *graph); - virtual bool hasNext() { return edge_iter_.hasNext(); } - virtual Edge *next() { return edge_iter_.next(); } + Network *network, + Graph *graph); + virtual bool hasNext(); + virtual Edge *next(); private: EdgeSet edges_; - EdgeSet::Iterator edge_iter_; + EdgeSet::iterator edge_iter_; }; -class VertexIdLess +// Helper function to create a VertexSet with the comparator initialized +inline VertexSet +makeVertexSet(StaState *sta) { -public: - VertexIdLess(Graph *&graph); - bool operator()(const Vertex *vertex1, - const Vertex *vertex2) const; - -private: - Graph *&graph_; -}; - -class VertexSet : public Set -{ -public: - VertexSet(Graph *&graph); -}; + return VertexSet(VertexIdLess(sta->graphRef())); +} } // namespace diff --git a/include/sta/GraphClass.hh b/include/sta/GraphClass.hh index 381a9b8b5..ebe184c83 100644 --- a/include/sta/GraphClass.hh +++ b/include/sta/GraphClass.hh @@ -25,10 +25,10 @@ #pragma once #include +#include +#include #include "ObjectId.hh" -#include "Set.hh" -#include "Vector.hh" #include "MinMax.hh" #include "Transition.hh" #include "Delay.hh" @@ -42,19 +42,29 @@ class Edge; class VertexIterator; class VertexInEdgeIterator; class VertexOutEdgeIterator; -class GraphLoop; -class VertexSet; -typedef ObjectId VertexId; -typedef ObjectId EdgeId; -typedef Vector VertexSeq; -typedef Vector EdgeSeq; -typedef Set EdgeSet; -typedef int Level; -typedef int DcalcAPIndex; -typedef int TagGroupIndex; -typedef Vector GraphLoopSeq; -typedef std::vector SlewSeq; +class VertexIdLess +{ +public: + VertexIdLess() = delete; + VertexIdLess(Graph *&graph); + bool operator()(const Vertex *vertex1, + const Vertex *vertex2) const; + +private: + Graph *&graph_; +}; + +using VertexId = ObjectId; +using EdgeId = ObjectId; +using VertexSeq = std::vector; +using VertexSet = std::set; +using EdgeSeq = std::vector; +using EdgeSet = std::set; +using Level = int; +using DcalcAPIndex = int; +using TagGroupIndex = int; +using SlewSeq = std::vector; static constexpr int level_max = std::numeric_limits::max(); diff --git a/include/sta/GraphCmp.hh b/include/sta/GraphCmp.hh index 1cafb0816..7f51b87fa 100644 --- a/include/sta/GraphCmp.hh +++ b/include/sta/GraphCmp.hh @@ -33,9 +33,9 @@ namespace sta { class VertexNameLess { public: - explicit VertexNameLess(Network *network); + VertexNameLess(Network *network); bool operator()(const Vertex *vertex1, - const Vertex *vertex2); + const Vertex *vertex2); private: Network *network_; @@ -45,9 +45,9 @@ class EdgeLess { public: EdgeLess(const Network *network, - Graph *&graph); + Graph *&graph); bool operator()(const Edge *edge1, - const Edge *edge2) const; + const Edge *edge2) const; private: const PinPathNameLess pin_less_; @@ -56,7 +56,7 @@ private: void sortEdges(EdgeSeq *edges, - Network *network, - Graph *graph); + Network *network, + Graph *graph); } // namespace diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 6a746fc4a..aea61fcb5 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -24,15 +24,15 @@ #pragma once +#include #include +#include #include -#include -#include "Map.hh" #include "NetworkClass.hh" #include "GraphClass.hh" #include "SearchClass.hh" -#include "DcalcAnalysisPt.hh" +#include "SdcClass.hh" #include "StaState.hh" #include "ArcDelayCalc.hh" @@ -42,9 +42,10 @@ class DelayCalcObserver; class MultiDrvrNet; class FindVertexDelays; class NetCaps; +class SearchPred; -typedef Map MultiDrvrNetMap; -typedef std::vector DrvrLoadSlews; +using MultiDrvrNetMap = std::map; +using DrvrLoadSlews = std::vector; // This class traverses the graph calling the arc delay calculator and // annotating delays on graph edges. @@ -73,7 +74,7 @@ public: // Returned string is owned by the caller. virtual std::string reportDelayCalc(const Edge *edge, const TimingArc *arc, - const Corner *corner, + const Scene *scene, const MinMax *min_max, int digits); // Percentage (0.0:1.0) change in delay that causes downstream @@ -82,19 +83,23 @@ public: virtual void setIncrementalDelayTolerance(float tol); float loadCap(const Pin *drvr_pin, - const DcalcAnalysisPt *dcalc_ap) const; + const Scene *scene, + const MinMax *min_max) const; float loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) const; + const Scene *scene, + const MinMax *min_max) const; void loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap) const; void netCaps(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, @@ -102,7 +107,8 @@ public: bool &has_set_load) const; void parasiticLoad(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, // Return values. @@ -112,14 +118,15 @@ public: void findDriverArcDelays(Vertex *drvr_vertex, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc); // Precedence: // SDF annotation // Liberty port timing group timing_type minimum_period. // Liberty port min_period attribute. void minPeriod(const Pin *pin, - const Corner *corner, + const Scene *scene, // Return values. float &min_period, bool &exists); @@ -127,11 +134,13 @@ public: Slew edgeFromSlew(const Vertex *from_vertex, const RiseFall *from_rf, const Edge *edge, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); Slew edgeFromSlew(const Vertex *from_vertex, const RiseFall *from_rf, const TimingRole *role, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); bool bidirectDrvrSlewFromLoad(const Pin *pin) const; protected: @@ -145,13 +154,15 @@ protected: void seedNoDrvrSlew(Vertex *drvr_vertex, const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc); void seedNoDrvrCellSlew(Vertex *drvr_vertex, const Pin *drvr_pin, const RiseFall *rf, const InputDrive *drive, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc); void seedLoadSlew(Vertex *vertex); void setInputPortWireDelays(Vertex *vertex); @@ -162,7 +173,8 @@ protected: const LibertyPort *from_port, float *from_slews, const LibertyPort *to_port, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); LibertyPort *driveCellDefaultFromPort(const LibertyCell *cell, const LibertyPort *to_port); int findPortIndex(const LibertyCell *cell, @@ -171,7 +183,8 @@ protected: Vertex *drvr_vertex, const TimingArc *arc, float from_slew, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); void findDriverDelays(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map); @@ -196,14 +209,16 @@ protected: const MultiDrvrNet *multi_drvr, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map); ArcDcalcArgSeq makeArcDcalcArgs(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc); void findParallelEdge(Vertex *vertex, const TimingArc *drvr_arc, @@ -225,34 +240,40 @@ protected: const TimingArc *arc, ArcDcalcResult &dcalc_result, LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); bool annotateDelaySlew(Edge *edge, const TimingArc *arc, ArcDelay &gate_delay, Slew &gate_slew, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); bool annotateLoadDelays(Vertex *drvr_vertex, const RiseFall *drvr_rf, ArcDcalcResult &dcalc_result, LoadPinIndexMap &load_pin_index_map, const ArcDelay &extra_delay, bool merge, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); void findLatchEdgeDelays(Edge *edge); void findCheckEdgeDelays(Edge *edge, ArcDelayCalc *arc_delay_calc); void deleteMultiDrvrNets(); Slew checkEdgeClkSlew(const Vertex *from_vertex, const RiseFall *from_rf, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); float loadCap(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, ArcDelayCalc *arc_delay_calc) const; void parasiticLoad(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, // Return values. @@ -261,7 +282,8 @@ protected: const Parasitic *¶sitic) const; void netCaps(const Pin *drvr_pin, const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const MultiDrvrNet *multi_drvr, // Return values. float &pin_cap, @@ -275,7 +297,7 @@ protected: bool incremental_; bool delays_exist_; // Vertices with invalid -to delays. - VertexSet *invalid_delays_; + VertexSet invalid_delays_; // Timing check edges with invalid delays. EdgeSet invalid_check_edges_; // Latch D->Q edges with invalid delays. @@ -284,7 +306,6 @@ protected: std::mutex invalid_edge_lock_; SearchPred *search_pred_; SearchPred *search_non_latch_pred_; - SearchPred *clk_pred_; BfsFwdIterator *iter_; MultiDrvrNetMap multi_drvr_net_map_; std::mutex multi_drvr_lock_; @@ -319,13 +340,14 @@ public: Vertex *dcalcDrvr() const { return dcalc_drvr_; } void setDcalcDrvr(Vertex *drvr); void netCaps(const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, float &fanout, bool &has_net_load) const; - void findCaps(const Sdc *sdc); + void findCaps(const StaState *sta); private: // Driver that triggers delay calculation for all the drivers on the net. diff --git a/include/sta/Hash.hh b/include/sta/Hash.hh index 4389d7ce3..be6d5375b 100644 --- a/include/sta/Hash.hh +++ b/include/sta/Hash.hh @@ -34,7 +34,7 @@ const size_t hash_init_value = 5381; // Dan Bernstein, comp.lang.c. inline size_t hashSum(size_t hash, - size_t add) + size_t add) { // hash * 31 ^ add. return ((hash << 5) + hash) ^ add; @@ -42,7 +42,7 @@ hashSum(size_t hash, inline void hashIncr(size_t &hash, - size_t add) + size_t add) { // hash * 31 ^ add. hash = ((hash << 5) + hash) ^ add; diff --git a/include/sta/HpinDrvrLoad.hh b/include/sta/HpinDrvrLoad.hh index dba934ccb..995e6e14c 100644 --- a/include/sta/HpinDrvrLoad.hh +++ b/include/sta/HpinDrvrLoad.hh @@ -24,7 +24,6 @@ #pragma once -#include "Set.hh" #include "NetworkClass.hh" namespace sta { @@ -34,14 +33,14 @@ class HpinDrvrLoadVisitor; void visitHpinDrvrLoads(const Pin *pin, - const Network *network, - HpinDrvrLoadVisitor *visitor); + const Network *network, + HpinDrvrLoadVisitor *visitor); class HpinDrvrLoadLess { public: bool operator()(const HpinDrvrLoad *drvr_load1, - const HpinDrvrLoad *drvr_load2) const; + const HpinDrvrLoad *drvr_load2) const; }; // Abstract base class for visitDrvrLoadsThruHierPin visitor. @@ -57,13 +56,13 @@ class HpinDrvrLoad { public: HpinDrvrLoad(const Pin *drvr, - const Pin *load, - PinSet *hpins_from_drvr, - PinSet *hpins_to_load); + const Pin *load, + PinSet *hpins_from_drvr, + PinSet *hpins_to_load); ~HpinDrvrLoad(); void report(const Network *network); HpinDrvrLoad(const Pin *drvr, - const Pin *load); + const Pin *load); const Pin *drvr() const { return drvr_; } const Pin *load() const { return load_; } PinSet *hpinsFromDrvr() { return hpins_from_drvr_; } diff --git a/include/sta/InputDrive.hh b/include/sta/InputDrive.hh index 1da228171..9a3e31198 100644 --- a/include/sta/InputDrive.hh +++ b/include/sta/InputDrive.hh @@ -40,45 +40,45 @@ class InputDriveCell; class InputDrive { public: - explicit InputDrive(); + InputDrive(); ~InputDrive(); void setSlew(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); + const MinMaxAll *min_max, + float slew); void setDriveResistance(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res); + const MinMaxAll *min_max, + float res); void driveResistance(const RiseFall *rf, - const MinMax *min_max, - float &res, - bool &exists) const; + const MinMax *min_max, + float &res, + bool &exists) const; bool hasDriveResistance(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; bool driveResistanceMinMaxEqual(const RiseFall *rf) const; void setDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max); + const LibertyCell *cell, + const LibertyPort *from_port, + float *from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max); void driveCell(const RiseFall *rf, - const MinMax *min_max, + const MinMax *min_max, // Return values. - const LibertyCell *&cell, - const LibertyPort *&from_port, - float *&from_slews, - const LibertyPort *&to_port) const; + const LibertyCell *&cell, + const LibertyPort *&from_port, + float *&from_slews, + const LibertyPort *&to_port) const; InputDriveCell *driveCell(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; bool hasDriveCell(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; // True if rise/fall/min/max drive cells are equal. bool driveCellsEqual() const; void slew(const RiseFall *rf, - const MinMax *min_max, - float &slew, - bool &exists) const; + const MinMax *min_max, + float &slew, + bool &exists) const; const RiseFallMinMax *slews() const { return &slews_; } private: @@ -92,10 +92,10 @@ class InputDriveCell { public: InputDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port); + const LibertyCell *cell, + const LibertyPort *from_port, + float *from_slews, + const LibertyPort *to_port); const LibertyLibrary *library() const { return library_; } void setLibrary(const LibertyLibrary *library); const LibertyCell *cell() const { return cell_; } diff --git a/include/sta/InternalPower.hh b/include/sta/InternalPower.hh index 0c30a38d3..408e946b3 100644 --- a/include/sta/InternalPower.hh +++ b/include/sta/InternalPower.hh @@ -41,7 +41,7 @@ public: FuncExpr *when() const { return when_; } void setWhen(FuncExpr *when); void setModel(const RiseFall *rf, - InternalPowerModel *model); + InternalPowerModel *model); InternalPowerModel *model(const RiseFall *rf) const; const char *relatedPgPin() const { return related_pg_pin_; } void setRelatedPgPin(const char *related_pg_pin); @@ -56,9 +56,9 @@ class InternalPower { public: InternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs); + LibertyPort *port, + LibertyPort *related_port, + InternalPowerAttrs *attrs); ~InternalPower(); LibertyCell *libertyCell() const; LibertyPort *port() const { return port_; } @@ -66,9 +66,9 @@ public: FuncExpr *when() const { return when_; } const char *relatedPgPin() const { return related_pg_pin_; } float power(const RiseFall *rf, - const Pvt *pvt, - float in_slew, - float load_cap); + const Pvt *pvt, + float in_slew, + float load_cap); protected: LibertyPort *port_; @@ -81,12 +81,12 @@ protected: class InternalPowerModel { public: - explicit InternalPowerModel(TableModel *model); + InternalPowerModel(TableModel *model); ~InternalPowerModel(); float power(const LibertyCell *cell, - const Pvt *pvt, - float in_slew, - float load_cap) const; + const Pvt *pvt, + float in_slew, + float load_cap) const; std::string reportPower(const LibertyCell *cell, const Pvt *pvt, float in_slew, @@ -95,14 +95,14 @@ public: protected: void findAxisValues(float in_slew, - float load_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const; + float load_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const; float axisValue(const TableAxis *axis, - float in_slew, - float load_cap) const; + float in_slew, + float load_cap) const; bool checkAxes(const TableModel *model); bool checkAxis(const TableAxis *axis); diff --git a/include/sta/Iterator.hh b/include/sta/Iterator.hh index 6c1ea6210..eade8d31c 100644 --- a/include/sta/Iterator.hh +++ b/include/sta/Iterator.hh @@ -40,4 +40,80 @@ public: virtual OBJ next() = 0; }; +template +class VectorIterator : public Iterator +{ +public: + VectorIterator(const VECTOR_TYPE *seq) : + seq_(seq) + { + if (seq_) + itr_ = seq_->begin(); + } + VectorIterator(const VECTOR_TYPE &seq) : + seq_(&seq), + itr_(seq.begin()) + { + } + + bool hasNext() { return seq_ && itr_ != seq_->end(); } + OBJ_TYPE next() { return *itr_++; } + +protected: + const VECTOR_TYPE *seq_; + VECTOR_TYPE::const_iterator itr_; +}; + +template +class MapIterator : public Iterator +{ +public: + MapIterator(const MAP_TYPE *map) : + map_(map) + { + if (map) + itr_ = map->begin(); + } + MapIterator(const MAP_TYPE &map) : + map_(&map), + itr_(map.begin()) + { + } + + bool hasNext() { return map_ && itr_ != map_->end(); } + OBJ_TYPE next() { + OBJ_TYPE next = itr_->second; + itr_++; + return next; + } + +protected: + const MAP_TYPE *map_; + MAP_TYPE::const_iterator itr_; +}; + +template +class SetIterator : public Iterator +{ +public: + SetIterator(const SET_TYPE *set) : + set_(set) + { + if (set) + itr_ = set->begin(); + } + SetIterator(const SET_TYPE &set) : + set_(&set), + itr_(set.begin()) + { + } + + bool hasNext() { return set_ && itr_ != set_->end(); } + OBJ_TYPE next() { return *itr_++; } + +protected: + const SET_TYPE *set_; + SET_TYPE::const_iterator itr_; +}; + } // namespace diff --git a/include/sta/LeakagePower.hh b/include/sta/LeakagePower.hh index a0dbd86d9..0b542636e 100644 --- a/include/sta/LeakagePower.hh +++ b/include/sta/LeakagePower.hh @@ -48,7 +48,7 @@ class LeakagePower { public: LeakagePower(LibertyCell *cell, - LeakagePowerAttrs *attrs); + LeakagePowerAttrs *attrs); ~LeakagePower(); LibertyCell *libertyCell() const { return cell_; } FuncExpr *when() const { return when_; } diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 09b4579a2..c89a5a66e 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -27,7 +27,11 @@ #include #include #include +#include +#include +#include +#include "ContainerHelpers.hh" #include "MinMax.hh" #include "RiseFallMinMax.hh" #include "ConcreteLibrary.hh" @@ -56,40 +60,37 @@ class OcvDerate; class TimingArcAttrs; class InternalPowerAttrs; class StaState; -class Corner; -class Corners; -class DcalcAnalysisPt; +class Scene; class DriverWaveform; -typedef Map TableTemplateMap; -typedef Vector TableTemplateSeq; -typedef Map BusDclMap; -typedef Vector BusDclSeq; -typedef Map ScaleFactorsMap; -typedef Map WireloadMap; -typedef Map WireloadSelectionMap; -typedef Map OperatingConditionsMap; -typedef Map PortToSequentialMap; -typedef Vector TimingArcSetSeq; -typedef Set TimingArcSetMap; -typedef Map LibertyPortPairTimingArcMap; -typedef Vector InternalPowerSeq; -typedef Map PortInternalPowerSeq; -typedef Vector LeakagePowerSeq; -typedef Map LibertyPortTimingArcMap; -typedef Map ScaledCellMap; -typedef Map ScaledPortMap; -typedef Map ModeDefMap; -typedef Map ModeValueMap; -typedef Map LatchEnableMap; -typedef Vector LatchEnableSeq; -typedef Map OcvDerateMap; -typedef Vector InternalPowerAttrsSeq; -typedef Map SupplyVoltageMap; -typedef Map DriverWaveformMap; -typedef Vector DcalcAnalysisPtSeq; +using TableTemplateMap = std::map; +using TableTemplateSeq = std::vector; +using BusDclMap = std::map; +using BusDclSeq = std::vector; +using ScaleFactorsMap = std::map; +using WireloadMap = std::map; +using WireloadSelectionMap = std::map; +using OperatingConditionsMap = std::map; +using PortToSequentialMap = std::map; +using TimingArcSetSeq = std::vector; +using TimingArcSetSet = std::set; +using LibertyPortPairTimingArcMap = std::map; +using InternalPowerSeq = std::vector; +using PortInternalPowerMap = std::map; +using LeakagePowerSeq = std::vector; +using LibertyPortTimingArcMap = std::map; +using ScaledCellMap = std::map; +using ScaledPortMap = std::map; +using ModeDefMap = std::map; +using ModeValueMap = std::map; +using LatchEnableMap = std::map; +using LatchEnableSeq = std::vector; +using OcvDerateMap = std::map; +using InternalPowerAttrsSeq = std::vector; +using SupplyVoltageMap = std::map; +using DriverWaveformMap = std::map; +using SceneSeq = std::vector; enum class ClockGateType { none, latch_posedge, latch_negedge, other }; @@ -98,11 +99,11 @@ enum class DelayModelType { cmos_linear, cmos_pwl, cmos2, table, polynomial, dcm enum class ScanSignalType { enable, enable_inverted, clock, clock_a, clock_b, input, input_inverted, output, output_inverted, none }; enum class PwrGndType { none, - primary_power, primary_ground, - backup_power, backup_ground, - internal_power, internal_ground, - nwell, pwell, - deepnwell, deeppwell}; + primary_power, primary_ground, + backup_power, backup_ground, + internal_power, internal_ground, + nwell, pwell, + deepnwell, deeppwell}; enum class ScaleFactorPvt { process, volt, temp, unknown }; constexpr int scale_factor_pvt_count = int(ScaleFactorPvt::unknown) + 1; @@ -151,7 +152,7 @@ class LibertyLibrary : public ConcreteLibrary { public: LibertyLibrary(const char *name, - const char *filename); + const char *filename); virtual ~LibertyLibrary(); LibertyCell *findLibertyCell(const char *name) const; LibertyCellSeq findLibertyCellsMatching(PatternMatch *pattern); @@ -165,9 +166,9 @@ public: BusDcl *findBusDcl(const char *name) const; BusDclSeq busDcls() const; void addTableTemplate(TableTemplate *tbl_template, - TableTemplateType type); + TableTemplateType type); TableTemplate *findTableTemplate(const char *name, - TableTemplateType type); + TableTemplateType type); TableTemplateSeq tableTemplates() const; float nominalProcess() const { return nominal_process_; } void setNominalProcess(float process); @@ -182,21 +183,21 @@ public: ScaleFactors *findScaleFactors(const char *name); ScaleFactors *scaleFactors() const { return scale_factors_; } float scaleFactor(ScaleFactorType type, - const Pvt *pvt) const; + const Pvt *pvt) const; float scaleFactor(ScaleFactorType type, - const LibertyCell *cell, - const Pvt *pvt) const; + const LibertyCell *cell, + const Pvt *pvt) const; float scaleFactor(ScaleFactorType type, - int rf_index, - const LibertyCell *cell, - const Pvt *pvt) const; + int rf_index, + const LibertyCell *cell, + const Pvt *pvt) const; void setWireSlewDegradationTable(TableModel *model, - const RiseFall *rf); + const RiseFall *rf); TableModel *wireSlewDegradationTable(const RiseFall *rf) const; float degradeWireSlew(const RiseFall *rf, - float in_slew, - float wire_delay) const; + float in_slew, + float wire_delay) const; // Check for supported axis variables. // Return true if axes are supported. static bool checkSlewDegradationAxes(const TablePtr &table); @@ -215,58 +216,58 @@ public: float &intrisic, bool &exists) const; void setDefaultIntrinsic(const RiseFall *rf, - float value); + float value); // Uses defaultOutputPinRes or defaultBidirectPinRes based on dir. void defaultPinResistance(const RiseFall *rf, - const PortDirection *dir, - // Return values. - float &res, - bool &exists) const; + const PortDirection *dir, + // Return values. + float &res, + bool &exists) const; void defaultBidirectPinRes(const RiseFall *rf, - // Return values. - float &res, - bool &exists) const; + // Return values. + float &res, + bool &exists) const; void setDefaultBidirectPinRes(const RiseFall *rf, - float value); + float value); void defaultOutputPinRes(const RiseFall *rf, - // Return values. - float &res, - bool &exists) const; + // Return values. + float &res, + bool &exists) const; void setDefaultOutputPinRes(const RiseFall *rf, - float value); + float value); void defaultMaxSlew(float &slew, - bool &exists) const; + bool &exists) const; void setDefaultMaxSlew(float slew); void defaultMaxCapacitance(float &cap, - bool &exists) const; + bool &exists) const; void setDefaultMaxCapacitance(float cap); void defaultMaxFanout(float &fanout, - bool &exists) const; + bool &exists) const; void setDefaultMaxFanout(float fanout); void defaultFanoutLoad(// Return values. - float &fanout, - bool &exists) const; + float &fanout, + bool &exists) const; void setDefaultFanoutLoad(float load); // Logic thresholds. float inputThreshold(const RiseFall *rf) const; void setInputThreshold(const RiseFall *rf, - float th); + float th); float outputThreshold(const RiseFall *rf) const; void setOutputThreshold(const RiseFall *rf, - float th); + float th); // Slew thresholds (measured). float slewLowerThreshold(const RiseFall *rf) const; void setSlewLowerThreshold(const RiseFall *rf, - float th); + float th); float slewUpperThreshold(const RiseFall *rf) const; void setSlewUpperThreshold(const RiseFall *rf, - float th); + float th); // The library and delay calculator use the liberty slew upper/lower // (measured) thresholds for the table axes and value. These slews // are scaled by slew_derate_from_library to get slews reported to @@ -304,37 +305,37 @@ public: OcvDerate *findOcvDerate(const char *derate_name); void addOcvDerate(OcvDerate *derate); void addSupplyVoltage(const char *suppy_name, - float voltage); + float voltage); bool supplyExists(const char *suppy_name) const; void supplyVoltage(const char *supply_name, - // Return value. - float &voltage, - bool &exists) const; + // Return value. + float &voltage, + bool &exists) const; // Make scaled cell. Call LibertyCell::addScaledCell after it is complete. LibertyCell *makeScaledCell(const char *name, - const char *filename); + const char *filename); static void - makeCornerMap(LibertyLibrary *lib, - int ap_index, - Network *network, - Report *report); - static void - makeCornerMap(LibertyCell *link_cell, - LibertyCell *map_cell, - int ap_index, - Report *report); + makeSceneMap(LibertyLibrary *lib, + int ap_index, + Network *network, + Report *report); static void - makeCornerMap(LibertyCell *cell1, - LibertyCell *cell2, - bool link, - int ap_index, - Report *report); + makeSceneMap(LibertyCell *link_cell, + LibertyCell *map_cell, + int ap_index, + Report *report); static void - checkCorners(LibertyCell *cell, - Corners *corners, + makeSceneMap(LibertyCell *cell1, + LibertyCell *cell2, + bool link, + int ap_index, Report *report); + static void + checkScenes(LibertyCell *cell, + const SceneSeq &scenes, + Report *report); DriverWaveform *findDriverWaveform(const char *name); DriverWaveform *driverWaveformDefault() { return driver_waveform_default_; } @@ -342,8 +343,8 @@ public: protected: float degradeWireSlew(const TableModel *model, - float in_slew, - float wire_delay) const; + float in_slew, + float wire_delay) const; Units *units_; DelayModelType delay_model_type_; @@ -404,12 +405,12 @@ private: class LibertyCellIterator : public Iterator { public: - explicit LibertyCellIterator(const LibertyLibrary *library); + LibertyCellIterator(const LibertyLibrary *library); bool hasNext(); LibertyCell *next(); private: - ConcreteCellMap::ConstIterator iter_; + ConcreteLibraryCellIterator iter_; }; //////////////////////////////////////////////////////////////// @@ -418,8 +419,8 @@ class LibertyCell : public ConcreteCell { public: LibertyCell(LibertyLibrary *library, - const char *name, - const char *filename); + const char *name, + const char *filename); virtual ~LibertyCell(); LibertyLibrary *libertyLibrary() const { return liberty_library_; } LibertyLibrary *libertyLibrary() { return liberty_library_; } @@ -474,8 +475,8 @@ public: const InternalPowerSeq &internalPowers(const LibertyPort *port); LeakagePowerSeq *leakagePowers() { return &leakage_powers_; } void leakagePower(// Return values. - float &leakage, - bool &exists) const; + float &leakage, + bool &exists) const; bool leakagePowerExists() const { return leakage_power_exists_; } // Register, Latch or Statetable. @@ -492,16 +493,14 @@ public: bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; } TestCell *testCell() const { return test_cell_; } void latchEnable(const TimingArcSet *arc_set, - // Return values. - const LibertyPort *&enable_port, - const FuncExpr *&enable_func, - const RiseFall *&enable_rf) const; + // Return values. + const LibertyPort *&enable_port, + const FuncExpr *&enable_func, + const RiseFall *&enable_rf) const; const RiseFall *latchCheckEnableEdge(TimingArcSet *check_set); - bool isDisabledConstraint() const { return is_disabled_constraint_; } - LibertyCell *cornerCell(const Corner *corner, - const MinMax *min_max); - LibertyCell *cornerCell(const DcalcAnalysisPt *dcalc_ap); - LibertyCell *cornerCell(int ap_index); + LibertyCell *sceneCell(const Scene *scene, + const MinMax *min_max); + LibertyCell *sceneCell(int ap_index); // AOCV float ocvArcDepth() const; @@ -510,22 +509,22 @@ public: // Build helpers. void makeSequential(int size, - bool is_register, - FuncExpr *clk, - FuncExpr *data, - FuncExpr *clear, - FuncExpr *preset, - LogicValue clr_preset_out, - LogicValue clr_preset_out_inv, - LibertyPort *output, - LibertyPort *output_inv); + bool is_register, + FuncExpr *clk, + FuncExpr *data, + FuncExpr *clear, + FuncExpr *preset, + LogicValue clr_preset_out, + LogicValue clr_preset_out_inv, + LibertyPort *output, + LibertyPort *output_inv); void makeStatetable(LibertyPortSeq &input_ports, LibertyPortSeq &internal_ports, StatetableRows &table); void addBusDcl(BusDcl *bus_dcl); // Add scaled cell after it is complete. void addScaledCell(OperatingConditions *op_cond, - LibertyCell *scaled_cell); + LibertyCell *scaled_cell); unsigned addTimingArcSet(TimingArcSet *set); void addInternalPower(InternalPower *power); void addInternalPowerAttrs(InternalPowerAttrs *attrs); @@ -536,23 +535,22 @@ public: void addOcvDerate(OcvDerate *derate); void setTestCell(TestCell *test); void setHasInferedRegTimingArcs(bool infered); - void setIsDisabledConstraint(bool is_disabled); - void setCornerCell(LibertyCell *corner_cell, - int ap_index); + void setSceneCell(LibertyCell *scene_cell, + int ap_index); // Call after cell is finished being constructed. void finish(bool infer_latches, - Report *report, - Debug *debug); + Report *report, + Debug *debug); bool isBuffer() const; bool isInverter() const; // Only valid when isBuffer() returns true. void bufferPorts(// Return values. - LibertyPort *&input, - LibertyPort *&output) const; + LibertyPort *&input, + LibertyPort *&output) const; // Check all liberty cells to make sure they exist - // for all the defined corners. - static void checkLibertyCorners(); - void ensureVoltageWaveforms(const DcalcAnalysisPtSeq &dcalc_aps); + // for all the defined scenes. + static void checkLibertyScenes(); + void ensureVoltageWaveforms(const SceneSeq &scenes); const char *footprint() const; void setFootprint(const char *footprint); const char *userFunctionClass() const; @@ -563,18 +561,18 @@ protected: void setHasInternalPorts(bool has_internal); void setLibertyLibrary(LibertyLibrary *library); void makeLatchEnables(Report *report, - Debug *debug); + Debug *debug); FuncExpr *findLatchEnableFunc(const LibertyPort *d, const LibertyPort *en, const RiseFall *en_rf) const; LatchEnable *makeLatchEnable(LibertyPort *d, - LibertyPort *en, + LibertyPort *en, const RiseFall *en_rf, - LibertyPort *q, - TimingArcSet *d_to_q, - TimingArcSet *en_to_q, - TimingArcSet *setup_check, - Debug *debug); + LibertyPort *q, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check, + Debug *debug); TimingArcSet *findLatchSetup(const LibertyPort *d, const LibertyPort *en, const RiseFall *en_rf, @@ -591,10 +589,10 @@ protected: void makeTimingArcMap(Report *report); void makeTimingArcPortMaps(); bool hasBufferFunc(const LibertyPort *input, - const LibertyPort *output) const; + const LibertyPort *output) const; bool hasInverterFunc(const LibertyPort *input, - const LibertyPort *output) const; - bool checkCornerCell(const Corner *corner, + const LibertyPort *output) const; + bool checkSceneCell(const Scene *scene, const MinMax *min_max) const; LibertyLibrary *liberty_library_; @@ -612,13 +610,13 @@ protected: bool interface_timing_; ClockGateType clock_gate_type_; TimingArcSetSeq timing_arc_sets_; - TimingArcSetMap timing_arc_set_map_; + TimingArcSetSet timing_arc_set_set_; LibertyPortPairTimingArcMap port_timing_arc_set_map_; LibertyPortTimingArcMap timing_arc_set_from_map_; LibertyPortTimingArcMap timing_arc_set_to_map_; bool has_infered_reg_timing_arcs_; InternalPowerSeq internal_powers_; - PortInternalPowerSeq port_internal_powers_; + PortInternalPowerMap port_internal_powers_; InternalPowerAttrsSeq internal_power_attrs_; LeakagePowerSeq leakage_powers_; SequentialSeq sequentials_; @@ -639,8 +637,7 @@ protected: float ocv_arc_depth_; OcvDerate *ocv_derate_; OcvDerateMap ocv_derate_map_; - bool is_disabled_constraint_; - Vector corner_cells_; + std::vector scene_cells_; float leakage_power_; bool leakage_power_exists_; bool has_internal_ports_; @@ -659,18 +656,18 @@ private: class LibertyCellPortIterator : public Iterator { public: - explicit LibertyCellPortIterator(const LibertyCell *cell); + LibertyCellPortIterator(const LibertyCell *cell); bool hasNext(); LibertyPort *next(); private: - ConcretePortSeq::ConstIterator iter_; + ConcreteCellPortIterator iter_; }; class LibertyCellPortBitIterator : public Iterator { public: - explicit LibertyCellPortBitIterator(const LibertyCell *cell); + LibertyCellPortBitIterator(const LibertyCell *cell); virtual ~LibertyCellPortBitIterator(); bool hasNext(); LibertyPort *next(); @@ -704,33 +701,33 @@ public: ScanSignalType scanSignalType() const { return scan_signal_type_; } void setScanSignalType(ScanSignalType type); void fanoutLoad(// Return values. - float &fanout_load, - bool &exists) const; + float &fanout_load, + bool &exists) const; void setFanoutLoad(float fanout_load); float capacitance() const; float capacitance(const MinMax *min_max) const; float capacitance(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; void capacitance(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists) const; + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const; // Capacitance at op_cond derated by library/cell scale factors // using pvt. float capacitance(const RiseFall *rf, - const MinMax *min_max, - const OperatingConditions *op_cond, - const Pvt *pvt) const; + const MinMax *min_max, + const OperatingConditions *op_cond, + const Pvt *pvt) const; bool capacitanceIsOneValue() const; void setCapacitance(float cap); void setCapacitance(const RiseFall *rf, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap); // Max of rise/fall. float driveResistance() const; float driveResistance(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; // Zero load delay. ArcDelay intrinsicDelay(const StaState *sta) const; ArcDelay intrinsicDelay(const RiseFall *rf, @@ -742,38 +739,38 @@ public: FuncExpr *tristateEnable() const { return tristate_enable_; } void setTristateEnable(FuncExpr *enable); void slewLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const; + // Return values. + float &limit, + bool &exists) const; void setSlewLimit(float slew, - const MinMax *min_max); + const MinMax *min_max); void capacitanceLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const; + // Return values. + float &limit, + bool &exists) const; void setCapacitanceLimit(float cap, - const MinMax *min_max); + const MinMax *min_max); void fanoutLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const; + // Return values. + float &limit, + bool &exists) const; void setFanoutLimit(float fanout, - const MinMax *min_max); + const MinMax *min_max); void minPeriod(const OperatingConditions *op_cond, - const Pvt *pvt, - float &min_period, - bool &exists) const; + const Pvt *pvt, + float &min_period, + bool &exists) const; // Unscaled value. void minPeriod(float &min_period, - bool &exists) const; + bool &exists) const; void setMinPeriod(float min_period); // This corresponds to the min_pulse_width_high/low port attribute. // high = rise, low = fall void minPulseWidth(const RiseFall *hi_low, - float &min_width, - bool &exists) const; + float &min_width, + bool &exists) const; void setMinPulseWidth(const RiseFall *hi_low, - float min_width); + float min_width); bool isClock() const; void setIsClock(bool is_clk); bool isClockGateClock() const { return is_clk_gate_clk_; } @@ -813,19 +810,14 @@ public: // Rise for high, fall for low. const RiseFall *pulseClkSense() const { return pulse_clk_sense_; } void setPulseClk(const RiseFall *rfigger, - const RiseFall *sense); - bool isDisabledConstraint() const { return is_disabled_constraint_; } - void setIsDisabledConstraint(bool is_disabled); - LibertyPort *cornerPort(const Corner *corner, - const MinMax *min_max); - const LibertyPort *cornerPort(const Corner *corner, - const MinMax *min_max) const; - LibertyPort *cornerPort(const DcalcAnalysisPt *dcalc_ap); - const LibertyPort *cornerPort(const DcalcAnalysisPt *dcalc_ap) const; - LibertyPort *cornerPort(int ap_index); - const LibertyPort *cornerPort(int ap_index) const; - void setCornerPort(LibertyPort *corner_port, - int ap_index); + const RiseFall *sense); + LibertyPort *scenePort(const Scene *scene, + const MinMax *min_max); + const LibertyPort *scenePort(const Scene *scene, + const MinMax *min_max) const; + const LibertyPort *scenePort(int ap_index) const; + void setScenePort(LibertyPort *scene_port, + int ap_index); const char *relatedGroundPin() const; void setRelatedGroundPin(const char *related_ground_pin); const char *relatedPowerPin() const; @@ -852,35 +844,36 @@ public: RiseFallMinMax clockTreePathDelays() const __attribute__ ((deprecated)); static bool equiv(const LibertyPort *port1, - const LibertyPort *port2); + const LibertyPort *port2); static bool less(const LibertyPort *port1, - const LibertyPort *port2); + const LibertyPort *port2); protected: // Constructor is internal to LibertyBuilder. LibertyPort(LibertyCell *cell, - const char *name, - bool is_bus, - BusDcl *bus_dcl, + const char *name, + bool is_bus, + BusDcl *bus_dcl, int from_index, - int to_index, - bool is_bundle, - ConcretePortSeq *members); + int to_index, + bool is_bundle, + ConcretePortSeq *members); virtual ~LibertyPort(); void setMinPort(LibertyPort *min); void addScaledPort(OperatingConditions *op_cond, - LibertyPort *scaled_port); + LibertyPort *scaled_port); RiseFallMinMax clkTreeDelays1() const; void setMemberFlag(bool value, - const std::function &setter); + const std::function &setter); void setMemberFloat(float value, - const std::function &setter); + const std::function &setter); void setMemberMinMaxFloat(float value, - const MinMax *min_max, - const std::function &setter); + const MinMax *min_max, + const std::function &setter); + LibertyPort *scenePort(int ap_index); LibertyCell *liberty_cell_; BusDcl *bus_dcl_; @@ -902,7 +895,7 @@ protected: const RiseFall *pulse_clk_sense_; std::string related_ground_pin_; std::string related_power_pin_; - Vector corner_ports_; + std::vector scene_ports_; ReceiverModelPtr receiver_model_; DriverWaveform *driver_waveform_[RiseFall::index_count]; // Redundant with clock_tree_path_delay timing arcs but faster to access. @@ -923,7 +916,6 @@ protected: bool isolation_cell_enable_:1; bool level_shifter_data_:1; bool is_switch_:1; - bool is_disabled_constraint_:1; bool is_pad_:1; private: @@ -939,7 +931,7 @@ sortByName(const LibertyPortSet *set); class LibertyPortMemberIterator : public Iterator { public: - explicit LibertyPortMemberIterator(const LibertyPort *port); + LibertyPortMemberIterator(const LibertyPort *port); virtual ~LibertyPortMemberIterator(); virtual bool hasNext(); virtual LibertyPort *next(); @@ -974,10 +966,10 @@ class OperatingConditions : public Pvt public: OperatingConditions(const char *name); OperatingConditions(const char *name, - float process, - float voltage, - float temperature, - WireloadTree wire_load_tree); + float process, + float voltage, + float temperature, + WireloadTree wire_load_tree); const char *name() const { return name_.c_str(); } WireloadTree wireloadTree() const { return wire_load_tree_; } void setWireloadTree(WireloadTree tree); @@ -990,23 +982,23 @@ protected: class ScaleFactors { public: - explicit ScaleFactors(const char *name); + ScaleFactors(const char *name); const char *name() const { return name_.c_str(); } float scale(ScaleFactorType type, - ScaleFactorPvt pvt, - const RiseFall *rf); + ScaleFactorPvt pvt, + const RiseFall *rf); float scale(ScaleFactorType type, - ScaleFactorPvt pvt, - int rf_index); + ScaleFactorPvt pvt, + int rf_index); float scale(ScaleFactorType type, - ScaleFactorPvt pvt); + ScaleFactorPvt pvt); void setScale(ScaleFactorType type, - ScaleFactorPvt pvt, - const RiseFall *rf, - float scale); + ScaleFactorPvt pvt, + const RiseFall *rf, + float scale); void setScale(ScaleFactorType type, - ScaleFactorPvt pvt, - float scale); + ScaleFactorPvt pvt, + float scale); void print(); protected: @@ -1018,8 +1010,8 @@ class BusDcl { public: BusDcl(const char *name, - int from, - int to); + int from, + int to); const char *name() const { return name_.c_str(); } int from() const { return from_; } int to() const { return to_; } @@ -1037,8 +1029,8 @@ public: ~ModeDef(); const char *name() const { return name_.c_str(); } ModeValueDef *defineValue(const char *value, - FuncExpr *cond, - const char *sdf_cond); + FuncExpr *cond, + const char *sdf_cond); ModeValueDef *findValueDef(const char *value); ModeValueMap *values() { return &values_; } @@ -1067,8 +1059,8 @@ public: protected: // Private to ModeDef::defineValue. ModeValueDef(const char *value, - FuncExpr *cond, - const char *sdf_cond); + FuncExpr *cond, + const char *sdf_cond); std::string value_; FuncExpr *cond_; @@ -1083,9 +1075,9 @@ class TableTemplate public: TableTemplate(const char *name); TableTemplate(const char *name, - TableAxisPtr axis1, - TableAxisPtr axis2, - TableAxisPtr axis3); + TableAxisPtr axis1, + TableAxisPtr axis2, + TableAxisPtr axis3); const char *name() const { return name_.c_str(); } void setName(const char *name); const TableAxis *axis1() const { return axis1_.get(); } @@ -1125,9 +1117,9 @@ public: const EarlyLate *early_late, PathType path_type); void setDerateTable(const RiseFall *rf, - const EarlyLate *early_late, - PathType path_type, - TablePtr derate); + const EarlyLate *early_late, + PathType path_type, + TablePtr derate); private: const char *name_; diff --git a/include/sta/LibertyClass.hh b/include/sta/LibertyClass.hh index 06f2729f9..1d200a762 100644 --- a/include/sta/LibertyClass.hh +++ b/include/sta/LibertyClass.hh @@ -25,10 +25,9 @@ #pragma once #include - -#include "Vector.hh" -#include "Map.hh" -#include "Set.hh" +#include +#include +#include namespace sta { @@ -67,19 +66,19 @@ class ReceiverModel; class Statetable; class StatetableRow; -typedef Vector LibertyLibrarySeq; -typedef Vector LibertyCellSeq; -typedef Vector SequentialSeq; -typedef Map LibertyCellEquivMap; -typedef Vector LibertyPortSeq; -typedef Set LibertyPortSet; -typedef std::pair LibertyPortPair; -typedef Set LibertyCellSet; -typedef std::shared_ptr TablePtr; -typedef std::shared_ptr TimingArcAttrsPtr; -typedef std::shared_ptr TableAxisPtr; -typedef std::shared_ptr ReceiverModelPtr; -typedef std::vector StatetableRows; +using LibertyLibrarySeq = std::vector; +using LibertyCellSeq = std::vector; +using SequentialSeq = std::vector; +using LibertyCellEquivMap = std::map; +using LibertyPortSeq = std::vector; +using LibertyPortSet = std::set; +using LibertyPortPair = std::pair; +using LibertyCellSet = std::set; +using TablePtr = std::shared_ptr
; +using TimingArcAttrsPtr = std::shared_ptr; +using TableAxisPtr = std::shared_ptr; +using ReceiverModelPtr = std::shared_ptr; +using StatetableRows = std::vector; enum class ScaleFactorType : unsigned { pin_cap, @@ -161,19 +160,19 @@ class LibertyPortPairLess { public: bool operator()(const LibertyPortPair &pair1, - const LibertyPortPair &pair2) const; + const LibertyPortPair &pair2) const; }; bool timingArcSetLess(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); class TimingArcSetLess { public: bool operator()(const TimingArcSet *set1, - const TimingArcSet *set2) const + const TimingArcSet *set2) const { return timingArcSetLess(set1, set2); } diff --git a/include/sta/LinearModel.hh b/include/sta/LinearModel.hh index f70cd7375..a173ad6b0 100644 --- a/include/sta/LinearModel.hh +++ b/include/sta/LinearModel.hh @@ -58,8 +58,8 @@ protected: class CheckLinearModel : public CheckTimingModel { public: - explicit CheckLinearModel(LibertyCell *cell, - float intrinsic); + CheckLinearModel(LibertyCell *cell, + float intrinsic); ArcDelay checkDelay(const Pvt *pvt, float from_slew, float to_slew, diff --git a/include/sta/Machine.hh b/include/sta/Machine.hh index c66b3ab9b..68e955778 100644 --- a/include/sta/Machine.hh +++ b/include/sta/Machine.hh @@ -72,7 +72,7 @@ #define vsnprint vsnprintf #endif -#include // size_t +#include // size_t namespace sta { diff --git a/include/sta/Map.hh b/include/sta/Map.hh deleted file mode 100644 index bfcb64550..000000000 --- a/include/sta/Map.hh +++ /dev/null @@ -1,191 +0,0 @@ -// 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 -#include - -namespace sta { - -// Add convenience functions around STL container. -template > -class Map : public std::map -{ -public: - Map() : - std::map() - { - } - explicit Map(const CMP &cmp) : - std::map(cmp) - { - } - - // Find out if key is in the set. - bool - hasKey(const KEY key) const - { - return this->find(key) != this->end(); - } - - // Find the value corresponding to key. - VALUE - findKey(const KEY key) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) - return find_iter->second; - else - return nullptr; - } - void - findKey(const KEY key, - // Return Values. - VALUE &value, - bool &exists) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) { - value = find_iter->second; - exists = true; - } - else - exists = false; - } - void - findKey(const KEY &key, - // Return Values. - KEY &map_key, - VALUE &value, - bool &exists) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) { - map_key = find_iter->first; - value = find_iter->second; - exists = true; - } - else - exists = false; - } - - void - insert(const KEY &key, - VALUE value) - { - this->operator[](key) = value; - } - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteKeysContents() - { - for (const auto [key, value] : this) { - delete key; - delete value; - } - } - - void - deleteArrayContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete [] iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - std::map::clear(); - } - - // Java style container itererator - // Map::Iterator iter(map); - // while (iter.hasNext()) { - // Value *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - explicit Iterator(std::map *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit Iterator(std::map &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(std::map *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(std::map &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - VALUE next() { return iter_++->second; } - void next(KEY &key, - VALUE &value) - { key = iter_->first; value = iter_->second; iter_++; } - std::map *container() { return container_; } - - private: - std::map *container_; - typename std::map::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - explicit ConstIterator(const std::map *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit ConstIterator(const std::map &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(const std::map *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(const std::map &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - VALUE next() { return iter_++->second; } - void next(KEY &key, - VALUE &value) - { key = iter_->first; value = iter_->second; iter_++; } - const std::map *container() { return container_; } - - private: - const std::map *container_; - typename std::map::const_iterator iter_; - }; -}; - -} // namespace diff --git a/include/sta/MinMax.hh b/include/sta/MinMax.hh index 15de0e721..4642c7905 100644 --- a/include/sta/MinMax.hh +++ b/include/sta/MinMax.hh @@ -36,8 +36,8 @@ class MinMax; class MinMaxAll; // Use typedefs to make early/late functional equivalents to min/max. -typedef MinMax EarlyLate; -typedef MinMaxAll EarlyLateAll; +using EarlyLate = MinMax; +using EarlyLateAll = MinMaxAll; // Large value used for min/max initial values. extern const float INF; @@ -62,10 +62,10 @@ public: int initValueInt() const { return init_value_int_; } // Max value1 > value2, Min value1 < value2. bool compare(float value1, - float value2) const; + float value2) const; // min/max(value1, value2) float minMax(float value1, - float value2) const; + float value2) const; const MinMaxAll *asMinMaxAll() const; const MinMax *opposite() const; // for range support. @@ -83,18 +83,18 @@ public: private: MinMax(const char *name, - int index, - float init_value, + int index, + float init_value, int init_value_int, - bool (*compare)(float value1, - float value2)); + bool (*compare)(float value1, + float value2)); const std::string name_; int index_; float init_value_; int init_value_int_; bool (*compare_)(float value1, - float value2); + float value2); static const MinMax min_; static const MinMax max_; @@ -112,6 +112,7 @@ public: static const MinMaxAll *max() { return &max_; } static const MinMaxAll *late() { return &max_; } static const MinMaxAll *all() { return &all_; } + static const MinMaxAll *minMax() { return &all_; } const std::string &to_string() const { return name_; } int index() const { return index_; } const MinMax *asMinMax() const; @@ -125,9 +126,9 @@ public: private: MinMaxAll(const char *name, - int index, - std::vector range, - std::vector range_index); + int index, + std::vector range, + std::vector range_index); const std::string name_; int index_; diff --git a/include/sta/MinMaxValues.hh b/include/sta/MinMaxValues.hh index aa3020710..6a96ee0bd 100644 --- a/include/sta/MinMaxValues.hh +++ b/include/sta/MinMaxValues.hh @@ -82,7 +82,7 @@ public: void setValue(const MinMaxAll *min_max, - TYPE value) + TYPE value) { for (auto mm_index : min_max->rangeIndex()) { values_[mm_index] = value; @@ -92,7 +92,7 @@ public: void setValue(const MinMax *min_max, - TYPE value) + TYPE value) { int mm_index = min_max->index(); values_[mm_index] = value; @@ -101,11 +101,11 @@ public: void mergeValue(const MinMax *min_max, - TYPE value) + TYPE value) { int mm_index = min_max->index(); if (!exists_[mm_index] - || min_max->compare(value, values_[mm_index])) { + || min_max->compare(value, values_[mm_index])) { values_[mm_index] = value; exists_[mm_index] = true; } @@ -126,9 +126,9 @@ public: void value(const MinMax *min_max, - // Return values. - TYPE &value, - bool &exists) const + // Return values. + TYPE &value, + bool &exists) const { int mm_index = min_max->index(); exists = exists_[mm_index]; @@ -150,36 +150,36 @@ public: } static bool equal(const MinMaxValues *values1, - const MinMaxValues *values2) + const MinMaxValues *values2) { return ((!values1->exists_[MinMax::minIndex()] - && !values2->exists_[MinMax::minIndex()]) - || (values1->exists_[MinMax::minIndex()] - && values2->exists_[MinMax::minIndex()] - && values1->values_[MinMax::minIndex()] - == values2->values_[MinMax::minIndex()])) + && !values2->exists_[MinMax::minIndex()]) + || (values1->exists_[MinMax::minIndex()] + && values2->exists_[MinMax::minIndex()] + && values1->values_[MinMax::minIndex()] + == values2->values_[MinMax::minIndex()])) && ((!values1->exists_[MinMax::maxIndex()] - && !values2->exists_[MinMax::maxIndex()]) - || (values1->exists_[MinMax::maxIndex()] - && values2->exists_[MinMax::maxIndex()] - && values1->values_[MinMax::maxIndex()] - == values2->values_[MinMax::maxIndex()])); + && !values2->exists_[MinMax::maxIndex()]) + || (values1->exists_[MinMax::maxIndex()] + && values2->exists_[MinMax::maxIndex()] + && values1->values_[MinMax::maxIndex()] + == values2->values_[MinMax::maxIndex()])); } static int cmp(const MinMaxValues *values1, - const MinMaxValues *values2) + const MinMaxValues *values2) { if (!values1->exists_[MinMax::minIndex()] - && values2->exists_[MinMax::minIndex()]) + && values2->exists_[MinMax::minIndex()]) return -1; if (values1->exists_[MinMax::minIndex()] - && !values2->exists_[MinMax::minIndex()]) + && !values2->exists_[MinMax::minIndex()]) return 1; if (!values1->exists_[MinMax::maxIndex()] - && values2->exists_[MinMax::maxIndex()]) + && values2->exists_[MinMax::maxIndex()]) return -1; if (values1->exists_[MinMax::maxIndex()] - && !values2->exists_[MinMax::maxIndex()]) + && !values2->exists_[MinMax::maxIndex()]) return 1; if (values1->values_[MinMax::minIndex()] < values2->values_[MinMax::minIndex()]) return -1; @@ -197,7 +197,7 @@ private: bool exists_[MinMax::index_count]; }; -typedef MinMaxValues MinMaxFloatValues; -typedef MinMaxValues MinMaxIntValues; +using MinMaxFloatValues = MinMaxValues; +using MinMaxIntValues = MinMaxValues; } // namespace diff --git a/include/sta/Mode.hh b/include/sta/Mode.hh new file mode 100644 index 000000000..0c0827e11 --- /dev/null +++ b/include/sta/Mode.hh @@ -0,0 +1,95 @@ +// 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 + +#include "StaState.hh" + +namespace sta { + +class Sdc; +class Sim; +class ClkNetwork; +class Genclks; +class PathGroups; + +using PathGroupSeq = std::vector; + +// Sdc and dependent state. +class Mode : public StaState +{ +public: + Mode(const std::string &name, + size_t mode_index, + StaState *sta); + virtual ~Mode(); + virtual void copyState(const StaState *sta); + void clear(); + const std::string &name() const { return name_; } + size_t modeIndex() const { return mode_index_; } + const SceneSeq &scenes() const { return scenes_; } + const SceneSet sceneSet() const; + void addScene(Scene *scene); + void removeScene(Scene *scene); + Sdc *sdc() { return sdc_; } + Sdc *sdc() const { return sdc_; } + Sim *sim() { return sim_; } + Sim *sim() const { return sim_; } + ClkNetwork *clkNetwork() { return clk_network_; } + ClkNetwork *clkNetwork() const { return clk_network_; } + Genclks *genclks() { return genclks_; } + Genclks *genclks() const { return genclks_; } + PathGroups *pathGroups() { return path_groups_; } + PathGroups *pathGroups() const { return path_groups_; } + PathGroupSeq pathGroups(const PathEnd *path_end) const; + PathGroups *makePathGroups(int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float min_slack, + float max_slack, + StdStringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold, + bool unconstrained_paths); + void deletePathGroups(); + +private: + std::string name_; + size_t mode_index_; + SceneSeq scenes_; + Sdc *sdc_; + Sim *sim_; + ClkNetwork *clk_network_; + Genclks *genclks_; + PathGroups *path_groups_; +}; + +} // namespace diff --git a/include/sta/Mutex.hh b/include/sta/Mutex.hh index 9a96a6ad8..943596ed5 100644 --- a/include/sta/Mutex.hh +++ b/include/sta/Mutex.hh @@ -29,6 +29,6 @@ namespace sta { // Hide a bit of the std verbosity. -typedef std::lock_guard LockGuard; +using LockGuard = std::lock_guard; } // namespace diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 00566c10b..5e88b04c3 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -25,8 +25,8 @@ #pragma once #include +#include -#include "Map.hh" #include "StringUtil.hh" #include "LibertyClass.hh" #include "VertexId.hh" @@ -39,12 +39,12 @@ class Report; class PatternMatch; class PinVisitor; -typedef Map LibertyLibraryMap; +using LibertyLibraryMap = std::map; // Link network function returns top level instance. // Return nullptr if link fails. -typedef std::function LinkNetworkFunc; -typedef Map NetDrvrPinsMap; +using LinkNetworkFunc = std::function; +using NetDrvrPinsMap = std::map; // The Network class defines the network API used by sta. // The interface to a network implementation is constructed by @@ -101,8 +101,8 @@ public: // linking is not necessary because the network has already been expanded. // Return true if successful. virtual bool linkNetwork(const char *top_cell_name, - bool make_black_boxes, - Report *report) = 0; + bool make_black_boxes, + Report *report) = 0; virtual bool isLinked() const; virtual bool isEditable() const { return false; } @@ -117,14 +117,14 @@ public: // Find liberty library by filename. virtual LibertyLibrary *findLibertyFilename(const char *filename); virtual Cell *findCell(const Library *library, - const char *name) const = 0; + const char *name) const = 0; // Search the design (non-liberty) libraries for cells matching pattern. virtual CellSeq findCellsMatching(const Library *library, const PatternMatch *pattern) const = 0; // Search liberty libraries for cell name. virtual LibertyCell *findLibertyCell(const char *name) const; virtual LibertyLibrary *makeLibertyLibrary(const char *name, - const char *filename) = 0; + const char *filename) = 0; // Hook for network after reading liberty library. virtual void readLibertyAfter(LibertyLibrary *library); // First liberty library read is used to look up defaults. @@ -132,10 +132,10 @@ public: virtual LibertyLibrary *defaultLibertyLibrary() const; void setDefaultLibertyLibrary(LibertyLibrary *library); // Check liberty cells used by the network to make sure they exist - // for all the defined corners. - void checkNetworkLibertyCorners(); - // Check liberty cells to make sure they exist for all the defined corners. - void checkLibertyCorners(); + // for all the defined scenes. + void checkNetworkLibertyScenes(); + // Check liberty cells to make sure they exist for all the defined scenes. + void checkLibertyScenes(); //////////////////////////////////////////////////////////////// // Cell functions. @@ -156,7 +156,7 @@ public: virtual const AttributeMap &attributeMap(const Cell *cell) const = 0; // Name can be a simple, bundle, bus, or bus bit name. virtual Port *findPort(const Cell *cell, - const char *name) const = 0; + const char *name) const = 0; virtual PortSeq findPortsMatching(const Cell *cell, const PatternMatch *pattern) const; virtual bool isLeaf(const Cell *cell) const = 0; @@ -182,17 +182,17 @@ public: virtual const char *busName(const Port *port) const = 0; // Bus member, bus[subscript]. virtual Port *findBusBit(const Port *port, - int index) const = 0; + int index) const = 0; virtual int fromIndex(const Port *port) const = 0; virtual int toIndex(const Port *port) const = 0; // Predicate to determine if index is within bus range. // (toIndex > fromIndex) && fromIndex <= index <= toIndex // || (fromIndex > toIndex) && fromIndex >= index >= toIndex bool busIndexInRange(const Port *port, - int index); + int index); // Find Bundle/bus member by index. virtual Port *findMember(const Port *port, - int index) const = 0; + int index) const = 0; // Iterate over the bits of a bus port or members of a bundle. // from_index -> to_index virtual PortMemberIterator *memberIterator(const Port *port) const = 0; @@ -222,13 +222,13 @@ public: // Hierarchical path name. virtual const char *pathName(const Instance *instance) const; bool pathNameLess(const Instance *inst1, - const Instance *inst2) const; + const Instance *inst2) const; int pathNameCmp(const Instance *inst1, - const Instance *inst2) const; + const Instance *inst2) const; // Path from instance up to top level (last in the sequence). void path(const Instance *inst, - // Return value. - InstanceSeq &path) const; + // Return value. + InstanceSeq &path) const; virtual Cell *cell(const Instance *instance) const = 0; virtual const char *cellName(const Instance *instance) const; virtual LibertyLibrary *libertyLibrary(const Instance *instance) const; @@ -237,14 +237,14 @@ public: virtual bool isLeaf(const Instance *instance) const = 0; virtual bool isHierarchical(const Instance *instance) const; virtual Instance *findChild(const Instance *parent, - const char *name) const = 0; + const char *name) const = 0; virtual void findChildrenMatching(const Instance *parent, - const PatternMatch *pattern, + const PatternMatch *pattern, // Return value. InstanceSeq &matches) const; // Is inst inside of hier_inst? bool isInside(const Instance *inst, - const Instance *hier_inst) const; + const Instance *hier_inst) const; // Iterate over all of the leaf instances in the hierarchy // This iterator is not virtual because it can be written in terms of @@ -274,14 +274,14 @@ public: virtual ObjectId id(const Pin *pin) const = 0; virtual Pin *findPin(const char *path_name) const; virtual Pin *findPin(const Instance *instance, - const char *port_name) const = 0; + const char *port_name) const = 0; virtual Pin *findPin(const Instance *instance, - const Port *port) const; + const Port *port) const; virtual Pin *findPin(const Instance *instance, - const LibertyPort *port) const; + const LibertyPort *port) const; // Find pin relative to hierarchical instance. Pin *findPinRelative(const Instance *inst, - const char *path_name) const; + const char *path_name) const; // Default implementation uses linear search. virtual PinSeq findPinsMatching(const Instance *instance, const PatternMatch *pattern) const; @@ -293,9 +293,9 @@ public: // Path name is instance_name/port_name. virtual const char *pathName(const Pin *pin) const; bool pathNameLess(const Pin *pin1, - const Pin *pin2) const; + const Pin *pin2) const; int pathNameCmp(const Pin *pin1, - const Pin *pin2) const; + const Pin *pin2) const; virtual Port *port(const Pin *pin) const = 0; virtual LibertyPort *libertyPort(const Pin *pin) const; virtual Instance *instance(const Pin *pin) const = 0; @@ -303,14 +303,14 @@ public: virtual Term *term(const Pin *pin) const = 0; virtual PortDirection *direction(const Pin *pin) const = 0; virtual bool isLeaf(const Pin *pin) const; - bool isHierarchical(const Pin *pin) const; - bool isTopLevelPort(const Pin *pin) const; + [[nodiscard]] bool isHierarchical(const Pin *pin) const; + [[nodiscard]] bool isTopLevelPort(const Pin *pin) const; // Is pin inside the instance hier_pin is attached to? bool isInside(const Pin *pin, - const Pin *hier_pin) const; + const Pin *hier_pin) const; // Is pin inside of hier_inst? bool isInside(const Pin *pin, - const Instance *hier_inst) const; + const Instance *hier_inst) const; bool isDriver(const Pin *pin) const; bool isLoad(const Pin *pin) const; // Has register/latch rise/fall edges from pin. @@ -324,23 +324,23 @@ public: // hierarchical pins). virtual PinConnectedPinIterator *connectedPinIterator(const Pin *pin) const; virtual void visitConnectedPins(const Pin *pin, - PinVisitor &visitor) const; + PinVisitor &visitor) const; // Find driver pins for the net connected to pin. // Return value is owned by the network. virtual PinSet *drivers(const Pin *pin); virtual bool pinLess(const Pin *pin1, - const Pin *pin2) const; + const Pin *pin2) const; // Return the id of the pin graph vertex. virtual VertexId vertexId(const Pin *pin) const = 0; virtual void setVertexId(Pin *pin, - VertexId id) = 0; + VertexId id) = 0; // Return the physical X/Y coordinates of the pin. virtual void location(const Pin *pin, - // Return values. - double &x, - double &y, - bool &exists) const; + // Return values. + double &x, + double &y, + bool &exists) const; int pinCount(); int pinCount(Instance *inst); @@ -369,7 +369,7 @@ public: virtual NetSeq findNetsMatching(const Instance *context, const PatternMatch *pattern) const; virtual Net *findNet(const Instance *instance, - const char *net_name) const = 0; + const char *net_name) const = 0; // Traverse the hierarchy from instance down and find nets matching // pattern of the form instance_name/net_name. virtual NetSeq findNetsHierMatching(const Instance *instance, @@ -380,25 +380,25 @@ public: NetSeq &matches) const = 0; virtual const char *pathName(const Net *net) const; bool pathNameLess(const Net *net1, - const Net *net2) const; + const Net *net2) const; int pathNameCmp(const Net *net1, - const Net *net2) const; + const Net *net2) const; virtual Instance *instance(const Net *net) const = 0; // Is net inside of hier_inst? virtual bool isInside(const Net *net, - const Instance *hier_inst) const; + const Instance *hier_inst) const; // Is pin connected to net anywhere in the hierarchy? virtual bool isConnected(const Net *net, - const Pin *pin) const; + const Pin *pin) const; // Is net1 connected to net2 anywhere in the hierarchy? virtual bool isConnected(const Net *net1, - const Net *net2) const; + const Net *net2) const; virtual Net *highestNetAbove(Net *net) const; virtual const Net *highestConnectedNet(Net *net) const; virtual void connectedNets(Net *net, - NetSet *nets) const; + NetSet *nets) const; virtual void connectedNets(const Pin *pin, - NetSet *nets) const; + NetSet *nets) const; virtual bool isPower(const Net *net) const = 0; virtual bool isGround(const Net *net) const = 0; @@ -411,7 +411,7 @@ public: // hierarchical pins). virtual NetConnectedPinIterator *connectedPinIterator(const Net *net) const; virtual void visitConnectedPins(const Net *net, - PinVisitor &visitor) const; + PinVisitor &visitor) const; // Find driver pins for net. // Return value is owned by the network. virtual PinSet *drivers(const Net *net); @@ -427,14 +427,14 @@ public: // first and tail are both null if there are no dividers in path. // Caller must delete first and tail. void pathNameFirst(const char *path_name, - char *&first, - char *&tail) const; + char *&first, + char *&tail) const; // Parse path into head/last (last hierarchy divider separated token). // head and last are both null if there are no dividers in path. // Caller must delete head and last. void pathNameLast(const char *path_name, - char *&head, - char *&last) const; + char *&head, + char *&last) const; // Divider between instance names in a hierarchical path name. virtual char pathDivider() const { return divider_; } @@ -445,11 +445,11 @@ public: protected: Pin *findPinLinear(const Instance *instance, - const char *port_name) const; + const char *port_name) const; void findInstancesMatching1(const Instance *context, - size_t context_name_length, - const PatternMatch *pattern, - InstanceSeq &insts) const; + size_t context_name_length, + const PatternMatch *pattern, + InstanceSeq &insts) const; void findInstancesHierMatching1(const Instance *instance, const PatternMatch *pattern, InstanceSeq &matches) const; @@ -464,15 +464,15 @@ protected: // Return value. PinSeq &matches) const; bool isConnected(const Net *net, - const Pin *pin, - NetSet &nets) const; + const Pin *pin, + NetSet &nets) const; bool isConnected(const Net *net1, - const Net *net2, - NetSet &nets) const; + const Net *net2, + NetSet &nets) const; int hierarchyLevel(const Net *net) const; virtual void visitConnectedPins(const Net *net, - PinVisitor &visitor, - NetSet &visited_nets) const; + PinVisitor &visitor, + NetSet &visited_nets) const; // Default implementation uses linear search. virtual void findInstPinsMatching(const Instance *instance, const PatternMatch *pattern, @@ -484,7 +484,7 @@ protected: PinSeq &matches) const; // findNet using linear search. Net *findNetLinear(const Instance *instance, - const char *net_name) const; + const char *net_name) const; // findNetsMatching using linear search. NetSeq findNetsMatchingLinear(const Instance *instance, const PatternMatch *pattern) const; @@ -506,33 +506,33 @@ public: NetworkEdit(); virtual bool isEditable() const { return true; } virtual Instance *makeInstance(LibertyCell *cell, - const char *name, - Instance *parent) = 0; + const char *name, + Instance *parent) = 0; virtual void makePins(Instance *inst) = 0; virtual void replaceCell(Instance *inst, - Cell *cell) = 0; + Cell *cell) = 0; // Deleting instance also deletes instance pins. virtual void deleteInstance(Instance *inst) = 0; // Connect the port on an instance to a net. virtual Pin *connect(Instance *inst, - Port *port, - Net *net) = 0; + Port *port, + Net *net) = 0; virtual Pin *connect(Instance *inst, - LibertyPort *port, - Net *net) = 0; + LibertyPort *port, + Net *net) = 0; // makePin/connectPin replaced by connect. // deprecated 2018-09-28 virtual void connectPin(Pin *pin, - Net *net) __attribute__ ((deprecated)); + Net *net) __attribute__ ((deprecated)); // Disconnect pin from net. virtual void disconnectPin(Pin *pin) = 0; virtual void deletePin(Pin *pin) = 0; virtual Net *makeNet(const char *name, - Instance *parent) = 0; + Instance *parent) = 0; // Deleting net disconnects (but does not delete) net pins. virtual void deleteNet(Net *net) = 0; virtual void mergeInto(Net *net, - Net *into_net) = 0; + Net *into_net) = 0; virtual Net *mergedInto(Net *net) = 0; }; @@ -545,19 +545,19 @@ public: virtual void readNetlistBefore() = 0; virtual void setLinkFunc(LinkNetworkFunc link) = 0; virtual Library *makeLibrary(const char *name, - const char *filename) = 0; + const char *filename) = 0; virtual void deleteLibrary(Library *library) = 0; // Search the libraries in read order for a cell by name. virtual Cell *findAnyCell(const char *name) = 0; virtual Cell *makeCell(Library *library, - const char *name, - bool is_leaf, - const char *filename) = 0; + const char *name, + bool is_leaf, + const char *filename) = 0; virtual void deleteCell(Cell *cell) = 0; virtual void setName(Cell *cell, - const char *name) = 0; + const char *name) = 0; virtual void setIsLeaf(Cell *cell, - bool is_leaf) = 0; + bool is_leaf) = 0; virtual void setAttribute(Cell *cell, const std::string &key, const std::string &value) = 0; @@ -565,42 +565,42 @@ public: const std::string &key, const std::string &value) = 0; virtual Port *makePort(Cell *cell, - const char *name) = 0; + const char *name) = 0; virtual Port *makeBusPort(Cell *cell, - const char *name, - int from_index, - int to_index) = 0; + const char *name, + int from_index, + int to_index) = 0; virtual void groupBusPorts(Cell *cell, std::function port_msb_first) = 0; virtual Port *makeBundlePort(Cell *cell, - const char *name, - PortSeq *members) = 0; + const char *name, + PortSeq *members) = 0; virtual Instance *makeInstance(Cell *cell, - const char *name, - Instance *parent) = 0; + const char *name, + Instance *parent) = 0; virtual Pin *makePin(Instance *inst, - Port *port, - Net *net) = 0; + Port *port, + Net *net) = 0; virtual Term *makeTerm(Pin *pin, - Net *net) = 0; + Net *net) = 0; virtual void setDirection(Port *port, - PortDirection *dir) = 0; + PortDirection *dir) = 0; // Instance is the network view for cell. virtual void setCellNetworkView(Cell *cell, - Instance *inst) = 0; + Instance *inst) = 0; virtual Instance *cellNetworkView(Cell *cell) = 0; virtual void deleteCellNetworkViews() = 0; virtual void addConstantNet(Net *net, - LogicValue const_value) = 0; + LogicValue const_value) = 0; using NetworkEdit::makeInstance; }; Instance * linkReaderNetwork(Cell *top_cell, - bool make_black_boxes, - Report *report, - NetworkReader *network); + bool make_black_boxes, + Report *report, + NetworkReader *network); // Abstract class for Network::constantPinIterator(). class ConstantPinIterator @@ -610,7 +610,7 @@ public: virtual ~ConstantPinIterator() {} virtual bool hasNext() = 0; virtual void next(const Pin *&pin, - LogicValue &value) = 0; + LogicValue &value) = 0; }; // Implementation class for Network::constantPinIterator(). @@ -618,20 +618,20 @@ class NetworkConstantPinIterator : public ConstantPinIterator { public: NetworkConstantPinIterator(const Network *network, - NetSet &zero_nets, - NetSet &one_nets); - ~NetworkConstantPinIterator(); + NetSet &zero_nets, + NetSet &one_nets); + virtual ~NetworkConstantPinIterator() {} virtual bool hasNext(); virtual void next(const Pin *&pin, LogicValue &value); private: void findConstantPins(NetSet &nets, - PinSet &pins); + PinSet &pins); const Network *network_; PinSet constant_pins_[2]; LogicValue value_; - PinSet::Iterator *pin_iter_; + PinSet::iterator pin_iter_; }; // Abstract base class for visitDrvrLoadsThruHierPin visitor. @@ -641,7 +641,7 @@ public: HierPinThruVisitor() {} virtual ~HierPinThruVisitor() {} virtual void visit(const Pin *drvr, - const Pin *load) = 0; + const Pin *load) = 0; }; class PinVisitor @@ -655,10 +655,10 @@ class FindNetDrvrLoads : public PinVisitor { public: FindNetDrvrLoads(const Pin *drvr_pin, - PinSet &visited_drvrs, - PinSeq &loads, - PinSeq &drvrs, - const Network *network); + PinSet &visited_drvrs, + PinSeq &loads, + PinSeq &drvrs, + const Network *network); virtual void operator()(const Pin *pin); protected: @@ -672,12 +672,12 @@ protected: // Visit driver/loads pins through a hierarcial pin. void visitDrvrLoadsThruHierPin(const Pin *hpin, - const Network *network, - HierPinThruVisitor *visitor); + const Network *network, + HierPinThruVisitor *visitor); void visitDrvrLoadsThruNet(const Net *net, - const Network *network, - HierPinThruVisitor *visitor); + const Network *network, + HierPinThruVisitor *visitor); char logicValueString(LogicValue value); diff --git a/include/sta/NetworkClass.hh b/include/sta/NetworkClass.hh index 8241481e0..9e72fef6a 100644 --- a/include/sta/NetworkClass.hh +++ b/include/sta/NetworkClass.hh @@ -26,10 +26,11 @@ #include #include +#include #include +#include +#include -#include "Set.hh" -#include "Vector.hh" #include "Iterator.hh" namespace sta { @@ -49,30 +50,31 @@ class ConstantPinIterator; class ViewType; class LibertyLibrary; -typedef Iterator LibraryIterator; -typedef Iterator LibertyLibraryIterator; -typedef Vector CellSeq; -typedef Vector PortSeq; -typedef Iterator CellPortIterator; -typedef Iterator CellPortBitIterator; -typedef Iterator PortMemberIterator; - -typedef Vector PinSeq; -typedef Vector InstanceSeq; -typedef Vector NetSeq; -typedef std::vector ConstNetSeq; -typedef Iterator InstanceChildIterator; -typedef Iterator InstancePinIterator; -typedef Iterator InstanceNetIterator; -typedef Iterator LeafInstanceIterator; -typedef Iterator NetIterator; -typedef Iterator NetPinIterator; -typedef Iterator NetTermIterator; -typedef Iterator ConnectedPinIterator; -typedef ConnectedPinIterator NetConnectedPinIterator; -typedef ConnectedPinIterator PinConnectedPinIterator; -typedef uint32_t ObjectId; -typedef std::map AttributeMap; +using LibraryIterator = Iterator; +using LibertyLibraryIterator = Iterator; +using CellSeq = std::vector; +using PortSeq = std::vector; +using CellPortIterator = Iterator; +using CellPortBitIterator = Iterator; +using PortMemberIterator = Iterator; + +using PinSeq = std::vector; +using PinUnorderedSet = std::unordered_set; +using InstanceSeq = std::vector; +using NetSeq = std::vector; +using ConstNetSeq = std::vector; +using InstanceChildIterator = Iterator; +using InstancePinIterator = Iterator; +using InstanceNetIterator = Iterator; +using LeafInstanceIterator = Iterator; +using NetIterator = Iterator; +using NetPinIterator = Iterator; +using NetTermIterator = Iterator; +using ConnectedPinIterator = Iterator; +using NetConnectedPinIterator = ConnectedPinIterator; +using PinConnectedPinIterator = ConnectedPinIterator; +using ObjectId = uint32_t; +using AttributeMap = std::map; enum class LogicValue : unsigned { zero, one, unknown, rise, fall }; @@ -138,55 +140,37 @@ private: //////////////////////////////////////////////////////////////// -class CellSet : public Set +class CellSet : public std::set { public: CellSet(const Network *network); }; -class PortSet : public Set +class PortSet : public std::set { public: PortSet(const Network *network); }; -class InstanceSet : public Set +class InstanceSet : public std::set { public: InstanceSet(); InstanceSet(const Network *network); - static int compare(const InstanceSet *set1, - const InstanceSet *set2, - const Network *network); - static bool intersects(const InstanceSet *set1, - const InstanceSet *set2, - const Network *network); }; -class PinSet : public Set +class PinSet : public std::set { public: PinSet(); PinSet(const Network *network); - static int compare(const PinSet *set1, - const PinSet *set2, - const Network *network); - static bool intersects(const PinSet *set1, - const PinSet *set2, - const Network *network); }; -class NetSet : public Set +class NetSet : public std::set { public: NetSet(); NetSet(const Network *network); - static int compare(const NetSet *set1, - const NetSet *set2, - const Network *network); - static bool intersects(const NetSet *set1, - const NetSet *set2, - const Network *network); }; } // namespace diff --git a/include/sta/NetworkCmp.hh b/include/sta/NetworkCmp.hh index c343ccab8..dd7b871f5 100644 --- a/include/sta/NetworkCmp.hh +++ b/include/sta/NetworkCmp.hh @@ -34,9 +34,9 @@ namespace sta { class PortNameLess { public: - explicit PortNameLess(const Network *network); + PortNameLess(const Network *network); bool operator()(const Port *port1, - const Port *port2) const; + const Port *port2) const; private: const Network *network_; @@ -45,9 +45,9 @@ private: class PinPathNameLess { public: - explicit PinPathNameLess(const Network *network); + PinPathNameLess(const Network *network); bool operator()(const Pin *pin1, - const Pin *pin2) const; + const Pin *pin2) const; private: const Network *network_; @@ -56,9 +56,9 @@ private: class InstancePathNameLess { public: - explicit InstancePathNameLess(const Network *network); + InstancePathNameLess(const Network *network); bool operator()(const Instance *inst1, - const Instance *inst2) const; + const Instance *inst2) const; private: const Network *network_; @@ -67,9 +67,9 @@ private: class NetPathNameLess { public: - explicit NetPathNameLess(const Network *network); + NetPathNameLess(const Network *network); bool operator()(const Net *net1, - const Net *net2) const; + const Net *net2) const; private: const Network *network_; @@ -78,6 +78,9 @@ private: PinSeq sortByPathName(const PinSet *set, const Network *network); +PinSeq +sortByPathName(const PinUnorderedSet *set, + const Network *network); PortSeq sortByName(const PortSet *set, const Network *network); diff --git a/include/sta/ObjectId.hh b/include/sta/ObjectId.hh index 6f3b77036..b4c04ef74 100644 --- a/include/sta/ObjectId.hh +++ b/include/sta/ObjectId.hh @@ -29,11 +29,11 @@ namespace sta { // ObjectId is block index and object index within the block. -typedef uint32_t ObjectId; +using ObjectId = uint32_t; // Block index. -typedef uint32_t BlockIdx; +using BlockIdx = uint32_t; // Object index within a block. -typedef uint32_t ObjectIdx; +using ObjectIdx = uint32_t; static constexpr int object_id_bits = sizeof(ObjectId) * 8; static constexpr BlockIdx block_idx_null = 0; diff --git a/include/sta/ObjectTable.hh b/include/sta/ObjectTable.hh index 3c7521786..515d476a5 100644 --- a/include/sta/ObjectTable.hh +++ b/include/sta/ObjectTable.hh @@ -24,7 +24,9 @@ #pragma once -#include "Vector.hh" +#include + +#include "ContainerHelpers.hh" #include "Error.hh" #include "ObjectId.hh" @@ -66,12 +68,12 @@ public: private: void makeBlock(); void freePush(TYPE *object, - ObjectId id); + ObjectId id); size_t size_; // Object ID of next free object. ObjectId free_; - Vector*> blocks_; + std::vector*> blocks_; static constexpr ObjectId idx_mask_ = block_object_count - 1; }; @@ -85,7 +87,7 @@ ObjectTable::ObjectTable() : template ObjectTable::~ObjectTable() { - blocks_.deleteContents(); + deleteContents(blocks_); } template @@ -106,7 +108,7 @@ ObjectTable::make() template void ObjectTable::freePush(TYPE *object, - ObjectId id) + ObjectId id) { // Link free objects into a list linked by Object ID. ObjectId *free_next = reinterpret_cast(object); @@ -181,7 +183,7 @@ template void ObjectTable::clear() { - blocks_.deleteContentsClear(); + deleteContents(blocks_);; size_ = 0; } @@ -192,7 +194,7 @@ class TableBlock { public: TableBlock(BlockIdx block_idx, - ObjectTable *table); + ObjectTable *table); BlockIdx index() const { return block_idx_; } TYPE &ref(ObjectIdx idx) { return objects_[idx]; } TYPE *pointer(ObjectIdx idx) { return &objects_[idx]; } @@ -205,7 +207,7 @@ private: template TableBlock::TableBlock(BlockIdx block_idx, - ObjectTable *table) : + ObjectTable *table) : block_idx_(block_idx), table_(table) { diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index 948843f2c..bd2a0940d 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -37,46 +37,42 @@ namespace sta { class Wireload; -class Corner; +class Scene; -typedef std::complex ComplexFloat; -typedef Vector ComplexFloatSeq; -typedef std::vector ParasiticNodeSeq; -typedef std::vector ParasiticResistorSeq; -typedef std::vector ParasiticCapacitorSeq; -typedef std::map ParasiticNodeResistorMap; -typedef std::map ParasiticNodeCapacitorMap; +using ComplexFloat = std::complex; +using ComplexFloatSeq = std::vector; +using ParasiticNodeSeq = std::vector; +using ParasiticResistorSeq = std::vector; +using ParasiticCapacitorSeq = std::vector; +using ParasiticNodeResistorMap = std::map; +using ParasiticNodeCapacitorMap = std::map; // Parasitics API. -// All parasitic parameters can have multiple values, each corresponding -// to an analysis point. -// Parasitic annotation for a pin or net may exist for one analysis point -// and not another. class Parasitics : public StaState { public: Parasitics(StaState *sta); virtual ~Parasitics() {} + virtual const std::string &name() const = 0; + virtual const std::string &filename() const = 0; virtual bool haveParasitics() = 0; + // Clear all state. virtual void clear() = 0; // Delete all parasitics. virtual void deleteParasitics() = 0; // Delete all parasitics on net at analysis point. - virtual void deleteParasitics(const Net *net, - const ParasiticAnalysisPt *ap) = 0; + virtual void deleteParasitics(const Net *net) = 0; // Delete all parasitics on pin at analysis point. - virtual void deleteParasitics(const Pin *pin, - const ParasiticAnalysisPt *ap) = 0; - virtual void deleteReducedParasitics(const Net *net, - const ParasiticAnalysisPt *ap) = 0; + virtual void deleteParasitics(const Pin *pin) = 0; + virtual void deleteReducedParasitics(const Net *net) = 0; virtual void deleteDrvrReducedParasitics(const Pin *drvr_pin) = 0; virtual bool isReducedParasiticNetwork(const Parasitic *parasitic) const = 0; // Flag this parasitic as reduced from a parasitic network. virtual void setIsReducedParasiticNetwork(Parasitic *parasitic, - bool is_reduced) = 0; + bool is_reduced) = 0; // Capacitance value of parasitic object. virtual float capacitance(const Parasitic *parasitic) const = 0; @@ -87,89 +83,83 @@ public: // capacitor on the driver pin. virtual bool isPiElmore(const Parasitic *parasitic) const = 0; virtual Parasitic *findPiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap) const = 0; + const RiseFall *rf, + const MinMax *min_max) const = 0; virtual Parasitic *makePiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, - float rpi, - float c1) = 0; + const RiseFall *rf, + const MinMax *min_max, + float c2, + float rpi, + float c1) = 0; //////////////////////////////////////////////////////////////// // Pi models are common to PiElmore and PiPoleResidue. virtual bool isPiModel(const Parasitic *parasitic) const = 0; virtual void piModel(const Parasitic *parasitic, - float &c2, - float &rpi, - float &c1) const = 0; + float &c2, + float &rpi, + float &c1) const = 0; // Set PI model parameters. virtual void setPiModel(Parasitic *parasitic, - float c2, - float rpi, - float c1) = 0; + float c2, + float rpi, + float c1) = 0; //////////////////////////////////////////////////////////////// // Elmore driver to load delay. // Common to LumpedElmore and PiElmore parasitics. virtual void findElmore(const Parasitic *parasitic, - const Pin *load_pin, - float &elmore, - bool &exists) const = 0; + const Pin *load_pin, + float &elmore, + bool &exists) const = 0; // Set load elmore delay. virtual void setElmore(Parasitic *parasitic, - const Pin *load_pin, - float elmore) = 0; + const Pin *load_pin, + float elmore) = 0; //////////////////////////////////////////////////////////////// // Pi model driver load with pole/residue interconnect model to load pins. virtual bool isPiPoleResidue(const Parasitic* parasitic) const = 0; virtual Parasitic *findPiPoleResidue(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap) const=0; + const RiseFall *rf, + const MinMax *min_max) const = 0; virtual Parasitic *makePiPoleResidue(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, - float rpi, - float c1) = 0; + const RiseFall *rf, + const MinMax *min_max, + float c2, + float rpi, + float c1) = 0; virtual Parasitic *findPoleResidue(const Parasitic *parasitic, - const Pin *load_pin) const = 0; + const Pin *load_pin) const = 0; // Make pole/residue model for load_pin. virtual void setPoleResidue(Parasitic *parasitic, - const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues) = 0; + const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) = 0; virtual bool isPoleResidue(const Parasitic* parasitic) const = 0; // Return the number of poles and residues in a pole/residue parasitic. virtual size_t poleResidueCount(const Parasitic *parasitic) const = 0; // Find the pole_index'th pole/residue in a pole/residue parasitic. virtual void poleResidue(const Parasitic *parasitic, - int pole_index, - ComplexFloat &pole, - ComplexFloat &residue) const = 0; + int pole_index, + ComplexFloat &pole, + ComplexFloat &residue) const = 0; //////////////////////////////////////////////////////////////// // Parasitic Network (detailed parasitics). // This api assumes that parasitic networks are not rise/fall // dependent because they do not include pin capacitances. virtual bool isParasiticNetwork(const Parasitic *parasitic) const = 0; - virtual Parasitic *findParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) const = 0; - virtual Parasitic *findParasiticNetwork(const Pin *pin, - const ParasiticAnalysisPt *ap) const = 0; + virtual Parasitic *findParasiticNetwork(const Net *net) = 0; + virtual Parasitic *findParasiticNetwork(const Pin *pin) = 0; virtual Parasitic *makeParasiticNetwork(const Net *net, - bool includes_pin_caps, - const ParasiticAnalysisPt *ap) = 0; + bool includes_pin_caps) = 0; virtual ParasiticNodeSeq nodes(const Parasitic *parasitic) const = 0; virtual void report(const Parasitic *parasitic) const; virtual const Net *net(const Parasitic *parasitic) const = 0; virtual ParasiticResistorSeq resistors(const Parasitic *parasitic) const = 0; virtual ParasiticCapacitorSeq capacitors(const Parasitic *parasitic) const = 0; - // Delete parasitic network if it exists. - virtual void deleteParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) = 0; - virtual void deleteParasiticNetworks(const Net *net) = 0; + virtual void deleteParasiticNetwork(const Net *net) = 0; // True if the parasitic network caps include pin capacitances. virtual bool includesPinCaps(const Parasitic *parasitic) const = 0; // Parasitic network component builders. @@ -179,22 +169,22 @@ public: const Network *network) const = 0; // Make a subnode of the parasitic network net. virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, - const Net *net, - int id, + const Net *net, + int id, const Network *network) = 0; // Find the parasitic node connected to pin. virtual ParasiticNode *findParasiticNode(const Parasitic *parasitic, const Pin *pin) const = 0; // deprecated 2024-02-27 virtual ParasiticNode *findNode(const Parasitic *parasitic, - const Pin *pin) const __attribute__ ((deprecated)); + const Pin *pin) const __attribute__ ((deprecated)); // Make a subnode of the parasitic network net connected to pin. virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, - const Pin *pin, + const Pin *pin, const Network *network) = 0; // Increment the grounded capacitance on node. virtual void incrCap(ParasiticNode *node, - float cap) = 0; + float cap) = 0; virtual const char *name(const ParasiticNode *node) const = 0; virtual const Pin *pin(const ParasiticNode *node) const = 0; virtual const Net *net(const ParasiticNode *node, @@ -218,10 +208,10 @@ public: ParasiticNode *node) const; virtual void makeResistor(Parasitic *parasitic, - size_t id, - float res, + size_t id, + float res, ParasiticNode *node1, - ParasiticNode *node2) = 0; + ParasiticNode *node2) = 0; virtual size_t id(const ParasiticResistor *resistor) const = 0; virtual float value(const ParasiticResistor *resistor) const = 0; virtual ParasiticNode *node1(const ParasiticResistor *resistor) const = 0; @@ -248,17 +238,15 @@ public: Parasitic *reduceToPiElmore(const Parasitic *parasitic, const Pin *drvr_pin, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); // Reduce parasitic network to pi and 2nd order pole/residue models // for drvr_pin. Parasitic *reduceToPiPoleResidue2(const Parasitic *parasitic, const Pin *drvr_pin, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); // Estimate parasitic as pi elmore using wireload model. Parasitic *estimatePiElmore(const Pin *drvr_pin, @@ -266,67 +254,49 @@ public: const Wireload *wireload, float fanout, float net_pin_cap, - const Corner *corner, + const Scene *scene, const MinMax *min_max); Parasitic *makeWireloadNetwork(const Pin *drvr_pin, - const Wireload *wireload, - float fanout, - const MinMax *min_max, - const ParasiticAnalysisPt *ap); + const Wireload *wireload, + float fanout, + const Scene *scene, + const MinMax *min_max); // Network edit before/after methods. - virtual void disconnectPinBefore(const Pin *pin, - const Network *network) = 0; + virtual void disconnectPinBefore(const Pin *pin) = 0; virtual void deletePinBefore(const Pin *pin) = 0; virtual void loadPinCapacitanceChanged(const Pin *pin) = 0; + float couplingCapFactor() const { return coupling_cap_factor_; } + void setCouplingCapFactor(float factor); protected: void makeWireloadNetworkWorst(Parasitic *parasitic, - const Pin *drvr_pin, + const Pin *drvr_pin, const Net *net, - float wireload_cap, - float wireload_res, - float fanout); + float wireload_cap, + float wireload_res, + float fanout); void makeWireloadNetworkBest(Parasitic *parasitic, - const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout); + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout); void makeWireloadNetworkBalanced(Parasitic *parasitic, - const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout); + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout); const Net *findParasiticNet(const Pin *pin) const; -}; -// Managed by the Corner class. -class ParasiticAnalysisPt -{ -public: - ParasiticAnalysisPt(const char *name, - int index, - int index_max); - const char *name() const { return name_.c_str(); } - int index() const { return index_; } - int indexMax() const { return index_max_; } - // Coupling capacitor factor used by all reduction functions. - float couplingCapFactor() const { return coupling_cap_factor_; } - void setCouplingCapFactor(float factor); - -private: - std::string name_; - int index_; - int index_max_; float coupling_cap_factor_; }; class ParasiticNodeLess { public: + ParasiticNodeLess(); ParasiticNodeLess(const Parasitics *parasitics, const Network *network); - ParasiticNodeLess(const ParasiticNodeLess &less); bool operator()(const ParasiticNode *node1, const ParasiticNode *node2) const; private: diff --git a/include/sta/ParasiticsClass.hh b/include/sta/ParasiticsClass.hh index 2d35781d5..02a7e2e4d 100644 --- a/include/sta/ParasiticsClass.hh +++ b/include/sta/ParasiticsClass.hh @@ -29,7 +29,6 @@ namespace sta { class Parasitics; class Parasitic; class ParasiticNode; -class ParasiticAnalysisPt; class ParasiticResistor; class ParasiticCapacitor; diff --git a/include/sta/ParseBus.hh b/include/sta/ParseBus.hh index a7058efd8..a33daf017 100644 --- a/include/sta/ParseBus.hh +++ b/include/sta/ParseBus.hh @@ -31,9 +31,9 @@ namespace sta { // Return true if name is a bus. bool isBusName(const char *name, - const char brkt_left, - const char brkt_right, - char escape); + const char brkt_left, + const char brkt_right, + char escape); // Parse name as a bus. // signal @@ -44,23 +44,23 @@ isBusName(const char *name, // Caller must delete returned bus_name string. void parseBusName(const char *name, - const char brkt_left, - const char brkt_right, - char escape, - // Return values. - bool &is_bus, + const char brkt_left, + const char brkt_right, + char escape, + // Return values. + bool &is_bus, std::string &bus_name, - int &index); + int &index); // Allow multiple different left/right bus brackets. void parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, - char escape, - // Return values. - bool &is_bus, - std::string &bus_name, - int &index); + const char *brkts_left, + const char *brkts_right, + char escape, + // Return values. + bool &is_bus, + std::string &bus_name, + int &index); // Parse a bus range, such as BUS[4:0]. // bus_name is set to null if name is not a range. @@ -96,8 +96,8 @@ parseBusName(const char *name, // Insert escapes before ch1 and ch2 in token. std::string escapeChars(const char *token, - const char ch1, - const char ch2, - const char escape); + const char ch1, + const char ch2, + const char escape); } // namespace diff --git a/include/sta/Path.hh b/include/sta/Path.hh index 09763f730..bf938249e 100644 --- a/include/sta/Path.hh +++ b/include/sta/Path.hh @@ -35,8 +35,6 @@ namespace sta { -class DcalcAnalysisPt; - class Path { public: @@ -60,7 +58,6 @@ public: TimingArc *prev_arc, bool is_enum, const StaState *sta); - ~Path(); std::string to_string(const StaState *sta) const; bool isNull() const; // prev_path null @@ -86,6 +83,9 @@ public: VertexId vertexId(const StaState *sta) const; Pin *pin(const StaState *sta) const; Tag *tag(const StaState *sta) const; + Scene *scene(const StaState *sta) const; + Mode *mode(const StaState *sta) const; + Sdc *sdc(const StaState *sta) const; TagIndex tagIndex(const StaState *sta) const; void setTag(Tag *tag); size_t pathIndex(const StaState *sta) const; @@ -96,9 +96,8 @@ public: const RiseFall *transition(const StaState *sta) const; int rfIndex(const StaState *sta) const; const MinMax *minMax(const StaState *sta) const; - PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; PathAPIndex pathAnalysisPtIndex(const StaState *sta) const; - DcalcAnalysisPt *dcalcAnalysisPt(const StaState *sta) const; + DcalcAPIndex dcalcAnalysisPtIndex(const StaState *sta) const; Arrival &arrival() { return arrival_; } const Arrival &arrival() const { return arrival_; } void setArrival(Arrival arrival); @@ -121,6 +120,8 @@ public: void setIsEnum(bool is_enum); void checkPrevPath(const StaState *sta) const; + const MinMax *tgtClkMinMax(const StaState *sta) const; + static Path *vertexPath(const Path *path, const StaState *sta); static Path *vertexPath(const Path &path, @@ -130,34 +131,34 @@ public: const StaState *sta); static bool less(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); static int cmp(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Compare all path attributes (vertex, transition, tag, analysis point). static bool equal(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Compare pin name and transition and source clock edge. static int cmpPinTrClk(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Compare source clock edge. static int cmpClk(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Compare vertex, transition, path ap and tag without crpr clk pin. static int cmpNoCrpr(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); // Search back on each path until finding a difference. static int cmpAll(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); static bool lessAll(const Path *path1, - const Path *path2, - const StaState *sta); + const Path *path2, + const StaState *sta); protected: Path *prev_path_; @@ -176,9 +177,9 @@ protected: class PathLess { public: - explicit PathLess(const StaState *sta); + PathLess(const StaState *sta); bool operator()(const Path *path1, - const Path *path2) const; + const Path *path2) const; protected: const StaState *sta_; @@ -190,22 +191,16 @@ class VertexPathIterator : public Iterator public: // Iterate over all vertex paths. VertexPathIterator(Vertex *vertex, - const StaState *sta); - // Iterate over vertex paths with the same transition and - // analysis pt but different tags. + const StaState *sta); VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap, - const StaState *sta); + const Scene *scene, + const MinMax *min_max, + const RiseFall *rf, + const StaState *sta); // Iterate over vertex paths with the same transition and // analysis pt min/max but different tags. - VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max, - const StaState *sta); VertexPathIterator(Vertex *vertex, const RiseFall *rf, - const PathAnalysisPt *path_ap, const MinMax *min_max, const StaState *sta); virtual ~VertexPathIterator(); @@ -216,10 +211,10 @@ private: void findNext(); const Search *search_; - bool filtered_; - const RiseFall *rf_; - const PathAnalysisPt *path_ap_; + const Scene *scene_; const MinMax *min_max_; + const RiseFall *rf_; + bool filtered_; Path *paths_; size_t path_count_; size_t path_index_; diff --git a/include/sta/PathAnalysisPt.hh b/include/sta/PathAnalysisPt.hh deleted file mode 100644 index 6d6a7e943..000000000 --- a/include/sta/PathAnalysisPt.hh +++ /dev/null @@ -1,69 +0,0 @@ -// 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 - -#include "Iterator.hh" -#include "MinMax.hh" -#include "SdcClass.hh" -#include "SearchClass.hh" - -namespace sta { - -class MinMax; -class DcalcAnalysisPt; -class Corner; - -class PathAnalysisPt -{ -public: - PathAnalysisPt(Corner *corner, - PathAPIndex index, - const MinMax *path_min_max, - DcalcAnalysisPt *dcalc_ap); - std::string to_string() const; - Corner *corner() const { return corner_; } - PathAPIndex index() const { return index_; } - const MinMax *pathMinMax() const { return path_min_max_; } - // Converging path arrival merging. - const MinMax *mergeMinMax() const { return path_min_max_; } - // Path analysis point for timing check target clock arrivals. - PathAnalysisPt *tgtClkAnalysisPt() const { return tgt_clk_ap_; } - void setTgtClkAnalysisPt(PathAnalysisPt *path_ap); - DcalcAnalysisPt *dcalcAnalysisPt() const { return dcalc_ap_; } - PathAnalysisPt *insertionAnalysisPt(const EarlyLate *early_late) const; - void setInsertionAnalysisPt(const EarlyLate *early_late, PathAnalysisPt *ap); - -private: - Corner *corner_; - PathAPIndex index_; - const MinMax *path_min_max_; - PathAnalysisPt *tgt_clk_ap_; - PathAnalysisPt *insertion_aps_[EarlyLate::index_count]; - DcalcAnalysisPt *dcalc_ap_; -}; - -} // namespace diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index c61eb16ae..4f974f3fa 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -59,13 +59,13 @@ class ReportPath; class PathEnd { public: - enum Type { unconstrained, - check, - data_check, - latch_check, - output_delay, - gated_clk, - path_delay + enum class Type { unconstrained, + check, + data_check, + latch_check, + output_delay, + gated_clk, + path_delay }; virtual PathEnd *copy() const = 0; @@ -80,8 +80,6 @@ public: const EarlyLate *pathEarlyLate(const StaState *sta) const; virtual const EarlyLate *clkEarlyLate(const StaState *sta) const; const RiseFall *transition(const StaState *sta) const; - PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; - PathAPIndex pathIndex(const StaState *sta) const; virtual void reportShort(const ReportPath *report) const = 0; virtual void reportFull(const ReportPath *report) const = 0; PathGroup *pathGroup() const { return path_group_; } @@ -89,17 +87,17 @@ public: // Predicates for PathEnd type. // Default methods overridden by respective types. - virtual bool isUnconstrained() const { return false; } - virtual bool isCheck() const { return false; } - virtual bool isDataCheck() const { return false; } - virtual bool isLatchCheck() const { return false; } - virtual bool isOutputDelay() const { return false; } - virtual bool isGatedClock() const { return false; } - virtual bool isPathDelay() const { return false; } + [[nodiscard]] virtual bool isUnconstrained() const { return false; } + [[nodiscard]] virtual bool isCheck() const { return false; } + [[nodiscard]] virtual bool isDataCheck() const { return false; } + [[nodiscard]] virtual bool isLatchCheck() const { return false; } + [[nodiscard]] virtual bool isOutputDelay() const { return false; } + [[nodiscard]] virtual bool isGatedClock() const { return false; } + [[nodiscard]] virtual bool isPathDelay() const { return false; } virtual Type type() const = 0; virtual const char *typeName() const = 0; virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + const StaState *sta) const; virtual Arrival dataArrivalTime(const StaState *sta) const; // Arrival time with source clock offset. Arrival dataArrivalTimeOffset(const StaState *sta) const; @@ -154,20 +152,20 @@ public: virtual bool ignoreClkLatency(const StaState * /* sta */) const { return false; } static bool less(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + const StaState *sta); static int cmp(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + const StaState *sta); static int cmpSlack(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + const StaState *sta); static int cmpArrival(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + const StaState *sta); static int cmpNoCrpr(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta); + const PathEnd *path_end2, + const StaState *sta); // Helper common to multiple PathEnd classes and used // externally. @@ -177,43 +175,43 @@ public: const TimingRole *check_role, const StaState *sta); static void checkTgtClkDelay(const Path *tgt_clk_path, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - // Return values. - Delay &insertion, - Delay &latency); + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta, + // Return values. + Delay &insertion, + Delay &latency); static float checkClkUncertainty(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const Path *tgt_clk_path, - const TimingRole *check_role, - const StaState *sta); + const ClockEdge *tgt_clk_edge, + const Path *tgt_clk_path, + const TimingRole *check_role, + const Sdc *sdc); // Non inter-clock uncertainty. static float checkTgtClkUncertainty(const Path *tgt_clk_path, const ClockEdge *tgt_clk_edge, const TimingRole *check_role, const StaState *sta); static float checkSetupMcpAdjustment(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const MultiCyclePath *mcp, - int default_cycles, - Sdc *sdc); + const ClockEdge *tgt_clk_edge, + const MultiCyclePath *mcp, + int default_cycles, + Sdc *sdc); protected: PathEnd(Path *path); static void checkInterClkUncertainty(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - float &uncertainty, - bool &exists); + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const Sdc *sdc, + float &uncertainty, + bool &exists); static float outputDelayMargin(OutputDelay *output_delay, - const Path *path, - const StaState *sta); + const Path *path, + const StaState *sta); static float pathDelaySrcClkOffset(const Path *path, - PathDelay *path_delay, - Arrival src_clk_arrival, - const StaState *sta); + PathDelay *path_delay, + Arrival src_clk_arrival, + const StaState *sta); static bool ignoreClkLatency(const Path *path, PathDelay *path_delay, const StaState *sta); @@ -224,7 +222,7 @@ protected: class PathEndUnconstrained : public PathEnd { public: - explicit PathEndUnconstrained(Path *path); + PathEndUnconstrained(Path *path); virtual Type type() const; virtual const char *typeName() const; virtual PathEnd *copy() const; @@ -262,21 +260,21 @@ public: virtual Slack slack(const StaState *sta) const; virtual Slack slackNoCrpr(const StaState *sta) const; virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + const StaState *sta) const; virtual void setPath(Path *path); protected: PathEndClkConstrained(Path *path, - Path *clk_path); + Path *clk_path); PathEndClkConstrained(Path *path, - Path *clk_path, - Crpr crpr, - bool crpr_valid); + Path *clk_path, + Crpr crpr, + bool crpr_valid); float sourceClkOffset(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta) const; + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta) const; // Internal to slackNoCrpr. virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const; virtual Required requiredTimeNoCrpr(const StaState *sta) const; @@ -292,24 +290,24 @@ public: virtual MultiCyclePath *multiCyclePath() const { return mcp_; } virtual float targetClkMcpAdjustment(const StaState *sta) const; virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + const StaState *sta) const; protected: PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp); + Path *clk_path, + MultiCyclePath *mcp); PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); + Path *clk_path, + MultiCyclePath *mcp, + Crpr crpr, + bool crpr_valid); float checkMcpAdjustment(const Path *path, - const ClockEdge *tgt_clk_edge, - const StaState *sta) const; + const ClockEdge *tgt_clk_edge, + const StaState *sta) const; void findHoldMcps(const ClockEdge *tgt_clk_edge, - const MultiCyclePath *&setup_mcp, - const MultiCyclePath *&hold_mcp, - const StaState *sta) const; + const MultiCyclePath *&setup_mcp, + const MultiCyclePath *&hold_mcp, + const StaState *sta) const; MultiCyclePath *mcp_; }; @@ -319,11 +317,11 @@ class PathEndCheck : public PathEndClkConstrainedMcp { public: PathEndCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - MultiCyclePath *mcp, - const StaState *sta); + TimingArc *check_arc, + Edge *check_edge, + Path *clk_path, + MultiCyclePath *mcp, + const StaState *sta); virtual PathEnd *copy() const; virtual Type type() const; virtual const char *typeName() const; @@ -335,17 +333,17 @@ public: virtual const TimingRole *checkRole(const StaState *sta) const; virtual TimingArc *checkArc() const { return check_arc_; } virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + const StaState *sta) const; 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); + 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; @@ -358,12 +356,12 @@ class PathEndLatchCheck : public PathEndCheck { public: PathEndLatchCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *disable_path, - MultiCyclePath *mcp, - PathDelay *path_delay, - const StaState *sta); + TimingArc *check_arc, + Edge *check_edge, + Path *disable_path, + MultiCyclePath *mcp, + PathDelay *path_delay, + const StaState *sta); virtual Type type() const; virtual const char *typeName() const; virtual float sourceClkOffset(const StaState *sta) const; @@ -382,36 +380,36 @@ public: virtual float targetClkOffset(const StaState *sta) const; Arrival targetClkWidth(const StaState *sta) const; virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + const StaState *sta) const; void latchRequired(const StaState *sta, - // Return values. - Required &required, - Delay &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const; + // Return values. + Required &required, + Delay &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const; void latchBorrowInfo(const StaState *sta, - // Return values. - float &nom_pulse_width, - Delay &open_latency, - Delay &latency_diff, - float &open_uncertainty, - Crpr &open_crpr, - Crpr &crpr_diff, - Delay &max_borrow, - bool &borrow_limit_exists) const; + // Return values. + float &nom_pulse_width, + Delay &open_latency, + Delay &latency_diff, + float &open_uncertainty, + Crpr &open_crpr, + Crpr &crpr_diff, + Delay &max_borrow, + bool &borrow_limit_exists) const; 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); + 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_; @@ -427,10 +425,10 @@ class PathEndOutputDelay : public PathEndClkConstrainedMcp { public: PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - const StaState *sta); + Path *path, + Path *clk_path, + MultiCyclePath *mcp, + const StaState *sta); virtual PathEnd *copy() const; virtual Type type() const; virtual const char *typeName() const; @@ -445,24 +443,24 @@ public: virtual Delay targetClkInsertionDelay(const StaState *sta) const; virtual Crpr crpr(const StaState *sta) const; virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + const StaState *sta) const; protected: PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid); + 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; + const TimingRole *check_role, + const StaState *sta) const; void tgtClkDelay(const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - // Return values. - Arrival &insertion, - Arrival &latency) const; + const TimingRole *check_role, + const StaState *sta, + // Return values. + Arrival &insertion, + Arrival &latency) const; OutputDelay *output_delay_; }; @@ -472,11 +470,11 @@ class PathEndGatedClock : public PathEndClkConstrainedMcp { public: PathEndGatedClock(Path *gating_ref, - Path *clk_path, - const TimingRole *check_role, - MultiCyclePath *mcp, - ArcDelay margin, - const StaState *sta); + Path *clk_path, + const TimingRole *check_role, + MultiCyclePath *mcp, + ArcDelay margin, + const StaState *sta); virtual PathEnd *copy() const; virtual Type type() const; virtual const char *typeName() const; @@ -486,16 +484,16 @@ public: virtual ArcDelay margin(const StaState *) const { return margin_; } virtual const TimingRole *checkRole(const StaState *sta) const; virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + 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); + Path *clk_path, + const TimingRole *check_role, + MultiCyclePath *mcp, + ArcDelay margin, + Crpr crpr, + bool crpr_valid); const TimingRole *check_role_; ArcDelay margin_; @@ -505,10 +503,10 @@ class PathEndDataCheck : public PathEndClkConstrainedMcp { public: PathEndDataCheck(DataCheck *check, - Path *data_path, - Path *data_clk_path, - MultiCyclePath *mcp, - const StaState *sta); + Path *data_path, + Path *data_clk_path, + MultiCyclePath *mcp, + const StaState *sta); virtual PathEnd *copy() const; virtual Type type() const; virtual const char *typeName() const; @@ -519,17 +517,17 @@ public: virtual const TimingRole *checkRole(const StaState *sta) const; virtual ArcDelay margin(const StaState *sta) const; virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + const StaState *sta) const; 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 *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; @@ -549,20 +547,20 @@ class PathEndPathDelay : public PathEndClkConstrained public: // Vanilla path delay. PathEndPathDelay(PathDelay *path_delay, - Path *path, - const StaState *sta); + Path *path, + const StaState *sta); // Path delay to timing check. PathEndPathDelay(PathDelay *path_delay, - Path *path, - Path *clk_path, - TimingArc *check_arc, - Edge *check_edge, - const StaState *sta); + Path *path, + Path *clk_path, + TimingArc *check_arc, + Edge *check_edge, + const StaState *sta); // Path delay to output with set_output_delay. PathEndPathDelay(PathDelay *path_delay, - Path *path, - OutputDelay *output_delay, - const StaState *sta); + Path *path, + OutputDelay *output_delay, + const StaState *sta); virtual PathEnd *copy() const; virtual Type type() const; virtual const char *typeName() const; @@ -581,20 +579,20 @@ public: virtual TimingArc *checkArc() const { return check_arc_; } virtual Required requiredTime(const StaState *sta) const; virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; - bool hasOutputDelay() const { return output_delay_ != nullptr; } + const StaState *sta) const; + [[nodiscard]] bool hasOutputDelay() const { return output_delay_ != nullptr; } 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); + 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_; @@ -613,9 +611,9 @@ protected: class PathEndLess { public: - explicit PathEndLess(const StaState *sta); + PathEndLess(const StaState *sta); bool operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const; + const PathEnd *path_end2) const; protected: const StaState *sta_; @@ -625,9 +623,9 @@ protected: class PathEndSlackLess { public: - explicit PathEndSlackLess(const StaState *sta); + PathEndSlackLess(const StaState *sta); bool operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const; + const PathEnd *path_end2) const; protected: const StaState *sta_; @@ -636,9 +634,9 @@ protected: class PathEndNoCrprLess { public: - explicit PathEndNoCrprLess(const StaState *sta); + PathEndNoCrprLess(const StaState *sta); bool operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const; + const PathEnd *path_end2) const; protected: const StaState *sta_; diff --git a/include/sta/PathExpanded.hh b/include/sta/PathExpanded.hh index 784409bf8..0269f6856 100644 --- a/include/sta/PathExpanded.hh +++ b/include/sta/PathExpanded.hh @@ -38,13 +38,13 @@ public: PathExpanded(const StaState *sta); // Expand path for lookup by index. PathExpanded(const Path *path, - const StaState *sta); + const StaState *sta); PathExpanded(const Path *path, - // Expand generated clk source paths. - bool expand_genclks, - const StaState *sta); + // Expand generated clk source paths. + bool expand_genclks, + const StaState *sta); void expand(const Path *path, - bool expand_genclks); + bool expand_genclks); size_t size() const { return paths_.size(); } // path(0) is the startpoint. // path(size()-1) is the endpoint. @@ -59,9 +59,9 @@ public: size_t startIndex() const; const Path *clkPath() const; void latchPaths(// Return values. - const Path *&d_path, - const Path *&q_path, - Edge *&d_q_edge) const; + const Path *&d_path, + const Path *&q_path, + Edge *&d_q_edge) const; protected: void expandGenclk(const Path *clk_path); diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index cc84f9ea2..a3bd6ee3b 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -24,10 +24,11 @@ #pragma once +#include +#include +#include #include -#include "Map.hh" -#include "Vector.hh" #include "SdcClass.hh" #include "StaState.hh" #include "SearchClass.hh" @@ -37,11 +38,11 @@ namespace sta { class MinMax; class PathEndVisitor; -typedef PathEndSeq::Iterator PathGroupIterator; -typedef Map PathGroupClkMap; -typedef Map PathGroupNamedMap; -typedef std::vector PathGroupSeq; -typedef std::vector StdStringSeq; +using PathGroupIterator = PathEndSeq::iterator; +using PathGroupClkMap = std::map; +using PathGroupNamedMap = std::map; +using PathGroupSeq = std::vector; +using StdStringSeq = std::vector; // A collection of PathEnds grouped and sorted for reporting. class PathGroup @@ -50,22 +51,22 @@ public: ~PathGroup(); // Path group that compares compare slacks. static PathGroup *makePathGroupArrival(const char *name, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - const MinMax *min_max, - const StaState *sta); + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + const MinMax *min_max, + const StaState *sta); // Path group that compares arrival time, sorted by min_max. static PathGroup *makePathGroupSlack(const char *name, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float min_slack, - float max_slack, - const StaState *sta); - const char *name() const { return name_; } + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float min_slack, + float max_slack, + const StaState *sta); + const char *name() const { return name_.c_str(); } const MinMax *minMax() const { return min_max_;} const PathEndSeq &pathEnds() const { return path_ends_; } void insert(PathEnd *path_end); @@ -75,27 +76,27 @@ public: bool saveable(PathEnd *path_end); bool enumMinSlackUnderMin(PathEnd *path_end); int maxPaths() const { return group_path_count_; } - PathGroupIterator *iterator(); + PathEndSeq &pathEnds() { return path_ends_; } // This does NOT delete the path ends. void clear(); static size_t group_path_count_max; protected: PathGroup(const char *name, - size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - float min_slack, - float max_slack, - bool cmp_slack, - const MinMax *min_max, - const StaState *sta); + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float min_slack, + float max_slack, + bool cmp_slack, + const MinMax *min_max, + const StaState *sta); void ensureSortedMaxPaths(); void prune(); void sort(); - const char *name_; + std::string name_; size_t group_path_count_; size_t endpoint_path_count_; bool unique_pins_; @@ -114,35 +115,37 @@ class PathGroups : public StaState { public: PathGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold, - bool unconstrained, - const StaState *sta); + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StdStringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold, + bool unconstrained, + const Mode *mode); ~PathGroups(); - // Use corner nullptr to make PathEnds for all corners. + // Use scene nullptr to make PathEnds for all scenes. // The PathEnds in the vector are owned by the PathGroups. - PathEndSeq makePathEnds(ExceptionTo *to, - bool unconstrained_paths, - const Corner *corner, - const MinMaxAll *min_max, - bool sort_by_slack); + void makePathEnds(ExceptionTo *to, + const SceneSeq &scenes, + const MinMaxAll *min_max, + bool sort_by_slack, + bool unconstrained_paths, + // Return value. + PathEndSeq &path_ends); PathGroup *findPathGroup(const char *name, - const MinMax *min_max) const; + const MinMax *min_max) const; PathGroup *findPathGroup(const Clock *clock, - const MinMax *min_max) const; + const MinMax *min_max) const; PathGroupSeq pathGroups(const PathEnd *path_end) const; static StdStringSeq pathGroupNames(const PathEnd *path_end, - const StaState *sta); + const StaState *sta); static const char *asyncPathGroupName() { return async_group_name_; } static const char *pathDelayGroupName() { return path_delay_group_name_; } static const char *gatedClkGroupName() { return gated_clk_group_name_; } @@ -150,46 +153,50 @@ public: protected: void makeGroupPathEnds(ExceptionTo *to, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - const Corner *corner, - const MinMaxAll *min_max); + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + const SceneSeq &scenes, + const MinMaxAll *min_max); void makeGroupPathEnds(ExceptionTo *to, - const Corner *corner, - const MinMaxAll *min_max, - PathEndVisitor *visitor); - void makeGroupPathEnds(VertexSet *endpoints, - const Corner *corner, - const MinMaxAll *min_max, - PathEndVisitor *visitor); + const SceneSeq &scenes, + const MinMaxAll *min_max, + PathEndVisitor *visitor); + void makeGroupPathEnds(VertexSet &endpoints, + const SceneSeq &scenes, + const MinMaxAll *min_max, + PathEndVisitor *visitor); void enumPathEnds(PathGroup *group, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - bool cmp_slack); + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + bool cmp_slack); - void pushGroupPathEnds(PathEndSeq &path_ends); + void pushEnds(PathEndSeq &path_ends); void pushUnconstrainedPathEnds(PathEndSeq &path_ends, - const MinMaxAll *min_max); + const MinMaxAll *min_max); void makeGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup_hold, - bool async, - bool gated_clk, - bool unconstrained, - const MinMax *min_max); + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StdStringSet &group_names, + bool setup_hold, + bool async, + bool gated_clk, + bool unconstrained, + const MinMax *min_max); bool reportGroup(const char *group_name, - PathGroupNameSet *group_names) const; + StdStringSet &group_names) const; + static GroupPath *groupPathTo(const PathEnd *path_end, + const StaState *sta); + StdStringSeq pathGroupNames(); + const Mode *mode_; int group_path_count_; int endpoint_path_count_; bool unique_pins_; diff --git a/include/sta/PatternMatch.hh b/include/sta/PatternMatch.hh index 3531b8a41..03c308aa9 100644 --- a/include/sta/PatternMatch.hh +++ b/include/sta/PatternMatch.hh @@ -29,8 +29,8 @@ #include "Error.hh" // Don't require all of tcl.h. -typedef struct Tcl_RegExp_ *Tcl_RegExp; -typedef struct Tcl_Interp Tcl_Interp; +using Tcl_RegExp = struct Tcl_RegExp_ *; +using Tcl_Interp = struct Tcl_Interp; namespace sta { @@ -46,15 +46,15 @@ public: // If nocase is true, ignore case in the pattern. // Tcl_Interp is optional for reporting regexp compile errors. PatternMatch(const char *pattern, - bool is_regexp, - bool nocase, - Tcl_Interp *interp); + bool is_regexp, + bool nocase, + Tcl_Interp *interp); // Use unix glob style matching. PatternMatch(const char *pattern); PatternMatch(const char *pattern, - const PatternMatch *inherit_from); + const PatternMatch *inherit_from); PatternMatch(const std::string &pattern, - const PatternMatch *inherit_from); + const PatternMatch *inherit_from); bool match(const char *str) const; bool match(const std::string &str) const; bool matchNoCase(const char *str) const; @@ -78,7 +78,7 @@ private: class RegexpCompileError : public Exception { public: - explicit RegexpCompileError(const char *pattern); + RegexpCompileError(const char *pattern); virtual ~RegexpCompileError() noexcept {} virtual const char *what() const noexcept; @@ -91,11 +91,11 @@ private: // '?' matches any character bool patternMatch(const char *pattern, - const char *str); + const char *str); bool patternMatchNoCase(const char *pattern, - const char *str, - bool nocase); + const char *str, + bool nocase); // Predicate to find out if there are wildcard characters in the pattern. bool patternWildcards(const char *pattern); diff --git a/include/sta/PinPair.hh b/include/sta/PinPair.hh index 7d3f27116..bdc511352 100644 --- a/include/sta/PinPair.hh +++ b/include/sta/PinPair.hh @@ -24,27 +24,28 @@ #pragma once +#include + #include "Hash.hh" -#include "Set.hh" #include "NetworkClass.hh" namespace sta { -typedef std::pair PinPair; +using PinPair = std::pair; class PinPairLess { public: PinPairLess(const Network *network); bool operator()(const PinPair &pair1, - const PinPair &pair2) const; + const PinPair &pair2) const; private: const Network *network_; }; -class PinPairSet : public Set +class PinPairSet : public std::set { public: PinPairSet(const Network *network); @@ -64,7 +65,7 @@ class PinPairEqual { public: bool operator()(const PinPair &pair1, - const PinPair &pair2) const; + const PinPair &pair2) const; }; } // namespace diff --git a/include/sta/PortDelay.hh b/include/sta/PortDelay.hh index 4220bf7cc..9f36e396b 100644 --- a/include/sta/PortDelay.hh +++ b/include/sta/PortDelay.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "RiseFallMinMax.hh" #include "SdcClass.hh" @@ -31,7 +33,7 @@ namespace sta { class PortDelay; -typedef Vector PortDelaySeq; +using PortDelaySeq = std::vector; // set_input_delay arrival, set_output_delay departure class PortDelay @@ -52,7 +54,7 @@ public: protected: PortDelay(const Pin *pin, - const ClockEdge *clk_edge, + const ClockEdge *clk_edge, const Network *network); const Pin *pin_; @@ -71,9 +73,9 @@ public: protected: InputDelay(const Pin *pin, - const ClockEdge *clk_edge, - int index, - const Network *network); + const ClockEdge *clk_edge, + int index, + const Network *network); private: int index_; @@ -87,8 +89,8 @@ public: protected: OutputDelay(const Pin *pin, - const ClockEdge *clk_edge, - const Network *network); + const ClockEdge *clk_edge, + const Network *network); private: friend class Sdc; @@ -98,9 +100,9 @@ private: class PortDelayLess { public: - explicit PortDelayLess(const Network *network); + PortDelayLess(const Network *network); bool operator()(const PortDelay *delay1, - const PortDelay *delay2) const; + const PortDelay *delay2) const; private: const Network *network_; diff --git a/include/sta/PortDirection.hh b/include/sta/PortDirection.hh index 186914a9b..b61f48f6c 100644 --- a/include/sta/PortDirection.hh +++ b/include/sta/PortDirection.hh @@ -64,7 +64,7 @@ public: private: PortDirection(const char *name, - int index); + int index); const char *name_; int index_; diff --git a/include/sta/PortExtCap.hh b/include/sta/PortExtCap.hh index 2bad21c9a..716dcbb90 100644 --- a/include/sta/PortExtCap.hh +++ b/include/sta/PortExtCap.hh @@ -32,39 +32,42 @@ namespace sta { -typedef MinMaxIntValues FanoutValues; +using FanoutValues = MinMaxIntValues; // Port external pin and wire capacitance (set_load -pin_load -wire_load). class PortExtCap { public: - PortExtCap(const Port *port); + PortExtCap(); const Port *port() { return port_; } void pinCap(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists); - RiseFallMinMax *pinCap() { return &pin_cap_; } - void setPinCap(float cap, - const RiseFall *rf, - const MinMax *min_max); + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const; + const RiseFallMinMax *pinCap() const { return &pin_cap_; } + void setPinCap(const Port *port, + float cap, + const RiseFall *rf, + const MinMax *min_max); void wireCap(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists); - RiseFallMinMax *wireCap() { return &wire_cap_; } - void setWireCap(float cap, - const RiseFall *rf, - const MinMax *min_max); - void setFanout(int fanout, - const MinMax *min_max); + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const; + const RiseFallMinMax *wireCap() const { return &wire_cap_; } + void setWireCap(const Port *port, + float cap, + const RiseFall *rf, + const MinMax *min_max); + void setFanout(const Port *port, + int fanout, + const MinMax *min_max); void fanout(const MinMax *min_max, - // Return values. - int &fanout, - bool &exists); - FanoutValues *fanout() { return &fanout_; } + // Return values. + int &fanout, + bool &exists) const; + const FanoutValues *fanout() const { return &fanout_; } private: const Port *port_; diff --git a/include/sta/PowerClass.hh b/include/sta/PowerClass.hh index ed4a4860a..5b1d36beb 100644 --- a/include/sta/PowerClass.hh +++ b/include/sta/PowerClass.hh @@ -24,6 +24,9 @@ #pragma once +#include +#include + namespace sta { class Power; @@ -47,8 +50,8 @@ class PwrActivity public: PwrActivity(); PwrActivity(float density, - float duty, - PwrActivityOrigin origin); + float duty, + PwrActivityOrigin origin); void init(); float density() const { return density_; } void setDensity(float density); @@ -58,8 +61,8 @@ public: void setOrigin(PwrActivityOrigin origin); const char *originName() const; void set(float density, - float duty, - PwrActivityOrigin origin); + float duty, + PwrActivityOrigin origin); bool isSet() const; private: @@ -92,4 +95,7 @@ private: float leakage_; }; +using InstPower = std::pair; +using InstPowers = std::vector; + } // namespace diff --git a/include/sta/Property.hh b/include/sta/Property.hh index 9ddafeacb..4bfa86ca3 100644 --- a/include/sta/Property.hh +++ b/include/sta/Property.hh @@ -26,6 +26,7 @@ #include #include +#include #include #include "LibertyClass.hh" @@ -47,7 +48,7 @@ class PropertyRegistry { public: typedef std::function PropertyHandler; - void defineProperty(const std::string &property, + void defineProperty(std::string_view property, PropertyHandler handler); PropertyValue getProperty(TYPE object, const std::string &property, @@ -65,84 +66,76 @@ public: virtual ~Properties() {} PropertyValue getProperty(const Library *lib, - const std::string property); + const std::string &property); PropertyValue getProperty(const LibertyLibrary *lib, - const std::string property); + const std::string &property); PropertyValue getProperty(const Cell *cell, - const std::string property); + const std::string &property); PropertyValue getProperty(const LibertyCell *cell, - const std::string property); + const std::string &property); PropertyValue getProperty(const Port *port, - const std::string property); + const std::string &property); PropertyValue getProperty(const LibertyPort *port, - const std::string property); + const std::string &property); PropertyValue getProperty(const Instance *inst, - const std::string property); + const std::string &property); PropertyValue getProperty(const Pin *pin, - const std::string property); + const std::string &property); PropertyValue getProperty(const Net *net, - const std::string property); + const std::string &property); PropertyValue getProperty(Edge *edge, - const std::string property); + const std::string &property); PropertyValue getProperty(const Clock *clk, - const std::string property); + const std::string &property); PropertyValue getProperty(PathEnd *end, - const std::string property); + const std::string &property); PropertyValue getProperty(Path *path, - const std::string property); + const std::string &property); PropertyValue getProperty(TimingArcSet *arc_set, - const std::string property); + const std::string &property); // Define handler for external property. // properties->defineProperty("foo", // [] (const Instance *, Sta *) -> PropertyValue { // return PropertyValue("bar"); // }); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); - void defineProperty(std::string &property, + void defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler); protected: PropertyValue portSlew(const Port *port, - const MinMax *min_max); - PropertyValue portSlew(const Port *port, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max); PropertyValue portSlack(const Port *port, - const MinMax *min_max); - PropertyValue portSlack(const Port *port, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max); PropertyValue pinArrival(const Pin *pin, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max); PropertyValue pinSlack(const Pin *pin, + const RiseFallBoth *rf, const MinMax *min_max); - PropertyValue pinSlack(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max); - PropertyValue pinSlew(const Pin *pin, - const MinMax *min_max); PropertyValue pinSlew(const Pin *pin, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max); PropertyValue delayPropertyValue(Delay delay); @@ -179,11 +172,11 @@ protected: class PropertyValue { public: - enum Type { type_none, type_string, type_float, type_bool, - type_library, type_cell, type_port, - type_liberty_library, type_liberty_cell, type_liberty_port, - type_instance, type_pin, type_pins, type_net, - type_clk, type_clks, type_paths, type_pwr_activity }; + enum class Type { none, string, float_, bool_, + library, cell, port, + liberty_library, liberty_cell, liberty_port, + instance, pin, pins, net, + clk, clks, paths, pwr_activity }; PropertyValue(); PropertyValue(const char *value); PropertyValue(std::string &value); @@ -210,7 +203,7 @@ public: // Copy constructor. PropertyValue(const PropertyValue &props); // Move constructor. - PropertyValue(PropertyValue &&props); + PropertyValue(PropertyValue &&props) noexcept; ~PropertyValue(); Type type() const { return type_; } const Unit *unit() const { return unit_; } @@ -237,7 +230,7 @@ public: // Copy assignment. PropertyValue &operator=(const PropertyValue &); // Move assignment. - PropertyValue &operator=(PropertyValue &&); + PropertyValue &operator=(PropertyValue &&) noexcept; private: Type type_; diff --git a/include/sta/Report.hh b/include/sta/Report.hh index 7d2aebe91..9e8aed56b 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -129,7 +129,7 @@ public: // Suppress message by id. void suppressMsgId(int id); void unsuppressMsgId(int id); - bool isSuppressed(int id); + [[nodiscard]] bool isSuppressed(int id); protected: // All sta print functions have an implicit return printed by this function. diff --git a/include/sta/ReportTcl.hh b/include/sta/ReportTcl.hh index 37ba416d3..fe3391fc2 100644 --- a/include/sta/ReportTcl.hh +++ b/include/sta/ReportTcl.hh @@ -62,8 +62,8 @@ protected: private: Tcl_ChannelType *makeEncapChannelType(Tcl_Channel channel, - char *channel_name, - Tcl_DriverOutputProc output_proc); + char *channel_name, + Tcl_DriverOutputProc output_proc); size_t printTcl(Tcl_Channel channel, const char *buffer, size_t length); diff --git a/include/sta/RiseFallMinMax.hh b/include/sta/RiseFallMinMax.hh index 1aacbdd40..81511ec51 100644 --- a/include/sta/RiseFallMinMax.hh +++ b/include/sta/RiseFallMinMax.hh @@ -35,41 +35,41 @@ class RiseFallMinMax public: RiseFallMinMax(); RiseFallMinMax(const RiseFallMinMax *rfmm); - explicit RiseFallMinMax(float init_value); + RiseFallMinMax(float init_value); float value(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; float value(const MinMax *min_max) const; void value(const RiseFall *rf, - const MinMax *min_max, - float &value, - bool &exists) const; + const MinMax *min_max, + float &value, + bool &exists) const; bool hasValue() const; void maxValue(// Return values - float &max_value, - bool &exists) const; - bool empty() const; + float &max_value, + bool &exists) const; + [[nodiscard]] bool empty() const; bool hasValue(const RiseFall *rf, - const MinMax *min_max) const; + const MinMax *min_max) const; void setValue(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float value); + const MinMaxAll *min_max, + float value); void setValue(const RiseFallBoth *rf, - const MinMax *min_max, - float value); + const MinMax *min_max, + float value); void setValue(const RiseFall *rf, - const MinMax *min_max, float value); + const MinMax *min_max, float value); void setValue(float value); void mergeValue(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float value); + const MinMaxAll *min_max, + float value); void mergeValue(const RiseFall *rf, - const MinMax *min_max, - float value); + const MinMax *min_max, + float value); void setValues(RiseFallMinMax *values); void removeValue(const RiseFallBoth *rf, - const MinMax *min_max); + const MinMax *min_max); void removeValue(const RiseFallBoth *rf, - const MinMaxAll *min_max); + const MinMaxAll *min_max); // Merge all values of rfmm. void mergeWith(RiseFallMinMax *rfmm); void clear(); @@ -77,8 +77,8 @@ public: bool isOneValue() const; bool isOneValue(float &value) const; bool isOneValue(const MinMax *min_max, - // Return values. - float &value) const; + // Return values. + float &value) const; private: float values_[RiseFall::index_count][MinMax::index_count]; diff --git a/include/sta/MakeConcreteParasitics.hh b/include/sta/RiseFallMinMaxDelay.hh similarity index 65% rename from include/sta/MakeConcreteParasitics.hh rename to include/sta/RiseFallMinMaxDelay.hh index 9bba835f4..5d102fdbe 100644 --- a/include/sta/MakeConcreteParasitics.hh +++ b/include/sta/RiseFallMinMaxDelay.hh @@ -24,9 +24,29 @@ #pragma once +#include "MinMax.hh" +#include "Transition.hh" +#include "Delay.hh" + namespace sta { -Parasitics * -makeConcreteParasitics(StaState *sta); +class RiseFallMinMaxDelay +{ +public: + RiseFallMinMaxDelay(); + [[nodiscard]] bool empty() const; + void value(const RiseFall *rf, + const MinMax *min_max, + Delay &value, + bool &exists) const; + void mergeValue(const RiseFall *rf, + const MinMax *min_max, + Delay &value, + const StaState *sta); + +private: + Delay values_[RiseFall::index_count][MinMax::index_count]; + bool exists_[RiseFall::index_count][MinMax::index_count]; +}; } // namespace diff --git a/include/sta/RiseFallValues.hh b/include/sta/RiseFallValues.hh index e3ba0ca70..a02c979e8 100644 --- a/include/sta/RiseFallValues.hh +++ b/include/sta/RiseFallValues.hh @@ -33,10 +33,10 @@ class RiseFallValues { public: RiseFallValues(); - explicit RiseFallValues(float init_value); + RiseFallValues(float init_value); float value(const RiseFall *rf) const; void value(const RiseFall *rf, - float &value, bool &exists) const; + float &value, bool &exists) const; bool hasValue(const RiseFall *rf) const; void setValue(const RiseFallBoth *rf, float value); void setValue(const RiseFall *rf, float value); diff --git a/include/sta/Scene.hh b/include/sta/Scene.hh new file mode 100644 index 000000000..9b2e61a0b --- /dev/null +++ b/include/sta/Scene.hh @@ -0,0 +1,99 @@ +// 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 +#include +#include + +#include "StringSeq.hh" +#include "GraphClass.hh" +#include "SearchClass.hh" + +namespace sta { + +class Mode; +class Sdc; +class MinMax; +class Scene; +class LibertyLibrary; +class Parasitics; + +using SceneSet = std::set; +using SceneSeq = std::vector; +using LibertySeq = std::vector; +using ModeSeq = std::vector; +using ModeSet = std::set; + +class Scene +{ +public: + Scene(const std::string &name, + size_t index, + Mode *mode, + Parasitics *parasitics); + Scene(const std::string &name, + size_t index, + Mode *mode, + Parasitics *parasitics_min, + Parasitics *parasitics_max); + const std::string &name() const { return name_; } + size_t index() const { return index_; } + Mode *mode() { return mode_; } + Mode *mode() const { return mode_; } + void setMode(Mode *mode); + Sdc *sdc(); + Sdc *sdc() const; + Parasitics *parasitics(const MinMax *min_max) const; + void setParasitics(Parasitics *parasitics, + const MinMaxAll *min_max); + size_t pathIndex(const MinMax *min_max) const; + + DcalcAPIndex dcalcAnalysisPtIndex(const MinMax *min_max) const; + const MinMax *checkClkSlewMinMax(const MinMax *min_max) const; + // Slew index of timing check clock. + DcalcAPIndex checkClkSlewIndex(const MinMax *min_max) const; + + const LibertySeq &libertyLibraries(const MinMax *min_max) const; + int libertyIndex(const MinMax *min_max) const; + void addLiberty(LibertyLibrary *lib, + const MinMax *min_max); + + static SceneSet sceneSet(const SceneSeq &scenes); + static ModeSeq modes(const SceneSeq &scenes); + static ModeSet modeSet(const SceneSeq &scenes); + static ModeSeq modesSorted(const SceneSeq &scenes); + +protected: + std::string name_; + size_t index_; + Mode *mode_; + LibertySeq liberty_[MinMax::index_count]; + std::array parasitics_; + + friend class Scenes; +}; + +} // namespace diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index b14e10cc8..441db5d6d 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -24,12 +24,13 @@ #pragma once +#include +#include +#include #include #include "StringUtil.hh" #include "StringSet.hh" -#include "Map.hh" -#include "UnorderedMap.hh" #include "MinMax.hh" #include "StaState.hh" #include "NetworkClass.hh" @@ -41,6 +42,7 @@ #include "DataCheck.hh" #include "CycleAccting.hh" #include "ExceptionPath.hh" +#include "PinPair.hh" namespace sta { @@ -58,18 +60,18 @@ class PatternMatch; class FindNetCaps; class ClkHpinDisable; class FindClkHpinDisables; -class Corner; +class Scene; class ClockPinIterator; class ClockIterator; -typedef std::pair PinClockPair; +using PinClockPair = std::pair; class ClockInsertionkLess { public: ClockInsertionkLess(const Network *network); bool operator()(const ClockInsertion *insert1, - const ClockInsertion *insert2) const; + const ClockInsertion *insert2) const; private: const Network *network_; @@ -80,7 +82,7 @@ class ClockLatencyLess public: ClockLatencyLess(const Network *network); bool operator()(const ClockLatency *latency1, - const ClockLatency *latency2) const; + const ClockLatency *latency2) const; private: const Network *network_; @@ -93,7 +95,7 @@ class ClockPairLess { public: bool operator()(const ClockPair &pair1, - const ClockPair &pair2) const; + const ClockPair &pair2) const; }; class PinClockPairLess @@ -101,7 +103,7 @@ class PinClockPairLess public: PinClockPairLess(const Network *network); bool operator()(const PinClockPair &pin_clk1, - const PinClockPair &pin_clk2) const; + const PinClockPair &pin_clk2) const; protected: const Network *network_; @@ -112,7 +114,7 @@ class ClkHpinDisableLess public: ClkHpinDisableLess(const Network *network); bool operator()(const ClkHpinDisable *disable1, - const ClkHpinDisable *disable2) const; + const ClkHpinDisable *disable2) const; private: const Network *network_; @@ -130,81 +132,81 @@ private: bool subtract_pin_cap_[MinMax::index_count]; }; -typedef Map ClockNameMap; -typedef UnorderedMap ClockPinMap; -typedef Set InputDelaySet; -typedef Map InputDelaysPinMap; -typedef Set OutputDelaySet; -typedef Map OutputDelaysPinMap; -typedef UnorderedMap PinExceptionsMap; -typedef UnorderedMap ClockExceptionsMap; -typedef UnorderedMap InstanceExceptionsMap; -typedef UnorderedMap NetExceptionsMap; -typedef UnorderedMap EdgeExceptionsMap; -typedef Vector ExceptionThruSeq; -typedef Map InputDriveMap; -typedef Map> ExceptionPathPtHash; -typedef Set ClockLatencies; -typedef Map PinClockUncertaintyMap; -typedef Set InterClockUncertaintySet; -typedef Map ClockGatingCheckMap; -typedef Map InstanceClockGatingCheckMap; -typedef Map PinClockGatingCheckMap; -typedef Set ClockInsertions; -typedef Map PinLatchBorrowLimitMap; -typedef Map InstLatchBorrowLimitMap; -typedef Map ClockLatchBorrowLimitMap; -typedef Set DataCheckSet; -typedef Map DataChecksMap; -typedef Map NetResistanceMap; -typedef Map PortSlewLimitMap; -typedef Map PinSlewLimitMap; -typedef Map CellSlewLimitMap; -typedef Map CellCapLimitMap; -typedef Map PortCapLimitMap; -typedef Map PinCapLimitMap; -typedef Map PortFanoutLimitMap; -typedef Map CellFanoutLimitMap; -typedef Map PortExtCapMap; -typedef Map NetWireCapMap; -typedef Map PinWireCapMap; -typedef Map InstancePvtMap; -typedef Map EdgeClockLatencyMap; -typedef Map PinMinPulseWidthMap; -typedef Map ClockMinPulseWidthMap; -typedef Map InstMinPulseWidthMap; -typedef Map NetDeratingFactorsMap; -typedef Map InstDeratingFactorsMap; -typedef Map CellDeratingFactorsMap; -typedef Set ClockGroupsSet; -typedef Map ClockGroupsClkMap; -typedef Map ClockGroupsNameMap; -typedef Map ClockSenseMap; -typedef Set ClkHpinDisables; -typedef Set GroupPathSet; -typedef Map GroupPathMap; -typedef Set ClockPairSet; -typedef Map NetVoltageMap; +using ClockNameMap = std::map; +using ClockPinMap = std::unordered_map; +using InputDelaySet = std::set; +using InputDelaysPinMap = std::map; +using OutputDelaySet = std::set; +using OutputDelaysPinMap = std::map; +using PinExceptionsMap = std::unordered_map; +using ClockExceptionsMap = std::unordered_map; +using InstanceExceptionsMap = std::unordered_map; +using NetExceptionsMap = std::unordered_map; +using EdgeExceptionsMap = std::unordered_map; +using InputDriveMap = std::map; +using ExceptionPathPtHash = std::map; +using ClockLatencies = std::set; +using EdgeClockLatencyMap = std::map; +using PinClockUncertaintyMap = std::map; +using InterClockUncertaintySet = std::set; +using ClockGatingCheckMap = std::map; +using InstanceClockGatingCheckMap = std::map; +using PinClockGatingCheckMap = std::map; +using ClockInsertions = std::set; +using PinLatchBorrowLimitMap = std::map; +using InstLatchBorrowLimitMap = std::map; +using ClockLatchBorrowLimitMap = std::map; +using DataCheckSet = std::set; +using DataChecksMap = std::map; +using NetResistanceMap = std::map; +using PortSlewLimitMap = std::map; +using PinSlewLimitMap = std::map; +using CellSlewLimitMap = std::map; +using CellCapLimitMap = std::map; +using PortCapLimitMap = std::map; +using PinCapLimitMap = std::map; +using PortFanoutLimitMap = std::map; +using CellFanoutLimitMap = std::map; +using PortExtCapMap = std::map; +using NetWireCapMap = std::map; +using PinWireCapMap = std::map; +using InstancePvtMap = std::map; +using PinMinPulseWidthMap = std::map; +using ClockMinPulseWidthMap = std::map; +using InstMinPulseWidthMap = std::map; +using NetDeratingFactorsMap = std::map; +using InstDeratingFactorsMap = std::map; +using CellDeratingFactorsMap = std::map; +using ClockGroupsSet = std::set; +using ClockGroupsClkMap = std::map; +using ClockGroupsNameMap = std::map; +using ClockSenseMap = std::map; +using ClkHpinDisables = std::set; +using GroupPathSet = std::set; +using GroupPathMap = std::map; +using ClockPairSet = std::set; +using NetVoltageMap = std::map; void findLeafLoadPins(const Pin *pin, - const Network *network, - PinSet *leaf_pins); + const Network *network, + PinSet *leaf_pins); void findLeafDriverPins(const Pin *pin, - const Network *network, - PinSet *leaf_pins); + const Network *network, + PinSet *leaf_pins); class Sdc : public StaState { public: - Sdc(StaState *sta); + Sdc(Mode *mode, + StaState *sta); ~Sdc(); + Mode *mode() const { return mode_; } // Note that Search may reference a Filter exception removed by clear(). void clear(); - void makeCornersBefore(); - void makeCornersAfter(Corners *corners); + void makeSceneBefore(); // Return true if pin is referenced by any constraint. bool isConstrained(const Pin *pin) const; // Return true if inst is referenced by any constraint. @@ -219,161 +221,161 @@ public: void deleteInstanceBefore(const Instance *inst); // SWIG sdc interface. - PortSeq allInputs(bool no_clks); - PortSeq allOutputs(); - AnalysisType analysisType() { return analysis_type_; } + PortSeq allInputs(bool no_clks) const; + PortSeq allOutputs() const; + AnalysisType analysisType() const { return analysis_type_; } void setAnalysisType(AnalysisType analysis_type); void setOperatingConditions(OperatingConditions *op_cond, - const MinMaxAll *min_max); + const MinMaxAll *min_max); void setOperatingConditions(OperatingConditions *op_cond, - const MinMax *min_max); + const MinMax *min_max); void setTimingDerate(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate); // Delay type is always net for net derating. void setTimingDerate(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate); void setTimingDerate(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate); void setTimingDerate(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate); float timingDerateInstance(const Pin *pin, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late) const; + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late) const; float timingDerateNet(const Pin *pin, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late) const; + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late) const; void unsetTimingDerate(); static void swapDeratingFactors(Sdc *sdc1, Sdc *sdc2); void setInputSlew(const Port *port, const RiseFallBoth *rf, - const MinMaxAll *min_max, + const MinMaxAll *min_max, float slew); // Set the rise/fall drive resistance on design port. void setDriveResistance(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res); // Set the drive on design port using external cell timing arcs of // cell driven by from_slews between from_port and to_port. void 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 MinMaxAll *min_max); + const LibertyCell *cell, + const Port *port, + const LibertyPort *from_port, + float *from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max); void setLatchBorrowLimit(const Pin *pin, - float limit); + float limit); void setLatchBorrowLimit(const Instance *inst, - float limit); + float limit); void setLatchBorrowLimit(const Clock *clk, - float limit); + float limit); // Return the latch borrow limit respecting precedence if multiple // limits apply. void latchBorrowLimit(const Pin *data_pin, - const Pin *enable_pin, - const Clock *clk, - // Return values. - float &limit, - bool &exists); + const Pin *enable_pin, + const Clock *clk, + // Return values. + float &limit, + bool &exists); void setMinPulseWidth(const RiseFallBoth *rf, - float min_width); + float min_width); void setMinPulseWidth(const Pin *pin, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width); void setMinPulseWidth(const Instance *inst, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width); void setMinPulseWidth(const Clock *clk, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width); // Return min pulse with respecting precedence. void minPulseWidth(const Pin *pin, - const Clock *clk, - const RiseFall *hi_low, - float &min_width, - bool &exists) const; + const Clock *clk, + const RiseFall *hi_low, + float &min_width, + bool &exists) const; void setSlewLimit(Clock *clk, - const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew); + const RiseFallBoth *rf, + const PathClkOrData clk_data, + const MinMax *min_max, + float slew); bool haveClkSlewLimits() const; - void slewLimit(Clock *clk, - const RiseFall *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float &slew, - bool &exists); + void slewLimit(const Clock *clk, + const RiseFall *rf, + const PathClkOrData clk_data, + const MinMax *min_max, + float &slew, + bool &exists) const; void slewLimit(Port *port, - const MinMax *min_max, - float &slew, - bool &exists); + const MinMax *min_max, + float &slew, + bool &exists) const; void setSlewLimit(Port *port, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew); void slewLimit(Cell *cell, - const MinMax *min_max, - float &slew, - bool &exists); + const MinMax *min_max, + float &slew, + bool &exists) const; void setSlewLimit(Cell *cell, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew); void capacitanceLimit(Port *port, - const MinMax *min_max, - float &cap, - bool &exists); + const MinMax *min_max, + float &cap, + bool &exists) const; void capacitanceLimit(Pin *pin, - const MinMax *min_max, - float &cap, - bool &exists); + const MinMax *min_max, + float &cap, + bool &exists) const; void capacitanceLimit(Cell *cell, - const MinMax *min_max, - float &cap, - bool &exists); + const MinMax *min_max, + float &cap, + bool &exists) const; void setCapacitanceLimit(Port *port, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap); void setCapacitanceLimit(Pin *pin, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap); void setCapacitanceLimit(Cell *cell, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap); void fanoutLimit(Port *port, - const MinMax *min_max, - float &fanout, - bool &exists); + const MinMax *min_max, + float &fanout, + bool &exists) const; void setFanoutLimit(Port *port, - const MinMax *min_max, - float fanout); + const MinMax *min_max, + float fanout); void fanoutLimit(Cell *cell, - const MinMax *min_max, - float &fanout, - bool &exists); + const MinMax *min_max, + float &fanout, + bool &exists) const; void setFanoutLimit(Cell *cell, - const MinMax *min_max, - float fanout); + const MinMax *min_max, + float fanout); void setMaxArea(float area); float maxArea() const; Clock *makeClock(const char *name, @@ -407,79 +409,79 @@ public: void removePropagatedClock(Clock *clk); void setPropagatedClock(Pin *pin); void removePropagatedClock(Pin *pin); - bool isPropagatedClock(const Pin *pin); + bool isPropagatedClock(const Pin *pin) const; void setClockSlew(Clock *clk, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew); void removeClockSlew(Clock *clk); // Latency can be on a clk, pin, or clk/pin combination. void setClockLatency(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay); + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float delay); void removeClockLatency(const Clock *clk, - const Pin *pin); + const Pin *pin); ClockLatency *clockLatency(Edge *edge) const; bool hasClockLatency(const Pin *pin) const; void clockLatency(Edge *edge, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const; + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const; ClockLatencies *clockLatencies() { return &clk_latencies_; } const ClockLatencies *clockLatencies() const { return &clk_latencies_; } // Clock latency on pin with respect to clk. // This does NOT check for latency on clk (without pin). void clockLatency(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const; + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const; void clockLatency(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const; + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const; float clockLatency(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max) const; + const RiseFall *rf, + const MinMax *min_max) const; // Clock insertion delay (set_clk_latency -source). // Insertion delay can be on a clk, pin, or clk/pin combination. void setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay); + const Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay); void setClockInsertion(const Clock *clk, const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - float delay); + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + float delay); void removeClockInsertion(const Clock *clk, - const Pin *pin); + const Pin *pin); static void swapClockInsertions(Sdc *sdc1, Sdc *sdc2); bool hasClockInsertion(const Pin *pin) const; float clockInsertion(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late) const; + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late) const; // Respects precedence of pin/clk and set_input_delay on clk pin. void clockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - // Return values. - float &insertion, - bool &exists) const; + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, + bool &exists) const; const ClockInsertions &clockInsertions() const { return clk_insertions_; } // Clock uncertainty. void setClockUncertainty(Pin *pin, @@ -499,158 +501,151 @@ public: const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold); ClockGroups *makeClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment); + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + const char *comment); void makeClockGroup(ClockGroups *clk_groups, - ClockSet *clks); + 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); bool sameClockGroup(const Clock *clk1, - const Clock *clk2); + const Clock *clk2) const; // Clocks explicitly excluded by set_clock_group. bool sameClockGroupExplicit(const Clock *clk1, - const Clock *clk2); + const Clock *clk2); void setClockSense(PinSet *pins, - ClockSet *clks, - ClockSense sense); + ClockSet *clks, + ClockSense sense); bool clkStopPropagation(const Pin *pin, - const Clock *clk) const; + const Clock *clk) const; bool clkStopPropagation(const Clock *clk, - const Pin *from_pin, - const RiseFall *from_rf, - const Pin *to_pin, - const RiseFall *to_rf) const; + const Pin *from_pin, + const RiseFall *from_rf, + const Pin *to_pin, + const RiseFall *to_rf) const; void setClockGatingCheck(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin); + const SetupHold *setup_hold, + float margin); void setClockGatingCheck(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value); void setClockGatingCheck(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin); void setClockGatingCheck(const Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value); void setDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, - float margin); + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin); void removeDataCheck(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); DataCheckSet *dataChecksFrom(const Pin *from) const; DataCheckSet *dataChecksTo(const Pin *to) const; void 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); void removeInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max); void setOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_tr, - 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_tr, + const Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, float delay); void removeOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max); static void swapPortDelays(Sdc *sdc1, Sdc *sdc2); // Set port external pin load (set_load -pin_load port). void setPortExtPinCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float cap); + const RiseFall *rf, + const MinMax *min_max, + float cap); // Set port external wire load (set_load -wire port). void setPortExtWireCap(const Port *port, - bool subtract_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float cap); + const RiseFall *rf, + const MinMax *min_max, + float cap); static void swapPortExtCaps(Sdc *sdc1, Sdc *sdc2); // Remove all "set_load net" annotations. void removeNetLoadCaps(); void setNetWireCap(const Net *net, - bool subtract_pin_cap, - const Corner *corner, - const MinMax *min_max, - float wire_cap); + bool subtract_pin_cap, + const MinMax *min_max, + float wire_cap); bool hasNetWireCap(const Net *net) const; // True if driver pin net has wire capacitance. - bool drvrPinHasWireCap(const Pin *pin, - const Corner *corner); + bool drvrPinHasWireCap(const Pin *pin) const; // Net wire capacitance (set_load -wire net). void drvrPinWireCap(const Pin *drvr_pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists, + const MinMax *min_max, + // Return values. + float &cap, + bool &exists, bool &subtract_pin_cap) const; // Pin capacitance derated by operating conditions and instance pvt. float pinCapacitance(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max); + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) const; void setResistance(const Net *net, - const MinMaxAll *min_max, - float res); + const MinMaxAll *min_max, + float res); void resistance(const Net *net, - const MinMax *min_max, - float &res, - bool &exists); - NetResistanceMap &netResistances() { return net_res_map_; } + const MinMax *min_max, + float &res, + bool &exists) const; + const NetResistanceMap &netResistances() const { return net_res_map_; } void setPortExtFanout(const Port *port, - const Corner *corner, - const MinMax *min_max, - int fanout); + const MinMax *min_max, + int fanout); // set_disable_timing cell [-from] [-to] // Disable all edges thru cell if from/to are null. // Bus and bundle ports are NOT supported. void disable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to); void removeDisable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to); // set_disable_timing liberty port. // Bus and bundle ports are NOT supported. void disable(LibertyPort *port); @@ -663,11 +658,11 @@ public: // Disable all edges thru instance if from/to are null. // Bus and bundle ports are NOT supported. void disable(Instance *inst, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to); void removeDisable(Instance *inst, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to); // set_disable_timing pin void disable(const Pin *pin); void removeDisable(Pin *pin); @@ -679,127 +674,140 @@ public: void removeDisable(TimingArcSet *arc_set); // Disable a wire edge. From/to pins musts be on the same net. // There is no SDC equivalent to this. - void disable(Pin *from, Pin *to); - void removeDisable(Pin *from, Pin *to); - bool isDisabled(const Pin *pin) const; + void disableWire(const Pin *from, + const Pin *to); + void removeDisableWire(Pin *from, + Pin *to); + [[nodiscard]] bool isDisabledWire(const Pin *from, + const Pin *to) const; + [[nodiscard]] bool isDisabledConstraint(const Pin *pin) const; // Edge disabled by hierarchical pin disable or instance/cell port pair. // Disables do NOT apply to timing checks. // inst can be either the from_pin or to_pin instance because it // is only referenced when they are the same (non-wire edge). - bool isDisabled(const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const TimingRole *role) const; - bool isDisabled(Edge *edge); - bool isDisabled(TimingArcSet *arc_set) const; - DisabledCellPortsMap *disabledCellPorts(); + [[nodiscard]] bool isDisabled(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + const TimingRole *role) const; + [[nodiscard]] bool isDisabled(const Edge *edge) const; + [[nodiscard]] bool isDisabled(TimingArcSet *arc_set) const; + const DisabledCellPortsMap *disabledCellPorts() const; const DisabledInstancePortsMap *disabledInstancePorts() const; const PinSet *disabledPins() const { return &disabled_pins_; } const PortSet *disabledPorts() const { return &disabled_ports_; } const LibertyPortSet *disabledLibPorts() const { return &disabled_lib_ports_; } const EdgeSet *disabledEdges() const { return &disabled_edges_; } + [[nodiscard]] bool isDisabledConstraint(const Edge *edge) const; + void disableClockGatingCheck(Instance *inst); void disableClockGatingCheck(Pin *pin); void removeDisableClockGatingCheck(Instance *inst); void removeDisableClockGatingCheck(Pin *pin); - bool isDisableClockGatingCheck(const Pin *pin); - bool isDisableClockGatingCheck(const Instance *inst); + bool isDisableClockGatingCheck(const Pin *pin) const; + bool isDisableClockGatingCheck(const Instance *inst) const; // set_LogicValue::zero, set_LogicValue::one, set_logic_dc void setLogicValue(const Pin *pin, - LogicValue value); + LogicValue value); // set_case_analysis void setCaseAnalysis(const Pin *pin, - LogicValue value); + LogicValue value); void removeCaseAnalysis(const Pin *pin); void logicValue(const Pin *pin, - LogicValue &value, - bool &exists); + LogicValue &value, + bool &exists) const; void caseLogicValue(const Pin *pin, LogicValue &value, - bool &exists); + bool &exists) const; // Pin has set_case_analysis or set_logic constant value. - bool hasLogicValue(const Pin *pin); + bool hasLogicValue(const Pin *pin) const; + // The from/thrus/to arguments passed into the following functions // that make exceptions are owned by the constraints once they are // passed in. The constraint internals may change or delete them do // to exception merging. void makeFalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const char *comment); // Loop paths are false paths used to disable paths around // combinational loops when dynamic loop breaking is enabled. void makeLoopExceptions(); void makeLoopExceptions(GraphLoop *loop); void deleteLoopExceptions(); void makeMulticyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + const char *comment); void 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, - const char *comment); + float delay, + const char *comment); bool pathDelaysWithoutTo() const { return path_delays_without_to_; } // Delete matching false/multicycle/path_delay exceptions. // Caller owns from, thrus, to exception points (and must delete them). void resetPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max); void makeGroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment); - bool isGroupPathName(const char *group_name); - GroupPathMap &groupPaths() { return group_path_map_; } + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const char *comment); + bool isGroupPathName(const char *group_name) const; + const GroupPathMap &groupPaths() const { return group_path_map_; } void addException(ExceptionPath *exception); // The pin/clk/instance/net set arguments passed into the following // functions that make exception from/thru/to's are owned by the // constraints once they are passed in. ExceptionFrom *makeExceptionFrom(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_rf); + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_rf) const; bool isExceptionStartpoint(const Pin *pin) const; // Make an exception -through specification. ExceptionThru *makeExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf); - bool isExceptionEndpoint(const Pin *pin); + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf) const; + bool isExceptionEndpoint(const Pin *pin) const; // Make an exception -to specification. ExceptionTo *makeExceptionTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - const RiseFallBoth *end_rf); + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + const RiseFallBoth *end_rf) const; + FilterPath *makeFilterPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to); - Wireload *wireload(const MinMax *min_max); + ExceptionThruSeq *thrus, + ExceptionTo *to); + void makeFilter(ExceptionFrom *from, + ExceptionThruSeq *thrus); + FilterPath *filter() const { return filter_; } + void deleteFilter(); + + Wireload *wireload(const MinMax *min_max) const; void setWireload(Wireload *wireload, - const MinMaxAll *min_max); - WireloadMode wireloadMode(); + const MinMaxAll *min_max); + WireloadMode wireloadMode() const { return wireload_mode_; }; void setWireloadMode(WireloadMode mode); const WireloadSelection *wireloadSelection(const MinMax *min_max); void setWireloadSelection(WireloadSelection *selection, - const MinMaxAll *min_max); + const MinMaxAll *min_max); // STA interface. InputDelaySet *refPinInputDelays(const Pin *ref_pin) const; - LogicValueMap &logicValues() { return logic_value_map_; } - LogicValueMap &caseLogicValues() { return case_value_map_; } + const LogicValueMap &logicValues() const { return logic_value_map_; } + const LogicValueMap &caseLogicValues() const { return case_value_map_; } // Returns nullptr if set_operating_conditions has not been called. OperatingConditions *operatingConditions(const MinMax *min_max) const; // Instance specific process/voltage/temperature. @@ -807,23 +815,23 @@ public: const MinMax *min_max) const; // Pvt may be shared among multiple instances. void setPvt(const Instance *inst, - const MinMaxAll *min_max, - const Pvt &pvt); + const MinMaxAll *min_max, + const Pvt &pvt); void voltage(const MinMax *min_max, // Return values. float &voltage, - bool &exists); + bool &exists) const; void voltage(const Net *net, const MinMax *min_max, // Return values. float &voltage, - bool &exists); + bool &exists) const; void setVoltage(const MinMax *min_max, float voltage); void setVoltage(const Net *net, const MinMax *min_max, float voltage); - InputDrive *findInputDrive(Port *port); + InputDrive *findInputDrive(Port *port) const; Clock *findClock(const char *name) const; ClockSeq findClocksMatching(PatternMatch *pattern) const; // True if pin is defined as a clock source (pin may be hierarchical). @@ -835,51 +843,63 @@ public: // Find the clocks defined for pin. ClockSet *findClocks(const Pin *pin) const; ClockSet *findLeafPinClocks(const Pin *pin) const; - void sortedClocks(ClockSeq &clks); - ClockSeq *clocks() { return &clocks_; } - ClockSeq &clks() { return clocks_; } + const ClockSeq &clocks() const { return clocks_; } + ClockSeq sortedClocks() const; bool clkDisabledByHpinThru(const Clock *clk, - const Pin *from_pin, - const Pin *to_pin); + const Pin *from_pin, + const Pin *to_pin) const; void clkHpinDisablesInvalid(); - ClockUncertainties *clockUncertainties(const Pin *pin); + const ClockUncertainties *clockUncertainties(const Pin *pin) const; void clockUncertainty(const Pin *pin, - const SetupHold *setup_hold, - float &uncertainty, - bool &exists); + const SetupHold *setup_hold, + // Return values. + float &uncertainty, + bool &exists); // Inter-clock uncertainty. void clockUncertainty(const Clock *src_clk, - const RiseFall *src_rf, - const Clock *tgt_clk, - const RiseFall *tgt_rf, - const SetupHold *setup_hold, - float &uncertainty, bool &exists); + const RiseFall *src_rf, + const Clock *tgt_clk, + const RiseFall *tgt_rf, + const SetupHold *setup_hold, + // Return values. + float &uncertainty, + bool &exists) const; void clockGatingMarginEnablePin(const Pin *enable_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const RiseFall *enable_rf, + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; void clockGatingMarginInstance(Instance *inst, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const RiseFall *enable_rf, + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; void clockGatingMarginClkPin(const Pin *clk_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const RiseFall *enable_rf, + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; void clockGatingMarginClk(const Clock *clk, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const RiseFall *enable_rf, + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; void clockGatingMargin(const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin); + const SetupHold *setup_hold, + // Return values. + bool &exists, + float &margin) const; // Gated clock active (non-controlling) logic value. LogicValue clockGatingActiveValue(const Pin *clk_pin, - const Pin *enable_pin); + const Pin *enable_pin) const; // Find the cycle accounting info for paths that start at src clock // edge and end at target clock edge. CycleAccting *cycleAccting(const ClockEdge *src, - const ClockEdge *tgt); + const ClockEdge *tgt); // Report clock to clock relationships that exceed max_cycle_count. void reportClkToClkMaxCycleWarnings(); @@ -887,7 +907,7 @@ public: // Pin -> input delays. const InputDelaysPinMap &inputDelayPinMap() const { return input_delay_pin_map_; } // Input delays on leaf_pin. - InputDelaySet *inputDelaysLeafPin(const Pin *leaf_pin); + InputDelaySet *inputDelaysLeafPin(const Pin *leaf_pin) const; bool hasInputDelay(const Pin *leaf_pin) const; // Pin is internal (not top level port) and has an input arrival. bool isInputDelayInternal(const Pin *pin) const; @@ -896,99 +916,94 @@ public: // Pin -> output delays. const OutputDelaysPinMap &outputDelaysPinMap() const { return output_delay_pin_map_; } // Output delays on leaf_pin. - OutputDelaySet *outputDelaysLeafPin(const Pin *leaf_pin); - bool hasOutputDelay(const Pin *leaf_pin) const; + OutputDelaySet *outputDelaysLeafPin(const Pin *leaf_pin) const; + [[nodiscard]] bool hasOutputDelay(const Pin *leaf_pin) const; - PortExtCap *portExtCap(const Port *port, - const Corner *corner) const; + const PortExtCap *portExtCap(const Port *port) const; bool hasPortExtCap(const Port *port) const; void portExtCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - bool &has_pin_cap, - float &wire_cap, - bool &has_wire_cap, - int &fanout, - bool &has_fanout) const; + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &pin_cap, + bool &has_pin_cap, + float &wire_cap, + bool &has_wire_cap, + int &fanout, + bool &has_fanout) const; float portExtCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) const; + const RiseFall *rf, + const MinMax *min_max) const; // Connected total capacitance. // pin_cap = pin capacitance + port external pin // wire_cap = port external wire capacitance + net wire capacitance void connectedCap(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load) const; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load) const; void pinCaps(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap, - float &fanout) const; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout) const; void portExtFanout(const Port *port, - const Corner *corner, - const MinMax *min_max, - // Return values. - int &fanout, - bool &exists); + const MinMax *min_max, + // Return values. + int &fanout, + bool &exists) const; int portExtFanout(Port *port, - const Corner *corner, - const MinMax *min_max); + const MinMax *min_max) const; // Return true if search should proceed from pin/clk (no false paths // start at pin/clk). When thru is true, consider -thru exceptions // that start at pin/net/instance also). Transition tr applies to // pin, not clk. bool exceptionFromStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const; + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + ExceptionStateSet *&states); bool exceptionFromStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - bool include_filter, - ExceptionStateSet *&states) const; + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states); void exceptionFromClkStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const; + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + ExceptionStateSet *&states); void filterRegQStates(const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const; + const RiseFall *to_rf, + const MinMax *min_max, + ExceptionStateSet *&states) const; // Return hierarchical -thru exceptions that start between // from_pin and to_pin. void exceptionThruStates(const Pin *from_pin, const Pin *to_pin, const RiseFall *to_rf, const MinMax *min_max, - ExceptionStateSet *&states) const; + ExceptionStateSet *&states); // Find the highest priority exception with first exception pt at // pin/clk end. void exceptionTo(ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority); bool exceptionMatchesTo(ExceptionPath *exception, const Pin *pin, const RiseFall *rf, @@ -997,18 +1012,18 @@ public: bool match_min_max_exactly, bool require_to_pin) const; void groupPathsTo(const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return value. - ExceptionPathSeq &group_paths) const; + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + // Return value. + ExceptionPathSeq &group_paths); bool isCompleteTo(ExceptionState *state, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin) const; bool isCompleteTo(ExceptionState *state, const Pin *pin, const RiseFall *rf, @@ -1018,29 +1033,39 @@ public: const PinSet &pathDelayInternalFrom() const; bool isPathDelayInternalTo(const Pin *pin) const; bool isPathDelayInternalToBreak(const Pin *pin) const; - ExceptionPathSet &exceptions() { return exceptions_; } + const ExceptionPathSet &exceptions() const { return exceptions_; } void deleteExceptions(); void deleteException(ExceptionPath *exception); void recordException(ExceptionPath *exception); void unrecordException(ExceptionPath *exception); - void annotateGraph(); - void removeGraphAnnotations(); // Network edit before/after methods. void deletePinBefore(const Pin *pin); void connectPinAfter(const Pin *pin); void clkHpinDisablesChanged(const Pin *pin); void makeClkHpinDisable(const Clock *clk, - const Pin *drvr, - const Pin *load); + const Pin *drvr, + const Pin *load); void ensureClkHpinDisables(); + //////////////////////////////////////////////////////////////// + // + // Sdc/Mode dependewnt state + // + //////////////////////////////////////////////////////////////// + + // Vertices are constrained if they have one or more of the + // following timing constraints: + // output delay constraints + // data check constraints + // path delay constraints + bool isConstrainedEnd(const Pin *pin) const; + protected: void portMembers(const Port *port, - PortSeq &ports); + PortSeq &ports) const; void initVariables(); void clearCycleAcctings(); - void removeLibertyAnnotations(); void deleteExceptionsReferencing(Clock *clk); void deleteClkPinMappings(Clock *clk); void makeClkPinMappings(Clock *clk); @@ -1048,8 +1073,6 @@ protected: PinSet *pins); void makeDefaultArrivalClock(); InputDrive *ensureInputDrive(const Port *port); - PortExtCap *ensurePortExtPinCap(const Port *port, - const Corner *corner); ExceptionPath *findMergeMatch(ExceptionPath *exception); void addException1(ExceptionPath *exception); void addException2(ExceptionPath *exception); @@ -1063,140 +1086,140 @@ protected: bool hasLibertyCheckTo(const Pin *pin); void deleteMatchingExceptions(ExceptionPath *exception); void findMatchingExceptions(ExceptionPath *exception, - ExceptionPathSet &matches); + ExceptionPathSet &matches); void checkForThruHpins(ExceptionPath *exception); void findMatchingExceptionsFirstFrom(ExceptionPath *exception, - ExceptionPathSet &matches); + ExceptionPathSet &matches); void findMatchingExceptionsFirstThru(ExceptionPath *exception, - ExceptionPathSet &matches); + ExceptionPathSet &matches); void findMatchingExceptionsFirstTo(ExceptionPath *exception, - ExceptionPathSet &matches); + ExceptionPathSet &matches); void findMatchingExceptionsClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map, - ExceptionPathSet &matches); + ClockSet *clks, + ClockExceptionsMap &exception_map, + ExceptionPathSet &matches); void findMatchingExceptionsPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map, - ExceptionPathSet &matches); + PinSet *pins, + PinExceptionsMap &exception_map, + ExceptionPathSet &matches); void findMatchingExceptionsInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map, - ExceptionPathSet &matches); + InstanceSet *insts, + InstanceExceptionsMap &exception_map, + ExceptionPathSet &matches); void findMatchingExceptions(ExceptionPath *exception, - ExceptionPathSet *potential_matches, - ExceptionPathSet &matches); + ExceptionPathSet *potential_matches, + ExceptionPathSet &matches); void expandExceptionExcluding(ExceptionPath *exception, - ExceptionPath *excluding, - ExceptionPathSet &expanded_matches); + ExceptionPath *excluding, + ExceptionPathSet &expanded_matches); void recordException1(ExceptionPath *exception); void recordExceptionFirstPts(ExceptionPath *exception); void recordExceptionFirstFrom(ExceptionPath *exception); void recordExceptionFirstThru(ExceptionPath *exception); void recordExceptionFirstTo(ExceptionPath *exception); void recordExceptionClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map); + ClockSet *clks, + ClockExceptionsMap &exception_map); void recordExceptionInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map); + InstanceSet *insts, + InstanceExceptionsMap &exception_map); void recordExceptionPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map); + PinSet *pins, + PinExceptionsMap &exception_map); void recordExceptionNets(ExceptionPath *exception, - NetSet *nets, - NetExceptionsMap &exception_map); + NetSet *nets, + NetExceptionsMap &exception_map); void recordExceptionHpin(ExceptionPath *exception, - Pin *pin, - PinExceptionsMap &exception_map); + Pin *pin, + PinExceptionsMap &exception_map); void recordExceptionEdges(ExceptionPath *exception, - EdgePinsSet *edges, - EdgeExceptionsMap &exception_map); + EdgePinsSet *edges, + EdgeExceptionsMap &exception_map); void recordMergeHash(ExceptionPath *exception, ExceptionPt *missing_pt); void recordMergeHashes(ExceptionPath *exception); void unrecordExceptionFirstPts(ExceptionPath *exception); void unrecordExceptionPins(ExceptionPath *exception); void unrecordExceptionClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map); + ClockSet *clks, + ClockExceptionsMap &exception_map); void unrecordExceptionPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map); + PinSet *pins, + PinExceptionsMap &exception_map); void unrecordExceptionInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map); + InstanceSet *insts, + InstanceExceptionsMap &exception_map); void unrecordExceptionEdges(ExceptionPath *exception, - EdgePinsSet *edges, - EdgeExceptionsMap &exception_map); + EdgePinsSet *edges, + EdgeExceptionsMap &exception_map); void unrecordExceptionNets(ExceptionPath *exception, - NetSet *nets, - NetExceptionsMap &exception_map); + NetSet *nets, + NetExceptionsMap &exception_map); void unrecordExceptionHpin(ExceptionPath *exception, - Pin *pin, - PinExceptionsMap &exception_map); + Pin *pin, + PinExceptionsMap &exception_map); void unrecordMergeHashes(ExceptionPath *exception); void unrecordMergeHash(ExceptionPath *exception, - ExceptionPt *missing_pt); + ExceptionPt *missing_pt); void mergeException(ExceptionPath *exception); void expandException(ExceptionPath *exception, - ExceptionPathSet &expansions); + ExceptionPathSet &expansions); bool exceptionFromStates(const ExceptionPathSet *exceptions, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - bool include_filter, - ExceptionStateSet *&states) const; + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) const; void exceptionThruStates(const ExceptionPathSet *exceptions, - const RiseFall *to_rf, - const MinMax *min_max, - // Return value. - ExceptionStateSet *&states) const; + const RiseFall *to_rf, + const MinMax *min_max, + // Return value. + ExceptionStateSet *&states) const; void exceptionTo(const ExceptionPathSet *to_exceptions, - ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const; + ExceptionPathType type, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const; void exceptionTo(ExceptionPath *exception, - ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const; + ExceptionPathType type, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const; void groupPathsTo(const ExceptionPathSet *to_exceptions, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return value. - ExceptionPathSeq &group_paths) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + // Return value. + ExceptionPathSeq &group_paths) const; void makeLoopPath(ExceptionThruSeq *thrus); void makeLoopException(const Pin *loop_input_pin, - const Pin *loop_pin, - const Pin *loop_prev_pin); + const Pin *loop_pin, + const Pin *loop_prev_pin); void makeLoopExceptionThru(const Pin *pin, - ExceptionThruSeq *thrus); + ExceptionThruSeq *thrus); void deleteConstraints(); InputDelay *findInputDelay(const Pin *pin, - const ClockEdge *clk_edge); + const ClockEdge *clk_edge); InputDelay *makeInputDelay(const Pin *pin, - const ClockEdge *clk_edge); + const ClockEdge *clk_edge); void deleteInputDelays(const Pin *pin, - InputDelay *except); + InputDelay *except); void deleteInputDelaysReferencing(const Clock *clk); void deleteInputDelay(InputDelay *input_delay); OutputDelay *findOutputDelay(const Pin *pin, - const ClockEdge *clk_edge); + const ClockEdge *clk_edge); OutputDelay *makeOutputDelay(const Pin *pin, - const ClockEdge *clk_edge); + const ClockEdge *clk_edge); void deleteOutputDelays(const Pin *pin, OutputDelay *except); void deleteOutputDelaysReferencing(const Clock *clk); @@ -1211,44 +1234,32 @@ protected: // Liberty library to look for defaults. LibertyLibrary *defaultLibertyLibrary(); void annotateGraphConstrainOutputs(); - void annotateDisables(); - void annotateGraphDisabled(const Pin *pin); - void setEdgeDisabledInstPorts(DisabledInstancePorts *disabled_inst); - void setEdgeDisabledInstFrom(Pin *from_pin, - bool disable_checks); - void setEdgeDisabledInstPorts(DisabledPorts *disabled_port, - Instance *inst); void deleteClockLatenciesReferencing(Clock *clk); void deleteClockLatency(ClockLatency *latency); void deleteDeratingFactors(); - void annotateGraphOutputDelays(); - void annotateGraphDataChecks(); - void annotateGraphConstrained(const PinSet *pins); - void annotateGraphConstrained(const InstanceSet *insts); - void annotateGraphConstrained(const Instance *inst); - void annotateGraphConstrained(const Pin *pin); + void annotateHierClkLatency(); void annotateHierClkLatency(const Pin *hpin, - ClockLatency *latency); + ClockLatency *latency); void netCaps(const Pin *drvr_pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, bool &has_net_load) const; // connectedCap pin_cap. float connectedPinCap(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max); + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max); float portCapacitance(Instance *inst, LibertyPort *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) const; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) const; void removeClockGroups(ClockGroups *groups); void ensureClkGroupExclusions(); void makeClkGroupExclusions(ClockGroups *clk_groups); @@ -1258,17 +1269,18 @@ protected: void clearClkGroupExclusions(); char *makeClockGroupsName(); void setClockSense(const Pin *pin, - const Clock *clk, - ClockSense sense); + const Clock *clk, + ClockSense sense); bool clkStopSense(const Pin *to_pin, - const Clock *clk, - const RiseFall *from_rf, - const RiseFall *to_rf) const; + const Clock *clk, + const RiseFall *from_rf, + const RiseFall *to_rf) const; void disconnectPinBefore(const Pin *pin, - ExceptionPathSet *exceptions); + ExceptionPathSet *exceptions); void clockGroupsDeleteClkRefs(Clock *clk); void clearGroupPathMap(); + Mode *mode_; AnalysisType analysis_type_; OperatingConditions *operating_conditions_[MinMax::index_count]; InstancePvtMap instance_pvt_maps_[MinMax::index_count]; @@ -1294,6 +1306,7 @@ protected: bool clk_hpin_disables_valid_; PinSet propagated_clk_pins_; ClockLatencies clk_latencies_; + EdgeClockLatencyMap edge_clk_latency_map_; ClockInsertions clk_insertions_; PinClockUncertaintyMap pin_clk_uncertainty_map_; InterClockUncertaintySet inter_clk_uncertainties_; @@ -1338,13 +1351,10 @@ protected: // External parasitics on top level ports. // set_load port // set_fanout_load port - // Indexed by corner_index. - std::vector port_ext_cap_maps_; + PortExtCapMap port_ext_cap_map_; // set_load net - // Indexed by corner_index. - std::vector net_wire_cap_maps_; - // Indexed by corner_index. - std::vector drvr_pin_wire_cap_maps_; + NetWireCapMap net_wire_cap_map_; + PinWireCapMap drvr_pin_wire_cap_map_; NetResistanceMap net_res_map_; PinSet disabled_pins_; PortSet disabled_ports_; @@ -1387,6 +1397,12 @@ protected: bool path_delays_without_to_; // Group path exception names. GroupPathMap group_path_map_; + + // Filter exception to tag arrivals for + // report_timing -from pin|inst -through. + // -to is always nullptr. + FilterPath *filter_; + InputDriveMap input_drive_map_; // set_LogicValue::one/zero/dc LogicValueMap logic_value_map_; @@ -1404,10 +1420,6 @@ protected: WireloadMode wireload_mode_; WireloadSelection *wireload_selection_[MinMax::index_count]; - // Annotations on graph objects that are stored in constraints - // rather on the graph itself. - EdgeClockLatencyMap edge_clk_latency_; - private: friend class WriteSdc; friend class FindNetCaps; diff --git a/include/sta/SdcClass.hh b/include/sta/SdcClass.hh index cb5e6712b..adf159478 100644 --- a/include/sta/SdcClass.hh +++ b/include/sta/SdcClass.hh @@ -24,9 +24,10 @@ #pragma once -#include "Map.hh" -#include "Set.hh" -#include "Vector.hh" +#include +#include +#include + #include "LibertyClass.hh" #include "NetworkClass.hh" #include "MinMaxValues.hh" @@ -67,11 +68,11 @@ class PortDelay; enum class AnalysisType { single, bc_wc, ocv }; enum class ExceptionPathType { false_path, loop, multi_cycle, path_delay, - group_path, filter, any}; + group_path, filter, any}; enum class ClockSense { positive, negative, stop }; -typedef std::pair ClockPair; +using ClockPair = std::pair; class ClockIndexLess { @@ -80,25 +81,25 @@ public: const Clock *clk2) const; }; -typedef Vector FloatSeq; -typedef Vector IntSeq; -typedef Vector ClockSeq; -typedef std::vector ConstClockSeq; -typedef Set ClockSet; -typedef std::set ConstClockSet; -typedef ClockSet ClockGroup; -typedef Vector PinSetSeq; -typedef MinMax SetupHold; -typedef MinMaxAll SetupHoldAll; -typedef Vector ExceptionThruSeq; -typedef Set LibertyPortPairSet; -typedef Map DisabledInstancePortsMap; -typedef Map DisabledCellPortsMap; -typedef MinMaxValues ClockUncertainties; -typedef std::set ExceptionPathSet; -typedef PinPair EdgePins; -typedef PinPairSet EdgePinsSet; -typedef Map LogicValueMap; +using FloatSeq = std::vector; +using IntSeq = std::vector; +using ClockSeq = std::vector; +using ConstClockSeq = std::vector; +using ClockSet = std::set; +using ConstClockSet = std::set; +using ClockGroup = ClockSet; +using PinSetSeq = std::vector; +using SetupHold = MinMax; +using SetupHoldAll = MinMaxAll; +using ExceptionThruSeq = std::vector; +using LibertyPortPairSet = std::set; +using DisabledInstancePortsMap = std::map; +using DisabledCellPortsMap = std::map; +using ClockUncertainties = MinMaxValues; +using ExceptionPathSet = std::set; +using EdgePins = PinPair; +using EdgePinsSet = PinPairSet; +using LogicValueMap = std::map; class ClockSetLess { @@ -107,7 +108,7 @@ public: const ClockSet *set2) const; }; -typedef Set ClockGroupSet; +using ClockGroupSet = std::set; // For Search. class ExceptionState; @@ -120,7 +121,7 @@ public: }; class ExceptionPath; -typedef Set ExceptionStateSet; +using ExceptionStateSet = std::set; // Constraint applies to clock or data paths. enum class PathClkOrData { clk, data }; diff --git a/include/sta/SdcNetwork.hh b/include/sta/SdcNetwork.hh index a3473759b..c20627f3b 100644 --- a/include/sta/SdcNetwork.hh +++ b/include/sta/SdcNetwork.hh @@ -248,28 +248,28 @@ public: protected: void parsePath(const char *path, - // Return values. - Instance *&inst, - const char *&path_tail) const; + // Return values. + Instance *&inst, + const char *&path_tail) const; void scanPath(const char *path, - // Return values. - // Unescaped divider count. - int ÷r_count, - int &path_length) const; + // Return values. + // Unescaped divider count. + int ÷r_count, + int &path_length) const; void parsePath(const char *path, - int divider_count, - int path_length, - // Return values. - Instance *&inst, - const char *&path_tail) const; + int divider_count, + int path_length, + // Return values. + Instance *&inst, + const char *&path_tail) const; bool visitMatches(const Instance *parent, - const PatternMatch *pattern, - std::function - visit_tail) const; + const PatternMatch *pattern, + std::function + visit_tail) const; bool visitPinTail(const Instance *instance, - const PatternMatch *tail, - PinSeq &matches) const; + const PatternMatch *tail, + PinSeq &matches) const; void findInstancesMatching1(const Instance *context, const PatternMatch *pattern, InstanceSeq &matches) const; diff --git a/include/sta/Search.hh b/include/sta/Search.hh index b23ef5c4a..9889be75f 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -26,9 +26,9 @@ #include #include +#include #include "MinMax.hh" -#include "UnorderedSet.hh" #include "Transition.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" @@ -47,6 +47,7 @@ class BfsFwdIterator; class BfsBkwdIterator; class SearchPred; class SearchThru; +class SearchAdj; class ClkInfoLess; class PathEndVisitor; class ArrivalVisitor; @@ -55,52 +56,49 @@ class ClkPathIterator; class EvalPred; class TagGroup; class TagGroupBldr; -class PathGroups; class WorstSlacks; -class DcalcAnalysisPt; class VisitPathEnds; class GatedClk; class CheckCrpr; -class Genclks; -class Corner; - -typedef Set ClkInfoSet; -typedef UnorderedSet TagSet; -typedef UnorderedSet TagGroupSet; -typedef Map VertexSlackMap; -typedef Vector VertexSlackMapSeq; -typedef Vector WorstSlacksSeq; -typedef std::vector DelayDblSeq; -typedef Vector ExceptionPathSeq; -typedef std::vector PathGroupSeq; +class Scene; + +using ClkInfoSet = std::set; +using TagSet = std::unordered_set; +using TagGroupSet = std::unordered_set; +using VertexSlackMap = std::map; +using VertexSlackMapSeq = std::vector; +using WorstSlacksSeq = std::vector; +using DelayDblSeq = std::vector; +using ExceptionPathSeq = std::vector; +using StdStringSeq = std::vector; class Search : public StaState { public: - explicit Search(StaState *sta); + Search(StaState *sta); virtual ~Search(); virtual void copyState(const StaState *sta); // Reset to virgin state. void clear(); // When enabled, non-critical path arrivals are pruned to improve // run time and reduce memory. - bool crprPathPruningEnabled() const; + [[nodiscard]] bool crprPathPruningEnabled() const; void setCrprpathPruningEnabled(bool enabled); // When path pruning is enabled required times for non-critical paths // that have been pruned require additional search. This option // disables additional search to returns approximate required times. - bool crprApproxMissingRequireds() const; + [[nodiscard]] bool crprApproxMissingRequireds() const; void setCrprApproxMissingRequireds(bool enabled); - bool unconstrainedPaths() const { return unconstrained_paths_; } + [[nodiscard]] bool unconstrainedPaths() const { return unconstrained_paths_; } // from/thrus/to are owned and deleted by Search. - // Use corner nullptr to report timing for all corners. - // PathEnds are owned by Search PathGroups and deleted on next call. + // Use scene nullptr to report timing for all scenes. + // PathEnds are owned by Mode PathGroups and deleted on next call. PathEndSeq findPathEnds(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, bool unconstrained, - const Corner *corner, + const SceneSeq &scenes, const MinMaxAll *min_max, size_t group_path_count, size_t endpoint_path_count, @@ -109,14 +107,14 @@ public: float slack_min, float slack_max, bool sort_by_slack, - PathGroupNameSet *group_names, + StdStringSeq &group_names, bool setup, bool hold, bool recovery, bool removal, bool clk_gating_setup, bool clk_gating_hold); - bool arrivalsValid(); + [[nodiscard]] bool arrivalsValid(); // Invalidate all arrival and required times. void arrivalsInvalid(); // Invalidate vertex arrival time. @@ -140,83 +138,60 @@ public: void findRequireds(); // Find required times down thru level. void findRequireds(Level level); - bool requiredsSeeded() const { return requireds_seeded_; } - bool requiredsExist() const { return requireds_exist_; } + [[nodiscard]] bool requiredsSeeded() const { return requireds_seeded_; } + [[nodiscard]] bool requiredsExist() const { return requireds_exist_; } // The sum of all negative endpoints slacks. // Incrementally updated. Slack totalNegativeSlack(const MinMax *min_max); - Slack totalNegativeSlack(const Corner *corner, - const MinMax *min_max); + Slack totalNegativeSlack(const Scene *scene, + const MinMax *min_max); // Worst endpoint slack and vertex. // Incrementally updated. void worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); - void worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); // Clock arrival respecting ideal clock insertion delay and latency. Arrival clkPathArrival(const Path *clk_path) const; Arrival clkPathArrival(const Path *clk_path, - const ClkInfo *clk_info, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap) const; + const ClkInfo *clk_info, + const ClockEdge *clk_edge, + const MinMax *min_max) const; // Clock arrival at the path source/launch point. Arrival pathClkPathArrival(const Path *path) const; - PathGroupSeq pathGroups(const PathEnd *path_end) const; void deletePathGroups(); - void makePathGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float min_slack, - float max_slack, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold); - virtual ExceptionPath *exceptionTo(ExceptionPathType type, - const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const; + ExceptionPath *exceptionTo(ExceptionPathType type, + const Path *path, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin, + Sdc *sdc) const; ExceptionPathSeq groupPathsTo(const PathEnd *path_end) const; - FilterPath *filter() const { return filter_; } void deleteFilter(); void deleteFilteredArrivals(); - VertexSet *endpoints(); + VertexSet &endpoints(); void endpointsInvalid(); - // Clock tree vertices between the clock source pin and register clk pins. - // This does NOT include generated clock source paths. - bool isClock(const Vertex *vertex) const; - // Vertices on propagated generated clock source paths. - bool isGenClkSrc(const Vertex *vertex) const; // The set of clocks that arrive at vertex in the clock network. - ClockSet clocks(const Vertex *vertex) const; - ClockSet clocks(const Pin *pin) const; + ClockSet clocks(const Pin *pin, + const Mode *mode) const; + ClockSet clocks(const Vertex *vertex, + const Mode *mode) const; // Clock domains for a vertex. - ClockSet clockDomains(const Vertex *vertex) const; - ClockSet clockDomains(const Pin *pin) const; - void visitStartpoints(VertexVisitor *visitor); - void visitEndpoints(VertexVisitor *visitor); - bool havePathGroups() const; - PathGroup *findPathGroup(const char *name, - const MinMax *min_max) const; - PathGroup *findPathGroup(const Clock *clk, - const MinMax *min_max) const; + ClockSet clockDomains(const Vertex *vertex, + const Mode *mode) const; + ClockSet clockDomains(const Pin *pin, + const Mode *mode) const; //////////////////////////////////////////////////////////////// // @@ -226,39 +201,43 @@ public: // Find arrivals for the clock tree. void findClkArrivals(); - void seedArrival(Vertex *vertex); EvalPred *evalPred() const { return eval_pred_; } - SearchPred *searchAdj() const { return search_adj_; } + SearchPred *searchAdj() const { return search_thru_; } Tag *tag(TagIndex index) const; TagIndex tagCount() const; TagGroupIndex tagGroupCount() const; void reportTagGroups() const; void reportPathCountHistogram() const; - virtual int clkInfoCount() const; - virtual bool isEndpoint(Vertex *vertex) const; - virtual bool isEndpoint(Vertex *vertex, - SearchPred *pred) const; + int clkInfoCount() const; + // Endpoint for any mode. + [[nodiscard]] bool isEndpoint(Vertex *vertex) const; + // Endpoint for one mode. + [[nodiscard]] bool isEndpoint(Vertex *vertex, + const Mode *mode) const; + [[nodiscard]] bool isEndpoint(Vertex *vertex, + const ModeSeq &modes) const; + [[nodiscard]] bool isEndpoint(Vertex *vertex, + SearchPred *pred, + const Mode *mode) const; void endpointInvalid(Vertex *vertex); Tag *fromUnclkedInputTag(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - bool is_segment_start, - bool require_exception); + const RiseFall *rf, + const MinMax *min_max, + bool is_segment_start, + bool require_exception, + Scene *scene); Tag *fromRegClkTag(const Pin *from_pin, - const RiseFall *from_rf, - const Clock *clk, - const RiseFall *clk_rf, - const ClkInfo *clk_info, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + const RiseFall *from_rf, + const Clock *clk, + const RiseFall *clk_rf, + const ClkInfo *clk_info, + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + Scene *scene); Tag *thruTag(Tag *from_tag, Edge *edge, const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap, TagSet *tag_cache); Tag *thruClkTag(Path *from_path, Vertex *from_vertex, @@ -268,69 +247,73 @@ public: const RiseFall *to_rf, bool arc_delay_min_max_eq, const MinMax *min_max, - const PathAnalysisPt *path_ap); + Scene *scene); const ClkInfo *thruClkInfo(Path *from_path, - Vertex *from_vertex, - const ClkInfo *from_clk_info, - bool from_is_clk, - Edge *edge, - Vertex *to_vertex, - const Pin *to_pin, - bool to_is_clk, - bool arc_delay_min_max_eq, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + Vertex *from_vertex, + const ClkInfo *from_clk_info, + bool from_is_clk, + Edge *edge, + Vertex *to_vertex, + const Pin *to_pin, + bool to_is_clk, + bool arc_delay_min_max_eq, + const MinMax *min_max, + Scene *scene); const ClkInfo *clkInfoWithCrprClkPath(const ClkInfo *from_clk_info, - Path *from_path, - const PathAnalysisPt *path_ap); + Path *from_path); void seedClkArrivals(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr); + const Mode *mode, + TagGroupBldr *tag_bldr); void setVertexArrivals(Vertex *vertex, - TagGroupBldr *group_bldr); + TagGroupBldr *group_bldr); void tnsInvalid(Vertex *vertex); - bool arrivalsChanged(Vertex *vertex, - TagGroupBldr *tag_bldr); + [[nodiscard]] bool arrivalsChanged(Vertex *vertex, + TagGroupBldr *tag_bldr); BfsFwdIterator *arrivalIterator() const { return arrival_iter_; } BfsBkwdIterator *requiredIterator() const { return required_iter_; } - bool arrivalsAtEndpointsExist()const{return arrivals_at_endpoints_exist_;} // Used by OpenROAD. bool makeUnclkedPaths(Vertex *vertex, - bool is_segment_start, + bool is_segment_start, bool require_exception, - TagGroupBldr *tag_bldr); + TagGroupBldr *tag_bldr, + const Mode *mode); bool makeUnclkedPaths2(Vertex *vertex, TagGroupBldr *tag_bldr); - bool isSegmentStart(const Pin *pin); - bool isInputArrivalSrchStart(Vertex *vertex); + [[nodiscard]] bool isInputArrivalSrchStart(Vertex *vertex); void seedInputSegmentArrival(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr); + Vertex *vertex, + const Mode *mode, + TagGroupBldr *tag_bldr); void enqueueLatchDataOutputs(Vertex *vertex); void enqueueLatchOutput(Vertex *vertex); - virtual void seedRequired(Vertex *vertex); - virtual void seedRequiredEnqueueFanin(Vertex *vertex); + void enqueuePendingClkFanouts(); + void postponeClkFanouts(Vertex *vertex); + void seedRequired(Vertex *vertex); + void seedRequiredEnqueueFanin(Vertex *vertex); void seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay); + Vertex *vertex, + InputDelay *input_delay, + const Mode *mode); void seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay, - bool is_segment_start, - TagGroupBldr *tag_bldr); + Vertex *vertex, + InputDelay *input_delay, + bool is_segment_start, + const Mode *mode, + TagGroupBldr *tag_bldr); // Insertion delay for regular or generated clock. Arrival clockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const; - bool propagateClkSense(const Pin *from_pin, - Path *from_path, - const RiseFall *to_rf); - - Tag *findTag(const RiseFall *rf, - const PathAnalysisPt *path_ap, + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Mode *mode) const; + [[nodiscard]] bool propagateClkSense(const Pin *from_pin, + Path *from_path, + const RiseFall *to_rf); + + Tag *findTag(Scene *scene, + const RiseFall *rf, + const MinMax *min_max, const ClkInfo *tag_clk, bool is_clk, InputDelay *input_delay, @@ -340,47 +323,51 @@ public: TagSet *tag_cache); void reportTags() const; void reportClkInfos() const; - const ClkInfo *findClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, - const Pin *gen_clk_src, - bool gen_clk_src_path, - const RiseFall *pulse_clk_sense, - Arrival insertion, - float latency, - ClockUncertainties *uncertainties, - const PathAnalysisPt *path_ap, - Path *crpr_clk_path); - const ClkInfo *findClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, - Arrival insertion, - const PathAnalysisPt *path_ap); + const ClkInfo *findClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + const Pin *gen_clk_src, + bool gen_clk_src_path, + const RiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + const ClockUncertainties *uncertainties, + const MinMax *min_max, + Path *crpr_clk_path); + const ClkInfo *findClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + Arrival insertion, + const MinMax *min_max); // Timing derated arc delay for a path analysis point. ArcDelay deratedDelay(const Vertex *from_vertex, - const TimingArc *arc, - const Edge *edge, - bool is_clk, - const PathAnalysisPt *path_ap); + const TimingArc *arc, + const Edge *edge, + bool is_clk, + const MinMax *min_max, + DcalcAPIndex dcalc_ap, + const Sdc *sdc); TagGroup *tagGroup(const Vertex *vertex) const; TagGroup *tagGroup(TagGroupIndex index) const; void reportArrivals(Vertex *vertex, - bool report_tag_index) const; + bool report_tag_index) const; Slack wnsSlack(Vertex *vertex, - PathAPIndex path_ap_index); + PathAPIndex path_ap_index); void levelsChangedBefore(); void levelChangedBefore(Vertex *vertex); void seedInputArrival(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr); + Vertex *vertex, + const Mode *mode, + TagGroupBldr *tag_bldr); void ensureDownstreamClkPins(); - bool matchesFilter(Path *path, - const ClockEdge *to_clk_edge); + [[nodiscard]] bool matchesFilter(Path *path, + const ClockEdge *to_clk_edge); CheckCrpr *checkCrpr() { return check_crpr_; } VisitPathEnds *visitPathEnds() { return visit_path_ends_; } GatedClk *gatedClk() { return gated_clk_; } - Genclks *genclks() { return genclks_; } void findClkVertexPins(PinSet &clk_pins); void findFilteredArrivals(ExceptionFrom *from, ExceptionThruSeq *thrus, @@ -391,10 +378,10 @@ public: Arrival *arrivals(const Vertex *vertex) const; Arrival *makeArrivals(const Vertex *vertex, - uint32_t count); + uint32_t count); void deleteArrivals(const Vertex *vertex); Required *requireds(const Vertex *vertex) const; - bool hasRequireds(const Vertex *vertex) const; + [[nodiscard]] bool hasRequireds(const Vertex *vertex) const; Required *makeRequireds(const Vertex *vertex, uint32_t count); void deleteRequireds(const Vertex *vertex); @@ -404,11 +391,11 @@ public: Path *makePrevPaths(const Vertex *vertex, uint32_t count); void deletePrevPaths(Vertex *vertex); - bool crprPathPruningDisabled(const Vertex *vertex) const; + [[nodiscard]] bool crprPathPruningDisabled(const Vertex *vertex) const; void setCrprPathPruningDisabled(const Vertex *vertex, bool disabled); - bool bfsInQueue(const Vertex *vertex, - BfsIndex index) const; + [[nodiscard]] bool bfsInQueue(const Vertex *vertex, + BfsIndex index) const; void setBfsInQueue(const Vertex *vertex, BfsIndex index, bool value); @@ -420,16 +407,11 @@ public: void deleteTagGroup(TagGroup *group); bool postponeLatchOutputs() const { return postpone_latch_outputs_; } void saveEnumPath(Path *path); + bool isSrchRoot(Vertex *vertex, + const Mode *mode) const; protected: - void init(StaState *sta); void initVars(); - void makeAnalysisPts(AnalysisType analysis_type); - void makeAnalysisPts(bool swap_clk_min_max, - bool report_min, - bool report_max, - DcalcAnalysisPt *dcalc_ap_min, - DcalcAnalysisPt *dcalc_ap_max); void deleteTags(); void deleteTagsPrev(); void deleteUnusedTagGroups(); @@ -437,90 +419,90 @@ protected: void seedArrivals(); void findClockVertices(VertexSet &vertices); void seedClkDataArrival(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - Arrival insertion, - TagGroupBldr *tag_bldr); + const RiseFall *rf, + const Clock *clk, + const ClockEdge *clk_edge, + const MinMax *min_max, + Arrival insertion, + Scene *scene, + TagGroupBldr *tag_bldr); void seedClkArrival(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - Arrival insertion, - TagGroupBldr *tag_bldr); + const RiseFall *rf, + const Clock *clk, + const ClockEdge *clk_edge, + const MinMax *min_max, + Arrival insertion, + Scene *scene, + TagGroupBldr *tag_bldr); Tag *clkDataTag(const Pin *pin, - const Clock *clk, - const RiseFall *rf, - const ClockEdge *clk_edge, - Arrival insertion, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + const Clock *clk, + const RiseFall *rf, + const ClockEdge *clk_edge, + Arrival insertion, + const MinMax *min_max, + Scene *scene); void findInputArrivalVertices(VertexSet &vertices); - void seedInputArrivals(ClockSet *clks); void findRootVertices(VertexSet &vertices); void findInputDrvrVertices(VertexSet &vertices); void seedInputArrival1(const Pin *pin, - Vertex *vertex, - bool is_segment_start, - TagGroupBldr *tag_bldr); + Vertex *vertex, + bool is_segment_start, + const Mode *mode, + TagGroupBldr *tag_bldr); void seedInputArrival(const Pin *pin, - Vertex *vertex, - ClockSet *wrt_clks); + Vertex *vertex, + ClockSet *wrt_clks); void seedInputDelayArrival(const Pin *pin, - InputDelay *input_delay, - const ClockEdge *clk_edge, - float clk_arrival, - float clk_insertion, - float clk_latency, - bool is_segment_start, - const MinMax *min_max, - PathAnalysisPt *path_ap, - TagGroupBldr *tag_bldr); + InputDelay *input_delay, + const ClockEdge *clk_edge, + float clk_arrival, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + Scene *scene, + TagGroupBldr *tag_bldr); void seedInputDelayArrival(const Pin *pin, - const RiseFall *rf, - float arrival, - InputDelay *input_delay, - const ClockEdge *clk_edge, - float clk_insertion, - float clk_latency, - bool is_segment_start, - const MinMax *min_max, - PathAnalysisPt *path_ap, - TagGroupBldr *tag_bldr); + const RiseFall *rf, + float arrival, + InputDelay *input_delay, + const ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + Scene *scene, + TagGroupBldr *tag_bldr); void inputDelayClkArrival(InputDelay *input_delay, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - // Return values. - float &clk_arrival, - float &clk_insertion, - float &clk_latency); + const ClockEdge *clk_edge, + const MinMax *min_max, + const Mode *mode, + // Return values. + float &clk_arrival, + float &clk_insertion, + float &clk_latency); void inputDelayRefPinArrival(Path *ref_path, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return values. - float &ref_arrival, - float &ref_insertion, - float &ref_latency); + const ClockEdge *clk_edge, + const MinMax *min_max, + const Sdc *sdc, + // Return values. + float &ref_arrival, + float &ref_insertion, + float &ref_latency); Tag *inputDelayTag(const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - float clk_insertion, - float clk_latency, - InputDelay *input_delay, - bool is_segment_start, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + const RiseFall *rf, + const ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + InputDelay *input_delay, + bool is_segment_start, + const MinMax *min_max, + Scene *scene); void seedClkVertexArrivals(); - void seedClkVertexArrivals(const Pin *pin, - Vertex *vertex); void findClkArrivals1(); - void findAllArrivals(bool thru_latches); + void findAllArrivals(bool thru_latches, + bool clks_only); void findArrivals1(Level level); Tag *mutateTag(Tag *from_tag, const Pin *from_pin, @@ -534,28 +516,28 @@ protected: bool to_is_segment_start, const ClkInfo *to_clk_info, InputDelay *to_input_delay, - const MinMax *min_max, - const PathAnalysisPt *path_ap, TagSet *tag_cache); ExceptionPath *exceptionTo(const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max) const; void seedRequireds(); void seedInvalidRequireds(); - bool havePendingLatchOutputs(); + [[nodiscard]] bool havePendingLatchOutputs(); void clearPendingLatchOutputs(); void enqueuePendingLatchOutputs(); void findFilteredArrivals(bool thru_latches); void findArrivalsSeed(); void seedFilterStarts(); - bool hasEnabledChecks(Vertex *vertex) const; - virtual float timingDerate(const Vertex *from_vertex, - const TimingArc *arc, - const Edge *edge, - bool is_clk, - const PathAnalysisPt *path_ap); + [[nodiscard]] bool hasEnabledChecks(Vertex *vertex, + const Mode *mode) const; + float timingDerate(const Vertex *from_vertex, + const TimingArc *arc, + const Edge *edge, + bool is_clk, + const Sdc *sdc, + const MinMax *min_max); void deletePaths(); // Delete with incremental tns/wns update. void deletePathsIncr(Vertex *vertex); @@ -569,29 +551,31 @@ protected: void updateInvalidTns(); void clearWorstSlack(); void wnsSlacks(Vertex *vertex, - // Return values. - SlackSeq &slacks); + // Return values. + SlackSeq &slacks); void wnsTnsPreamble(); void worstSlackPreamble(); void deleteWorstSlacks(); void updateWorstSlacks(Vertex *vertex, - Slack slacks); + Slack slacks); void updateTns(Vertex *vertex, - SlackSeq &slacks); + SlackSeq &slacks); void tnsIncr(Vertex *vertex, - Slack slack, - PathAPIndex path_ap_index); + Slack slack, + PathAPIndex path_ap_index); void tnsDecr(Vertex *vertex, - PathAPIndex path_ap_index); + PathAPIndex path_ap_index); void tnsNotifyBefore(Vertex *vertex); - bool matchesFilterTo(Path *path, - const ClockEdge *to_clk_edge) const; + [[nodiscard]] bool matchesFilterTo(Path *path, + const ClockEdge *to_clk_edge) const; const Path *pathClkPathArrival1(const Path *path) const; void deletePathsState(const Vertex *vertex) const; void clocks(const Vertex *vertex, + const Mode *mode, // Return value. ClockSet &clks) const; void clockDomains(const Vertex *vertex, + const Mode *mode, // Return value. ClockSet &clks) const; @@ -601,84 +585,90 @@ protected: bool unconstrained_paths_; bool crpr_path_pruning_enabled_; bool crpr_approx_missing_requireds_; + // Search predicates. - SearchPred *search_adj_; - SearchPred *search_clk_; + SearchPred *search_thru_; + SearchAdj *search_adj_; EvalPred *eval_pred_; - ArrivalVisitor *arrival_visitor_; - // Clock arrivals are known. - bool clk_arrivals_valid_; + // Some arrivals exist. bool arrivals_exist_; - // Arrivals at end points exist (but may be invalid). - bool arrivals_at_endpoints_exist_; // Arrivals at start points have been initialized. bool arrivals_seeded_; + // Vertices with invalid arrival times to update and search from. + VertexSet invalid_arrivals_; + std::mutex invalid_arrivals_lock_; + BfsFwdIterator *arrival_iter_; + ArrivalVisitor *arrival_visitor_; + // Some requireds exist. bool requireds_exist_; // Requireds have been seeded by searching arrivals to all endpoints. bool requireds_seeded_; - // Vertices with invalid arrival times to update and search from. - VertexSet *invalid_arrivals_; - std::mutex invalid_arrivals_lock_; - BfsFwdIterator *arrival_iter_; // Vertices with invalid required times to update and search from. - VertexSet *invalid_requireds_; + VertexSet invalid_requireds_; BfsBkwdIterator *required_iter_; + bool tns_exists_; // Endpoint vertices with slacks that have changed since tns was found. - VertexSet *invalid_tns_; + VertexSet invalid_tns_; // Indexed by path_ap->index(). DelayDblSeq tns_; // Indexed by path_ap->index(). VertexSlackMapSeq tns_slacks_; std::mutex tns_lock_; + // Indexed by path_ap->index(). WorstSlacks *worst_slacks_; + // Use pointer to clk_info set so Tag.hh does not need to be included. ClkInfoSet *clk_info_set_; std::mutex clk_info_lock_; - // Use pointer to tag set so Tag.hh does not need to be included. - TagSet *tag_set_; + // Entries in tags_ may be missing where previous filter tags were deleted. TagIndex tag_capacity_; std::atomic tags_; + // Use pointer to tag set so Tag.hh does not need to be included. + TagSet *tag_set_; std::vector tags_prev_; TagIndex tag_next_; - // Holes in tags_ left by deleting filter tags. - std::vector tag_free_indices_; std::mutex tag_lock_; - TagGroupSet *tag_group_set_; + + // Capacity of tag_groups_. + TagGroupIndex tag_group_capacity_; std::atomic tag_groups_; + TagGroupSet *tag_group_set_; std::vector tag_groups_prev_; TagGroupIndex tag_group_next_; // Holes in tag_groups_ left by deleting filter tag groups. std::vector tag_group_free_indices_; - // Capacity of tag_groups_. - TagGroupIndex tag_group_capacity_; std::mutex tag_group_lock_; + // Latches data outputs to queue on the next search pass. - VertexSet *pending_latch_outputs_; + VertexSet pending_latch_outputs_; std::mutex pending_latch_outputs_lock_; - VertexSet *endpoints_; - VertexSet *invalid_endpoints_; - // Filter exception to tag arrivals for - // report_timing -from pin|inst -through. - // -to is always nullptr. - FilterPath *filter_; - // filter_from_ is owned by filter_ if it exists. + // Clock network endpoints where arrival search was suppended by findClkArrivals(). + VertexSet pending_clk_endpoints_; + std::mutex pending_clk_endpoints_lock_; + + VertexSet endpoints_; + bool endpoints_initialized_; + VertexSet invalid_endpoints_; + + bool have_filter_; ExceptionFrom *filter_from_; + ExceptionThruSeq *filter_thrus_; ExceptionTo *filter_to_; - VertexSet *filtered_arrivals_; + VertexSet filtered_arrivals_; std::mutex filtered_arrivals_lock_; + bool found_downstream_clk_pins_; bool postpone_latch_outputs_; - PathGroups *path_groups_; - VisitPathEnds *visit_path_ends_; std::vector enum_paths_; + + VisitPathEnds *visit_path_ends_; GatedClk *gated_clk_; CheckCrpr *check_crpr_; - Genclks *genclks_; }; // Eval across latch D->Q edges. @@ -690,22 +680,21 @@ protected: class EvalPred : public SearchPred0 { public: - explicit EvalPred(const StaState *sta); - virtual bool searchThru(Edge *edge); + EvalPred(const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; void setSearchThruLatches(bool thru_latches); - virtual bool searchTo(const Vertex *to_vertex); + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; + + using SearchPred::searchFrom; + using SearchPred::searchThru; + using SearchPred::searchTo; protected: bool search_thru_latches_; }; -class ClkArrivalSearchPred : public EvalPred -{ -public: - ClkArrivalSearchPred(const StaState *sta); - virtual bool searchThru(Edge *edge); -}; - // Class for visiting fanin/fanout paths of a vertex. // This used by forward/backward search to find arrival/required path times. class PathVisitor : public VertexVisitor, public StaState @@ -714,56 +703,58 @@ public: // Uses search->evalPred() for search predicate. PathVisitor(const StaState *sta); PathVisitor(SearchPred *pred, - bool make_tag_cache, - const StaState *sta); + bool make_tag_cache, + const StaState *sta); virtual ~PathVisitor(); virtual void visitFaninPaths(Vertex *to_vertex); virtual void visitFanoutPaths(Vertex *from_vertex); protected: // Return false to stop visiting. - virtual bool visitEdge(const Pin *from_pin, Vertex *from_vertex, - Edge *edge, const Pin *to_pin, Vertex *to_vertex); + virtual bool visitEdge(const Pin *from_pin, + Vertex *from_vertex, + Edge *edge, + const Pin *to_pin, + Vertex *to_vertex); // Return false to stop visiting. - bool visitArc(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Path *from_path, - Edge *edge, - TimingArc *arc, - const Pin *to_pin, - Vertex *to_vertex, - const MinMax *min_max, - PathAnalysisPt *path_ap); + [[nodiscard]] bool visitArc(const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + Path *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const MinMax *min_max, + const Mode *mode); // This calls visit below with everything required to make to_path. // Return false to stop visiting. virtual bool visitFromPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Path *from_path, - Edge *edge, - TimingArc *arc, - const Pin *to_pin, - Vertex *to_vertex, - const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap); + Vertex *from_vertex, + const RiseFall *from_rf, + Path *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const RiseFall *to_rf, + const MinMax *min_max); // Return false to stop visiting. virtual bool visitFromToPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, + 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, - const PathAnalysisPt *path_ap) = 0; + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max) = 0; + SearchPred *pred_; TagSet *tag_cache_; }; @@ -776,45 +767,44 @@ public: ArrivalVisitor(const StaState *sta); virtual ~ArrivalVisitor(); // Initialize the visitor. - // Defaults pred to search->eval_pred_. - void init(bool always_to_endpoints); void init(bool always_to_endpoints, - SearchPred *pred); + bool clks_only, + SearchPred *pred); + void copyState(const StaState *sta); virtual void visit(Vertex *vertex); virtual VertexVisitor *copy() const; // Return false to stop visiting. virtual bool visitFromToPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, + 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, - const PathAnalysisPt *path_ap); + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max); void setAlwaysToEndpoints(bool to_endpoints); TagGroupBldr *tagBldr() const { return tag_bldr_; } protected: ArrivalVisitor(bool always_to_endpoints, - SearchPred *pred, - const StaState *sta); + SearchPred *pred, + const StaState *sta); void init0(); - void enqueueRefPinInputDelays(const Pin *ref_pin); - void seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay); + void enqueueRefPinInputDelays(const Pin *ref_pin, + const Sdc *sdc); + void seedArrivals(Vertex *vertex); void pruneCrprArrivals(); void constrainedRequiredsInvalid(Vertex *vertex, - bool is_clk); + bool is_clk); bool always_to_endpoints_; bool always_save_prev_paths_; + bool clks_only_; TagGroupBldr *tag_bldr_; TagGroupBldr *tag_bldr_no_crpr_; SearchPred *adj_pred_; @@ -827,14 +817,14 @@ class RequiredCmp public: RequiredCmp(); void requiredsInit(Vertex *vertex, - const StaState *sta); + const StaState *sta); void requiredSet(size_t path_index, - Required &required, - const MinMax *min_max, - const StaState *sta); + Required &required, + const MinMax *min_max, + const StaState *sta); // Return true if the requireds changed. bool requiredsSave(Vertex *vertex, - const StaState *sta); + const StaState *sta); Required required(size_t path_index); protected: @@ -854,46 +844,25 @@ public: protected: RequiredVisitor(bool make_tag_cache, - const StaState *sta); + const StaState *sta); // Return false to stop visiting. virtual bool visitFromToPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, + 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, - const PathAnalysisPt *path_ap); + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max); RequiredCmp *required_cmp_; VisitPathEnds *visit_path_ends_; }; -// This does not use SearchPred as a base class to avoid getting -// two sets of StaState variables when multiple inheritance is used -// to add the functions in this class to another. -class DynLoopSrchPred -{ -public: - DynLoopSrchPred(TagGroupBldr *tag_bldr); - -protected: - bool loopEnabled(Edge *edge, - bool dynamic_loop_breaking_enabled, - const Graph *graph, - Search *search); - bool hasPendingLoopPaths(Edge *edge, - const Graph *graph, - Search *search); - - TagGroupBldr *tag_bldr_; -}; - } // namespace diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index d9268b946..656839664 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -25,11 +25,10 @@ #pragma once #include +#include +#include -#include "Vector.hh" -#include "Set.hh" -#include "Map.hh" -#include "UnorderedMap.hh" +#include "VectorMap.hh" #include "StringSet.hh" #include "MinMaxValues.hh" #include "Delay.hh" @@ -39,7 +38,7 @@ namespace sta { class Search; -class Corner; +class Scene; class Path; class PathEnd; class PathGroup; @@ -56,8 +55,6 @@ class ClkInfo; class ClkInfoHash; class ClkInfoEqual; class VertexPathIterator; -class PathAnalysisPt; -class PathAnalysisPtIterator; class MinPulseWidthCheck; class MinPeriodCheck; class MaxSkewCheck; @@ -70,10 +67,10 @@ class ClkDelays; class TagMatchLess { public: - explicit TagMatchLess(bool match_crpr_clk_pin, - const StaState *sta); + TagMatchLess(bool match_crpr_clk_pin, + const StaState *sta); bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; protected: bool match_crpr_clk_pin_; @@ -84,7 +81,7 @@ class TagMatchHash { public: TagMatchHash(bool match_crpr_clk_pin, - const StaState *sta); + const StaState *sta); size_t operator()(const Tag *tag) const; protected: @@ -96,46 +93,42 @@ class TagMatchEqual { public: TagMatchEqual(bool match_crpr_clk_pin, - const StaState *sta); + const StaState *sta); bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; protected: bool match_crpr_clk_pin_; const StaState *sta_; }; -typedef int PathAPIndex; -typedef uint32_t TagIndex; -typedef Vector TagSeq; -typedef Vector MinPulseWidthCheckSeq; -typedef Vector MinPeriodCheckSeq; -typedef Vector MaxSkewCheckSeq; -typedef StringSet PathGroupNameSet; -typedef Vector PathEndSeq; -typedef Vector ArrivalSeq; -typedef Map VertexPathCountMap; -typedef Map PathIndexMap; -typedef Vector SlackSeq; -typedef Delay Crpr; -typedef Vector PathSeq; -typedef std::vector ConstPathSeq; +using PathAPIndex = int; +using TagIndex = uint32_t; +using TagSeq = std::vector; +using PathEndSeq = std::vector; +using ArrivalSeq = std::vector; +using VertexPathCountMap = std::map; +using PathIndexMap = VectorMap; +using SlackSeq = std::vector; +using Crpr = Delay; +using PathSeq = std::vector; +using ConstPathSeq = std::vector; enum class ReportPathFormat { full, - full_clock, - full_clock_expanded, - shorter, - endpoint, - summary, - slack_only, - json + full_clock, + full_clock_expanded, + shorter, + endpoint, + summary, + slack_only, + json }; static const TagIndex tag_index_bit_count = 28; static const TagIndex tag_index_max = (1 << tag_index_bit_count) - 1; static const TagIndex tag_index_null = tag_index_max; static const int path_ap_index_bit_count = 8; -// One path analysis point per corner min/max. -static const int corner_count_max = (1 << path_ap_index_bit_count) / 2; +// One path analysis point per scene min/max. +static const int scene_count_max = (1 << path_ap_index_bit_count) / 2; } // namespace diff --git a/include/sta/SearchPred.hh b/include/sta/SearchPred.hh index 618dd2ea2..c1e4c21a9 100644 --- a/include/sta/SearchPred.hh +++ b/include/sta/SearchPred.hh @@ -33,40 +33,49 @@ namespace sta { // Class hierarchy: // SearchPred +// SearchAdj (unless loop disabled, latch D->Q, timing check, dynamic loop) // SearchPred0 (unless disabled or constant) // EvalPred (unless timing check) -// SearchThru (unless latch D->Q, outside vertex subset) +// SearchThru (unless latch D->Q) // SearchPred1 (unless loop disabled) -// ClkTreeSearchPred (only wire or combinational) -// SearchPred2 (unless timing check) -// SearchPredNonLatch2 (unless latch D->Q) -// SearchPredNonReg2 (unless reg CLK->Q, latch D->Q) +// ClkTreeSearchPred (only wire or combinational) // Virtual base class for search predicates. class SearchPred { public: - SearchPred() {} + SearchPred(const StaState *sta); virtual ~SearchPred() {} // Search is allowed from from_vertex. - virtual bool searchFrom(const Vertex *from_vertex) = 0; + virtual bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const = 0; + bool searchFrom(const Vertex *from_vertex) const; // Search is allowed through edge. // from/to pins are NOT checked. // inst can be either the from_pin or to_pin instance because it // is only referenced when they are the same (non-wire edge). - virtual bool searchThru(Edge *edge) = 0; + virtual bool searchThru(Edge *edge, + const Mode *mode) const = 0; + bool searchThru(Edge *edge) const; // Search is allowed to to_pin. - virtual bool searchTo(const Vertex *to_vertex) = 0; + virtual bool searchTo(const Vertex *to_vertex, + const Mode *mode) const = 0; + bool searchTo(const Vertex *to_vertex) const; + void copyState(const StaState *sta); + +protected: + const StaState *sta_; }; class SearchPred0 : public SearchPred { public: - explicit SearchPred0(const StaState *sta); + SearchPred0(const StaState *sta); // Search from a vertex unless // disabled by constraint // constant logic zero/one - virtual bool searchFrom(const Vertex *from_vertex); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; // Search thru an edge unless // traverses disabled from/to pin pair // disabled by condition expression @@ -75,13 +84,12 @@ public: // cond expression is disabled // non-controlling constant values on other pins that disable the // edge (such as a mux select) - virtual bool searchThru(Edge *edge); + bool searchThru(Edge *edge, + const Mode *mode) const override; // Search to a vertex unless // constant logic zero/one - virtual bool searchTo(const Vertex *to_vertex); - -protected: - const StaState *sta_; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; }; // SearchPred0 unless @@ -89,75 +97,63 @@ protected: class SearchPred1 : public SearchPred0 { public: - explicit SearchPred1(const StaState *sta); - virtual bool searchThru(Edge *edge); -}; - -// SearchPred1 unless -// timing check edge -class SearchPred2 : public SearchPred1 -{ -public: - explicit SearchPred2(const StaState *sta); - virtual bool searchThru(Edge *edge); -}; + SearchPred1(const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; -// SearchPred2 unless -// latch D->Q edge -class SearchPredNonLatch2 : public SearchPred2 -{ -public: - explicit SearchPredNonLatch2(const StaState *sta); - virtual bool searchThru(Edge *edge); -}; - -// SearchPred2 unless -// register/latch CLK->Q edges. -class SearchPredNonReg2 : public SearchPred2 -{ -public: - explicit SearchPredNonReg2(const StaState *sta); - virtual bool searchThru(Edge *edge); + using SearchPred::searchFrom; + using SearchPred::searchThru; + using SearchPred::searchTo; }; // Predicate for BFS search to stop at the end of the clock tree. // Search only thru combinational gates and wires. -class ClkTreeSearchPred : public SearchPred1 +class ClkTreeSearchPred : public SearchPred { public: - explicit ClkTreeSearchPred(const StaState *sta); - virtual bool searchThru(Edge *edge); + ClkTreeSearchPred(const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + // The variable part of searchThru used by descendents. + virtual bool searchThruAllow(const TimingRole *role) const; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; }; bool -isClkEnd(Vertex *vertex, Graph *graph); +isClkEnd(Vertex *vertex, + const Mode *mode); // Predicate to see if arc/edge is disabled by constants on other pins // that effect the unateness of the edge. bool searchThru(const Edge *edge, - const TimingArc *arc, - const Graph *graph); + const TimingArc *arc, + const Mode *mode); bool searchThru(Vertex *from_vertex, - const RiseFall *from_rf, - const Edge *edge, - Vertex *to_vertex, - const RiseFall *to_rf); - + const RiseFall *from_rf, + const Edge *edge, + Vertex *to_vertex, + const RiseFall *to_rf, + const Mode *mode); //////////////////////////////////////////////////////////////// bool hasFanin(Vertex *vertex, - SearchPred *pred, - const Graph *graph); + SearchPred *pred, + const Graph *graph, + const Mode *mode); // Vertices with no fanout have at no enabled (non-disabled) edges // leaving them. bool hasFanout(Vertex *vertex, - SearchPred *pred, - const Graph *graph); + SearchPred *pred, + const Graph *graph, + const Mode *mode); } // namespace diff --git a/include/sta/Sequential.hh b/include/sta/Sequential.hh index cba09523b..1923c270a 100644 --- a/include/sta/Sequential.hh +++ b/include/sta/Sequential.hh @@ -55,8 +55,8 @@ enum class StateInternalValue { class StatetableRow; -typedef std::vector StateInputValues; -typedef std::vector StateInternalValues; +using StateInputValues = std::vector; +using StateInternalValues = std::vector; // Register/Latch class Sequential @@ -81,14 +81,14 @@ protected: // clocked_on/next_state for registers // enable/data for latches Sequential(bool is_register, - FuncExpr *clock, - FuncExpr *data, - FuncExpr *clear, - FuncExpr *preset, - LogicValue clr_preset_out, - LogicValue clr_preset_out_inv, - LibertyPort *output, - LibertyPort *output_inv); + FuncExpr *clock, + FuncExpr *data, + FuncExpr *clear, + FuncExpr *preset, + LogicValue clr_preset_out, + LogicValue clr_preset_out_inv, + LibertyPort *output, + LibertyPort *output_inv); bool is_register_; FuncExpr *clock_; diff --git a/include/sta/Set.hh b/include/sta/Set.hh deleted file mode 100644 index 7e08c3b17..000000000 --- a/include/sta/Set.hh +++ /dev/null @@ -1,220 +0,0 @@ -// 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 - -namespace sta { - -// Add convenience functions around STL container. -template > -class Set : public std::set -{ -public: - Set() : std::set() {} - explicit Set(const CMP &cmp) : std::set(cmp) {} - - // Find the entry corresponding to key. - KEY findKey(const KEY key) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) - return *find_iter; - else - return nullptr; - } - // Find out if key is in the set. - bool hasKey(const KEY key) const - { - auto find_iter = this->find(key); - return find_iter != this->end(); - } - - // Slowaris STL doesn't support operator== on sets. - static bool equal(const std::set *set1, - const std::set *set2); - - // True if set2 is a subset of this set. - bool isSubset(const std::set *set2); - - void insertSet(const std::set *set2); - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - this->clear(); - } - - static bool - intersects(const std::set *set1, - const std::set *set2, - CMP key_less); - - // Java style container itererator - // Set::Iterator iter(set); - // while (iter.hasNext()) { - // Key *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - explicit Iterator(std::set *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit Iterator(std::set &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(std::set *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(std::set &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - KEY next() { return *iter_++; } - std::set *container() { return container_; } - - private: - std::set *container_; - typename std::set::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - explicit ConstIterator(const std::set *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit ConstIterator(const std::set &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(const std::set *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(const std::set &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - KEY next() { return *iter_++; } - const std::set *container() { return container_; } - - private: - const std::set *container_; - typename std::set::const_iterator iter_; - }; -}; - -template -bool -Set::equal(const std::set *set1, - const std::set *set2) -{ - if ((set1 == nullptr || set1->empty()) - && (set2 == nullptr || set2->empty())) - return true; - else if (set1 && set2) { - if (set1->size() == set2->size()) { - typename Set::ConstIterator iter1(set1); - typename Set::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - if (iter1.next() != iter2.next()) - return false; - } - return true; - } - else - return false; - } - else - return false; -} - -template -bool -Set::isSubset(const std::set *set2) -{ - if (this->empty() && set2->empty()) - return true; - else { - typename Set::ConstIterator iter2(set2); - while (iter2.hasNext()) { - const KEY key2 = iter2.next(); - if (!hasKey(key2)) - return false; - } - return true; - } -} - -template -bool -Set::intersects(const std::set *set1, - const std::set *set2, - CMP key_less) -{ - if (set1 && set2) { - auto iter1 = set1->begin(); - auto end1 = set1->end(); - auto iter2 = set2->begin(); - auto end2 = set2->end(); - while (iter1 != end1 && iter2 != end2) { - if (key_less(*iter1, *iter2)) - iter1++; - else if (key_less(*iter2, *iter1)) - iter2++; - else - return true; - } - } - return false; -} - -// A complicated way to call the base class operator<. -template -bool -operator<(const Set &set1, const Set &set2) -{ - const std::set &set1_base = set1; - const std::set &set2_base = set2; - return set1_base < set2_base; -} - -template -void -Set::insertSet(const std::set *set2) -{ - if (set2) - this->insert(set2->begin(), set2->end()); -} - -} // namespace diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index cdf67203c..e0f307875 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -24,12 +24,16 @@ #pragma once +#include #include +#include +#include #include "StringSeq.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" #include "SdcClass.hh" +#include "Scene.hh" #include "GraphClass.hh" #include "ParasiticsClass.hh" #include "StaState.hh" @@ -40,6 +44,7 @@ #include "CircuitSim.hh" #include "Variables.hh" #include "Property.hh" +#include "RiseFallMinMaxDelay.hh" struct Tcl_Interp; @@ -53,10 +58,9 @@ class RiseFall; class VerilogReader; class ReportPath; class CheckTiming; -class DcalcAnalysisPt; -class CheckSlewLimits; -class CheckFanoutLimits; -class CheckCapacitanceLimits; +class CheckSlews; +class CheckFanouts; +class CheckCapacitances; class CheckMinPulseWidths; class CheckMinPeriods; class CheckMaxSkews; @@ -64,18 +68,24 @@ class PatternMatch; class CheckPeriods; class LibertyReader; class SearchPred; -class Corner; +class Scene; class ClkSkews; class ReportField; class EquivCells; +class StaSimObserver; +class GraphLoop; -typedef InstanceSeq::Iterator SlowDrvrIterator; -typedef Vector CheckError; -typedef Vector CheckErrorSeq; -typedef Vector CornerSeq; -typedef std::vector StdStringSeq; - +using ModeNameMap = std::map; +using SceneNameMap = std::map; +using SlowDrvrIterator = Iterator; +using CheckError = StringSeq; +using CheckErrorSeq = std::vector; +using StdStringSeq = std::vector; enum class CmdNamespace { sta, sdc }; +using ParasiticsNameMap = std::map; +// Path::slack/arrival/required function. +using PathDelayFunc = std::function; +using GraphLoopSeq = std::vector; // Initialize sta functions that are not part of the Sta class. void initSta(); @@ -115,10 +125,34 @@ public: virtual int defaultThreadCount() const; void setThreadCount(int thread_count); + // define_corners compatibility. + void makeScenes(StringSeq *scene_names); + void makeScene(const std::string &name, + const std::string &mode_name, + const StdStringSeq &liberty_min_files, + const StdStringSeq &liberty_max_files, + const std::string &spef_min_file, + const std::string &spef_max_file); + Scene *findScene(const std::string &name) const; + // Pattern match name. + SceneSeq findScenes(const std::string &name) const; + SceneSeq findScenes(const std::string &name, + ModeSeq &modes) const; + Scene *cmdScene() const; + void setCmdScene(Scene *scene); + SceneSeq makeSceneSeq(Scene *scene) const; + + Mode *cmdMode() const { return cmd_scene_->mode(); } + const std::string &cmdModeName(); + void setCmdMode(const std::string &mode_name); + Mode *findMode(const std::string &mode_name) const; + ModeSeq findModes(const std::string &mode_name) const; + Sdc *cmdSdc() const; + virtual LibertyLibrary *readLiberty(const char *filename, - Corner *corner, - const MinMaxAll *min_max, - bool infer_latches); + Scene *scene, + const MinMaxAll *min_max, + bool infer_latches); bool readVerilog(const char *filename); // Network readers call this to notify the Sta to delete any previously // linked network. @@ -130,729 +164,818 @@ public: // SDC Swig API. Instance *currentInstance() const; void setCurrentInstance(Instance *inst); - virtual void setAnalysisType(AnalysisType analysis_type); + virtual void setAnalysisType(AnalysisType analysis_type, + Sdc *sdc); void setOperatingConditions(OperatingConditions *op_cond, - const MinMaxAll *min_max); + const MinMaxAll *min_max, + Sdc *sdc); void setTimingDerate(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc); // Delay type is always net for net derating. void setTimingDerate(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc); void setTimingDerate(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc); void setTimingDerate(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate); - void unsetTimingDerate(); + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate, + Sdc *sdc); + void unsetTimingDerate(Sdc *sdc); void setInputSlew(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew, + Sdc *sdc); // Set port external pin load (set_load -pin port). void setPortExtPinCap(const Port *port, - const RiseFallBoth *rf, - const Corner *corner, - const MinMaxAll *min_max, - float cap); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float cap, + Sdc *sdc); void portExtCaps(const Port *port, - const Corner *corner, const MinMax *min_max, + const Sdc *sdc, float &pin_cap, float &wire_cap, int &fanout); // Set port external wire load (set_load -wire port). void setPortExtWireCap(const Port *port, - bool subtract_pin_cap, - const RiseFallBoth *rf, - const Corner *corner, - const MinMaxAll *min_max, - float cap); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float cap, + Sdc *sdc); // Set net wire capacitance (set_load -wire net). void setNetWireCap(const Net *net, - bool subtract_pin_load, - const Corner *corner, - const MinMaxAll *min_max, - float cap); + bool subtract_pin_load, + const MinMaxAll *min_max, + float cap, + Sdc *sdc); // Remove all "set_load net" annotations. - void removeNetLoadCaps() const; + void removeNetLoadCaps(Sdc *sdc) const; // Set port external fanout (used by wireload models). void setPortExtFanout(const Port *port, - int fanout, - const Corner *corner, - const MinMaxAll *min_max); + int fanout, + const MinMaxAll *min_max, + Sdc *sdc); // Liberty port capacitance. float capacitance(const LibertyPort *port, - Corner *corner, + Scene *scene, const MinMax *min_max); // pin_cap = net pin capacitances + port external pin capacitance, // wire_cap = annotated net capacitance + port external wire capacitance. void connectedCap(const Pin *drvr_pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const; + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const; void connectedCap(const Net *net, - Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const; + Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const; void setResistance(const Net *net, - const MinMaxAll *min_max, - float res); + const MinMaxAll *min_max, + float res, + Sdc *sdc); void 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 MinMaxAll *min_max); + 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); void setDriveResistance(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res, + Sdc *sdc); void setLatchBorrowLimit(const Pin *pin, - float limit); + float limit, + Sdc *sdc); void setLatchBorrowLimit(const Instance *inst, - float limit); + float limit, + Sdc *sdc); void setLatchBorrowLimit(const Clock *clk, - float limit); + float limit, + Sdc *sdc); void setMinPulseWidth(const RiseFallBoth *rf, - float min_width); + float min_width, + Sdc *sdc); void setMinPulseWidth(const Pin *pin, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width, + Sdc *sdc); void setMinPulseWidth(const Instance *inst, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width, + Sdc *sdc); void setMinPulseWidth(const Clock *clk, - const RiseFallBoth *rf, - float min_width); + const RiseFallBoth *rf, + float min_width, + Sdc *sdc); void setWireload(Wireload *wireload, - const MinMaxAll *min_max); - void setWireloadMode(WireloadMode mode); + const MinMaxAll *min_max, + Sdc *sdc); + void setWireloadMode(WireloadMode mode, + Sdc *sdc); void setWireloadSelection(WireloadSelection *selection, - const MinMaxAll *min_max); + const MinMaxAll *min_max, + Sdc *sdc); void setSlewLimit(Clock *clk, - const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew); + const RiseFallBoth *rf, + const PathClkOrData clk_data, + const MinMax *min_max, + float slew, + Sdc *sdc); void setSlewLimit(Port *port, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew, + Sdc *sdc); void setSlewLimit(Cell *cell, - const MinMax *min_max, - float slew); + const MinMax *min_max, + float slew, + Sdc *sdc); void setCapacitanceLimit(Cell *cell, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap, + Sdc *sdc); void setCapacitanceLimit(Port *port, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap, + Sdc *sdc); void setCapacitanceLimit(Pin *pin, - const MinMax *min_max, - float cap); + const MinMax *min_max, + float cap, + Sdc *sdc); void setFanoutLimit(Cell *cell, - const MinMax *min_max, - float fanout); + const MinMax *min_max, + float fanout, + Sdc *sdc); void setFanoutLimit(Port *port, - const MinMax *min_max, - float fanout); - void setMaxArea(float area); + const MinMax *min_max, + float fanout, + Sdc *sdc); + void setMaxArea(float area, + Sdc *sdc); void makeClock(const char *name, - PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - char *comment); + PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, + char *comment, + const Mode *mode); // edges size must be 3. void 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, - char *comment); - void removeClock(Clock *clk); + 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); + void removeClock(Clock *clk, + Sdc *sdc); // Update period/waveform for generated clocks from source pin clock. void updateGeneratedClks(); // True if pin is defined as a clock source (pin may be hierarchical). - bool isClockSrc(const Pin *pin) const; + bool isClockSrc(const Pin *pin, + const Sdc *sdc) const; // Propagated (non-ideal) clocks. - void setPropagatedClock(Clock *clk); - void removePropagatedClock(Clock *clk); - void setPropagatedClock(Pin *pin); - void removePropagatedClock(Pin *pin); + void setPropagatedClock(Clock *clk, + const Mode *mode); + void removePropagatedClock(Clock *clk, + const Mode *mode); + void setPropagatedClock(Pin *pin, + const Mode *mode); + void removePropagatedClock(Pin *pin, + const Mode *mode); void setClockSlew(Clock *clock, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew); - void removeClockSlew(Clock *clk); + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew, + Sdc *sdc); + void removeClockSlew(Clock *clk, + Sdc *sdc); // Clock latency. // Latency can be on a clk, pin, or clk/pin combination. void setClockLatency(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay); + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float delay, + Sdc *sdc); void removeClockLatency(const Clock *clk, - const Pin *pin); + const Pin *pin, + Sdc *sdc); // Clock insertion delay (source latency). void setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay); + const Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay, + Sdc *sdc); void removeClockInsertion(const Clock *clk, - const Pin *pin); + const Pin *pin, + Sdc *sdc); // Clock uncertainty. - virtual void setClockUncertainty(Clock *clk, - const SetupHoldAll *setup_hold, - float uncertainty); - virtual void removeClockUncertainty(Clock *clk, - const SetupHoldAll *setup_hold); - virtual void setClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold, - float uncertainty); - virtual void removeClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold); + void setClockUncertainty(Clock *clk, + const SetupHoldAll *setup_hold, + float uncertainty); + void removeClockUncertainty(Clock *clk, + const SetupHoldAll *setup_hold); + void setClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold, + float uncertainty, + Sdc *sdc); + void removeClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold, + Sdc *sdc); // Inter-clock uncertainty. - virtual void setClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, - float uncertainty); - virtual void removeClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold); + void setClockUncertainty(Clock *from_clk, + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + float uncertainty, + Sdc *sdc); + void removeClockUncertainty(Clock *from_clk, + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + Sdc *sdc); ClockGroups *makeClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment); + 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 removeClockGroupsPhysicallyExclusive(const char *name); - void removeClockGroupsAsynchronous(const char *name); + void removeClockGroupsLogicallyExclusive(const char *name, + Sdc *sdc); + void removeClockGroupsPhysicallyExclusive(const char *name, + Sdc *sdc); + void removeClockGroupsAsynchronous(const char *name, + Sdc *sdc); void makeClockGroup(ClockGroups *clk_groups, - ClockSet *clks); + ClockSet *clks, + Sdc *sdc); void setClockSense(PinSet *pins, - ClockSet *clks, - ClockSense sense); + ClockSet *clks, + ClockSense sense, + Sdc *sdc); void setClockGatingCheck(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin); + const SetupHold *setup_hold, + float margin, + Sdc *sdc); void setClockGatingCheck(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + Sdc *sdc); void setClockGatingCheck(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value, + Sdc *sdc); void setClockGatingCheck(Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value); + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value, + Sdc *sdc); void setDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, - float margin); + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin, + Sdc *sdc); void removeDataCheck(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, + Sdc *sdc); // set_disable_timing cell [-from] [-to] // Disable all edges thru cell if from/to are null. // Bus and bundle ports are NOT supported. void disable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to, + Sdc *sdc); void removeDisable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to, + Sdc *sdc); // set_disable_timing liberty port. // Bus and bundle ports are NOT supported. - void disable(LibertyPort *port); - void removeDisable(LibertyPort *port); + void disable(LibertyPort *port, + Sdc *sdc); + void removeDisable(LibertyPort *port, + Sdc *sdc); // set_disable_timing port (top level instance port). // Bus and bundle ports are NOT supported. - void disable(Port *port); - void removeDisable(Port *port); + void disable(Port *port, + Sdc *sdc); + void removeDisable(Port *port, + Sdc *sdc); // set_disable_timing instance [-from] [-to]. // Disable all edges thru instance if from/to are null. // Bus and bundle ports are NOT supported. // Hierarchical instances are NOT supported. void disable(Instance *inst, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to, + Sdc *sdc); void removeDisable(Instance *inst, - LibertyPort *from, - LibertyPort *to); + LibertyPort *from, + LibertyPort *to, + Sdc *sdc); // set_disable_timing pin - void disable(Pin *pin); - void removeDisable(Pin *pin); + void disable(Pin *pin, + Sdc *sdc); + void removeDisable(Pin *pin, + Sdc *sdc); // set_disable_timing [get_timing_arc -of_objects instance]] - void disable(Edge *edge); - void removeDisable(Edge *edge); + void disable(Edge *edge, + Sdc *sdc); + void removeDisable(Edge *edge, + Sdc *sdc); // set_disable_timing [get_timing_arc -of_objects lib_cell]] - void disable(TimingArcSet *arc_set); - void removeDisable(TimingArcSet *arc_set); + void disable(TimingArcSet *arc_set, + Sdc *sdc); + void removeDisable(TimingArcSet *arc_set, + Sdc *sdc); // Edge is disabled by constant. - bool isDisabledConstant(Edge *edge); + [[nodiscard]] bool isDisabledConstant(Edge *edge, + const Mode *mode); // Return a set of constant pins that disabled edge. // Caller owns the returned set. - PinSet disabledConstantPins(Edge *edge); + PinSet disabledConstantPins(Edge *edge, + const Mode *mode); // Edge timing sense with propagated constants. - TimingSense simTimingSense(Edge *edge); + TimingSense simTimingSense(Edge *edge, + const Mode *mode); // Edge is disabled by set_disable_timing constraint. - bool isDisabledConstraint(Edge *edge); + [[nodiscard]] bool isDisabledConstraint(Edge *edge, + const Sdc *sdc); // Edge is disabled to break combinational loops. - bool isDisabledLoop(Edge *edge) const; + [[nodiscard]] bool isDisabledLoop(Edge *edge) const; // Edge is disabled internal bidirect output path. - bool isDisabledBidirectInstPath(Edge *edge) const; + [[nodiscard]] bool isDisabledBidirectInstPath(Edge *edge) const; // Edge is disabled bidirect net path. - bool isDisabledBidirectNetPath(Edge *edge) const; - bool isDisabledPresetClr(Edge *edge) const; + [[nodiscard]] bool isDisabledBidirectNetPath(Edge *edge) const; + [[nodiscard]] bool isDisabledPresetClr(Edge *edge) const; // Return a vector of graph edges that are disabled, sorted by // from/to vertex names. Caller owns the returned vector. - EdgeSeq disabledEdges(); - EdgeSeq disabledEdgesSorted(); - void disableClockGatingCheck(Instance *inst); - void disableClockGatingCheck(Pin *pin); - void removeDisableClockGatingCheck(Instance *inst); - void removeDisableClockGatingCheck(Pin *pin); + EdgeSeq disabledEdges(const Mode *mode); + EdgeSeq disabledEdgesSorted(const Mode *mode); + void disableClockGatingCheck(Instance *inst, + Sdc *sdc); + void disableClockGatingCheck(Pin *pin, + Sdc *sdc); + void removeDisableClockGatingCheck(Instance *inst, + Sdc *sdc); + void removeDisableClockGatingCheck(Pin *pin, + Sdc *sdc); void setLogicValue(Pin *pin, - LogicValue value); + LogicValue value, + Mode *mode); void setCaseAnalysis(Pin *pin, - LogicValue value); - void removeCaseAnalysis(Pin *pin); + LogicValue value, + Mode *mode); + void removeCaseAnalysis(Pin *pin, + Mode *mode); void 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, - float delay); + 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); void removeInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max, + Sdc *sdc); void 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, - float delay); + 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); void removeOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max); + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max, + Sdc *sdc); void makeFalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const char *comment, + Sdc *sdc); void makeMulticyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + const char *comment, + Sdc *sdc); void 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, - const char *comment); + float delay, + const char *comment, + Sdc *sdc); void makeGroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment); + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const char *comment, + Sdc *sdc); // Deprecated 10/24/2025 - bool isGroupPathName(const char *group_name) __attribute__ ((deprecated)); - bool isPathGroupName(const char *group_name) const; - StdStringSeq pathGroupNames() const; + bool isGroupPathName(const char *group_name, + const Sdc *sdc) __attribute__ ((deprecated)); + bool isPathGroupName(const char *group_name, + const Sdc *sdc) const; + StdStringSeq pathGroupNames(const Sdc *sdc) const; void resetPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max); + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + Sdc *sdc); // Make an exception -from specification. ExceptionFrom *makeExceptionFrom(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_rf); + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_rf, + const Sdc *sdc); void checkExceptionFromPins(ExceptionFrom *from, - const char *file, - int line) const; + const char *file, + int line, + const Sdc *sdc) const; void deleteExceptionFrom(ExceptionFrom *from); // Make an exception -through specification. ExceptionThru *makeExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf); + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf, + const Sdc *sdc); void deleteExceptionThru(ExceptionThru *thru); // Make an exception -to specification. ExceptionTo *makeExceptionTo(PinSet *to_pins, - ClockSet *to_clks, - InstanceSet *to_insts, - const RiseFallBoth *rf, - const RiseFallBoth *end_rf); + ClockSet *to_clks, + InstanceSet *to_insts, + const RiseFallBoth *rf, + const RiseFallBoth *end_rf, + const Sdc *sdc); void checkExceptionToPins(ExceptionTo *to, - const char *file, int) const; + const char *file, + int line, + const Sdc *sdc) const; void deleteExceptionTo(ExceptionTo *to); + InstanceSet findRegisterInstances(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, - bool latches); + bool latches, + const Mode *mode); PinSet findRegisterDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool registers, - bool latches); + bool latches, + const Mode *mode); PinSet findRegisterClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool registers, - bool latches); + bool latches, + const Mode *mode); PinSet findRegisterAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool registers, - bool latches); + bool latches, + const Mode *mode); PinSet findRegisterOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool registers, - bool latches); + bool latches, + const Mode *mode); PinSet findFaninPins(PinSeq *to, bool flat, bool startpoints_only, int inst_levels, int pin_levels, bool thru_disabled, - bool thru_constants); + bool thru_constants, + const Mode *mode); InstanceSet findFaninInstances(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants); + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode); PinSet findFanoutPins(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants); + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode); InstanceSet findFanoutInstances(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants); + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants, + const Mode *mode); // The set of clocks that arrive at vertex in the clock network. - ClockSet clocks(const Pin *pin); + ClockSet clocks(const Pin *pin, + const Mode *mode); // Clock domains for a pin. - ClockSet clockDomains(const Pin *pin); + ClockSet clockDomains(const Pin *pin, + const Mode *mode); - void checkSlewLimitPreamble(); - // Return pins with the min/max slew limit slack. + //////////////////////////////////////////////////////////////// // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkSlewLimits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max); - void reportSlewLimitShortHeader(); - void reportSlewLimitShort(Pin *pin, - const Corner *corner, - const MinMax *min_max); - void reportSlewLimitVerbose(Pin *pin, - const Corner *corner, - const MinMax *min_max); - // requires checkSlewLimitPreamble() + void reportSlewChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max); + void checkSlewsPreamble(); + // requires checkSlewsPreamble() void checkSlew(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - const Corner *&corner1, - const RiseFall *&tr, - Slew &slew, - float &limit, - float &slack); + const SceneSeq &scenes, + const MinMax *min_max, + bool check_clks, + // Return values. + Slew &slew, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&Scene); void maxSlewCheck(// Return values. const Pin *&pin, Slew &slew, float &slack, float &limit); void findSlewLimit(const LibertyPort *port, - const Corner *corner, + const Scene *scene, const MinMax *min_max, // Return values. float &limit, bool &exists); + size_t maxSlewViolationCount(); - void checkFanoutLimitPreamble(); - // Return pins with the min/max fanout limit slack. - // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkFanoutLimits(Net *net, - bool violators, - const MinMax *min_max); - void reportFanoutLimitShortHeader(); - void reportFanoutLimitShort(Pin *pin, - const MinMax *min_max); - void reportFanoutLimitVerbose(Pin *pin, - const MinMax *min_max); - // requires checkFanoutLimitPreamble() + //////////////////////////////////////////////////////////////// + // net == nullptr to check all. + void reportFanoutChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max); + void checkFanoutPreamble(); + // requires checkFanoutPreamble() void checkFanout(const Pin *pin, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack); - void maxFanoutCheck(// Return values. - const Pin *&pin, - float &fanout, - float &slack, - float &limit); - - void checkCapacitanceLimitPreamble(); - // Return pins with the min/max slew limit slack. + const Mode *mode, + const MinMax *min_max, + // Return values. + float &fanout, + float &limit, + float &slack); + // Return the pin etc with max fanout check min slack. + void maxFanoutMinSlackPin(const ModeSeq &modes, + // Return values. + const Pin *&pin, + float &fanout, + float &limit, + float &slack, + const Mode *&mode); + size_t fanoutViolationCount(const MinMax *min_max, + const ModeSeq &modes); + + //////////////////////////////////////////////////////////////// // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkCapacitanceLimits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max); - void reportCapacitanceLimitShortHeader(); - void reportCapacitanceLimitShort(Pin *pin, - const Corner *corner, - const MinMax *min_max); - void reportCapacitanceLimitVerbose(Pin *pin, - const Corner *corner, - const MinMax *min_max); + void reportCapacitanceChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max); + size_t maxCapacitanceViolationCount(); + void checkCapacitancesPreamble(const SceneSeq &scenes); // requires checkCapacitanceLimitPreamble() void checkCapacitance(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&tr, - float &capacitance, - float &limit, - float &slack); + const SceneSeq &scenes, + const MinMax *min_max, + // Return values. + float &capacitance, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&scene); void maxCapacitanceCheck(// Return values. const Pin *&pin, float &capacitance, float &slack, float &limit); - // Min pulse width check with the least slack. - // corner=nullptr checks all corners. - MinPulseWidthCheck *minPulseWidthSlack(const Corner *corner); - // All violating min pulse width checks. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &minPulseWidthViolations(const Corner *corner); - // Min pulse width checks for pins. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &minPulseWidthChecks(PinSeq *pins, - const Corner *corner); - // All min pulse width checks. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &minPulseWidthChecks(const Corner *corner); - void reportMpwChecks(MinPulseWidthCheckSeq *checks, - bool verbose); - void reportMpwCheck(MinPulseWidthCheck *check, - bool verbose); - - // Min period check with the least slack. - MinPeriodCheck *minPeriodSlack(); - // All violating min period checks. - MinPeriodCheckSeq &minPeriodViolations(); - void reportChecks(MinPeriodCheckSeq *checks, - bool verbose); - void reportCheck(MinPeriodCheck *check, - bool verbose); - - // Max skew check with the least slack. - MaxSkewCheck *maxSkewSlack(); - // All violating min period checks. - MaxSkewCheckSeq &maxSkewViolations(); - void reportChecks(MaxSkewCheckSeq *checks, - bool verbose); - void reportCheck(MaxSkewCheck *check, - bool verbose); + //////////////////////////////////////////////////////////////// + void reportMinPulseWidthChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes); + + //////////////////////////////////////////////////////////////// + void reportMinPeriodChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes); + //////////////////////////////////////////////////////////////// + void reportMaxSkewChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes); //////////////////////////////////////////////////////////////// // User visible but non SDC commands. - // Instance specific process/voltage/temperature. - // Defaults to operating condition if instance is not annotated. - const Pvt *pvt(Instance *inst, - const MinMax *min_max); - void setPvt(Instance *inst, - const MinMaxAll *min_max, - float process, - float voltage, - float temperature); - // Pvt may be shared among multiple instances. - void setPvt(const Instance *inst, - const MinMaxAll *min_max, - const Pvt &pvt); - void setVoltage(const MinMax *min_max, - float voltage); - void setVoltage(const Net *net, - const MinMax *min_max, - float voltage); // Clear all state except network. virtual void clear(); - // Remove all constraints. - virtual void removeConstraints(); - // Notify the sta that the constraints have changed directly rather - // than thru this sta API. - virtual void constraintsChanged(); // Namespace used by command interpreter. CmdNamespace cmdNamespace(); void setCmdNamespace(CmdNamespace namespc); - OperatingConditions *operatingConditions(const MinMax *min_max) const; + OperatingConditions *operatingConditions(const MinMax *min_max, + const Sdc *sdc) const; // Set the delay on a timing arc. // Required/arrival times are incrementally updated. void setArcDelay(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMaxAll *min_max, - ArcDelay delay); + TimingArc *arc, + const Scene *scene, + const MinMaxAll *min_max, + ArcDelay delay); // Set annotated slew on a vertex for delay calculation. void setAnnotatedSlew(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - const RiseFallBoth *rf, - float slew); + const Scene *scene, + const MinMaxAll *min_max, + const RiseFallBoth *rf, + float slew); void writeSdf(const char *filename, - const Corner *corner, - char divider, + const Scene *scene, + char divider, bool include_typ, - int digits, - bool gzip, - bool no_timestamp, - bool no_version); + int digits, + bool gzip, + bool no_timestamp, + bool no_version); // Remove all delay and slew annotations. void removeDelaySlewAnnotations(); + // Instance specific process/voltage/temperature. + // Defaults to operating condition if instance is not annotated. + const Pvt *pvt(Instance *inst, + const MinMax *min_max, + Sdc *sdc); + void setPvt(Instance *inst, + const MinMaxAll *min_max, + float process, + float voltage, + float temperature, + Sdc *sdc); + // Pvt may be shared among multiple instances. + void setPvt(const Instance *inst, + const MinMaxAll *min_max, + const Pvt &pvt, + Sdc *sdc); + void setVoltage(const MinMax *min_max, + float voltage, + Sdc *sdc); + void setVoltage(const Net *net, + const MinMax *min_max, + float voltage, + Sdc *sdc); - virtual CheckErrorSeq &checkTiming(bool no_input_delay, - bool no_output_delay, - bool reg_multiple_clks, - bool reg_no_clks, - bool unconstrained_endpoints, - bool loops, - bool generated_clks); + CheckErrorSeq &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); // Path from/thrus/to filter. // from/thrus/to are owned and deleted by Search. // PathEnds in the returned PathEndSeq are owned by Search PathGroups // and deleted on next call. - virtual PathEndSeq findPathEnds(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool unconstrained, - // Use corner nullptr to report timing - // for all corners. - const Corner *corner, - // max for setup checks. - // min for hold checks. - // min_max for setup and hold checks. - const MinMaxAll *min_max, - // Number of path ends to report in - // each group. - int group_path_count, - // Number of paths to report for - // each endpoint. - int endpoint_path_count, - // endpoint_path_count paths report paths with - // unique pins. - bool unique_pins, - // endpoint_path_count paths report paths with - // unique pins and rise/fall edges. - bool unique_edges, - // Min/max bounds for slack of - // returned path ends. - float slack_min, - float slack_max, - // Sort path ends by slack ignoring path groups. - bool sort_by_slack, - // Path groups to report. - // Null or empty list reports all groups. - PathGroupNameSet *group_names, - // Predicates to filter the type of path - // ends returned. - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold); + PathEndSeq findPathEnds(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + const SceneSeq &scenes, + // max for setup checks. + // min for hold checks. + // min_max for setup and hold checks. + const MinMaxAll *min_max, + // Number of path ends to report in + // each group. + int group_path_count, + // Number of paths to report for + // each endpoint. + int endpoint_path_count, + // endpoint_path_count paths report unique pins + // without rise/fall variations. + bool unique_pins, + // endpoint_path_count paths report paths with + // unique pins and rise/fall edges. + bool unique_edges, + // Min/max bounds for slack of + // returned path ends. + float slack_min, + float slack_max, + // Sort path ends by slack ignoring path groups. + bool sort_by_slack, + // Path groups to report. + // Empty list reports all groups. + StdStringSeq &group_names, + // Predicates to filter the type of path + // ends returned. + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold); void setReportPathFormat(ReportPathFormat format); void setReportPathFieldOrder(StringSeq *field_names); void setReportPathFields(bool report_input_pin, bool report_hier_pins, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout, - bool report_src_attr); + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout, + bool report_src_attr); ReportField *findReportPathField(const char *name); void setReportPathDigits(int digits); void setReportPathNoSplit(bool no_split); @@ -865,7 +988,7 @@ public: // Previous path end is used to detect path group changes // so headers are reported by group. void reportPathEnd(PathEnd *end, - PathEnd *prev_end, + PathEnd *prev_end, bool last); void reportPathEnd(PathEnd *end); void reportPathEnds(PathEndSeq *ends); @@ -874,19 +997,20 @@ public: // Report clk skews for clks. void reportClkSkew(ConstClockSeq &clks, - const Corner *corner, - const SetupHold *setup_hold, + const SceneSeq &scenes, + const SetupHold *setup_hold, bool include_internal_latency, - int digits); + int digits); float findWorstClkSkew(const SetupHold *setup_hold, bool include_internal_latency); void reportClkLatency(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency, int digits); // Find min/max/rise/fall delays for clk. ClkDelays findClkDelays(const Clock *clk, + const Scene *scene, bool include_internal_latency); // Update arrival times for all pins. @@ -905,46 +1029,41 @@ public: void arrivalsInvalid(); PinSet startpointPins(); PinSet endpointPins(); - VertexSet *endpoints(); + VertexSet &endpoints(); int endpointViolationCount(const MinMax *min_max); // Find all required times after updateTiming(). void findRequireds(); std::string reportDelayCalc(Edge *edge, TimingArc *arc, - const Corner *corner, + const Scene *scene, const MinMax *min_max, int digits); - void writeSdc(const char *filename, - // Map hierarchical pins and instances to leaf pins and instances. - bool leaf, - // Replace non-sdc get functions with OpenSTA equivalents. - bool native, - int digits, + void writeSdc(const Sdc *sdc, + const char *filename, + // Map hierarchical pins and instances to leaf pins and instances. + bool leaf, + // Replace non-sdc get functions with OpenSTA equivalents. + bool native, + int digits, bool gzip, - bool no_timestamp); + bool no_timestamp); // The sum of all negative endpoints slacks. // Incrementally updated. Slack totalNegativeSlack(const MinMax *min_max); - Slack totalNegativeSlack(const Corner *corner, - const MinMax *min_max); + Slack totalNegativeSlack(const Scene *scene, + const MinMax *min_max); // Worst endpoint slack and vertex. // Incrementally updated. Slack worstSlack(const MinMax *min_max); void worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); - void worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); - VertexPathIterator *vertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap); - VertexPathIterator *vertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max); + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); Path *vertexWorstArrivalPath(Vertex *vertex, const RiseFall *rf, const MinMax *min_max); @@ -971,93 +1090,75 @@ public: // update timing to the level of the vertex. They do NOT do multiple // passes required propagate arrivals around latch loops. // See Sta::updateTiming() to propagate arrivals around latch loops. - Arrival vertexArrival(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap, - const MinMax *min_max); - // Min/max across all clock tags. - Arrival vertexArrival(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap); - Arrival vertexArrival(Vertex *vertex, - const MinMax *min_max); - Arrival pinArrival(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max); - Required vertexRequired(Vertex *vertex, - const MinMax *min_max); - Required vertexRequired(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max); - // Min/max across all clock tags. - Required vertexRequired(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap); - Required vertexRequired(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap); - - Slack netSlack(const Net *net, - const MinMax *min_max); - Slack pinSlack(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max); - Slack pinSlack(const Pin *pin, - const MinMax *min_max); + Arrival arrival(const Pin *pin, + const RiseFallBoth *rf, + const MinMax *min_max); + Arrival arrival(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + + Required required(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + + Slack slack(const Net *net, + const MinMax *min_max); + Slack slack(const Pin *pin, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + + Slack slack(Vertex *vertex, + const MinMax *min_max); + Slack slack(Vertex *vertex, + const RiseFall *rf, + const MinMax *min_max); + Slack slack(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + + void slacks(Vertex *vertex, + Slack (&slacks)[RiseFall::index_count][MinMax::index_count]); // Worst slack for an endpoint in a path group. Slack endpointSlack(const Pin *pin, - const std::string &path_group_name, - const MinMax *min_max); - Slack vertexSlack(Vertex *vertex, - const MinMax *min_max); - Slack vertexSlack(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max); - // Slack with respect to clk_edge. - Slack vertexSlack(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap); - // Min slack across all clock tags. - Slack vertexSlack(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap); - void vertexSlacks(Vertex *vertex, - Slack (&slacks)[RiseFall::index_count][MinMax::index_count]); - // Slew for one corner. - Slew vertexSlew(Vertex *vertex, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max); - // Slew for one delay calc analysis pt (corner min/max). - Slew vertexSlew(Vertex *vertex, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap); - // Slew across all corners. - Slew vertexSlew(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max); - Slew vertexSlew(Vertex *vertex, - const MinMax *min_max); + const std::string &path_group_name, + const MinMax *min_max); + + void reportArrivalWrtClks(const Pin *pin, + const Scene *scene, + int digits); + void reportRequiredWrtClks(const Pin *pin, + const Scene *scene, + int digits); + void reportSlackWrtClks(const Pin *pin, + const Scene *scene, + int digits); + + Slew slew(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max); + ArcDelay arcDelay(Edge *edge, - TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap); + TimingArc *arc, + DcalcAPIndex ap_index); // True if the timing arc has been back-annotated. bool arcDelayAnnotated(Edge *edge, - TimingArc *arc, - DcalcAnalysisPt *dcalc_ap); + TimingArc *arc, + const Scene *scene, + const MinMax *min_max); // Set/unset the back-annotation flag for a timing arc. void setArcDelayAnnotated(Edge *edge, - TimingArc *arc, - DcalcAnalysisPt *dcalc_ap, - bool annotated); + TimingArc *arc, + const Scene *scene, + const MinMax *min_max, + bool annotated); // Make sure levels are up to date and return vertex level. Level vertexLevel(Vertex *vertex); GraphLoopSeq &graphLoops(); - PathAnalysisPt *pathAnalysisPt(Path *path); - DcalcAnalysisPt *pathDcalcAnalysisPt(Path *path); TagIndex tagCount() const; TagGroupIndex tagGroupCount() const; int clkInfoCount() const; @@ -1065,90 +1166,91 @@ public: int vertexPathCount(Vertex *vertex) const; Vertex *maxPathCountVertex() const; - LogicValue simLogicValue(const Pin *pin); // Propagate liberty constant functions and pins tied high/low through - // combinational logic and registers. + // combinational logic and registers. This is mode/sdc independent. + // Used by OpenROAD/Restructure.cpp void findLogicConstants(); - // Clear the constants found by findLogicConstants so they do not interfere - // with normal constant propagate for timing. + LogicValue simLogicValue(const Pin *pin, + const Mode *mode); + // Clear propagated sim constants. void clearLogicConstants(); // Instances sorted by max driver pin slew. InstanceSeq slowDrivers(int count); - // Make parasitic analysis points. - // per_corner ap_count - // false 2 - // true corners*2 - void setParasiticAnalysisPts(bool per_corner); + Parasitics *makeConcreteParasitics(std::string name, + std::string filename); // Annotate hierarchical "instance" with parasitics. // The parasitic analysis point is ap_name. // The parasitic memory footprint is much smaller if parasitic // networks (dspf) are reduced and deleted after reading each net // with reduce_to and delete_after_reduce. // Return true if successful. - bool readSpef(const char *filename, - Instance *instance, - const Corner *corner, + bool readSpef(const std::string &name, + const std::string &filename, + Instance *instance, + Scene *scene, const MinMaxAll *min_max, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce); - void reportParasiticAnnotation(bool report_unannotated, - const Corner *corner); + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce); + Parasitics *findParasitics(const std::string &name); + void reportParasiticAnnotation(const std::string &spef_name, + bool report_unannotated); // Parasitics. void 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; void 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; void 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); void 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); void deleteParasitics(); Parasitic *makeParasiticNetwork(const Net *net, bool includes_pin_caps, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); //////////////////////////////////////////////////////////////// // TCL network edit function support. virtual Instance *makeInstance(const char *name, - LibertyCell *cell, - Instance *parent); + LibertyCell *cell, + Instance *parent); virtual void deleteInstance(Instance *inst); // replace_cell virtual void replaceCell(Instance *inst, - Cell *to_cell); + Cell *to_cell); virtual void replaceCell(Instance *inst, - LibertyCell *to_lib_cell); + LibertyCell *to_lib_cell); virtual Net *makeNet(const char *name, - Instance *parent); + Instance *parent); virtual void deleteNet(Net *net); // connect_net virtual void connectPin(Instance *inst, - Port *port, - Net *net); + Port *port, + Net *net); virtual void connectPin(Instance *inst, - LibertyPort *port, - Net *net); + LibertyPort *port, + Net *net); // disconnect_net virtual void disconnectPin(Pin *pin); virtual void makePortPin(const char *port_name, @@ -1163,12 +1265,12 @@ public: // Replace the instance cell with to_cell. // equivCells(from_cell, to_cell) must be true. virtual void replaceEquivCellBefore(const Instance *inst, - const LibertyCell *to_cell); + const LibertyCell *to_cell); virtual void replaceEquivCellAfter(const Instance *inst); // Replace the instance cell with to_cell. // equivCellPorts(from_cell, to_cell) must be true. virtual void replaceCellBefore(const Instance *inst, - const LibertyCell *to_cell); + const LibertyCell *to_cell); virtual void replaceCellAfter(const Instance *inst); virtual void makePortPinAfter(Pin *pin); virtual void connectPinAfter(const Pin *pin); @@ -1179,14 +1281,19 @@ public: //////////////////////////////////////////////////////////////// - void ensureClkNetwork(); - void clkPinsInvalid(); + void ensureClkNetwork(const Mode *mode); + void clkPinsInvalid(const Mode *mode); // The following functions assume ensureClkNetwork() has been called. - bool isClock(const Pin *pin) const; - bool isClock(const Net *net) const; - bool isIdealClock(const Pin *pin) const; - bool isPropagatedClock(const Pin *pin) const; - const PinSet *pins(const Clock *clk); + bool isClock(const Pin *pin, + const Mode *mode) const; + bool isClock(const Net *net, + const Mode *mode) const; + bool isIdealClock(const Pin *pin, + const Mode *mode) const; + bool isPropagatedClock(const Pin *pin, + const Mode *mode) const; + const PinSet *pins(const Clock *clk, + const Mode *mode); //////////////////////////////////////////////////////////////// @@ -1200,11 +1307,7 @@ public: // Ensure that the timing graph has been built. Graph *ensureGraph(); void ensureClkArrivals(); - Corner *cmdCorner() const; - void setCmdCorner(Corner *corner); - Corner *findCorner(const char *corner_name); - bool multiCorner(); - virtual void makeCorners(StringSet *corner_names); + // Find all arc delays and vertex slews with delay calculator. virtual void findDelays(); // Find arc delays and vertex slews thru to level of to_vertex. @@ -1222,7 +1325,7 @@ public: void setArcDelayCalc(const char *delay_calc_name); void setDebugLevel(const char *what, - int level); + int level); // Delays and arrivals downsteam from inst are invalid. void delaysInvalidFrom(const Instance *inst); @@ -1235,33 +1338,47 @@ public: void delaysInvalidFromFanin(const Pin *pin); void delaysInvalidFromFanin(Vertex *vertex); void replaceCellPinInvalidate(const LibertyPort *from_port, - Vertex *vertex, - const LibertyCell *to_cell); + Vertex *vertex, + const LibertyCell *to_cell); // Power API. + void reportPowerDesign(const Scene *scene, + int digits); + void reportPowerInsts(const InstanceSeq &insts, + const Scene *scene, + int digits); + void reportPowerHighestInsts(size_t count, + const Scene *scene, + int digits); + void reportPowerDesignJson(const Scene *scene, + int digits); + void reportPowerInstsJson(const InstanceSeq &insts, + const Scene *scene, + int digits); Power *power() { return power_; } const Power *power() const { return power_; } - void power(const Corner *corner, - // Return values. - PowerResult &total, - PowerResult &sequential, - PowerResult &combinational, - PowerResult &clock, - PowerResult ¯o, - PowerResult &pad); + void power(const Scene *scene, + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult &clock, + PowerResult ¯o, + PowerResult &pad); PowerResult power(const Instance *inst, - const Corner *corner); - PwrActivity activity(const Pin *pin); + const Scene *scene); + PwrActivity activity(const Pin *pin, + const Scene *scene); void writeTimingModel(const char *lib_name, const char *cell_name, const char *filename, - const Corner *corner); + const Scene *scene); // Find equivalent cells in equiv_libs. // Optionally add mappings for cells in map_libs. void makeEquivCells(LibertyLibrarySeq *equiv_libs, - LibertyLibrarySeq *map_libs); + LibertyLibrarySeq *map_libs); LibertyCellSeq *equivCells(LibertyCell *cell); void writePathSpice(Path *path, @@ -1307,10 +1424,6 @@ public: // Enable/disable timing from bidirect pins back into the instance. bool bidirectInstPathsEnabled() const; void setBidirectInstPathsEnabled(bool enabled); - // TCL variable sta_bidirect_net_paths_enabled. - // Enable/disable timing from bidirect driver pins to their own loads. - bool bidirectNetPathsEnabled() const; - void setBidirectNetPathsEnabled(bool enabled); // TCL variable sta_recovery_removal_checks_enabled. bool recoveryRemovalChecksEnabled() const; void setRecoveryRemovalChecksEnabled(bool enabled); @@ -1345,21 +1458,17 @@ protected: virtual void makeUnits(); virtual void makeNetwork(); virtual void makeSdcNetwork(); - virtual void makeSdc(); virtual void makeGraph(); - virtual void makeCorners(); + virtual void makeDefaultScene(); virtual void makeLevelize(); - virtual void makeParasitics(); virtual void makeArcDelayCalc(); virtual void makeGraphDelayCalc(); - virtual void makeSim(); virtual void makeSearch(); virtual void makeLatches(); - virtual void makeClkNetwork(); virtual void makeCheckTiming(); - virtual void makeCheckSlewLimits(); - virtual void makeCheckFanoutLimits(); - virtual void makeCheckCapacitanceLimits(); + virtual void makeCheckSlews(); + virtual void makeCheckFanouts(); + virtual void makeCheckCapacitances(); virtual void makeCheckMinPulseWidths(); virtual void makeCheckMinPeriods(); virtual void makeCheckMaxSkews(); @@ -1370,101 +1479,133 @@ protected: NetworkEdit *networkCmdEdit(); LibertyLibrary *readLibertyFile(const char *filename, - Corner *corner, - const MinMaxAll *min_max, - bool infer_latches); + Scene *scene, + const MinMaxAll *min_max, + bool infer_latches); // Allow external Liberty reader to parse forms not used by Sta. virtual LibertyLibrary *readLibertyFile(const char *filename, - bool infer_latches); + bool infer_latches); void delayCalcPreamble(); void delaysInvalidFrom(const Port *port); void delaysInvalidFromFanin(const Port *port); void deleteEdge(Edge *edge); void netParasiticCaps(Net *net, - const RiseFall *rf, - const MinMax *min_max, - float &pin_cap, - float &wire_cap) const; + const RiseFall *rf, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const; const Pin *findNetParasiticDrvrPin(const Net *net) const; void exprConstantPins(FuncExpr *expr, - const Instance *inst, - PinSet &pins); - Slack vertexSlack1(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap); + const Instance *inst, + const Mode *mode, + // Return value. + PinSet &pins); void findRequired(Vertex *vertex); - Required vertexRequired(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap, - const MinMax *min_max); + + void reportDelaysWrtClks(const Pin *pin, + const Scene *scene, + int digits, + PathDelayFunc get_path_delay); + void reportDelaysWrtClks(Vertex *vertex, + const Scene *scene, + int digits, + PathDelayFunc get_path_delay); + void reportDelaysWrtClks(Vertex *vertex, + const ClockEdge *clk_edge, + const Scene *scene, + int digits, + PathDelayFunc get_path_delay); + RiseFallMinMaxDelay findDelaysWrtClks(Vertex *vertex, + const ClockEdge *clk_edge, + const Scene *scene, + PathDelayFunc get_path_delay); + std::string formatDelay(const RiseFall *rf, + const MinMax *min_max, + const RiseFallMinMaxDelay &delays, + int digits); + void connectDrvrPinAfter(Vertex *vertex); void connectLoadPinAfter(Vertex *vertex); Path *latchEnablePath(Path *q_path, - Edge *d_q_edge, - const ClockEdge *en_clk_edge); + Edge *d_q_edge, + const ClockEdge *en_clk_edge); void clockSlewChanged(Clock *clk); - void minPulseWidthPreamble(); - void minPeriodPreamble(); void maxSkewPreamble(); bool idealClockMode(); void disableAfter(); void findFaninPins(Vertex *vertex, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - PinSet &fanin, - SearchPred &pred); + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + PinSet &fanin, + SearchPred &pred, + const Mode *mode); void findFaninPins(Vertex *to, - bool flat, - int inst_levels, - int pin_levels, - VertexSet &visited, - SearchPred *pred, - int inst_level, - int pin_level); + bool flat, + int inst_levels, + int pin_levels, + VertexSet &visited, + SearchPred *pred, + int inst_level, + int pin_level, + const Mode *mode); void findFanoutPins(Vertex *vertex, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - PinSet &fanout, - SearchPred &pred); + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + PinSet &fanout, + SearchPred &pred, + const Mode *mode); void findFanoutPins(Vertex *from, - bool flat, - int inst_levels, - int pin_levels, - VertexSet &visited, - SearchPred *pred, - int inst_level, - int pin_level); - void findRegisterPreamble(); + bool flat, + int inst_levels, + int pin_levels, + VertexSet &visited, + SearchPred *pred, + int inst_level, + int pin_level, + const Mode *mode); + void findRegisterPreamble(const Mode *mode); bool crossesHierarchy(Edge *edge) const; void readLibertyAfter(LibertyLibrary *liberty, - Corner *corner, - const MinMax *min_max); + Scene *scene, + const MinMax *min_max); void powerPreamble(); + void powerPreamble(const Scene *scene); virtual void replaceCell(Instance *inst, Cell *to_cell, LibertyCell *to_lib_cell); - void sdcChangedGraph(); - void ensureGraphSdcAnnotated(); - CornerSeq makeCornerSeq(Corner *corner) const; - void makeParasiticAnalysisPts(); void clkSkewPreamble(); void setCmdNamespace1(CmdNamespace namespc); void setThreadCount1(int thread_count); + void updateLibertyScenes(); + void updateSceneLiberty(Scene *scene, + const StdStringSeq &liberty_files, + const MinMax *min_max); + LibertyLibrary *findLibertyFileBasename(const std::string &filename) const; + + Scene *makeScene(const std::string &name, + Mode *mode, + Parasitics *parasitics_min, + Parasitics *parasitics_max); + Scene *makeScene(const std::string &name, + Mode *mode, + Parasitics *parasitics); + void deleteScenes(); + Scene *cmd_scene_; CmdNamespace cmd_namespace_; Instance *current_instance_; - Corner *cmd_corner_; + SceneNameMap scene_name_map_; + ModeNameMap mode_name_map_; + ParasiticsNameMap parasitics_name_map_; VerilogReader *verilog_reader_; CheckTiming *check_timing_; - CheckSlewLimits *check_slew_limits_; - CheckFanoutLimits *check_fanout_limits_; - CheckCapacitanceLimits *check_capacitance_limits_; + CheckSlews *check_slews_; + CheckFanouts *check_fanouts_; + CheckCapacitances *check_capacitances_; CheckMinPulseWidths *check_min_pulse_widths_; CheckMinPeriods *check_min_periods_; CheckMaxSkews *check_max_skews_; @@ -1474,9 +1615,6 @@ protected: Tcl_Interp *tcl_interp_; bool update_genclks_; EquivCells *equiv_cells_; - bool graph_sdc_annotated_; - bool parasitics_per_corner_; - bool parasitics_per_min_max_; Properties properties_; // Singleton sta used by tcl command interpreter. diff --git a/include/sta/StaMain.hh b/include/sta/StaMain.hh index 92a9bbe46..0d34ff079 100644 --- a/include/sta/StaMain.hh +++ b/include/sta/StaMain.hh @@ -33,16 +33,16 @@ class Sta; // Parse command line argument int staTclAppInit(int argc, - char *argv[], - const char *init_filename, - Tcl_Interp *interp); + char *argv[], + const char *init_filename, + Tcl_Interp *interp); // Sta initialization. // Makes the Sta object and registers TCL commands. void initSta(int argc, - char *argv[], - Tcl_Interp *interp); + char *argv[], + Tcl_Interp *interp); // TCL init files are encoded into the string init using the three // digit decimal equivalent for each ascii character. This function @@ -51,28 +51,26 @@ initSta(int argc, // separate files that have to be located and loaded at run time. void evalTclInit(Tcl_Interp *interp, - const char *inits[]); + const char *inits[]); char * unencode(const char *inits[]); bool findCmdLineFlag(int &argc, - char *argv[], - const char *flag); + char *argv[], + const char *flag); char * findCmdLineKey(int &argc, - char *argv[], - const char *key); + char *argv[], + const char *key); int parseThreadsArg(int &argc, - char *argv[]); + char *argv[]); int sourceTclFile(const char *filename, - bool echo, - bool verbose, - Tcl_Interp *interp); -bool -is_regular_file(const char *filename); + bool echo, + bool verbose, + Tcl_Interp *interp); } // namespace diff --git a/include/sta/StaState.hh b/include/sta/StaState.hh index cce271be2..7d6ce5a70 100644 --- a/include/sta/StaState.hh +++ b/include/sta/StaState.hh @@ -24,6 +24,10 @@ #pragma once +#include + +#include "Scene.hh" + namespace sta { class Report; @@ -32,8 +36,6 @@ class Units; class Network; class NetworkEdit; class NetworkReader; -class Sdc; -class Corners; class Graph; class Edge; class Levelize; @@ -43,10 +45,12 @@ class Parasitics; class ArcDelayCalc; class GraphDelayCalc; class Latches; -class ClkNetwork; class DispatchQueue; class Variables; +using ModeSeq = std::vector; +using ModeSet = std::set; + // Most STA components use functionality in other components. // This class simplifies the process of copying pointers to the // components. It is deliberately simple to minimize circular @@ -81,35 +85,38 @@ public: // Command network uses the SDC namespace. Network *cmdNetwork() { return cmd_network_; } Network *cmdNetwork() const { return cmd_network_; } - Sdc *sdc() { return sdc_; } - Sdc *sdc() const { return sdc_; } - Corners *corners() { return corners_; } - Corners *corners() const { return corners_; } Graph *graph() { return graph_; } Graph *graph() const { return graph_; } + Graph *&graphRef() { return graph_; } Levelize *levelize() { return levelize_; } Levelize *levelize() const { return levelize_; } - Parasitics *parasitics() { return parasitics_; } - Parasitics *parasitics() const { return parasitics_; } ArcDelayCalc *arcDelayCalc() { return arc_delay_calc_; } ArcDelayCalc *arcDelayCalc() const { return arc_delay_calc_; } GraphDelayCalc *graphDelayCalc() { return graph_delay_calc_; } GraphDelayCalc *graphDelayCalc() const { return graph_delay_calc_; } - Sim *sim() { return sim_; } - Sim *sim() const { return sim_; } Search *search() { return search_; } Search *search() const { return search_; } Latches *latches() { return latches_; } Latches *latches() const { return latches_; } - ClkNetwork *clkNetwork() { return clk_network_; } - ClkNetwork *clkNetwork() const { return clk_network_; } unsigned threadCount() const { return thread_count_; } float sigmaFactor() const { return sigma_factor_; } - bool crprActive() const; + bool crprActive(const Mode *mode) const; Variables *variables() { return variables_; } const Variables *variables() const { return variables_; } // Edge is default cond disabled by timing_disable_cond_default_arcs var. - bool isDisabledCondDefault(Edge *edge) const; + [[nodiscard]] bool isDisabledCondDefault(const Edge *edge) const; + + const SceneSeq &scenes() { return scenes_; } + const SceneSeq &scenes() const { return scenes_; } + bool multiScene() const { return scenes_.size() > 1; } + size_t scenePathCount() const; + DcalcAPIndex dcalcAnalysisPtCount() const; + + const SceneSet scenesSet(); + + ModeSeq &modes() { return modes_; } + const ModeSeq &modes() const { return modes_; } + bool multiMode() const { return modes_.size() > 1; } protected: Report *report_; @@ -119,17 +126,14 @@ protected: Network *sdc_network_; // Network used by command interpreter (SdcNetwork). Network *cmd_network_; - Sdc *sdc_; - Corners *corners_; + SceneSeq scenes_; + ModeSeq modes_; Graph *graph_; Levelize *levelize_; - Parasitics *parasitics_; ArcDelayCalc *arc_delay_calc_; GraphDelayCalc *graph_delay_calc_; - Sim *sim_; Search *search_; Latches *latches_; - ClkNetwork *clk_network_; Variables *variables_; int thread_count_; DispatchQueue *dispatch_queue_; diff --git a/include/sta/Stats.hh b/include/sta/Stats.hh index 7f10effeb..4d40594b8 100644 --- a/include/sta/Stats.hh +++ b/include/sta/Stats.hh @@ -35,8 +35,8 @@ class Report; class Stats { public: - explicit Stats(Debug *debug, - Report *report); + Stats(Debug *debug, + Report *report); void report(const char *step); private: diff --git a/include/sta/StringSeq.hh b/include/sta/StringSeq.hh index 453af7ef4..ca7c304f5 100644 --- a/include/sta/StringSeq.hh +++ b/include/sta/StringSeq.hh @@ -24,12 +24,14 @@ #pragma once +#include + #include "StringUtil.hh" -#include "Vector.hh" namespace sta { -typedef Vector StringSeq; +using StringSeq = std::vector; +using StdStringSeq = std::vector; void deleteContents(StringSeq *strings); diff --git a/include/sta/StringSet.hh b/include/sta/StringSet.hh index ae643e351..36fa7e67f 100644 --- a/include/sta/StringSet.hh +++ b/include/sta/StringSet.hh @@ -26,12 +26,12 @@ #include #include "StringUtil.hh" -#include "Set.hh" namespace sta { -typedef Set StringSet; -typedef std::set StdStringSet; +using StringSet = std::set; +using StdStringSet = std::set; +using StdStringSeq = std::vector; void deleteContents(StringSet *strings); diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index c858df5f4..9523670b7 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -27,15 +27,15 @@ #include #include #include +#include #include "Machine.hh" // __attribute__ -#include "Vector.hh" namespace sta { inline bool stringEq(const char *str1, - const char *str2) + const char *str2) { return strcmp(str1, str2) == 0; } @@ -43,15 +43,15 @@ stringEq(const char *str1, // Compare the first length characters. inline bool stringEq(const char *str1, - const char *str2, - size_t length) + const char *str2, + size_t length) { return strncmp(str1, str2, length) == 0; } inline bool stringEqIf(const char *str1, - const char *str2) + const char *str2) { return (str1 == nullptr && str2 == nullptr) || (str1 && str2 && strcmp(str1, str2) == 0); @@ -60,7 +60,7 @@ stringEqIf(const char *str1, // Case sensitive compare the beginning of str1 to str2. inline bool stringBeginEq(const char *str1, - const char *str2) + const char *str2) { return strncmp(str1, str2, strlen(str2)) == 0; } @@ -68,7 +68,7 @@ stringBeginEq(const char *str1, // Case insensitive compare the beginning of str1 to str2. inline bool stringBeginEqual(const char *str1, - const char *str2) + const char *str2) { return strncasecmp(str1, str2, strlen(str2)) == 0; } @@ -76,14 +76,14 @@ stringBeginEqual(const char *str1, // Case insensitive compare. inline bool stringEqual(const char *str1, - const char *str2) + const char *str2) { return strcasecmp(str1, str2) == 0; } inline bool stringEqualIf(const char *str1, - const char *str2) + const char *str2) { return (str1 == nullptr && str2 == nullptr) || (str1 && str2 && strcasecmp(str1, str2) == 0); @@ -91,14 +91,14 @@ stringEqualIf(const char *str1, inline bool stringLess(const char *str1, - const char *str2) + const char *str2) { return strcmp(str1, str2) < 0; } inline bool stringLessIf(const char *str1, - const char *str2) + const char *str2) { return (str1 == nullptr && str2 != nullptr) || (str1 != nullptr && str2 != nullptr && strcmp(str1, str2) < 0); @@ -108,7 +108,7 @@ class CharPtrLess { public: bool operator()(const char *string1, - const char *string2) const + const char *string2) const { return stringLess(string1, string2); } @@ -119,7 +119,7 @@ class CharPtrCaseLess { public: bool operator()(const char *string1, - const char *string2) const + const char *string2) const { return strcasecmp(string1, string2) < 0; } @@ -129,7 +129,7 @@ class StringLessIf { public: bool operator()(const char *string1, - const char *string2) const + const char *string2) const { return stringLessIf(string1, string2); } @@ -141,7 +141,7 @@ stringCopy(const char *str); inline void stringAppend(char *&str1, - const char *str2) + const char *str2) { strcpy(str1, str2); str1 += strlen(str2); @@ -164,17 +164,17 @@ isDigits(const char *str); // Caller owns returned string. char * stringPrint(const char *fmt, - ...) __attribute__((format (printf, 1, 2))); + ...) __attribute__((format (printf, 1, 2))); std::string stdstrPrint(const char *fmt, - ...) __attribute__((format (printf, 1, 2))); + ...) __attribute__((format (printf, 1, 2))); char * stringPrintArgs(const char *fmt, - va_list args); + va_list args); void stringPrint(std::string &str, - const char *fmt, - ...) __attribute__((format (printf, 2, 3))); + const char *fmt, + ...) __attribute__((format (printf, 2, 3))); // Formated append to std::string. void stringAppend(std::string &str, @@ -184,7 +184,7 @@ stringAppend(std::string &str, // Print to a temporary string. char * stringPrintTmp(const char *fmt, - ...) __attribute__((format (printf, 1, 2))); + ...) __attribute__((format (printf, 1, 2))); char * makeTmpString(size_t length); @@ -199,7 +199,7 @@ isTmpString(const char *str); void trimRight(std::string &str); -typedef Vector StringVector; +using StringVector = std::vector; void split(const std::string &text, diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 40dd60c68..a392e7d63 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -26,9 +26,9 @@ #include #include +#include #include "MinMax.hh" -#include "Vector.hh" #include "Transition.hh" #include "LibertyClass.hh" #include "TimingModel.hh" @@ -42,10 +42,10 @@ class Table; class OutputWaveforms; class Table1; -typedef Vector FloatSeq; -typedef Vector FloatTable; -typedef Vector Table1Seq; -typedef Table1 Waveform; +using FloatSeq = std::vector; +using FloatTable = std::vector; +using Table1Seq = std::vector; +using Waveform = Table1; TableAxisVariable stringTableAxisVariable(const char *variable); @@ -53,16 +53,16 @@ const char * tableVariableString(TableAxisVariable variable); const Unit * tableVariableUnit(TableAxisVariable variable, - const Units *units); + const Units *units); class GateTableModel : public GateTimingModel { public: GateTableModel(LibertyCell *cell, TableModel *delay_model, - TableModel *delay_sigma_models[EarlyLate::index_count], - TableModel *slew_model, - TableModel *slew_sigma_models[EarlyLate::index_count], + TableModel *delay_sigma_models[EarlyLate::index_count], + TableModel *slew_model, + TableModel *slew_sigma_models[EarlyLate::index_count], ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms); virtual ~GateTableModel(); @@ -99,19 +99,19 @@ public: protected: void maxCapSlew(float in_slew, - const Pvt *pvt, - float &slew, - float &cap) const; + const Pvt *pvt, + float &slew, + float &cap) const; void setIsScaled(bool is_scaled) override; float axisValue(const TableAxis *axis, - float load_cap, - float in_slew, - float related_out_cap) const; + float load_cap, + float in_slew, + float related_out_cap) const; float findValue(const Pvt *pvt, - const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap) const; + const TableModel *model, + float in_slew, + float load_cap, + float related_out_cap) const; std::string reportTableLookup(const char *result_name, const Pvt *pvt, const TableModel *model, @@ -120,13 +120,13 @@ protected: float related_out_cap, int digits) const; void findAxisValues(const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const; + float in_slew, + float load_cap, + float related_out_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const; static bool checkAxis(const TableAxis *axis); TableModel *delay_model_; @@ -140,9 +140,9 @@ protected: class CheckTableModel : public CheckTimingModel { public: - explicit CheckTableModel(LibertyCell *cell, - TableModel *model, - TableModel *sigma_models[EarlyLate::index_count]); + CheckTableModel(LibertyCell *cell, + TableModel *model, + TableModel *sigma_models[EarlyLate::index_count]); virtual ~CheckTableModel(); ArcDelay checkDelay(const Pvt *pvt, float from_slew, @@ -165,21 +165,21 @@ public: protected: void setIsScaled(bool is_scaled) override; float findValue(const Pvt *pvt, - const TableModel *model, - float from_slew, - float to_slew, - float related_out_cap) const; + const TableModel *model, + float from_slew, + float to_slew, + float related_out_cap) const; void findAxisValues(float from_slew, - float to_slew, - float related_out_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const; + float to_slew, + float related_out_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const; float axisValue(const TableAxis *axis, - float load_cap, - float in_slew, - float related_out_cap) const; + float load_cap, + float in_slew, + float related_out_cap) const; std::string reportTableDelay(const char *result_name, const Pvt *pvt, const TableModel *model, @@ -200,8 +200,8 @@ class TableModel public: TableModel(TablePtr table, TableTemplate *tbl_template, - ScaleFactorType scale_factor_type, - const RiseFall *rf); + ScaleFactorType scale_factor_type, + const RiseFall *rf); void setScaleFactorType(ScaleFactorType type); int order() const; TableTemplate *tblTemplate() const { return tbl_template_; } @@ -214,14 +214,14 @@ public: size_t index3) const; // Table interpolated lookup. float findValue(float value1, - float value2, - float value3) const; + float value2, + float value3) const; // Table interpolated lookup with scale factor. float findValue(const LibertyCell *cell, - const Pvt *pvt, - float value1, - float value2, - float value3) const; + const Pvt *pvt, + float value1, + float value2, + float value3) const; std::string reportValue(const char *result_name, const LibertyCell *cell, const Pvt *pvt, @@ -236,7 +236,7 @@ public: protected: float scaleFactor(const LibertyCell *cell, - const Pvt *pvt) const; + const Pvt *pvt) const; std::string reportPvtScaleFactor(const LibertyCell *cell, const Pvt *pvt, int digits) const; @@ -266,15 +266,15 @@ public: size_t axis_idx3) const = 0; // Table interpolated lookup. virtual float findValue(float axis_value1, - float axis_value2, - float axis_value3) const = 0; + float axis_value2, + float axis_value3) const = 0; // Table interpolated lookup with scale factor. float findValue(const LibertyLibrary *library, - const LibertyCell *cell, - const Pvt *pvt, - float axis_value1, - float axis_value2, - float axis_value3) const; + const LibertyCell *cell, + const Pvt *pvt, + float axis_value1, + float axis_value2, + float axis_value3) const; virtual std::string reportValue(const char *result_name, const LibertyCell *cell, const Pvt *pvt, @@ -285,7 +285,7 @@ public: const Unit *table_unit, int digits) const = 0; virtual void report(const Units *units, - Report *report) const = 0; + Report *report) const = 0; }; // Zero dimension (scalar) table. @@ -323,7 +323,7 @@ class Table1 : public Table public: Table1(); Table1(FloatSeq *values, - TableAxisPtr axis1); + TableAxisPtr axis1); virtual ~Table1(); Table1(Table1 &&table); Table1(const Table1 &table); @@ -370,8 +370,8 @@ class Table2 : public Table { public: Table2(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2); + TableAxisPtr axis1, + TableAxisPtr axis2); virtual ~Table2(); int order() const override { return 2; } const TableAxis *axis1() const override { return axis1_.get(); } @@ -414,9 +414,9 @@ class Table3 : public Table2 { public: Table3(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2, - TableAxisPtr axis3); + TableAxisPtr axis1, + TableAxisPtr axis2, + TableAxisPtr axis3); virtual ~Table3() {} int order() const override { return 3; } const TableAxis *axis1() const override { return axis1_.get(); } @@ -449,7 +449,7 @@ class TableAxis { public: TableAxis(TableAxisVariable variable, - FloatSeq *values); + FloatSeq *values); ~TableAxis(); TableAxisVariable variable() const { return variable_; } const char *variableString() const; diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index a70f8400a..e521c4a93 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -36,15 +36,21 @@ namespace sta { StringSet * tclListSetConstChar(Tcl_Obj *const source, - Tcl_Interp *interp); + Tcl_Interp *interp); StringSeq * tclListSeqConstChar(Tcl_Obj *const source, - Tcl_Interp *interp); + Tcl_Interp *interp); +StdStringSeq +tclListSeqStdString(Tcl_Obj *const source, + Tcl_Interp *interp); +StdStringSeq * +tclListSeqStdStringPtr(Tcl_Obj *const source, + Tcl_Interp *interp); StdStringSet * tclListSetStdString(Tcl_Obj *const source, - Tcl_Interp *interp); + Tcl_Interp *interp); void tclArgError(Tcl_Interp *interp, @@ -54,10 +60,10 @@ tclArgError(Tcl_Interp *interp, void objectListNext(const char *list, - const char *type, - // Return values. - bool &type_match, - const char *&next); + const char *type, + // Return values. + bool &type_match, + const char *&next); Tcl_Obj * tclArcDcalcArg(ArcDcalcArg &gate, diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 3884cbd30..080cf84d5 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -25,8 +25,9 @@ #pragma once #include +#include +#include -#include "Vector.hh" #include "Transition.hh" #include "Delay.hh" #include "LibertyClass.hh" @@ -36,11 +37,11 @@ namespace sta { class TimingArcAttrs; class WireTimingArc; class GateTableModel; -class DcalcAnalysisPt; +class Scene; -typedef int TimingArcIndex; -typedef Vector TimingArcSeq; -typedef Map ScaledTimingModelMap; +using TimingArcIndex = int; +using TimingArcSeq = std::vector; +using ScaledTimingModelMap = std::map; enum class TimingType { clear, @@ -117,7 +118,7 @@ public: void setModeValue(const char *value); TimingModel *model(const RiseFall *rf) const; void setModel(const RiseFall *rf, - TimingModel *model); + TimingModel *model); float ocvArcDepth() const { return ocv_arc_depth_; } void setOcvArcDepth(float depth); @@ -143,11 +144,11 @@ class TimingArcSet { public: TimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs); + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs); virtual ~TimingArcSet(); LibertyCell *libertyCell() const; LibertyPort *from() const { return from_; } @@ -162,9 +163,9 @@ public: TimingArcSeq &arcs() { return arcs_; } // Return 1 or 2 arcs matching from transition. void arcsFrom(const RiseFall *from_rf, - // Return values. - TimingArc *&arc1, - TimingArc *&arc2) const; + // Return values. + TimingArc *&arc1, + TimingArc *&arc2) const; TimingArc *arcTo(const RiseFall *to_rf) const; const TimingArcSeq &arcs() const { return arcs_; } TimingArcIndex addTimingArc(TimingArc *arc); @@ -186,15 +187,13 @@ public: const char *modeValue() const { return attrs_->modeValue(); } // Timing arc set index in cell. TimingArcIndex index() const { return index_; } - bool isDisabledConstraint() const { return is_disabled_constraint_; } - void setIsDisabledConstraint(bool is_disabled); // OCV arc depth from timing/cell/library. float ocvArcDepth() const; static bool equiv(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); static bool less(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); static void init(); static void destroy(); @@ -217,7 +216,6 @@ protected: TimingArcSeq arcs_; bool is_cond_default_; unsigned index_; - bool is_disabled_constraint_; TimingArc *from_arc1_[RiseFall::index_count]; TimingArc *from_arc2_[RiseFall::index_count]; TimingArc *to_arc_[RiseFall::index_count]; @@ -232,9 +230,9 @@ class TimingArc { public: TimingArc(TimingArcSet *set, - const Transition *from_rf, - const Transition *to_rf, - TimingModel *model); + const Transition *from_rf, + const Transition *to_rf, + TimingModel *model); ~TimingArc(); std::string to_string() const; LibertyPort *from() const { return set_->from(); } @@ -247,24 +245,28 @@ public: // Index in TimingArcSet. unsigned index() const { return index_; } TimingModel *model() const { return model_; } - GateTimingModel *gateModel(const DcalcAnalysisPt *dcalc_ap) const; - CheckTimingModel *checkModel(const DcalcAnalysisPt *dcalc_ap) const; + GateTimingModel *gateModel(const Scene *scene, + const MinMax *min_max) const; + CheckTimingModel *checkModel(const Scene *scene, + const MinMax *min_max) const; GateTableModel *gateTableModel() const; - GateTableModel *gateTableModel(const DcalcAnalysisPt *dcalc_ap) const; - const TimingArc *cornerArc(int ap_index) const; - void setCornerArc(TimingArc *corner_arc, - int ap_index); + GateTableModel *gateTableModel(const Scene *scene, + const MinMax *min_max) const; + const TimingArc *sceneArc(int ap_index) const; + void setSceneArc(TimingArc *scene_arc, + int ap_index); float driveResistance() const; ArcDelay intrinsicDelay() const; static bool equiv(const TimingArc *arc1, - const TimingArc *arc2); + const TimingArc *arc2); protected: - TimingModel *model(const DcalcAnalysisPt *dcalc_ap) const; + TimingModel *model(const Scene *scene, + const MinMax *min_max) const; void setIndex(unsigned index); void addScaledModel(const OperatingConditions *op_cond, - TimingModel *scaled_model); + TimingModel *scaled_model); TimingArcSet *set_; const Transition *from_rf_; @@ -272,7 +274,7 @@ protected: unsigned index_; TimingModel *model_; ScaledTimingModelMap *scaled_models_; - Vector corner_arcs_; + std::vector scene_arcs_; private: friend class LibertyLibrary; diff --git a/include/sta/TimingModel.hh b/include/sta/TimingModel.hh index 3ee8cab89..c9f5ee8a7 100644 --- a/include/sta/TimingModel.hh +++ b/include/sta/TimingModel.hh @@ -50,12 +50,12 @@ public: GateTimingModel(LibertyCell *cell); // Gate delay calculation. 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 in_slew, + float load_cap, + bool pocv_enabled, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) const = 0; virtual std::string reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, diff --git a/include/sta/TimingRole.hh b/include/sta/TimingRole.hh index b9456cc15..3621f63c6 100644 --- a/include/sta/TimingRole.hh +++ b/include/sta/TimingRole.hh @@ -33,7 +33,7 @@ namespace sta { class TimingRole; -typedef std::map TimingRoleMap; +using TimingRoleMap = std::map; class TimingRole { @@ -74,10 +74,10 @@ public: bool isTimingCheck() const { return is_timing_check_; } // TIming check but not width or period. bool isTimingCheckBetween() const; - bool isAsyncTimingCheck() const; - bool isNonSeqTimingCheck() const { return is_non_seq_check_; } - bool isDataCheck() const; - bool isLatchDtoQ() const; + [[nodiscard]] bool isAsyncTimingCheck() const; + [[nodiscard]] bool isNonSeqTimingCheck() const { return is_non_seq_check_; } + [[nodiscard]] bool isDataCheck() const; + [[nodiscard]] bool isLatchDtoQ() const; const TimingRole *genericRole() const; const TimingRole *sdfRole() const; // Timing check data path min/max. @@ -88,18 +88,18 @@ public: // Pseudo role to match sdf IOPATH. static const TimingRole *sdfIopath() { return &sdf_iopath_; } static bool less(const TimingRole *role1, - const TimingRole *role2); + const TimingRole *role2); static const int index_max = 26; private: TimingRole(const char *name, - bool is_sdf_iopath, - bool is_timing_check, - bool is_non_seq_check, - const MinMax *path_min_max, - // generic_type = nullptr means type is the same as this. - const TimingRole *generic_role, - int index); + bool is_sdf_iopath, + bool is_timing_check, + bool is_non_seq_check, + const MinMax *path_min_max, + // generic_type = nullptr means type is the same as this. + const TimingRole *generic_role, + int index); const std::string name_; bool is_timing_check_; diff --git a/include/sta/TokenParser.hh b/include/sta/TokenParser.hh index f5f1bb20a..a339a6a8c 100644 --- a/include/sta/TokenParser.hh +++ b/include/sta/TokenParser.hh @@ -37,7 +37,7 @@ class TokenParser { public: TokenParser(const char *str, - const char *delimiters); + const char *delimiters); bool hasNext(); char *next(); diff --git a/include/sta/Transition.hh b/include/sta/Transition.hh index c7a490a8b..39190c6cf 100644 --- a/include/sta/Transition.hh +++ b/include/sta/Transition.hh @@ -26,9 +26,9 @@ #include #include +#include #include "Iterator.hh" -#include "Map.hh" #include "StringUtil.hh" namespace sta { @@ -37,7 +37,7 @@ class Transition; class RiseFall; class RiseFallBoth; -typedef Map TransitionMap; +using TransitionMap = std::map; // Rise/fall transition. class RiseFall @@ -164,9 +164,9 @@ public: private: Transition(const char *name, - const char *init_final, - const RiseFall *as_rise_fall, - int sdf_triple_index); + const char *init_final, + const RiseFall *as_rise_fall, + int sdf_triple_index); const std::string name_; const std::string init_final_; diff --git a/include/sta/Units.hh b/include/sta/Units.hh index 8b54be5f8..79bf472ac 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -59,7 +59,7 @@ public: const char *asString(float value) const; const char *asString(double value) const; const char *asString(float value, - int digits) const; + int digits) const; private: void setScaleAbbrevSuffix(); diff --git a/include/sta/UnorderedMap.hh b/include/sta/UnorderedMap.hh deleted file mode 100644 index 316a254fa..000000000 --- a/include/sta/UnorderedMap.hh +++ /dev/null @@ -1,203 +0,0 @@ -// 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 -#include - -namespace sta { - -// Add convenience functions around STL container. -template , class EQUAL = std::equal_to> -class UnorderedMap : public std::unordered_map -{ -public: - UnorderedMap() : - std::unordered_map() - { - } - - explicit UnorderedMap(const HASH &hash) : - std::unordered_map(0, hash, std::equal_to()) - { - } - - explicit UnorderedMap(size_t size, - const HASH &hash, - const EQUAL &equal) : - std::unordered_map(size, hash, equal) - { - } - - // Find out if key is in the set. - bool - hasKey(const KEY key) const - { - return this->find(key) != this->end(); - } - - // Find the value corresponding to key. - VALUE - findKey(const KEY key) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) - return find_iter->second; - else - return nullptr; - } - void - findKey(const KEY key, - // Return Values. - VALUE &value, - bool &exists) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) { - value = find_iter->second; - exists = true; - } - else - exists = false; - } - void - findKey(const KEY &key, - // Return Values. - KEY &map_key, - VALUE &value, - bool &exists) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) { - map_key = find_iter->first; - value = find_iter->second; - exists = true; - } - else - exists = false; - } - - void - insert(const KEY &key, - VALUE value) - { - this->operator[](key) = value; - } - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteKeysContents() - { - Iterator iter(this); - while (iter.hasNext()) { - KEY key; - VALUE value; - iter.next(key, value); - delete key; - delete value; - } - } - - void - deleteArrayContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete [] iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - std::unordered_map::clear(); - } - - // Java style container itererator - // Map::Iterator iter(map); - // while (iter.hasNext()) { - // Value *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - explicit Iterator(std::unordered_map *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit Iterator(std::unordered_map &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(std::unordered_map *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(std::unordered_map &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - VALUE next() { return iter_++->second; } - void next(KEY &key, - VALUE &value) - { key = iter_->first; value = iter_->second; iter_++; } - std::unordered_map *container() { return container_; } - - private: - std::unordered_map *container_; - typename std::unordered_map::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - explicit ConstIterator(const std::unordered_map *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit ConstIterator(const std::unordered_map &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(const std::unordered_map *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(const std::unordered_map &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - VALUE next() { return iter_++->second; } - void next(KEY &key, - VALUE &value) - { key = iter_->first; value = iter_->second; iter_++; } - const std::unordered_map *container() { return container_; } - - private: - const std::unordered_map *container_; - typename std::unordered_map::const_iterator iter_; - }; -}; - -} // namespace diff --git a/include/sta/UnorderedSet.hh b/include/sta/UnorderedSet.hh deleted file mode 100644 index 2d8598152..000000000 --- a/include/sta/UnorderedSet.hh +++ /dev/null @@ -1,139 +0,0 @@ -// 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 -#include - -namespace sta { - -// Add convenience functions around STL container. -template , class EQUAL = std::equal_to > -class UnorderedSet : public std::unordered_set -{ -public: - UnorderedSet() : - std::unordered_set() - { - } - - explicit UnorderedSet(size_t size) : - std::unordered_set(size) - { - } - - explicit UnorderedSet(size_t size, - const HASH &hash, - const EQUAL &equal) : - std::unordered_set(size, hash, equal) - { - } - - // Find out if key is in the set. - bool - hasKey(const KEY key) const - { - return this->find(key) != this->end(); - } - - // Find the value corresponding to key. - KEY - findKey(const KEY key) const - { - auto find_iter = this->find(key); - if (find_iter != this->end()) - return *find_iter; - else - return nullptr; - } - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - std::unordered_set::clear(); - } - - // Java style container itererator - // Set::Iterator iter(set); - // while (iter.hasNext()) { - // Value *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - explicit Iterator(std::unordered_set *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit Iterator(std::unordered_set &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(std::unordered_set *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(std::unordered_set &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - KEY next() { return *iter_++; } - std::unordered_set *container() { return container_; } - - private: - std::unordered_set *container_; - typename std::unordered_set::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - explicit ConstIterator(const std::unordered_set *container) : - container_(container) - { if (container_ != nullptr) iter_ = container_->begin(); } - explicit ConstIterator(const std::unordered_set &container) : - container_(&container) - { if (container_ != nullptr) iter_ = container_->begin(); } - void init(const std::unordered_set *container) - { container_ = container; if (container_ != nullptr) iter_=container_->begin();} - void init(const std::unordered_set &container) - { container_ = &container; if (container_ != nullptr) iter_=container_->begin();} - bool hasNext() { return container_ != nullptr && iter_ != container_->end(); } - KEY next() { return iter_++->second; } - const std::unordered_set *container() { return container_; } - - private: - const std::unordered_set *container_; - typename std::unordered_set::const_iterator iter_; - }; -}; - -} // namespace diff --git a/include/sta/Variables.hh b/include/sta/Variables.hh index 1d60408ec..2bd999901 100644 --- a/include/sta/Variables.hh +++ b/include/sta/Variables.hh @@ -54,10 +54,6 @@ public: // Enable/disable timing from bidirect pins back into the instance. bool bidirectInstPathsEnabled() const { return bidirect_inst_paths_enabled_; } void setBidirectInstPathsEnabled(bool enabled); - // TCL variable sta_bidirect_net_paths_enabled. - // Enable/disable timing from bidirect driver pins to their own loads. - bool bidirectNetPathsEnabled() const { return bidirect_net_paths_enabled_; } - void setBidirectNetPathsEnabled(bool enabled); // TCL variable sta_recovery_removal_checks_enabled. bool recoveryRemovalChecksEnabled() const { return recovery_removal_checks_enabled_; } void setRecoveryRemovalChecksEnabled(bool enabled); @@ -85,7 +81,6 @@ private: bool propagate_gated_clock_enable_; bool preset_clr_arcs_enabled_; bool cond_default_arcs_enabled_; - bool bidirect_net_paths_enabled_; bool bidirect_inst_paths_enabled_; bool recovery_removal_checks_enabled_; bool gated_clk_checks_enabled_; diff --git a/include/sta/Vector.hh b/include/sta/Vector.hh deleted file mode 100644 index 001b37131..000000000 --- a/include/sta/Vector.hh +++ /dev/null @@ -1,146 +0,0 @@ -// 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 -#include - -namespace sta { - -// Add convenience functions around STL container. -template -class Vector : public std::vector -{ -public: - Vector() : std::vector() {} - Vector(size_t n) : std::vector(n) {} - Vector(size_t n, const OBJ &obj) : std::vector(n, obj) {} - - // Erase an object from the vector (slow). - void - eraseObject(OBJ obj) - { - auto find_iter = std::find(this->begin(), this->end(), obj); - if (find_iter != this->end()) - this->erase(find_iter); - } - - void - deleteContents() - { - Iterator iter(this); - while (iter.hasNext()) - delete iter.next(); - } - - void - deleteContentsClear() - { - deleteContents(); - this->clear(); - } - - void - deleteArrayContentsClear() - { - Iterator iter(this); - while (iter.hasNext()) - delete [] iter.next(); - this->clear(); - } - - // Java style container itererator - // Vector::Iterator iter(vector); - // while (iter.hasNext()) { - // Object *v = iter.next(); - // } - class Iterator - { - public: - Iterator() : container_(nullptr) {} - Iterator(std::vector *container) : - container_(container) - { if (container) iter_ = container->begin(); } - Iterator(std::vector &container) : - container_(&container) - { if (container_) iter_ = container_->begin(); } - void init() { iter_ = container_->begin(); } - void init(std::vector *container) - { container_ = container; if (container_) iter_=container_->begin(); } - void init(std::vector &container) - { container_ = &container; iter_ = container_->begin(); } - bool hasNext() { return container_ && iter_ != container_->end(); } - OBJ& next() { return *iter_++; } - std::vector *container() { return container_; } - - private: - std::vector *container_; - typename std::vector::iterator iter_; - }; - - class ConstIterator - { - public: - ConstIterator() : container_(nullptr) {} - ConstIterator(const std::vector *container) : - container_(container) - { if (container_) iter_ = container_->begin(); } - ConstIterator(const std::vector &container) : - container_(&container) - { iter_ = container_->begin(); } - void init() { iter_ = container_->begin(); } - void init(const std::vector *container) - { container_ = container; if (container_) iter_=container_->begin();} - void init(const std::vector &container) - { container_ = &container; if (container_) iter_=container_->begin();} - bool hasNext() { return container_ && iter_ != container_->end(); } - const OBJ& next() { return *iter_++; } - const std::vector *container() { return container_; } - - private: - const std::vector *container_; - typename std::vector::const_iterator iter_; - }; -}; - -template -void -sort(Vector &seq, SortCmp cmp) -{ - // For some strange reason std::sort goes off into never never land - // when optimization is turned on in gcc. - std::stable_sort(seq.begin(), seq.end(), cmp); -} - -template -void -sort(Vector *seq, SortCmp cmp) -{ - // For some strange reason std::sort goes off into never never land - // when optimization is turned on in gcc. - std::stable_sort(seq->begin(), seq->end(), cmp); -} - -} // namespace sta diff --git a/include/sta/VectorMap.hh b/include/sta/VectorMap.hh new file mode 100644 index 000000000..33cc50808 --- /dev/null +++ b/include/sta/VectorMap.hh @@ -0,0 +1,494 @@ +// 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 +#include +#include +#include +#include +#include + +namespace sta { + +// Proxy reference type that mimics std::pair +// and supports structured bindings via tuple interface +template +class VectorMapReferenceProxy { +public: + // Constructor that stores pointer to underlying pair (non-const) + VectorMapReferenceProxy(std::pair* ptr) : pair_ptr_(ptr) {} + + // Constructor that stores pointer to underlying pair (const) + VectorMapReferenceProxy(const std::pair* ptr) + : pair_ptr_(const_cast*>(ptr)) {} + + // Access members like std::pair (for compatibility) + const Key& first() const { return pair_ptr_->first; } + Value& second() { return pair_ptr_->second; } + const Value& second() const { return pair_ptr_->second; } + + // Tuple-like access for structured bindings + template + auto get() const { + if constexpr (I == 0) return pair_ptr_->first; + if constexpr (I == 1) return pair_ptr_->second; + } + + template + auto get() { + if constexpr (I == 0) return pair_ptr_->first; + if constexpr (I == 1) return pair_ptr_->second; + } + + // Assignment (only second can be assigned) + VectorMapReferenceProxy& operator=(const std::pair& other) { + pair_ptr_->second = other.second; + return *this; + } + +private: + std::pair* pair_ptr_; +}; + +// VectorMap: A map implementation using a sorted vector with binary search. +// This provides O(log n) lookup, O(n) insertion/deletion, but better cache +// locality than std::map for small to medium-sized maps. +template > +class VectorMap { +public: + using key_type = Key; + using mapped_type = Value; + using value_type = std::pair; + using key_compare = Compare; + using size_type = size_t; + using difference_type = ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + +private: + // Internal storage: vector of pairs + // We use std::pair internally, but expose it as + // std::pair through iterators + using storage_type = std::vector>; + storage_type data_; + Compare comp_; + + // Helper to find insertion point using binary search + typename storage_type::iterator findInsertPos(const Key& key) { + return std::lower_bound(data_.begin(), data_.end(), key, + [this](const auto& pair, const Key& k) { + return comp_(pair.first, k); + }); + } + + typename storage_type::const_iterator findInsertPos(const Key& key) const { + return std::lower_bound(data_.begin(), data_.end(), key, + [this](const auto& pair, const Key& k) { + return comp_(pair.first, k); + }); + } + + // Iterator adapter to expose const Key in pair + // Uses a proxy reference to safely expose std::pair + template + class IteratorAdapter { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = std::pair; + using difference_type = ptrdiff_t; + using proxy_type = VectorMapReferenceProxy; + + using reference = proxy_type; + using pointer = proxy_type*; + + IteratorAdapter() = default; + explicit IteratorAdapter(BaseIter it) : it_(it) {} + + // Conversion constructor: allow conversion from non-const to const iterator + template + IteratorAdapter(const IteratorAdapter& other, + typename std::enable_if< + std::is_convertible_v + >::type* = nullptr) + : it_(other.base()) {} + + reference operator*() { + return proxy_type(&*it_); + } + + reference operator*() const { + return proxy_type(&*it_); + } + + // For operator->, we return a pointer to a temporary + // This is not ideal but necessary for compatibility + class arrow_proxy { + public: + explicit arrow_proxy(const proxy_type& ref) + : value_(ref.first(), ref.second()) {} + value_type* operator->() { return &value_; } + private: + value_type value_; + }; + + arrow_proxy operator->() { + return arrow_proxy(operator*()); + } + + arrow_proxy operator->() const { + return arrow_proxy(operator*()); + } + + // Assignment operator: allow assignment from non-const to const iterator + template + typename std::enable_if< + std::is_convertible_v, + IteratorAdapter& + >::type + operator=(const IteratorAdapter& other) { + it_ = other.base(); + return *this; + } + + IteratorAdapter& operator++() { + ++it_; + return *this; + } + + IteratorAdapter operator++(int) { + IteratorAdapter tmp = *this; + ++it_; + return tmp; + } + + IteratorAdapter& operator--() { + --it_; + return *this; + } + + IteratorAdapter operator--(int) { + IteratorAdapter tmp = *this; + --it_; + return tmp; + } + + IteratorAdapter& operator+=(difference_type n) { + it_ += n; + return *this; + } + + IteratorAdapter& operator-=(difference_type n) { + it_ -= n; + return *this; + } + + IteratorAdapter operator+(difference_type n) const { + return IteratorAdapter(it_ + n); + } + + IteratorAdapter operator-(difference_type n) const { + return IteratorAdapter(it_ - n); + } + + difference_type operator-(const IteratorAdapter& other) const { + return it_ - other.it_; + } + + reference operator[](difference_type n) { + return proxy_type(&it_[n]); + } + + reference operator[](difference_type n) const { + return proxy_type(&it_[n]); + } + + bool operator==(const IteratorAdapter& other) const { + return it_ == other.it_; + } + + bool operator!=(const IteratorAdapter& other) const { + return it_ != other.it_; + } + + bool operator<(const IteratorAdapter& other) const { + return it_ < other.it_; + } + + bool operator<=(const IteratorAdapter& other) const { + return it_ <= other.it_; + } + + bool operator>(const IteratorAdapter& other) const { + return it_ > other.it_; + } + + bool operator>=(const IteratorAdapter& other) const { + return it_ >= other.it_; + } + + BaseIter base() const { return it_; } + + private: + BaseIter it_; + }; + +public: + using iterator = IteratorAdapter; + using const_iterator = IteratorAdapter; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + VectorMap() = default; + explicit VectorMap(const Compare& comp) : comp_(comp) {} + + template + VectorMap(InputIt first, InputIt last, const Compare& comp = Compare()) + : comp_(comp) { + for (; first != last; ++first) { + insert(*first); + } + } + + VectorMap(std::initializer_list init, + const Compare& comp = Compare()) + : comp_(comp) { + for (const auto& pair : init) { + insert(pair); + } + } + + // Element access + Value& operator[](const Key& key) { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + // Key exists + return it->second; + } else { + // Key doesn't exist, insert it + return data_.insert(it, std::make_pair(key, Value{}))->second; + } + } + + Value& at(const Key& key) { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + return it->second; + } + throw std::out_of_range("VectorMap::at"); + } + + const Value& at(const Key& key) const { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + return it->second; + } + throw std::out_of_range("VectorMap::at"); + } + + // Lookup + bool contains(const Key& key) const { + auto it = findInsertPos(key); + return it != data_.end() && !comp_(key, it->first); + } + + iterator find(const Key& key) { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + return iterator(it); + } + return end(); + } + + const_iterator find(const Key& key) const { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + return const_iterator(it); + } + return end(); + } + + size_type count(const Key& key) const { + return contains(key) ? 1 : 0; + } + + // Modifiers + std::pair insert(const value_type& value) { + auto it = findInsertPos(value.first); + if (it != data_.end() && !comp_(value.first, it->first)) { + // Key already exists + return std::make_pair(iterator(it), false); + } else { + // Insert new key-value pair + it = data_.insert(it, std::make_pair(value.first, value.second)); + return std::make_pair(iterator(it), true); + } + } + + std::pair insert(value_type&& value) { + auto it = findInsertPos(value.first); + if (it != data_.end() && !comp_(value.first, it->first)) { + // Key already exists + return std::make_pair(iterator(it), false); + } else { + // Insert new key-value pair + it = data_.insert(it, std::make_pair(std::move(value.first), + std::move(value.second))); + return std::make_pair(iterator(it), true); + } + } + + template + std::pair insert(P&& value) { + return insert(value_type(std::forward

(value))); + } + + template + void insert(InputIt first, InputIt last) { + for (; first != last; ++first) { + insert(*first); + } + } + + void insert(std::initializer_list ilist) { + for (const auto& pair : ilist) { + insert(pair); + } + } + + iterator erase(const_iterator pos) { + return iterator(data_.erase(pos.base())); + } + + iterator erase(const_iterator first, const_iterator last) { + return iterator(data_.erase(first.base(), last.base())); + } + + size_type erase(const Key& key) { + auto it = findInsertPos(key); + if (it != data_.end() && !comp_(key, it->first)) { + data_.erase(it); + return 1; + } + return 0; + } + + void clear() { + data_.clear(); + } + + // Capacity + bool empty() const { + return data_.empty(); + } + + size_type size() const { + return data_.size(); + } + + size_type max_size() const { + return data_.max_size(); + } + + // Iterators + iterator begin() { + return iterator(data_.begin()); + } + + const_iterator begin() const { + return const_iterator(data_.begin()); + } + + const_iterator cbegin() const { + return const_iterator(data_.begin()); + } + + iterator end() { + return iterator(data_.end()); + } + + const_iterator end() const { + return const_iterator(data_.end()); + } + + const_iterator cend() const { + return const_iterator(data_.end()); + } + + reverse_iterator rbegin() { + return reverse_iterator(end()); + } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + const_reverse_iterator crbegin() const { + return const_reverse_iterator(end()); + } + + reverse_iterator rend() { + return reverse_iterator(begin()); + } + + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + const_reverse_iterator crend() const { + return const_reverse_iterator(begin()); + } + + // Observers + key_compare key_comp() const { + return comp_; + } +}; + +} // namespace sta + +// Specializations for structured bindings support +namespace std { + +template +struct tuple_size> + : std::integral_constant {}; + +template +struct tuple_element> { + static_assert(I < 2, "Index out of bounds for pair"); + using type = std::conditional_t; +}; + +template +struct tuple_element> { + static_assert(I < 2, "Index out of bounds for pair"); + using type = std::conditional_t; +}; + +} // namespace std + diff --git a/include/sta/VerilogReader.hh b/include/sta/VerilogReader.hh index dc76ccc6e..2b244d0c2 100644 --- a/include/sta/VerilogReader.hh +++ b/include/sta/VerilogReader.hh @@ -25,10 +25,10 @@ #pragma once #include +#include +#include #include "StringSet.hh" -#include "Vector.hh" -#include "Map.hh" #include "NetworkClass.hh" namespace sta { @@ -62,13 +62,13 @@ class VerilogNetPortRef; class VerilogError; class LibertyCell; -typedef Map VerilogModuleMap; -typedef Vector VerilogStmtSeq; -typedef Vector VerilogNetSeq; -typedef Vector VerilogDclArgSeq; -typedef Vector VerilogAttrStmtSeq; -typedef Vector VerilogAttrEntrySeq; -typedef Vector VerilogErrorSeq; +using VerilogModuleMap = std::map; +using VerilogStmtSeq = std::vector; +using VerilogNetSeq = std::vector; +using VerilogDclArgSeq = std::vector; +using VerilogAttrStmtSeq = std::vector; +using VerilogAttrEntrySeq = std::vector; +using VerilogErrorSeq = std::vector; class VerilogReader { @@ -115,47 +115,47 @@ public: VerilogAttrStmtSeq *attr_stmts, const int line); VerilogAssign *makeAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line); + VerilogNet *rhs, + int line); VerilogNetScalar *makeNetScalar(const std::string *name); VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_vname); VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_name, - const std::string *net_name); + const std::string *net_name); VerilogNetPortRef *makeNetNamedPortRefBitSelect(const std::string *port_name, - const std::string *bus_name, - int index); + const std::string *bus_name, + int index); VerilogNetPortRef *makeNetNamedPortRefScalar(const std::string *port_name, - VerilogNet *net); + VerilogNet *net); VerilogNetPortRef *makeNetNamedPortRefBit(const std::string *port_name, - int index, - VerilogNet *net); + int index, + VerilogNet *net); VerilogNetPortRef *makeNetNamedPortRefPart(const std::string *port_name, - int from_index, - int to_index, - VerilogNet *net); + int from_index, + int to_index, + VerilogNet *net); VerilogNetConcat *makeNetConcat(VerilogNetSeq *nets); VerilogNetConstant *makeNetConstant(const std::string *constant, int line); VerilogNetBitSelect *makeNetBitSelect(const std::string *name, - int index); + int index); VerilogNetPartSelect *makeNetPartSelect(const std::string *name, - int from_index, - int to_index); + int from_index, + int to_index); VerilogModule *module(Cell *cell); Instance *linkNetwork(const char *top_cell_name, - bool make_black_boxes, + bool make_black_boxes, bool delete_modules); const char *filename() const { return filename_.c_str(); } void incrLine(); Report *report() const { return report_; } void error(int id, const char *filename, - int line, - const char *fmt, ...); + int line, + const char *fmt, ...); void warn(int id, const char *filename, - int line, - const char *fmt, ...); + int line, + const char *fmt, ...); const std::string &zeroNetName() const { return zero_net_name_; } const std::string &oneNetName() const { return one_net_name_; } void deleteModules(); @@ -165,95 +165,95 @@ public: protected: void init(const char *filename); void makeCellPorts(Cell *cell, - VerilogModule *module, - VerilogNetSeq *ports); + VerilogModule *module, + VerilogNetSeq *ports); Port *makeCellPort(Cell *cell, - VerilogModule *module, - const std::string &port_name); + VerilogModule *module, + const std::string &port_name); void makeNamedPortRefCellPorts(Cell *cell, - VerilogModule *module, - VerilogNet *mod_port, - StdStringSet &port_names); + VerilogModule *module, + VerilogNet *mod_port, + StdStringSet &port_names); void checkModuleDcls(VerilogModule *module, - std::set &port_names); + std::set &port_names); void makeModuleInstBody(VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings, - bool make_black_boxes); + Instance *inst, + VerilogBindingTbl *bindings, + bool make_black_boxes); void makeModuleInstNetwork(VerilogModuleInst *mod_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool make_black_boxes); + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool make_black_boxes); void makeLibertyInst(VerilogLibertyInst *lib_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings); + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings); void bindGlobalNets(VerilogBindingTbl *bindings); void makeNamedInstPins1(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf); void makeNamedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool is_leaf); void makeOrderedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool is_leaf); void mergeAssignNet(VerilogAssign *assign, - VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings); + VerilogModule *module, + Instance *inst, + VerilogBindingTbl *bindings); void makeInstPin(Instance *inst, - Port *port, - VerilogNetNameIterator *net_name_iter, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Port *port, + VerilogNetNameIterator *net_name_iter, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf); void makeInstPin(Instance *inst, - Port *port, - const std::string &net_name, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); + Port *port, + const std::string &net_name, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf); void linkWarn(int id, const char *filename, - int line, - const char *msg, ...) + int line, + const char *msg, ...) __attribute__((format (printf, 5, 6))); void linkError(int id, const char *filename, - int line, - const char *msg, ...) + int line, + const char *msg, ...) __attribute__((format (printf, 5, 6))); bool reportLinkErrors(); bool haveLinkErrors(); Cell *makeBlackBox(VerilogModuleInst *mod_inst, - VerilogModule *parent_module); + VerilogModule *parent_module); void makeBlackBoxNamedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module); + VerilogModuleInst *mod_inst, + VerilogModule *parent_module); void makeBlackBoxOrderedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module); + VerilogModuleInst *mod_inst, + VerilogModule *parent_module); bool isBlackBox(Cell *cell); bool hasScalarNamedPortRefs(LibertyCell *liberty_cell, - VerilogNetSeq *pins); + VerilogNetSeq *pins); std::string filename_; Report *report_; @@ -294,4 +294,3 @@ protected: }; } // namespace sta - diff --git a/include/sta/VerilogWriter.hh b/include/sta/VerilogWriter.hh index b4a08e8cb..a0ba63468 100644 --- a/include/sta/VerilogWriter.hh +++ b/include/sta/VerilogWriter.hh @@ -30,8 +30,8 @@ namespace sta { void writeVerilog(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells, - Network *network); + bool include_pwr_gnd, + CellSeq *remove_cells, + Network *network); } // namespace diff --git a/include/sta/VertexId.hh b/include/sta/VertexId.hh index b56b8b7c3..c2dbcfbc3 100644 --- a/include/sta/VertexId.hh +++ b/include/sta/VertexId.hh @@ -30,7 +30,7 @@ namespace sta { -typedef ObjectId VertexId; +using VertexId = ObjectId; static constexpr VertexId vertex_id_null = object_id_null; diff --git a/include/sta/VisitPathEnds.hh b/include/sta/VisitPathEnds.hh index f6114b3f6..e07fb2d09 100644 --- a/include/sta/VisitPathEnds.hh +++ b/include/sta/VisitPathEnds.hh @@ -38,109 +38,104 @@ class VisitPathEnds : public StaState { public: VisitPathEnds(const StaState *sta); - // All corners, unfiltered. + // All scenes, unfiltered. void visitPathEnds(Vertex *vertex, - PathEndVisitor *visitor); - // Use corner nullptr to visit PathEnds for all corners. + PathEndVisitor *visitor); void visitPathEnds(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor); - bool checkEdgeEnabled(Edge *edge) const; + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor); + bool checkEdgeEnabled(const Edge *edge, + const Mode *mode) const; protected: void visitClkedPathEnds(const Pin *pin, - Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Vertex *vertex, + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); virtual void visitCheckEnd(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); void visitCheckEndUnclked(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); void visitOutputDelayEnd(const Pin *pin, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); virtual void visitOutputDelayEnd1(OutputDelay *output_delay, - const Pin *pin, - Path *path, - const RiseFall *end_rf, - const ClockEdge *tgt_clk_edge, - Path *ref_path, - const MinMax *min_max, - PathEndVisitor *visitor, - bool &is_constrained); + const Pin *pin, + Path *path, + const RiseFall *end_rf, + const ClockEdge *tgt_clk_edge, + Path *ref_path, + const MinMax *min_max, + PathEndVisitor *visitor, + bool &is_constrained); virtual void visitGatedClkEnd(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); float clockGatingMargin(const Clock *clk, - const Pin *clk_pin, - const Pin *enable_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold); + const Pin *clk_pin, + const Pin *enable_pin, + const RiseFall *enable_rf, + const SetupHold *setup_hold, + const Sdc *sdc); void visitDataCheckEnd(const Pin *pin, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); bool visitDataCheckEnd1(DataCheck *check, - const Pin *pin, - Path *path, - const Clock *src_clk, - const RiseFall *end_rf, - const MinMax *min_max, - const PathAnalysisPt *clk_ap, - const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained); + const Pin *pin, + Path *path, + const Clock *src_clk, + const RiseFall *end_rf, + const MinMax *min_max, + const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); virtual void visitUnconstrainedPathEnds(const Pin *pin, - Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor); + Vertex *vertex, + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor); bool falsePathTo(Path *path, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max); + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max); PathDelay *pathDelayTo(Path *path, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max); + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max); ExceptionPath *exceptionTo(const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max) const; + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max) const; }; // Abstract base class used by visitPathEnds to visit vertex path ends. diff --git a/include/sta/Wireload.hh b/include/sta/Wireload.hh index d0ea001c0..426211714 100644 --- a/include/sta/Wireload.hh +++ b/include/sta/Wireload.hh @@ -24,16 +24,18 @@ #pragma once -#include "Vector.hh" +#include +#include + #include "LibertyClass.hh" namespace sta { class WireloadForArea; -typedef std::pair FanoutLength; -typedef Vector FanoutLengthSeq; -typedef Vector WireloadForAreaSeq; +using FanoutLength = std::pair; +using FanoutLengthSeq = std::vector; +using WireloadForAreaSeq = std::vector; const char * wireloadTreeString(WireloadTree tree); @@ -49,13 +51,13 @@ class Wireload { public: Wireload(const char *name, - LibertyLibrary *library); + LibertyLibrary *library); Wireload(const char *name, - LibertyLibrary *library, - float area, - float resistance, - float capacitance, - float slope); + LibertyLibrary *library, + float area, + float resistance, + float capacitance, + float slope); virtual ~Wireload(); const char *name() const { return name_; } void setArea(float area); @@ -63,12 +65,12 @@ public: void setCapacitance(float cap); void setSlope(float slope); void addFanoutLength(float fanout, - float length); + float length); // Find wireload resistance/capacitance for fanout. virtual void findWireload(float fanout, - const OperatingConditions *op_cond, - float &cap, - float &res) const; + const OperatingConditions *op_cond, + float &cap, + float &res) const; protected: const char *name_; @@ -84,12 +86,12 @@ protected: class WireloadSelection { public: - explicit WireloadSelection(const char *name); + WireloadSelection(const char *name); ~WireloadSelection(); const char *name() const { return name_; } void addWireloadFromArea(float min_area, - float max_area, - const Wireload *wireload); + float max_area, + const Wireload *wireload); const Wireload *findWireload(float area) const; private: diff --git a/include/sta/WriteSdc.hh b/include/sta/WriteSdc.hh index 3aa517687..02abd6938 100644 --- a/include/sta/WriteSdc.hh +++ b/include/sta/WriteSdc.hh @@ -32,16 +32,16 @@ namespace sta { // Write SDC to a file. // Allow SDC to apply to an instance to support write_context. void -writeSdc(Instance *instance, - const char *filename, - const char *creator, - // Map hierarchical pins and instances to leaf pins and instances. - bool map_hpins, - // Replace non-sdc get functions with OpenSTA equivalents. - bool native, - int digits, +writeSdc(const Sdc *sdc, + Instance *instance, + const char *filename, + const char *creator, + // Map hierarchical pins and instances to leaf pins and instances. + bool map_hpins, + // Replace non-sdc get functions with OpenSTA equivalents. + bool native, + int digits, bool gzip, - bool no_timestamp, - Sdc *sdc); + bool no_timestamp); } // namespace diff --git a/include/sta/Zlib.hh b/include/sta/Zlib.hh index 7a9399de0..c1e143449 100644 --- a/include/sta/Zlib.hh +++ b/include/sta/Zlib.hh @@ -47,7 +47,7 @@ #define Z_NULL nullptr namespace gzstream { -typedef std::ifstream igzstream; +using igzstream = std::ifstream; } #endif // ZLIB_FOUND diff --git a/liberty/EquivCells.cc b/liberty/EquivCells.cc index f8910791e..c39553ecc 100644 --- a/liberty/EquivCells.cc +++ b/liberty/EquivCells.cc @@ -24,6 +24,7 @@ #include "EquivCells.hh" +#include "ContainerHelpers.hh" #include "Hash.hh" #include "MinMax.hh" #include "PortDirection.hh" @@ -49,7 +50,7 @@ static unsigned hashSequential(const Sequential *seq); bool equivCellStatetables(const LibertyCell *cell1, - const LibertyCell *cell2); + const LibertyCell *cell2); static bool equivCellPortSeq(const LibertyPortSeq &ports1, const LibertyPortSeq &ports2); @@ -86,77 +87,75 @@ class CellDriveResistanceGreater { public: bool operator()(const LibertyCell *cell1, - const LibertyCell *cell2) const + const LibertyCell *cell2) const { return cellDriveResistance(cell1) > cellDriveResistance(cell2); } }; EquivCells::EquivCells(LibertyLibrarySeq *equiv_libs, - LibertyLibrarySeq *map_libs) + LibertyLibrarySeq *map_libs) { LibertyCellHashMap hash_matches; for (auto lib : *equiv_libs) findEquivCells(lib, hash_matches); // Sort the equiv sets by drive resistance. for (auto cell : unique_equiv_cells_) { - auto equivs = equiv_cells_.findKey(cell); + auto equivs = findKey(equiv_cells_, cell); sort(equivs, CellDriveResistanceGreater()); } if (map_libs) { for (auto lib : *map_libs) mapEquivCells(lib, hash_matches); } - hash_matches.deleteContents(); + deleteContents(hash_matches); } EquivCells::~EquivCells() { for (auto cell : unique_equiv_cells_) - delete equiv_cells_.findKey(cell); + delete findKey(equiv_cells_, cell); } LibertyCellSeq * EquivCells::equivs(LibertyCell *cell) { - return equiv_cells_.findKey(cell); + return findKey(equiv_cells_, cell); } // Use a comprehensive hash on cell properties to segregate // cells into groups of potential matches. void EquivCells::findEquivCells(const LibertyLibrary *library, - LibertyCellHashMap &hash_matches) + LibertyCellHashMap &hash_matches) { LibertyCellIterator cell_iter(library); while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); if (!cell->dontUse()) { unsigned hash = hashCell(cell); - LibertyCellSeq *matches = hash_matches.findKey(hash); + LibertyCellSeq *matches = findKey(hash_matches, hash); if (matches) { - LibertyCellSeq::Iterator match_iter(matches); - while (match_iter.hasNext()) { - LibertyCell *match = match_iter.next(); - if (equivCells(match, cell)) { - LibertyCellSeq *equivs = equiv_cells_.findKey(match); - if (equivs == nullptr) { - equivs = new LibertyCellSeq; - equivs->push_back(match); - unique_equiv_cells_.push_back(match); - equiv_cells_[match] = equivs; - } - equivs->push_back(cell); - equiv_cells_[cell] = equivs; - break; - } - } - matches->push_back(cell); + for (LibertyCell *match : *matches) { + if (equivCells(match, cell)) { + LibertyCellSeq *equivs = findKey(equiv_cells_, match); + if (equivs == nullptr) { + equivs = new LibertyCellSeq; + equivs->push_back(match); + unique_equiv_cells_.push_back(match); + equiv_cells_[match] = equivs; + } + equivs->push_back(cell); + equiv_cells_[cell] = equivs; + break; + } + } + matches->push_back(cell); } else { - matches = new LibertyCellSeq; - hash_matches[hash] = matches; - matches->push_back(cell); + matches = new LibertyCellSeq; + hash_matches[hash] = matches; + matches->push_back(cell); } } } @@ -165,7 +164,7 @@ EquivCells::findEquivCells(const LibertyLibrary *library, // Map library cells to equiv cells. void EquivCells::mapEquivCells(const LibertyLibrary *library, - LibertyCellHashMap &hash_matches) + LibertyCellHashMap &hash_matches) { LibertyCellIterator cell_iter(library); @@ -173,17 +172,15 @@ EquivCells::mapEquivCells(const LibertyLibrary *library, LibertyCell *cell = cell_iter.next(); if (!cell->dontUse()) { unsigned hash = hashCell(cell); - LibertyCellSeq *matches = hash_matches.findKey(hash); + LibertyCellSeq *matches = findKey(hash_matches, hash); if (matches) { - LibertyCellSeq::Iterator match_iter(matches); - while (match_iter.hasNext()) { - LibertyCell *match = match_iter.next(); - if (equivCells(match, cell)) { - LibertyCellSeq *equivs = equiv_cells_.findKey(match); - equiv_cells_[cell] = equivs; - break; - } - } + for (LibertyCell *match : *matches) { + if (equivCells(match, cell)) { + LibertyCellSeq *equivs = findKey(equiv_cells_, match); + equiv_cells_[cell] = equivs; + break; + } + } } } } @@ -286,22 +283,22 @@ hashFuncExpr(const FuncExpr *expr) return 0; else { switch (expr->op()) { - case FuncExpr::op_port: + case FuncExpr::Op::port: return hashPort(expr->port()) * 17; break; - case FuncExpr::op_not: + case FuncExpr::Op::not_: return hashFuncExpr(expr->left()) * 31; break; default: return (hashFuncExpr(expr->left()) + hashFuncExpr(expr->right())) - * ((1 << expr->op()) - 1); + * ((1 << static_cast(expr->op())) - 1); } } } bool equivCells(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { return equivCellPorts(cell1, cell2) && equivCellFuncs(cell1, cell2) @@ -356,7 +353,7 @@ equivCellFuncs(const LibertyCell *cell1, bool equivCellPorts(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { if (cell1->portCount() != cell2->portCount()) return false; @@ -375,7 +372,7 @@ equivCellPorts(const LibertyCell *cell1, bool equivCellSequentials(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { const SequentialSeq &seqs1 = cell1->sequentials(); const SequentialSeq &seqs2 = cell2->sequentials(); @@ -386,11 +383,11 @@ equivCellSequentials(const LibertyCell *cell1, const Sequential *seq1 = *seq_itr1; const Sequential *seq2 = *seq_itr2; if (!(FuncExpr::equiv(seq1->clock(), seq2->clock()) - && FuncExpr::equiv(seq1->data(), seq2->data()) - && LibertyPort::equiv(seq1->output(), seq2->output()) - && LibertyPort::equiv(seq1->outputInv(), seq2->outputInv()) - && FuncExpr::equiv(seq1->clear(), seq2->clear()) - && FuncExpr::equiv(seq1->preset(), seq2->preset()))) + && FuncExpr::equiv(seq1->data(), seq2->data()) + && LibertyPort::equiv(seq1->output(), seq2->output()) + && LibertyPort::equiv(seq1->outputInv(), seq2->outputInv()) + && FuncExpr::equiv(seq1->clear(), seq2->clear()) + && FuncExpr::equiv(seq1->preset(), seq2->preset()))) return false; } return seq_itr1 == seqs1.end() && seq_itr2 == seqs2.end(); @@ -398,7 +395,7 @@ equivCellSequentials(const LibertyCell *cell1, bool equivCellStatetables(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { const Statetable *statetable1 = cell1->statetable(); @@ -494,7 +491,7 @@ equivStatetableRow(const StatetableRow &row1, bool equivCellTimingArcSets(const LibertyCell *cell1, - const LibertyCell *cell2) + const LibertyCell *cell2) { if (cell1->timingArcSetCount() != cell2->timingArcSetCount()) return false; @@ -502,7 +499,7 @@ equivCellTimingArcSets(const LibertyCell *cell1, for (TimingArcSet *arc_set1 : cell1->timingArcSets()) { TimingArcSet *arc_set2 = cell2->findTimingArcSet(arc_set1); if (!(arc_set2 && TimingArcSet::equiv(arc_set1, arc_set2))) - return false; + return false; } return true; } diff --git a/liberty/FuncExpr.cc b/liberty/FuncExpr.cc index 67de253c6..41faa2f68 100644 --- a/liberty/FuncExpr.cc +++ b/liberty/FuncExpr.cc @@ -35,53 +35,53 @@ using std::string; FuncExpr * FuncExpr::makePort(LibertyPort *port) { - return new FuncExpr(op_port, nullptr, nullptr, port); + return new FuncExpr(Op::port, nullptr, nullptr, port); } FuncExpr * FuncExpr::makeNot(FuncExpr *expr) { - return new FuncExpr(op_not, expr, nullptr, nullptr); + return new FuncExpr(Op::not_, expr, nullptr, nullptr); } FuncExpr * FuncExpr::makeAnd(FuncExpr *left, - FuncExpr *right) + FuncExpr *right) { - return new FuncExpr(op_and, left, right, nullptr); + return new FuncExpr(Op::and_, left, right, nullptr); } FuncExpr * FuncExpr::makeOr(FuncExpr *left, - FuncExpr *right) + FuncExpr *right) { - return new FuncExpr(op_or, left, right, nullptr); + return new FuncExpr(Op::or_, left, right, nullptr); } FuncExpr * FuncExpr::makeXor(FuncExpr *left, - FuncExpr *right) + FuncExpr *right) { - return new FuncExpr(op_xor, left, right, nullptr); + return new FuncExpr(Op::xor_, left, right, nullptr); } FuncExpr * FuncExpr::makeZero() { - return new FuncExpr(op_zero, nullptr, nullptr, nullptr); + return new FuncExpr(Op::zero, nullptr, nullptr, nullptr); } FuncExpr * FuncExpr::makeOne() { - return new FuncExpr(op_one, nullptr, nullptr, nullptr); + return new FuncExpr(Op::one, nullptr, nullptr, nullptr); } -FuncExpr::FuncExpr(Operator op, - FuncExpr *left, - FuncExpr *right, - LibertyPort *port) : +FuncExpr::FuncExpr(Op op, + FuncExpr *left, + FuncExpr *right, + LibertyPort *port) : op_(op), left_(left), right_(right), @@ -110,7 +110,7 @@ FuncExpr::copy() LibertyPort * FuncExpr::port() const { - if (op_ == op_port) + if (op_ == Op::port) return port_; else return nullptr; @@ -123,29 +123,29 @@ FuncExpr::portTimingSense(const LibertyPort *port) const TimingSense left_sense, right_sense; switch (op_) { - case op_port: + case Op::port: if (port == port_) return TimingSense::positive_unate; else return TimingSense::none; - case op_not: + case Op::not_: if (left_) { switch (left_->portTimingSense(port)) { case TimingSense::positive_unate: - return TimingSense::negative_unate; + return TimingSense::negative_unate; case TimingSense::negative_unate: - return TimingSense::positive_unate; + return TimingSense::positive_unate; case TimingSense::non_unate: - return TimingSense::non_unate; + return TimingSense::non_unate; case TimingSense::none: - return TimingSense::none; + return TimingSense::none; case TimingSense::unknown: - return TimingSense::unknown; + return TimingSense::unknown; } } return TimingSense::unknown; - case op_or: - case op_and: + case Op::or_: + case Op::and_: left_sense = TimingSense::unknown; right_sense = TimingSense::unknown; if (left_) @@ -156,21 +156,21 @@ FuncExpr::portTimingSense(const LibertyPort *port) const if (left_sense == right_sense) return left_sense; else if (left_sense == TimingSense::non_unate - || right_sense == TimingSense::non_unate - || (left_sense == TimingSense::positive_unate - && right_sense == TimingSense::negative_unate) - || (left_sense == TimingSense::negative_unate - && right_sense == TimingSense::positive_unate)) + || right_sense == TimingSense::non_unate + || (left_sense == TimingSense::positive_unate + && right_sense == TimingSense::negative_unate) + || (left_sense == TimingSense::negative_unate + && right_sense == TimingSense::positive_unate)) return TimingSense::non_unate; else if (left_sense == TimingSense::none - || left_sense == TimingSense::unknown) + || left_sense == TimingSense::unknown) return right_sense; else if (right_sense == TimingSense::none - || right_sense == TimingSense::unknown) + || right_sense == TimingSense::unknown) return left_sense; else return TimingSense::unknown; - case op_xor: + case Op::xor_: left_sense = TimingSense::unknown; right_sense = TimingSense::unknown; if (left_) @@ -178,17 +178,17 @@ FuncExpr::portTimingSense(const LibertyPort *port) const if (right_) right_sense = right_->portTimingSense(port); if (left_sense == TimingSense::positive_unate - || left_sense == TimingSense::negative_unate - || left_sense == TimingSense::non_unate - || right_sense == TimingSense::positive_unate - || right_sense == TimingSense::negative_unate - || right_sense == TimingSense::non_unate) + || left_sense == TimingSense::negative_unate + || left_sense == TimingSense::non_unate + || right_sense == TimingSense::positive_unate + || right_sense == TimingSense::negative_unate + || right_sense == TimingSense::non_unate) return TimingSense::non_unate; else return TimingSense::unknown; - case op_one: + case Op::one: return TimingSense::none; - case op_zero: + case Op::zero: return TimingSense::none; } // Prevent warnings from lame compilers. @@ -205,22 +205,22 @@ string FuncExpr::to_string(bool with_parens) const { switch (op_) { - case op_port: + case Op::port: return port_->name(); - case op_not: { + case Op::not_: { string result = "!"; result += left_->to_string(true); return result; } - case op_or: + case Op::or_: return to_string(with_parens, '+'); - case op_and: + case Op::and_: return to_string(with_parens, '*'); - case op_xor: + case Op::xor_: return to_string(with_parens, '^'); - case op_one: + case Op::one: return "1"; - case op_zero: + case Op::zero: return "0"; default: return "?"; @@ -247,11 +247,11 @@ FuncExpr * FuncExpr::bitSubExpr(int bit_offset) { switch (op_) { - case op_port: + case Op::port: if (port_->hasMembers()) { if (port_->size() == 1) { - LibertyPort *port = port_->findLibertyMember(0); - return makePort(port); + LibertyPort *port = port_->findLibertyMember(0); + return makePort(port); } else { if (bit_offset < port_->size()) { @@ -265,19 +265,19 @@ FuncExpr::bitSubExpr(int bit_offset) else // Always copy so the subexpr doesn't share memory. return makePort(port_); - case op_not: + case Op::not_: return makeNot(left_->bitSubExpr(bit_offset)); - case op_or: + case Op::or_: return makeOr(left_->bitSubExpr(bit_offset), - right_->bitSubExpr(bit_offset)); - case op_and: + right_->bitSubExpr(bit_offset)); + case Op::and_: return makeAnd(left_->bitSubExpr(bit_offset), - right_->bitSubExpr(bit_offset)); - case op_xor: + right_->bitSubExpr(bit_offset)); + case Op::xor_: return makeXor(left_->bitSubExpr(bit_offset), - right_->bitSubExpr(bit_offset)); - case op_one: - case op_zero: + right_->bitSubExpr(bit_offset)); + case Op::one: + case Op::zero: return this; } // Prevent warnings from lame compilers. @@ -288,17 +288,17 @@ bool FuncExpr::hasPort(const LibertyPort *port) const { switch (op_) { - case op_port: + case Op::port: return (port_ == port); - case op_not: + case Op::not_: return left_ && left_->hasPort(port); - case op_or: - case op_and: - case op_xor: + case Op::or_: + case Op::and_: + case Op::xor_: return (left_ && left_->hasPort(port)) || (right_ && right_->hasPort(port)); - case op_one: - case op_zero: + case Op::one: + case Op::zero: return false; } // Prevent warnings from lame compilers. @@ -316,18 +316,18 @@ FuncExpr::checkSize(size_t size) { size_t port_size; switch (op_) { - case op_port: + case Op::port: port_size = port_->size(); return !(port_size == size - || port_size == 1); - case op_not: + || port_size == 1); + case Op::not_: return left_->checkSize(size); - case op_or: - case op_and: - case op_xor: + case Op::or_: + case Op::and_: + case Op::xor_: return left_->checkSize(size) || right_->checkSize(size); - case op_one: - case op_zero: + case Op::one: + case Op::zero: return false; } // Prevent warnings from lame compilers. @@ -337,7 +337,7 @@ FuncExpr::checkSize(size_t size) FuncExpr * funcExprNot(FuncExpr *expr) { - if (expr->op() == FuncExpr::op_not) { + if (expr->op() == FuncExpr::Op::not_) { FuncExpr *not_expr = expr->left(); delete expr; return not_expr; @@ -346,43 +346,44 @@ funcExprNot(FuncExpr *expr) return FuncExpr::makeNot(expr); } -//////////////////////////////////////////////////////////////// - -FuncExprPortIterator::FuncExprPortIterator(const FuncExpr *expr) +LibertyPortSet +FuncExpr::ports() const { - findPorts(expr); - iter_.init(ports_); + LibertyPortSet ports; + findPorts(this, ports); + return ports; } void -FuncExprPortIterator::findPorts(const FuncExpr *expr) +FuncExpr::findPorts(const FuncExpr *expr, + LibertyPortSet &ports) const { if (expr) { - if (expr->op() == FuncExpr::op_port) - ports_.insert(expr->port()); + if (expr->op() == FuncExpr::Op::port) + ports.insert(expr->port()); else { - findPorts(expr->left()); - findPorts(expr->right()); + findPorts(expr->left(), ports); + findPorts(expr->right(), ports); } } } bool FuncExpr::equiv(const FuncExpr *expr1, - const FuncExpr *expr2) + const FuncExpr *expr2) { if (expr1 == nullptr && expr2 == nullptr) return true; else if (expr1 != nullptr && expr2 != nullptr - && expr1->op() == expr2->op()) { + && expr1->op() == expr2->op()) { switch (expr1->op()) { - case FuncExpr::op_port: + case FuncExpr::Op::port: return LibertyPort::equiv(expr1->port(), expr2->port()); - case FuncExpr::op_not: + case FuncExpr::Op::not_: return equiv(expr1->left(), expr2->left()); default: return equiv(expr1->left(), expr2->left()) - && equiv(expr1->right(), expr2->right()); + && equiv(expr1->right(), expr2->right()); } } else @@ -391,22 +392,22 @@ FuncExpr::equiv(const FuncExpr *expr1, bool FuncExpr::less(const FuncExpr *expr1, - const FuncExpr *expr2) + const FuncExpr *expr2) { if (expr1 != nullptr && expr2 != nullptr) { - Operator op1 = expr1->op(); - Operator op2 = expr2->op(); + Op op1 = expr1->op(); + Op op2 = expr2->op(); if (op1 == op2) { switch (expr1->op()) { - case FuncExpr::op_port: - return LibertyPort::less(expr1->port(), expr2->port()); - case FuncExpr::op_not: - return less(expr1->left(), expr2->left()); + case FuncExpr::Op::port: + return LibertyPort::less(expr1->port(), expr2->port()); + case FuncExpr::Op::not_: + return less(expr1->left(), expr2->left()); default: - if (equiv(expr1->left(), expr2->left())) - return less(expr1->right(), expr2->right()); - else - return less(expr1->left(), expr2->left()); + if (equiv(expr1->left(), expr2->left())) + return less(expr1->right(), expr2->right()); + else + return less(expr1->left(), expr2->left()); } } else diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index a68dd1965..407b152e1 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -71,7 +71,7 @@ InternalPowerAttrs::setWhen(FuncExpr *when) void InternalPowerAttrs::setModel(const RiseFall *rf, - InternalPowerModel *model) + InternalPowerModel *model) { models_[rf->index()] = model; } @@ -86,9 +86,9 @@ InternalPowerAttrs::setRelatedPgPin(const char *related_pg_pin) //////////////////////////////////////////////////////////////// InternalPower::InternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs) : + LibertyPort *port, + LibertyPort *related_port, + InternalPowerAttrs *attrs) : port_(port), related_port_(related_port), when_(attrs->when()), @@ -114,9 +114,9 @@ InternalPower::libertyCell() const float InternalPower::power(const RiseFall *rf, - const Pvt *pvt, - float in_slew, - float load_cap) + const Pvt *pvt, + float in_slew, + float load_cap) { InternalPowerModel *model = models_[rf->index()]; if (model) @@ -139,14 +139,14 @@ InternalPowerModel::~InternalPowerModel() float InternalPowerModel::power(const LibertyCell *cell, - const Pvt *pvt, - float in_slew, - float load_cap) const + const Pvt *pvt, + float in_slew, + float load_cap) const { if (model_) { float axis_value1, axis_value2, axis_value3; findAxisValues(in_slew, load_cap, - axis_value1, axis_value2, axis_value3); + axis_value1, axis_value2, axis_value3); return model_->findValue(cell, pvt, axis_value1, axis_value2, axis_value3); } else @@ -155,15 +155,15 @@ InternalPowerModel::power(const LibertyCell *cell, string InternalPowerModel::reportPower(const LibertyCell *cell, - const Pvt *pvt, - float in_slew, - float load_cap, - int digits) const + const Pvt *pvt, + float in_slew, + float load_cap, + int digits) const { if (model_) { float axis_value1, axis_value2, axis_value3; findAxisValues(in_slew, load_cap, - axis_value1, axis_value2, axis_value3); + axis_value1, axis_value2, axis_value3); const LibertyLibrary *library = cell->libertyLibrary(); return model_->reportValue("Power", cell, pvt, axis_value1, nullptr, axis_value2, axis_value3, @@ -174,11 +174,11 @@ InternalPowerModel::reportPower(const LibertyCell *cell, void InternalPowerModel::findAxisValues(float in_slew, - float load_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const + float load_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const { switch (model_->order()) { case 0: @@ -211,8 +211,8 @@ InternalPowerModel::findAxisValues(float in_slew, float InternalPowerModel::axisValue(const TableAxis *axis, - float in_slew, - float load_cap) const + float in_slew, + float load_cap) const { TableAxisVariable var = axis->variable(); if (var == TableAxisVariable::input_transition_time) diff --git a/liberty/LeakagePower.cc b/liberty/LeakagePower.cc index 73e4a618c..4219cd960 100644 --- a/liberty/LeakagePower.cc +++ b/liberty/LeakagePower.cc @@ -51,7 +51,7 @@ LeakagePowerAttrs::setPower(float power) //////////////////////////////////////////////////////////////// LeakagePower::LeakagePower(LibertyCell *cell, - LeakagePowerAttrs *attrs) : + LeakagePowerAttrs *attrs) : cell_(cell), when_(attrs->when()), power_(attrs->power()) diff --git a/liberty/LibExprReader.cc b/liberty/LibExprReader.cc index f4b873c2f..c9bc84bc2 100644 --- a/liberty/LibExprReader.cc +++ b/liberty/LibExprReader.cc @@ -37,9 +37,9 @@ namespace sta { FuncExpr * parseFuncExpr(const char *func, - LibertyCell *cell, - const char *error_msg, - Report *report) + LibertyCell *cell, + const char *error_msg, + Report *report) { if (func != nullptr && func[0] != '\0') { std::string func1(func); @@ -56,9 +56,9 @@ parseFuncExpr(const char *func, } LibExprReader::LibExprReader(const char *func, - LibertyCell *cell, - const char *error_msg, - Report *report) : + LibertyCell *cell, + const char *error_msg, + Report *report) : func_(func), cell_(cell), error_msg_(error_msg), @@ -97,7 +97,7 @@ LibExprReader::makeFuncExprNot(FuncExpr *arg) FuncExpr * LibExprReader::makeFuncExprXor(FuncExpr *arg1, - FuncExpr *arg2) + FuncExpr *arg2) { if (arg1 && arg2) return FuncExpr::makeXor(arg1, arg2); @@ -107,7 +107,7 @@ LibExprReader::makeFuncExprXor(FuncExpr *arg1, FuncExpr * LibExprReader::makeFuncExprAnd(FuncExpr *arg1, - FuncExpr *arg2) + FuncExpr *arg2) { if (arg1 && arg2) return FuncExpr::makeAnd(arg1, arg2); @@ -117,7 +117,7 @@ LibExprReader::makeFuncExprAnd(FuncExpr *arg1, FuncExpr * LibExprReader::makeFuncExprOr(FuncExpr *arg1, - FuncExpr *arg2) + FuncExpr *arg2) { if (arg1 && arg2) return FuncExpr::makeOr(arg1, arg2); diff --git a/liberty/LibExprReader.hh b/liberty/LibExprReader.hh index 3e15ac351..79d6d35f9 100644 --- a/liberty/LibExprReader.hh +++ b/liberty/LibExprReader.hh @@ -32,8 +32,8 @@ class LibertyCell; FuncExpr * parseFuncExpr(const char *func, - LibertyCell *cell, - const char *error_msg, - Report *report); + LibertyCell *cell, + const char *error_msg, + Report *report); } // namespace diff --git a/liberty/LibExprReaderPvt.hh b/liberty/LibExprReaderPvt.hh index 78cf21b61..4532b2e87 100644 --- a/liberty/LibExprReaderPvt.hh +++ b/liberty/LibExprReaderPvt.hh @@ -35,22 +35,22 @@ class LibExprReader { public: LibExprReader(const char *func, - LibertyCell *cell, - const char *error_msg, - Report *report); + LibertyCell *cell, + const char *error_msg, + Report *report); FuncExpr *makeFuncExprPort(const char *port_name); FuncExpr *makeFuncExprOr(FuncExpr *arg1, - FuncExpr *arg2); + FuncExpr *arg2); FuncExpr *makeFuncExprAnd(FuncExpr *arg1, - FuncExpr *arg2); + FuncExpr *arg2); FuncExpr *makeFuncExprXor(FuncExpr *arg1, - FuncExpr *arg2); + FuncExpr *arg2); FuncExpr *makeFuncExprNot(FuncExpr *arg); void setResult(FuncExpr *result); FuncExpr *result() { return result_; } void parseError(const char *msg); size_t copyInput(char *buf, - size_t max_size); + size_t max_size); Report *report() const { return report_; } private: diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 3cf867f20..cb986a59d 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -24,6 +24,7 @@ #include "Liberty.hh" +#include "ContainerHelpers.hh" #include "Mutex.hh" #include "EnumNameMap.hh" #include "Report.hh" @@ -45,15 +46,12 @@ #include "EquivCells.hh" #include "Network.hh" #include "PortDirection.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" +#include "Scene.hh" namespace sta { using std::string; -typedef Set LatchEnableSet; - void initLiberty() { @@ -67,7 +65,7 @@ deleteLiberty() } LibertyLibrary::LibertyLibrary(const char *name, - const char *filename) : + const char *filename) : ConcreteLibrary(name, filename, true), units_(new Units()), delay_model_type_(DelayModelType::table), // default @@ -101,7 +99,7 @@ LibertyLibrary::LibertyLibrary(const char *name, for (int i = 0; i != table_template_type_count; i++) { TableTemplateType type = static_cast(i); TableTemplate *scalar_template = new TableTemplate("scalar", nullptr, - nullptr, nullptr); + nullptr, nullptr); addTableTemplate(scalar_template, type); } @@ -116,26 +114,26 @@ LibertyLibrary::LibertyLibrary(const char *name, LibertyLibrary::~LibertyLibrary() { - bus_dcls_.deleteContents(); + deleteContents(bus_dcls_); for (int i = 0; i < table_template_type_count; i++) - template_maps_[i].deleteContents(); - scale_factors_map_.deleteContents(); + deleteContents(template_maps_[i]); + deleteContents(scale_factors_map_); delete scale_factors_; for (auto rf_index : RiseFall::rangeIndex()) { TableModel *model = wire_slew_degradation_tbls_[rf_index]; delete model; } - operating_conditions_.deleteContents(); - wireloads_.deleteContents(); - wire_load_selections_.deleteContents(); + deleteContents(operating_conditions_); + deleteContents(wireloads_); + deleteContents(wire_load_selections_); delete units_; // Also deletes default_ocv_derate_ - ocv_derate_map_.deleteContents(); + deleteContents(ocv_derate_map_); delete buffers_; delete inverters_; - driver_waveform_map_.deleteContents(); + deleteContents(driver_waveform_map_); delete driver_waveform_default_; } @@ -183,8 +181,8 @@ LibertyLibrary::buffers() while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); if (!cell->dontUse() - && cell->isBuffer()) - buffers_->push_back(cell); + && cell->isBuffer()) + buffers_->push_back(cell); } } return buffers_; @@ -205,7 +203,7 @@ LibertyLibrary::addBusDcl(BusDcl *bus_dcl) BusDcl * LibertyLibrary::findBusDcl(const char *name) const { - return bus_dcls_.findKey(name); + return findKey(bus_dcls_, name); } BusDclSeq @@ -219,16 +217,16 @@ LibertyLibrary::busDcls() const void LibertyLibrary::addTableTemplate(TableTemplate *tbl_template, - TableTemplateType type) + TableTemplateType type) { template_maps_[int(type)][tbl_template->name()] = tbl_template; } TableTemplate * LibertyLibrary::findTableTemplate(const char *name, - TableTemplateType type) + TableTemplateType type) { - return template_maps_[int(type)].findKey(name); + return findKey(template_maps_[int(type)], name); } TableTemplateSeq @@ -280,24 +278,24 @@ LibertyLibrary::findScaleFactors(const char *name) float LibertyLibrary::scaleFactor(ScaleFactorType type, - const Pvt *pvt) const + const Pvt *pvt) const { return scaleFactor(type, 0, nullptr, pvt); } float LibertyLibrary::scaleFactor(ScaleFactorType type, - const LibertyCell *cell, - const Pvt *pvt) const + const LibertyCell *cell, + const Pvt *pvt) const { return scaleFactor(type, 0, cell, pvt); } float LibertyLibrary::scaleFactor(ScaleFactorType type, - int rf_index, - const LibertyCell *cell, - const Pvt *pvt) const + int rf_index, + const LibertyCell *cell, + const Pvt *pvt) const { if (pvt == nullptr) pvt = default_operating_conditions_; @@ -312,11 +310,11 @@ LibertyLibrary::scaleFactor(ScaleFactorType type, scale_factors = scale_factors_; if (scale_factors) { float process_scale = 1.0F + (pvt->process() - nominal_process_) - * scale_factors->scale(type, ScaleFactorPvt::process, rf_index); + * scale_factors->scale(type, ScaleFactorPvt::process, rf_index); float temp_scale = 1.0F + (pvt->temperature() - nominal_temperature_) - * scale_factors->scale(type, ScaleFactorPvt::temp, rf_index); + * scale_factors->scale(type, ScaleFactorPvt::temp, rf_index); float volt_scale = 1.0F + (pvt->voltage() - nominal_voltage_) - * scale_factors->scale(type, ScaleFactorPvt::volt, rf_index); + * scale_factors->scale(type, ScaleFactorPvt::volt, rf_index); float scale = process_scale * temp_scale * volt_scale; return scale; } @@ -326,7 +324,7 @@ LibertyLibrary::scaleFactor(ScaleFactorType type, void LibertyLibrary::setWireSlewDegradationTable(TableModel *model, - const RiseFall *rf) + const RiseFall *rf) { int rf_index = rf->index(); if (wire_slew_degradation_tbls_[rf_index]) @@ -342,8 +340,8 @@ LibertyLibrary::wireSlewDegradationTable(const RiseFall *rf) const float LibertyLibrary::degradeWireSlew(const RiseFall *rf, - float in_slew, - float wire_delay) const + float in_slew, + float wire_delay) const { const TableModel *model = wireSlewDegradationTable(rf); if (model) @@ -354,8 +352,8 @@ LibertyLibrary::degradeWireSlew(const RiseFall *rf, float LibertyLibrary::degradeWireSlew(const TableModel *model, - float in_slew, - float wire_delay) const + float in_slew, + float wire_delay) const { switch (model->order()) { case 0: @@ -378,10 +376,10 @@ LibertyLibrary::degradeWireSlew(const TableModel *model, TableAxisVariable var1 = axis1->variable(); TableAxisVariable var2 = axis2->variable(); if (var1 == TableAxisVariable::output_pin_transition - && var2 == TableAxisVariable::connect_delay) + && var2 == TableAxisVariable::connect_delay) return model->findValue(in_slew, wire_delay, 0.0); else if (var1 == TableAxisVariable::connect_delay - && var2 == TableAxisVariable::output_pin_transition) + && var2 == TableAxisVariable::output_pin_transition) return model->findValue(wire_delay, in_slew, 0.0); else { criticalError(1117, "unsupported slew degradation table axes"); @@ -414,9 +412,9 @@ LibertyLibrary::checkSlewDegradationAxes(const TablePtr &table) TableAxisVariable var1 = axis1->variable(); TableAxisVariable var2 = axis2->variable(); return (var1 == TableAxisVariable::output_pin_transition - && var2 == TableAxisVariable::connect_delay) + && var2 == TableAxisVariable::connect_delay) || (var1 == TableAxisVariable::connect_delay - && var2 == TableAxisVariable::output_pin_transition); + && var2 == TableAxisVariable::output_pin_transition); } default: criticalError(1119, "unsupported slew degradation table axes"); @@ -426,7 +424,7 @@ LibertyLibrary::checkSlewDegradationAxes(const TablePtr &table) void LibertyLibrary::defaultMaxFanout(float &fanout, - bool &exists) const + bool &exists) const { fanout = default_max_fanout_; exists = default_max_fanout_exists_; @@ -441,7 +439,7 @@ LibertyLibrary::setDefaultMaxFanout(float fanout) void LibertyLibrary::defaultMaxSlew(float &slew, - bool &exists) const + bool &exists) const { slew = default_max_slew_; exists = default_max_slew_exists_; @@ -456,7 +454,7 @@ LibertyLibrary::setDefaultMaxSlew(float slew) void LibertyLibrary::defaultMaxCapacitance(float &cap, - bool &exists) const + bool &exists) const { cap = default_max_cap_; exists = default_max_cap_exists_; @@ -471,8 +469,8 @@ LibertyLibrary::setDefaultMaxCapacitance(float cap) void LibertyLibrary::defaultFanoutLoad(// Return values. - float &fanout, - bool &exists) const + float &fanout, + bool &exists) const { fanout = default_fanout_load_; exists = default_fanout_load_exists_; @@ -505,26 +503,26 @@ LibertyLibrary::setDefaultOutputPinCap(float cap) void LibertyLibrary::defaultIntrinsic(const RiseFall *rf, - // Return values. - float &intrinsic, - bool &exists) const + // Return values. + float &intrinsic, + bool &exists) const { default_intrinsic_.value(rf, intrinsic, exists); } void LibertyLibrary::setDefaultIntrinsic(const RiseFall *rf, - float value) + float value) { default_intrinsic_.setValue(rf, value); } void LibertyLibrary::defaultPinResistance(const RiseFall *rf, - const PortDirection *dir, - // Return values. - float &res, - bool &exists) const + const PortDirection *dir, + // Return values. + float &res, + bool &exists) const { if (dir->isAnyTristate()) defaultBidirectPinRes(rf, res, exists); @@ -534,32 +532,32 @@ LibertyLibrary::defaultPinResistance(const RiseFall *rf, void LibertyLibrary::defaultBidirectPinRes(const RiseFall *rf, - // Return values. - float &res, - bool &exists) const + // Return values. + float &res, + bool &exists) const { return default_inout_pin_res_.value(rf, res, exists); } void LibertyLibrary::setDefaultBidirectPinRes(const RiseFall *rf, - float value) + float value) { default_inout_pin_res_.setValue(rf, value); } void LibertyLibrary::defaultOutputPinRes(const RiseFall *rf, - // Return values. - float &res, - bool &exists) const + // Return values. + float &res, + bool &exists) const { default_output_pin_res_.value(rf, res, exists); } void LibertyLibrary::setDefaultOutputPinRes(const RiseFall *rf, - float value) + float value) { default_output_pin_res_.setValue(rf, value); } @@ -573,7 +571,7 @@ LibertyLibrary::addWireload(Wireload *wireload) Wireload * LibertyLibrary::findWireload(const char *name) const { - return wireloads_.findKey(name); + return findKey(wireloads_, name); } void @@ -597,7 +595,7 @@ LibertyLibrary::addWireloadSelection(WireloadSelection *selection) WireloadSelection * LibertyLibrary::findWireloadSelection(const char *name) const { - return wire_load_selections_.findKey(name); + return findKey(wire_load_selections_, name); } WireloadSelection * @@ -633,7 +631,7 @@ LibertyLibrary::addOperatingConditions(OperatingConditions *op_cond) OperatingConditions * LibertyLibrary::findOperatingConditions(const char *name) { - return operating_conditions_.findKey(name); + return findKey(operating_conditions_, name); } OperatingConditions * @@ -656,7 +654,7 @@ LibertyLibrary::inputThreshold(const RiseFall *rf) const void LibertyLibrary::setInputThreshold(const RiseFall *rf, - float th) + float th) { input_threshold_[rf->index()] = th; } @@ -669,7 +667,7 @@ LibertyLibrary::outputThreshold(const RiseFall *rf) const void LibertyLibrary::setOutputThreshold(const RiseFall *rf, - float th) + float th) { output_threshold_[rf->index()] = th; } @@ -682,7 +680,7 @@ LibertyLibrary::slewLowerThreshold(const RiseFall *rf) const void LibertyLibrary::setSlewLowerThreshold(const RiseFall *rf, - float th) + float th) { slew_lower_threshold_[rf->index()] = th; } @@ -695,7 +693,7 @@ LibertyLibrary::slewUpperThreshold(const RiseFall *rf) const void LibertyLibrary::setSlewUpperThreshold(const RiseFall *rf, - float th) + float th) { slew_upper_threshold_[rf->index()] = th; } @@ -714,7 +712,7 @@ LibertyLibrary::setSlewDerateFromLibrary(float derate) LibertyCell * LibertyLibrary::makeScaledCell(const char *name, - const char *filename) + const char *filename) { LibertyCell *cell = new LibertyCell(this, name, filename); return cell; @@ -723,10 +721,10 @@ LibertyLibrary::makeScaledCell(const char *name, //////////////////////////////////////////////////////////////// void -LibertyLibrary::makeCornerMap(LibertyLibrary *lib, - int ap_index, - Network *network, - Report *report) +LibertyLibrary::makeSceneMap(LibertyLibrary *lib, + int ap_index, + Network *network, + Report *report) { LibertyCellIterator cell_iter(lib); while (cell_iter.hasNext()) { @@ -734,30 +732,30 @@ LibertyLibrary::makeCornerMap(LibertyLibrary *lib, const char *name = cell->name(); LibertyCell *link_cell = network->findLibertyCell(name); if (link_cell) - makeCornerMap(link_cell, cell, ap_index, report); + makeSceneMap(link_cell, cell, ap_index, report); } } // Map a cell linked in the network to the corresponding liberty cell -// to use for delay calculation at a corner. +// to use for delay calculation at a scene. void -LibertyLibrary::makeCornerMap(LibertyCell *link_cell, - LibertyCell *corner_cell, - int ap_index, - Report *report) +LibertyLibrary::makeSceneMap(LibertyCell *link_cell, + LibertyCell *scene_cell, + int ap_index, + Report *report) { - link_cell->setCornerCell(corner_cell, ap_index); - makeCornerMap(link_cell, corner_cell, true, ap_index, report); + link_cell->setSceneCell(scene_cell, ap_index); + makeSceneMap(link_cell, scene_cell, true, ap_index, report); // Check for brain damage in the other direction. - makeCornerMap(corner_cell, link_cell, false, ap_index, report); + makeSceneMap(scene_cell, link_cell, false, ap_index, report); } void -LibertyLibrary::makeCornerMap(LibertyCell *cell1, - LibertyCell *cell2, - bool link, - int ap_index, - Report *report) +LibertyLibrary::makeSceneMap(LibertyCell *cell1, + LibertyCell *cell2, + bool link, + int ap_index, + Report *report) { LibertyCellPortBitIterator port_iter1(cell1); while (port_iter1.hasNext()) { @@ -766,15 +764,15 @@ LibertyLibrary::makeCornerMap(LibertyCell *cell1, LibertyPort *port2 = cell2->findLibertyPort(port_name); if (port2) { if (link) - port1->setCornerPort(port2, ap_index); + port1->setScenePort(port2, ap_index); } else report->warn(1110, "cell %s/%s port %s not found in cell %s/%s.", - cell1->library()->name(), - cell1->name(), - port_name, - cell2->library()->name(), - cell2->name()); + cell1->library()->name(), + cell1->name(), + port_name, + cell2->library()->name(), + cell2->name()); } for (TimingArcSet *arc_set1 : cell1->timing_arc_sets_) { @@ -789,35 +787,35 @@ LibertyLibrary::makeCornerMap(LibertyCell *cell1, arc_itr1++, arc_itr2++) { TimingArc *arc1 = *arc_itr1; TimingArc *arc2 = *arc_itr2; - if (TimingArc::equiv(arc1, arc2)) - arc1->setCornerArc(arc2, ap_index); - } + if (TimingArc::equiv(arc1, arc2)) + arc1->setSceneArc(arc2, ap_index); + } } } else report->warn(1111, "cell %s/%s %s -> %s timing group %s not found in cell %s/%s.", - cell1->library()->name(), - cell1->name(), - arc_set1->from() ? arc_set1->from()->name() : "", - arc_set1->to()->name(), - arc_set1->role()->to_string().c_str(), - cell2->library()->name(), - cell2->name()); + cell1->library()->name(), + cell1->name(), + arc_set1->from() ? arc_set1->from()->name() : "", + arc_set1->to()->name(), + arc_set1->role()->to_string().c_str(), + cell2->library()->name(), + cell2->name()); } } void -LibertyLibrary::checkCorners(LibertyCell *cell, - Corners *corners, +LibertyLibrary::checkScenes(LibertyCell *cell, + const SceneSeq &scenes, Report *report) { - for (const Corner *corner : *corners) { + for (const Scene *scene : scenes) { for (auto min_max : MinMax::range()) { - if (!cell->checkCornerCell(corner, min_max)) + if (!cell->checkSceneCell(scene, min_max)) report->error(1112, "Liberty cell %s/%s for corner %s/%s not found.", cell->libertyLibrary()->name(), cell->name(), - corner->name(), + scene->name().c_str(), min_max->to_string().c_str()); } } @@ -852,7 +850,7 @@ LibertyLibrary::setDefaultOcvDerate(OcvDerate *derate) OcvDerate * LibertyLibrary::findOcvDerate(const char *derate_name) { - return ocv_derate_map_.findKey(derate_name); + return findKey(ocv_derate_map_, derate_name); } void @@ -863,24 +861,32 @@ LibertyLibrary::addOcvDerate(OcvDerate *derate) void LibertyLibrary::addSupplyVoltage(const char *supply_name, - float voltage) + float voltage) { supply_voltage_map_[supply_name] = voltage; } void LibertyLibrary::supplyVoltage(const char *supply_name, - // Return value. - float &voltage, - bool &exists) const -{ - supply_voltage_map_.findKey(supply_name, voltage, exists); + // Return value. + float &voltage, + bool &exists) const +{ + auto itr = supply_voltage_map_.find(supply_name); + if (itr != supply_voltage_map_.end()) { + voltage = itr->second; + exists = true; + } + else { + voltage = 0.0; + exists = false; + } } bool LibertyLibrary::supplyExists(const char *supply_name) const { - return supply_voltage_map_.hasKey(supply_name); + return supply_voltage_map_.contains(supply_name); } DriverWaveform * @@ -922,8 +928,8 @@ LibertyCellIterator::next() //////////////////////////////////////////////////////////////// LibertyCell::LibertyCell(LibertyLibrary *library, - const char *name, - const char *filename) : + const char *name, + const char *filename) : ConcreteCell(name, filename, true, library), liberty_library_(library), area_(0.0), @@ -945,7 +951,6 @@ LibertyCell::LibertyCell(LibertyLibrary *library, test_cell_(nullptr), ocv_arc_depth_(0.0), ocv_derate_(nullptr), - is_disabled_constraint_(false), leakage_power_(0.0), leakage_power_exists_(false), has_internal_ports_(false), @@ -956,25 +961,25 @@ LibertyCell::LibertyCell(LibertyLibrary *library, LibertyCell::~LibertyCell() { - mode_defs_.deleteContents(); - latch_enables_.deleteContents(); + deleteContents(mode_defs_); + deleteContents(latch_enables_); - timing_arc_sets_.deleteContents(); - port_timing_arc_set_map_.deleteContents(); - timing_arc_set_from_map_.deleteContents(); - timing_arc_set_to_map_.deleteContents(); + deleteContents(timing_arc_sets_); + deleteContents(port_timing_arc_set_map_); + deleteContents(timing_arc_set_from_map_); + deleteContents(timing_arc_set_to_map_); deleteInternalPowerAttrs(); - internal_powers_.deleteContents(); - leakage_powers_.deleteContents(); + deleteContents(internal_powers_); + deleteContents(leakage_powers_); - sequentials_.deleteContents(); + deleteContents(sequentials_); delete statetable_; - bus_dcls_.deleteContents(); - scaled_cells_.deleteContents(); + deleteContents(bus_dcls_); + deleteContents(scaled_cells_); + deleteContents(ocv_derate_map_); delete test_cell_; - ocv_derate_map_.deleteContents(); } LibertyPort * @@ -995,9 +1000,9 @@ LibertyCell::findLibertyPortsMatching(PatternMatch *pattern) const if (port->hasMembers()) { LibertyPortMemberIterator port_iter2(port); while (port_iter2.hasNext()) { - LibertyPort *port2 = port_iter2.next(); - if (pattern->match(port2->name())) - matches.push_back(port2); + LibertyPort *port2 = port_iter2.next(); + if (pattern->match(port2->name())) + matches.push_back(port2); } } } @@ -1030,7 +1035,7 @@ LibertyCell::makeModeDef(const char *name) ModeDef * LibertyCell::findModeDef(const char *name) { - return mode_defs_.findKey(name); + return findKey(mode_defs_, name); } void @@ -1048,7 +1053,7 @@ LibertyCell::addBusDcl(BusDcl *bus_dcl) BusDcl * LibertyCell::findBusDcl(const char *name) const { - return bus_dcls_.findKey(name); + return findKey(bus_dcls_, name); } void @@ -1167,11 +1172,11 @@ LibertyCell::isBuffer() const bool LibertyCell::hasBufferFunc(const LibertyPort *input, - const LibertyPort *output) const + const LibertyPort *output) const { FuncExpr *func = output->function(); return func - && func->op() == FuncExpr::op_port + && func->op() == FuncExpr::Op::port && func->port() == input; } @@ -1189,19 +1194,19 @@ LibertyCell::isInverter() const bool LibertyCell::hasInverterFunc(const LibertyPort *input, - const LibertyPort *output) const + const LibertyPort *output) const { FuncExpr *func = output->function(); return func - && func->op() == FuncExpr::op_not - && func->left()->op() == FuncExpr::op_port + && func->op() == FuncExpr::Op::not_ + && func->left()->op() == FuncExpr::Op::port && func->left()->port() == input; } void LibertyCell::bufferPorts(// Return values. - LibertyPort *&input, - LibertyPort *&output) const + LibertyPort *&input, + LibertyPort *&output) const { input = nullptr; output = nullptr; @@ -1210,19 +1215,19 @@ LibertyCell::bufferPorts(// Return values. PortDirection *dir = port->direction(); if (dir->isInput()) { if (input) { - // More than one input. - input = nullptr; - output = nullptr; - break; + // More than one input. + input = nullptr; + output = nullptr; + break; } input = port; } else if (dir->isOutput()) { if (output) { - // More than one output. - input = nullptr; - output = nullptr; - break; + // More than one output. + input = nullptr; + output = nullptr; + break; } output = port; } @@ -1276,7 +1281,7 @@ LibertyCell::addInternalPowerAttrs(InternalPowerAttrs *attrs) void LibertyCell::deleteInternalPowerAttrs() { - for (auto attrs : internal_power_attrs_) { + for (InternalPowerAttrs *attrs : internal_power_attrs_) { attrs->deleteContents(); delete attrs; } @@ -1297,8 +1302,8 @@ LibertyCell::setLeakagePower(float leakage) void LibertyCell::leakagePower(// Return values. - float &leakage, - bool &exists) const + float &leakage, + bool &exists) const { leakage = leakage_power_; exists = leakage_power_exists_; @@ -1306,8 +1311,8 @@ LibertyCell::leakagePower(// Return values. void LibertyCell::finish(bool infer_latches, - Report *report, - Debug *debug) + Report *report, + Debug *debug) { translatePresetClrCheckRoles(); makeTimingArcMap(report); @@ -1325,14 +1330,14 @@ LibertyCell::findDefaultCondArcs() bool has_cond_arcs = false; for (auto set : *sets) { if (set->cond()) { - has_cond_arcs = true; - break; + has_cond_arcs = true; + break; } } if (has_cond_arcs) { for (auto set : *sets) { - if (!set->cond()) - set->setIsCondDefault(true); + if (!set->cond()) + set->setIsCondDefault(true); } } } @@ -1352,11 +1357,11 @@ LibertyCell::translatePresetClrCheckRoles() if (!pre_clr_ports.empty()) { for (auto arc_set : timing_arc_sets_) { - if (pre_clr_ports.findKey(arc_set->to())) { - if (arc_set->role() == TimingRole::setup()) - arc_set->setRole(TimingRole::recovery()); - else if (arc_set->role() == TimingRole::hold()) - arc_set->setRole(TimingRole::removal()); + if (findKey(pre_clr_ports, arc_set->to())) { + if (arc_set->role() == TimingRole::setup()) + arc_set->setRole(TimingRole::recovery()); + else if (arc_set->role() == TimingRole::hold()) + arc_set->setRole(TimingRole::removal()); } } } @@ -1368,22 +1373,22 @@ LibertyCell::makeTimingArcMap(Report *) // Filter duplicate timing arcs, keeping the later definition. for (auto arc_set : timing_arc_sets_) // The last definition will be left in the set. - timing_arc_set_map_.insert(arc_set); + timing_arc_set_set_.insert(arc_set); // Prune the arc sets not in the map. int j = 0; for (size_t i = 0; i < timing_arc_sets_.size(); i++) { TimingArcSet *arc_set = timing_arc_sets_[i]; - TimingArcSet *match = timing_arc_set_map_.findKey(arc_set); + TimingArcSet *match = findKey(timing_arc_set_set_, arc_set); if (match != arc_set) { // Unfortunately these errors are common in some brain damaged // libraries. // report->warn("cell %s/%s has duplicate %s -> %s %s timing groups.", - // library_->name(), - // name_, - // match->from()->name(), - // match->to()->name(), - // match->role()->asString()); + // library_->name(), + // name_, + // match->from()->name(), + // match->to()->name(), + // match->role()->asString()); delete arc_set; } else @@ -1392,7 +1397,7 @@ LibertyCell::makeTimingArcMap(Report *) } timing_arc_sets_.resize(j); - if (timing_arc_set_map_.size() != timing_arc_sets_.size()) + if (timing_arc_sets_.size() != timing_arc_sets_.size()) criticalError(1121, "timing arc count mismatch"); } @@ -1403,7 +1408,8 @@ LibertyCell::makeTimingArcPortMaps() LibertyPort *from = arc_set->from(); LibertyPort *to = arc_set->to(); LibertyPortPair port_pair(from, to); - TimingArcSetSeq *sets = port_timing_arc_set_map_.findKey(port_pair); + TimingArcSetSeq *sets = + findKey(port_timing_arc_set_map_, port_pair); if (sets == nullptr) { // First arc set for from/to ports. sets = new TimingArcSetSeq; @@ -1411,14 +1417,14 @@ LibertyCell::makeTimingArcPortMaps() } sets->push_back(arc_set); - sets = timing_arc_set_from_map_.findKey(from); + sets = findKey(timing_arc_set_from_map_, from); if (sets == nullptr) { sets = new TimingArcSetSeq; timing_arc_set_from_map_[from] = sets; } sets->push_back(arc_set); - sets = timing_arc_set_to_map_.findKey(to); + sets = findKey(timing_arc_set_to_map_, to); if (sets == nullptr) { sets = new TimingArcSetSeq; timing_arc_set_to_map_[to] = sets; @@ -1429,17 +1435,17 @@ LibertyCell::makeTimingArcPortMaps() const TimingArcSetSeq & LibertyCell::timingArcSets(const LibertyPort *from, - const LibertyPort *to) const + const LibertyPort *to) const { TimingArcSetSeq *arc_sets = nullptr; if (from && to) { LibertyPortPair port_pair(from, to); - arc_sets = port_timing_arc_set_map_.findKey(port_pair); + arc_sets = findKey(port_timing_arc_set_map_, port_pair); } else if (from) - arc_sets = timing_arc_set_from_map_.findKey(from); + arc_sets = findKey(timing_arc_set_from_map_, from); else if (to) - arc_sets = timing_arc_set_to_map_.findKey(to); + arc_sets = findKey(timing_arc_set_to_map_, to); if (arc_sets) return *arc_sets; @@ -1450,9 +1456,9 @@ LibertyCell::timingArcSets(const LibertyPort *from, } TimingArcSet * -LibertyCell::findTimingArcSet(TimingArcSet *key) const +LibertyCell::findTimingArcSet(TimingArcSet *arc_set) const { - return timing_arc_set_map_.findKey(key); + return findKey(timing_arc_set_set_, arc_set); } TimingArcSet * @@ -1470,21 +1476,21 @@ LibertyCell::timingArcSetCount() const bool LibertyCell::hasTimingArcs(LibertyPort *port) const { - return timing_arc_set_from_map_.findKey(port) - || timing_arc_set_to_map_.findKey(port); + return findKey(timing_arc_set_from_map_, port) + || findKey(timing_arc_set_to_map_, port); } void LibertyCell::makeSequential(int size, - bool is_register, - FuncExpr *clk, - FuncExpr *data, - FuncExpr *clear, - FuncExpr *preset, - LogicValue clr_preset_out, - LogicValue clr_preset_out_inv, - LibertyPort *output, - LibertyPort *output_inv) + bool is_register, + FuncExpr *clk, + FuncExpr *data, + FuncExpr *clear, + FuncExpr *preset, + LogicValue clr_preset_out, + LogicValue clr_preset_out_inv, + LibertyPort *output, + LibertyPort *output_inv) { for (int bit = 0; bit < size; bit++) { FuncExpr *clk_bit = nullptr; @@ -1506,9 +1512,9 @@ LibertyCell::makeSequential(int size, if (output_inv && output_inv->hasMembers()) out_inv_bit = output_inv->findLibertyMember(bit); Sequential *seq = new Sequential(is_register, clk_bit, data_bit, - clear_bit,preset_bit, - clr_preset_out, clr_preset_out_inv, - out_bit, out_inv_bit); + clear_bit,preset_bit, + clr_preset_out, clr_preset_out_inv, + out_bit, out_inv_bit); sequentials_.push_back(seq); port_to_seq_map_[seq->output()] = seq; port_to_seq_map_[seq->outputInv()] = seq; @@ -1518,7 +1524,7 @@ LibertyCell::makeSequential(int size, Sequential * LibertyCell::outputPortSequential(LibertyPort *port) { - return port_to_seq_map_.findKey(port); + return findKey(port_to_seq_map_, port); } bool @@ -1538,7 +1544,7 @@ LibertyCell::makeStatetable(LibertyPortSeq &input_ports, void LibertyCell::addScaledCell(OperatingConditions *op_cond, - LibertyCell *scaled_cell) + LibertyCell *scaled_cell) { scaled_cells_[op_cond] = scaled_cell; @@ -1567,9 +1573,9 @@ LibertyCell::addScaledCell(OperatingConditions *op_cond, const TimingArc *scaled_arc = *arc_itr2; if (TimingArc::equiv(arc, scaled_arc)) { - TimingModel *model = scaled_arc->model(); - model->setIsScaled(true); - arc->addScaledModel(op_cond, model); + TimingModel *model = scaled_arc->model(); + model->setIsScaled(true); + arc->addScaledModel(op_cond, model); } } } @@ -1594,53 +1600,41 @@ LibertyCell::setTestCell(TestCell *test) test_cell_ = test; } -void -LibertyCell::setIsDisabledConstraint(bool is_disabled) -{ - is_disabled_constraint_ = is_disabled; -} - LibertyCell * -LibertyCell::cornerCell(const Corner *corner, - const MinMax *min_max) +LibertyCell::sceneCell(const Scene *scene, + const MinMax *min_max) { - return cornerCell(corner->libertyIndex(min_max)); + return sceneCell(scene->libertyIndex(min_max)); } LibertyCell * -LibertyCell::cornerCell(const DcalcAnalysisPt *dcalc_ap) +LibertyCell::sceneCell(int ap_index) { - return cornerCell(dcalc_ap->libertyIndex()); -} - -LibertyCell * -LibertyCell::cornerCell(int ap_index) -{ - if (corner_cells_.empty()) + if (scene_cells_.empty()) return this; - else if (ap_index < static_cast(corner_cells_.size())) - return corner_cells_[ap_index]; + else if (ap_index < static_cast(scene_cells_.size())) + return scene_cells_[ap_index]; else return nullptr; } bool -LibertyCell::checkCornerCell(const Corner *corner, +LibertyCell::checkSceneCell(const Scene *scene, const MinMax *min_max) const { - unsigned lib_index = corner->libertyIndex(min_max); - return corner_cells_.empty() - || (lib_index < corner_cells_.size() - && corner_cells_[lib_index]); + unsigned lib_index = scene->libertyIndex(min_max); + return scene_cells_.empty() + || (lib_index < scene_cells_.size() + && scene_cells_[lib_index]); } void -LibertyCell::setCornerCell(LibertyCell *corner_cell, - int ap_index) +LibertyCell::setSceneCell(LibertyCell *scene_cell, + int ap_index) { - if (ap_index >= static_cast(corner_cells_.size())) - corner_cells_.resize(ap_index + 1); - corner_cells_[ap_index] = corner_cell; + if (ap_index >= static_cast(scene_cells_.size())) + scene_cells_.resize(ap_index + 1); + scene_cells_[ap_index] = scene_cell; } //////////////////////////////////////////////////////////////// @@ -1675,7 +1669,7 @@ LibertyCell::setOcvDerate(OcvDerate *derate) OcvDerate * LibertyCell::findOcvDerate(const char *derate_name) { - return ocv_derate_map_.findKey(derate_name); + return findKey(ocv_derate_map_, derate_name); } void @@ -1691,13 +1685,13 @@ class LatchEnable { public: LatchEnable(LibertyPort *data, - LibertyPort *enable, - const RiseFall *enable_edge, - FuncExpr *enable_func, - LibertyPort *output, - TimingArcSet *d_to_q, - TimingArcSet *en_to_q, - TimingArcSet *setup_check); + LibertyPort *enable, + const RiseFall *enable_edge, + FuncExpr *enable_func, + LibertyPort *output, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check); LibertyPort *data() const { return data_; } LibertyPort *output() const { return output_; } LibertyPort *enable() const { return enable_; } @@ -1719,13 +1713,13 @@ class LatchEnable }; LatchEnable::LatchEnable(LibertyPort *data, - LibertyPort *enable, - const RiseFall *enable_edge, - FuncExpr *enable_func, - LibertyPort *output, - TimingArcSet *d_to_q, - TimingArcSet *en_to_q, - TimingArcSet *setup_check) : + LibertyPort *enable, + const RiseFall *enable_edge, + FuncExpr *enable_func, + LibertyPort *output, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check) : data_(data), enable_(enable), enable_edge_(enable_edge), @@ -1742,20 +1736,20 @@ LatchEnable::LatchEnable(LibertyPort *data, // Use timing arcs rather than sequentials (because they are optional). void LibertyCell::makeLatchEnables(Report *report, - Debug *debug) + Debug *debug) { if (hasSequentials() || hasInferedRegTimingArcs()) { for (auto en_to_q : timing_arc_sets_) { if (en_to_q->role() == TimingRole::latchEnToQ()) { - LibertyPort *en = en_to_q->from(); - LibertyPort *q = en_to_q->to(); + LibertyPort *en = en_to_q->from(); + LibertyPort *q = en_to_q->to(); for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { - if (d_to_q->role() == TimingRole::latchDtoQ() + if (d_to_q->role() == TimingRole::latchDtoQ() && condMatch(en_to_q, d_to_q)) { - LibertyPort *d = d_to_q->from(); + LibertyPort *d = d_to_q->from(); const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); - if (en_rf) { + if (en_rf) { TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, report); LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, @@ -1784,7 +1778,7 @@ LibertyCell::makeLatchEnables(Report *report, } } } - } + } } } } @@ -1845,15 +1839,15 @@ LibertyCell::findLatchSetup(const LibertyPort *d, FuncExpr * LibertyCell::findLatchEnableFunc(const LibertyPort *d, - const LibertyPort *en, + const LibertyPort *en, const RiseFall *en_rf) const { for (auto seq : sequentials_) { if (seq->isLatch() - && seq->data() - && seq->data()->hasPort(d) - && seq->clock() - && seq->clock()->hasPort(en)) { + && seq->data() + && seq->data()->hasPort(d) + && seq->clock() + && seq->clock()->hasPort(en)) { FuncExpr *en_func = seq->clock(); TimingSense en_sense = en_func->portTimingSense(en); if ((en_sense == TimingSense::positive_unate @@ -1868,17 +1862,17 @@ LibertyCell::findLatchEnableFunc(const LibertyPort *d, LatchEnable * LibertyCell::makeLatchEnable(LibertyPort *d, - LibertyPort *en, + LibertyPort *en, const RiseFall *en_rf, - LibertyPort *q, - TimingArcSet *d_to_q, - TimingArcSet *en_to_q, - TimingArcSet *setup_check, - Debug *debug) + LibertyPort *q, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check, + Debug *debug) { FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf); LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q, - d_to_q, en_to_q, setup_check); + d_to_q, en_to_q, setup_check); latch_enables_.push_back(latch_enable); latch_d_to_q_map_[d_to_q] = latch_enable; latch_check_map_[setup_check] = latch_enable; @@ -1902,25 +1896,25 @@ LibertyCell::inferLatchRoles(Report *report, { if (hasInferedRegTimingArcs()) { // Hunt down potential latch D/EN/Q triples. - LatchEnableSet latch_enables; + std::set latch_enables; for (TimingArcSet *en_to_q : timingArcSets()) { // Locate potential d->q arcs from reg clk->q arcs. if (en_to_q->role() == TimingRole::regClkToQ()) { - LibertyPort *en = en_to_q->from(); - LibertyPort *q = en_to_q->to(); + LibertyPort *en = en_to_q->from(); + LibertyPort *q = en_to_q->to(); for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { - // Look for combinational d->q arcs. - const TimingRole *d_to_q_role = d_to_q->role(); - if (((d_to_q_role == TimingRole::combinational() + // Look for combinational d->q arcs. + const TimingRole *d_to_q_role = d_to_q->role(); + if (((d_to_q_role == TimingRole::combinational() && d_to_q->arcCount() == 2 && (d_to_q->sense() == TimingSense::positive_unate || d_to_q->sense() == TimingSense::negative_unate)) // Previously identified as D->Q arc. || d_to_q_role == TimingRole::latchDtoQ()) && condMatch(en_to_q, d_to_q)) { - LibertyPort *d = d_to_q->from(); + LibertyPort *d = d_to_q->from(); const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); - if (en_rf) { + if (en_rf) { TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, en_to_q, report); makeLatchEnable(d, en, en_rf, q, d_to_q, en_to_q, setup_check, debug); @@ -1928,7 +1922,7 @@ LibertyCell::inferLatchRoles(Report *report, en_to_q->setRole(TimingRole::latchEnToQ()); } } - } + } } } } @@ -1936,12 +1930,12 @@ LibertyCell::inferLatchRoles(Report *report, void LibertyCell::latchEnable(const TimingArcSet *d_to_q_set, - // Return values. - const LibertyPort *&enable_port, - const FuncExpr *&enable_func, - const RiseFall *&enable_edge) const + // Return values. + const LibertyPort *&enable_port, + const FuncExpr *&enable_func, + const RiseFall *&enable_edge) const { - LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set); + LatchEnable *latch_enable = findKey(latch_d_to_q_map_, d_to_q_set); if (latch_enable) { enable_port = latch_enable->enable(); enable_func = latch_enable->enableFunc(); @@ -1957,7 +1951,7 @@ LibertyCell::latchEnable(const TimingArcSet *d_to_q_set, const RiseFall * LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set) { - LatchEnable *latch_enable = latch_check_map_.findKey(check_set); + LatchEnable *latch_enable = findKey(latch_check_map_, check_set); if (latch_enable) return latch_enable->enableEdge(); else @@ -1965,7 +1959,7 @@ LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set) } void -LibertyCell::ensureVoltageWaveforms(const DcalcAnalysisPtSeq &dcalc_aps) +LibertyCell::ensureVoltageWaveforms(const SceneSeq &scenes) { if (!have_voltage_waveforms_) { LockGuard lock(waveform_lock_); @@ -1978,12 +1972,14 @@ LibertyCell::ensureVoltageWaveforms(const DcalcAnalysisPtSeq &dcalc_aps) criticalError(1120, "library missing vdd"); for (TimingArcSet *arc_set : timingArcSets()) { for (TimingArc *arc : arc_set->arcs()) { - for (const DcalcAnalysisPt *dcalc_ap : dcalc_aps) { - GateTableModel *model = arc->gateTableModel(dcalc_ap); - if (model) { - OutputWaveforms *output_waveforms = model->outputWaveforms(); - if (output_waveforms) - output_waveforms->ensureVoltageWaveforms(vdd); + for (const Scene *scene : scenes) { + for (const MinMax *min_max : MinMax::range()) { + GateTableModel *model = arc->gateTableModel(scene, min_max); + if (model) { + OutputWaveforms *output_waveforms = model->outputWaveforms(); + if (output_waveforms) + output_waveforms->ensureVoltageWaveforms(vdd); + } } } } @@ -2070,13 +2066,13 @@ LibertyCellPortBitIterator::next() //////////////////////////////////////////////////////////////// LibertyPort::LibertyPort(LibertyCell *cell, - const char *name, - bool is_bus, - BusDcl *bus_dcl, + const char *name, + bool is_bus, + BusDcl *bus_dcl, int from_index, - int to_index, - bool is_bundle, - ConcretePortSeq *members) : + int to_index, + bool is_bundle, + ConcretePortSeq *members) : ConcretePort(name, is_bus, from_index, to_index, is_bundle, members, cell), liberty_cell_(cell), bus_dcl_(bus_dcl), @@ -2107,7 +2103,6 @@ LibertyPort::LibertyPort(LibertyCell *cell, isolation_cell_enable_(false), level_shifter_data_(false), is_switch_(false), - is_disabled_constraint_(false), is_pad_(false) { liberty_port_ = this; @@ -2236,8 +2231,8 @@ LibertyPort::setCapacitance(float cap) void LibertyPort::setCapacitance(const RiseFall *rf, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { capacitance_.setValue(rf, min_max, cap); if (hasMembers()) { @@ -2269,7 +2264,7 @@ LibertyPort::capacitance(const MinMax *min_max) const float LibertyPort::capacitance(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { float cap; bool exists; @@ -2282,19 +2277,19 @@ LibertyPort::capacitance(const RiseFall *rf, void LibertyPort::capacitance(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists) const + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const { capacitance_.value(rf, min_max, cap, exists); } float LibertyPort::capacitance(const RiseFall *rf, - const MinMax *min_max, - const OperatingConditions *op_cond, - const Pvt *pvt) const + const MinMax *min_max, + const OperatingConditions *op_cond, + const Pvt *pvt) const { if (scaled_ports_) { LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; @@ -2325,22 +2320,22 @@ LibertyPort::driveResistance() const // Min/max "drive" for all cell timing arcs. float LibertyPort::driveResistance(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { float max_drive = min_max->initValue(); bool found_drive = false; for (TimingArcSet *arc_set : liberty_cell_->timingArcSets(nullptr, this)) { if (!arc_set->role()->isTimingCheck()) { for (TimingArc *arc : arc_set->arcs()) { - if (rf == nullptr - || arc->toEdge()->asRiseFall() == rf) { + if (rf == nullptr + || arc->toEdge()->asRiseFall() == rf) { float drive = arc->driveResistance(); if (drive > 0.0) { if (min_max->compare(drive, max_drive)) max_drive = drive; - found_drive = true; - } - } + found_drive = true; + } + } } } } @@ -2358,7 +2353,7 @@ LibertyPort::intrinsicDelay(const StaState *sta) const ArcDelay LibertyPort::intrinsicDelay(const RiseFall *rf, - const MinMax *min_max, + const MinMax *min_max, const StaState *sta) const { ArcDelay max_delay = min_max->initValue(); @@ -2366,14 +2361,14 @@ LibertyPort::intrinsicDelay(const RiseFall *rf, for (TimingArcSet *arc_set : liberty_cell_->timingArcSets(nullptr, this)) { if (!arc_set->role()->isTimingCheck()) { for (TimingArc *arc : arc_set->arcs()) { - if (rf == nullptr - || arc->toEdge()->asRiseFall() == rf) { + if (rf == nullptr + || arc->toEdge()->asRiseFall() == rf) { ArcDelay delay = arc->intrinsicDelay(); if (delayGreater(delay, 0.0, sta)) { - if (delayGreater(delay, max_delay, min_max, sta)) - max_delay = delay; - found_delay = true; - } + if (delayGreater(delay, max_delay, min_max, sta)) + max_delay = delay; + found_delay = true; + } } } } @@ -2411,7 +2406,7 @@ LibertyPort::setTristateEnable(FuncExpr *enable) while (member_iter.hasNext()) { LibertyPort *port_bit = member_iter.next(); FuncExpr *sub_expr = - (enable) ? enable->bitSubExpr(port_bit->busBitIndex()) : nullptr; + (enable) ? enable->bitSubExpr(port_bit->busBitIndex()) : nullptr; port_bit->setTristateEnable(sub_expr); } } @@ -2419,16 +2414,16 @@ LibertyPort::setTristateEnable(FuncExpr *enable) void LibertyPort::slewLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const + // Return values. + float &limit, + bool &exists) const { slew_limit_.value(min_max, limit, exists); } void LibertyPort::setSlewLimit(float slew, - const MinMax *min_max) + const MinMax *min_max) { slew_limit_.setValue(min_max, slew); setMemberMinMaxFloat(slew, min_max, &LibertyPort::setSlewLimit); @@ -2436,16 +2431,16 @@ LibertyPort::setSlewLimit(float slew, void LibertyPort::capacitanceLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const + // Return values. + float &limit, + bool &exists) const { return cap_limit_.value(min_max, limit, exists); } void LibertyPort::setCapacitanceLimit(float cap, - const MinMax *min_max) + const MinMax *min_max) { cap_limit_.setValue(min_max, cap); setMemberMinMaxFloat(cap, min_max, &LibertyPort::setCapacitanceLimit); @@ -2453,8 +2448,8 @@ LibertyPort::setCapacitanceLimit(float cap, void LibertyPort::fanoutLoad(// Return values. - float &fanout_load, - bool &exists) const + float &fanout_load, + bool &exists) const { fanout_load = fanout_load_; exists = fanout_load_exists_; @@ -2470,25 +2465,25 @@ LibertyPort::setFanoutLoad(float fanout_load) void LibertyPort::fanoutLimit(const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const + // Return values. + float &limit, + bool &exists) const { return fanout_limit_.value(min_max, limit, exists); } void LibertyPort::setFanoutLimit(float fanout, - const MinMax *min_max) + const MinMax *min_max) { fanout_limit_.setValue(min_max, fanout); } void LibertyPort::minPeriod(const OperatingConditions *op_cond, - const Pvt *pvt, - float &min_period, - bool &exists) const + const Pvt *pvt, + float &min_period, + bool &exists) const { if (scaled_ports_) { LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; @@ -2499,13 +2494,13 @@ LibertyPort::minPeriod(const OperatingConditions *op_cond, } LibertyLibrary *lib = liberty_cell_->libertyLibrary(); min_period = min_period_ * lib->scaleFactor(ScaleFactorType::min_period, - liberty_cell_, pvt); + liberty_cell_, pvt); exists = min_period_exists_; } void LibertyPort::minPeriod(float &min_period, - bool &exists) const + bool &exists) const { min_period = min_period_; exists = min_period_exists_; @@ -2527,8 +2522,8 @@ LibertyPort::setMinPeriod(float min_period) void LibertyPort::minPulseWidth(const RiseFall *hi_low, - float &min_width, - bool &exists) const + float &min_width, + bool &exists) const { int hi_low_index = hi_low->index(); min_width = min_pulse_width_[hi_low_index]; @@ -2537,7 +2532,7 @@ LibertyPort::minPulseWidth(const RiseFall *hi_low, void LibertyPort::setMinPulseWidth(const RiseFall *hi_low, - float min_width) + float min_width) { int hi_low_index = hi_low->index(); min_pulse_width_[hi_low_index] = min_width; @@ -2553,18 +2548,18 @@ LibertyPort::setMinPulseWidth(const RiseFall *hi_low, bool LibertyPort::equiv(const LibertyPort *port1, - const LibertyPort *port2) + const LibertyPort *port2) { return (port1 == nullptr && port2 == nullptr) || (port1 != nullptr && port2 != nullptr - && stringEq(port1->name(), port2->name()) - && port1->direction() == port2->direction() - && port1->pwr_gnd_type_ == port2->pwr_gnd_type_); + && stringEq(port1->name(), port2->name()) + && port1->direction() == port2->direction() + && port1->pwr_gnd_type_ == port2->pwr_gnd_type_); } bool LibertyPort::less(const LibertyPort *port1, - const LibertyPort *port2) + const LibertyPort *port2) { if (port1 == nullptr && port2 != nullptr) return true; @@ -2582,7 +2577,7 @@ LibertyPort::less(const LibertyPort *port1, void LibertyPort::addScaledPort(OperatingConditions *op_cond, - LibertyPort *scaled_port) + LibertyPort *scaled_port) { if (scaled_ports_ == nullptr) scaled_ports_ = new ScaledPortMap; @@ -2683,18 +2678,12 @@ LibertyPort::setIsSwitch(bool is_switch) void LibertyPort::setPulseClk(const RiseFall *trigger, - const RiseFall *sense) + const RiseFall *sense) { pulse_clk_trigger_ = trigger; pulse_clk_sense_ = sense; } -void -LibertyPort::setIsDisabledConstraint(bool is_disabled) -{ - is_disabled_constraint_ = is_disabled; -} - void LibertyPort::setIsPad(bool is_pad) { @@ -2702,60 +2691,48 @@ LibertyPort::setIsPad(bool is_pad) } LibertyPort * -LibertyPort::cornerPort(const Corner *corner, - const MinMax *min_max) -{ - return cornerPort(corner->libertyIndex(min_max)); -} - -const LibertyPort * -LibertyPort::cornerPort(const Corner *corner, - const MinMax *min_max) const -{ - return cornerPort(corner->libertyIndex(min_max)); -} - -LibertyPort * -LibertyPort::cornerPort(const DcalcAnalysisPt *dcalc_ap) +LibertyPort::scenePort(const Scene *scene, + const MinMax *min_max) { - return cornerPort(dcalc_ap->libertyIndex()); + return scenePort(scene->libertyIndex(min_max)); } const LibertyPort * -LibertyPort::cornerPort(const DcalcAnalysisPt *dcalc_ap) const +LibertyPort::scenePort(const Scene *scene, + const MinMax *min_max) const { - return cornerPort(dcalc_ap->libertyIndex()); + return scenePort(scene->libertyIndex(min_max)); } LibertyPort * -LibertyPort::cornerPort(int ap_index) +LibertyPort::scenePort(int ap_index) { - if (corner_ports_.empty()) + if (scene_ports_.empty()) return this; - else if (ap_index < static_cast(corner_ports_.size())) - return corner_ports_[ap_index]; + else if (ap_index < static_cast(scene_ports_.size())) + return scene_ports_[ap_index]; else return nullptr; } const LibertyPort * -LibertyPort::cornerPort(int ap_index) const +LibertyPort::scenePort(int ap_index) const { - if (corner_ports_.empty()) + if (scene_ports_.empty()) return this; - else if (ap_index < static_cast(corner_ports_.size())) - return corner_ports_[ap_index]; + else if (ap_index < static_cast(scene_ports_.size())) + return scene_ports_[ap_index]; else return nullptr; } void -LibertyPort::setCornerPort(LibertyPort *corner_port, - int ap_index) +LibertyPort::setScenePort(LibertyPort *scene_port, + int ap_index) { - if (ap_index >= static_cast(corner_ports_.size())) - corner_ports_.resize(ap_index + 1); - corner_ports_[ap_index] = corner_port; + if (ap_index >= static_cast(scene_ports_.size())) + scene_ports_.resize(ap_index + 1); + scene_ports_[ap_index] = scene_port; } const char * @@ -2892,8 +2869,8 @@ LibertyPort::setClkTreeDelay(const TableModel *model, void LibertyPort::setMemberFlag(bool value, - const std::function &setter) + const std::function &setter) { if (hasMembers()) { LibertyPortMemberIterator member_iter(this); @@ -2906,8 +2883,8 @@ LibertyPort::setMemberFlag(bool value, void LibertyPort::setMemberFloat(float value, - const std::function &setter) + const std::function &setter) { if (hasMembers()) { LibertyPortMemberIterator member_iter(this); @@ -2920,10 +2897,10 @@ LibertyPort::setMemberFloat(float value, void LibertyPort::setMemberMinMaxFloat(float value, - const MinMax *min_max, - const std::function &setter) + const MinMax *min_max, + const std::function &setter) { if (hasMembers()) { LibertyPortMemberIterator member_iter(this); @@ -2955,20 +2932,20 @@ LibertyPortLess::operator()(const LibertyPort *port1, bool LibertyPortNameLess::operator()(const LibertyPort *port1, - const LibertyPort *port2) const + const LibertyPort *port2) const { return stringLess(port1->name(), port2->name()); } bool LibertyPortPairLess::operator()(const LibertyPortPair &pair1, - const LibertyPortPair &pair2) const + const LibertyPortPair &pair2) const { ObjectId id1 = pair1.first ? pair1.first->id() : 0; ObjectId id2 = pair2.first ? pair2.first->id() : 0; return id1 < id2 || (id1 == id2 - && pair1.second->id() < pair2.second->id()); + && pair1.second->id() < pair2.second->id()); } //////////////////////////////////////////////////////////////// @@ -2998,8 +2975,8 @@ LibertyPortMemberIterator::next() //////////////////////////////////////////////////////////////// BusDcl::BusDcl(const char *name, - int from, - int to) : + int from, + int to) : name_(name), from_(from), to_(to) @@ -3015,13 +2992,13 @@ ModeDef::ModeDef(const char *name) : ModeDef::~ModeDef() { - values_.deleteContents(); + deleteContents(values_); } ModeValueDef * ModeDef::defineValue(const char *value, - FuncExpr *cond, - const char *sdf_cond) + FuncExpr *cond, + const char *sdf_cond) { ModeValueDef *val_def = new ModeValueDef(value, cond, sdf_cond); values_[val_def->value()] = val_def; @@ -3037,8 +3014,8 @@ ModeDef::findValueDef(const char *value) //////////////////////////////////////////////////////////////// ModeValueDef::ModeValueDef(const char *value, - FuncExpr *cond, - const char *sdf_cond) : + FuncExpr *cond, + const char *sdf_cond) : value_(value), cond_(cond), sdf_cond_(sdf_cond ? sdf_cond : "") @@ -3111,8 +3088,8 @@ TableTemplate::setAxis3(TableAxisPtr axis) //////////////////////////////////////////////////////////////// Pvt::Pvt(float process, - float voltage, - float temperature) : + float voltage, + float temperature) : process_(process), voltage_(voltage), temperature_(temperature) @@ -3146,10 +3123,10 @@ OperatingConditions::OperatingConditions(const char *name) : } OperatingConditions::OperatingConditions(const char *name, - float process, - float voltage, - float temperature, - WireloadTree wire_load_tree) : + float process, + float voltage, + float temperature, + WireloadTree wire_load_tree) : Pvt(process, voltage, temperature), name_(name), wire_load_tree_(wire_load_tree) @@ -3246,7 +3223,7 @@ ScaleFactors::ScaleFactors(const char *name) : for (int type = 0; type < scale_factor_type_count; type++) { for (int pvt = 0; pvt < scale_factor_pvt_count; pvt++) { for (auto rf_index : RiseFall::rangeIndex()) { - scales_[type][pvt][rf_index] = 0.0; + scales_[type][pvt][rf_index] = 0.0; } } } @@ -3254,40 +3231,40 @@ ScaleFactors::ScaleFactors(const char *name) : void ScaleFactors::setScale(ScaleFactorType type, - ScaleFactorPvt pvt, - const RiseFall *rf, - float scale) + ScaleFactorPvt pvt, + const RiseFall *rf, + float scale) { scales_[int(type)][int(pvt)][rf->index()] = scale; } void ScaleFactors::setScale(ScaleFactorType type, - ScaleFactorPvt pvt, - float scale) + ScaleFactorPvt pvt, + float scale) { scales_[int(type)][int(pvt)][0] = scale; } float ScaleFactors::scale(ScaleFactorType type, - ScaleFactorPvt pvt, - const RiseFall *rf) + ScaleFactorPvt pvt, + const RiseFall *rf) { return scales_[int(type)][int(pvt)][rf->index()]; } float ScaleFactors::scale(ScaleFactorType type, - ScaleFactorPvt pvt, - int rf_index) + ScaleFactorPvt pvt, + int rf_index) { return scales_[int(type)][int(pvt)][rf_index]; } float ScaleFactors::scale(ScaleFactorType type, - ScaleFactorPvt pvt) + ScaleFactorPvt pvt) { return scales_[int(type)][int(pvt)][0]; } @@ -3306,15 +3283,15 @@ ScaleFactors::print() printf("%10s ", scaleFactorTypeName(type)); for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { if (scaleFactorTypeRiseFallSuffix(type) - || scaleFactorTypeRiseFallPrefix(type) - || scaleFactorTypeLowHighSuffix(type)) { - printf(" %.3f,%.3f", - scales_[type_index][pvt_index][RiseFall::riseIndex()], - scales_[type_index][pvt_index][RiseFall::fallIndex()]); + || scaleFactorTypeRiseFallPrefix(type) + || scaleFactorTypeLowHighSuffix(type)) { + printf(" %.3f,%.3f", + scales_[type_index][pvt_index][RiseFall::riseIndex()], + scales_[type_index][pvt_index][RiseFall::fallIndex()]); } else { - printf(" %.3f", - scales_[type_index][pvt_index][0]); + printf(" %.3f", + scales_[type_index][pvt_index][0]); } } printf("\n"); @@ -3348,17 +3325,17 @@ OcvDerate::~OcvDerate() const Table * OcvDerate::derateTable(const RiseFall *rf, - const EarlyLate *early_late, - PathType path_type) + const EarlyLate *early_late, + PathType path_type) { return derate_[rf->index()][early_late->index()][int(path_type)].get(); } void OcvDerate::setDerateTable(const RiseFall *rf, - const EarlyLate *early_late, - const PathType path_type, - TablePtr derate) + const EarlyLate *early_late, + const PathType path_type, + TablePtr derate) { derate_[rf->index()][early_late->index()][int(path_type)] = derate; } diff --git a/liberty/Liberty.i b/liberty/Liberty.i index e56891e28..10bb9963d 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -116,12 +116,12 @@ private: bool read_liberty_cmd(char *filename, - Corner *corner, - const MinMaxAll *min_max, - bool infer_latches) + Scene *scene, + const MinMaxAll *min_max, + bool infer_latches) { Sta *sta = Sta::sta(); - LibertyLibrary *lib = sta->readLiberty(filename, corner, min_max, infer_latches); + LibertyLibrary *lib = sta->readLiberty(filename, scene, min_max, infer_latches); return (lib != nullptr); } @@ -148,21 +148,21 @@ find_equiv_cells(LibertyCell *cell) bool equiv_cells(LibertyCell *cell1, - LibertyCell *cell2) + LibertyCell *cell2) { return sta::equivCells(cell1, cell2); } bool equiv_cell_ports(LibertyCell *cell1, - LibertyCell *cell2) + LibertyCell *cell2) { return equivCellPorts(cell1, cell2); } bool equiv_cell_timing_arcs(LibertyCell *cell1, - LibertyCell *cell2) + LibertyCell *cell2) { return equivCellTimingArcSets(cell1, cell2); } @@ -178,7 +178,7 @@ liberty_port_direction(const LibertyPort *port) { return port->direction()->name(); } - + bool liberty_supply_exists(const char *supply_name) { @@ -230,8 +230,8 @@ find_liberty_cell(const char *name) LibertyCellSeq find_liberty_cells_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); return self->findLibertyCellsMatching(&matcher); @@ -278,8 +278,8 @@ find_liberty_port(const char *name) LibertyPortSeq find_liberty_ports_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); return self->findLibertyPortsMatching(&matcher); @@ -297,9 +297,8 @@ timing_arc_sets() void ensure_voltage_waveforms() { - Corners *corners = Sta::sta()->corners(); - const DcalcAnalysisPtSeq &dcalc_aps = corners->dcalcAnalysisPts(); - self->ensureVoltageWaveforms(dcalc_aps); + const SceneSeq &scenes = Sta::sta()->scenes(); + self->ensureVoltageWaveforms(scenes); } LibertyCell *test_cell() { return self->testCell(); } @@ -319,7 +318,7 @@ member_iterator() { return new LibertyPortMemberIterator(self); } LibertyPort *bundle_port() { return self->bundlePort(); } bool is_pwr_gnd() { return self->isPwrGnd(); } -string +std::string function() { FuncExpr *func = self->function(); @@ -329,7 +328,7 @@ function() return ""; } -string +std::string tristate_enable() { FuncExpr *enable = self->tristateEnable(); @@ -340,11 +339,11 @@ tristate_enable() } float -capacitance(Corner *corner, - const MinMax *min_max) +capacitance(Scene *scene, + const MinMax *min_max) { Sta *sta = Sta::sta(); - return sta->capacitance(self, corner, min_max); + return sta->capacitance(self, scene, min_max); } void @@ -374,9 +373,9 @@ full_name() const char *to = self->to()->name(); const char *cell_name = self->libertyCell()->name(); return stringPrintTmp("%s %s -> %s", - cell_name, - from, - to); + cell_name, + from, + to); } TimingArcSeq & diff --git a/liberty/Liberty.tcl b/liberty/Liberty.tcl index d4ac84a1d..672e876fa 100644 --- a/liberty/Liberty.tcl +++ b/liberty/Liberty.tcl @@ -35,7 +35,7 @@ proc_redirect read_liberty { check_argc_eq1 "read_liberty" $args set filename [file nativename [lindex $args 0]] - set corner [parse_corner keys] + set corner [parse_scene keys] set min_max [parse_min_max_all_flags flags] set infer_latches [info exists flags(-infer_latches)] read_liberty_cmd $filename $corner $min_max $infer_latches @@ -58,13 +58,13 @@ proc_redirect report_lib_cell { check_argc_eq1 "report_lib_cell" $args set arg [lindex $args 0] set cell [get_lib_cell_warn "lib_cell" $arg] - set corner [cmd_corner] + set scene [cmd_scene] if { $cell != "NULL" } { - report_lib_cell_ $cell $corner + report_lib_cell_ $cell $scene } } -proc report_lib_cell_ { cell corner } { +proc report_lib_cell_ { cell scene } { global sta_report_default_digits set lib [$cell liberty_library] @@ -78,21 +78,21 @@ proc report_lib_cell_ { cell corner } { while {[$iter has_next]} { set port [$iter next] if { [$port is_bus] || [$port is_bundle] } { - report_lib_port $port $corner + report_lib_port $port $scene set member_iter [$port member_iterator] while { [$member_iter has_next] } { set port [$member_iter next] - report_lib_port $port $corner + report_lib_port $port $scene } $member_iter finish } elseif { ![$port is_bundle_member] && ![$port is_bus_bit] } { - report_lib_port $port $corner + report_lib_port $port $scene } } $iter finish } -proc report_lib_port { port corner } { +proc report_lib_port { port scene } { global sta_report_default_digits if { [$port is_bus] } { @@ -112,7 +112,7 @@ proc report_lib_port { port corner } { if { $func != "" } { set func " function=$func" } - report_line " ${indent}$port_name [liberty_port_direction $port]$enable$func[port_capacitance_str $port $corner $sta_report_default_digits]" + report_line " ${indent}$port_name [liberty_port_direction $port]$enable$func[port_capacitance_str $port $scene $sta_report_default_digits]" } # sta namespace end diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index 46bde91af..fa29f5ecc 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -49,8 +49,8 @@ LibertyBuilder::init(Debug *debug, LibertyCell * LibertyBuilder::makeCell(LibertyLibrary *library, - const char *name, - const char *filename) + const char *name, + const char *filename) { LibertyCell *cell = new LibertyCell(library, name, filename); library->addCell(cell); @@ -59,7 +59,7 @@ LibertyBuilder::makeCell(LibertyLibrary *library, LibertyPort * LibertyBuilder::makePort(LibertyCell *cell, - const char *port_name) + const char *port_name) { LibertyPort *port = new LibertyPort(cell, port_name, false, nullptr, -1, -1, false, nullptr); @@ -69,14 +69,14 @@ LibertyBuilder::makePort(LibertyCell *cell, LibertyPort * LibertyBuilder::makeBusPort(LibertyCell *cell, - const char *bus_name, + const char *bus_name, int from_index, - int to_index, - BusDcl *bus_dcl) + int to_index, + BusDcl *bus_dcl) { LibertyPort *port = new LibertyPort(cell, bus_name, true, bus_dcl, from_index, to_index, - false, new ConcretePortSeq); + false, new ConcretePortSeq); cell->addPort(port); makeBusPortBits(cell->library(), cell, port, bus_name, from_index, to_index); return port; @@ -84,11 +84,11 @@ LibertyBuilder::makeBusPort(LibertyCell *cell, void LibertyBuilder::makeBusPortBits(ConcreteLibrary *library, - LibertyCell *cell, - ConcretePort *bus_port, - const char *bus_name, - int from_index, - int to_index) + LibertyCell *cell, + ConcretePort *bus_port, + const char *bus_name, + int from_index, + int to_index) { if (from_index < to_index) { for (int index = from_index; index <= to_index; index++) @@ -102,10 +102,10 @@ LibertyBuilder::makeBusPortBits(ConcreteLibrary *library, void LibertyBuilder::makeBusPortBit(ConcreteLibrary *library, - LibertyCell *cell, - ConcretePort *bus_port, - const char *bus_name, - int bit_index) + LibertyCell *cell, + ConcretePort *bus_port, + const char *bus_name, + int bit_index) { string bit_name; stringPrint(bit_name, "%s%c%d%c", @@ -120,18 +120,18 @@ LibertyBuilder::makeBusPortBit(ConcreteLibrary *library, LibertyPort * LibertyBuilder::makePort(LibertyCell *cell, - const char *bit_name, - int bit_index) + const char *bit_name, + int bit_index) { LibertyPort *port = new LibertyPort(cell, bit_name, false, nullptr, - bit_index, bit_index, false, nullptr); + bit_index, bit_index, false, nullptr); return port; } LibertyPort * LibertyBuilder::makeBundlePort(LibertyCell *cell, - const char *name, - ConcretePortSeq *members) + const char *name, + ConcretePortSeq *members) { LibertyPort *port = new LibertyPort(cell, name, false, nullptr, -1, -1, true, members); cell->addPort(port); @@ -144,10 +144,10 @@ LibertyBuilder::makeBundlePort(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeTimingArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - LibertyPort *related_out, - TimingArcAttrsPtr attrs, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TimingArcAttrsPtr attrs, int /* line */) { FuncExpr *to_func = to_port->function(); @@ -203,20 +203,20 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, return makeCombinationalArcs(cell, from_port, to_port, true, false, attrs); case TimingType::setup_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), TimingRole::setup(), - attrs); + RiseFall::rise(), TimingRole::setup(), + attrs); case TimingType::setup_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), TimingRole::setup(), - attrs); + RiseFall::fall(), TimingRole::setup(), + attrs); case TimingType::hold_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), TimingRole::hold(), - attrs); + RiseFall::rise(), TimingRole::hold(), + attrs); case TimingType::hold_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), TimingRole::hold(), - attrs); + RiseFall::fall(), TimingRole::hold(), + attrs); case TimingType::rising_edge: return makeRegLatchArcs(cell, from_port, to_port, RiseFall::rise(), attrs); case TimingType::falling_edge: @@ -227,20 +227,20 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, return makePresetClrArcs(cell, from_port, to_port, RiseFall::fall(), attrs); case TimingType::recovery_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(),TimingRole::recovery(), - attrs); + RiseFall::rise(),TimingRole::recovery(), + attrs); case TimingType::recovery_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(),TimingRole::recovery(), - attrs); + RiseFall::fall(),TimingRole::recovery(), + attrs); case TimingType::removal_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), TimingRole::removal(), - attrs); + RiseFall::rise(), TimingRole::removal(), + attrs); case TimingType::removal_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), TimingRole::removal(), - attrs); + RiseFall::fall(), TimingRole::removal(), + attrs); case TimingType::three_state_disable: return makeTristateDisableArcs(cell, from_port, to_port, true, true, attrs); case TimingType::three_state_disable_fall: @@ -255,30 +255,30 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, return makeTristateEnableArcs(cell, from_port, to_port, true, false, attrs); case TimingType::skew_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), TimingRole::skew(), - attrs); + RiseFall::fall(), TimingRole::skew(), + attrs); case TimingType::skew_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), TimingRole::skew(), - attrs); + RiseFall::rise(), TimingRole::skew(), + attrs); case TimingType::non_seq_setup_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), - TimingRole::nonSeqSetup(), attrs); + RiseFall::rise(), + TimingRole::nonSeqSetup(), attrs); case TimingType::non_seq_setup_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), - TimingRole::nonSeqSetup(), attrs); + RiseFall::fall(), + TimingRole::nonSeqSetup(), attrs); case TimingType::non_seq_hold_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), - TimingRole::nonSeqHold(), - attrs); + RiseFall::rise(), + TimingRole::nonSeqHold(), + attrs); case TimingType::non_seq_hold_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), - TimingRole::nonSeqHold(), - attrs); + RiseFall::fall(), + TimingRole::nonSeqHold(), + attrs); case TimingType::min_clock_tree_path: return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMin(), MinMax::min(), attrs); @@ -305,16 +305,16 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + TimingArcAttrsPtr attrs) { FuncExpr *func = to_port->function(); FuncExpr *enable = to_port->tristateEnable(); TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::combinational(), attrs); + TimingRole::combinational(), attrs); TimingSense sense = attrs->timingSense(); if (sense == TimingSense::unknown) { // Timing sense not specified - find it from function. @@ -340,13 +340,13 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); + makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); + makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); } break; case TimingSense::negative_unate: @@ -354,13 +354,13 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); + makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); } if (to_rise) { to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); + makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); } break; case TimingSense::non_unate: @@ -370,16 +370,16 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); - makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); + makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); + makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); } } if (to_rise) { to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); - makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); + makeTimingArc(arc_set, RiseFall::rise(), to_rf, model); + makeTimingArc(arc_set, RiseFall::fall(), to_rf, model); } } break; @@ -389,13 +389,13 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, + LibertyPort *from_port, + LibertyPort *to_port, TimingSense sense, - TimingArcAttrsPtr attrs) + TimingArcAttrsPtr attrs) { TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::latchDtoQ(), attrs); + TimingRole::latchDtoQ(), attrs); TimingModel *model; const RiseFall *to_rf = RiseFall::rise(); model = attrs->model(to_rf); @@ -418,31 +418,30 @@ LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeRegLatchArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - const RiseFall *from_rf, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + const RiseFall *from_rf, + TimingArcAttrsPtr attrs) { FuncExpr *to_func = to_port->function(); - FuncExprPortIterator port_iter(to_func); - while (port_iter.hasNext()) { - LibertyPort *func_port = port_iter.next(); + LibertyPortSet to_ports = to_func->ports(); + for (LibertyPort *func_port : to_ports) { Sequential *seq = cell->outputPortSequential(func_port); if (seq) { if (seq->clock() && seq->clock()->hasPort(from_port)) { - const TimingRole *role = seq->isRegister() ? - TimingRole::regClkToQ() : TimingRole::latchEnToQ(); - return makeFromTransitionArcs(cell, from_port, to_port, nullptr, + const TimingRole *role = seq->isRegister() ? + TimingRole::regClkToQ() : TimingRole::latchEnToQ(); + return makeFromTransitionArcs(cell, from_port, to_port, nullptr, from_rf, role, attrs); } else if (seq->isLatch() - && seq->data() - && seq->data()->hasPort(from_port)) - return makeFromTransitionArcs(cell, from_port, to_port, nullptr, + && seq->data() + && seq->data()->hasPort(from_port)) + return makeFromTransitionArcs(cell, from_port, to_port, nullptr, from_rf, TimingRole::latchDtoQ(), attrs); else if ((seq->clear() && seq->clear()->hasPort(from_port)) - || (seq->preset() && seq->preset()->hasPort(from_port))) - return makeFromTransitionArcs(cell, from_port, to_port, nullptr, + || (seq->preset() && seq->preset()->hasPort(from_port))) + return makeFromTransitionArcs(cell, from_port, to_port, nullptr, from_rf, TimingRole::regSetClr(), attrs); } } @@ -454,15 +453,15 @@ LibertyBuilder::makeRegLatchArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeFromTransitionArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - LibertyPort *related_out, - const RiseFall *from_rf, - const TimingRole *role, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + const RiseFall *from_rf, + const TimingRole *role, + TimingArcAttrsPtr attrs) { TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - related_out, role, attrs); + related_out, role, attrs); for (auto to_rf : RiseFall::range()) { TimingModel *model = attrs->model(to_rf); if (model) @@ -473,16 +472,16 @@ LibertyBuilder::makeFromTransitionArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makePresetClrArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - const RiseFall *to_rf, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + const RiseFall *to_rf, + TimingArcAttrsPtr attrs) { TimingArcSet *arc_set = nullptr; TimingModel *model = attrs->model(to_rf); if (model) { arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::regSetClr(), attrs); + TimingRole::regSetClr(), attrs); const RiseFall *opp_rf = to_rf->opposite(); switch (attrs->timingSense()) { case TimingSense::positive_unate: @@ -508,14 +507,14 @@ LibertyBuilder::makePresetClrArcs(LibertyCell *cell, // 1Z, Z0 fall TimingArcSet * LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + TimingArcAttrsPtr attrs) { TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::tristateEnable(), attrs); + TimingRole::tristateEnable(), attrs); FuncExpr *tristate_enable = to_port->tristateEnable(); TimingSense sense = attrs->timingSense(); if (sense == TimingSense::unknown && tristate_enable) @@ -528,13 +527,13 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); } break; case TimingSense::negative_unate: @@ -542,13 +541,13 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); } break; case TimingSense::non_unate: @@ -557,16 +556,16 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); - makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); } } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); - makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); } } break; @@ -578,15 +577,15 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs) + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + TimingArcAttrsPtr attrs) { TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::tristateDisable(), - attrs); + TimingRole::tristateDisable(), + attrs); TimingSense sense = attrs->timingSense(); FuncExpr *tristate_enable = to_port->tristateEnable(); if (sense == TimingSense::unknown && tristate_enable) @@ -599,13 +598,13 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); } break; case TimingSense::negative_unate: @@ -613,13 +612,13 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) - makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); } break; case TimingSense::non_unate: @@ -628,16 +627,16 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, to_rf = RiseFall::rise(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); - makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); } } if (to_fall) { to_rf = RiseFall::fall(); model = attrs->model(to_rf); if (model) { - makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); - makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); } } break; @@ -708,40 +707,40 @@ LibertyBuilder::makeMinPulseWidthArcs(LibertyCell *cell, TimingArcSet * LibertyBuilder::makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - const TimingRole *role, - TimingArcAttrsPtr attrs) + LibertyPort *from, + LibertyPort *to, + const TimingRole *role, + TimingArcAttrsPtr attrs) { return new TimingArcSet(cell, from, to, nullptr, role, attrs); } TimingArcSet * LibertyBuilder::makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs) + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs) { return new TimingArcSet(cell, from, to, related_out, role, attrs); } TimingArc * LibertyBuilder::makeTimingArc(TimingArcSet *set, - const RiseFall *from_rf, - const RiseFall *to_rf, - TimingModel *model) + const RiseFall *from_rf, + const RiseFall *to_rf, + TimingModel *model) { return new TimingArc(set, from_rf->asTransition(), - to_rf->asTransition(), model); + to_rf->asTransition(), model); } TimingArc * LibertyBuilder::makeTimingArc(TimingArcSet *set, - const Transition *from_rf, - const Transition *to_rf, - TimingModel *model) + const Transition *from_rf, + const Transition *to_rf, + TimingModel *model) { return new TimingArc(set, from_rf, to_rf, model); } @@ -750,16 +749,16 @@ LibertyBuilder::makeTimingArc(TimingArcSet *set, InternalPower * LibertyBuilder::makeInternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs) + LibertyPort *port, + LibertyPort *related_port, + InternalPowerAttrs *attrs) { return new InternalPower(cell, port, related_port, attrs); } LeakagePower * LibertyBuilder::makeLeakagePower(LibertyCell *cell, - LeakagePowerAttrs *attrs) + LeakagePowerAttrs *attrs) { return new LeakagePower(cell, attrs); } diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh index 9ce879318..27b60b69c 100644 --- a/liberty/LibertyBuilder.hh +++ b/liberty/LibertyBuilder.hh @@ -25,7 +25,6 @@ #pragma once #include "MinMax.hh" -#include "Vector.hh" #include "Transition.hh" #include "LibertyClass.hh" #include "ConcreteLibrary.hh" @@ -46,47 +45,47 @@ public: void init(Debug *debug, Report *report); virtual LibertyCell *makeCell(LibertyLibrary *library, - const char *name, - const char *filename); + const char *name, + const char *filename); virtual LibertyPort *makePort(LibertyCell *cell, - const char *name); + const char *name); virtual LibertyPort *makeBusPort(LibertyCell *cell, - const char *bus_name, - int from_index, - int to_index, + const char *bus_name, + int from_index, + int to_index, BusDcl *bus_dcl); virtual LibertyPort *makeBundlePort(LibertyCell *cell, - const char *name, - ConcretePortSeq *members); + const char *name, + ConcretePortSeq *members); // Build timing arc sets and their arcs given a type and sense. // Port functions and cell latches are also used by this builder // to get the correct roles. TimingArcSet *makeTimingArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - LibertyPort *related_out, - TimingArcAttrsPtr attrs, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TimingArcAttrsPtr attrs, int line); InternalPower *makeInternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs); + LibertyPort *port, + LibertyPort *related_port, + InternalPowerAttrs *attrs); LeakagePower *makeLeakagePower(LibertyCell *cell, - LeakagePowerAttrs *attrs); + LeakagePowerAttrs *attrs); TimingArcSet *makeFromTransitionArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - LibertyPort *related_out, - const RiseFall *from_rf, - const TimingRole *role, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + const RiseFall *from_rf, + const TimingRole *role, + TimingArcAttrsPtr attrs); TimingArcSet *makeCombinationalArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + TimingArcAttrsPtr attrs); TimingArcSet *makeClockTreePathArcs(LibertyCell *cell, LibertyPort *to_port, const TimingRole *role, @@ -101,70 +100,70 @@ public: protected: ConcretePort *makeBusPort(const char *name, - int from_index, - int to_index, - ConcretePortSeq *members); + int from_index, + int to_index, + ConcretePortSeq *members); void makeBusPortBits(ConcreteLibrary *library, - LibertyCell *cell, - ConcretePort *bus_port, - const char *bus_name, - int from_index, - int to_index); + LibertyCell *cell, + ConcretePort *bus_port, + const char *bus_name, + int from_index, + int to_index); // Bus port bit (internal to makeBusPortBits). virtual LibertyPort *makePort(LibertyCell *cell, const char *bit_name, int bit_index); void makeBusPortBit(ConcreteLibrary *library, - LibertyCell *cell, - ConcretePort *bus_port, - const char *bus_name, - int index); + LibertyCell *cell, + ConcretePort *bus_port, + const char *bus_name, + int index); virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - const TimingRole *role, - TimingArcAttrsPtr attrs); + LibertyPort *from, + LibertyPort *to, + const TimingRole *role, + TimingArcAttrsPtr attrs); virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs); + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs); virtual TimingArc *makeTimingArc(TimingArcSet *set, - const Transition *from_rf, - const Transition *to_rf, - TimingModel *model); + const Transition *from_rf, + const Transition *to_rf, + TimingModel *model); TimingArc *makeTimingArc(TimingArcSet *set, - const RiseFall *from_rf, - const RiseFall *to_rf, - TimingModel *model); + const RiseFall *from_rf, + const RiseFall *to_rf, + TimingModel *model); TimingArcSet *makeLatchDtoQArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, + LibertyPort *from_port, + LibertyPort *to_port, TimingSense sense, - TimingArcAttrsPtr attrs); + TimingArcAttrsPtr attrs); TimingArcSet *makeRegLatchArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - const RiseFall *from_rf, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + const RiseFall *from_rf, + TimingArcAttrsPtr attrs); TimingArcSet *makePresetClrArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - const RiseFall *to_rf, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + const RiseFall *to_rf, + TimingArcAttrsPtr attrs); TimingArcSet *makeTristateEnableArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + TimingArcAttrsPtr attrs); TimingArcSet *makeTristateDisableArcs(LibertyCell *cell, - LibertyPort *from_port, - LibertyPort *to_port, - bool to_rise, - bool to_fall, - TimingArcAttrsPtr attrs); + LibertyPort *from_port, + LibertyPort *to_port, + bool to_rise, + bool to_fall, + TimingArcAttrsPtr attrs); Debug *debug_; Report *report_; diff --git a/liberty/LibertyExt.cc b/liberty/LibertyExt.cc index aa2850e81..10a214b12 100644 --- a/liberty/LibertyExt.cc +++ b/liberty/LibertyExt.cc @@ -67,7 +67,7 @@ class BigcoCell : public LibertyCell }; BigcoCell::BigcoCell(LibertyLibrary *library, const char *name, - const char *filename) : + const char *filename) : LibertyCell(library, name, filename), thingy_(0) { @@ -110,17 +110,17 @@ class BigcoTimingArcSet : public TimingArcSet { public: BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, LibertyPort *to, - LibertyPort *related_out, TimingRole *role, - TimingArcAttrs *attrs); + LibertyPort *related_out, TimingRole *role, + TimingArcAttrs *attrs); protected: const char *frob_; }; BigcoTimingArcSet::BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, TimingRole *role, - TimingArcAttrs *attrs) : + LibertyPort *to, + LibertyPort *related_out, TimingRole *role, + TimingArcAttrs *attrs) : TimingArcSet(cell, from, to, related_out, role, attrs) { const char *frob = static_cast(attrs)->frob(); @@ -135,19 +135,19 @@ class BigcoLibertyBuilder : public LibertyBuilder { public: virtual LibertyCell *makeCell(LibertyLibrary *library, const char *name, - const char *filename); + const char *filename); protected: virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - TimingRole *role, - TimingArcAttrs *attrs); + LibertyPort *to, + LibertyPort *related_out, + TimingRole *role, + TimingArcAttrs *attrs); }; LibertyCell * BigcoLibertyBuilder::makeCell(LibertyLibrary *library, const char *name, - const char *filename) + const char *filename) { LibertyCell *cell = new BigcoCell(library, name, filename); library->addCell(cell); @@ -156,10 +156,10 @@ BigcoLibertyBuilder::makeCell(LibertyLibrary *library, const char *name, TimingArcSet * BigcoLibertyBuilder::makeTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - TimingRole *role, - TimingArcAttrs *attrs) + LibertyPort *to, + LibertyPort *related_out, + TimingRole *role, + TimingArcAttrs *attrs) { return new BigcoTimingArcSet(cell, from, to, related_out, role, attrs); } @@ -255,8 +255,8 @@ class BigcoSta : public Sta protected: virtual LibertyLibrary *readLibertyFile(const char *filename, - bool infer_latches, - Network *network); + bool infer_latches, + Network *network); }; BigcoSta::BigcoSta() : @@ -267,8 +267,8 @@ BigcoSta::BigcoSta() : // Replace Sta liberty file reader with Bigco's very own. LibertyLibrary * Sta::readLibertyFile(const char *filename, - bool infer_latches, - Network *network) + bool infer_latches, + Network *network) { BigcoLibertyBuilder builder; BigcoLibertyReader reader(&builder); diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index f1eddb1b6..4dff3a40f 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -28,6 +28,7 @@ #include #include +#include "ContainerHelpers.hh" #include "Zlib.hh" #include "Report.hh" #include "Error.hh" @@ -40,8 +41,8 @@ using std::string; void parseLibertyFile(const char *filename, - LibertyGroupVisitor *library_visitor, - Report *report) + LibertyGroupVisitor *library_visitor, + Report *report) { gzstream::igzstream stream(filename); if (stream.is_open()) { @@ -81,7 +82,7 @@ LibertyParser::makeDefine(LibertyAttrValueSeq *values, LibertyAttrType value_type = attrValueType(value_type_name); LibertyGroupType group_type = groupType(group_type_name); define = new LibertyDefine(define_name, group_type, - value_type, line); + value_type, line); LibertyGroup *group = this->group(); group->addDefine(define); } @@ -162,7 +163,7 @@ LibertyParser::group() void LibertyParser::deleteGroups() { - group_stack_.deleteContentsClear(); + deleteContents(group_stack_); } LibertyStmt * @@ -194,9 +195,7 @@ LibertyParser::makeComplexAttr(const char *name, if (stringEq(name, "define")) { LibertyStmt *define = makeDefine(values, line); stringDelete(name); - LibertyAttrValueSeq::Iterator attr_iter(values); - while (attr_iter.hasNext()) - delete attr_iter.next(); + deleteContents(values); delete values; return define; } @@ -252,8 +251,8 @@ LibertyStmt::LibertyStmt(int line) : } LibertyGroup::LibertyGroup(const char *type, - LibertyAttrValueSeq *params, - int line) : + LibertyAttrValueSeq *params, + int line) : LibertyStmt(line), type_(type), params_(params), @@ -278,7 +277,7 @@ LibertyGroup::addDefine(LibertyDefine *define) if (define_map_ == nullptr) define_map_ = new LibertyDefineMap; const char *define_name = define->name(); - LibertyDefine *prev_define = define_map_->findKey(define_name); + LibertyDefine *prev_define = findKey(define_map_, define_name); if (prev_define) { define_map_->erase(define_name); delete prev_define; @@ -299,21 +298,20 @@ LibertyGroup::addAttribute(LibertyAttr *attr) LibertyGroup::~LibertyGroup() { if (params_) { - params_->deleteContents(); + deleteContents(params_); delete params_; } if (attrs_) { - LibertyAttrSeq::Iterator iter(attrs_); - attrs_->deleteContents(); + deleteContents(attrs_); delete attrs_; delete attr_map_; } if (subgroups_) { - subgroups_->deleteContents(); + deleteContents(subgroups_); delete subgroups_; } if (define_map_) { - define_map_->deleteContents(); + deleteContents(define_map_); delete define_map_; } } @@ -346,40 +344,27 @@ LibertyGroup::findAttr(const char *name) if (attrs_) { if (attr_map_ == nullptr) { // Build attribute name map on demand. - LibertyAttrSeq::Iterator attr_iter(attrs_); - while (attr_iter.hasNext()) { - LibertyAttr *attr = attr_iter.next(); - (*attr_map_)[attr->name()] = attr; - } + for (LibertyAttr *attr : *attrs_) + (*attr_map_)[attr->name()] = attr; } - return attr_map_->findKey(name); + return findKey(attr_map_, name); } else return nullptr; } -LibertySubgroupIterator::LibertySubgroupIterator(LibertyGroup *group) : - LibertyGroupSeq::Iterator(group->subgroups()) -{ -} - -LibertyAttrIterator::LibertyAttrIterator(LibertyGroup *group) : - LibertyAttrSeq::Iterator(group->attrs()) -{ -} - //////////////////////////////////////////////////////////////// LibertyAttr::LibertyAttr(const char *name, - int line) : + int line) : LibertyStmt(line), name_(name) { } LibertySimpleAttr::LibertySimpleAttr(const char *name, - LibertyAttrValue *value, - int line) : + LibertyAttrValue *value, + int line) : LibertyAttr(name, line), value_(value) { @@ -398,8 +383,8 @@ LibertySimpleAttr::values() const } LibertyComplexAttr::LibertyComplexAttr(const char *name, - LibertyAttrValueSeq *values, - int line) : + LibertyAttrValueSeq *values, + int line) : LibertyAttr(name, line), values_(values) { @@ -408,7 +393,7 @@ LibertyComplexAttr::LibertyComplexAttr(const char *name, LibertyComplexAttr::~LibertyComplexAttr() { if (values_) { - values_->deleteContents(); + deleteContents(values_); delete values_; } } @@ -429,14 +414,14 @@ LibertyStringAttrValue::LibertyStringAttrValue(const char *value) : } float -LibertyStringAttrValue::floatValue() +LibertyStringAttrValue::floatValue() const { criticalError(1126, "LibertyStringAttrValue called for float value"); return 0.0; } const char * -LibertyStringAttrValue::stringValue() +LibertyStringAttrValue::stringValue() const { return value_.c_str(); } @@ -447,13 +432,13 @@ LibertyFloatAttrValue::LibertyFloatAttrValue(float value) : } float -LibertyFloatAttrValue::floatValue() +LibertyFloatAttrValue::floatValue() const { return value_; } const char * -LibertyFloatAttrValue::stringValue() +LibertyFloatAttrValue::stringValue() const { criticalError(1127, "LibertyStringAttrValue called for float value"); return nullptr; @@ -462,9 +447,9 @@ LibertyFloatAttrValue::stringValue() //////////////////////////////////////////////////////////////// LibertyDefine::LibertyDefine(const char *name, - LibertyGroupType group_type, - LibertyAttrType value_type, - int line) : + LibertyGroupType group_type, + LibertyAttrType value_type, + int line) : LibertyStmt(line), name_(name), group_type_(group_type), @@ -475,8 +460,8 @@ LibertyDefine::LibertyDefine(const char *name, //////////////////////////////////////////////////////////////// LibertyVariable::LibertyVariable(const char *var, - float value, - int line) : + float value, + int line) : LibertyStmt(line), var_(var), value_(value) diff --git a/liberty/LibertyParser.hh b/liberty/LibertyParser.hh index c1a9ec462..9b1fce2ca 100644 --- a/liberty/LibertyParser.hh +++ b/liberty/LibertyParser.hh @@ -24,10 +24,10 @@ #pragma once +#include +#include + #include "Zlib.hh" -#include "Vector.hh" -#include "Map.hh" -#include "Set.hh" #include "StringUtil.hh" namespace sta { @@ -45,19 +45,17 @@ class LibertySubgroupIterator; class LibertyAttrIterator; class LibertyScanner; -typedef Vector LibertyStmtSeq; -typedef Vector LibertyGroupSeq; -typedef Vector LibertyAttrSeq; -typedef Map LibertyAttrMap; -typedef Map LibertyDefineMap; -typedef Vector LibertyAttrValueSeq; -typedef Map LibertyVariableMap; -typedef MapLibertyGroupVisitorMap; -typedef LibertyAttrValueSeq::Iterator LibertyAttrValueIterator; -typedef Vector LibertyGroupSeq; +using LibertyStmtSeq = std::vector; +using LibertyGroupSeq = std::vector; +using LibertyAttrSeq = std::vector; +using LibertyAttrMap = std::map; +using LibertyDefineMap = std::map; +using LibertyAttrValueSeq = std::vector; +using LibertyVariableMap = std::map; +using LibertyGroupVisitorMap = std::map; enum class LibertyAttrType { attr_string, attr_int, attr_double, - attr_boolean, attr_unknown }; + attr_boolean, attr_unknown }; enum class LibertyGroupType { library, cell, pin, timing, unknown }; @@ -122,8 +120,8 @@ class LibertyGroup : public LibertyStmt { public: LibertyGroup(const char *type, - LibertyAttrValueSeq *params, - int line); + LibertyAttrValueSeq *params, + int line); virtual ~LibertyGroup(); virtual bool isGroup() const { return true; } const char *type() const { return type_.c_str(); } @@ -151,24 +149,12 @@ protected: LibertyDefineMap *define_map_; }; -class LibertySubgroupIterator : public LibertyGroupSeq::Iterator -{ -public: - LibertySubgroupIterator(LibertyGroup *group); -}; - -class LibertyAttrIterator : public LibertyAttrSeq::Iterator -{ -public: - LibertyAttrIterator(LibertyGroup *group); -}; - // Abstract base class for attributes. class LibertyAttr : public LibertyStmt { public: LibertyAttr(const char *name, - int line); + int line); const char *name() const { return name_.c_str(); } virtual bool isAttribute() const { return true; } virtual bool isSimple() const = 0; @@ -186,13 +172,13 @@ class LibertySimpleAttr : public LibertyAttr { public: LibertySimpleAttr(const char *name, - LibertyAttrValue *value, - int line); + LibertyAttrValue *value, + int line); virtual ~LibertySimpleAttr(); - virtual bool isSimple() const { return true; } - virtual bool isComplex() const { return false; } - virtual LibertyAttrValue *firstValue() { return value_; } - virtual LibertyAttrValueSeq *values() const; + bool isSimple() const override { return true; }; + bool isComplex() const override { return false; }; + LibertyAttrValue *firstValue() override { return value_; }; + LibertyAttrValueSeq *values() const override; private: LibertyAttrValue *value_; @@ -204,13 +190,13 @@ class LibertyComplexAttr : public LibertyAttr { public: LibertyComplexAttr(const char *name, - LibertyAttrValueSeq *values, - int line); + LibertyAttrValueSeq *values, + int line); virtual ~LibertyComplexAttr(); - virtual bool isSimple() const { return false; } - virtual bool isComplex() const { return true; } - virtual LibertyAttrValue *firstValue(); - virtual LibertyAttrValueSeq *values() const { return values_; } + bool isSimple() const override { return false; } + bool isComplex() const override { return true; } + LibertyAttrValue *firstValue() override ; + LibertyAttrValueSeq *values() const override { return values_; } private: LibertyAttrValueSeq *values_; @@ -222,10 +208,10 @@ class LibertyAttrValue public: LibertyAttrValue() {} virtual ~LibertyAttrValue() {} - virtual bool isString() = 0; - virtual bool isFloat() = 0; - virtual float floatValue() = 0; - virtual const char *stringValue() = 0; + virtual bool isString() const = 0; + virtual bool isFloat() const = 0; + virtual float floatValue() const = 0; + virtual const char *stringValue() const = 0; }; class LibertyStringAttrValue : public LibertyAttrValue @@ -233,10 +219,10 @@ class LibertyStringAttrValue : public LibertyAttrValue public: LibertyStringAttrValue(const char *value); virtual ~LibertyStringAttrValue() {} - virtual bool isFloat() { return false; } - virtual bool isString() { return true; } - virtual float floatValue(); - virtual const char *stringValue(); + bool isFloat() const override { return false; } + bool isString() const override { return true; } + float floatValue() const override ; + const char *stringValue() const override; private: std::string value_; @@ -247,10 +233,10 @@ class LibertyFloatAttrValue : public LibertyAttrValue public: LibertyFloatAttrValue(float value); virtual ~LibertyFloatAttrValue() {} - virtual bool isString() { return false; } - virtual bool isFloat() { return true; } - virtual float floatValue(); - virtual const char *stringValue(); + bool isString() const override { return false; } + bool isFloat() const override { return true; } + float floatValue() const override; + const char *stringValue() const override; private: float value_; @@ -263,9 +249,9 @@ class LibertyDefine : public LibertyStmt { public: LibertyDefine(const char *name, - LibertyGroupType group_type, - LibertyAttrType value_type, - int line); + LibertyGroupType group_type, + LibertyAttrType value_type, + int line); virtual bool isDefine() const { return true; } const char *name() const { return name_.c_str(); } LibertyGroupType groupType() const { return group_type_; } @@ -285,9 +271,9 @@ class LibertyVariable : public LibertyStmt { public: LibertyVariable(const char *var, - float value, - int line); - virtual bool isVariable() const { return true; } + float value, + int line); + bool isVariable() const override { return true; } const char *variable() const { return var_.c_str(); } float value() const { return value_; } @@ -313,6 +299,6 @@ public: void parseLibertyFile(const char *filename, - LibertyGroupVisitor *library_visitor, - Report *report); + LibertyGroupVisitor *library_visitor, + Report *report); } // namespace diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index bb24b5fe6..5479093eb 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -28,6 +28,7 @@ #include #include +#include "ContainerHelpers.hh" #include "EnumNameMap.hh" #include "Report.hh" #include "Debug.hh" @@ -59,12 +60,12 @@ using std::string; static void scaleFloats(FloatSeq *floats, - float scale); + float scale); LibertyLibrary * readLibertyFile(const char *filename, - bool infer_latches, - Network *network) + bool infer_latches, + Network *network) { LibertyReader reader(filename, infer_latches, network); return reader.readLibertyFile(filename); @@ -153,8 +154,8 @@ LibertyReader::readLibertyFile(const char *filename) void LibertyReader::defineGroupVisitor(const char *type, - LibraryGroupVisitor begin_visitor, - LibraryGroupVisitor end_visitor) + LibraryGroupVisitor begin_visitor, + LibraryGroupVisitor end_visitor) { group_begin_map_[type] = begin_visitor; group_end_map_[type] = end_visitor; @@ -162,7 +163,7 @@ LibertyReader::defineGroupVisitor(const char *type, void LibertyReader::defineAttrVisitor(const char *attr_name, - LibraryAttrVisitor visitor) + LibraryAttrVisitor visitor) { attr_visitor_map_[attr_name] = visitor; } @@ -172,13 +173,13 @@ LibertyReader::defineVisitors() { // Library defineGroupVisitor("library", &LibertyReader::beginLibrary, - &LibertyReader::endLibrary); + &LibertyReader::endLibrary); defineAttrVisitor("time_unit", &LibertyReader::visitTimeUnit); defineAttrVisitor("pulling_resistance_unit", - &LibertyReader::visitPullingResistanceUnit); + &LibertyReader::visitPullingResistanceUnit); defineAttrVisitor("resistance_unit", &LibertyReader::visitResistanceUnit); defineAttrVisitor("capacitive_load_unit", - &LibertyReader::visitCapacitiveLoadUnit); + &LibertyReader::visitCapacitiveLoadUnit); defineAttrVisitor("voltage_unit", &LibertyReader::visitVoltageUnit); defineAttrVisitor("current_unit", &LibertyReader::visitCurrentUnit); defineAttrVisitor("leakage_power_unit", &LibertyReader::visitPowerUnit); @@ -190,62 +191,62 @@ LibertyReader::defineVisitors() defineAttrVisitor("nom_voltage", &LibertyReader::visitNomVolt); defineAttrVisitor("nom_process", &LibertyReader::visitNomProc); defineAttrVisitor("default_inout_pin_cap", - &LibertyReader::visitDefaultInoutPinCap); + &LibertyReader::visitDefaultInoutPinCap); defineAttrVisitor("default_input_pin_cap", - &LibertyReader::visitDefaultInputPinCap); + &LibertyReader::visitDefaultInputPinCap); defineAttrVisitor("default_output_pin_cap", - &LibertyReader::visitDefaultOutputPinCap); + &LibertyReader::visitDefaultOutputPinCap); defineAttrVisitor("default_max_transition", - &LibertyReader::visitDefaultMaxTransition); + &LibertyReader::visitDefaultMaxTransition); defineAttrVisitor("default_max_fanout", - &LibertyReader::visitDefaultMaxFanout); + &LibertyReader::visitDefaultMaxFanout); defineAttrVisitor("default_intrinsic_rise", - &LibertyReader::visitDefaultIntrinsicRise); + &LibertyReader::visitDefaultIntrinsicRise); defineAttrVisitor("default_intrinsic_fall", - &LibertyReader::visitDefaultIntrinsicFall); + &LibertyReader::visitDefaultIntrinsicFall); defineAttrVisitor("default_inout_pin_rise_res", - &LibertyReader::visitDefaultInoutPinRiseRes); + &LibertyReader::visitDefaultInoutPinRiseRes); defineAttrVisitor("default_inout_pin_fall_res", - &LibertyReader::visitDefaultInoutPinFallRes); + &LibertyReader::visitDefaultInoutPinFallRes); defineAttrVisitor("default_output_pin_rise_res", - &LibertyReader::visitDefaultOutputPinRiseRes); + &LibertyReader::visitDefaultOutputPinRiseRes); defineAttrVisitor("default_output_pin_fall_res", - &LibertyReader::visitDefaultOutputPinFallRes); + &LibertyReader::visitDefaultOutputPinFallRes); defineAttrVisitor("default_fanout_load", - &LibertyReader::visitDefaultFanoutLoad); + &LibertyReader::visitDefaultFanoutLoad); defineAttrVisitor("default_wire_load", - &LibertyReader::visitDefaultWireLoad); + &LibertyReader::visitDefaultWireLoad); defineAttrVisitor("default_wire_load_mode", - &LibertyReader::visitDefaultWireLoadMode); + &LibertyReader::visitDefaultWireLoadMode); defineAttrVisitor("default_wire_load_selection", - &LibertyReader::visitDefaultWireLoadSelection); + &LibertyReader::visitDefaultWireLoadSelection); defineAttrVisitor("default_operating_conditions", - &LibertyReader::visitDefaultOperatingConditions); + &LibertyReader::visitDefaultOperatingConditions); defineAttrVisitor("input_threshold_pct_fall", - &LibertyReader::visitInputThresholdPctFall); + &LibertyReader::visitInputThresholdPctFall); defineAttrVisitor("input_threshold_pct_rise", - &LibertyReader::visitInputThresholdPctRise); + &LibertyReader::visitInputThresholdPctRise); defineAttrVisitor("output_threshold_pct_fall", - &LibertyReader::visitOutputThresholdPctFall); + &LibertyReader::visitOutputThresholdPctFall); defineAttrVisitor("output_threshold_pct_rise", - &LibertyReader::visitOutputThresholdPctRise); + &LibertyReader::visitOutputThresholdPctRise); defineAttrVisitor("slew_lower_threshold_pct_fall", - &LibertyReader::visitSlewLowerThresholdPctFall); + &LibertyReader::visitSlewLowerThresholdPctFall); defineAttrVisitor("slew_lower_threshold_pct_rise", - &LibertyReader::visitSlewLowerThresholdPctRise); + &LibertyReader::visitSlewLowerThresholdPctRise); defineAttrVisitor("slew_upper_threshold_pct_fall", - &LibertyReader::visitSlewUpperThresholdPctFall); + &LibertyReader::visitSlewUpperThresholdPctFall); defineAttrVisitor("slew_upper_threshold_pct_rise", - &LibertyReader::visitSlewUpperThresholdPctRise); + &LibertyReader::visitSlewUpperThresholdPctRise); defineAttrVisitor("slew_derate_from_library", - &LibertyReader::visitSlewDerateFromLibrary); + &LibertyReader::visitSlewDerateFromLibrary); defineGroupVisitor("lu_table_template", - &LibertyReader::beginTableTemplateDelay, - &LibertyReader::endTableTemplate); + &LibertyReader::beginTableTemplateDelay, + &LibertyReader::endTableTemplate); defineGroupVisitor("output_current_template", - &LibertyReader::beginTableTemplateOutputCurrent, - &LibertyReader::endTableTemplate); + &LibertyReader::beginTableTemplateOutputCurrent, + &LibertyReader::endTableTemplate); defineAttrVisitor("variable_1", &LibertyReader::visitVariable1); defineAttrVisitor("variable_2", &LibertyReader::visitVariable2); defineAttrVisitor("variable_3", &LibertyReader::visitVariable3); @@ -257,47 +258,47 @@ LibertyReader::defineVisitors() &LibertyReader::beginTechnology, &LibertyReader::endTechnology); defineGroupVisitor("rise_transition_degradation", - &LibertyReader::beginRiseTransitionDegredation, - &LibertyReader::endRiseFallTransitionDegredation); + &LibertyReader::beginRiseTransitionDegredation, + &LibertyReader::endRiseFallTransitionDegredation); defineGroupVisitor("fall_transition_degradation", - &LibertyReader::beginFallTransitionDegredation, - &LibertyReader::endRiseFallTransitionDegredation); + &LibertyReader::beginFallTransitionDegredation, + &LibertyReader::endRiseFallTransitionDegredation); defineGroupVisitor("type", &LibertyReader::beginType, - &LibertyReader::endType); + &LibertyReader::endType); defineAttrVisitor("bit_from", &LibertyReader::visitBitFrom); defineAttrVisitor("bit_to", &LibertyReader::visitBitTo); defineGroupVisitor("scaling_factors", &LibertyReader::beginScalingFactors, - &LibertyReader::endScalingFactors); + &LibertyReader::endScalingFactors); defineScalingFactorVisitors(); defineGroupVisitor("operating_conditions", &LibertyReader::beginOpCond, - &LibertyReader::endOpCond); + &LibertyReader::endOpCond); defineAttrVisitor("process", &LibertyReader::visitProc); defineAttrVisitor("voltage", &LibertyReader::visitVolt); defineAttrVisitor("temperature", &LibertyReader::visitTemp); defineAttrVisitor("tree_type", &LibertyReader::visitTreeType); defineGroupVisitor("wire_load", &LibertyReader::beginWireload, - &LibertyReader::endWireload); + &LibertyReader::endWireload); defineAttrVisitor("resistance", &LibertyReader::visitResistance); defineAttrVisitor("slope", &LibertyReader::visitSlope); defineAttrVisitor("fanout_length", &LibertyReader::visitFanoutLength); defineGroupVisitor("wire_load_selection", - &LibertyReader::beginWireloadSelection, - &LibertyReader::endWireloadSelection); + &LibertyReader::beginWireloadSelection, + &LibertyReader::endWireloadSelection); defineAttrVisitor("wire_load_from_area", - &LibertyReader::visitWireloadFromArea); + &LibertyReader::visitWireloadFromArea); // Cells defineGroupVisitor("cell", &LibertyReader::beginCell, - &LibertyReader::endCell); + &LibertyReader::endCell); defineGroupVisitor("scaled_cell", &LibertyReader::beginScaledCell, - &LibertyReader::endScaledCell); + &LibertyReader::endScaledCell); defineAttrVisitor("clock_gating_integrated_cell", - &LibertyReader::visitClockGatingIntegratedCell); + &LibertyReader::visitClockGatingIntegratedCell); defineAttrVisitor("area", &LibertyReader::visitArea); defineAttrVisitor("dont_use", &LibertyReader::visitDontUse); defineAttrVisitor("is_macro_cell", &LibertyReader::visitIsMacro); @@ -320,7 +321,7 @@ LibertyReader::defineVisitors() defineGroupVisitor("pin", &LibertyReader::beginPin,&LibertyReader::endPin); defineGroupVisitor("bus", &LibertyReader::beginBus,&LibertyReader::endBus); defineGroupVisitor("bundle", &LibertyReader::beginBundle, - &LibertyReader::endBundle); + &LibertyReader::endBundle); defineAttrVisitor("direction", &LibertyReader::visitDirection); defineAttrVisitor("clock", &LibertyReader::visitClock); defineAttrVisitor("bus_type", &LibertyReader::visitBusType); @@ -331,9 +332,9 @@ LibertyReader::defineVisitors() defineAttrVisitor("rise_capacitance", &LibertyReader::visitRiseCap); defineAttrVisitor("fall_capacitance", &LibertyReader::visitFallCap); defineAttrVisitor("rise_capacitance_range", - &LibertyReader::visitRiseCapRange); + &LibertyReader::visitRiseCapRange); defineAttrVisitor("fall_capacitance_range", - &LibertyReader::visitFallCapRange); + &LibertyReader::visitFallCapRange); defineAttrVisitor("fanout_load", &LibertyReader::visitFanoutLoad); defineAttrVisitor("max_fanout", &LibertyReader::visitMaxFanout); defineAttrVisitor("min_fanout", &LibertyReader::visitMinFanout); @@ -343,19 +344,19 @@ LibertyReader::defineVisitors() defineAttrVisitor("min_capacitance", &LibertyReader::visitMinCapacitance); defineAttrVisitor("min_period", &LibertyReader::visitMinPeriod); defineAttrVisitor("min_pulse_width_low", - &LibertyReader::visitMinPulseWidthLow); + &LibertyReader::visitMinPulseWidthLow); defineAttrVisitor("min_pulse_width_high", - &LibertyReader::visitMinPulseWidthHigh); + &LibertyReader::visitMinPulseWidthHigh); defineAttrVisitor("pulse_clock", - &LibertyReader::visitPulseClock); + &LibertyReader::visitPulseClock); defineAttrVisitor("clock_gate_clock_pin", - &LibertyReader::visitClockGateClockPin); + &LibertyReader::visitClockGateClockPin); defineAttrVisitor("clock_gate_enable_pin", - &LibertyReader::visitClockGateEnablePin); + &LibertyReader::visitClockGateEnablePin); defineAttrVisitor("clock_gate_out_pin", - &LibertyReader::visitClockGateOutPin); + &LibertyReader::visitClockGateOutPin); defineAttrVisitor("is_pll_feedback_pin", - &LibertyReader::visitIsPllFeedbackPin); + &LibertyReader::visitIsPllFeedbackPin); defineAttrVisitor("signal_type", &LibertyReader::visitSignalType); defineAttrVisitor("isolation_cell_data_pin", @@ -368,16 +369,16 @@ LibertyReader::defineVisitors() // Memory defineGroupVisitor("memory", &LibertyReader::beginMemory, - &LibertyReader::endMemory); + &LibertyReader::endMemory); // Register/latch defineGroupVisitor("ff", &LibertyReader::beginFF, &LibertyReader::endFF); defineGroupVisitor("ff_bank", &LibertyReader::beginFFBank, - &LibertyReader::endFFBank); + &LibertyReader::endFFBank); defineGroupVisitor("latch", &LibertyReader::beginLatch, - &LibertyReader::endLatch); + &LibertyReader::endLatch); defineGroupVisitor("latch_bank", &LibertyReader::beginLatchBank, - &LibertyReader::endLatchBank); + &LibertyReader::endLatchBank); defineAttrVisitor("clocked_on", &LibertyReader::visitClockedOn); defineAttrVisitor("enable", &LibertyReader::visitClockedOn); defineAttrVisitor("data_in", &LibertyReader::visitDataIn); @@ -393,11 +394,11 @@ LibertyReader::defineVisitors() defineAttrVisitor("table", &LibertyReader::visitTable); defineGroupVisitor("timing", &LibertyReader::beginTiming, - &LibertyReader::endTiming); + &LibertyReader::endTiming); defineAttrVisitor("related_pin", &LibertyReader::visitRelatedPin); defineAttrVisitor("related_bus_pins", &LibertyReader::visitRelatedBusPins); defineAttrVisitor("related_output_pin", - &LibertyReader::visitRelatedOutputPin); + &LibertyReader::visitRelatedOutputPin); defineAttrVisitor("timing_type", &LibertyReader::visitTimingType); defineAttrVisitor("timing_sense", &LibertyReader::visitTimingSense); defineAttrVisitor("sdf_cond_start", &LibertyReader::visitSdfCondStart); @@ -408,47 +409,47 @@ LibertyReader::defineVisitors() defineAttrVisitor("rise_resistance", &LibertyReader::visitRiseResistance); defineAttrVisitor("fall_resistance", &LibertyReader::visitFallResistance); defineGroupVisitor("cell_rise", &LibertyReader::beginCellRise, - &LibertyReader::endCellRiseFall); + &LibertyReader::endCellRiseFall); defineGroupVisitor("cell_fall", &LibertyReader::beginCellFall, - &LibertyReader::endCellRiseFall); + &LibertyReader::endCellRiseFall); defineGroupVisitor("rise_transition", &LibertyReader::beginRiseTransition, - &LibertyReader::endRiseFallTransition); + &LibertyReader::endRiseFallTransition); defineGroupVisitor("fall_transition", &LibertyReader::beginFallTransition, - &LibertyReader::endRiseFallTransition); + &LibertyReader::endRiseFallTransition); defineGroupVisitor("rise_constraint", &LibertyReader::beginRiseConstraint, - &LibertyReader::endRiseFallConstraint); + &LibertyReader::endRiseFallConstraint); defineGroupVisitor("fall_constraint", &LibertyReader::beginFallConstraint, - &LibertyReader::endRiseFallConstraint); + &LibertyReader::endRiseFallConstraint); defineAttrVisitor("value", &LibertyReader::visitValue); defineAttrVisitor("values", &LibertyReader::visitValues); defineGroupVisitor("lut", &LibertyReader::beginLut,&LibertyReader::endLut); defineGroupVisitor("test_cell", &LibertyReader::beginTestCell, - &LibertyReader::endTestCell); + &LibertyReader::endTestCell); defineGroupVisitor("mode_definition", &LibertyReader::beginModeDef, - &LibertyReader::endModeDef); + &LibertyReader::endModeDef); defineGroupVisitor("mode_value", &LibertyReader::beginModeValue, - &LibertyReader::endModeValue); + &LibertyReader::endModeValue); defineAttrVisitor("when", &LibertyReader::visitWhen); defineAttrVisitor("sdf_cond", &LibertyReader::visitSdfCond); // Power attributes. defineGroupVisitor("power_lut_template", - &LibertyReader::beginTableTemplatePower, - &LibertyReader::endTableTemplate); + &LibertyReader::beginTableTemplatePower, + &LibertyReader::endTableTemplate); defineGroupVisitor("leakage_power", &LibertyReader::beginLeakagePower, - &LibertyReader::endLeakagePower); + &LibertyReader::endLeakagePower); defineGroupVisitor("internal_power", &LibertyReader::beginInternalPower, - &LibertyReader::endInternalPower); + &LibertyReader::endInternalPower); // power group for both rise/fall defineGroupVisitor("power", &LibertyReader::beginRisePower, - &LibertyReader::endPower); + &LibertyReader::endPower); defineGroupVisitor("fall_power", &LibertyReader::beginFallPower, - &LibertyReader::endRiseFallPower); + &LibertyReader::endRiseFallPower); defineGroupVisitor("rise_power", &LibertyReader::beginRisePower, - &LibertyReader::endRiseFallPower); + &LibertyReader::endRiseFallPower); defineAttrVisitor("related_ground_pin",&LibertyReader::visitRelatedGroundPin); defineAttrVisitor("related_power_pin", &LibertyReader::visitRelatedPowerPin); defineAttrVisitor("related_pg_pin", &LibertyReader::visitRelatedPgPin); @@ -456,43 +457,43 @@ LibertyReader::defineVisitors() // AOCV attributes. defineAttrVisitor("ocv_arc_depth", &LibertyReader::visitOcvArcDepth); defineAttrVisitor("default_ocv_derate_group", - &LibertyReader::visitDefaultOcvDerateGroup); + &LibertyReader::visitDefaultOcvDerateGroup); defineAttrVisitor("ocv_derate_group", &LibertyReader::visitOcvDerateGroup); defineGroupVisitor("ocv_table_template", - &LibertyReader::beginTableTemplateOcv, - &LibertyReader::endTableTemplate); + &LibertyReader::beginTableTemplateOcv, + &LibertyReader::endTableTemplate); defineGroupVisitor("ocv_derate", - &LibertyReader::beginOcvDerate, - &LibertyReader::endOcvDerate); + &LibertyReader::beginOcvDerate, + &LibertyReader::endOcvDerate); defineGroupVisitor("ocv_derate_factors", - &LibertyReader::beginOcvDerateFactors, - &LibertyReader::endOcvDerateFactors); + &LibertyReader::beginOcvDerateFactors, + &LibertyReader::endOcvDerateFactors); defineAttrVisitor("rf_type", &LibertyReader::visitRfType); defineAttrVisitor("derate_type", &LibertyReader::visitDerateType); defineAttrVisitor("path_type", &LibertyReader::visitPathType); // POCV attributes. defineGroupVisitor("ocv_sigma_cell_rise", &LibertyReader::beginOcvSigmaCellRise, - &LibertyReader::endOcvSigmaCell); + &LibertyReader::endOcvSigmaCell); defineGroupVisitor("ocv_sigma_cell_fall", &LibertyReader::beginOcvSigmaCellFall, - &LibertyReader::endOcvSigmaCell); + &LibertyReader::endOcvSigmaCell); defineGroupVisitor("ocv_sigma_rise_transition", - &LibertyReader::beginOcvSigmaRiseTransition, - &LibertyReader::endOcvSigmaTransition); + &LibertyReader::beginOcvSigmaRiseTransition, + &LibertyReader::endOcvSigmaTransition); defineGroupVisitor("ocv_sigma_fall_transition", - &LibertyReader::beginOcvSigmaFallTransition, - &LibertyReader::endOcvSigmaTransition); + &LibertyReader::beginOcvSigmaFallTransition, + &LibertyReader::endOcvSigmaTransition); defineGroupVisitor("ocv_sigma_rise_constraint", - &LibertyReader::beginOcvSigmaRiseConstraint, - &LibertyReader::endOcvSigmaConstraint); + &LibertyReader::beginOcvSigmaRiseConstraint, + &LibertyReader::endOcvSigmaConstraint); defineGroupVisitor("ocv_sigma_fall_constraint", - &LibertyReader::beginOcvSigmaFallConstraint, - &LibertyReader::endOcvSigmaConstraint); + &LibertyReader::beginOcvSigmaFallConstraint, + &LibertyReader::endOcvSigmaConstraint); defineAttrVisitor("sigma_type", &LibertyReader::visitSigmaType); defineAttrVisitor("cell_leakage_power", &LibertyReader::visitCellLeakagePower); defineGroupVisitor("pg_pin", &LibertyReader::beginPgPin, - &LibertyReader::endPgPin); + &LibertyReader::endPgPin); defineAttrVisitor("pg_type", &LibertyReader::visitPgType); defineAttrVisitor("voltage_name", &LibertyReader::visitVoltageName); @@ -539,28 +540,28 @@ LibertyReader::defineVisitors() // ccsn (not implemented, this is needed to properly ignore ccsn groups) defineGroupVisitor("ccsn_first_stage", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); + &LibertyReader::endCcsn); defineGroupVisitor("ccsn_last_stage", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); + &LibertyReader::endCcsn); defineGroupVisitor("output_voltage_rise", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); + &LibertyReader::endCcsn); defineGroupVisitor("output_voltage_fall", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); + &LibertyReader::endCcsn); defineGroupVisitor("propagated_noise_low", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); + &LibertyReader::endCcsn); defineGroupVisitor("propagated_noise_high", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); + &LibertyReader::endCcsn); defineGroupVisitor("input_ccb", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); + &LibertyReader::endCcsn); defineGroupVisitor("output_ccb", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); + &LibertyReader::endCcsn); defineGroupVisitor("ecsm_waveform", &LibertyReader::beginEcsmWaveform, - &LibertyReader::endEcsmWaveform); + &LibertyReader::endEcsmWaveform); defineGroupVisitor("ecsm_waveform_set", &LibertyReader::beginEcsmWaveform, - &LibertyReader::endEcsmWaveform); + &LibertyReader::endEcsmWaveform); defineGroupVisitor("ecsm_capacitance", &LibertyReader::beginEcsmWaveform, - &LibertyReader::endEcsmWaveform); + &LibertyReader::endEcsmWaveform); } void @@ -573,40 +574,40 @@ LibertyReader::defineScalingFactorVisitors() ScaleFactorPvt pvt = static_cast(pvt_index); const char *pvt_name = scaleFactorPvtName(pvt); if (scaleFactorTypeRiseFallSuffix(type)) { - for (auto tr : RiseFall::range()) { - const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; - string attr_name; + for (auto tr : RiseFall::range()) { + const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; + string attr_name; stringPrint(attr_name, "k_%s_%s_%s", pvt_name, type_name, tr_name); - defineAttrVisitor(attr_name.c_str() ,&LibertyReader::visitScaleFactorSuffix); - } + defineAttrVisitor(attr_name.c_str() ,&LibertyReader::visitScaleFactorSuffix); + } } else if (scaleFactorTypeRiseFallPrefix(type)) { - for (auto tr : RiseFall::range()) { - const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; - string attr_name; + for (auto tr : RiseFall::range()) { + const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; + string attr_name; stringPrint(attr_name, "k_%s_%s_%s", pvt_name, tr_name, type_name); - defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorPrefix); - } + defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorPrefix); + } } else if (scaleFactorTypeLowHighSuffix(type)) { - for (auto tr : RiseFall::range()) { - const char *tr_name = (tr == RiseFall::rise()) ? "high":"low"; - string attr_name; + for (auto tr : RiseFall::range()) { + const char *tr_name = (tr == RiseFall::rise()) ? "high":"low"; + string attr_name; stringPrint(attr_name, "k_%s_%s_%s", pvt_name, tr_name, type_name); - defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorHiLow); - } + defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorHiLow); + } } else { - string attr_name; + string attr_name; stringPrint(attr_name, "k_%s_%s", pvt_name, type_name); @@ -619,25 +620,25 @@ LibertyReader::defineScalingFactorVisitors() void LibertyReader::visitAttr(LibertyAttr *attr) { - LibraryAttrVisitor visitor = attr_visitor_map_.findKey(attr->name()); + LibraryAttrVisitor *visitor = findKeyValuePtr(attr_visitor_map_, attr->name()); if (visitor) - (this->*visitor)(attr); + (this->**visitor)(attr); } void LibertyReader::begin(LibertyGroup *group) { - LibraryGroupVisitor visitor = group_begin_map_.findKey(group->type()); + LibraryGroupVisitor *visitor = findKeyValuePtr(group_begin_map_, group->type()); if (visitor) - (this->*visitor)(group); + (this->**visitor)(group); } void LibertyReader::end(LibertyGroup *group) { - LibraryGroupVisitor visitor = group_end_map_.findKey(group->type()); + LibraryGroupVisitor *visitor = findKeyValuePtr(group_end_map_, group->type()); if (visitor) - (this->*visitor)(group); + (this->**visitor)(group); } void @@ -718,7 +719,7 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group) library_->setDefaultWireloadSelection(selection); else libWarn(1143, group, "default_wire_selection %s not found.", - default_wireload_selection_); + default_wireload_selection_); stringDelete(default_wireload_selection_); default_wireload_selection_ = nullptr; } @@ -730,7 +731,7 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group) library_->setDefaultOperatingConditions(op_cond); else libWarn(1144, group, "default_operating_condition %s not found.", - default_operating_condition_); + default_operating_condition_); stringDelete(default_operating_condition_); default_operating_condition_ = nullptr; } @@ -757,7 +758,7 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group) } if (missing_threshold) libError(1149, group, "Library %s is missing one or more thresholds.", - library_->name()); + library_->name()); } void @@ -772,7 +773,7 @@ LibertyReader::visitPullingResistanceUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "ohm", res_scale_, - library_->units()->resistanceUnit()); + library_->units()->resistanceUnit()); } void @@ -813,9 +814,9 @@ LibertyReader::visitDistanceUnit(LibertyAttr *attr) void LibertyReader::parseUnits(LibertyAttr *attr, - const char *unit_suffix, - float &scale_var, - Unit *unit) + const char *unit_suffix, + float &scale_var, + Unit *unit) { string units = getAttrString(attr); if (!units.empty()) { @@ -875,48 +876,46 @@ LibertyReader::visitCapacitiveLoadUnit(LibertyAttr *attr) { if (library_) { if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - bool valid = false; - float scale; - if (value->isFloat()) { - scale = value->floatValue(); - valid = true; - } - else if (value->isString()) { - try { - scale = std::stof(value->stringValue()); - valid = true; - } - catch (...) { - valid = false; - } - } - - if (valid) { - if (value_iter.hasNext()) { - value = value_iter.next(); - if (value->isString()) { - const char *suffix = value->stringValue(); - if (stringEqual(suffix, "ff")) - cap_scale_ = scale * 1E-15F; - else if (stringEqual(suffix, "pf")) - cap_scale_ = scale * 1E-12F; - else - libWarn(1154, attr, "capacitive_load_units are not ff or pf."); - } - else - libWarn(1155, attr, "capacitive_load_units are not a string."); - } - else - libWarn(1156, attr, "capacitive_load_units missing suffix."); - } - else - libWarn(1157, attr, "capacitive_load_units scale is not a float."); + LibertyAttrValueSeq *values = attr->values(); + if (values->size() == 2) { + LibertyAttrValue *value = (*values)[0]; + bool valid = false; + float scale; + if (value->isFloat()) { + scale = value->floatValue(); + valid = true; + } + else if (value->isString()) { + try { + scale = std::stof(value->stringValue()); + valid = true; + } + catch (...) { + valid = false; + } + } + + if (valid) { + value = (*values)[1]; + if (value->isString()) { + const char *suffix = value->stringValue(); + if (stringEqual(suffix, "ff")) + cap_scale_ = scale * 1E-15F; + else if (stringEqual(suffix, "pf")) + cap_scale_ = scale * 1E-12F; + else + libWarn(1154, attr, "capacitive_load_units are not ff or pf."); + } + else + libWarn(1155, attr, "capacitive_load_units are not a string."); + } + else + libWarn(1157, attr, "capacitive_load_units scale is not a float."); } + else if (values->size() == 1) + libWarn(1156, attr, "capacitive_load_units missing suffix."); else - libWarn(1158, attr, "capacitive_load_units missing scale and suffix."); + libWarn(1158, attr, "capacitive_load_units missing scale and suffix."); } else libWarn(1159, attr, "capacitive_load_unit missing values suffix."); @@ -932,28 +931,28 @@ LibertyReader::visitDelayModel(LibertyAttr *attr) const char *type_name = getAttrString(attr); if (type_name) { if (stringEq(type_name, "table_lookup")) - library_->setDelayModelType(DelayModelType::table); + library_->setDelayModelType(DelayModelType::table); else if (stringEq(type_name, "generic_cmos")) - library_->setDelayModelType(DelayModelType::cmos_linear); + library_->setDelayModelType(DelayModelType::cmos_linear); else if (stringEq(type_name, "piecewise_cmos")) { - library_->setDelayModelType(DelayModelType::cmos_pwl); - libWarn(1160, attr, "delay_model %s not supported.", type_name); + library_->setDelayModelType(DelayModelType::cmos_pwl); + libWarn(1160, attr, "delay_model %s not supported.", type_name); } else if (stringEq(type_name, "cmos2")) { - library_->setDelayModelType(DelayModelType::cmos2); - libWarn(1161, attr, "delay_model %s not supported.", type_name); + library_->setDelayModelType(DelayModelType::cmos2); + libWarn(1161, attr, "delay_model %s not supported.", type_name); } else if (stringEq(type_name, "polynomial")) { - library_->setDelayModelType(DelayModelType::polynomial); - libWarn(1162, attr, "delay_model %s not supported.", type_name); + library_->setDelayModelType(DelayModelType::polynomial); + libWarn(1162, attr, "delay_model %s not supported.", type_name); } // Evil IBM garbage. else if (stringEq(type_name, "dcm")) { - library_->setDelayModelType(DelayModelType::dcm); - libWarn(1163, attr, "delay_model %s not supported..", type_name); + library_->setDelayModelType(DelayModelType::dcm); + libWarn(1163, attr, "delay_model %s not supported..", type_name); } else - libWarn(1164, attr, "unknown delay_model %s.", type_name); + libWarn(1164, attr, "unknown delay_model %s.", type_name); } } } @@ -965,11 +964,11 @@ LibertyReader::visitBusStyle(LibertyAttr *attr) const char *bus_style = getAttrString(attr); // Assume bus style is of the form "%s[%d]". if (bus_style - && strlen(bus_style) == 6 - && bus_style[0] == '%' - && bus_style[1] == 's' - && bus_style[3] == '%' - && bus_style[4] == 'd') + && strlen(bus_style) == 6 + && bus_style[0] == '%' + && bus_style[1] == 's' + && bus_style[3] == '%' + && bus_style[4] == 'd') library_->setBusBrkts(bus_style[2], bus_style[5]); else libWarn(1165, attr, "unknown bus_naming_style format."); @@ -981,42 +980,42 @@ LibertyReader::visitVoltageMap(LibertyAttr *attr) { if (library_) { if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - if (value->isString()) { - const char *supply_name = value->stringValue(); - if (value_iter.hasNext()) { - value = value_iter.next(); + LibertyAttrValueSeq *values = attr->values(); + if (values->size() >= 1) { + LibertyAttrValue *value = (*values)[0]; + if (value->isString()) { + const char *supply_name = value->stringValue(); + if (values->size() == 2) { + value = (*values)[1]; bool valid = false; - float voltage; - if (value->isFloat()) { - voltage = value->floatValue(); - valid = true; - } - else if (value->isString()) { - try { + float voltage; + if (value->isFloat()) { + voltage = value->floatValue(); + valid = true; + } + else if (value->isString()) { + try { voltage = std::stof(value->stringValue()); valid = true; - } - catch (...) { - valid = false; - } - } - - if (valid) - library_->addSupplyVoltage(supply_name, voltage); - else - libWarn(1166, attr, "voltage_map voltage is not a float."); - } - else - libWarn(1167, attr, "voltage_map missing voltage."); - } - else - libWarn(1168, attr, "voltage_map supply name is not a string."); + } + catch (...) { + valid = false; + } + } + + if (valid) + library_->addSupplyVoltage(supply_name, voltage); + else + libWarn(1166, attr, "voltage_map voltage is not a float."); + } + else + libWarn(1167, attr, "voltage_map missing voltage."); + } + else + libWarn(1168, attr, "voltage_map supply name is not a string."); } else - libWarn(1169, attr, "voltage_map missing supply name and voltage."); + libWarn(1169, attr, "voltage_map missing supply name and voltage."); } else libWarn(1170, attr, "voltage_map missing values suffix."); @@ -1104,7 +1103,7 @@ LibertyReader::visitDefaultMaxTransition(LibertyAttr *attr) getAttrFloat(attr, value, exists); if (exists) { if (value == 0.0) - libWarn(1171, attr, "default_max_transition is 0.0."); + libWarn(1171, attr, "default_max_transition is 0.0."); library_->setDefaultMaxSlew(value * time_scale_); } } @@ -1119,7 +1118,7 @@ LibertyReader::visitDefaultMaxFanout(LibertyAttr *attr) getAttrFloat(attr, value, exists); if (exists) { if (value == 0.0) - libWarn(1172, attr, "default_max_fanout is 0.0."); + libWarn(1172, attr, "default_max_fanout is 0.0."); library_->setDefaultMaxFanout(value); } } @@ -1139,7 +1138,7 @@ LibertyReader::visitDefaultIntrinsicFall(LibertyAttr *attr) void LibertyReader::visitDefaultIntrinsic(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (library_) { float value; @@ -1164,7 +1163,7 @@ LibertyReader::visitDefaultInoutPinFallRes(LibertyAttr *attr) void LibertyReader::visitDefaultInoutPinRes(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (library_) { float value; @@ -1189,7 +1188,7 @@ LibertyReader::visitDefaultOutputPinFallRes(LibertyAttr *attr) void LibertyReader::visitDefaultOutputPinRes(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (library_) { float value; @@ -1209,7 +1208,7 @@ LibertyReader::visitDefaultFanoutLoad(LibertyAttr *attr) getAttrFloat(attr, value, exists); if (exists) { if (value == 0.0) - libWarn(1173, attr, "default_fanout_load is 0.0."); + libWarn(1173, attr, "default_fanout_load is 0.0."); library_->setDefaultFanoutLoad(value); } } @@ -1235,10 +1234,10 @@ LibertyReader::visitDefaultWireLoadMode(LibertyAttr *attr) if (wire_load_mode) { WireloadMode mode = stringWireloadMode(wire_load_mode); if (mode != WireloadMode::unknown) - library_->setDefaultWireloadMode(mode); + library_->setDefaultWireloadMode(mode); else - libWarn(1174, attr, "default_wire_load_mode %s not found.", - wire_load_mode); + libWarn(1174, attr, "default_wire_load_mode %s not found.", + wire_load_mode); } } } @@ -1281,7 +1280,7 @@ LibertyReader::visitInputThresholdPctRise(LibertyAttr *attr) void LibertyReader::visitInputThresholdPct(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (library_) { float value; @@ -1307,7 +1306,7 @@ LibertyReader::visitOutputThresholdPctRise(LibertyAttr *attr) void LibertyReader::visitOutputThresholdPct(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (library_) { float value; @@ -1333,7 +1332,7 @@ LibertyReader::visitSlewLowerThresholdPctRise(LibertyAttr *attr) void LibertyReader::visitSlewLowerThresholdPct(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (library_) { float value; @@ -1359,7 +1358,7 @@ LibertyReader::visitSlewUpperThresholdPctRise(LibertyAttr *attr) void LibertyReader::visitSlewUpperThresholdPct(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (library_) { float value; @@ -1414,7 +1413,7 @@ LibertyReader::beginTableTemplateOutputCurrent(LibertyGroup *group) void LibertyReader::beginTableTemplate(LibertyGroup *group, - TableTemplateType type) + TableTemplateType type) { if (library_) { const char *name = group->firstName(); @@ -1455,7 +1454,7 @@ LibertyReader::endTableTemplate(LibertyGroup *group) TableAxisPtr LibertyReader::makeAxis(int index, - LibertyGroup *group) + LibertyGroup *group) { TableAxisVariable axis_var = axis_var_[index]; FloatSeq *axis_values = axis_values_[index]; @@ -1504,7 +1503,7 @@ LibertyReader::visitVariable3(LibertyAttr *attr) void LibertyReader::visitVariable(int index, - LibertyAttr *attr) + LibertyAttr *attr) { if (tbl_template_) { const char *type = getAttrString(attr); @@ -1536,7 +1535,7 @@ LibertyReader::visitIndex3(LibertyAttr *attr) void LibertyReader::visitIndex(int index, - LibertyAttr *attr) + LibertyAttr *attr) { if (tbl_template_ // Ignore index_xx in ecsm_waveform groups. @@ -1576,15 +1575,15 @@ LibertyReader::endType(LibertyGroup *group) if (type_bit_from_exists_ && type_bit_to_exists_) { BusDcl *bus_dcl = new BusDcl(name, type_bit_from_, type_bit_to_); if (cell_) - cell_->addBusDcl(bus_dcl); + cell_->addBusDcl(bus_dcl); else if (library_) - library_->addBusDcl(bus_dcl); + library_->addBusDcl(bus_dcl); } else { if (!type_bit_from_exists_) - libWarn(1179, group, "bus type %s missing bit_from.", name); + libWarn(1179, group, "bus type %s missing bit_from.", name); if (!type_bit_to_exists_) - libWarn(1180, group, "bus type %s missing bit_to.", name); + libWarn(1180, group, "bus type %s missing bit_to.", name); } } else @@ -1646,18 +1645,18 @@ LibertyReader::visitScaleFactorSuffix(LibertyAttr *attr) if (parser.hasNext()) { const char *tr_name = parser.next(); if (stringEq(tr_name, "rise")) - rf = RiseFall::rise(); + rf = RiseFall::rise(); else if (stringEq(tr_name, "fall")) - rf = RiseFall::fall(); + rf = RiseFall::fall(); } if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown - && rf) { + && type != ScaleFactorType::unknown + && rf) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) - scale_factors_->setScale(type, pvt, rf, value); + scale_factors_->setScale(type, pvt, rf, value); } } } @@ -1680,22 +1679,22 @@ LibertyReader::visitScaleFactorPrefix(LibertyAttr *attr) if (parser.hasNext()) { const char *tr_name = parser.next(); if (stringEq(tr_name, "rise")) - rf = RiseFall::rise(); + rf = RiseFall::rise(); else if (stringEq(tr_name, "fall")) - rf = RiseFall::fall(); + rf = RiseFall::fall(); } if (parser.hasNext()) { const char *type_name = parser.next(); type = findScaleFactorType(type_name); } if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown - && rf) { + && type != ScaleFactorType::unknown + && rf) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) - scale_factors_->setScale(type, pvt, rf, value); + scale_factors_->setScale(type, pvt, rf, value); } } } @@ -1725,18 +1724,18 @@ LibertyReader::visitScaleFactorHiLow(LibertyAttr *attr) if (parser.hasNext()) { tr_name = parser.next(); if (stringEq(tr_name, "high")) - rf = RiseFall::rise(); + rf = RiseFall::rise(); else if (stringEq(tr_name, "low")) - rf = RiseFall::fall(); + rf = RiseFall::fall(); } if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown - && rf) { + && type != ScaleFactorType::unknown + && rf) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) - scale_factors_->setScale(type, pvt, rf, value); + scale_factors_->setScale(type, pvt, rf, value); } } } @@ -1762,12 +1761,12 @@ LibertyReader::visitScaleFactor(LibertyAttr *attr) type = findScaleFactorType(type_name); } if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown) { + && type != ScaleFactorType::unknown) { float value; bool exists; getAttrFloat(attr, value, exists); if (exists) - scale_factors_->setScale(type, pvt, value); + scale_factors_->setScale(type, pvt, value); } } } @@ -1927,37 +1926,38 @@ LibertyReader::visitWireloadFromArea(LibertyAttr *attr) { if (wireload_selection_) { if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - if (value->isFloat()) { - float min_area = value->floatValue(); - value = value_iter.next(); - if (value->isFloat()) { - float max_area = value->floatValue(); - value = value_iter.next(); - if (value->isString()) { - const char *wireload_name = value->stringValue(); - const Wireload *wireload = - library_->findWireload(wireload_name); - if (wireload) - wireload_selection_->addWireloadFromArea(min_area, max_area, - wireload); - else - libWarn(1187, attr, "wireload %s not found.", wireload_name); - } - else - libWarn(1188, attr, - "wire_load_from_area wireload name not a string."); - } - else - libWarn(1189, attr, "wire_load_from_area min not a float."); - } - else - libWarn(1190, attr, "wire_load_from_area max not a float."); + LibertyAttrValueSeq *values = attr->values(); + if (values->size() == 3) { + LibertyAttrValue *value = (*values)[0]; + if (value->isFloat()) { + float min_area = value->floatValue(); + value = (*values)[1]; + if (value->isFloat()) { + float max_area = value->floatValue(); + + value = (*values)[2]; + if (value->isString()) { + const char *wireload_name = value->stringValue(); + const Wireload *wireload = + library_->findWireload(wireload_name); + if (wireload) + wireload_selection_->addWireloadFromArea(min_area, max_area, + wireload); + else + libWarn(1187, attr, "wireload %s not found.", wireload_name); + } + else + libWarn(1188, attr, + "wire_load_from_area wireload name not a string."); + } + else + libWarn(1189, attr, "wire_load_from_area min not a float."); + } + else + libWarn(1190, attr, "wire_load_from_area max not a float."); } else - libWarn(1191, attr, "wire_load_from_area missing parameters."); + libWarn(1191, attr, "wire_load_from_area missing parameters."); } else libWarn(1192, attr, "wire_load_from_area missing parameters."); @@ -1999,12 +1999,12 @@ LibertyReader::endCell(LibertyGroup *group) if (ocv_derate_name_) { OcvDerate *derate = cell_->findOcvDerate(ocv_derate_name_); if (derate == nullptr) - derate = library_->findOcvDerate(ocv_derate_name_); + derate = library_->findOcvDerate(ocv_derate_name_); if (derate) - cell_->setOcvDerate(derate); + cell_->setOcvDerate(derate); else - libWarn(1194, group, "cell %s ocv_derate_group %s not found.", - cell_->name(), ocv_derate_name_); + libWarn(1194, group, "cell %s ocv_derate_group %s not found.", + cell_->name(), ocv_derate_name_); stringDelete(ocv_derate_name_); ocv_derate_name_ = nullptr; } @@ -2031,13 +2031,13 @@ LibertyReader::finishPortGroups() void LibertyReader::checkPort(LibertyPort *port, - int line) + int line) { FuncExpr *func_expr = port->function(); if (func_expr) { if (func_expr->checkSize(port)) { libWarn(1195, line, "port %s function size does not match port size.", - port->name()); + port->name()); } } if (port->tristateEnable() @@ -2170,9 +2170,9 @@ LibertyReader::makeCellSequential(SequentialGroup *seq) } } cell_->makeSequential(size, is_register, clk_expr, data_expr, clr_expr, - preset_expr, seq->clrPresetVar1(), - seq->clrPresetVar2(), - seq->outPort(), seq->outInvPort()); + preset_expr, seq->clrPresetVar1(), + seq->clrPresetVar2(), + seq->outPort(), seq->outInvPort()); if (!is_register) checkLatchEnableSense(clk_expr, line); @@ -2189,11 +2189,10 @@ LibertyReader::makeCellSequential(SequentialGroup *seq) void LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, - int line) + int line) { - FuncExprPortIterator enable_iter(enable_func); - while (enable_iter.hasNext()) { - LibertyPort *enable_port = enable_iter.next(); + LibertyPortSet enable_ports = enable_func->ports(); + for (LibertyPort *enable_port : enable_ports) { TimingSense enable_sense = enable_func->portTimingSense(enable_port); switch (enable_sense) { case TimingSense::positive_unate: @@ -2201,12 +2200,12 @@ LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, break; case TimingSense::non_unate: libWarn(1200, line, "latch enable function is non-unate for port %s.", - enable_port->name()); + enable_port->name()); break; case TimingSense::none: case TimingSense::unknown: libWarn(1201, line, "latch enable function is unknown for port %s.", - enable_port->name()); + enable_port->name()); break; } } @@ -2224,14 +2223,14 @@ LibertyReader::makeStatetable() if (port) input_ports.push_back(port); else - libWarn(1298, statetable_->line(), "statetable input port %s not found.", + libWarn(1298, statetable_->line(), "statetable input port %s not found.", input.c_str()); } LibertyPortSeq internal_ports; for (const string &internal : statetable_->internalPorts()) { LibertyPort *port = cell_->findLibertyPort(internal.c_str()); if (port == nullptr) - port = makePort(cell_, internal.c_str()); + port = makePort(cell_, internal.c_str()); internal_ports.push_back(port); } cell_->makeStatetable(input_ports, internal_ports, statetable_->table()); @@ -2257,9 +2256,9 @@ LibertyReader::makeLeakagePowers() void LibertyReader::makeLibertyFunc(const char *expr, LibertySetFunc set_func, - bool invert, - const char *attr_name, - LibertyStmt *stmt) + bool invert, + const char *attr_name, + LibertyStmt *stmt) { LibertyFunc *func = new LibertyFunc(expr, set_func, invert, attr_name, stmt->line()); @@ -2272,13 +2271,13 @@ LibertyReader::parseCellFuncs() for (LibertyFunc *func : cell_funcs_) { FuncExpr *expr = parseFunc(func->expr(), func->attrName(), func->line()); if (func->invert() && expr) { - if (expr->op() == FuncExpr::op_not) { - FuncExpr *inv = expr; - expr = expr->left(); - delete inv; + if (expr->op() == FuncExpr::Op::not_) { + FuncExpr *inv = expr; + expr = expr->left(); + delete inv; } else - expr = FuncExpr::makeNot(expr); + expr = FuncExpr::makeNot(expr); } if (expr) func->setFunc()(expr); @@ -2296,17 +2295,17 @@ LibertyReader::beginScaledCell(LibertyGroup *group) if (scaled_cell_owner_) { const char *op_cond_name = group->secondName(); if (op_cond_name) { - op_cond_ = library_->findOperatingConditions(op_cond_name); - if (op_cond_) { - debugPrint(debug_, "liberty", 1, "scaled cell %s %s", + op_cond_ = library_->findOperatingConditions(op_cond_name); + if (op_cond_) { + debugPrint(debug_, "liberty", 1, "scaled cell %s %s", name, op_cond_name); - cell_ = library_->makeScaledCell(name, filename_); - } - else - libWarn(1202, group, "operating conditions %s not found.", op_cond_name); + cell_ = library_->makeScaledCell(name, filename_); + } + else + libWarn(1202, group, "operating conditions %s not found.", op_cond_name); } else - libWarn(1203, group, "scaled_cell missing operating condition."); + libWarn(1203, group, "scaled_cell missing operating condition."); } else libWarn(1204, group, "scaled_cell cell %s has not been defined.", name); @@ -2339,25 +2338,25 @@ LibertyReader::checkScaledCell(LibertyGroup *group) if (equivCellPorts(cell_, scaled_cell_owner_)) { if (!equivCellPorts(cell_, scaled_cell_owner_)) libWarn(1206, group, "scaled_cell %s, %s ports do not match cell ports", - cell_->name(), - op_cond_->name()); + cell_->name(), + op_cond_->name()); if (!equivCellFuncs(cell_, scaled_cell_owner_)) libWarn(1206, group, "scaled_cell %s, %s port functions do not match cell port functions.", - cell_->name(), - op_cond_->name()); + cell_->name(), + op_cond_->name()); } else libWarn(1207, group, "scaled_cell ports do not match cell ports."); if (!equivCellTimingArcSets(cell_, scaled_cell_owner_)) libWarn(1208, group, "scaled_cell %s, %s timing does not match cell timing.", - cell_->name(), - op_cond_->name()); + cell_->name(), + op_cond_->name()); } void LibertyReader::makeTimingArcs(LibertyPort *to_port, - TimingGroup *timing) + TimingGroup *timing) { LibertyPort *related_out_port = nullptr; const char *related_out_port_name = timing->relatedOutputPortName(); @@ -2387,7 +2386,7 @@ LibertyReader::makeTimingArcs(LibertyPort *to_port, void TimingGroup::makeTimingModels(LibertyCell *cell, - LibertyReader *visitor) + LibertyReader *visitor) { switch (cell->libertyLibrary()->delayModelType()) { case DelayModelType::cmos_linear: @@ -2417,18 +2416,18 @@ TimingGroup::makeLinearModels(LibertyCell *cell) TimingModel *model = nullptr; if (timingTypeIsCheck(attrs_->timingType())) { if (intr_exists) - model = new CheckLinearModel(cell, intr); + model = new CheckLinearModel(cell, intr); } else { float res = resistance_[rf_index]; bool res_exists = resistance_exists_[rf_index]; if (!res_exists) - library->defaultPinResistance(rf, PortDirection::output(), - res, res_exists); + library->defaultPinResistance(rf, PortDirection::output(), + res, res_exists); if (!res_exists) - res = 0.0F; + res = 0.0F; if (intr_exists) - model = new GateLinearModel(cell, intr, res); + model = new GateLinearModel(cell, intr, res); } attrs_->setModel(rf, model); } @@ -2451,22 +2450,22 @@ TimingGroup::makeTableModels(LibertyCell *cell, output_waveforms_[rf_index])); TimingType timing_type = attrs_->timingType(); if (timing_type == TimingType::clear - || timing_type == TimingType::combinational - || timing_type == TimingType::combinational_fall - || timing_type == TimingType::combinational_rise - || timing_type == TimingType::falling_edge - || timing_type == TimingType::preset - || timing_type == TimingType::rising_edge - || timing_type == TimingType::three_state_disable - || timing_type == TimingType::three_state_disable_rise - || timing_type == TimingType::three_state_disable_fall - || timing_type == TimingType::three_state_enable - || timing_type == TimingType::three_state_enable_fall - || timing_type == TimingType::three_state_enable_rise) { - if (transition == nullptr) - reader->libWarn(1210, line_, "missing %s_transition.", rf->name()); - if (delay == nullptr) - reader->libWarn(1211, line_, "missing cell_%s.", rf->name()); + || timing_type == TimingType::combinational + || timing_type == TimingType::combinational_fall + || timing_type == TimingType::combinational_rise + || timing_type == TimingType::falling_edge + || timing_type == TimingType::preset + || timing_type == TimingType::rising_edge + || timing_type == TimingType::three_state_disable + || timing_type == TimingType::three_state_disable_rise + || timing_type == TimingType::three_state_disable_fall + || timing_type == TimingType::three_state_enable + || timing_type == TimingType::three_state_enable_fall + || timing_type == TimingType::three_state_enable_rise) { + if (transition == nullptr) + reader->libWarn(1210, line_, "missing %s_transition.", rf->name()); + if (delay == nullptr) + reader->libWarn(1211, line_, "missing cell_%s.", rf->name()); } } else if (constraint) @@ -2477,10 +2476,10 @@ TimingGroup::makeTableModels(LibertyCell *cell, void LibertyReader::makeTimingArcs(const char *from_port_name, - PortNameBitIterator &from_port_iter, - LibertyPort *to_port, - LibertyPort *related_out_port, - TimingGroup *timing) + PortNameBitIterator &from_port_iter, + LibertyPort *to_port, + LibertyPort *related_out_port, + TimingGroup *timing) { if (from_port_iter.size() == 1 && !to_port->hasMembers()) { // one -> one @@ -2510,8 +2509,8 @@ LibertyReader::makeTimingArcs(const char *from_port_name, libWarn(1214, timing->line(), "timing group from output port."); LibertyPortMemberIterator bit_iter(to_port); while (bit_iter.hasNext()) { - LibertyPort *to_port_bit = bit_iter.next(); - builder_.makeTimingArcs(cell_, from_port, to_port_bit, related_out_port, + LibertyPort *to_port_bit = bit_iter.next(); + builder_.makeTimingArcs(cell_, from_port, to_port_bit, related_out_port, timing->attrs(), timing->line()); } } @@ -2524,42 +2523,42 @@ LibertyReader::makeTimingArcs(const char *from_port_name, 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.", - from_port_name, - to_port->name()); + libWarn(1216, timing->line(), + "timing port %s and related port %s are different sizes.", + from_port_name, + to_port->name()); // align to/from iterators for one-to-one mapping while (from_size > to_size) { - from_size--; - from_port_iter.next(); + from_size--; + from_port_iter.next(); } while (to_size > from_size) { - to_size--; - to_port_iter.next(); + to_size--; + to_port_iter.next(); } // make timing arcs while (from_port_iter.hasNext() && to_port_iter.hasNext()) { - 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."); - builder_.makeTimingArcs(cell_, from_port_bit, to_port_bit, - related_out_port, timing->attrs(), - timing->line()); + 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."); + builder_.makeTimingArcs(cell_, from_port_bit, to_port_bit, + related_out_port, timing->attrs(), + timing->line()); } } else { while (from_port_iter.hasNext()) { - LibertyPort *from_port_bit = from_port_iter.next(); + LibertyPort *from_port_bit = from_port_iter.next(); if (from_port_bit->direction()->isOutput()) libWarn(1217, timing->line(), "timing group from output port."); - LibertyPortMemberIterator to_iter(to_port); - while (to_iter.hasNext()) { - LibertyPort *to_port_bit = to_iter.next(); - builder_.makeTimingArcs(cell_, from_port_bit, to_port_bit, + LibertyPortMemberIterator to_iter(to_port); + while (to_iter.hasNext()) { + LibertyPort *to_port_bit = to_iter.next(); + builder_.makeTimingArcs(cell_, from_port_bit, to_port_bit, related_out_port, timing->attrs(), timing->line()); - } + } } } } @@ -2568,7 +2567,7 @@ LibertyReader::makeTimingArcs(const char *from_port_name, void LibertyReader::makeTimingArcs(LibertyPort *to_port, LibertyPort *related_out_port, - TimingGroup *timing) + TimingGroup *timing) { if (to_port->hasMembers()) { LibertyPortMemberIterator bit_iter(to_port); @@ -2707,17 +2706,17 @@ void LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) { if (timing_) { - Set slew_set, cap_set; + std::set slew_set, cap_set; FloatSeq *slew_values = new FloatSeq; FloatSeq *cap_values = new FloatSeq; for (OutputWaveform *waveform : output_currents_) { float slew = waveform->slew(); - if (!slew_set.hasKey(slew)) { + if (!slew_set.contains(slew)) { slew_set.insert(slew); slew_values->push_back(slew); } float cap = waveform->cap(); - if (!cap_set.hasKey(cap)) { + if (!cap_set.contains(cap)) { cap_set.insert(cap); cap_values->push_back(cap); } @@ -2750,7 +2749,7 @@ LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) current_waveforms, ref_time_tbl); timing_->setOutputWaveforms(rf_, output_current); - output_currents_.deleteContentsClear(); + deleteContents(output_currents_); } } @@ -2878,7 +2877,7 @@ LibertyReader::visitDriverWaveformRiseFall(LibertyAttr *attr, void LibertyReader::makeInternalPowers(LibertyPort *port, - InternalPowerGroup *power_group) + InternalPowerGroup *power_group) { int line = power_group->line(); StringSeq *related_port_names = power_group->relatedPortNames(); @@ -2886,9 +2885,9 @@ LibertyReader::makeInternalPowers(LibertyPort *port, for (const char *related_port_name : *related_port_names) { PortNameBitIterator related_port_iter(cell_, related_port_name, this, line); if (related_port_iter.hasNext()) { - debugPrint(debug_, "liberty", 2, " power %s -> %s", + debugPrint(debug_, "liberty", 2, " power %s -> %s", related_port_name, port->name()); - makeInternalPowers(port, related_port_name, related_port_iter, power_group); + makeInternalPowers(port, related_port_name, related_port_iter, power_group); } } } @@ -2896,8 +2895,8 @@ LibertyReader::makeInternalPowers(LibertyPort *port, if (port->hasMembers()) { LibertyPortMemberIterator bit_iter(port); while (bit_iter.hasNext()) { - LibertyPort *port_bit = bit_iter.next(); - builder_.makeInternalPower(cell_, port_bit, nullptr, power_group); + LibertyPort *port_bit = bit_iter.next(); + builder_.makeInternalPower(cell_, port_bit, nullptr, power_group); } } else @@ -2907,9 +2906,9 @@ LibertyReader::makeInternalPowers(LibertyPort *port, void LibertyReader::makeInternalPowers(LibertyPort *port, - const char *related_port_name, - PortNameBitIterator &related_port_iter, - InternalPowerGroup *power_group) + const char *related_port_name, + PortNameBitIterator &related_port_iter, + InternalPowerGroup *power_group) { if (related_port_iter.size() == 1 && !port->hasMembers()) { // one -> one @@ -2931,8 +2930,8 @@ LibertyReader::makeInternalPowers(LibertyPort *port, LibertyPort *related_port = related_port_iter.next(); LibertyPortMemberIterator bit_iter(port); while (bit_iter.hasNext()) { - LibertyPort *port_bit = bit_iter.next(); - builder_.makeInternalPower(cell_, port_bit, related_port, power_group); + LibertyPort *port_bit = bit_iter.next(); + builder_.makeInternalPower(cell_, port_bit, related_port, power_group); } } } @@ -2940,27 +2939,27 @@ LibertyReader::makeInternalPowers(LibertyPort *port, // bus -> bus if (power_group->isOneToOne()) { if (static_cast(related_port_iter.size()) == port->size()) { - LibertyPortMemberIterator to_iter(port); - while (related_port_iter.hasNext() && to_iter.hasNext()) { - LibertyPort *related_port_bit = related_port_iter.next(); - LibertyPort *port_bit = to_iter.next(); - builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); - } + LibertyPortMemberIterator to_iter(port); + while (related_port_iter.hasNext() && to_iter.hasNext()) { + LibertyPort *related_port_bit = related_port_iter.next(); + LibertyPort *port_bit = to_iter.next(); + builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); + } } else - libWarn(1227, power_group->line(), - "internal_power port %s and related port %s are different sizes.", - related_port_name, - port->name()); + libWarn(1227, power_group->line(), + "internal_power port %s and related port %s are different sizes.", + related_port_name, + port->name()); } else { while (related_port_iter.hasNext()) { - LibertyPort *related_port_bit = related_port_iter.next(); - LibertyPortMemberIterator to_iter(port); - while (to_iter.hasNext()) { - LibertyPort *port_bit = to_iter.next(); - builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); - } + LibertyPort *related_port_bit = related_port_iter.next(); + LibertyPortMemberIterator to_iter(port); + while (to_iter.hasNext()) { + LibertyPort *port_bit = to_iter.next(); + builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); + } } } } @@ -3136,11 +3135,11 @@ LibertyReader::visitClockGatingIntegratedCell(LibertyAttr *attr) const char *clock_gate_type = getAttrString(attr); if (clock_gate_type) { if (stringBeginEqual(clock_gate_type, "latch_posedge")) - cell_->setClockGateType(ClockGateType::latch_posedge); + cell_->setClockGateType(ClockGateType::latch_posedge); else if (stringBeginEqual(clock_gate_type, "latch_negedge")) - cell_->setClockGateType(ClockGateType::latch_negedge); + cell_->setClockGateType(ClockGateType::latch_negedge); else - cell_->setClockGateType(ClockGateType::other); + cell_->setClockGateType(ClockGateType::other); } } } @@ -3176,17 +3175,17 @@ LibertyReader::beginPin(LibertyGroup *group) saved_port_group_ = port_group_; ports_ = new LibertyPortSeq; for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *port_name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " port %s", port_name); - PortNameBitIterator port_iter(cell_, port_name, this, group->line()); - while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); - ports_->push_back(port); - } - } - else - libWarn(1231, group, "pin name is not a string."); + if (param->isString()) { + const char *port_name = param->stringValue(); + debugPrint(debug_, "liberty", 1, " port %s", port_name); + PortNameBitIterator port_iter(cell_, port_name, this, group->line()); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + ports_->push_back(port); + } + } + else + libWarn(1231, group, "pin name is not a string."); } } else if (in_bundle_) { @@ -3194,30 +3193,30 @@ LibertyReader::beginPin(LibertyGroup *group) saved_port_group_ = port_group_; ports_ = new LibertyPortSeq; for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " port %s", name); - LibertyPort *port = findPort(name); - if (port == nullptr) - port = makePort(cell_, name); - ports_->push_back(port); - } - else - libWarn(1232, group, "pin name is not a string."); + if (param->isString()) { + const char *name = param->stringValue(); + debugPrint(debug_, "liberty", 1, " port %s", name); + LibertyPort *port = findPort(name); + if (port == nullptr) + port = makePort(cell_, name); + ports_->push_back(port); + } + else + libWarn(1232, group, "pin name is not a string."); } } else { ports_ = new LibertyPortSeq; // Multiple port names can share group def. for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " port %s", name); - LibertyPort *port = makePort(cell_, name); - ports_->push_back(port); - } - else - libWarn(1233, group, "pin name is not a string."); + if (param->isString()) { + const char *name = param->stringValue(); + debugPrint(debug_, "liberty", 1, " port %s", name); + LibertyPort *port = makePort(cell_, name); + ports_->push_back(port); + } + else + libWarn(1233, group, "pin name is not a string."); } } port_group_ = new PortGroup(ports_, group->line()); @@ -3289,7 +3288,7 @@ LibertyReader::setPortCapDefault(LibertyPort *port) bool exists; port->capacitance(tr, min_max, cap, exists); if (!exists) - port->setCapacitance(tr, min_max, defaultCap(port)); + port->setCapacitance(tr, min_max, defaultCap(port)); } } } @@ -3322,7 +3321,7 @@ LibertyReader::beginBusOrBundle(LibertyGroup *group) if (param->isString()) { const char *name = param->stringValue(); if (name) - bus_names_.push_back(stringCopy(name)); + bus_names_.push_back(stringCopy(name)); } } ports_ = new LibertyPortSeq; @@ -3350,17 +3349,17 @@ LibertyReader::visitBusType(LibertyAttr *attr) // Look for bus dcl local to cell first. BusDcl *bus_dcl = cell_->findBusDcl(bus_type); if (bus_dcl == nullptr) - bus_dcl = library_->findBusDcl(bus_type); + bus_dcl = library_->findBusDcl(bus_type); if (bus_dcl) { for (const char *name : bus_names_) { - debugPrint(debug_, "liberty", 1, " bus %s", name); - LibertyPort *port = makeBusPort(cell_, name, bus_dcl->from(), + debugPrint(debug_, "liberty", 1, " bus %s", name); + LibertyPort *port = makeBusPort(cell_, name, bus_dcl->from(), bus_dcl->to(), bus_dcl); - ports_->push_back(port); - } + ports_->push_back(port); + } } else - libWarn(1235, attr, "bus_type %s not found.", bus_type); + libWarn(1235, attr, "bus_type %s not found.", bus_type); } else libWarn(1236, attr, "bus_type is not a string."); @@ -3393,21 +3392,21 @@ LibertyReader::visitMembers(LibertyAttr *attr) if (cell_) { if (attr->isComplex()) { for (const char *name : bus_names_) { - debugPrint(debug_, "liberty", 1, " bundle %s", name); - ConcretePortSeq *members = new ConcretePortSeq; + debugPrint(debug_, "liberty", 1, " bundle %s", name); + ConcretePortSeq *members = new ConcretePortSeq; for (LibertyAttrValue *value : *attr->values()) { - if (value->isString()) { - const char *port_name = value->stringValue(); - LibertyPort *port = findPort(port_name); - if (port == nullptr) - port = makePort(cell_, port_name); - members->push_back(port); - } - else - libWarn(1238, attr, "member is not a string."); - } - LibertyPort *port = builder_.makeBundlePort(cell_, name, members); - ports_->push_back(port); + if (value->isString()) { + const char *port_name = value->stringValue(); + LibertyPort *port = findPort(port_name); + if (port == nullptr) + port = makePort(cell_, port_name); + members->push_back(port); + } + else + libWarn(1238, attr, "member is not a string."); + } + LibertyPort *port = builder_.makeBundlePort(cell_, name, members); + ports_->push_back(port); } } else @@ -3441,7 +3440,7 @@ libertyReaderFindPort(LibertyCell *cell, LibertyPort * LibertyReader::findPort(LibertyCell *cell, - const char *port_name) + const char *port_name) { return libertyReaderFindPort(cell, port_name); } @@ -3454,21 +3453,21 @@ LibertyReader::visitDirection(LibertyAttr *attr) if (dir) { PortDirection *port_dir = PortDirection::unknown(); if (stringEq(dir, "input")) - port_dir = PortDirection::input(); + port_dir = PortDirection::input(); else if (stringEq(dir, "output")) - port_dir = PortDirection::output(); + port_dir = PortDirection::output(); else if (stringEq(dir, "inout")) - port_dir = PortDirection::bidirect(); + port_dir = PortDirection::bidirect(); else if (stringEq(dir, "internal")) - port_dir = PortDirection::internal(); + port_dir = PortDirection::internal(); else - libWarn(1240, attr, "unknown port direction."); + libWarn(1240, attr, "unknown port direction."); for (LibertyPort *port : *ports_) { - // Tristate enable function sets direction to tristate; don't - // clobber it. - if (!port->direction()->isTristate()) - port->setDirection(port_dir); + // Tristate enable function sets direction to tristate; don't + // clobber it. + if (!port->direction()->isTristate()) + port->setDirection(port_dir); } } } @@ -3495,7 +3494,7 @@ LibertyReader::visitThreeState(LibertyAttr *attr) const char *three_state = getAttrString(attr); if (three_state) { for (LibertyPort *port : *ports_) - makeLibertyFunc(three_state, + makeLibertyFunc(three_state, [port] (FuncExpr *expr) { port->setTristateEnable(expr); }, true, "three_state", attr); } @@ -3523,7 +3522,7 @@ LibertyReader::visitClock(LibertyAttr *attr) getAttrBool(attr, is_clk, exists); if (exists) { for (LibertyPort *port : *ports_) - port->setIsClock(is_clk); + port->setIsClock(is_clk); } } } @@ -3551,7 +3550,7 @@ LibertyReader::visitCapacitance(LibertyAttr *attr) if (exists) { cap *= cap_scale_; for (LibertyPort *port : *ports_) - port->setCapacitance(cap); + port->setCapacitance(cap); } } if (wireload_) { @@ -3573,8 +3572,8 @@ LibertyReader::visitRiseCap(LibertyAttr *attr) if (exists) { cap *= cap_scale_; for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::rise(), MinMax::min(), cap); - port->setCapacitance(RiseFall::rise(), MinMax::max(), cap); + port->setCapacitance(RiseFall::rise(), MinMax::min(), cap); + port->setCapacitance(RiseFall::rise(), MinMax::max(), cap); } } } @@ -3590,8 +3589,8 @@ LibertyReader::visitFallCap(LibertyAttr *attr) if (exists) { cap *= cap_scale_; for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::fall(), MinMax::min(), cap); - port->setCapacitance(RiseFall::fall(), MinMax::max(), cap); + port->setCapacitance(RiseFall::fall(), MinMax::min(), cap); + port->setCapacitance(RiseFall::fall(), MinMax::max(), cap); } } } @@ -3608,8 +3607,8 @@ LibertyReader::visitRiseCapRange(LibertyAttr *attr) min *= cap_scale_; max *= cap_scale_; for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::rise(), MinMax::min(), min); - port->setCapacitance(RiseFall::rise(), MinMax::max(), max); + port->setCapacitance(RiseFall::rise(), MinMax::min(), min); + port->setCapacitance(RiseFall::rise(), MinMax::max(), max); } } } @@ -3626,8 +3625,8 @@ LibertyReader::visitFallCapRange(LibertyAttr *attr) min *= cap_scale_; max *= cap_scale_; for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::fall(), MinMax::min(), min); - port->setCapacitance(RiseFall::fall(), MinMax::max(), max); + port->setCapacitance(RiseFall::fall(), MinMax::min(), min); + port->setCapacitance(RiseFall::fall(), MinMax::max(), max); } } } @@ -3641,7 +3640,7 @@ LibertyReader::defaultCap(LibertyPort *port) if (dir->isInput()) cap = library_->defaultInputPinCap(); else if (dir->isOutput() - || dir->isTristate()) + || dir->isTristate()) cap = library_->defaultOutputPinCap(); else if (dir->isBidirect()) cap = library_->defaultBidirectPinCap(); @@ -3657,8 +3656,8 @@ LibertyReader::visitFanoutLoad(LibertyAttr *attr) getAttrFloat(attr, fanout, exists); if (exists) { visitPorts([&] (LibertyPort *port) { - port->setFanoutLoad(fanout); - }); + port->setFanoutLoad(fanout); + }); } } } @@ -3677,7 +3676,7 @@ LibertyReader::visitMinFanout(LibertyAttr *attr) void LibertyReader::visitFanout(LibertyAttr *attr, - const MinMax *min_max) + const MinMax *min_max) { if (ports_) { float fanout; @@ -3685,8 +3684,8 @@ LibertyReader::visitFanout(LibertyAttr *attr, getAttrFloat(attr, fanout, exists); if (exists) { visitPorts([&] (LibertyPort *port) { - port->setFanoutLimit(fanout, min_max); - }); + port->setFanoutLimit(fanout, min_max); + }); } } } @@ -3713,11 +3712,11 @@ LibertyReader::visitMinMaxTransition(LibertyAttr *attr, getAttrFloat(attr, value, exists); if (exists) { if (min_max == MinMax::max() && value == 0.0) - libWarn(1241, attr, "max_transition is 0.0."); + libWarn(1241, attr, "max_transition is 0.0."); value *= time_scale_; visitPorts([&] (LibertyPort *port) { - port->setSlewLimit(value, min_max); - }); + port->setSlewLimit(value, min_max); + }); } } } @@ -3736,7 +3735,7 @@ LibertyReader::visitMinCapacitance(LibertyAttr *attr) void LibertyReader::visitMinMaxCapacitance(LibertyAttr *attr, - const MinMax *min_max) + const MinMax *min_max) { if (cell_) { float value; @@ -3744,10 +3743,9 @@ LibertyReader::visitMinMaxCapacitance(LibertyAttr *attr, getAttrFloat(attr, value, exists); if (exists) { value *= cap_scale_; - LibertyPortSeq::Iterator port_iter(ports_); visitPorts([&] (LibertyPort *port) { - port->setCapacitanceLimit(value, min_max); - }); + port->setCapacitanceLimit(value, min_max); + }); } } } @@ -3761,7 +3759,7 @@ LibertyReader::visitMinPeriod(LibertyAttr *attr) getAttrFloat(attr, value, exists); if (exists) { for (LibertyPort *port : *ports_) - port->setMinPeriod(value * time_scale_); + port->setMinPeriod(value * time_scale_); } } } @@ -3780,7 +3778,7 @@ LibertyReader::visitMinPulseWidthHigh(LibertyAttr *attr) void LibertyReader::visitMinPulseWidth(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (cell_) { float value; @@ -3789,7 +3787,7 @@ LibertyReader::visitMinPulseWidth(LibertyAttr *attr, if (exists) { value *= time_scale_; for (LibertyPort *port : *ports_) - port->setMinPulseWidth(rf, value); + port->setMinPulseWidth(rf, value); } } } @@ -3803,26 +3801,26 @@ LibertyReader::visitPulseClock(LibertyAttr *attr) const RiseFall *trigger = nullptr; const RiseFall *sense = nullptr; if (stringEq(pulse_clk, "rise_triggered_high_pulse")) { - trigger = RiseFall::rise(); - sense = RiseFall::rise(); + trigger = RiseFall::rise(); + sense = RiseFall::rise(); } else if (stringEq(pulse_clk, "rise_triggered_low_pulse")) { - trigger = RiseFall::rise(); - sense = RiseFall::fall(); + trigger = RiseFall::rise(); + sense = RiseFall::fall(); } else if (stringEq(pulse_clk, "fall_triggered_high_pulse")) { - trigger = RiseFall::fall(); - sense = RiseFall::rise(); + trigger = RiseFall::fall(); + sense = RiseFall::rise(); } else if (stringEq(pulse_clk, "fall_triggered_low_pulse")) { - trigger = RiseFall::fall(); - sense = RiseFall::fall(); + trigger = RiseFall::fall(); + sense = RiseFall::fall(); } else - libWarn(1242,attr, "pulse_latch unknown pulse type."); + libWarn(1242,attr, "pulse_latch unknown pulse type."); if (trigger) { for (LibertyPort *port : *ports_) - port->setPulseClk(trigger, sense); + port->setPulseClk(trigger, sense); } } } @@ -3920,7 +3918,7 @@ LibertyReader::visitPortBoolAttr(LibertyAttr *attr, getAttrBool(attr, value, exists); if (exists) { for (LibertyPort *port : *ports_) - (port->*setter)(value); + (port->*setter)(value); } } } @@ -3992,8 +3990,8 @@ LibertyReader::endLatchBank(LibertyGroup *) void LibertyReader::beginSequential(LibertyGroup *group, - bool is_register, - bool is_bank) + bool is_register, + bool is_bank) { if (cell_) { // Define ff/latch state variables as internal ports. @@ -4005,31 +4003,31 @@ LibertyReader::beginSequential(LibertyGroup *group, LibertyPort *out_port_inv = nullptr; if (out_name) { if (has_size) - out_port = makeBusPort(cell_, out_name, size - 1, 0, nullptr); + out_port = makeBusPort(cell_, out_name, size - 1, 0, nullptr); else - out_port = makePort(cell_, out_name); + out_port = makePort(cell_, out_name); out_port->setDirection(PortDirection::internal()); } if (out_inv_name) { if (has_size) - out_port_inv = makeBusPort(cell_, out_inv_name, size - 1, 0, nullptr); + out_port_inv = makeBusPort(cell_, out_inv_name, size - 1, 0, nullptr); else - out_port_inv = makePort(cell_, out_inv_name); + out_port_inv = makePort(cell_, out_inv_name); out_port_inv->setDirection(PortDirection::internal()); } sequential_ = new SequentialGroup(is_register, is_bank, - out_port, out_port_inv, size, - group->line()); + out_port, out_port_inv, size, + group->line()); cell_sequentials_.push_back(sequential_); } } void LibertyReader::seqPortNames(LibertyGroup *group, - const char *&out_name, - const char *&out_inv_name, - bool &has_size, - int &size) + const char *&out_name, + const char *&out_inv_name, + bool &has_size, + int &size) { out_name = nullptr; out_inv_name = nullptr; @@ -4262,14 +4260,14 @@ LibertyReader::endTiming(LibertyGroup *group) for (auto rf : RiseFall::range()) { TableModel *model = timing_->constraint(rf); if (model) { - ScaleFactorType type=timingTypeScaleFactorType(timing_->attrs()->timingType()); - model->setScaleFactorType(type); + ScaleFactorType type=timingTypeScaleFactorType(timing_->attrs()->timingType()); + model->setScaleFactorType(type); } } TimingType timing_type = timing_->attrs()->timingType(); if (timing_->relatedPortNames() == nullptr && !(timing_type == TimingType::min_pulse_width - || timing_type == TimingType::minimum_period + || timing_type == TimingType::minimum_period || timing_type == TimingType::min_clock_tree_path || timing_type == TimingType::max_clock_tree_path)) libWarn(1243, group, "timing group missing related_pin/related_bus_pin."); @@ -4289,7 +4287,7 @@ LibertyReader::visitRelatedPin(LibertyAttr *attr) void LibertyReader::visitRelatedPin(LibertyAttr *attr, - RelatedPortGroup *group) + RelatedPortGroup *group) { const char *port_names = getAttrString(attr); if (port_names) { @@ -4344,7 +4342,7 @@ LibertyReader::visitRelatedBusPins(LibertyAttr *attr) void LibertyReader::visitRelatedBusPins(LibertyAttr *attr, - RelatedPortGroup *group) + RelatedPortGroup *group) { const char *port_names = getAttrString(attr); if (port_names) { @@ -4371,9 +4369,9 @@ LibertyReader::visitTimingType(LibertyAttr *attr) if (type_name) { TimingType type = findTimingType(type_name); if (type == TimingType::unknown) - libWarn(1244, attr, "unknown timing_type %s.", type_name); + libWarn(1244, attr, "unknown timing_type %s.", type_name); else - timing_->attrs()->setTimingType(type); + timing_->attrs()->setTimingType(type); } } } @@ -4385,13 +4383,13 @@ LibertyReader::visitTimingSense(LibertyAttr *attr) const char *sense_name = getAttrString(attr); if (sense_name) { if (stringEq(sense_name, "non_unate")) - timing_->attrs()->setTimingSense(TimingSense::non_unate); + timing_->attrs()->setTimingSense(TimingSense::non_unate); else if (stringEq(sense_name, "positive_unate")) - timing_->attrs()->setTimingSense(TimingSense::positive_unate); + timing_->attrs()->setTimingSense(TimingSense::positive_unate); else if (stringEq(sense_name, "negative_unate")) - timing_->attrs()->setTimingSense(TimingSense::negative_unate); + timing_->attrs()->setTimingSense(TimingSense::negative_unate); else - libWarn(1245, attr, "unknown timing_sense %s.", sense_name); + libWarn(1245, attr, "unknown timing_sense %s.", sense_name); } } } @@ -4421,26 +4419,22 @@ LibertyReader::visitMode(LibertyAttr *attr) { if (timing_) { if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); - if (value->isString()) { - timing_->attrs()->setModeName(value->stringValue()); - if (value_iter.hasNext()) { - value = value_iter.next(); - if (value->isString()) - timing_->attrs()->setModeValue(value->stringValue()); - else - libWarn(1246, attr, "mode value is not a string."); - } - else - libWarn(1247, attr, "missing mode value."); - } - else - libWarn(1248, attr, "mode name is not a string."); + LibertyAttrValueSeq *values = attr->values(); + if (values->size() == 2) { + LibertyAttrValue *value = (*values)[0]; + if (value->isString()) + timing_->attrs()->setModeName(value->stringValue()); + else + libWarn(1248, attr, "mode name is not a string."); + + value = (*values)[1]; + if (value->isString()) + timing_->attrs()->setModeValue(value->stringValue()); + else + libWarn(1246, attr, "mode value is not a string."); } else - libWarn(1249, attr, "mode missing values."); + libWarn(1249, attr, "mode requirees 2 values."); } else libWarn(1250, attr, "mode missing mode name and value."); @@ -4461,7 +4455,7 @@ LibertyReader::visitIntrinsicFall(LibertyAttr *attr) void LibertyReader::visitIntrinsic(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (timing_) { float value; @@ -4486,7 +4480,7 @@ LibertyReader::visitFallResistance(LibertyAttr *attr) void LibertyReader::visitRiseFallResistance(LibertyAttr *attr, - const RiseFall *rf) + const RiseFall *rf) { if (timing_) { float value; @@ -4587,8 +4581,8 @@ LibertyReader::beginRiseTransitionDegredation(LibertyGroup *group) { if (library_) beginTableModel(group, TableTemplateType::delay, - RiseFall::rise(), time_scale_, - ScaleFactorType::transition); + RiseFall::rise(), time_scale_, + ScaleFactorType::transition); } void @@ -4596,8 +4590,8 @@ LibertyReader::beginFallTransitionDegredation(LibertyGroup *group) { if (library_) beginTableModel(group, TableTemplateType::delay, - RiseFall::fall(), time_scale_, - ScaleFactorType::transition); + RiseFall::fall(), time_scale_, + ScaleFactorType::transition); } void @@ -4619,22 +4613,22 @@ LibertyReader::endRiseFallTransitionDegredation(LibertyGroup *group) void LibertyReader::beginTimingTableModel(LibertyGroup *group, - const RiseFall *rf, - ScaleFactorType scale_factor_type) + const RiseFall *rf, + ScaleFactorType scale_factor_type) { if (timing_) beginTableModel(group, TableTemplateType::delay, rf, - time_scale_, scale_factor_type); + time_scale_, scale_factor_type); else libWarn(1255, group, "%s group not in timing group.", group->firstName()); } void LibertyReader::beginTableModel(LibertyGroup *group, - TableTemplateType type, - const RiseFall *rf, - float scale, - ScaleFactorType scale_factor_type) + TableTemplateType type, + const RiseFall *rf, + float scale, + ScaleFactorType scale_factor_type) { beginTable(group, type, scale); rf_ = rf; @@ -4653,8 +4647,8 @@ LibertyReader::endTableModel() void LibertyReader::beginTable(LibertyGroup *group, - TableTemplateType type, - float scale) + TableTemplateType type, + float scale) { const char *template_name = group->firstName(); if (library_ && template_name) { @@ -4720,8 +4714,8 @@ LibertyReader::makeTable(LibertyAttr *attr, // Column index1*size(index2) + index2 // Row index3 FloatTable *table = makeFloatTable(attr, - axis_[0]->size()*axis_[1]->size(), - axis_[2]->size(), scale); + axis_[0]->size()*axis_[1]->size(), + axis_[2]->size(), scale); table_ = make_shared(table, axis_[0], axis_[1], axis_[2]); } else if (axis_[0] && axis_[1]) { @@ -4729,7 +4723,7 @@ LibertyReader::makeTable(LibertyAttr *attr, // Row variable1/axis[0] // Column variable2/axis[1] FloatTable *table = makeFloatTable(attr, axis_[0]->size(), - axis_[1]->size(), scale); + axis_[1]->size(), scale); table_ = make_shared(table, axis_[0], axis_[1]); } else if (axis_[0]) { @@ -4754,9 +4748,9 @@ LibertyReader::makeTable(LibertyAttr *attr, FloatTable * LibertyReader::makeFloatTable(LibertyAttr *attr, - size_t rows, - size_t cols, - float scale) + size_t rows, + size_t cols, + float scale) { FloatTable *table = new FloatTable; table->reserve(rows); @@ -4775,24 +4769,24 @@ LibertyReader::makeFloatTable(LibertyAttr *attr, libWarn(1258, attr, "%s is not a list of floats.", attr->name()); if (row->size() != cols) { libWarn(1259, attr, "table row has %zu columns but axis has %zu.", - row->size(), - cols); + row->size(), + cols); // Fill out row columns with zeros. for (size_t c = row->size(); c < cols; c++) - row->push_back(0.0); + row->push_back(0.0); } } if (table->size() != rows) { libWarn(1260, attr, "table has %zu rows but axis has %zu.", - table->size(), - rows); + table->size(), + rows); // Fill with zero'd rows. for (size_t r = table->size(); r < rows; r++) { FloatSeq *row = new FloatSeq; table->push_back(row); // Fill out row with zeros. for (size_t c = row->size(); c < cols; c++) - row->push_back(0.0); + row->push_back(0.0); } } return table; @@ -4827,19 +4821,19 @@ LibertyReader::beginLut(LibertyGroup *group) if (cell_) { for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { - const char *names = param->stringValue(); - // Parse space separated list of related port names. - TokenParser parser(names, " "); - while (parser.hasNext()) { - char *name = parser.next(); - if (name[0] != '\0') { - LibertyPort *port = makePort(cell_, name); - port->setDirection(PortDirection::internal()); - } - } + const char *names = param->stringValue(); + // Parse space separated list of related port names. + TokenParser parser(names, " "); + while (parser.hasNext()) { + char *name = parser.next(); + if (name[0] != '\0') { + LibertyPort *port = makePort(cell_, name); + port->setDirection(PortDirection::internal()); + } + } } else - libWarn(1261, group, "lut output is not a string."); + libWarn(1261, group, "lut output is not a string."); } } } @@ -5008,9 +5002,9 @@ LibertyReader::getAttrString(LibertyAttr *attr) void LibertyReader::getAttrInt(LibertyAttr *attr, - // Return values. - int &value, - bool &exists) + // Return values. + int &value, + bool &exists) { value = 0; exists = false; @@ -5030,9 +5024,9 @@ LibertyReader::getAttrInt(LibertyAttr *attr, void LibertyReader::getAttrFloat(LibertyAttr *attr, - // Return values. - float &value, - bool &valid) + // Return values. + float &value, + bool &valid) { valid = false; if (attr->isSimple()) @@ -5043,10 +5037,10 @@ LibertyReader::getAttrFloat(LibertyAttr *attr, void LibertyReader::getAttrFloat(LibertyAttr *attr, - LibertyAttrValue *attr_value, - // Return values. - float &value, - bool &valid) + LibertyAttrValue *attr_value, + // Return values. + float &value, + bool &valid) { if (attr_value->isFloat()) { valid = true; @@ -5064,9 +5058,9 @@ LibertyReader::getAttrFloat(LibertyAttr *attr, if ((*end && !isspace(*end)) // strtof support INF as a valid float. || stringEqual(string, "inf")) - libWarn(1271, attr, "%s value %s is not a float.", - attr->name(), - string); + libWarn(1271, attr, "%s value %s is not a float.", + attr->name(), + string); valid = true; } } @@ -5076,31 +5070,30 @@ LibertyReader::getAttrFloat(LibertyAttr *attr, // attr(float1, float2); void LibertyReader::getAttrFloat2(LibertyAttr *attr, - // Return values. - float &value1, - float &value2, - bool &exists) + // Return values. + float &value1, + float &value2, + bool &exists) { exists = false; if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); + LibertyAttrValueSeq *values = attr->values(); + if (values->size() == 2) { + LibertyAttrValue *value = (*values)[0]; getAttrFloat(attr, value, value1, exists); - if (exists) { - if (value_iter.hasNext()) { - value = value_iter.next(); - getAttrFloat(attr, value, value2, exists); - } - else - libWarn(1272, attr, "%s missing values.", attr->name()); - } + if (!exists) + libWarn(1272, attr, "%s 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()); } else - libWarn(1273, attr, "%s missing values.", attr->name()); + libWarn(1274, attr, "%s requires 2 valules.", attr->name()); } else - libWarn(1274, attr, "%s is not a complex attribute.", attr->name()); + libWarn(1345, attr, "%s requires 2 valules.", attr->name()); } // Parse string of comma separated floats. @@ -5108,9 +5101,9 @@ LibertyReader::getAttrFloat2(LibertyAttr *attr, // consistent about including the delimiters. void LibertyReader::parseStringFloatList(const char *float_list, - float scale, - FloatSeq *values, - LibertyAttr *attr) + float scale, + FloatSeq *values, + LibertyAttr *attr) { const char *delimiters = ", "; TokenParser parser(float_list, delimiters); @@ -5122,10 +5115,10 @@ LibertyReader::parseStringFloatList(const char *float_list, char *end; float value = strtof(token, &end) * scale; if (end == token - || (end && !(*end == '\0' - || isspace(*end) - || strchr(delimiters, *end) != nullptr - || *end == '}'))) + || (end && !(*end == '\0' + || isspace(*end) + || strchr(delimiters, *end) != nullptr + || *end == '}'))) libWarn(1275, attr, "%s is not a float.", token); values->push_back(value); } @@ -5133,25 +5126,25 @@ LibertyReader::parseStringFloatList(const char *float_list, FloatSeq * LibertyReader::readFloatSeq(LibertyAttr *attr, - float scale) + float scale) { FloatSeq *values = nullptr; if (attr->isComplex()) { - LibertyAttrValueIterator value_iter(attr->values()); - if (value_iter.hasNext()) { - LibertyAttrValue *value = value_iter.next(); + LibertyAttrValueSeq *attr_values = attr->values(); + if (attr_values->size() == 1) { + LibertyAttrValue *value = (*attr_values)[0]; if (value->isString()) { - values = new FloatSeq; - parseStringFloatList(value->stringValue(), scale, values, attr); + values = new FloatSeq; + parseStringFloatList(value->stringValue(), scale, values, attr); } else if (value->isFloat()) { - values = new FloatSeq; + values = new FloatSeq; values->push_back(value->floatValue()); } else - libWarn(1276, attr, "%s is missing values.", attr->name()); + libWarn(1276, attr, "%s is missing values.", attr->name()); } - if (value_iter.hasNext()) + else libWarn(1277, attr, "%s has more than one string.", attr->name()); } else { @@ -5168,9 +5161,9 @@ LibertyReader::readFloatSeq(LibertyAttr *attr, void LibertyReader::getAttrBool(LibertyAttr *attr, - // Return values. - bool &value, - bool &exists) + // Return values. + bool &value, + bool &exists) { exists = false; if (attr->isSimple()) { @@ -5178,15 +5171,15 @@ LibertyReader::getAttrBool(LibertyAttr *attr, if (val->isString()) { const char *str = val->stringValue(); if (stringEqual(str, "true")) { - value = true; - exists = true; + value = true; + exists = true; } else if (stringEqual(str, "false")) { - value = false; - exists = true; + value = false; + exists = true; } else - libWarn(1279, attr, "%s attribute is not boolean.", attr->name()); + libWarn(1279, attr, "%s attribute is not boolean.", attr->name()); } else libWarn(1280, attr, "%s attribute is not boolean.", attr->name()); @@ -5209,7 +5202,7 @@ LibertyReader::getAttrLogicValue(LibertyAttr *attr) return LogicValue::unknown; else libWarn(1282, attr, "attribute %s value %s not recognized.", - attr->name(), str); + attr->name(), str); // fall thru } return LogicValue::unknown; @@ -5217,12 +5210,12 @@ LibertyReader::getAttrLogicValue(LibertyAttr *attr) FuncExpr * LibertyReader::parseFunc(const char *func, - const char *attr_name, - int line) + const char *attr_name, + int line) { string error_msg; stringPrint(error_msg, "%s, line %d %s", - filename_, + filename_, line, attr_name); return parseFuncExpr(func, cell_, error_msg.c_str(), report_); @@ -5255,7 +5248,7 @@ LibertyReader::visitVariable(LibertyVariable *var) string key; float value; bool exists; - var_map_->findKey(var_name, key, value, exists); + findKeyValue(var_map_, var_name, value, exists); if (exists) { // Duplicate variable name. (*var_map_)[key] = var->value(); @@ -5266,11 +5259,11 @@ LibertyReader::visitVariable(LibertyVariable *var) void LibertyReader::variableValue(const char *var, - float &value, - bool &exists) + float &value, + bool &exists) { if (var_map_) - var_map_->findKey(var, value, exists); + findKeyValue(var_map_, var, value, exists); else exists = false; } @@ -5280,8 +5273,8 @@ LibertyReader::variableValue(const char *var, void LibertyReader::libWarn(int id, LibertyStmt *stmt, - const char *fmt, - ...) + const char *fmt, + ...) { va_list args; va_start(args, fmt); @@ -5292,8 +5285,8 @@ LibertyReader::libWarn(int id, void LibertyReader::libWarn(int id, int line, - const char *fmt, - ...) + const char *fmt, + ...) { va_list args; va_start(args, fmt); @@ -5304,8 +5297,8 @@ LibertyReader::libWarn(int id, void LibertyReader::libError(int id, LibertyStmt *stmt, - const char *fmt, - ...) + const char *fmt, + ...) { va_list args; va_start(args, fmt); @@ -5362,8 +5355,8 @@ LibertyReader::beginFallPower(LibertyGroup *group) { if (internal_power_) beginTableModel(group, TableTemplateType::power, - RiseFall::fall(), energy_scale_, - ScaleFactorType::internal_power); + RiseFall::fall(), energy_scale_, + ScaleFactorType::internal_power); } void @@ -5371,8 +5364,8 @@ LibertyReader::beginRisePower(LibertyGroup *group) { if (internal_power_) beginTableModel(group, TableTemplateType::power, - RiseFall::rise(), energy_scale_, - ScaleFactorType::internal_power); + RiseFall::rise(), energy_scale_, + ScaleFactorType::internal_power); } void @@ -5505,12 +5498,12 @@ LibertyReader::endOcvDerateFactors(LibertyGroup *) if (ocv_derate_) { for (auto early_late : derate_type_->range()) { for (auto rf : rf_type_->range()) { - if (path_type_ == PathType::clk_and_data) { - ocv_derate_->setDerateTable(rf, early_late, PathType::clk, table_); - ocv_derate_->setDerateTable(rf, early_late, PathType::data, table_); - } - else - ocv_derate_->setDerateTable(rf, early_late, path_type_, table_); + if (path_type_ == PathType::clk_and_data) { + ocv_derate_->setDerateTable(rf, early_late, PathType::clk, table_); + ocv_derate_->setDerateTable(rf, early_late, PathType::data, table_); + } + else + ocv_derate_->setDerateTable(rf, early_late, path_type_, table_); } } } @@ -5573,11 +5566,11 @@ LibertyReader::endOcvSigmaCell(LibertyGroup *group) TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); if (sigma_type_ == EarlyLateAll::all()) { - timing_->setDelaySigma(rf_, EarlyLate::min(), table_model); - timing_->setDelaySigma(rf_, EarlyLate::max(), table_model); + timing_->setDelaySigma(rf_, EarlyLate::min(), table_model); + timing_->setDelaySigma(rf_, EarlyLate::max(), table_model); } else - timing_->setDelaySigma(rf_, sigma_type_->asMinMax(), table_model); + timing_->setDelaySigma(rf_, sigma_type_->asMinMax(), table_model); } else libWarn(1288, group, "unsupported model axis."); @@ -5605,11 +5598,11 @@ LibertyReader::endOcvSigmaTransition(LibertyGroup *group) TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); if (sigma_type_ == EarlyLateAll::all()) { - timing_->setSlewSigma(rf_, EarlyLate::min(), table_model); - timing_->setSlewSigma(rf_, EarlyLate::max(), table_model); + timing_->setSlewSigma(rf_, EarlyLate::min(), table_model); + timing_->setSlewSigma(rf_, EarlyLate::max(), table_model); } else - timing_->setSlewSigma(rf_, sigma_type_->asMinMax(), table_model); + timing_->setSlewSigma(rf_, sigma_type_->asMinMax(), table_model); } else libWarn(1289, group, "unsupported model axis."); @@ -5637,11 +5630,11 @@ LibertyReader::endOcvSigmaConstraint(LibertyGroup *group) TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); if (sigma_type_ == EarlyLateAll::all()) { - timing_->setConstraintSigma(rf_, EarlyLate::min(), table_model); - timing_->setConstraintSigma(rf_, EarlyLate::max(), table_model); + timing_->setConstraintSigma(rf_, EarlyLate::min(), table_model); + timing_->setConstraintSigma(rf_, EarlyLate::max(), table_model); } else - timing_->setConstraintSigma(rf_, sigma_type_->asMinMax(), table_model); + timing_->setConstraintSigma(rf_, sigma_type_->asMinMax(), table_model); } else libWarn(1290, group, "unsupported model axis."); @@ -5750,9 +5743,9 @@ LibertyReader::endEcsmWaveform(LibertyGroup *) LibertyFunc::LibertyFunc(const char *expr, LibertySetFunc set_func, - bool invert, - const char *attr_name, - int line) : + bool invert, + const char *attr_name, + int line) : expr_(stringCopy(expr)), set_func_(set_func), invert_(invert), @@ -5770,7 +5763,7 @@ LibertyFunc::~LibertyFunc() //////////////////////////////////////////////////////////////// PortGroup::PortGroup(LibertyPortSeq *ports, - int line) : + int line) : ports_(ports), line_(line) { @@ -5778,7 +5771,7 @@ PortGroup::PortGroup(LibertyPortSeq *ports, PortGroup::~PortGroup() { - timings_.deleteContents(); + deleteContents(timings_); delete ports_; } @@ -5797,11 +5790,11 @@ PortGroup::addInternalPowerGroup(InternalPowerGroup *internal_power) //////////////////////////////////////////////////////////////// SequentialGroup::SequentialGroup(bool is_register, - bool is_bank, - LibertyPort *out_port, - LibertyPort *out_inv_port, - int size, - int line) : + bool is_bank, + LibertyPort *out_port, + LibertyPort *out_inv_port, + int size, + int line) : is_register_(is_register), is_bank_(is_bank), out_port_(out_port), @@ -5952,7 +5945,7 @@ TimingGroup::setRelatedOutputPortName(const char *name) void TimingGroup::setIntrinsic(const RiseFall *rf, - float value) + float value) { int rf_index = rf->index(); intrinsic_[rf_index] = value; @@ -5961,9 +5954,9 @@ TimingGroup::setIntrinsic(const RiseFall *rf, void TimingGroup::intrinsic(const RiseFall *rf, - // Return values. - float &value, - bool &exists) + // Return values. + float &value, + bool &exists) { int rf_index = rf->index(); value = intrinsic_[rf_index]; @@ -5972,7 +5965,7 @@ TimingGroup::intrinsic(const RiseFall *rf, void TimingGroup::setResistance(const RiseFall *rf, - float value) + float value) { int rf_index = rf->index(); resistance_[rf_index] = value; @@ -5981,9 +5974,9 @@ TimingGroup::setResistance(const RiseFall *rf, void TimingGroup::resistance(const RiseFall *rf, - // Return values. - float &value, - bool &exists) + // Return values. + float &value, + bool &exists) { int rf_index = rf->index(); value = resistance_[rf_index]; @@ -5998,7 +5991,7 @@ TimingGroup::cell(const RiseFall *rf) void TimingGroup::setCell(const RiseFall *rf, - TableModel *model) + TableModel *model) { cell_[rf->index()] = model; } @@ -6011,7 +6004,7 @@ TimingGroup::constraint(const RiseFall *rf) void TimingGroup::setConstraint(const RiseFall *rf, - TableModel *model) + TableModel *model) { constraint_[rf->index()] = model; } @@ -6024,31 +6017,31 @@ TimingGroup::transition(const RiseFall *rf) void TimingGroup::setTransition(const RiseFall *rf, - TableModel *model) + TableModel *model) { transition_[rf->index()] = model; } void TimingGroup::setDelaySigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model) + const EarlyLate *early_late, + TableModel *model) { delay_sigma_[rf->index()][early_late->index()] = model; } void TimingGroup::setSlewSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model) + const EarlyLate *early_late, + TableModel *model) { slew_sigma_[rf->index()][early_late->index()] = model; } void TimingGroup::setConstraintSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model) + const EarlyLate *early_late, + TableModel *model) { constraint_sigma_[rf->index()][early_late->index()] = model; } @@ -6099,9 +6092,9 @@ LeakagePowerGroup::~LeakagePowerGroup() //////////////////////////////////////////////////////////////// PortNameBitIterator::PortNameBitIterator(LibertyCell *cell, - const char *port_name, - LibertyReader *visitor, - int line) : + const char *port_name, + LibertyReader *visitor, + int line) : cell_(cell), visitor_(visitor), line_(line), @@ -6137,29 +6130,29 @@ PortNameBitIterator::init(const char *port_name) if (is_range) { port = visitor_->findPort(port_name); if (port) { - if (port->isBus()) { - if (port->busIndexInRange(from) - && port->busIndexInRange(to)) { - range_bus_port_ = port; - range_from_ = from; - range_to_ = to; - range_bit_ = from; - } - else - visitor_->libWarn(1292, line_, "port %s subscript out of range.", - port_name); - } - else - visitor_->libWarn(1293, line_, "port range %s of non-bus port %s.", - port_name, - bus_name.c_str()); + if (port->isBus()) { + if (port->busIndexInRange(from) + && port->busIndexInRange(to)) { + range_bus_port_ = port; + range_from_ = from; + range_to_ = to; + range_bit_ = from; + } + else + visitor_->libWarn(1292, line_, "port %s subscript out of range.", + port_name); + } + else + visitor_->libWarn(1293, line_, "port range %s of non-bus port %s.", + port_name, + bus_name.c_str()); } else { - range_bus_name_ = bus_name; - range_from_ = from; - range_to_ = to; - range_bit_ = from; - findRangeBusNameNext(); + range_bus_name_ = bus_name; + range_from_ = from; + range_to_ = to; + range_bit_ = from; + findRangeBusNameNext(); } size_ = abs(from - to) + 1; } @@ -6179,11 +6172,11 @@ PortNameBitIterator::hasNext() return port_ || (bit_iterator_ && bit_iterator_->hasNext()) || (range_bus_port_ - && ((range_from_ > range_to_) - ? range_bit_ >= range_to_ - : range_bit_ <= range_from_)) + && ((range_from_ > range_to_) + ? range_bit_ >= range_to_ + : range_bit_ <= range_from_)) || (!range_bus_name_.empty() - && range_name_next_); + && range_name_next_); } LibertyPort * @@ -6229,9 +6222,9 @@ PortNameBitIterator::findRangeBusNameNext() range_name_next_ = visitor_->findPort(bus_bit_name.c_str()); if (range_name_next_) { if (range_from_ > range_to_) - range_bit_--; + range_bit_--; else - range_bit_++; + range_bit_++; } else visitor_->libWarn(1295, line_, "port %s not found.", bus_bit_name.c_str()); diff --git a/liberty/LibertyReader.hh b/liberty/LibertyReader.hh index bc1a47a78..648e8f1fa 100644 --- a/liberty/LibertyReader.hh +++ b/liberty/LibertyReader.hh @@ -31,7 +31,7 @@ class LibertyLibrary; LibertyLibrary * readLibertyFile(const char *filename, - bool infer_latches, - Network *network); + bool infer_latches, + Network *network); } // namespace diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index c31888f7b..a87a20397 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -26,9 +26,8 @@ #include #include +#include -#include "Vector.hh" -#include "Map.hh" #include "StringSeq.hh" #include "MinMax.hh" #include "NetworkClass.hh" @@ -60,20 +59,20 @@ class TimingArcBuilder; class LibertyAttr; class OutputWaveform; -typedef void (LibertyReader::*LibraryAttrVisitor)(LibertyAttr *attr); -typedef void (LibertyReader::*LibraryGroupVisitor)(LibertyGroup *group); -typedef Map LibraryAttrMap; -typedef Map LibraryGroupMap; -typedef Vector PortGroupSeq; -typedef Vector SequentialGroupSeq; -typedef Vector LibertyFuncSeq; -typedef Vector TimingGroupSeq; -typedef Vector InternalPowerGroupSeq; -typedef Vector LeakagePowerGroupSeq; -typedef void (LibertyPort::*LibertyPortBoolSetter)(bool value); -typedef Vector OutputWaveformSeq; -typedef std::vector StdStringSeq; -typedef std::function LibertySetFunc; +using LibraryAttrVisitor = void (LibertyReader::*)(LibertyAttr *attr); +using LibraryGroupVisitor = void (LibertyReader::*)(LibertyGroup *group); +using LibraryAttrMap = std::map; +using LibraryGroupMap = std::map; +using PortGroupSeq = std::vector; +using SequentialGroupSeq = std::vector; +using LibertyFuncSeq = std::vector; +using TimingGroupSeq = std::vector; +using InternalPowerGroupSeq = std::vector; +using LeakagePowerGroupSeq = std::vector; +using LibertyPortBoolSetter = void (LibertyPort::*)(bool value); +using OutputWaveformSeq = std::vector; +using StdStringSeq = std::vector; +using LibertySetFunc = std::function; class LibertyReader : public LibertyGroupVisitor { @@ -104,9 +103,9 @@ public: virtual void visitPowerUnit(LibertyAttr *attr); virtual void visitDistanceUnit(LibertyAttr *attr); virtual void parseUnits(LibertyAttr *attr, - const char *suffix, - float &scale_var, - Unit *unit_suffix); + const char *suffix, + float &scale_var, + Unit *unit_suffix); virtual void visitDelayModel(LibertyAttr *attr); virtual void visitVoltageMap(LibertyAttr *attr); virtual void visitBusStyle(LibertyAttr *attr); @@ -121,15 +120,15 @@ public: virtual void visitDefaultIntrinsicRise(LibertyAttr *attr); virtual void visitDefaultIntrinsicFall(LibertyAttr *attr); virtual void visitDefaultIntrinsic(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitDefaultInoutPinRiseRes(LibertyAttr *attr); virtual void visitDefaultInoutPinFallRes(LibertyAttr *attr); virtual void visitDefaultInoutPinRes(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitDefaultOutputPinRiseRes(LibertyAttr *attr); virtual void visitDefaultOutputPinFallRes(LibertyAttr *attr); virtual void visitDefaultOutputPinRes(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitDefaultFanoutLoad(LibertyAttr *attr); virtual void visitDefaultWireLoad(LibertyAttr *attr); virtual void visitDefaultWireLoadMode(LibertyAttr *attr); @@ -138,19 +137,19 @@ public: virtual void visitInputThresholdPctFall(LibertyAttr *attr); virtual void visitInputThresholdPctRise(LibertyAttr *attr); virtual void visitInputThresholdPct(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitOutputThresholdPctFall(LibertyAttr *attr); virtual void visitOutputThresholdPctRise(LibertyAttr *attr); virtual void visitOutputThresholdPct(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitSlewLowerThresholdPctFall(LibertyAttr *attr); virtual void visitSlewLowerThresholdPctRise(LibertyAttr *attr); virtual void visitSlewLowerThresholdPct(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitSlewUpperThresholdPctFall(LibertyAttr *attr); virtual void visitSlewUpperThresholdPctRise(LibertyAttr *attr); virtual void visitSlewUpperThresholdPct(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitSlewDerateFromLibrary(LibertyAttr *attr); virtual void beginTechnology(LibertyGroup *group); @@ -158,7 +157,7 @@ public: virtual void beginTableTemplateDelay(LibertyGroup *group); virtual void beginTableTemplateOutputCurrent(LibertyGroup *group); virtual void beginTableTemplate(LibertyGroup *group, - TableTemplateType type); + TableTemplateType type); virtual void endTableTemplate(LibertyGroup *group); virtual void visitVariable1(LibertyAttr *attr); virtual void visitVariable2(LibertyAttr *attr); @@ -179,7 +178,7 @@ public: virtual void checkScaledCell(LibertyGroup *group); virtual void finishPortGroups(); virtual void checkPort(LibertyPort *port, - int line); + int line); virtual void makeTimingArcs(PortGroup *port_group); virtual void makeInternalPowers(PortGroup *port_group); virtual void makeCellSequentials(); @@ -189,19 +188,19 @@ public: virtual void parseCellFuncs(); virtual void makeLibertyFunc(const char *expr, LibertySetFunc set_func, - bool invert, - const char *attr_name, - LibertyStmt *stmt); + bool invert, + const char *attr_name, + LibertyStmt *stmt); virtual void makeTimingArcs(LibertyPort *to_port, - TimingGroup *timing); + TimingGroup *timing); virtual void makeTimingArcs(const char *from_port_name, - PortNameBitIterator &from_port_iter, - LibertyPort *to_port, - LibertyPort *related_out_port, - TimingGroup *timing); + PortNameBitIterator &from_port_iter, + LibertyPort *to_port, + LibertyPort *related_out_port, + TimingGroup *timing); virtual void makeTimingArcs(LibertyPort *to_port, LibertyPort *related_out_port, - TimingGroup *timing); + TimingGroup *timing); virtual void visitClockGatingIntegratedCell(LibertyAttr *attr); virtual void visitArea(LibertyAttr *attr); @@ -246,20 +245,20 @@ public: virtual void visitMaxFanout(LibertyAttr *attr); virtual void visitMinFanout(LibertyAttr *attr); virtual void visitFanout(LibertyAttr *attr, - const MinMax *min_max); + const MinMax *min_max); virtual void visitMaxTransition(LibertyAttr *attr); virtual void visitMinTransition(LibertyAttr *attr); virtual void visitMinMaxTransition(LibertyAttr *attr, - const MinMax *min_max); + const MinMax *min_max); virtual void visitMaxCapacitance(LibertyAttr *attr); virtual void visitMinCapacitance(LibertyAttr *attr); virtual void visitMinMaxCapacitance(LibertyAttr *attr, - const MinMax *min_max); + const MinMax *min_max); virtual void visitMinPeriod(LibertyAttr *attr); virtual void visitMinPulseWidthLow(LibertyAttr *attr); virtual void visitMinPulseWidthHigh(LibertyAttr *attr); virtual void visitMinPulseWidth(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitPulseClock(LibertyAttr *attr); virtual void visitClockGateClockPin(LibertyAttr *attr); virtual void visitClockGateEnablePin(LibertyAttr *attr); @@ -312,15 +311,15 @@ public: virtual void beginLatchBank(LibertyGroup *group); virtual void endLatchBank(LibertyGroup *group); virtual void beginSequential(LibertyGroup *group, - bool is_register, - bool is_bank); + bool is_register, + bool is_bank); virtual void seqPortNames(LibertyGroup *group, - const char *&out_name, - const char *&out_inv_name, - bool &has_size, - int &size); + const char *&out_name, + const char *&out_inv_name, + bool &has_size, + int &size); virtual void checkLatchEnableSense(FuncExpr *enable_func, - int line); + int line); virtual void visitClockedOn(LibertyAttr *attr); virtual void visitDataIn(LibertyAttr *attr); virtual void visitClear(LibertyAttr *attr); @@ -336,10 +335,10 @@ public: virtual void endTiming(LibertyGroup *group); virtual void visitRelatedPin(LibertyAttr *attr); virtual void visitRelatedPin(LibertyAttr *attr, - RelatedPortGroup *group); + RelatedPortGroup *group); virtual void visitRelatedBusPins(LibertyAttr *attr); virtual void visitRelatedBusPins(LibertyAttr *attr, - RelatedPortGroup *group); + RelatedPortGroup *group); virtual void visitRelatedOutputPin(LibertyAttr *attr); virtual void visitTimingType(LibertyAttr *attr); virtual void visitTimingSense(LibertyAttr *attr); @@ -349,11 +348,11 @@ public: virtual void visitIntrinsicRise(LibertyAttr *attr); virtual void visitIntrinsicFall(LibertyAttr *attr); virtual void visitIntrinsic(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitRiseResistance(LibertyAttr *attr); virtual void visitFallResistance(LibertyAttr *attr); virtual void visitRiseFallResistance(LibertyAttr *attr, - const RiseFall *rf); + const RiseFall *rf); virtual void visitValue(LibertyAttr *attr); virtual void visitValues(LibertyAttr *attr); virtual void beginCellRise(LibertyGroup *group); @@ -371,24 +370,24 @@ public: virtual void endRiseFallTransitionDegredation(LibertyGroup *group); virtual void beginTableModel(LibertyGroup *group, - TableTemplateType type, - const RiseFall *rf, - float scale, - ScaleFactorType scale_factor_type); + TableTemplateType type, + const RiseFall *rf, + float scale, + ScaleFactorType scale_factor_type); virtual void endTableModel(); virtual void beginTimingTableModel(LibertyGroup *group, - const RiseFall *rf, - ScaleFactorType scale_factor_type); + const RiseFall *rf, + ScaleFactorType scale_factor_type); virtual void beginTable(LibertyGroup *group, - TableTemplateType type, - float scale); + TableTemplateType type, + float scale); virtual void endTable(); virtual void makeTable(LibertyAttr *attr, - float scale); + float scale); virtual FloatTable *makeFloatTable(LibertyAttr *attr, - size_t rows, - size_t cols, - float scale); + size_t rows, + size_t cols, + float scale); virtual void beginLut(LibertyGroup *group); virtual void endLut(LibertyGroup *group); @@ -418,11 +417,11 @@ public: virtual void visitRelatedPowerPin(LibertyAttr *attr); virtual void visitRelatedPgPin(LibertyAttr *attr); virtual void makeInternalPowers(LibertyPort *port, - InternalPowerGroup *power_group); + InternalPowerGroup *power_group); virtual void makeInternalPowers(LibertyPort *port, - const char *related_port_name, - PortNameBitIterator &related_port_iter, - InternalPowerGroup *power_group); + const char *related_port_name, + PortNameBitIterator &related_port_iter, + InternalPowerGroup *power_group); // AOCV attributes. virtual void beginTableTemplateOcv(LibertyGroup *group); @@ -496,7 +495,7 @@ public: void beginEcsmWaveform(LibertyGroup *group); void endEcsmWaveform(LibertyGroup *group); LibertyPort *findPort(LibertyCell *cell, - const char *port_name); + const char *port_name); protected: LibertyPort *makePort(LibertyCell *cell, @@ -517,10 +516,10 @@ protected: virtual void begin(LibertyGroup *group); virtual void end(LibertyGroup *group); void defineGroupVisitor(const char *type, - LibraryGroupVisitor begin_visitor, - LibraryGroupVisitor end_visitor); + LibraryGroupVisitor begin_visitor, + LibraryGroupVisitor end_visitor); void defineAttrVisitor(const char *attr_name, - LibraryAttrVisitor visitor); + LibraryAttrVisitor visitor); void parseNames(const char *name_str); void clearAxisValues(); void makeTableAxis(int index, @@ -540,59 +539,59 @@ protected: const char *getAttrString(LibertyAttr *attr); void getAttrInt(LibertyAttr *attr, - // Return values. - int &value, - bool &exists); + // Return values. + int &value, + bool &exists); void getAttrFloat(LibertyAttr *attr, - // Return values. - float &value, - bool &valid); + // Return values. + float &value, + bool &valid); void getAttrFloat(LibertyAttr *attr, - LibertyAttrValue *attr_value, - // Return values. - float &value, - bool &valid); + LibertyAttrValue *attr_value, + // Return values. + float &value, + bool &valid); void getAttrFloat2(LibertyAttr *attr, - // Return values. - float &value1, - float &value2, - bool &exists); + // Return values. + float &value1, + float &value2, + bool &exists); void parseStringFloatList(const char *float_list, - float scale, - FloatSeq *values, - LibertyAttr *attr); + float scale, + FloatSeq *values, + LibertyAttr *attr); LogicValue getAttrLogicValue(LibertyAttr *attr); void getAttrBool(LibertyAttr *attr, - // Return values. - bool &value, - bool &exists); + // Return values. + bool &value, + bool &exists); void visitVariable(int index, - LibertyAttr *attr); + LibertyAttr *attr); void visitIndex(int index, - LibertyAttr *attr); + LibertyAttr *attr); TableAxisPtr makeAxis(int index, LibertyGroup *group); FloatSeq *readFloatSeq(LibertyAttr *attr, - float scale); + float scale); void variableValue(const char *var, - float &value, - bool &exists); + float &value, + bool &exists); FuncExpr *parseFunc(const char *func, - const char *attr_name, - int line); + const char *attr_name, + int line); void libWarn(int id, LibertyStmt *stmt, - const char *fmt, - ...) + const char *fmt, + ...) __attribute__((format (printf, 4, 5))); void libWarn(int id, int line, - const char *fmt, - ...) + const char *fmt, + ...) __attribute__((format (printf, 4, 5))); void libError(int id, LibertyStmt *stmt, - const char *fmt, ...) + const char *fmt, ...) __attribute__((format (printf, 4, 5))); const char *filename_; @@ -697,9 +696,9 @@ class LibertyFunc public: LibertyFunc(const char *expr, LibertySetFunc set_func, - bool invert, - const char *attr_name, - int line); + bool invert, + const char *attr_name, + int line); ~LibertyFunc(); const char *expr() const { return expr_; } LibertySetFunc setFunc() const { return set_func_; } @@ -722,7 +721,7 @@ class PortGroup { public: PortGroup(LibertyPortSeq *ports, - int line); + int line); ~PortGroup(); LibertyPortSeq *ports() const { return ports_; } TimingGroupSeq &timingGroups() { return timings_; } @@ -745,7 +744,7 @@ private: class RelatedPortGroup { public: - explicit RelatedPortGroup(int line); + RelatedPortGroup(int line); virtual ~RelatedPortGroup(); int line() const { return line_; } StringSeq *relatedPortNames() const { return related_port_names_; } @@ -763,11 +762,11 @@ class SequentialGroup { public: SequentialGroup(bool is_register, - bool is_bank, - LibertyPort *out_port, - LibertyPort *out_inv_port, - int size, - int line); + bool is_bank, + LibertyPort *out_port, + LibertyPort *out_inv_port, + int size, + int line); ~SequentialGroup(); LibertyPort *outPort() const { return out_port_; } LibertyPort *outInvPort() const { return out_inv_port_; } @@ -827,43 +826,43 @@ private: class TimingGroup : public RelatedPortGroup { public: - explicit TimingGroup(int line); + TimingGroup(int line); virtual ~TimingGroup(); TimingArcAttrsPtr attrs() { return attrs_; } const char *relatedOutputPortName()const {return related_output_port_name_;} void setRelatedOutputPortName(const char *name); void intrinsic(const RiseFall *rf, - // Return values. - float &value, - bool &exists); + // Return values. + float &value, + bool &exists); void setIntrinsic(const RiseFall *rf, - float value); + float value); void resistance(const RiseFall *rf, - // Return values. - float &value, - bool &exists); + // Return values. + float &value, + bool &exists); void setResistance(const RiseFall *rf, - float value); + float value); TableModel *cell(const RiseFall *rf); void setCell(const RiseFall *rf, - TableModel *model); + TableModel *model); TableModel *constraint(const RiseFall *rf); void setConstraint(const RiseFall *rf, - TableModel *model); + TableModel *model); TableModel *transition(const RiseFall *rf); void setTransition(const RiseFall *rf, - TableModel *model); + TableModel *model); void makeTimingModels(LibertyCell *cell, - LibertyReader *visitor); + LibertyReader *visitor); void setDelaySigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model); + const EarlyLate *early_late, + TableModel *model); void setSlewSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model); + const EarlyLate *early_late, + TableModel *model); void setConstraintSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model); + const EarlyLate *early_late, + TableModel *model); void setReceiverModel(ReceiverModelPtr receiver_model); OutputWaveforms *outputWaveforms(const RiseFall *rf); void setOutputWaveforms(const RiseFall *rf, @@ -893,14 +892,14 @@ protected: class InternalPowerGroup : public InternalPowerAttrs, public RelatedPortGroup { public: - explicit InternalPowerGroup(int line); + InternalPowerGroup(int line); virtual ~InternalPowerGroup(); }; class LeakagePowerGroup : public LeakagePowerAttrs { public: - explicit LeakagePowerGroup(int line); + LeakagePowerGroup(int line); virtual ~LeakagePowerGroup(); protected: @@ -915,9 +914,9 @@ class PortNameBitIterator : public Iterator { public: PortNameBitIterator(LibertyCell *cell, - const char *port_name, - LibertyReader *visitor, - int line); + const char *port_name, + LibertyReader *visitor, + int line); ~PortNameBitIterator(); virtual bool hasNext(); virtual LibertyPort *next(); diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 2c4dd1a79..4afa66f00 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -312,7 +312,7 @@ LibertyWriter::writeCell(const LibertyCell *cell) const LibertyPort *port = port_iter.next(); if (!port->direction()->isInternal()) { if (port->isPwrGnd()) - writePwrGndPort(port); + writePwrGndPort(port); else if (port->isBus()) writeBusPort(port); else if (port->isBundle()) @@ -364,13 +364,14 @@ LibertyWriter::writePortAttrs(const LibertyPort *port) fprintf(stream_, " function : \"%s\";\n", func->to_string().c_str()); auto tristate_enable = port->tristateEnable(); if (tristate_enable) { - if (tristate_enable->op() == FuncExpr::op_not) { + 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()); } else { - FuncExpr three_state(FuncExpr::op_not, tristate_enable, nullptr, nullptr); + FuncExpr three_state(FuncExpr::Op::not_, tristate_enable, + nullptr, nullptr); fprintf(stream_, " three_state : \"%s\";\n", three_state.to_string().c_str()); } @@ -460,24 +461,24 @@ LibertyWriter::writeTimingModels(const TimingArc *arc, if (gate_model) { const TableModel *delay_model = gate_model->delayModel(); const char *template_name = delay_model->tblTemplate()->name(); - fprintf(stream_, " cell_%s(%s) {\n", rf->name(), template_name); + fprintf(stream_, " cell_%s(%s) {\n", rf->name(), template_name); writeTableModel(delay_model); - fprintf(stream_, " }\n"); + fprintf(stream_, " }\n"); const TableModel *slew_model = gate_model->slewModel(); if (slew_model) { template_name = slew_model->tblTemplate()->name(); - fprintf(stream_, " %s_transition(%s) {\n", rf->name(), template_name); + fprintf(stream_, " %s_transition(%s) {\n", rf->name(), template_name); writeTableModel(slew_model); - fprintf(stream_, " }\n"); + fprintf(stream_, " }\n"); } } else if (check_model) { const TableModel *model = check_model->model(); const char *template_name = model->tblTemplate()->name(); - fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name); + fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name); writeTableModel(model); - fprintf(stream_, " }\n"); + fprintf(stream_, " }\n"); } else report_->error(1341, "%s/%s/%s timing model not supported.", diff --git a/liberty/LinearModel.cc b/liberty/LinearModel.cc index fbb47d9eb..1001e74b0 100644 --- a/liberty/LinearModel.cc +++ b/liberty/LinearModel.cc @@ -33,7 +33,7 @@ using std::string; GateLinearModel::GateLinearModel(LibertyCell *cell, float intrinsic, - float resistance) : + float resistance) : GateTimingModel(cell), intrinsic_(intrinsic), resistance_(resistance) @@ -42,12 +42,12 @@ GateLinearModel::GateLinearModel(LibertyCell *cell, void GateLinearModel::gateDelay(const Pvt *, - float, - float load_cap, - bool, - // return values - ArcDelay &gate_delay, - Slew &drvr_slew) const + float, + float load_cap, + bool, + // return values + ArcDelay &gate_delay, + Slew &drvr_slew) const { gate_delay = intrinsic_ + resistance_ * load_cap; drvr_slew = 0.0; @@ -55,10 +55,10 @@ GateLinearModel::gateDelay(const Pvt *, string GateLinearModel::reportGateDelay(const Pvt *, - float, - float load_cap, - bool, - int digits) const + float, + float load_cap, + bool, + int digits) const { const LibertyLibrary *library = cell_->libertyLibrary(); const Units *units = library->units(); @@ -97,22 +97,22 @@ CheckLinearModel::CheckLinearModel(LibertyCell *cell, ArcDelay CheckLinearModel::checkDelay(const Pvt *, - float, - float, - float, - bool) const + float, + float, + float, + bool) const { return intrinsic_; } string CheckLinearModel::reportCheckDelay(const Pvt *, - float, - const char *, - float, - float, - bool, - int digits) const + float, + const char *, + float, + float, + bool, + int digits) const { const LibertyLibrary *library = cell_->libertyLibrary(); const Units *units = library->units(); diff --git a/liberty/Sequential.cc b/liberty/Sequential.cc index 559c61587..a89ae99b1 100644 --- a/liberty/Sequential.cc +++ b/liberty/Sequential.cc @@ -29,14 +29,14 @@ namespace sta { Sequential::Sequential(bool is_register, - FuncExpr *clock, - FuncExpr *data, - FuncExpr *clear, - FuncExpr *preset, - LogicValue clr_preset_out, - LogicValue clr_preset_out_inv, - LibertyPort *output, - LibertyPort *output_inv) : + FuncExpr *clock, + FuncExpr *data, + FuncExpr *clear, + FuncExpr *preset, + LogicValue clr_preset_out, + LogicValue clr_preset_out_inv, + LibertyPort *output, + LibertyPort *output_inv) : is_register_(is_register), clock_(clock), data_(data), diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index b4a0b3258..287416302 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -29,6 +29,7 @@ #include "Error.hh" #include "EnumNameMap.hh" +#include "ContainerHelpers.hh" #include "Units.hh" #include "Liberty.hh" @@ -48,10 +49,10 @@ deleteSigmaModels(TableModel *models[EarlyLate::index_count]); static string reportPvt(const LibertyCell *cell, const Pvt *pvt, - int digits); + int digits); static void appendSpaces(string &result, - int count); + int count); TimingModel::TimingModel(LibertyCell *cell) : cell_(cell) @@ -60,9 +61,9 @@ TimingModel::TimingModel(LibertyCell *cell) : GateTableModel::GateTableModel(LibertyCell *cell, TableModel *delay_model, - TableModel *delay_sigma_models[EarlyLate::index_count], - TableModel *slew_model, - TableModel *slew_sigma_models[EarlyLate::index_count], + TableModel *delay_sigma_models[EarlyLate::index_count], + TableModel *slew_model, + TableModel *slew_sigma_models[EarlyLate::index_count], ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms) : GateTimingModel(cell), @@ -114,31 +115,31 @@ GateTableModel::setIsScaled(bool 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 in_slew, + float load_cap, + bool pocv_enabled, + // return values + ArcDelay &gate_delay, + Slew &drvr_slew) const { float delay = findValue(pvt, delay_model_, 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); + 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); + in_slew, load_cap, 0.0); gate_delay = makeDelay(delay, sigma_early, sigma_late); float slew = findValue(pvt, slew_model_, 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); + 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); + in_slew, load_cap, 0.0); // Clip negative slews to zero. if (slew < 0.0) slew = 0.0; @@ -159,10 +160,10 @@ GateTableModel::gateDelay(const Pvt *pvt, string GateTableModel::reportGateDelay(const Pvt *pvt, - float in_slew, - float load_cap, - bool pocv_enabled, - int digits) const + float in_slew, + float load_cap, + bool pocv_enabled, + int digits) const { string result = reportPvt(cell_, pvt, digits); result += reportTableLookup("Delay", pvt, delay_model_, in_slew, @@ -180,11 +181,11 @@ GateTableModel::reportGateDelay(const Pvt *pvt, 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); + 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()], + slew_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, 0.0, digits); float drvr_slew = findValue(pvt, slew_model_, in_slew, load_cap, 0.0); if (drvr_slew < 0.0) @@ -194,17 +195,17 @@ GateTableModel::reportGateDelay(const Pvt *pvt, string GateTableModel::reportTableLookup(const char *result_name, - const Pvt *pvt, - const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap, - int digits) const + const Pvt *pvt, + const TableModel *model, + float in_slew, + float load_cap, + float related_out_cap, + int digits) const { if (model) { float axis_value1, axis_value2, axis_value3; findAxisValues(model, in_slew, load_cap, related_out_cap, - axis_value1, axis_value2, axis_value3); + 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, @@ -215,15 +216,15 @@ GateTableModel::reportTableLookup(const char *result_name, float GateTableModel::findValue(const Pvt *pvt, - const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap) const + const TableModel *model, + float in_slew, + float load_cap, + float related_out_cap) const { if (model) { float axis_value1, axis_value2, axis_value3; findAxisValues(model, in_slew, load_cap, related_out_cap, - axis_value1, axis_value2, axis_value3); + axis_value1, axis_value2, axis_value3); return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3); } else @@ -232,13 +233,13 @@ GateTableModel::findValue(const Pvt *pvt, void GateTableModel::findAxisValues(const TableModel *model, - float in_slew, - float load_cap, - float related_out_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const + float in_slew, + float load_cap, + float related_out_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const { switch (model->order()) { case 0: @@ -248,24 +249,24 @@ GateTableModel::findAxisValues(const TableModel *model, break; case 1: axis_value1 = axisValue(model->axis1(), in_slew, load_cap, - related_out_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); + related_out_cap); axis_value2 = axisValue(model->axis2(), in_slew, load_cap, - related_out_cap); + related_out_cap); axis_value3 = 0.0; break; case 3: axis_value1 = axisValue(model->axis1(), in_slew, load_cap, - related_out_cap); + related_out_cap); axis_value2 = axisValue(model->axis2(), in_slew, load_cap, - related_out_cap); + related_out_cap); axis_value3 = axisValue(model->axis3(), in_slew, load_cap, - related_out_cap); + related_out_cap); break; default: axis_value1 = 0.0; @@ -287,9 +288,9 @@ GateTableModel::driveResistance(const Pvt *pvt) const void GateTableModel::maxCapSlew(float in_slew, - const Pvt *pvt, - float &slew, - float &cap) const + const Pvt *pvt, + float &slew, + float &cap) const { const TableAxis *axis1 = slew_model_->axis1(); const TableAxis *axis2 = slew_model_->axis2(); @@ -300,12 +301,12 @@ GateTableModel::maxCapSlew(float in_slew, slew = findValue(pvt, slew_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, slew_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, slew_model_, in_slew, cap, 0.0); } @@ -321,9 +322,9 @@ GateTableModel::maxCapSlew(float in_slew, float GateTableModel::axisValue(const TableAxis *axis, - float in_slew, - float load_cap, - float related_out_cap) const + float in_slew, + float load_cap, + float related_out_cap) const { TableAxisVariable var = axis->variable(); if (var == TableAxisVariable::input_transition_time @@ -405,7 +406,7 @@ ReceiverModel::checkAxes(TablePtr table) CheckTableModel::CheckTableModel(LibertyCell *cell, TableModel *model, - TableModel *sigma_models[EarlyLate::index_count]) : + TableModel *sigma_models[EarlyLate::index_count]) : CheckTimingModel(cell), model_(model) { @@ -427,10 +428,10 @@ CheckTableModel::setIsScaled(bool is_scaled) ArcDelay CheckTableModel::checkDelay(const Pvt *pvt, - float from_slew, - float to_slew, - float related_out_cap, - bool pocv_enabled) const + float from_slew, + float to_slew, + float related_out_cap, + bool pocv_enabled) const { if (model_) { float mean = findValue(pvt, model_, from_slew, to_slew, related_out_cap); @@ -438,10 +439,10 @@ CheckTableModel::checkDelay(const Pvt *pvt, 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); + 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); + from_slew, to_slew, related_out_cap); return makeDelay(mean, sigma_early, sigma_late); } else @@ -450,15 +451,15 @@ CheckTableModel::checkDelay(const Pvt *pvt, float CheckTableModel::findValue(const Pvt *pvt, - const TableModel *model, - float from_slew, - float to_slew, - float related_out_cap) const + const TableModel *model, + float from_slew, + float to_slew, + float related_out_cap) const { if (model) { float axis_value1, axis_value2, axis_value3; findAxisValues(from_slew, to_slew, related_out_cap, - axis_value1, axis_value2, axis_value3); + axis_value1, axis_value2, axis_value3); return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3); } else @@ -467,12 +468,12 @@ CheckTableModel::findValue(const Pvt *pvt, string CheckTableModel::reportCheckDelay(const Pvt *pvt, - float from_slew, - const char *from_slew_annotation, - float to_slew, - float related_out_cap, - bool pocv_enabled, - int digits) const + float from_slew, + const char *from_slew_annotation, + float to_slew, + float related_out_cap, + bool pocv_enabled, + int digits) const { string result = reportTableDelay("Check", pvt, model_, from_slew, from_slew_annotation, to_slew, @@ -492,18 +493,18 @@ CheckTableModel::reportCheckDelay(const Pvt *pvt, string CheckTableModel::reportTableDelay(const char *result_name, - const Pvt *pvt, - const TableModel *model, - float from_slew, - const char *from_slew_annotation, - float to_slew, - float related_out_cap, - int digits) const + const Pvt *pvt, + const TableModel *model, + float from_slew, + const char *from_slew_annotation, + float to_slew, + float related_out_cap, + int digits) const { if (model) { float axis_value1, axis_value2, axis_value3; findAxisValues(from_slew, to_slew, related_out_cap, - axis_value1, axis_value2, axis_value3); + axis_value1, axis_value2, axis_value3); string result = reportPvt(cell_, pvt, digits); result += model_->reportValue(result_name, cell_, pvt, axis_value1, from_slew_annotation, axis_value2, @@ -516,12 +517,12 @@ CheckTableModel::reportTableDelay(const char *result_name, void CheckTableModel::findAxisValues(float from_slew, - float to_slew, - float related_out_cap, - // Return values. - float &axis_value1, - float &axis_value2, - float &axis_value3) const + float to_slew, + float related_out_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const { switch (model_->order()) { case 0: @@ -531,24 +532,24 @@ CheckTableModel::findAxisValues(float from_slew, break; case 1: axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, - related_out_cap); + related_out_cap); axis_value2 = 0.0; axis_value3 = 0.0; break; case 2: axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, - related_out_cap); + related_out_cap); axis_value2 = axisValue(model_->axis2(), from_slew, to_slew, - related_out_cap); + related_out_cap); axis_value3 = 0.0; break; case 3: axis_value1 = axisValue(model_->axis1(), from_slew, to_slew, - related_out_cap); + related_out_cap); axis_value2 = axisValue(model_->axis2(), from_slew, to_slew, - related_out_cap); + related_out_cap); axis_value3 = axisValue(model_->axis3(), from_slew, to_slew, - related_out_cap); + related_out_cap); break; default: criticalError(241, "unsupported table order"); @@ -557,9 +558,9 @@ CheckTableModel::findAxisValues(float from_slew, float CheckTableModel::axisValue(const TableAxis *axis, - float from_slew, - float to_slew, - float related_out_cap) const + float from_slew, + float to_slew, + float related_out_cap) const { TableAxisVariable var = axis->variable(); if (var == TableAxisVariable::related_pin_transition) @@ -603,8 +604,8 @@ CheckTableModel::checkAxis(const TableAxis *axis) TableModel::TableModel(TablePtr table, TableTemplate *tbl_template, - ScaleFactorType scale_factor_type, - const RiseFall *rf) : + ScaleFactorType scale_factor_type, + const RiseFall *rf) : table_(table), tbl_template_(tbl_template), scale_factor_type_(int(scale_factor_type)), @@ -659,18 +660,18 @@ TableModel::value(size_t axis_index1, float TableModel::findValue(float axis_value1, - float axis_value2, - float axis_value3) const + float axis_value2, + float axis_value3) const { return table_->findValue(axis_value1, axis_value2, axis_value3); } float TableModel::findValue(const LibertyCell *cell, - const Pvt *pvt, - float axis_value1, - float axis_value2, - float axis_value3) const + const Pvt *pvt, + float axis_value1, + float axis_value2, + float axis_value3) const { return table_->findValue(axis_value1, axis_value2, axis_value3) * scaleFactor(cell, pvt); @@ -678,7 +679,7 @@ TableModel::findValue(const LibertyCell *cell, float TableModel::scaleFactor(const LibertyCell *cell, - const Pvt *pvt) const + const Pvt *pvt) const { if (is_scaled_) // Scaled tables are not derated because scale factors are wrt @@ -691,14 +692,14 @@ TableModel::scaleFactor(const LibertyCell *cell, string TableModel::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + const char *comment1, + float value2, + float value3, const Unit *table_unit, - int digits) const + int digits) const { string result = table_->reportValue("Table value", cell, pvt, value1, comment1, value2, value3, table_unit, digits); @@ -714,8 +715,8 @@ TableModel::reportValue(const char *result_name, static string reportPvt(const LibertyCell *cell, - const Pvt *pvt, - int digits) + const Pvt *pvt, + int digits) { const LibertyLibrary *library = cell->libertyLibrary(); if (pvt == nullptr) @@ -723,9 +724,9 @@ reportPvt(const LibertyCell *cell, if (pvt) { string result; stringPrint(result, "P = %.*f V = %.*f T = %.*f\n", - digits, pvt->process(), - digits, pvt->voltage(), - digits, pvt->temperature()); + digits, pvt->process(), + digits, pvt->voltage(), + digits, pvt->temperature()); return result; } return ""; @@ -733,16 +734,16 @@ reportPvt(const LibertyCell *cell, string TableModel::reportPvtScaleFactor(const LibertyCell *cell, - const Pvt *pvt, - int digits) const + const Pvt *pvt, + int digits) const { if (pvt == nullptr) pvt = cell->libertyLibrary()->defaultOperatingConditions(); if (pvt) { string result; stringPrint(result, "PVT scale factor = %.*f\n", - digits, - scaleFactor(cell, pvt)); + digits, + scaleFactor(cell, pvt)); return result; } return ""; @@ -766,22 +767,22 @@ Table0::value(size_t, float Table0::findValue(float, - float, - float) const + float, + float) const { return value_; } string Table0::reportValue(const char *result_name, - const LibertyCell *, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, + const LibertyCell *, + const Pvt *, + float value1, + const char *comment1, + float value2, + float value3, const Unit *table_unit, - int digits) const + int digits) const { string result = result_name; result += " constant = "; @@ -794,7 +795,7 @@ Table0::reportValue(const char *result_name, void Table0::report(const Units *units, - Report *report) const + Report *report) const { int digits = 4; const Unit *table_unit = units->timeUnit(); @@ -811,7 +812,7 @@ Table1::Table1() : } Table1::Table1(FloatSeq *values, - TableAxisPtr axis1) : + TableAxisPtr axis1) : Table(), values_(values), axis1_(axis1) @@ -865,8 +866,8 @@ Table1::value(size_t axis_index1) const float Table1::findValue(float axis_value1, - float, - float) const + float, + float) const { return findValue(axis_value1); } @@ -913,14 +914,14 @@ Table1::findValueClip(float axis_value1) const string Table1::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, + const LibertyCell *cell, + const Pvt *, + float value1, + const char *comment1, + float value2, + float value3, const Unit *table_unit, - int digits) const + int digits) const { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); @@ -946,7 +947,7 @@ Table1::reportValue(const char *result_name, result += table_unit->asString(value(index1), digits); result += " "; result += table_unit->asString(value(index1 + 1), - digits); + digits); result += '\n'; } @@ -959,7 +960,7 @@ Table1::reportValue(const char *result_name, void Table1::report(const Units *units, - Report *report) const + Report *report) const { int digits = 4; const Unit *unit1 = axis1_->unit(units); @@ -984,8 +985,8 @@ Table1::report(const Units *units, //////////////////////////////////////////////////////////////// Table2::Table2(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2) : + TableAxisPtr axis1, + TableAxisPtr axis2) : Table(), values_(values), axis1_(axis1), @@ -995,7 +996,7 @@ Table2::Table2(FloatTable *values, Table2::~Table2() { - values_->deleteContents(); + deleteContents(*values_); delete values_; } @@ -1018,8 +1019,8 @@ Table2::value(size_t axis_index1, // Bilinear Interpolation. float Table2::findValue(float axis_value1, - float axis_value2, - float) const + float axis_value2, + float) const { size_t size1 = axis1_->size(); size_t size2 = axis2_->size(); @@ -1035,8 +1036,8 @@ Table2::findValue(float axis_value1, double dx2 = (x2 - x2l) / (x2u - x2l); double y01 = value(0, axis_index2 + 1); double tbl_value - = (1 - dx2) * y00 - + dx2 * y01; + = (1 - dx2) * y00 + + dx2 * y01; return tbl_value; } } @@ -1079,14 +1080,14 @@ Table2::findValue(float axis_value1, string Table2::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, + const LibertyCell *cell, + const Pvt *, + float value1, + const char *comment1, + float value2, + float value3, const Unit *table_unit, - int digits) const + int digits) const { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); @@ -1146,7 +1147,7 @@ Table2::reportValue(const char *result_name, void Table2::report(const Units *units, - Report *report) const + Report *report) const { int digits = 4; const Unit *table_unit = units->timeUnit(); @@ -1175,9 +1176,9 @@ Table2::report(const Units *units, //////////////////////////////////////////////////////////////// Table3::Table3(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2, - TableAxisPtr axis3) : + TableAxisPtr axis1, + TableAxisPtr axis2, + TableAxisPtr axis3) : Table2(values, axis1, axis2), axis3_(axis3) { @@ -1195,8 +1196,8 @@ Table3::value(size_t axis_index1, // Bilinear Interpolation. float Table3::findValue(float axis_value1, - float axis_value2, - float axis_value3) const + float axis_value2, + float axis_value3) const { size_t axis_index1 = axis1_->findAxisIndex(axis_value1); size_t axis_index2 = axis2_->findAxisIndex(axis_value2); @@ -1226,7 +1227,7 @@ Table3::findValue(float axis_value1, if (axis2_->size() != 1) { y110 = value(axis_index1 + 1, axis_index2 + 1, axis_index3); if (axis3_->size() != 1) - y111 = value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1); + y111 = value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1); } } if (axis2_->size() != 1) { @@ -1269,14 +1270,14 @@ Table3::findValue(float axis_value1, // 0.40 | 0.20 0.30 string Table3::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, + const LibertyCell *cell, + const Pvt *, + float value1, + const char *comment1, + float value2, + float value3, const Unit *table_unit, - int digits) const + int digits) const { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); @@ -1322,11 +1323,11 @@ Table3::reportValue(const char *result_name, result += unit1->asString(axis1_->axisValue(axis_index1+1), digits); result += " v / "; result += table_unit->asString(value(axis_index1+1,axis_index2,axis_index3), - digits); + digits); if (axis3_->size() != 1) { result += " "; result += table_unit->asString(value(axis_index1+1,axis_index2,axis_index3+1), - digits); + digits); } } else { @@ -1343,7 +1344,7 @@ Table3::reportValue(const char *result_name, if (axis3_->size() != 1) { result += " "; result += table_unit->asString(value(axis_index1, axis_index2, axis_index3+1), - digits); + digits); } result += '\n'; @@ -1351,11 +1352,11 @@ Table3::reportValue(const char *result_name, if (axis1_->size() != 1 && axis2_->size() != 1) { result += table_unit->asString(value(axis_index1+1,axis_index2+1,axis_index3), - digits); + digits); if (axis3_->size() != 1) { result += " "; result +=table_unit->asString(value(axis_index1+1,axis_index2+1,axis_index3+1), - digits); + digits); } } result += '\n'; @@ -1383,7 +1384,7 @@ Table3::reportValue(const char *result_name, static void appendSpaces(string &result, - int count) + int count) { while (count--) result += ' '; @@ -1391,7 +1392,7 @@ appendSpaces(string &result, void Table3::report(const Units *units, - Report *report) const + Report *report) const { int digits = 4; const Unit *table_unit = units->timeUnit(); @@ -1426,7 +1427,7 @@ Table3::report(const Units *units, //////////////////////////////////////////////////////////////// TableAxis::TableAxis(TableAxisVariable variable, - FloatSeq *values) : + FloatSeq *values) : variable_(variable), values_(values) { @@ -1489,9 +1490,9 @@ findValueIndex(float value, while (upper - lower > 1) { int mid = (upper + lower) >> 1; if (value >= (*values)[mid]) - lower = mid; + lower = mid; else - upper = mid; + upper = mid; } return lower; } @@ -1517,9 +1518,9 @@ TableAxis::findAxisIndex(float value, return; } if (value > (*values_)[mid]) - lower = mid; + lower = mid; else - upper = mid; + upper = mid; } } exists = false; @@ -1539,9 +1540,9 @@ TableAxis::findAxisClosestIndex(float value) const while (upper - lower > 1) { int mid = (upper + lower) >> 1; if (value >= (*values_)[mid]) - lower = mid; + lower = mid; else - upper = mid; + upper = mid; } if ((value - (*values_)[lower]) < ((*values_)[upper] - value)) return lower; @@ -1599,7 +1600,7 @@ tableVariableString(TableAxisVariable variable) const Unit * tableVariableUnit(TableAxisVariable variable, - const Units *units) + const Units *units) { switch (variable) { case TableAxisVariable::total_output_net_capacitance: @@ -1649,9 +1650,9 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, OutputWaveforms::~OutputWaveforms() { - current_waveforms_.deleteContents(); - voltage_waveforms_.deleteContents(); - voltage_currents_.deleteContents(); + deleteContents(current_waveforms_);; + deleteContents(voltage_waveforms_); + deleteContents(voltage_currents_); delete ref_times_; } diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index f203a4afe..0a7acff5c 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -24,13 +24,14 @@ #include "TimingModel.hh" +#include "ContainerHelpers.hh" #include "EnumNameMap.hh" #include "FuncExpr.hh" #include "TimingRole.hh" #include "Liberty.hh" #include "TimingArc.hh" -#include "DcalcAnalysisPt.hh" #include "TableModel.hh" +#include "Sdc.hh" namespace sta { @@ -39,10 +40,10 @@ using std::make_shared; static bool timingArcsEquiv(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); static bool timingArcsLess(const TimingArcSet *set1, - const TimingArcSet *set2); + const TimingArcSet *set2); //////////////////////////////////////////////////////////////// @@ -151,7 +152,7 @@ TimingArcAttrs::model(const RiseFall *rf) const void TimingArcAttrs::setModel(const RiseFall *rf, - TimingModel *model) + TimingModel *model) { models_[rf->index()] = model; } @@ -192,11 +193,11 @@ TimingArcAttrsPtr TimingArcSet::wire_timing_arc_attrs_ = nullptr; TimingArcSet *TimingArcSet::wire_timing_arc_set_ = nullptr; TimingArcSet::TimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs) : + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs) : from_(from), to_(to), related_out_(related_out), @@ -204,7 +205,6 @@ TimingArcSet::TimingArcSet(LibertyCell *cell, attrs_(attrs), is_cond_default_(false), index_(cell->addTimingArcSet(this)), - is_disabled_constraint_(false), from_arc1_{nullptr, nullptr}, from_arc2_{nullptr, nullptr}, to_arc_{nullptr, nullptr} @@ -220,7 +220,6 @@ TimingArcSet::TimingArcSet(const TimingRole *role, attrs_(attrs), is_cond_default_(false), index_(0), - is_disabled_constraint_(false), from_arc1_{nullptr, nullptr}, from_arc2_{nullptr, nullptr}, to_arc_{nullptr, nullptr} @@ -229,7 +228,7 @@ TimingArcSet::TimingArcSet(const TimingRole *role, TimingArcSet::~TimingArcSet() { - arcs_.deleteContents(); + deleteContents(arcs_); } bool @@ -312,9 +311,9 @@ TimingArcSet::setIsCondDefault(bool is_default) void TimingArcSet::arcsFrom(const RiseFall *from_rf, - // Return values. - TimingArc *&arc1, - TimingArc *&arc2) const + // Return values. + TimingArc *&arc1, + TimingArc *&arc2) const { int rf_index = from_rf->index(); arc1 = from_arc1_[rf_index]; @@ -349,12 +348,6 @@ TimingArcSet::isRisingFallingEdge() const return nullptr; } -void -TimingArcSet::setIsDisabledConstraint(bool is_disabled) -{ - is_disabled_constraint_ = is_disabled; -} - float TimingArcSet::ocvArcDepth() const { @@ -366,11 +359,11 @@ TimingArcSet::ocvArcDepth() const LibertyCell *cell = from_->libertyCell(); depth = cell->ocvArcDepth(); if (depth != 0.0) - return depth; + return depth; else { - depth = cell->libertyLibrary()->ocvArcDepth(); - if (depth != 0.0) - return depth; + depth = cell->libertyLibrary()->ocvArcDepth(); + if (depth != 0.0) + return depth; } } } @@ -380,7 +373,7 @@ TimingArcSet::ocvArcDepth() const bool TimingArcSet::equiv(const TimingArcSet *set1, - const TimingArcSet *set2) + const TimingArcSet *set2) { return LibertyPort::equiv(set1->from(), set2->from()) && LibertyPort::equiv(set1->to(), set2->to()) @@ -394,7 +387,7 @@ TimingArcSet::equiv(const TimingArcSet *set1, static bool timingArcsEquiv(const TimingArcSet *arc_set1, - const TimingArcSet *arc_set2) + const TimingArcSet *arc_set2) { const TimingArcSeq &arcs1 = arc_set1->arcs(); const TimingArcSeq &arcs2 = arc_set2->arcs(); @@ -414,14 +407,14 @@ timingArcsEquiv(const TimingArcSet *arc_set1, bool TimingArcSet::less(const TimingArcSet *set1, - const TimingArcSet *set2) + const TimingArcSet *set2) { return timingArcSetLess(set1, set2); } bool timingArcSetLess(const TimingArcSet *set1, - const TimingArcSet *set2) + const TimingArcSet *set2) { LibertyPort *from1 = set1->from(); LibertyPort *from2 = set2->from(); @@ -432,45 +425,45 @@ timingArcSetLess(const TimingArcSet *set1, const TimingRole *role1 = set1->role(); const TimingRole *role2 = set2->role(); if (role1 == role2) { - const FuncExpr *cond1 = set1->cond(); - const FuncExpr *cond2 = set2->cond(); - if (FuncExpr::equiv(cond1, cond2)) { - const char *sdf_cond1 = set1->sdfCond(); - const char *sdf_cond2 = set2->sdfCond(); - if (stringEqIf(sdf_cond1, sdf_cond2)) { - const char *sdf_cond_start1 = set1->sdfCondStart(); - const char *sdf_cond_start2 = set2->sdfCondStart(); - if (stringEqIf(sdf_cond_start1, sdf_cond_start2)) { - const char *sdf_cond_end1 = set1->sdfCondEnd(); - const char *sdf_cond_end2 = set2->sdfCondEnd(); - if (stringEqIf(sdf_cond_end1, sdf_cond_end2)) { - const char *mode_name1 = set1->modeName(); - const char *mode_name2 = set2->modeName(); - if (stringEqIf(mode_name1, mode_name2)) { - const char *mode_value1 = set1->modeValue(); - const char *mode_value2 = set2->modeValue(); - if (stringEqIf(mode_value1, mode_value2)) - return timingArcsLess(set1, set2); - else - return stringLessIf(mode_value1, mode_value2); - } - else - return stringLessIf(mode_name1, mode_name2); - } - else - return stringLessIf(sdf_cond_end1, sdf_cond_end2); - } - else - return stringLessIf(sdf_cond_start1, sdf_cond_start2); - } - else - return stringLessIf(sdf_cond1, sdf_cond2); - } - else - return FuncExpr::less(cond1, cond2); + const FuncExpr *cond1 = set1->cond(); + const FuncExpr *cond2 = set2->cond(); + if (FuncExpr::equiv(cond1, cond2)) { + const char *sdf_cond1 = set1->sdfCond(); + const char *sdf_cond2 = set2->sdfCond(); + if (stringEqIf(sdf_cond1, sdf_cond2)) { + const char *sdf_cond_start1 = set1->sdfCondStart(); + const char *sdf_cond_start2 = set2->sdfCondStart(); + if (stringEqIf(sdf_cond_start1, sdf_cond_start2)) { + const char *sdf_cond_end1 = set1->sdfCondEnd(); + const char *sdf_cond_end2 = set2->sdfCondEnd(); + if (stringEqIf(sdf_cond_end1, sdf_cond_end2)) { + const char *mode_name1 = set1->modeName(); + const char *mode_name2 = set2->modeName(); + if (stringEqIf(mode_name1, mode_name2)) { + const char *mode_value1 = set1->modeValue(); + const char *mode_value2 = set2->modeValue(); + if (stringEqIf(mode_value1, mode_value2)) + return timingArcsLess(set1, set2); + else + return stringLessIf(mode_value1, mode_value2); + } + else + return stringLessIf(mode_name1, mode_name2); + } + else + return stringLessIf(sdf_cond_end1, sdf_cond_end2); + } + else + return stringLessIf(sdf_cond_start1, sdf_cond_start2); + } + else + return stringLessIf(sdf_cond1, sdf_cond2); + } + else + return FuncExpr::less(cond1, cond2); } else - return TimingRole::less(role1, role2); + return TimingRole::less(role1, role2); } else return LibertyPort::less(to1, to2); @@ -481,7 +474,7 @@ timingArcSetLess(const TimingArcSet *set1, static bool timingArcsLess(const TimingArcSet *arc_set1, - const TimingArcSet *arc_set2) + const TimingArcSet *arc_set2) { const TimingArcSeq &arcs1 = arc_set1->arcs(); const TimingArcSeq &arcs2 = arc_set2->arcs(); @@ -527,9 +520,9 @@ TimingArcSet::init() wire_timing_arc_attrs_ = make_shared(TimingSense::positive_unate); wire_timing_arc_set_ = new TimingArcSet(TimingRole::wire(), wire_timing_arc_attrs_); new TimingArc(wire_timing_arc_set_, Transition::rise(), - Transition::rise(), nullptr); + Transition::rise(), nullptr); new TimingArc(wire_timing_arc_set_, Transition::fall(), - Transition::fall(), nullptr); + Transition::fall(), nullptr); } void @@ -543,9 +536,9 @@ TimingArcSet::destroy() //////////////////////////////////////////////////////////////// TimingArc::TimingArc(TimingArcSet *set, - const Transition *from_rf, - const Transition *to_rf, - TimingModel *model) : + const Transition *from_rf, + const Transition *to_rf, + TimingModel *model) : set_(set), from_rf_(from_rf), to_rf_(to_rf), @@ -585,9 +578,10 @@ TimingArc::to_string() const } GateTimingModel * -TimingArc::gateModel(const DcalcAnalysisPt *dcalc_ap) const +TimingArc::gateModel(const Scene *scene, + const MinMax *min_max) const { - return dynamic_cast(model(dcalc_ap)); + return dynamic_cast(model(scene, min_max)); } GateTableModel * @@ -597,34 +591,38 @@ TimingArc::gateTableModel() const } GateTableModel * -TimingArc::gateTableModel(const DcalcAnalysisPt *dcalc_ap) const +TimingArc::gateTableModel(const Scene *scene, + const MinMax *min_max) const { - return dynamic_cast(model(dcalc_ap)); + return dynamic_cast(model(scene, min_max)); } CheckTimingModel * -TimingArc::checkModel(const DcalcAnalysisPt *dcalc_ap) const +TimingArc::checkModel(const Scene *scene, + const MinMax *min_max) const { - return dynamic_cast(model(dcalc_ap)); + return dynamic_cast(model(scene, min_max)); } TimingModel * -TimingArc::model(const DcalcAnalysisPt *dcalc_ap) const +TimingArc::model(const Scene *scene, + const MinMax *min_max) const { - const TimingArc *corner_arc = cornerArc(dcalc_ap->libertyIndex()); - ScaledTimingModelMap *scaled_models = corner_arc->scaled_models_; + const TimingArc *scene_arc = sceneArc(scene->libertyIndex(min_max)); + ScaledTimingModelMap *scaled_models = scene_arc->scaled_models_; if (scaled_models) { - const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); - TimingModel *scaled_model = scaled_models->findKey(op_cond); + const OperatingConditions *op_cond = + scene->sdc()->operatingConditions(min_max); + TimingModel *scaled_model = findKey(*scaled_models, op_cond); if (scaled_model) return scaled_model; } - return corner_arc->model(); + return scene_arc->model(); } void TimingArc::addScaledModel(const OperatingConditions *op_cond, - TimingModel *scaled_model) + TimingModel *scaled_model) { if (scaled_models_ == nullptr) scaled_models_ = new ScaledTimingModelMap; @@ -633,7 +631,7 @@ TimingArc::addScaledModel(const OperatingConditions *op_cond, bool TimingArc::equiv(const TimingArc *arc1, - const TimingArc *arc2) + const TimingArc *arc2) { return arc1->fromEdge() == arc2->fromEdge() && arc1->toEdge() == arc2->toEdge(); @@ -646,23 +644,23 @@ TimingArc::setIndex(unsigned index) } const TimingArc * -TimingArc::cornerArc(int ap_index) const +TimingArc::sceneArc(int ap_index) const { - if (ap_index < static_cast(corner_arcs_.size())) { - TimingArc *corner_arc = corner_arcs_[ap_index]; - if (corner_arc) - return corner_arc; + if (ap_index < static_cast(scene_arcs_.size())) { + TimingArc *scene_arc = scene_arcs_[ap_index]; + if (scene_arc) + return scene_arc; } return this; } void -TimingArc::setCornerArc(TimingArc *corner_arc, - int ap_index) +TimingArc::setSceneArc(TimingArc *scene_arc, + int ap_index) { - if (ap_index >= static_cast(corner_arcs_.size())) - corner_arcs_.resize(ap_index + 1); - corner_arcs_[ap_index] = corner_arc; + if (ap_index >= static_cast(scene_arcs_.size())) + scene_arcs_.resize(ap_index + 1); + scene_arcs_[ap_index] = scene_arc; } //////////////////////////////////////////////////////////////// @@ -673,12 +671,12 @@ TimingArc::sense() const if ((from_rf_ == Transition::rise() && to_rf_ == Transition::rise()) || (from_rf_ == Transition::fall() - && to_rf_ == Transition::fall())) + && to_rf_ == Transition::fall())) return TimingSense::positive_unate; else if ((from_rf_ == Transition::rise() && to_rf_ == Transition::fall()) || (from_rf_ == Transition::fall() - && to_rf_ == Transition::rise())) + && to_rf_ == Transition::rise())) return TimingSense::negative_unate; else return TimingSense::non_unate; diff --git a/liberty/TimingRole.cc b/liberty/TimingRole.cc index 819ba9297..1839e52f4 100644 --- a/liberty/TimingRole.cc +++ b/liberty/TimingRole.cc @@ -90,12 +90,12 @@ const TimingRole TimingRole::clock_tree_path_max_("max clock tree path", false, false, MinMax::max(), nullptr, 28); TimingRole::TimingRole(const char *name, - bool is_sdf_iopath, - bool is_timing_check, - bool is_non_seq_check, - const MinMax *path_min_max, - const TimingRole *generic_role, - int index) : + bool is_sdf_iopath, + bool is_timing_check, + bool is_non_seq_check, + const MinMax *path_min_max, + const TimingRole *generic_role, + int index) : name_(name), is_timing_check_(is_timing_check), is_sdf_iopath_(is_sdf_iopath), @@ -173,7 +173,7 @@ TimingRole::isTimingCheckBetween() const bool TimingRole::less(const TimingRole *role1, - const TimingRole *role2) + const TimingRole *role2) { return role1->index() < role2->index(); } diff --git a/liberty/Units.cc b/liberty/Units.cc index 8dcbb4f0f..97e333099 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -44,8 +44,8 @@ Unit::Unit(const char *suffix) : } Unit::Unit(float scale, - const char *suffix, - int digits) : + const char *suffix, + int digits) : scale_(scale), suffix_(suffix), digits_(digits) @@ -172,7 +172,7 @@ Unit::asString(double value) const const char * Unit::asString(float value, - int digits) const + int digits) const { // Special case INF because it blows up otherwise. if (abs(value) >= INF * .1) diff --git a/liberty/Wireload.cc b/liberty/Wireload.cc index 9c8ea3e5b..e85ee6038 100644 --- a/liberty/Wireload.cc +++ b/liberty/Wireload.cc @@ -32,7 +32,7 @@ namespace sta { Wireload::Wireload(const char *name, - LibertyLibrary *library) : + LibertyLibrary *library) : name_(stringCopy(name)), library_(library), area_(0.0F), @@ -43,11 +43,11 @@ Wireload::Wireload(const char *name, } Wireload::Wireload(const char *name, - LibertyLibrary *library, - float area, - float resistance, - float capacitance, - float slope) : + LibertyLibrary *library, + float area, + float resistance, + float capacitance, + float slope) : name_(stringCopy(name)), library_(library), area_(area), @@ -59,7 +59,7 @@ Wireload::Wireload(const char *name, Wireload::~Wireload() { - fanout_lengths_.deleteContents(); + deleteContents(fanout_lengths_); stringDelete(name_); } @@ -90,7 +90,7 @@ Wireload::setSlope(float slope) struct FanoutLess { bool operator()(FanoutLength *fanout1, - FanoutLength *fanout2) const + FanoutLength *fanout2) const { return fanout1->first < fanout2->first; } @@ -98,7 +98,7 @@ struct FanoutLess void Wireload::addFanoutLength(float fanout, - float length) + float length) { FanoutLength *fanout_length = new FanoutLength(fanout, length); fanout_lengths_.push_back(fanout_length); @@ -110,9 +110,9 @@ Wireload::addFanoutLength(float fanout, void Wireload::findWireload(float fanout, - const OperatingConditions *op_cond, - float &cap, - float &res) const + const OperatingConditions *op_cond, + float &cap, + float &res) const { size_t size = fanout_lengths_.size(); float length; @@ -126,7 +126,7 @@ Wireload::findWireload(float fanout, // Extrapolate from lowest fanout entry. length = fanout_lengths_[0]->second - (fanout0 - fanout) * slope_; if (length < 0) - length = 0; + length = 0; } else if (fanout == fanout0) length = fanout_lengths_[0]->second; @@ -138,11 +138,11 @@ Wireload::findWireload(float fanout, int lower = -1; int upper = size; while (upper - lower > 1) { - int mid = (upper + lower) >> 1; - if (fanout >= fanout_lengths_[mid]->first) - lower = mid; - else - upper = mid; + int mid = (upper + lower) >> 1; + if (fanout >= fanout_lengths_[mid]->first) + lower = mid; + else + upper = mid; } // Interpolate between lower and lower+1 entries. float fanout1 = fanout_lengths_[lower]->first; @@ -165,8 +165,8 @@ class WireloadForArea { public: WireloadForArea(float min_area, - float max_area, - const Wireload *wireload); + float max_area, + const Wireload *wireload); float minArea() const { return min_area_; } float maxArea() const { return max_area_; } const Wireload *wireload() const { return wireload_; } @@ -178,8 +178,8 @@ class WireloadForArea }; WireloadForArea::WireloadForArea(float min_area, - float max_area, - const Wireload *wireload) : + float max_area, + const Wireload *wireload) : min_area_(min_area), max_area_(max_area), wireload_(wireload) @@ -193,14 +193,14 @@ WireloadSelection::WireloadSelection(const char *name) : WireloadSelection::~WireloadSelection() { - wireloads_.deleteContents(); + deleteContents(wireloads_); stringDelete(name_); } struct WireloadForAreaMinLess { bool operator()(WireloadForArea *wireload1, - WireloadForArea *wireload2) const + WireloadForArea *wireload2) const { return wireload1->minArea() < wireload2->minArea(); } @@ -208,11 +208,11 @@ struct WireloadForAreaMinLess void WireloadSelection::addWireloadFromArea(float min_area, - float max_area, - const Wireload *wireload) + float max_area, + const Wireload *wireload) { WireloadForArea *wireload_area = new WireloadForArea(min_area, max_area, - wireload); + wireload); wireloads_.push_back(wireload_area); // Keep wireloads sorted by area for lookup. if (wireloads_.size() > 1 diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index 2dedc9f4c..0ecba436c 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -27,6 +27,7 @@ #include #include +#include "ContainerHelpers.hh" #include "PatternMatch.hh" #include "PortDirection.hh" #include "ParseBus.hh" @@ -44,8 +45,8 @@ using std::swap; static constexpr char escape_ = '\\'; ConcreteLibrary::ConcreteLibrary(const char *name, - const char *filename, - bool is_liberty) : + const char *filename, + bool is_liberty) : name_(name), id_(ConcreteNetwork::nextObjectId()), filename_(filename ? filename : ""), @@ -57,13 +58,13 @@ ConcreteLibrary::ConcreteLibrary(const char *name, ConcreteLibrary::~ConcreteLibrary() { - cell_map_.deleteContents(); + deleteContents(cell_map_); } ConcreteCell * ConcreteLibrary::makeCell(const char *name, - bool is_leaf, - const char *filename) + bool is_leaf, + const char *filename) { ConcreteCell *cell = new ConcreteCell(name, filename, is_leaf, this); addCell(cell); @@ -78,7 +79,7 @@ ConcreteLibrary::addCell(ConcreteCell *cell) void ConcreteLibrary::renameCell(ConcreteCell *cell, - const char *cell_name) + const char *cell_name) { cell_map_.erase(cell->name()); cell_map_[cell_name] = cell; @@ -100,17 +101,15 @@ ConcreteLibrary::cellIterator() const ConcreteCell * ConcreteLibrary::findCell(const char *name) const { - return cell_map_.findKey(name); + return findKey(cell_map_, name); } CellSeq ConcreteLibrary::findCellsMatching(const PatternMatch *pattern) const { CellSeq matches; - ConcreteLibraryCellIterator cell_iter=ConcreteLibraryCellIterator(cell_map_); - while (cell_iter.hasNext()) { - ConcreteCell *cell = cell_iter.next(); - if (pattern->match(cell->name())) + for (auto [name, cell] : cell_map_) { + if (pattern->match(name)) matches.push_back(reinterpret_cast(cell)); } return matches; @@ -118,7 +117,7 @@ ConcreteLibrary::findCellsMatching(const PatternMatch *pattern) const void ConcreteLibrary::setBusBrkts(char left, - char right) + char right) { bus_brkt_left_ = left; bus_brkt_right_ = right; @@ -127,8 +126,8 @@ ConcreteLibrary::setBusBrkts(char left, //////////////////////////////////////////////////////////////// ConcreteCell::ConcreteCell(const char *name, - const char *filename, - bool is_leaf, + const char *filename, + bool is_leaf, ConcreteLibrary *library) : name_(name), id_(ConcreteNetwork::nextObjectId()), @@ -143,7 +142,7 @@ ConcreteCell::ConcreteCell(const char *name, ConcreteCell::~ConcreteCell() { - ports_.deleteContents(); + deleteContents(ports_); } void @@ -175,7 +174,7 @@ ConcreteCell::makePort(const char *name) ConcretePort * ConcreteCell::makeBundlePort(const char *name, - ConcretePortSeq *members) + ConcretePortSeq *members) { ConcretePort *port = new ConcretePort(name, false, -1, -1, true, members, this); addPort(port); @@ -186,11 +185,11 @@ ConcreteCell::makeBundlePort(const char *name, ConcretePort * ConcreteCell::makeBusPort(const char *name, - int from_index, - int to_index) + int from_index, + int to_index) { ConcretePort *port = new ConcretePort(name, true, from_index, to_index, - false, new ConcretePortSeq, this); + false, new ConcretePortSeq, this); addPort(port); makeBusPortBits(port, name, from_index, to_index); return port; @@ -198,21 +197,21 @@ ConcreteCell::makeBusPort(const char *name, ConcretePort * ConcreteCell::makeBusPort(const char *name, - int from_index, - int to_index, - ConcretePortSeq *members) + int from_index, + int to_index, + ConcretePortSeq *members) { ConcretePort *port = new ConcretePort(name, true, from_index, to_index, - false, members, this); + false, members, this); addPort(port); return port; } void ConcreteCell::makeBusPortBits(ConcretePort *bus_port, - const char *name, - int from_index, - int to_index) + const char *name, + int from_index, + int to_index) { if (from_index < to_index) { for (int index = from_index; index <= to_index; index++) @@ -226,15 +225,15 @@ ConcreteCell::makeBusPortBits(ConcretePort *bus_port, void ConcreteCell::makeBusPortBit(ConcretePort *bus_port, - const char *bus_name, - int bit_index) + const char *bus_name, + int bit_index) { string bit_name; stringPrint(bit_name, "%s%c%d%c", - bus_name, - library_->busBrktLeft(), - bit_index, - library_->busBrktRight()); + bus_name, + library_->busBrktLeft(), + bit_index, + library_->busBrktRight()); ConcretePort *port = makePort(bit_name.c_str(), bit_index); bus_port->addPortBit(port); addPortBit(port); @@ -242,10 +241,10 @@ ConcreteCell::makeBusPortBit(ConcretePort *bus_port, ConcretePort * ConcreteCell::makePort(const char *bit_name, - int bit_index) + int bit_index) { ConcretePort *port = new ConcretePort(bit_name, false, bit_index, - bit_index, false, nullptr, this); + bit_index, false, nullptr, this); addPortBit(port); return port; } @@ -291,7 +290,7 @@ ConcreteCell::getAttribute(const string &key) const ConcretePort * ConcreteCell::findPort(const char *name) const { - return port_map_.findKey(name); + return findKey(port_map_, name); } size_t @@ -358,7 +357,7 @@ BusPort::addBusBit(ConcretePort *port, void ConcreteCell::groupBusPorts(const char bus_brkt_left, - const char bus_brkt_right, + const char bus_brkt_right, std::function port_msb_first) { const char bus_brkts_left[2]{bus_brkt_left, '\0'}; @@ -376,11 +375,11 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, string bus_name; int index; parseBusName(port_name, bus_brkts_left, bus_brkts_right, escape_, - is_bus, bus_name, index); + is_bus, bus_name, index); if (is_bus) { if (!port->isBusBit()) { BusPort &bus_port = bus_map[bus_name]; - bus_port.addBusBit(port, index); + bus_port.addBusBit(port, index); port->setBusBitIndex(index); bus_port.setDirection(port->direction()); } @@ -412,11 +411,11 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, //////////////////////////////////////////////////////////////// ConcretePort::ConcretePort(const char *name, - bool is_bus, - int from_index, - int to_index, - bool is_bundle, - ConcretePortSeq *member_ports, + bool is_bus, + int from_index, + int to_index, + bool is_bundle, + ConcretePortSeq *member_ports, ConcreteCell *cell) : name_(name), id_(ConcreteNetwork::nextObjectId()), @@ -439,7 +438,7 @@ ConcretePort::~ConcretePort() // The member ports of a bus are owned by the bus port. // The member ports of a bundle are NOT owned by the bus port. if (is_bus_) - member_ports_->deleteContents(); + deleteContents(member_ports_); delete member_ports_; } @@ -473,11 +472,11 @@ 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()); + name(), + lib->busBrktLeft(), + from_index_, + to_index_, + lib->busBrktRight()); } else return name(); @@ -548,8 +547,8 @@ ConcretePort::findBusBit(int index) const && index >= from_index_) return (*member_ports_)[index - from_index_]; else if (from_index_ >= to_index_ - && index >= to_index_ - && index <= from_index_) + && index >= to_index_ + && index <= from_index_) return (*member_ports_)[from_index_ - index]; else return nullptr; @@ -559,11 +558,11 @@ bool ConcretePort::busIndexInRange(int index) const { return (from_index_ <= to_index_ - && index <= to_index_ - && index >= from_index_) + && index <= to_index_ + && index >= from_index_) || (from_index_ > to_index_ - && index >= to_index_ - && index <= from_index_); + && index >= to_index_ + && index <= from_index_); } bool @@ -580,9 +579,9 @@ ConcretePort::memberIterator() const //////////////////////////////////////////////////////////////// -ConcreteCellPortBitIterator::ConcreteCellPortBitIterator(const ConcreteCell* - cell) : - port_iter_(cell->ports_), +ConcreteCellPortBitIterator::ConcreteCellPortBitIterator(const ConcreteCell* cell) : + ports_(cell->ports_), + port_iter_(ports_.begin()), member_iter_(nullptr), next_(nullptr) { @@ -616,8 +615,8 @@ ConcreteCellPortBitIterator::findNext() member_iter_ = nullptr; } } - while (port_iter_.hasNext()) { - ConcretePort *next = port_iter_.next(); + while (port_iter_ != ports_.end()) { + ConcretePort *next = *port_iter_++; if (next->isBus()) { member_iter_ = next->memberIterator(); next_ = member_iter_->next(); diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index d165e9f31..6f4b26320 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -24,6 +24,8 @@ #include "ConcreteNetwork.hh" +#include + #include "PatternMatch.hh" #include "Report.hh" #include "Liberty.hh" @@ -37,17 +39,17 @@ using std::string; static void makeChildNetwork(Instance *proto, - Instance *parent, - ConcreteBindingTbl *parent_bindings, - NetworkReader *network); + Instance *parent, + ConcreteBindingTbl *parent_bindings, + NetworkReader *network); static void makeClonePins(Instance *proto, - Instance *clone, - Instance *clone_view, - ConcreteBindingTbl *bindings, - Instance *parent, - ConcreteBindingTbl *parent_bindings, - NetworkReader *network); + Instance *clone, + Instance *clone_view, + ConcreteBindingTbl *bindings, + Instance *parent, + ConcreteBindingTbl *parent_bindings, + NetworkReader *network); //////////////////////////////////////////////////////////////// @@ -61,49 +63,56 @@ makeConcreteNetwork() class ConcreteInstanceChildIterator : public InstanceChildIterator { public: - explicit ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map); + ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map); bool hasNext(); Instance *next(); private: - ConcreteInstanceChildMap::ConstIterator iter_; + ConcreteInstanceChildMap *map_; + ConcreteInstanceChildMap::const_iterator iter_; }; ConcreteInstanceChildIterator:: ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map) : - iter_(map) + map_(map) { + if (map_) + iter_ = map_->begin(); } bool ConcreteInstanceChildIterator::hasNext() { - return iter_.hasNext(); + return map_ && iter_ != map_->end(); } Instance * ConcreteInstanceChildIterator::next() { - return reinterpret_cast(iter_.next()); + Instance *next = reinterpret_cast(iter_->second); + iter_++; + return next; } class ConcreteInstanceNetIterator : public InstanceNetIterator { public: - explicit ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets); + ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets); bool hasNext(); Net *next(); private: void findNext(); - ConcreteInstanceNetMap::Iterator iter_; + ConcreteInstanceNetMap *nets_; + ConcreteInstanceNetMap::iterator iter_; ConcreteNet *next_; }; ConcreteInstanceNetIterator:: ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets): - iter_(nets), + nets_(nets), + iter_(nets->begin()), next_(nullptr) { findNext(); @@ -119,8 +128,9 @@ ConcreteInstanceNetIterator::hasNext() void ConcreteInstanceNetIterator::findNext() { - while (iter_.hasNext()) { - next_ = iter_.next(); + while (iter_ != nets_->end()) { + next_ = iter_->second; + iter_++; if (next_->mergedInto() == nullptr) return; } @@ -141,7 +151,7 @@ class ConcreteInstancePinIterator : public InstancePinIterator { public: ConcreteInstancePinIterator(const ConcreteInstance *inst, - int pin_count); + int pin_count); bool hasNext(); Pin *next(); @@ -156,7 +166,7 @@ class ConcreteInstancePinIterator : public InstancePinIterator ConcreteInstancePinIterator:: ConcreteInstancePinIterator(const ConcreteInstance *inst, - int pin_count) : + int pin_count) : pins_(inst->pins_), pin_count_(pin_count), pin_index_(0) @@ -195,7 +205,7 @@ ConcreteInstancePinIterator::findNext() class ConcreteNetPinIterator : public NetPinIterator { public: - explicit ConcreteNetPinIterator(const ConcreteNet *net); + ConcreteNetPinIterator(const ConcreteNet *net); bool hasNext(); Pin *next(); @@ -227,7 +237,7 @@ ConcreteNetPinIterator::next() class ConcreteNetTermIterator : public NetTermIterator { public: - explicit ConcreteNetTermIterator(const ConcreteNet *net); + ConcreteNetTermIterator(const ConcreteNet *net); bool hasNext(); Term *next(); @@ -276,7 +286,7 @@ ConcreteNetwork::clear() { deleteTopInstance(); deleteCellNetworkViews(); - library_seq_.deleteContentsClear(); + deleteContents(library_seq_); library_map_.clear(); Network::clear(); } @@ -293,9 +303,7 @@ ConcreteNetwork::deleteTopInstance() void ConcreteNetwork::deleteCellNetworkViews() { - CellNetworkViewMap::Iterator view_iter(cell_network_view_map_); - while (view_iter.hasNext()) { - Instance *view = view_iter.next(); + for (auto [cell, view] : cell_network_view_map_) { if (view) deleteInstance(view); } @@ -313,29 +321,31 @@ ConcreteNetwork::topInstance() const class ConcreteLibraryIterator1 : public Iterator { public: - explicit ConcreteLibraryIterator1(const ConcreteLibrarySeq &lib_seq_); + ConcreteLibraryIterator1(const ConcreteLibrarySeq &libs); virtual bool hasNext(); virtual Library *next(); private: - ConcreteLibraryIterator iter_; + const ConcreteLibrarySeq &libs_; + ConcreteLibrarySeq::const_iterator iter_; }; -ConcreteLibraryIterator1::ConcreteLibraryIterator1(const ConcreteLibrarySeq &lib_seq_): - iter_(lib_seq_) +ConcreteLibraryIterator1::ConcreteLibraryIterator1(const ConcreteLibrarySeq &libs): + libs_(libs), + iter_(libs.begin()) { } bool ConcreteLibraryIterator1::hasNext() { - return iter_.hasNext(); + return iter_ != libs_.end(); } Library * ConcreteLibraryIterator1::next() { - return reinterpret_cast(iter_.next()); + return reinterpret_cast(*iter_++); } LibraryIterator * @@ -349,7 +359,7 @@ ConcreteNetwork::libraryIterator() const class ConcreteLibertyLibraryIterator : public Iterator { public: - explicit ConcreteLibertyLibraryIterator(const ConcreteNetwork *network); + ConcreteLibertyLibraryIterator(const ConcreteNetwork *network); virtual ~ConcreteLibertyLibraryIterator(); virtual bool hasNext(); virtual LibertyLibrary *next(); @@ -357,13 +367,15 @@ class ConcreteLibertyLibraryIterator : public Iterator private: void findNext(); - ConcreteLibrarySeq::ConstIterator iter_; + const ConcreteLibrarySeq &libs_; + ConcreteLibrarySeq::const_iterator iter_; LibertyLibrary *next_; }; ConcreteLibertyLibraryIterator:: ConcreteLibertyLibraryIterator(const ConcreteNetwork *network): - iter_(network->library_seq_), + libs_(network->library_seq_), + iter_(libs_.begin()), next_(nullptr) { findNext(); @@ -391,13 +403,13 @@ void ConcreteLibertyLibraryIterator::findNext() { next_ = nullptr; - while (iter_.hasNext()) { - ConcreteLibrary *lib = iter_.next(); + while (iter_ != libs_.end()) { + ConcreteLibrary *lib = *iter_++; if (lib->isLiberty()) { LibertyLibrary *liberty = static_cast(lib); if (liberty) { - next_ = liberty; - break; + next_ = liberty; + break; } } } @@ -413,7 +425,7 @@ ConcreteNetwork::libertyLibraryIterator() const Library * ConcreteNetwork::makeLibrary(const char *name, - const char *filename) + const char *filename) { ConcreteLibrary *library = new ConcreteLibrary(name, filename, false); addLibrary(library); @@ -422,7 +434,7 @@ ConcreteNetwork::makeLibrary(const char *name, LibertyLibrary * ConcreteNetwork::makeLibertyLibrary(const char *name, - const char *filename) + const char *filename) { LibertyLibrary *library = new LibertyLibrary(name, filename); addLibrary(library); @@ -439,7 +451,7 @@ ConcreteNetwork::addLibrary(ConcreteLibrary *library) Library * ConcreteNetwork::findLibrary(const char *name) { - return reinterpret_cast(library_map_.findKey(name)); + return reinterpret_cast(findKey(library_map_, name)); } void @@ -447,7 +459,7 @@ ConcreteNetwork::deleteLibrary(Library *library) { ConcreteLibrary *clib = reinterpret_cast(library); library_map_.erase(clib->name()); - library_seq_.eraseObject(clib); + library_seq_.erase(std::find(library_seq_.begin(), library_seq_.end(), clib)); delete clib; } @@ -470,16 +482,16 @@ ConcreteNetwork::id(const Library *library) const LibertyLibrary * ConcreteNetwork::findLiberty(const char *name) { - ConcreteLibrary *lib = library_map_.findKey(name); + ConcreteLibrary *lib = findKey(library_map_, name); if (lib) { if (lib->isLiberty()) return static_cast(lib); // Potential name conflict else { for (ConcreteLibrary *lib : library_seq_) { - if (stringEq(lib->name(), name) - && lib->isLiberty()) - return static_cast(lib); + if (stringEq(lib->name(), name) + && lib->isLiberty()) + return static_cast(lib); } } } @@ -488,9 +500,9 @@ ConcreteNetwork::findLiberty(const char *name) Cell * ConcreteNetwork::makeCell(Library *library, - const char *name, - bool is_leaf, - const char *filename) + const char *name, + bool is_leaf, + const char *filename) { ConcreteLibrary *clib = reinterpret_cast(library); return reinterpret_cast(clib->makeCell(name, is_leaf, filename)); @@ -498,7 +510,7 @@ ConcreteNetwork::makeCell(Library *library, Cell * ConcreteNetwork::findCell(const Library *library, - const char *name) const + const char *name) const { const ConcreteLibrary *clib = reinterpret_cast(library); @@ -508,9 +520,7 @@ ConcreteNetwork::findCell(const Library *library, Cell * ConcreteNetwork::findAnyCell(const char *name) { - ConcreteLibrarySeq::Iterator lib_iter(library_seq_); - while (lib_iter.hasNext()) { - ConcreteLibrary *lib = lib_iter.next(); + for (ConcreteLibrary *lib : library_seq_) { ConcreteCell *cell = lib->findCell(name); if (cell) return reinterpret_cast(cell); @@ -520,7 +530,7 @@ ConcreteNetwork::findAnyCell(const char *name) CellSeq ConcreteNetwork::findCellsMatching(const Library *library, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { const ConcreteLibrary *clib = reinterpret_cast(library); @@ -553,7 +563,7 @@ ConcreteNetwork::id(const Cell *cell) const void ConcreteNetwork::setName(Cell *cell, - const char *name) + const char *name) { ConcreteCell *ccell = reinterpret_cast(cell); ccell->setName(name); @@ -561,7 +571,7 @@ ConcreteNetwork::setName(Cell *cell, void ConcreteNetwork::setIsLeaf(Cell *cell, - bool is_leaf) + bool is_leaf) { ConcreteCell *ccell = reinterpret_cast(cell); ccell->setIsLeaf(is_leaf); @@ -633,7 +643,7 @@ ConcreteNetwork::attributeMap(const Cell *cell) const Port * ConcreteNetwork::findPort(const Cell *cell, - const char *name) const + const char *name) const { const ConcreteCell *ccell = reinterpret_cast(cell); return reinterpret_cast(ccell->findPort(name)); @@ -648,7 +658,7 @@ ConcreteNetwork::isLeaf(const Cell *cell) const Port * ConcreteNetwork::makePort(Cell *cell, - const char *name) + const char *name) { ConcreteCell *ccell = reinterpret_cast(cell); ConcretePort *port = ccell->makePort(name); @@ -657,9 +667,9 @@ ConcreteNetwork::makePort(Cell *cell, Port * ConcreteNetwork::makeBusPort(Cell *cell, - const char *name, - int from_index, - int to_index) + const char *name, + int from_index, + int to_index) { ConcreteCell *ccell = reinterpret_cast(cell); ConcretePort *port = ccell->makeBusPort(name, from_index, to_index); @@ -679,8 +689,8 @@ ConcreteNetwork::groupBusPorts(Cell *cell, Port * ConcreteNetwork::makeBundlePort(Cell *cell, - const char *name, - PortSeq *members) + const char *name, + PortSeq *members) { ConcreteCell *ccell = reinterpret_cast(cell); ConcretePortSeq *cmembers = reinterpret_cast(members); @@ -690,7 +700,7 @@ ConcreteNetwork::makeBundlePort(Cell *cell, void ConcreteNetwork::setDirection(Port *port, - PortDirection *dir) + PortDirection *dir) { ConcretePort *cport = reinterpret_cast(port); cport->setDirection(dir); @@ -707,7 +717,7 @@ ConcreteNetwork::nextObjectId() class ConcreteCellPortIterator1 : public CellPortIterator { public: - explicit ConcreteCellPortIterator1(const ConcreteCell *cell); + ConcreteCellPortIterator1(const ConcreteCell *cell); ~ConcreteCellPortIterator1(); virtual bool hasNext() { return iter_->hasNext(); } virtual Port *next(); @@ -744,7 +754,7 @@ ConcreteNetwork::portIterator(const Cell *cell) const class ConcreteCellPortBitIterator1 : public CellPortIterator { public: - explicit ConcreteCellPortBitIterator1(const ConcreteCell *cell); + ConcreteCellPortBitIterator1(const ConcreteCell *cell); ~ConcreteCellPortBitIterator1(); virtual bool hasNext() { return iter_->hasNext(); } virtual Port *next(); @@ -864,7 +874,7 @@ ConcreteNetwork::toIndex(const Port *port) const Port * ConcreteNetwork::findBusBit(const Port *port, - int index) const + int index) const { const ConcretePort *cport = reinterpret_cast(port); return reinterpret_cast(cport->findBusBit(index)); @@ -872,7 +882,7 @@ ConcreteNetwork::findBusBit(const Port *port, Port * ConcreteNetwork::findMember(const Port *port, - int index) const + int index) const { const ConcretePort *cport = reinterpret_cast(port); return reinterpret_cast(cport->findMember(index)); @@ -891,7 +901,7 @@ ConcreteNetwork::hasMembers(const Port *port) const class ConcretePortMemberIterator1 : public PortMemberIterator { public: - explicit ConcretePortMemberIterator1(const ConcretePort *port); + ConcretePortMemberIterator1(const ConcretePort *port); ~ConcretePortMemberIterator1(); virtual bool hasNext(); virtual Port *next(); @@ -902,7 +912,7 @@ class ConcretePortMemberIterator1 : public PortMemberIterator }; ConcretePortMemberIterator1::ConcretePortMemberIterator1(const ConcretePort * - port) : + port) : iter_(port->memberIterator()), next_(nullptr) { @@ -997,7 +1007,7 @@ ConcreteNetwork::isLeaf(const Instance *instance) const Instance * ConcreteNetwork::findChild(const Instance *parent, - const char *name) const + const char *name) const { const ConcreteInstance *inst = reinterpret_cast(parent); @@ -1006,7 +1016,7 @@ ConcreteNetwork::findChild(const Instance *parent, Pin * ConcreteNetwork::findPin(const Instance *instance, - const char *port_name) const + const char *port_name) const { const ConcreteInstance *inst = reinterpret_cast(instance); @@ -1015,7 +1025,7 @@ ConcreteNetwork::findPin(const Instance *instance, Pin * ConcreteNetwork::findPin(const Instance *instance, - const Port *port) const + const Port *port) const { const ConcreteInstance *inst = reinterpret_cast(instance); @@ -1024,7 +1034,7 @@ ConcreteNetwork::findPin(const Instance *instance, Net * ConcreteNetwork::findNet(const Instance *instance, - const char *net_name) const + const char *net_name) const { const ConcreteInstance *inst = reinterpret_cast(instance); @@ -1033,8 +1043,8 @@ ConcreteNetwork::findNet(const Instance *instance, void ConcreteNetwork::findInstNetsMatching(const Instance *instance, - const PatternMatch *pattern, - NetSeq &matches) const + const PatternMatch *pattern, + NetSeq &matches) const { const ConcreteInstance *inst = reinterpret_cast(instance); @@ -1123,7 +1133,7 @@ ConcreteNetwork::vertexId(const Pin *pin) const void ConcreteNetwork::setVertexId(Pin *pin, - VertexId id) + VertexId id) { ConcretePin *cpin = reinterpret_cast(pin); cpin->setVertexId(id); @@ -1178,13 +1188,13 @@ ConcreteNetwork::instance(const Net *net) const bool ConcreteNetwork::isPower(const Net *net) const { - return constant_nets_[int(LogicValue::one)].hasKey(const_cast(net)); + return constant_nets_[int(LogicValue::one)].contains(const_cast(net)); } bool ConcreteNetwork::isGround(const Net *net) const { - return constant_nets_[int(LogicValue::zero)].hasKey(const_cast(net)); + return constant_nets_[int(LogicValue::zero)].contains(const_cast(net)); } NetPinIterator * @@ -1203,7 +1213,7 @@ ConcreteNetwork::termIterator(const Net *net) const void ConcreteNetwork::mergeInto(Net *net, - Net *into_net) + Net *into_net) { ConcreteNet *cnet = reinterpret_cast(net); ConcreteNet *cinto_net = reinterpret_cast(into_net); @@ -1228,8 +1238,8 @@ ConcreteInstance::cell() const Instance * ConcreteNetwork::makeInstance(Cell *cell, - const char *name, - Instance *parent) + const char *name, + Instance *parent) { ConcreteCell *ccell = reinterpret_cast(cell); return makeConcreteInstance(ccell, name, parent); @@ -1237,16 +1247,16 @@ ConcreteNetwork::makeInstance(Cell *cell, Instance * ConcreteNetwork::makeInstance(LibertyCell *cell, - const char *name, - Instance *parent) + const char *name, + Instance *parent) { return makeConcreteInstance(cell, name, parent); } Instance * ConcreteNetwork::makeConcreteInstance(ConcreteCell *cell, - const char *name, - Instance *parent) + const char *name, + Instance *parent) { ConcreteInstance *cparent = reinterpret_cast(parent); @@ -1269,7 +1279,7 @@ ConcreteNetwork::makePins(Instance *inst) void ConcreteNetwork::replaceCell(Instance *inst, - Cell *cell) + Cell *cell) { ConcreteCell *ccell = reinterpret_cast(cell); int port_count = ccell->portBitCount(); @@ -1297,20 +1307,22 @@ void ConcreteNetwork::deleteInstance(Instance *inst) { ConcreteInstance *cinst = reinterpret_cast(inst); - - // Delete nets first (so children pin deletes are not required). - ConcreteInstanceNetMap::Iterator net_iter(cinst->nets_); - while (net_iter.hasNext()) { - ConcreteNet *cnet = net_iter.next(); - Net *net = reinterpret_cast(cnet); - // Delete terminals connected to net. - NetTermIterator *term_iter = termIterator(net); - while (term_iter->hasNext()) { - ConcreteTerm *term = reinterpret_cast(term_iter->next()); - delete term; + ConcreteInstanceNetMap *nets = cinst->nets_; + if (nets) { + // Delete nets first (so children pin deletes are not required). + for (auto itr = nets->begin(); itr != nets->end(); /*no incr*/) { + auto [name, cnet] = *itr; + Net *net = reinterpret_cast(cnet); + // Delete terminals connected to net. + NetTermIterator *term_iter = termIterator(net); + while (term_iter->hasNext()) { + ConcreteTerm *term = reinterpret_cast(term_iter->next()); + delete term; + } + delete term_iter; + itr = nets->erase(itr); + deleteNet(net); } - delete term_iter; - deleteNet(net); } // Delete children. @@ -1339,8 +1351,8 @@ ConcreteNetwork::deleteInstance(Instance *inst) Pin * ConcreteNetwork::makePin(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { ConcreteInstance *cinst = reinterpret_cast(inst); ConcretePort *cport = reinterpret_cast(port); @@ -1354,7 +1366,7 @@ ConcreteNetwork::makePin(Instance *inst, Term * ConcreteNetwork::makeTerm(Pin *pin, - Net *net) + Net *net) { ConcretePin *cpin = reinterpret_cast(pin); ConcreteNet *cnet = reinterpret_cast(net); @@ -1367,8 +1379,8 @@ ConcreteNetwork::makeTerm(Pin *pin, Pin * ConcreteNetwork::connect(Instance *inst, - LibertyPort *port, - Net *net) + LibertyPort *port, + Net *net) { return connect(inst, reinterpret_cast(port), net); } @@ -1384,8 +1396,8 @@ ConcreteNetwork::setAttribute(Instance *inst, Pin * ConcreteNetwork::connect(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { ConcreteNet *cnet = reinterpret_cast(net); ConcreteInstance *cinst = reinterpret_cast(inst); @@ -1417,7 +1429,7 @@ ConcreteNetwork::connect(Instance *inst, void ConcreteNetwork::connectNetPin(ConcreteNet *cnet, - ConcretePin *cpin) + ConcretePin *cpin) { cnet->addPin(cpin); @@ -1427,9 +1439,9 @@ ConcreteNetwork::connectNetPin(ConcreteNet *cnet, if (isDriver(pin)) { if (cnet->terms_ == nullptr) { Net *net = reinterpret_cast(cnet); - PinSet *drvrs = net_drvr_pin_map_.findKey(net); + PinSet *drvrs = findKey(net_drvr_pin_map_, net); if (drvrs) - drvrs->insert(pin); + drvrs->insert(pin); } else clearNetDrvrPinMap(); @@ -1445,8 +1457,8 @@ ConcreteNetwork::disconnectPin(Pin *pin) if (cterm) { ConcreteNet *cnet = cterm->net_; if (cnet) { - cnet->deleteTerm(cterm); - clearNetDrvrPinMap(); + cnet->deleteTerm(cterm); + clearNetDrvrPinMap(); } cpin->term_ = nullptr; delete cterm; @@ -1462,7 +1474,7 @@ ConcreteNetwork::disconnectPin(Pin *pin) void ConcreteNetwork::disconnectNetPin(ConcreteNet *cnet, - ConcretePin *cpin) + ConcretePin *cpin) { cnet->deletePin(cpin); @@ -1473,9 +1485,9 @@ ConcreteNetwork::disconnectNetPin(ConcreteNet *cnet, // and it is safe to incrementally update the drivers. if (cnet->terms_ == nullptr) { Net *net = reinterpret_cast(cnet); - PinSet *drvrs = net_drvr_pin_map_.findKey(net); + PinSet *drvrs = findKey(net_drvr_pin_map_, net); if (drvrs) - drvrs->erase(pin); + drvrs->erase(pin); } else clearNetDrvrPinMap(); @@ -1498,7 +1510,7 @@ ConcreteNetwork::deletePin(Pin *pin) Net * ConcreteNetwork::makeNet(const char *name, - Instance *parent) + Instance *parent) { ConcreteInstance *cparent = reinterpret_cast(parent); ConcreteNet *net = new ConcreteNet(name, cparent); @@ -1520,7 +1532,7 @@ ConcreteNetwork::deleteNet(Net *net) constant_nets_[int(LogicValue::zero)].erase(net); constant_nets_[int(LogicValue::one)].erase(net); - PinSet *drvrs = net_drvr_pin_map_.findKey(net); + PinSet *drvrs = findKey(net_drvr_pin_map_, net); if (drvrs) { delete drvrs; net_drvr_pin_map_.erase(net); @@ -1541,7 +1553,7 @@ ConcreteNetwork::clearConstantNets() void ConcreteNetwork::addConstantNet(Net *net, - LogicValue value) + LogicValue value) { if (value == LogicValue::zero || value == LogicValue::one) @@ -1552,8 +1564,8 @@ ConstantPinIterator * ConcreteNetwork::constantPinIterator() { return new NetworkConstantPinIterator(this, - constant_nets_[int(LogicValue::zero)], - constant_nets_[int(LogicValue::one)]); + constant_nets_[int(LogicValue::zero)], + constant_nets_[int(LogicValue::one)]); } //////////////////////////////////////////////////////////////// @@ -1561,22 +1573,22 @@ ConcreteNetwork::constantPinIterator() // Optimized version of Network::visitConnectedPins. void ConcreteNetwork::visitConnectedPins(const Net *net, - PinVisitor &visitor, - NetSet &visited_nets) const + PinVisitor &visitor, + NetSet &visited_nets) const { - if (!visited_nets.hasKey(net)) { + if (!visited_nets.contains(net)) { visited_nets.insert(net); // Search up from net terminals. const ConcreteNet *cnet = reinterpret_cast(net); for (ConcreteTerm *term = cnet->terms_; term; term = term->net_next_) { ConcretePin *above_pin = term->pin_; if (above_pin) { - ConcreteNet *above_net = above_pin->net_; - if (above_net) - visitConnectedPins(reinterpret_cast(above_net), - visitor, visited_nets); - else - visitor(reinterpret_cast(above_pin)); + ConcreteNet *above_net = above_pin->net_; + if (above_net) + visitConnectedPins(reinterpret_cast(above_net), + visitor, visited_nets); + else + visitor(reinterpret_cast(above_pin)); } } @@ -1585,10 +1597,10 @@ ConcreteNetwork::visitConnectedPins(const Net *net, visitor(reinterpret_cast(pin)); ConcreteTerm *below_term = pin->term_; if (below_term) { - ConcreteNet *below_net = below_term->net_; - if (below_net) - visitConnectedPins(reinterpret_cast(below_net), - visitor, visited_nets); + ConcreteNet *below_net = below_term->net_; + if (below_net) + visitConnectedPins(reinterpret_cast(below_net), + visitor, visited_nets); } } } @@ -1597,7 +1609,7 @@ ConcreteNetwork::visitConnectedPins(const Net *net, //////////////////////////////////////////////////////////////// ConcreteInstance::ConcreteInstance(const char *name, - ConcreteCell *cell, + ConcreteCell *cell, ConcreteInstance *parent) : name_(stringCopy(name)), id_(ConcreteNetwork::nextObjectId()), @@ -1627,7 +1639,7 @@ Instance * ConcreteInstance::findChild(const char *name) const { if (children_) - return reinterpret_cast(children_->findKey(name)); + return reinterpret_cast(findKey(children_, name)); else return nullptr; } @@ -1661,11 +1673,11 @@ ConcreteInstance::findNet(const char *net_name) const { ConcreteNet *net = nullptr; if (nets_) { - net = nets_->findKey(net_name); + net = findKey(nets_, net_name); // Follow merge pointer to surviving net. if (net) { while (net->mergedInto()) - net = net->mergedInto(); + net = net->mergedInto(); } } return net; @@ -1675,20 +1687,18 @@ void ConcreteInstance::findNetsMatching(const PatternMatch *pattern, NetSeq &matches) const { - if (pattern->hasWildcards()) { - ConcreteInstanceNetMap::Iterator net_iter(nets_); - while (net_iter.hasNext()) { - const char *net_name; - ConcreteNet *cnet; - net_iter.next(net_name, cnet); - if (pattern->match(net_name)) - matches.push_back(reinterpret_cast(cnet)); + if (nets_) { + if (pattern->hasWildcards()) { + for (auto [net_name, cnet] : *nets_) { + if (pattern->match(net_name)) + matches.push_back(reinterpret_cast(cnet)); + } + } + else { + ConcreteNet *cnet = findNet(pattern->pattern()); + if (cnet) + matches.push_back(reinterpret_cast(cnet)); } - } - else { - ConcreteNet *cnet = findNet(pattern->pattern()); - if (cnet) - matches.push_back(reinterpret_cast(cnet)); } } @@ -1762,7 +1772,7 @@ ConcreteInstance::addNet(ConcreteNet *net) void ConcreteInstance::addNet(const char *name, - ConcreteNet *net) + ConcreteNet *net) { if (nets_ == nullptr) nets_ = new ConcreteInstanceNetMap; @@ -1784,8 +1794,8 @@ ConcreteInstance::setCell(ConcreteCell *cell) //////////////////////////////////////////////////////////////// ConcretePin::ConcretePin(ConcreteInstance *instance, - ConcretePort *port, - ConcreteNet *net) : + ConcretePort *port, + ConcreteNet *net) : instance_(instance), port_(port), net_(net), @@ -1821,7 +1831,7 @@ ConcreteTerm::name() const } ConcreteTerm::ConcreteTerm(ConcretePin *pin, - ConcreteNet *net) : + ConcreteNet *net) : pin_(pin), net_(net), id_(ConcreteNetwork::nextObjectId()), @@ -1832,7 +1842,7 @@ ConcreteTerm::ConcreteTerm(ConcretePin *pin, //////////////////////////////////////////////////////////////// ConcreteNet::ConcreteNet(const char *name, - ConcreteInstance *instance) : + ConcreteInstance *instance) : name_(stringCopy(name)), id_(ConcreteNetwork::nextObjectId()), instance_(instance), @@ -1912,9 +1922,9 @@ ConcreteNet::deleteTerm(ConcreteTerm *term) for (ConcreteTerm *net_term=terms_;net_term;net_term=net_term->net_next_) { if (net_term == term) { if (net_prev_term) - net_prev_term->net_next_ = term->net_next_; + net_prev_term->net_next_ = term->net_next_; else - terms_ = term->net_next_; + terms_ = term->net_next_; break; } net_prev_term = net_term; @@ -1923,18 +1933,18 @@ ConcreteNet::deleteTerm(ConcreteTerm *term) //////////////////////////////////////////////////////////////// -typedef Map BindingMap; +using BindingMap = std::map ; // Binding table used for linking/expanding network. class ConcreteBindingTbl { public: - explicit ConcreteBindingTbl(NetworkEdit *network); + ConcreteBindingTbl(NetworkEdit *network); Net *ensureBinding(Net *proto_net, - Instance *parent); + Instance *parent); Net *find(Net *net); void bind(Net *proto_net, - Net *clone_net); + Net *clone_net); private: BindingMap map_; @@ -1943,7 +1953,7 @@ class ConcreteBindingTbl void ConcreteNetwork::setCellNetworkView(Cell *cell, - Instance *inst) + Instance *inst) { cell_network_view_map_[cell] = inst; } @@ -1951,7 +1961,7 @@ ConcreteNetwork::setCellNetworkView(Cell *cell, Instance * ConcreteNetwork::cellNetworkView(Cell *cell) { - return cell_network_view_map_.findKey(cell); + return findKey(cell_network_view_map_, cell); } void @@ -1981,15 +1991,15 @@ ConcreteNetwork::setLinkFunc(LinkNetworkFunc link) bool ConcreteNetwork::linkNetwork(const char *top_cell_name, - bool make_black_boxes, - Report *report) + bool make_black_boxes, + Report *report) { if (link_func_) { clearConstantNets(); deleteTopInstance(); top_instance_ = link_func_(top_cell_name, make_black_boxes); if (top_instance_) - checkNetworkLibertyCorners(); + checkNetworkLibertyScenes(); return top_instance_ != nullptr; } else { @@ -2000,8 +2010,8 @@ ConcreteNetwork::linkNetwork(const char *top_cell_name, Instance * linkReaderNetwork(Cell *top_cell, - bool, Report *, - NetworkReader *network) + bool, Report *, + NetworkReader *network) { Instance *view = network->cellNetworkView(top_cell); if (view) { @@ -2023,13 +2033,13 @@ linkReaderNetwork(Cell *top_cell, static void makeChildNetwork(Instance *proto, - Instance *parent, - ConcreteBindingTbl *parent_bindings, - NetworkReader *network) + Instance *parent, + ConcreteBindingTbl *parent_bindings, + NetworkReader *network) { Cell *proto_cell = network->cell(proto); Instance *clone = network->makeInstance(proto_cell, network->name(proto), - parent); + parent); if (network->isLeaf(proto_cell)) makeClonePins(proto, clone, nullptr, nullptr, parent, parent_bindings, network); else { @@ -2037,12 +2047,12 @@ makeChildNetwork(Instance *proto, ConcreteBindingTbl bindings(network); Instance *clone_view = network->cellNetworkView(proto_cell); makeClonePins(proto, clone, clone_view, &bindings, parent, - parent_bindings, network); + parent_bindings, network); if (clone_view) { InstanceChildIterator *child_iter = network->childIterator(clone_view); while (child_iter->hasNext()) { - Instance *child = child_iter->next(); - makeChildNetwork(child, clone, &bindings, network); + Instance *child = child_iter->next(); + makeChildNetwork(child, clone, &bindings, network); } delete child_iter; } @@ -2051,12 +2061,12 @@ makeChildNetwork(Instance *proto, static void makeClonePins(Instance *proto, - Instance *clone, - Instance *clone_view, - ConcreteBindingTbl *bindings, - Instance *parent, - ConcreteBindingTbl *parent_bindings, - NetworkReader *network) + Instance *clone, + Instance *clone_view, + ConcreteBindingTbl *bindings, + Instance *parent, + ConcreteBindingTbl *parent_bindings, + NetworkReader *network) { InstancePinIterator *proto_pin_iter = network->pinIterator(proto); while (proto_pin_iter->hasNext()) { @@ -2072,7 +2082,7 @@ makeClonePins(Instance *proto, Net *clone_proto_net = network->net(clone_proto_pin); Net *clone_child_net = nullptr; if (clone_proto_net) - clone_child_net = bindings->ensureBinding(clone_proto_net, clone); + clone_child_net = bindings->ensureBinding(clone_proto_net, clone); network->makeTerm(clone_pin, clone_child_net); } } @@ -2092,7 +2102,7 @@ ConcreteBindingTbl::ConcreteBindingTbl(NetworkEdit *network) : Net * ConcreteBindingTbl::find(Net *proto_net) { - ConcreteNet *net = reinterpret_cast(map_.findKey(proto_net)); + ConcreteNet *net = reinterpret_cast(findKey(map_, proto_net)); while (net && net->mergedInto()) net = net->mergedInto(); return reinterpret_cast(net); @@ -2100,14 +2110,14 @@ ConcreteBindingTbl::find(Net *proto_net) void ConcreteBindingTbl::bind(Net *proto_net, - Net *net) + Net *net) { map_[proto_net] = net; } Net * ConcreteBindingTbl::ensureBinding(Net *proto_net, - Instance *parent) + Instance *parent) { Net *net = find(proto_net); if (net == nullptr) { diff --git a/network/HpinDrvrLoad.cc b/network/HpinDrvrLoad.cc index 944777390..a6f1bb1f2 100644 --- a/network/HpinDrvrLoad.cc +++ b/network/HpinDrvrLoad.cc @@ -25,39 +25,41 @@ #include "HpinDrvrLoad.hh" #include +#include +#include "ContainerHelpers.hh" #include "Network.hh" namespace sta { -typedef Set HpinDrvrLoads; +typedef std::set HpinDrvrLoads; static void visitPinsAboveNet2(const Pin *hpin, - Net *above_net, - NetSet &visited, - HpinDrvrLoads &above_drvrs, - HpinDrvrLoads &above_loads, - PinSet *hpin_path, - const Network *network); + Net *above_net, + NetSet &visited, + HpinDrvrLoads &above_drvrs, + HpinDrvrLoads &above_loads, + PinSet *hpin_path, + const Network *network); static void visitPinsBelowNet2(const Pin *hpin, - Net *above_net, - Net *below_net, - NetSet &visited, - HpinDrvrLoads &below_drvrs, - HpinDrvrLoads &below_loads, - PinSet *hpin_path, - const Network *network); + Net *above_net, + Net *below_net, + NetSet &visited, + HpinDrvrLoads &below_drvrs, + HpinDrvrLoads &below_loads, + PinSet *hpin_path, + const Network *network); static void visitHpinDrvrLoads(HpinDrvrLoads drvrs, - HpinDrvrLoads loads, - HpinDrvrLoadVisitor *visitor); + HpinDrvrLoads loads, + HpinDrvrLoadVisitor *visitor); void visitHpinDrvrLoads(const Pin *pin, - const Network *network, - HpinDrvrLoadVisitor *visitor) + const Network *network, + HpinDrvrLoadVisitor *visitor) { NetSet visited(network); HpinDrvrLoads above_drvrs; @@ -66,8 +68,8 @@ visitHpinDrvrLoads(const Pin *pin, Net *above_net = network->net(pin); if (above_net) { visitPinsAboveNet2(pin, above_net, visited, - above_drvrs, above_loads, - &hpin_path, network); + above_drvrs, above_loads, + &hpin_path, network); } // Search down from hpin terminal. @@ -78,8 +80,8 @@ visitHpinDrvrLoads(const Pin *pin, Net *below_net = network->net(term); if (below_net) visitPinsBelowNet2(pin, above_net, below_net, visited, - below_drvrs, below_loads, - &hpin_path, network); + below_drvrs, below_loads, + &hpin_path, network); } if (network->isHierarchical(pin)) { visitHpinDrvrLoads(above_drvrs, below_loads, visitor); @@ -102,20 +104,20 @@ visitHpinDrvrLoads(const Pin *pin, visitHpinDrvrLoads(above_drvrs, loads, visitor); } } - above_drvrs.deleteContents(); - above_loads.deleteContents(); - below_drvrs.deleteContents(); - below_loads.deleteContents(); + deleteContents(above_drvrs); + deleteContents(above_loads); + deleteContents(below_drvrs); + deleteContents(below_loads); } static void visitPinsAboveNet2(const Pin *hpin, - Net *above_net, - NetSet &visited, - HpinDrvrLoads &above_drvrs, - HpinDrvrLoads &above_loads, - PinSet *hpin_path, - const Network *network) + Net *above_net, + NetSet &visited, + HpinDrvrLoads &above_drvrs, + HpinDrvrLoads &above_loads, + PinSet *hpin_path, + const Network *network) { visited.insert(above_net); // Visit above net pins. @@ -124,25 +126,25 @@ visitPinsAboveNet2(const Pin *hpin, const Pin *above_pin = pin_iter->next(); if (above_pin != hpin) { if (network->isDriver(above_pin)) { - HpinDrvrLoad *drvr = new HpinDrvrLoad(above_pin, nullptr, - hpin_path, nullptr); - above_drvrs.insert(drvr); + HpinDrvrLoad *drvr = new HpinDrvrLoad(above_pin, nullptr, + hpin_path, nullptr); + above_drvrs.insert(drvr); } if (network->isLoad(above_pin)) { - HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, above_pin, - nullptr, hpin_path); - above_loads.insert(load); + HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, above_pin, + nullptr, hpin_path); + above_loads.insert(load); } Term *above_term = network->term(above_pin); if (above_term) { - Net *above_net1 = network->net(above_term); - if (above_net1 && !visited.hasKey(above_net1)) { - hpin_path->insert(above_pin); - visitPinsAboveNet2(above_pin, above_net1, visited, - above_drvrs, above_loads, - hpin_path, network); - hpin_path->erase(above_pin); - } + Net *above_net1 = network->net(above_term); + if (above_net1 && !visited.contains(above_net1)) { + hpin_path->insert(above_pin); + visitPinsAboveNet2(above_pin, above_net1, visited, + above_drvrs, above_loads, + hpin_path, network); + hpin_path->erase(above_pin); + } } } } @@ -154,25 +156,25 @@ visitPinsAboveNet2(const Pin *hpin, Term *term = term_iter->next(); Pin *above_pin = network->pin(term); if (above_pin - && above_pin != hpin) { + && above_pin != hpin) { Net *above_net1 = network->net(above_pin); - if (above_net1 && !visited.hasKey(above_net1)) { - hpin_path->insert(above_pin); - visitPinsAboveNet2(above_pin, above_net1, visited, - above_drvrs, above_loads, - hpin_path, network); - hpin_path->erase(above_pin); + if (above_net1 && !visited.contains(above_net1)) { + hpin_path->insert(above_pin); + visitPinsAboveNet2(above_pin, above_net1, visited, + above_drvrs, above_loads, + hpin_path, network); + hpin_path->erase(above_pin); } if (network->isDriver(above_pin)) { - HpinDrvrLoad *drvr = new HpinDrvrLoad(above_pin, nullptr, - hpin_path, nullptr); - above_drvrs.insert(drvr); + HpinDrvrLoad *drvr = new HpinDrvrLoad(above_pin, nullptr, + hpin_path, nullptr); + above_drvrs.insert(drvr); } if (network->isLoad(above_pin)) { - HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, above_pin, - nullptr, hpin_path); - above_loads.insert(load); + HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, above_pin, + nullptr, hpin_path); + above_loads.insert(load); } } } @@ -181,13 +183,13 @@ visitPinsAboveNet2(const Pin *hpin, static void visitPinsBelowNet2(const Pin *hpin, - Net *above_net, - Net *below_net, - NetSet &visited, - HpinDrvrLoads &below_drvrs, - HpinDrvrLoads &below_loads, - PinSet *hpin_path, - const Network *network) + Net *above_net, + Net *below_net, + NetSet &visited, + HpinDrvrLoads &below_drvrs, + HpinDrvrLoads &below_loads, + PinSet *hpin_path, + const Network *network) { visited.insert(below_net); // Visit below net pins. @@ -195,32 +197,32 @@ visitPinsBelowNet2(const Pin *hpin, while (pin_iter->hasNext()) { const Pin *below_pin = pin_iter->next(); if (below_pin != hpin) { - if (above_net && !visited.hasKey(above_net)) - visitPinsAboveNet2(below_pin, above_net, - visited, below_drvrs, below_loads, - hpin_path, network); + if (above_net && !visited.contains(above_net)) + visitPinsAboveNet2(below_pin, above_net, + visited, below_drvrs, below_loads, + hpin_path, network); if (network->isDriver(below_pin)) { - HpinDrvrLoad *drvr = new HpinDrvrLoad(below_pin, nullptr, - hpin_path, nullptr); - below_drvrs.insert(drvr); + HpinDrvrLoad *drvr = new HpinDrvrLoad(below_pin, nullptr, + hpin_path, nullptr); + below_drvrs.insert(drvr); } if (network->isLoad(below_pin)) { - HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, below_pin, - nullptr, hpin_path); - below_loads.insert(load); + HpinDrvrLoad *load = new HpinDrvrLoad(nullptr, below_pin, + nullptr, hpin_path); + below_loads.insert(load); } if (network->isHierarchical(below_pin)) { - Term *term = network->term(below_pin); - if (term) { - Net *below_net1 = network->net(term); - if (below_net1 && !visited.hasKey(below_net1)) { - hpin_path->insert(below_pin); - visitPinsBelowNet2(below_pin, below_net, below_net1, visited, - below_drvrs, below_loads, - hpin_path, network); - hpin_path->erase(below_pin); - } - } + Term *term = network->term(below_pin); + if (term) { + Net *below_net1 = network->net(term); + if (below_net1 && !visited.contains(below_net1)) { + hpin_path->insert(below_pin); + visitPinsBelowNet2(below_pin, below_net, below_net1, visited, + below_drvrs, below_loads, + hpin_path, network); + hpin_path->erase(below_pin); + } + } } } } @@ -232,14 +234,14 @@ visitPinsBelowNet2(const Pin *hpin, Term *term = term_iter->next(); Pin *above_pin = network->pin(term); if (above_pin - && above_pin != hpin) { + && above_pin != hpin) { Net *above_net1 = network->net(above_pin); - if (above_net1 && !visited.hasKey(above_net1)) { - hpin_path->insert(above_pin); - visitPinsAboveNet2(above_pin, above_net1, - visited, below_drvrs, below_loads, - hpin_path, network); - hpin_path->erase(above_pin); + if (above_net1 && !visited.contains(above_net1)) { + hpin_path->insert(above_pin); + visitPinsAboveNet2(above_pin, above_net1, + visited, below_drvrs, below_loads, + hpin_path, network); + hpin_path->erase(above_pin); } } } @@ -248,19 +250,15 @@ visitPinsBelowNet2(const Pin *hpin, static void visitHpinDrvrLoads(HpinDrvrLoads drvrs, - HpinDrvrLoads loads, - HpinDrvrLoadVisitor *visitor) + HpinDrvrLoads loads, + HpinDrvrLoadVisitor *visitor) { - HpinDrvrLoads::Iterator drvr_iter(drvrs); - while (drvr_iter.hasNext()) { - HpinDrvrLoad *drvr = drvr_iter.next(); - HpinDrvrLoads::Iterator load_iter(loads); - while (load_iter.hasNext()) { - HpinDrvrLoad *load = load_iter.next(); + for (HpinDrvrLoad *drvr : drvrs) { + for (HpinDrvrLoad *load : loads) { HpinDrvrLoad clone(drvr->drvr(), - load->load(), - drvr->hpinsFromDrvr(), - load->hpinsToLoad()); + load->load(), + drvr->hpinsFromDrvr(), + load->hpinsToLoad()); visitor->visit(&clone); } } @@ -269,9 +267,9 @@ visitHpinDrvrLoads(HpinDrvrLoads drvrs, //////////////////////////////////////////////////////////////// HpinDrvrLoad::HpinDrvrLoad(const Pin *drvr, - const Pin *load, - PinSet *hpins_from_drvr, - PinSet *hpins_to_load) : + const Pin *load, + PinSet *hpins_from_drvr, + PinSet *hpins_to_load) : drvr_(drvr), load_(load), hpins_from_drvr_(hpins_from_drvr ? new PinSet(*hpins_from_drvr) : nullptr), @@ -280,7 +278,7 @@ HpinDrvrLoad::HpinDrvrLoad(const Pin *drvr, } HpinDrvrLoad::HpinDrvrLoad(const Pin *drvr, - const Pin *load) : + const Pin *load) : drvr_(drvr), load_(load) { @@ -296,19 +294,13 @@ void HpinDrvrLoad::report(const Network *network) { printf("%s -> %s: ", - drvr_ ? network->pathName(drvr_) : "-", - load_ ? network->pathName(load_) : "-"); - PinSet::Iterator pin_iter(hpins_from_drvr_); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + drvr_ ? network->pathName(drvr_) : "-", + load_ ? network->pathName(load_) : "-"); + for (const Pin *pin : *hpins_from_drvr_) printf("%s ", network->pathName(pin)); - } printf("* "); - PinSet::Iterator pin_iter2(hpins_to_load_); - while (pin_iter2.hasNext()) { - const Pin *pin = pin_iter2.next(); + for (const Pin *pin : *hpins_to_load_) printf("%s ", network->pathName(pin)); - } printf("\n"); } @@ -320,7 +312,7 @@ HpinDrvrLoad::setDrvr(const Pin *drvr) bool HpinDrvrLoadLess::operator()(const HpinDrvrLoad *drvr_load1, - const HpinDrvrLoad *drvr_load2) const + const HpinDrvrLoad *drvr_load2) const { const Pin *load1 = drvr_load1->load(); const Pin *load2 = drvr_load2->load(); diff --git a/network/Network.cc b/network/Network.cc index 578c06645..8e03a7045 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -25,11 +25,14 @@ #include "Network.hh" +#include + +#include "ContainerHelpers.hh" #include "StringUtil.hh" #include "PatternMatch.hh" #include "Liberty.hh" #include "PortDirection.hh" -#include "Corner.hh" +#include "Scene.hh" #include "ParseBus.hh" namespace sta { @@ -45,7 +48,7 @@ Network::Network() : Network::~Network() { - net_drvr_pin_map_.deleteContents(); + deleteContents(net_drvr_pin_map_); } void @@ -105,8 +108,8 @@ Network::findPortsMatching(const Cell *cell, else { // bus[0] Port *port_bit = findBusBit(port, from); - if (port_bit != nullptr) - matches.push_back(port_bit); + if (port_bit != nullptr) + matches.push_back(port_bit); } } } @@ -165,9 +168,9 @@ Network::setDefaultLibertyLibrary(LibertyLibrary *library) } void -Network::checkLibertyCorners() +Network::checkLibertyScenes() { - if (corners_->count() > 1) { + if (multiScene()) { LibertyLibraryIterator *lib_iter = libertyLibraryIterator(); LibertyCellSet cells; while (lib_iter->hasNext()) { @@ -182,14 +185,14 @@ Network::checkLibertyCorners() delete lib_iter; for (LibertyCell *cell : cells) - LibertyLibrary::checkCorners(cell, corners_, report_); + LibertyLibrary::checkScenes(cell, scenes_, report_); } } void -Network::checkNetworkLibertyCorners() +Network::checkNetworkLibertyScenes() { - if (corners_->count() > 1) { + if (multiScene()) { LibertyCellSet network_cells; LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator(); while (leaf_iter->hasNext()) { @@ -201,7 +204,7 @@ Network::checkNetworkLibertyCorners() delete leaf_iter; for (LibertyCell *cell : network_cells) - LibertyLibrary::checkCorners(cell, corners_, report_); + LibertyLibrary::checkScenes(cell, scenes_, report_); } } @@ -239,16 +242,16 @@ Network::libertyPort(const Pin *pin) const bool Network::busIndexInRange(const Port *port, - int index) + int index) { int from_index = fromIndex(port); int to_index = toIndex(port); return (from_index <= to_index - && index <= to_index - && index >= from_index) + && index <= to_index + && index >= from_index) || (from_index > to_index - && index >= to_index - && index <= from_index); + && index >= to_index + && index <= from_index); } bool @@ -263,11 +266,8 @@ Network::pathName(const Instance *instance) const InstanceSeq inst_path; path(instance, inst_path); size_t name_length = 0; - InstanceSeq::Iterator path_iter1(inst_path); - while (path_iter1.hasNext()) { - const Instance *inst = path_iter1.next(); + 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. @@ -287,14 +287,14 @@ Network::pathName(const Instance *instance) const bool Network::pathNameLess(const Instance *inst1, - const Instance *inst2) const + const Instance *inst2) const { return pathNameCmp(inst1, inst2) < 0; } int Network::pathNameCmp(const Instance *inst1, - const Instance *inst2) const + const Instance *inst2) const { if (inst1 == nullptr && inst2) return -1; @@ -312,7 +312,7 @@ Network::pathNameCmp(const Instance *inst1, const Instance *inst2 = path2.back(); int cmp = strcmp(name(inst1), name(inst2)); if (cmp != 0) - return cmp; + return cmp; path1.pop_back(); path2.pop_back(); } @@ -327,8 +327,8 @@ Network::pathNameCmp(const Instance *inst1, void Network::path(const Instance *inst, - // Return value. - InstanceSeq &path) const + // Return value. + InstanceSeq &path) const { while (!isTopInstance(inst)) { path.push_back(inst); @@ -344,7 +344,7 @@ Network::isTopInstance(const Instance *inst) const bool Network::isInside(const Instance *inst, - const Instance *hier_inst) const + const Instance *hier_inst) const { while (inst) { if (inst == hier_inst) @@ -398,14 +398,14 @@ Network::pathName(const Pin *pin) const bool Network::pathNameLess(const Pin *pin1, - const Pin *pin2) const + const Pin *pin2) const { return pathNameCmp(pin1, pin2) < 0; } int Network::pathNameCmp(const Pin *pin1, - const Pin *pin2) const + const Pin *pin2) const { int inst_cmp = pathNameCmp(instance(pin1), instance(pin2)); if (inst_cmp == 0) @@ -416,7 +416,7 @@ Network::pathNameCmp(const Pin *pin1, bool Network::isInside(const Net *net, - const Instance *hier_inst) const + const Instance *hier_inst) const { return isInside(instance(net), hier_inst); } @@ -441,21 +441,21 @@ Network::isTopLevelPort(const Pin *pin) const bool Network::isInside(const Pin *pin, - const Pin *hier_pin) const + const Pin *hier_pin) const { return isInside(pin, instance(hier_pin)); } bool Network::isInside(const Pin *pin, - const Instance *hier_inst) const + const Instance *hier_inst) const { return isInside(instance(pin), hier_inst); } bool Network::pinLess(const Pin *pin1, - const Pin *pin2) const + const Pin *pin2) const { return pathNameLess(pin1, pin2); } @@ -486,14 +486,14 @@ Network::pathName(const Net *net) const bool Network::pathNameLess(const Net *net1, - const Net *net2) const + const Net *net2) const { return pathNameCmp(net1, net2) < 0; } int Network::pathNameCmp(const Net *net1, - const Net *net2) const + const Net *net2) const { int inst_cmp = pathNameCmp(instance(net1), instance(net2)); if (inst_cmp == 0) @@ -514,8 +514,8 @@ Network::highestNetAbove(Net *net) const if (above_pin) { Net *above_net = this->net(above_pin); if (above_net) { - highest_net = highestNetAbove(above_net); - break; + highest_net = highestNetAbove(above_net); + break; } } } @@ -530,13 +530,11 @@ Network::highestConnectedNet(Net *net) const connectedNets(net, &nets); const Net *highest_net = net; int highest_level = hierarchyLevel(net); - NetSet::Iterator net_iter(nets); - while (net_iter.hasNext()) { - const Net *net1 = net_iter.next(); + for (const Net *net1 : nets) { int level = hierarchyLevel(net1); if (level < highest_level - || (level == highest_level - && stringLess(pathName(net1), pathName(highest_net)))) { + || (level == highest_level + && stringLess(pathName(net1), pathName(highest_net)))) { highest_net = net1; highest_level = level; } @@ -546,9 +544,9 @@ Network::highestConnectedNet(Net *net) const void Network::connectedNets(Net *net, - NetSet *nets) const + NetSet *nets) const { - if (!nets->hasKey(net)) { + if (!nets->contains(net)) { nets->insert(net); // Search up from net terminals. NetTermIterator *term_iter = termIterator(net); @@ -556,9 +554,9 @@ Network::connectedNets(Net *net, Term *term = term_iter->next(); Pin *above_pin = pin(term); if (above_pin) { - Net *above_net = this->net(above_pin); - if (above_net) - connectedNets(above_net, nets); + Net *above_net = this->net(above_pin); + if (above_net) + connectedNets(above_net, nets); } } delete term_iter; @@ -569,9 +567,9 @@ Network::connectedNets(Net *net, const Pin *pin1 = pin_iter->next(); Term *below_term = term(pin1); if (below_term) { - Net *below_net = this->net(below_term); - if (below_net) - connectedNets(below_net, nets); + Net *below_net = this->net(below_term); + if (below_net) + connectedNets(below_net, nets); } } delete pin_iter; @@ -580,7 +578,7 @@ Network::connectedNets(Net *net, void Network::connectedNets(const Pin *pin, - NetSet *nets) const + NetSet *nets) const { Net *net = this->net(pin); if (net) @@ -590,7 +588,7 @@ Network::connectedNets(const Pin *pin, if (term) { Net *below_net = this->net(term); if (below_net) - connectedNets(below_net, nets); + connectedNets(below_net, nets); } } } @@ -605,8 +603,8 @@ Network::hierarchyLevel(const Net *net) const if (pin) { Net *above_net = network_->net(pin); if (above_net) { - delete term_iter; - return hierarchyLevel(above_net) + 1; + delete term_iter; + return hierarchyLevel(above_net) + 1; } } } @@ -696,7 +694,7 @@ Network::findInstance(const char *path_name) const Instance * Network::findInstanceRelative(const Instance *inst, - const char *path_name) const + const char *path_name) const { char *first, *tail; pathNameFirst(path_name, first, tail); @@ -707,11 +705,11 @@ Network::findInstanceRelative(const Instance *inst, char *next_tail; pathNameFirst(tail, first, next_tail); if (first) { - inst1 = findChild(inst1, first); - stringDelete(first); + inst1 = findChild(inst1, first); + stringDelete(first); } else - inst1 = findChild(inst1, tail); + inst1 = findChild(inst1, tail); stringDelete(tail); tail = next_tail; } @@ -724,7 +722,7 @@ Network::findInstanceRelative(const Instance *inst, InstanceSeq Network::findInstancesMatching(const Instance *context, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { InstanceSeq matches; if (pattern->hasWildcards()) { @@ -744,9 +742,9 @@ Network::findInstancesMatching(const Instance *context, void Network::findInstancesMatching1(const Instance *context, - size_t context_name_length, - const PatternMatch *pattern, - InstanceSeq &matches) const + size_t context_name_length, + const PatternMatch *pattern, + InstanceSeq &matches) const { InstanceChildIterator *child_iter = childIterator(context); while (child_iter->hasNext()) { @@ -764,7 +762,7 @@ Network::findInstancesMatching1(const Instance *context, InstanceSeq Network::findInstancesHierMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { InstanceSeq matches; findInstancesHierMatching1(instance, pattern, matches); @@ -789,15 +787,15 @@ Network::findInstancesHierMatching1(const Instance *instance, void Network::findChildrenMatching(const Instance *parent, - const PatternMatch *pattern, - InstanceSeq &matches) const + const PatternMatch *pattern, + InstanceSeq &matches) const { if (pattern->hasWildcards()) { InstanceChildIterator *child_iter = childIterator(parent); while (child_iter->hasNext()) { Instance *child = child_iter->next(); if (pattern->match(name(child))) - matches.push_back(child); + matches.push_back(child); } delete child_iter; } @@ -816,7 +814,7 @@ Network::findPin(const char *path_name) const Pin * Network::findPinRelative(const Instance *inst, - const char *path_name) const + const char *path_name) const { char *inst_path, *port_name; pathNameLast(path_name, inst_path, port_name); @@ -839,7 +837,7 @@ Network::findPinRelative(const Instance *inst, Pin * Network::findPinLinear(const Instance *instance, - const char *port_name) const + const char *port_name) const { InstancePinIterator *pin_iter = pinIterator(instance); while (pin_iter->hasNext()) { @@ -855,14 +853,14 @@ Network::findPinLinear(const Instance *instance, Pin * Network::findPin(const Instance *instance, - const Port *port) const + const Port *port) const { return findPin(instance, name(port)); } Pin * Network::findPin(const Instance *instance, - const LibertyPort *port) const + const LibertyPort *port) const { return findPin(instance, port->name()); } @@ -875,7 +873,7 @@ Network::findNet(const char *path_name) const Net * Network::findNetRelative(const Instance *inst, - const char *path_name) const + const char *path_name) const { char *inst_path, *net_name; pathNameLast(path_name, inst_path, net_name); @@ -898,7 +896,7 @@ Network::findNetRelative(const Instance *inst, Net * Network::findNetLinear(const Instance *instance, - const char *net_name) const + const char *net_name) const { InstanceNetIterator *net_iter = netIterator(instance); while (net_iter->hasNext()) { @@ -914,7 +912,7 @@ Network::findNetLinear(const Instance *instance, NetSeq Network::findNetsMatching(const Instance *context, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { NetSeq matches; findNetsMatching(context, pattern, matches); @@ -923,7 +921,7 @@ Network::findNetsMatching(const Instance *context, void Network::findNetsMatching(const Instance *context, - const PatternMatch *pattern, + const PatternMatch *pattern, NetSeq &matches) const { if (pattern->hasWildcards()) { @@ -933,11 +931,8 @@ Network::findNetsMatching(const Instance *context, PatternMatch inst_pattern(inst_path, pattern); PatternMatch net_pattern(net_name, pattern); InstanceSeq insts = findInstancesMatching(context, &inst_pattern); - InstanceSeq::Iterator inst_iter(insts); - while (inst_iter.hasNext()) { - const Instance *inst = inst_iter.next(); - findNetsMatching(inst, &net_pattern, matches); - } + for (const Instance *inst : insts) + findNetsMatching(inst, &net_pattern, matches); stringDelete(inst_path); stringDelete(net_name); } @@ -954,7 +949,7 @@ Network::findNetsMatching(const Instance *context, NetSeq Network::findNetsHierMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { NetSeq matches; findNetsHierMatching(instance, pattern, matches); @@ -963,7 +958,7 @@ Network::findNetsHierMatching(const Instance *instance, void Network::findNetsHierMatching(const Instance *instance, - const PatternMatch *pattern, + const PatternMatch *pattern, NetSeq &matches) const { findInstNetsMatching(instance, pattern, matches); @@ -977,7 +972,7 @@ Network::findNetsHierMatching(const Instance *instance, NetSeq Network::findNetsMatchingLinear(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { NetSeq matches; InstanceNetIterator *net_iter = netIterator(instance); @@ -992,7 +987,7 @@ Network::findNetsMatchingLinear(const Instance *instance, PinSeq Network::findPinsMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { PinSeq matches; if (pattern->hasWildcards()) { @@ -1002,11 +997,8 @@ Network::findPinsMatching(const Instance *instance, PatternMatch inst_pattern(inst_path, pattern); PatternMatch port_pattern(port_name, pattern); InstanceSeq insts = findInstancesMatching(instance, &inst_pattern); - InstanceSeq::Iterator inst_iter(insts); - while (inst_iter.hasNext()) { - const Instance *inst = inst_iter.next(); - findInstPinsMatching(inst, &port_pattern, matches); - } + for (const Instance *inst : insts) + findInstPinsMatching(inst, &port_pattern, matches); stringDelete(inst_path); stringDelete(port_name); } @@ -1024,7 +1016,7 @@ Network::findPinsMatching(const Instance *instance, PinSeq Network::findPinsHierMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { PinSeq matches; findPinsHierMatching(instance, pattern, matches); @@ -1033,7 +1025,7 @@ Network::findPinsHierMatching(const Instance *instance, void Network::findPinsHierMatching(const Instance *instance, - const PatternMatch *pattern, + const PatternMatch *pattern, // Return value. PinSeq &matches) const { @@ -1048,9 +1040,9 @@ Network::findPinsHierMatching(const Instance *instance, void Network::findInstPinsHierMatching(const Instance *instance, - const PatternMatch *pattern, - // Return value. - PinSeq &matches) const + const PatternMatch *pattern, + // Return value. + PinSeq &matches) const { string inst_name = name(instance); InstancePinIterator *pin_iter = pinIterator(instance); @@ -1066,15 +1058,15 @@ Network::findInstPinsHierMatching(const Instance *instance, void Network::findInstPinsMatching(const Instance *instance, - const PatternMatch *pattern, - PinSeq &matches) const + const PatternMatch *pattern, + PinSeq &matches) const { if (pattern->hasWildcards()) { InstancePinIterator *pin_iter = pinIterator(instance); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (pattern->match(name(pin))) - matches.push_back(pin); + matches.push_back(pin); } delete pin_iter; } @@ -1087,10 +1079,10 @@ Network::findInstPinsMatching(const Instance *instance, void Network::location(const Pin *, - // Return values. - double &x, - double &y, - bool &exists) const + // Return values. + double &x, + double &y, + bool &exists) const { x = y = 0.0; exists = false; @@ -1225,13 +1217,13 @@ Network::setPathEscape(char escape) //////////////////////////////////////////////////////////////// -typedef Vector InstanceChildIteratorSeq; +typedef std::vector InstanceChildIteratorSeq; class LeafInstanceIterator1 : public LeafInstanceIterator { public: LeafInstanceIterator1(const Instance *inst, - const Network *network); + const Network *network); bool hasNext() { return next_; } Instance *next(); @@ -1246,7 +1238,7 @@ class LeafInstanceIterator1 : public LeafInstanceIterator }; LeafInstanceIterator1::LeafInstanceIterator1(const Instance *inst, - const Network *network) : + const Network *network) : network_(network), child_iter_(network->childIterator(inst)), next_(nullptr) @@ -1305,7 +1297,7 @@ Network::leafInstanceIterator(const Instance *hier_inst) const void Network::visitConnectedPins(const Pin *pin, - PinVisitor &visitor) const + PinVisitor &visitor) const { NetSet visited_nets(network_); Net *pin_net = net(pin); @@ -1326,7 +1318,7 @@ Network::visitConnectedPins(const Pin *pin, void Network::visitConnectedPins(const Net *net, - PinVisitor &visitor) const + PinVisitor &visitor) const { NetSet visited_nets(this); visitConnectedPins(net, visitor, visited_nets); @@ -1334,10 +1326,10 @@ Network::visitConnectedPins(const Net *net, void Network::visitConnectedPins(const Net *net, - PinVisitor &visitor, - NetSet &visited_nets) const + PinVisitor &visitor, + NetSet &visited_nets) const { - if (!visited_nets.hasKey(net)) { + if (!visited_nets.contains(net)) { visited_nets.insert(net); // Search up from net terminals. NetTermIterator *term_iter = termIterator(net); @@ -1345,11 +1337,11 @@ Network::visitConnectedPins(const Net *net, Term *term = term_iter->next(); Pin *above_pin = pin(term); if (above_pin) { - Net *above_net = this->net(above_pin); - if (above_net) - visitConnectedPins(above_net, visitor, visited_nets); - else - visitor(above_pin); + Net *above_net = this->net(above_pin); + if (above_net) + visitConnectedPins(above_net, visitor, visited_nets); + else + visitor(above_pin); } } delete term_iter; @@ -1361,9 +1353,9 @@ Network::visitConnectedPins(const Net *net, visitor(pin); Term *below_term = term(pin); if (below_term) { - Net *below_net = this->net(below_term); - if (below_net) - visitConnectedPins(below_net, visitor, visited_nets); + Net *below_net = this->net(below_term); + if (below_net) + visitConnectedPins(below_net, visitor, visited_nets); } } delete pin_iter; @@ -1375,41 +1367,43 @@ Network::visitConnectedPins(const Net *net, class ConnectedPinIterator1 : public ConnectedPinIterator { public: - explicit ConnectedPinIterator1(PinSet *pins); + ConnectedPinIterator1(PinSet *pins); virtual ~ConnectedPinIterator1(); virtual bool hasNext(); virtual const Pin *next(); protected: - PinSet::Iterator pin_iter_; + PinSet *pins_; + PinSet::iterator pin_iter_; }; ConnectedPinIterator1::ConnectedPinIterator1(PinSet *pins) : - pin_iter_(pins) + pins_(pins), + pin_iter_(pins_->begin()) { } ConnectedPinIterator1::~ConnectedPinIterator1() { - delete pin_iter_.container(); + delete pins_; } bool ConnectedPinIterator1::hasNext() { - return pin_iter_.hasNext(); + return pin_iter_ != pins_->end(); } const Pin * ConnectedPinIterator1::next() { - return pin_iter_.next(); + return *pin_iter_++; } class FindConnectedPins : public PinVisitor { public: - explicit FindConnectedPins(PinSet *pins); + FindConnectedPins(PinSet *pins); virtual void operator()(const Pin *pin); protected: @@ -1462,7 +1456,7 @@ Network::connectedPinIterator(const Pin *pin) const bool Network::isConnected(const Net *net, - const Pin *pin) const + const Pin *pin) const { if (this->net(pin) == net) return true; @@ -1474,10 +1468,10 @@ Network::isConnected(const Net *net, bool Network::isConnected(const Net *net, - const Pin *pin, - NetSet &nets) const + const Pin *pin, + NetSet &nets) const { - if (!nets.hasKey(net)) { + if (!nets.contains(net)) { nets.insert(net); // Search up from net terminals. NetTermIterator *term_iter = termIterator(net); @@ -1485,17 +1479,17 @@ Network::isConnected(const Net *net, Term *term = term_iter->next(); Pin *above_pin = this->pin(term); if (above_pin) { - if (above_pin == pin) { - delete term_iter; - return true; - } - else { - Net *above_net = this->net(above_pin); - if (above_net && isConnected(above_net, pin, nets)) { - delete term_iter; - return true; - } - } + if (above_pin == pin) { + delete term_iter; + return true; + } + else { + Net *above_net = this->net(above_pin); + if (above_net && isConnected(above_net, pin, nets)) { + delete term_iter; + return true; + } + } } } delete term_iter; @@ -1505,18 +1499,18 @@ Network::isConnected(const Net *net, while (pin_iter->hasNext()) { const Pin *pin1 = pin_iter->next(); if (pin1 == pin) { - delete pin_iter; - return true; + delete pin_iter; + return true; } else { - Term *below_term = term(pin1); - if (below_term) { - Net *below_net = this->net(below_term); - if (below_net && isConnected(below_net, pin, nets)) { - delete pin_iter; - return true; - } - } + Term *below_term = term(pin1); + if (below_term) { + Net *below_net = this->net(below_term); + if (below_net && isConnected(below_net, pin, nets)) { + delete pin_iter; + return true; + } + } } } delete pin_iter; @@ -1526,7 +1520,7 @@ Network::isConnected(const Net *net, bool Network::isConnected(const Net *net1, - const Net *net2) const + const Net *net2) const { NetSet nets(this); return isConnected(net1, net2, nets); @@ -1534,12 +1528,12 @@ Network::isConnected(const Net *net1, bool Network::isConnected(const Net *net1, - const Net *net2, - NetSet &nets) const + const Net *net2, + NetSet &nets) const { if (net1 == net2) return true; - else if (!nets.hasKey(net1)) { + else if (!nets.contains(net1)) { nets.insert(net1); // Search up from net terminals. NetTermIterator *term_iter = termIterator(net1); @@ -1547,11 +1541,11 @@ Network::isConnected(const Net *net1, Term *term = term_iter->next(); Pin *above_pin = pin(term); if (above_pin) { - Net *above_net = net(above_pin); - if (above_net && isConnected(above_net, net2, nets)) { - delete term_iter; - return true; - } + Net *above_net = net(above_pin); + if (above_net && isConnected(above_net, net2, nets)) { + delete term_iter; + return true; + } } } delete term_iter; @@ -1562,11 +1556,11 @@ Network::isConnected(const Net *net1, const Pin *pin1 = pin_iter->next(); Term *below_term = term(pin1); if (below_term) { - Net *below_net = net(below_term); - if (below_net && isConnected(below_net, net2, nets)) { - delete pin_iter; - return true; - } + Net *below_net = net(below_term); + if (below_net && isConnected(below_net, net2, nets)) { + delete pin_iter; + return true; + } } } delete pin_iter; @@ -1579,8 +1573,8 @@ Network::isConnected(const Net *net1, class FindDrvrPins : public PinVisitor { public: - explicit FindDrvrPins(PinSet *pins, - const Network *network); + FindDrvrPins(PinSet *pins, + const Network *network); virtual void operator()(const Pin *pin); protected: @@ -1589,7 +1583,7 @@ class FindDrvrPins : public PinVisitor }; FindDrvrPins::FindDrvrPins(PinSet *pins, - const Network *network) : + const Network *network) : PinVisitor(), pins_(pins), network_(network) @@ -1616,13 +1610,13 @@ Network::drivers(const Pin *pin) void Network::clearNetDrvrPinMap() { - net_drvr_pin_map_.deleteContentsClear(); + deleteContents(net_drvr_pin_map_); } PinSet * Network::drivers(const Net *net) { - PinSet *drvrs = net_drvr_pin_map_.findKey(net); + PinSet *drvrs = findKey(net_drvr_pin_map_, net); if (drvrs == nullptr) { drvrs = new PinSet(this); FindDrvrPins visitor(drvrs, this); @@ -1636,16 +1630,16 @@ Network::drivers(const Net *net) void Network::pathNameFirst(const char *path_name, - char *&first, - char *&tail) const + char *&first, + char *&tail) const { char escape = pathEscape(); char divider = pathDivider(); const char *d = strchr(path_name, divider); // Skip escaped dividers. while (d != nullptr - && d > path_name - && d[-1] == escape) + && d > path_name + && d[-1] == escape) d = strchr(d + 1, divider); if (d) { first = new char[d - path_name + 1]; @@ -1665,8 +1659,8 @@ Network::pathNameFirst(const char *path_name, void Network::pathNameLast(const char *path_name, - char *&head, - char *&last) const + char *&head, + char *&last) const { char escape = pathEscape(); char divider = pathDivider(); @@ -1674,10 +1668,10 @@ Network::pathNameLast(const char *path_name, // Search for a non-escaped divider. if (d) { while (d > path_name - && (d[0] != divider - || (d[0] == divider - && d > &path_name[1] - && d[-1] == escape))) + && (d[0] != divider + || (d[0] == divider + && d > &path_name[1] + && d[-1] == escape))) d--; } if (d && d != path_name) { @@ -1705,7 +1699,7 @@ NetworkEdit::NetworkEdit() : void NetworkEdit::connectPin(Pin *pin, - Net *net) + Net *net) { connect(instance(pin), port(pin), net); } @@ -1714,8 +1708,8 @@ NetworkEdit::connectPin(Pin *pin, NetworkConstantPinIterator:: NetworkConstantPinIterator(const Network *network, - NetSet &zero_nets, - NetSet &one_nets) : + NetSet &zero_nets, + NetSet &one_nets) : ConstantPinIterator(), network_(network), constant_pins_{PinSet(network), PinSet(network)} @@ -1723,21 +1717,14 @@ NetworkConstantPinIterator(const Network *network, findConstantPins(zero_nets, constant_pins_[0]); findConstantPins(one_nets, constant_pins_[1]); value_ = LogicValue::zero; - pin_iter_ = new PinSet::Iterator(constant_pins_[0]); -} - -NetworkConstantPinIterator::~NetworkConstantPinIterator() -{ - delete pin_iter_; + pin_iter_ = constant_pins_[0].begin(); } void NetworkConstantPinIterator::findConstantPins(NetSet &nets, - PinSet &pins) + PinSet &pins) { - NetSet::Iterator net_iter(nets); - while (net_iter.hasNext()) { - const Net *net = net_iter.next(); + for (const Net *net : nets) { NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); @@ -1750,13 +1737,12 @@ NetworkConstantPinIterator::findConstantPins(NetSet &nets, bool NetworkConstantPinIterator::hasNext() { - if (pin_iter_->hasNext()) + if (pin_iter_ != constant_pins_[(int)value_].end()) return true; else if (value_ == LogicValue::zero) { - delete pin_iter_; value_ = LogicValue::one; - pin_iter_ = new PinSet::Iterator(constant_pins_[1]); - return pin_iter_->hasNext(); + pin_iter_ = constant_pins_[1].begin(); + return pin_iter_ != constant_pins_[1].end(); } else return false; @@ -1764,19 +1750,19 @@ NetworkConstantPinIterator::hasNext() void NetworkConstantPinIterator::next(const Pin *&pin, - LogicValue &value) + LogicValue &value) { - pin = pin_iter_->next(); + pin = *pin_iter_++; value = value_; } //////////////////////////////////////////////////////////////// FindNetDrvrLoads::FindNetDrvrLoads(const Pin *drvr_pin, - PinSet &visited_drvrs, - PinSeq &loads, - PinSeq &drvrs, - const Network *network) : + PinSet &visited_drvrs, + PinSeq &loads, + PinSeq &drvrs, + const Network *network) : drvr_pin_(drvr_pin), visited_drvrs_(visited_drvrs), loads_(loads), @@ -1801,11 +1787,11 @@ FindNetDrvrLoads::operator()(const Pin *pin) static void visitPinsAboveNet1(const Pin *hpin, - Net *above_net, - NetSet &visited, - PinSet &above_drvrs, - PinSet &above_loads, - const Network *network) + Net *above_net, + NetSet &visited, + PinSet &above_drvrs, + PinSet &above_loads, + const Network *network) { visited.insert(above_net); // Visit above net pins. @@ -1814,15 +1800,15 @@ visitPinsAboveNet1(const Pin *hpin, const Pin *above_pin = pin_iter->next(); if (above_pin != hpin) { if (network->isDriver(above_pin)) - above_drvrs.insert(above_pin); + above_drvrs.insert(above_pin); if (network->isLoad(above_pin)) - above_loads.insert(above_pin); + above_loads.insert(above_pin); Term *above_term = network->term(above_pin); if (above_term) { - Net *above_net1 = network->net(above_term); - if (above_net1 && !visited.hasKey(above_net1)) - visitPinsAboveNet1(above_pin, above_net1, visited, - above_drvrs, above_loads, network); + Net *above_net1 = network->net(above_term); + if (above_net1 && !visited.contains(above_net1)) + visitPinsAboveNet1(above_pin, above_net1, visited, + above_drvrs, above_loads, network); } } } @@ -1834,15 +1820,15 @@ visitPinsAboveNet1(const Pin *hpin, Term *term = term_iter->next(); Pin *above_pin = network->pin(term); if (above_pin - && above_pin != hpin) { + && above_pin != hpin) { Net *above_net1 = network->net(above_pin); - if (above_net1 && !visited.hasKey(above_net1)) - visitPinsAboveNet1(above_pin, above_net1, visited, - above_drvrs, above_loads, network); + if (above_net1 && !visited.contains(above_net1)) + visitPinsAboveNet1(above_pin, above_net1, visited, + above_drvrs, above_loads, network); if (network->isDriver(above_pin)) - above_drvrs.insert(above_pin); + above_drvrs.insert(above_pin); if (network->isLoad(above_pin)) - above_loads.insert(above_pin); + above_loads.insert(above_pin); } } delete term_iter; @@ -1850,11 +1836,11 @@ visitPinsAboveNet1(const Pin *hpin, static void visitPinsBelowNet1(const Pin *hpin, - Net *below_net, - NetSet &visited, - PinSet &below_drvrs, - PinSet &below_loads, - const Network *network) + Net *below_net, + NetSet &visited, + PinSet &below_drvrs, + PinSet &below_loads, + const Network *network) { visited.insert(below_net); // Visit below net pins. @@ -1864,17 +1850,17 @@ visitPinsBelowNet1(const Pin *hpin, if (below_pin != hpin) { NetSet visited_above(network); if (network->isDriver(below_pin)) - below_drvrs.insert(below_pin); + below_drvrs.insert(below_pin); if (network->isLoad(below_pin)) - below_loads.insert(below_pin); + below_loads.insert(below_pin); if (network->isHierarchical(below_pin)) { - Term *term = network->term(below_pin); - if (term) { - Net *below_net1 = network->net(term); - if (below_net1 && !visited.hasKey(below_net1)) - visitPinsBelowNet1(below_pin, below_net1, visited, - below_drvrs, below_loads, network); - } + Term *term = network->term(below_pin); + if (term) { + Net *below_net1 = network->net(term); + if (below_net1 && !visited.contains(below_net1)) + visitPinsBelowNet1(below_pin, below_net1, visited, + below_drvrs, below_loads, network); + } } } } @@ -1883,24 +1869,19 @@ visitPinsBelowNet1(const Pin *hpin, static void visitDrvrLoads(PinSet drvrs, - PinSet loads, - HierPinThruVisitor *visitor) -{ - PinSet::Iterator drvr_iter(drvrs); - while (drvr_iter.hasNext()) { - const Pin *drvr = drvr_iter.next(); - PinSet::Iterator load_iter(loads); - while (load_iter.hasNext()) { - const Pin *load = load_iter.next(); + PinSet loads, + HierPinThruVisitor *visitor) +{ + for (const Pin *drvr : drvrs) { + for (const Pin *load : loads) visitor->visit(drvr, load); - } } } void visitDrvrLoadsThruHierPin(const Pin *hpin, - const Network *network, - HierPinThruVisitor *visitor) + const Network *network, + HierPinThruVisitor *visitor) { Net *above_net = network->net(hpin); if (above_net) { @@ -1909,17 +1890,17 @@ visitDrvrLoadsThruHierPin(const Pin *hpin, if (term) { Net *below_net = network->net(term); if (below_net) { - NetSet visited(network); - PinSet above_drvrs(network); - PinSet above_loads(network); - visitPinsAboveNet1(hpin, above_net, visited, - above_drvrs, above_loads, network); - PinSet below_drvrs(network); - PinSet below_loads(network); - visitPinsBelowNet1(hpin, below_net, visited, - below_drvrs, below_loads, network); - visitDrvrLoads(above_drvrs, below_loads, visitor); - visitDrvrLoads(below_drvrs, above_loads, visitor); + NetSet visited(network); + PinSet above_drvrs(network); + PinSet above_loads(network); + visitPinsAboveNet1(hpin, above_net, visited, + above_drvrs, above_loads, network); + PinSet below_drvrs(network); + PinSet below_loads(network); + visitPinsBelowNet1(hpin, below_net, visited, + below_drvrs, below_loads, network); + visitDrvrLoads(above_drvrs, below_loads, visitor); + visitDrvrLoads(below_drvrs, above_loads, visitor); } } } @@ -1927,8 +1908,8 @@ visitDrvrLoadsThruHierPin(const Pin *hpin, void visitDrvrLoadsThruNet(const Net *net, - const Network *network, - HierPinThruVisitor *visitor) + const Network *network, + HierPinThruVisitor *visitor) { NetSet visited(network); PinSet above_drvrs(network); @@ -1944,18 +1925,18 @@ visitDrvrLoadsThruNet(const Net *net, // Search down from pin terminal. const Term *term = network->term(pin); if (term) { - Net *below_net = network->net(term); - if (below_net) { - visitPinsBelowNet1(pin, below_net, visited, - below_drvrs, below_loads, network); - } + Net *below_net = network->net(term); + if (below_net) { + visitPinsBelowNet1(pin, below_net, visited, + below_drvrs, below_loads, network); + } } } else { if (network->isDriver(pin)) - net_drvrs.insert(pin); + net_drvrs.insert(pin); if (network->isLoad(pin)) - net_loads.insert(pin); + net_loads.insert(pin); } } delete pin_iter; @@ -1966,13 +1947,13 @@ visitDrvrLoadsThruNet(const Net *net, Pin *above_pin = network->pin(term); if (above_pin) { if (network->isDriver(above_pin)) - above_drvrs.insert(above_pin); + above_drvrs.insert(above_pin); if (network->isLoad(above_pin)) - above_loads.insert(above_pin); + above_loads.insert(above_pin); Net *above_net = network->net(above_pin); if (above_net) - visitPinsAboveNet1(above_pin, above_net, visited, - above_drvrs, above_loads, network); + visitPinsAboveNet1(above_pin, above_net, visited, + above_drvrs, above_loads, network); } } delete term_iter; @@ -2070,152 +2051,43 @@ NetIdLess::operator()(const Net *net1, //////////////////////////////////////////////////////////////// CellSet::CellSet(const Network *network) : - Set(CellIdLess(network)) + std::set(CellIdLess(network)) { } PortSet::PortSet(const Network *network) : - Set(PortIdLess(network)) + std::set(PortIdLess(network)) { } InstanceSet::InstanceSet() : - Set(InstanceIdLess(nullptr)) + std::set(InstanceIdLess(nullptr)) { } InstanceSet::InstanceSet(const Network *network) : - Set(InstanceIdLess(network)) -{ -} - -int -InstanceSet::compare(const InstanceSet *set1, - const InstanceSet *set2, - const Network *network) -{ - size_t size1 = set1 ? set1->size() : 0; - size_t size2 = set2 ? set2->size() : 0; - if (size1 == size2) { - InstanceSet::ConstIterator iter1(set1); - InstanceSet::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - const Instance *inst1 = iter1.next(); - const Instance *inst2 = iter2.next(); - ObjectId id1 = network->id(inst1); - ObjectId id2 = network->id(inst2); - if (id1 < id2) - return -1; - else if (id1 > id2) - return 1; - } - // Sets are equal. - return 0; - } - else - return (size1 > size2) ? 1 : -1; -} - -bool -InstanceSet::intersects(const InstanceSet *set1, - const InstanceSet *set2, - const Network *network) + std::set(InstanceIdLess(network)) { - return Set::intersects(set1, set2, InstanceIdLess(network)); } -//////////////////////////////////////////////////////////////// - PinSet::PinSet() : - Set(PinIdLess(nullptr)) + std::set(PinIdLess(nullptr)) { } PinSet::PinSet(const Network *network) : - Set(PinIdLess(network)) + std::set(PinIdLess(network)) { } -int -PinSet::compare(const PinSet *set1, - const PinSet *set2, - const Network *network) -{ - size_t size1 = set1 ? set1->size() : 0; - size_t size2 = set2 ? set2->size() : 0; - if (size1 == size2) { - PinSet::ConstIterator iter1(set1); - PinSet::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - const Pin *pin1 = iter1.next(); - const Pin *pin2 = iter2.next(); - ObjectId id1 = network->id(pin1); - ObjectId id2 = network->id(pin2); - if (id1 < id2) - return -1; - else if (id1 > id2) - return 1; - } - // Sets are equal. - return 0; - } - else - return (size1 > size2) ? 1 : -1; -} - -bool -PinSet::intersects(const PinSet *set1, - const PinSet *set2, - const Network *network) -{ - return Set::intersects(set1, set2, PinIdLess(network)); -} - -//////////////////////////////////////////////////////////////// - NetSet::NetSet() : - Set(NetIdLess(nullptr)) + std::set(NetIdLess(nullptr)) { } NetSet::NetSet(const Network *network) : - Set(NetIdLess(network)) -{ -} - -int -NetSet::compare(const NetSet *set1, - const NetSet *set2, - const Network *network) -{ - size_t size1 = set1 ? set1->size() : 0; - size_t size2 = set2 ? set2->size() : 0; - if (size1 == size2) { - NetSet::ConstIterator iter1(set1); - NetSet::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - const Net *net1 = iter1.next(); - const Net *net2 = iter2.next(); - ObjectId id1 = network->id(net1); - ObjectId id2 = network->id(net2); - if (id1 < id2) - return -1; - else if (id1 > id2) - return 1; - } - // Sets are equal. - return 0; - } - else - return (size1 > size2) ? 1 : -1; -} - -bool -NetSet::intersects(const NetSet *set1, - const NetSet *set2, - const Network *network) + std::set(NetIdLess(network)) { - return Set::intersects(set1, set2, NetIdLess(network)); } } // namespace diff --git a/network/Network.i b/network/Network.i index 52bf1ca08..349e94057 100644 --- a/network/Network.i +++ b/network/Network.i @@ -234,8 +234,8 @@ library_iterator() CellSeq find_cells_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Network *network = Sta::sta()->ensureLinked(); PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); @@ -289,7 +289,7 @@ port_direction(const Port *port) { return Sta::sta()->ensureLinked()->direction(port)->name(); } - + const char * pin_direction(const Pin *pin) { @@ -298,8 +298,8 @@ pin_direction(const Pin *pin) PortSeq find_ports_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -310,11 +310,11 @@ find_ports_matching(const char *pattern, PortSeq matches; for (const Port *port : matches1) { if (network->isBus(port) - || network->isBundle(port)) { + || network->isBundle(port)) { PortMemberIterator *member_iter = network->memberIterator(port); while (member_iter->hasNext()) { - Port *member = member_iter->next(); - matches.push_back(member); + Port *member = member_iter->next(); + matches.push_back(member); } delete member_iter; } @@ -326,8 +326,8 @@ find_ports_matching(const char *pattern, PinSeq find_port_pins_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -338,20 +338,20 @@ find_port_pins_matching(const char *pattern, PinSeq pins; for (const Port *port : ports) { if (network->isBus(port) - || network->isBundle(port)) { + || network->isBundle(port)) { PortMemberIterator *member_iter = network->memberIterator(port); while (member_iter->hasNext()) { - Port *member = member_iter->next(); - Pin *pin = network->findPin(top_inst, member); - if (pin) - pins.push_back(pin); + Port *member = member_iter->next(); + Pin *pin = network->findPin(top_inst, member); + if (pin) + pins.push_back(pin); } delete member_iter; } else { Pin *pin = network->findPin(top_inst, port); if (pin) - pins.push_back(pin); + pins.push_back(pin); } } return pins; @@ -373,8 +373,8 @@ get_port_pin(const Port *port) PinSeq find_pins_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -386,8 +386,8 @@ find_pins_matching(const char *pattern, PinSeq find_pins_hier_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -411,8 +411,8 @@ network_leaf_instances() InstanceSeq find_instances_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -424,8 +424,8 @@ find_instances_matching(const char *pattern, InstanceSeq find_instances_hier_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -435,72 +435,6 @@ find_instances_hier_matching(const char *pattern, return matches; } -InstanceSet -find_register_instances(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - InstanceSet insts = sta->findRegisterInstances(clks, clk_tr, - edge_triggered, - latches); - delete clks; - return insts; -} - -PinSet -find_register_data_pins(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - PinSet pins = sta->findRegisterDataPins(clks, clk_tr, - edge_triggered, latches); - delete clks; - return pins; -} - -PinSet -find_register_clk_pins(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - PinSet pins = sta->findRegisterClkPins(clks, clk_tr, - edge_triggered, latches); - delete clks; - return pins; -} - -PinSet -find_register_async_pins(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - PinSet pins = sta->findRegisterAsyncPins(clks, clk_tr, - edge_triggered, latches); - delete clks; - return pins; -} - -PinSet -find_register_output_pins(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - Sta *sta = Sta::sta(); - PinSet pins = sta->findRegisterOutputPins(clks, clk_tr, - edge_triggered, latches); - delete clks; - return pins; -} - Net * find_net(char *path_name) { @@ -509,8 +443,8 @@ find_net(char *path_name) NetSeq find_nets_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -522,8 +456,8 @@ find_nets_matching(const char *pattern, NetSeq find_nets_hier_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -621,8 +555,8 @@ find_cell(const char *name) CellSeq find_cells_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -646,7 +580,8 @@ LibertyCell *liberty_cell() { return Sta::sta()->cmdNetwork()->libertyCell(self) bool is_leaf() { return Sta::sta()->cmdNetwork()->isLeaf(self); } CellPortIterator * port_iterator() { return Sta::sta()->cmdNetwork()->portIterator(self); } -string get_attribute(const char *key) +std::string +get_attribute(const char *key) { return Sta::sta()->cmdNetwork()->getAttribute(self, key); } @@ -659,8 +594,8 @@ find_port(const char *name) PortSeq find_ports_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); Network *network = sta->ensureLinked(); @@ -708,7 +643,8 @@ find_pin(const char *name) { return Sta::sta()->ensureLinked()->findPin(self, name); } -string get_attribute(const char *key) { +std::string +get_attribute(const char *key) { return Sta::sta()->ensureLinked()->getAttribute(self, key); } @@ -801,35 +737,35 @@ bool is_power() { return Sta::sta()->ensureLinked()->isPower(self);} bool is_ground() { return Sta::sta()->ensureLinked()->isGround(self);} float -capacitance(Corner *corner, - const MinMax *min_max) +capacitance(Scene *scene, + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); float pin_cap, wire_cap; - sta->connectedCap(self, corner, min_max, pin_cap, wire_cap); + sta->connectedCap(self, scene, min_max, pin_cap, wire_cap); return pin_cap + wire_cap; } float -pin_capacitance(Corner *corner, - const MinMax *min_max) +pin_capacitance(Scene *scene, + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); float pin_cap, wire_cap; - sta->connectedCap(self, corner, min_max, pin_cap, wire_cap); + sta->connectedCap(self, scene, min_max, pin_cap, wire_cap); return pin_cap; } float -wire_capacitance(Corner *corner, - const MinMax *min_max) +wire_capacitance(Scene *scene, + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); float pin_cap, wire_cap; - sta->connectedCap(self, corner, min_max, pin_cap, wire_cap); + sta->connectedCap(self, scene, min_max, pin_cap, wire_cap); return wire_cap; } diff --git a/network/Network.tcl b/network/Network.tcl index b9d958a69..a8cd501c5 100644 --- a/network/Network.tcl +++ b/network/Network.tcl @@ -87,8 +87,8 @@ proc report_instance_pins1 {instance header header_optional dirs} { set dir [pin_direction $pin] if { [lsearch $dirs $dir] != -1 } { if { !$header_shown } { - report_line $header - set header_shown 1 + report_line $header + set header_shown 1 } report_instance_pin $pin } @@ -140,14 +140,14 @@ proc instance_sorted_children { instance } { ################################################################ -define_cmd_args "report_net" {[-corner corner] [-digits digits]\ +define_cmd_args "report_net" {[-scene scene] [-digits digits]\ net_path [> filename] [>> filename]} # -hpins to show hierarchical pins proc_redirect report_net { global sta_report_default_digits - parse_key_args "report_net" args keys {-corner -digits} \ + parse_key_args "report_net" args keys {-corner -scene -digits} \ flags {-connections -verbose -hier_pins} check_argc_eq1 "report_net" $args @@ -164,7 +164,7 @@ proc_redirect report_net { sta_warn 237 "report_net -hier_pins is deprecated." } - set corner [parse_corner_or_all keys] + set scene [parse_scene_or_default keys] set digits $sta_report_default_digits if { [info exists keys(-digits)] } { set digits $keys(-digits) @@ -173,15 +173,15 @@ proc_redirect report_net { set net_path [lindex $args 0] set net [find_net $net_path] if { $net != "NULL" } { - report_net1 $net $corner $digits + report_net1 $net $scene $digits } else { set pin [find_pin $net_path] if { $pin != "NULL" } { set net [$pin net] if { $net != "NULL" } { - report_net1 $net $corner $digits + report_net1 $net $scene $digits } else { - sta_error 231 "net $net_path not found." + sta_error 231 "net $net_path not found." } } else { sta_error 232 "net $net_path not found." @@ -189,14 +189,14 @@ proc_redirect report_net { } } -proc report_net1 { net corner digits } { +proc report_net1 { net scene digits } { report_line "Net [get_full_name $net]" set pins [net_connected_pins_sorted $net] - report_net_caps $net $pins $corner $digits - report_net_pins $pins "Driver pins" "is_driver" $corner $digits - report_net_pins $pins "Load pins" "is_load" $corner $digits - report_net_pins $pins "Hierarchical pins" "is_hierarchical" $corner $digits - report_net_other_pins $pins $corner $digits + report_net_caps $net $pins $scene $digits + report_net_pins $pins "Driver pins" "is_driver" $scene $digits + report_net_pins $pins "Load pins" "is_load" $scene $digits + report_net_pins $pins "Hierarchical pins" "is_hierarchical" $scene $digits + report_net_other_pins $pins $scene $digits } proc net_connected_pins_sorted { net } { @@ -211,10 +211,10 @@ proc net_connected_pins_sorted { net } { return $pins } -proc report_net_caps { net pins corner digits } { - report_net_cap $net "Pin" "pin_capacitance" $corner $digits - report_net_cap $net "Wire" "wire_capacitance" $corner $digits - report_net_cap $net "Total" "capacitance" $corner $digits +proc report_net_caps { net pins scene digits } { + report_net_cap $net "Pin" "pin_capacitance" $scene $digits + report_net_cap $net "Wire" "wire_capacitance" $scene $digits + report_net_cap $net "Total" "capacitance" $scene $digits set pin_count 0 set driver_count 0 @@ -236,13 +236,13 @@ proc report_net_caps { net pins corner digits } { report_line "" } -proc report_net_cap { net caption cap_msg corner digits } { - set cap_min [$net $cap_msg $corner "min"] - set cap_max [$net $cap_msg $corner "max"] +proc report_net_cap { net caption cap_msg scene digits } { + set cap_min [$net $cap_msg $scene "min"] + set cap_max [$net $cap_msg $scene "max"] report_line " $caption capacitance: [capacitance_range_str $cap_min $cap_max $digits]" } -proc report_net_pins { pins header pin_pred corner digits } { +proc report_net_pins { pins header pin_pred scene digits } { set found 0 foreach pin $pins { if {[$pin $pin_pred]} { @@ -250,7 +250,7 @@ proc report_net_pins { pins header pin_pred corner digits } { report_line $header set found 1 } - report_net_pin $pin $corner $digits + report_net_pin $pin $scene $digits } } if { $found } { @@ -258,43 +258,42 @@ proc report_net_pins { pins header pin_pred corner digits } { } } -proc report_net_other_pins { pins corner digits } { +proc report_net_other_pins { pins scene digits } { set header 0 foreach pin $pins { if { !([$pin is_driver] || [$pin is_load] || [$pin is_hierarchical]) } { if { !$header } { - report_line "" - report_line "Other pins" - set header 1 + report_line "" + report_line "Other pins" + set header 1 } - report_net_pin $pin $corner $digits + report_net_pin $pin $scene $digits } } } -proc report_net_pin { pin corner digits } { +proc report_net_pin { pin scene digits } { if [$pin is_leaf] { set cell_name [get_name [[$pin instance] cell]] set cap "" set liberty_port [$pin liberty_port] if { $liberty_port != "NULL" } { - set cap [port_capacitance_str $liberty_port $corner $digits] + set cap [port_capacitance_str $liberty_port $scene $digits] } report_line " [get_full_name $pin] [pin_direction $pin] ($cell_name)$cap[pin_location_str $pin]" } elseif [$pin is_top_level_port] { set wire_cap "" set pin_cap "" - set corner [sta::cmd_corner] set port [$pin port] - set cap_min [port_ext_wire_cap $port $corner "min"] - set cap_max [port_ext_wire_cap $port $corner "max"] + set cap_min [port_ext_wire_cap $port "min"] + set cap_max [port_ext_wire_cap $port "max"] if { $cap_min > 0 || $cap_max > 0 } { set wire_cap " wire [capacitance_range_str $cap_min $cap_max $digits]" } - set cap_min [port_ext_pin_cap $port $corner "min"] - set cap_max [port_ext_pin_cap $port $corner "max"] + set cap_min [port_ext_pin_cap $port "min"] + set cap_max [port_ext_pin_cap $port "max"] if { $cap_min > 0 || $cap_max > 0} { set pin_cap " pin [capacitance_range_str $cap_min $cap_max $digits]" } @@ -317,10 +316,10 @@ proc pin_location_str { pin } { ################################################################ -proc report_pin_ { pin corner digits } { +proc report_pin_ { pin scene digits } { set liberty_port [$pin liberty_port] if { $liberty_port != "NULL" } { - set cap [port_capacitance_str $liberty_port $corner $digits] + set cap [port_capacitance_str $liberty_port $scene $digits] } else { set cap "" } @@ -354,9 +353,9 @@ proc pin_direction_desc { pin } { } # Do not preceed this field by a space in the caller. -proc port_capacitance_str { liberty_port corner digits } { - set cap_min [$liberty_port capacitance $corner "min"] - set cap_max [$liberty_port capacitance $corner "max"] +proc port_capacitance_str { liberty_port scene digits } { + set cap_min [$liberty_port capacitance $scene "min"] + set cap_max [$liberty_port capacitance $scene "max"] if { $cap_min > 0 || $cap_max > 0 } { return " [capacitance_range_str $cap_min $cap_max $digits]" } else { @@ -374,13 +373,13 @@ proc capacitance_range_str { cap_min cap_max digits } { proc capacitances_str { cap_r_min cap_r_max cap_f_min cap_f_max digits } { if { $cap_r_min == $cap_r_max \ - && $cap_f_min == $cap_f_max \ - && $cap_r_max == $cap_f_max } { + && $cap_f_min == $cap_f_max \ + && $cap_r_max == $cap_f_max } { # All 4 values are the same. set cap $cap_r_min return "[format_capacitance $cap $digits]" } elseif { $cap_r_min == $cap_r_max \ - && $cap_f_min == $cap_f_max } { + && $cap_f_min == $cap_f_max } { # Mins equal maxes. return "r [format_capacitance $cap_r_min $digits] f [format_capacitance $cap_f_min $digits]" } else { diff --git a/network/NetworkCmp.cc b/network/NetworkCmp.cc index e00df1316..dd82c3448 100644 --- a/network/NetworkCmp.cc +++ b/network/NetworkCmp.cc @@ -39,7 +39,7 @@ PortNameLess::PortNameLess(const Network *network) : bool PortNameLess::operator()(const Port *port1, - const Port *port2) const + const Port *port2) const { return stringLess(network_->name(port1), network_->name(port2)); } @@ -51,7 +51,7 @@ PinPathNameLess::PinPathNameLess(const Network *network) : bool PinPathNameLess::operator()(const Pin *pin1, - const Pin *pin2) const + const Pin *pin2) const { return network_->pathNameLess(pin1, pin2); } @@ -63,7 +63,7 @@ NetPathNameLess::NetPathNameLess(const Network *network) : bool NetPathNameLess::operator()(const Net *net1, - const Net *net2) const + const Net *net2) const { return network_->pathNameLess(net1, net2); } @@ -75,7 +75,7 @@ InstancePathNameLess::InstancePathNameLess(const Network *network) : bool InstancePathNameLess::operator()(const Instance *inst1, - const Instance *inst2) const + const Instance *inst2) const { return network_->pathNameLess(inst1, inst2); } @@ -93,6 +93,17 @@ sortByPathName(const PinSet *set, return pins; } +PinSeq +sortByPathName(const PinUnorderedSet *set, + const Network *network) +{ + PinSeq pins; + for (const Pin *pin : *set) + pins.push_back(pin); + sort(pins, PinPathNameLess(network)); + return pins; +} + PortSeq sortByName(const PortSet *set, const Network *network) diff --git a/network/NetworkEdit.i b/network/NetworkEdit.i index c0fa6527f..8584ff0a4 100644 --- a/network/NetworkEdit.i +++ b/network/NetworkEdit.i @@ -44,8 +44,8 @@ using sta::NetworkEdit; Instance * make_instance_cmd(const char *name, - LibertyCell *cell, - Instance *parent) + LibertyCell *cell, + Instance *parent) { return Sta::sta()->makeInstance(name, cell, parent); } @@ -58,14 +58,14 @@ delete_instance_cmd(Instance *inst) void replace_cell_cmd(Instance *inst, - LibertyCell *to_cell) + LibertyCell *to_cell) { Sta::sta()->replaceCell(inst, to_cell); } Net * make_net_cmd(const char *name, - Instance *parent) + Instance *parent) { return Sta::sta()->makeNet(name, parent); } @@ -85,8 +85,8 @@ delete_net_cmd(Net *net) void connect_pin_cmd(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { Sta::sta()->connectPin(inst, port, net); } diff --git a/network/NetworkEdit.tcl b/network/NetworkEdit.tcl index 0f70b461d..a13285fd6 100644 --- a/network/NetworkEdit.tcl +++ b/network/NetworkEdit.tcl @@ -35,11 +35,11 @@ proc make_instance { inst_path lib_cell } { if {[regexp $path_regexp $inst_path ignore path_name inst_name]} { set parent [find_instance $path_name] if { $parent == "NULL" } { - # Parent instance not found. This could be a typo, but since - # SDC does not escape hierarchy dividers it can also be - # an escaped name. - set inst_name $inst_path - set parent [top_instance] + # Parent instance not found. This could be a typo, but since + # SDC does not escape hierarchy dividers it can also be + # an escaped name. + set inst_name $inst_path + set parent [top_instance] } } else { set inst_name $inst_path @@ -118,7 +118,7 @@ proc parse_connect_pin { arg } { if {[regexp $path_regexp $arg ignore path_name port_name]} { set inst [find_instance $path_name] if { $inst == "NULL" } { - return 0 + return 0 } } else { set inst [top_instance] @@ -134,8 +134,8 @@ proc parse_connect_pin { arg } { # Make sure the pin is not currently connected to a net. if { $pin != "NULL" \ - && ![$pin is_hierarchical] \ - && [$pin net] != "NULL" } { + && ![$pin is_hierarchical] \ + && [$pin net] != "NULL" } { return 0 } return [list $inst $port] @@ -217,7 +217,7 @@ proc replace_cell { instance lib_cell } { set inst [get_instance_error "instance" $instance] set inst_cell [$inst liberty_cell] if { $inst_cell == "NULL" \ - || ![equiv_cell_ports $inst_cell $cell] } { + || ![equiv_cell_ports $inst_cell $cell] } { return 0 } replace_cell_cmd $inst $cell diff --git a/network/ParseBus.cc b/network/ParseBus.cc index 18ca03f30..17108412a 100644 --- a/network/ParseBus.cc +++ b/network/ParseBus.cc @@ -36,9 +36,9 @@ using std::string; bool isBusName(const char *name, - const char brkt_left, - const char brkt_right, - char escape) + const char brkt_left, + const char brkt_right, + char escape) { size_t len = strlen(name); // Shortest bus name is a[0]. @@ -55,13 +55,13 @@ isBusName(const char *name, void parseBusName(const char *name, - const char brkt_left, - const char brkt_right, - const char escape, - // Return values. - bool &is_bus, - string &bus_name, - int &index) + const char brkt_left, + const char brkt_right, + const char escape, + // Return values. + bool &is_bus, + string &bus_name, + int &index) { const char brkts_left[2] = {brkt_left, '\0'}; const char brkts_right[2] = {brkt_right, '\0'}; @@ -71,13 +71,13 @@ parseBusName(const char *name, void parseBusName(const char *name, - const char *brkts_left, - const char *brkts_right, - char escape, - // Return values. - bool &is_bus, - string &bus_name, - int &index) + const char *brkts_left, + const char *brkts_right, + char escape, + // Return values. + bool &is_bus, + string &bus_name, + int &index) { is_bus = false; size_t len = strlen(name); @@ -93,10 +93,10 @@ parseBusName(const char *name, const char *left = strrchr(name, brkt_left); if (left) { is_bus = true; - size_t bus_name_len = left - name; - bus_name.append(name, bus_name_len); - // Simple bus subscript. - index = atoi(left + 1); + size_t bus_name_len = left - name; + bus_name.append(name, bus_name_len); + // Simple bus subscript. + index = atoi(left + 1); } } } @@ -150,20 +150,20 @@ parseBusName(const char *name, const char *left = strrchr(name, brkt_left); if (left) { is_bus = true; - // Check for bus range. - const char range_sep = ':'; - const char *range = strchr(name, range_sep); - if (range) { + // Check for bus range. + const char range_sep = ':'; + const char *range = strchr(name, range_sep); + if (range) { 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); - } + // No need to terminate bus subscript because atoi stops + // scanning at first non-digit character. + from = atoi(left + 1); + to = atoi(range + 1); + } else { bus_name.append(name, left - name); - if (left[1] == '*') + if (left[1] == '*') subscript_wild = true; else from = to = atoi(left + 1); @@ -175,9 +175,9 @@ parseBusName(const char *name, string escapeChars(const char *token, - const char ch1, - const char ch2, - const char escape) + const char ch1, + const char ch2, + const char escape) { string escaped; for (const char *s = token; *s; s++) { diff --git a/network/PortDirection.cc b/network/PortDirection.cc index 693f6908c..4c6946af3 100644 --- a/network/PortDirection.cc +++ b/network/PortDirection.cc @@ -72,7 +72,7 @@ PortDirection::destroy() } PortDirection::PortDirection(const char *name, - int index) : + int index) : name_(name), index_(index) { diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index 3967a82b7..29a0037e9 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -35,10 +35,10 @@ using std::to_string; static string escapeDividers(const char *token, - const Network *network); + const Network *network); static string escapeBrackets(const char *token, - const Network *network); + const Network *network); NetworkNameAdapter::NetworkNameAdapter(Network *network) : NetworkEdit(), @@ -49,8 +49,8 @@ NetworkNameAdapter::NetworkNameAdapter(Network *network) : bool NetworkNameAdapter::linkNetwork(const char *top_cell_name, - bool make_black_boxes, - Report *report) + bool make_black_boxes, + Report *report) { return network_->linkNetwork(top_cell_name, make_black_boxes, report); } @@ -111,14 +111,14 @@ NetworkNameAdapter::id(const Library *library) const Cell * NetworkNameAdapter::findCell(const Library *library, - const char *name) const + const char *name) const { return network_->findCell(library, name); } CellSeq NetworkNameAdapter::findCellsMatching(const Library *library, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { return network_->findCellsMatching(library, pattern); } @@ -188,14 +188,14 @@ NetworkNameAdapter::cell(const LibertyCell *cell) const Port * NetworkNameAdapter::findPort(const Cell *cell, - const char *name) const + const char *name) const { return network_->findPort(cell, name); } PortSeq NetworkNameAdapter::findPortsMatching(const Cell *cell, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { return network_->findPortsMatching(cell, pattern); } @@ -264,17 +264,17 @@ NetworkNameAdapter::vertexId(const Pin *pin) const void NetworkNameAdapter::setVertexId(Pin *pin, - VertexId id) + VertexId id) { network_->setVertexId(pin, id); } void NetworkNameAdapter::location(const Pin *pin, - // Return values. - double &x, - double &y, - bool &exists) const + // Return values. + double &x, + double &y, + bool &exists) const { network_->location(pin, x, y, exists); } @@ -299,7 +299,7 @@ NetworkNameAdapter::busName(const Port *port) const Port * NetworkNameAdapter::findBusBit(const Port *port, - int index) const + int index) const { return network_->findMember(port, index); } @@ -330,7 +330,7 @@ NetworkNameAdapter::hasMembers(const Port *port) const Port * NetworkNameAdapter::findMember(const Port *port, - int index) const + int index) const { return network_->findMember(port, index); } @@ -382,14 +382,14 @@ NetworkNameAdapter::isLeaf(const Instance *instance) const Pin * NetworkNameAdapter::findPin(const Instance *instance, - const Port *port) const + const Port *port) const { return network_->findPin(instance, port); } Pin * NetworkNameAdapter::findPin(const Instance *instance, - const LibertyPort *port) const + const LibertyPort *port) const { return network_->findPin(instance, port); } @@ -541,15 +541,15 @@ NetworkNameAdapter::isEditable() const LibertyLibrary * NetworkNameAdapter::makeLibertyLibrary(const char *name, - const char *filename) + const char *filename) { return network_edit_->makeLibertyLibrary(name, filename); } Instance * NetworkNameAdapter::makeInstance(LibertyCell *cell, - const char *name, - Instance *parent) + const char *name, + Instance *parent) { return network_edit_->makeInstance(cell, name, parent); } @@ -562,7 +562,7 @@ NetworkNameAdapter::makePins(Instance *inst) void NetworkNameAdapter::mergeInto(Net *net, - Net *into_net) + Net *into_net) { network_edit_->mergeInto(net, into_net); } @@ -575,30 +575,30 @@ NetworkNameAdapter::mergedInto(Net *net) Net * NetworkNameAdapter::makeNet(const char *name, - Instance *parent) + Instance *parent) { return network_edit_->makeNet(name, parent); } void NetworkNameAdapter::replaceCell(Instance *inst, - Cell *to_cell) + Cell *to_cell) { network_edit_->replaceCell(inst, to_cell); } Pin * NetworkNameAdapter::connect(Instance *inst, - Port *port, - Net *net) + Port *port, + Net *net) { return network_edit_->connect(inst, port, net); } Pin * NetworkNameAdapter::connect(Instance *inst, - LibertyPort *port, - Net *net) + LibertyPort *port, + Net *net) { return network_edit_->connect(inst, port, net); } @@ -654,9 +654,9 @@ SdcNetwork::staToSdc(const char *sta_name) const char next_ch = s[1]; // Escaped escape. if (next_ch == escape) { - *d++ = ch; - *d++ = next_ch; - s++; + *d++ = ch; + *d++ = next_ch; + s++; } } else @@ -669,7 +669,7 @@ SdcNetwork::staToSdc(const char *sta_name) const Port * SdcNetwork::findPort(const Cell *cell, - const char *name) const + const char *name) const { Port *port = network_->findPort(cell, name); if (port == nullptr) { @@ -682,13 +682,13 @@ SdcNetwork::findPort(const Cell *cell, string escaped1 = escapeBrackets(name, this); port = network_->findPort(cell, escaped1.c_str()); if (port == nullptr) { - // Try escaping base foo\[0\][1] + // Try escaping base foo\[0\][1] string escaped2; string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index); - port = network_->findPort(cell, escaped2.c_str()); + port = network_->findPort(cell, escaped2.c_str()); } } else { @@ -702,7 +702,7 @@ SdcNetwork::findPort(const Cell *cell, PortSeq SdcNetwork::findPortsMatching(const Cell *cell, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { PortSeq matches = network_->findPortsMatching(cell, pattern); if (matches.empty()) { @@ -717,13 +717,13 @@ SdcNetwork::findPortsMatching(const Cell *cell, PatternMatch escaped_pattern1(escaped1.c_str(), pattern); matches = network_->findPortsMatching(cell, &escaped_pattern1); if (matches.empty()) { - // Try escaping base foo\[0\][1] + // Try escaping base foo\[0\][1] string escaped_name = escapeBrackets(bus_name.c_str(), this); escaped_name += '['; escaped_name += to_string(index); escaped_name += ']'; - PatternMatch escaped_pattern2(escaped_name.c_str(), pattern); - matches = network_->findPortsMatching(cell, &escaped_pattern2); + PatternMatch escaped_pattern2(escaped_name.c_str(), pattern); + matches = network_->findPortsMatching(cell, &escaped_pattern2); } } else { @@ -820,7 +820,7 @@ SdcNetwork::findInstanceRelative(const Instance *inst, InstanceSeq SdcNetwork::findInstancesMatching(const Instance *context, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { InstanceSeq matches; findInstancesMatching1(context, pattern, matches); @@ -833,18 +833,18 @@ SdcNetwork::findInstancesMatching1(const Instance *context, InstanceSeq &matches) const { visitMatches(context, pattern, - [&](const Instance *instance, - const PatternMatch *tail) - { - size_t match_count = matches.size(); - network_->findChildrenMatching(instance, tail, matches); - return matches.size() != match_count; - }); + [&](const Instance *instance, + const PatternMatch *tail) + { + size_t match_count = matches.size(); + network_->findChildrenMatching(instance, tail, matches); + return matches.size() != match_count; + }); } Instance * SdcNetwork::findChild(const Instance *parent, - const char *name) const + const char *name) const { Instance *child = network_->findChild(parent, name); if (child == nullptr) { @@ -894,8 +894,8 @@ SdcNetwork::findNetRelative(const Instance *inst, net = network_->findNetRelative(inst, path_name2.c_str()); if (net == nullptr) { - string path_name3 = escapeDividers(path_name2.c_str(), network_); - net = network_->findNetRelative(inst, path_name3.c_str()); + string path_name3 = escapeDividers(path_name2.c_str(), network_); + net = network_->findNetRelative(inst, path_name3.c_str()); } } } @@ -904,24 +904,24 @@ SdcNetwork::findNetRelative(const Instance *inst, NetSeq SdcNetwork::findNetsMatching(const Instance *parent, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { NetSeq matches; visitMatches(parent, pattern, - [&](const Instance *instance, - const PatternMatch *tail) - { - size_t match_count = matches.size(); - network_->findInstNetsMatching(instance, tail, matches); - return matches.size() != match_count; - }); + [&](const Instance *instance, + const PatternMatch *tail) + { + size_t match_count = matches.size(); + network_->findInstNetsMatching(instance, tail, matches); + return matches.size() != match_count; + }); return matches; } void SdcNetwork::findInstNetsMatching(const Instance *instance, - const PatternMatch *pattern, - NetSeq &matches) const + const PatternMatch *pattern, + NetSeq &matches) const { network_->findInstNetsMatching(instance, pattern, matches); if (matches.empty()) { @@ -953,7 +953,7 @@ SdcNetwork::findPin(const char *path_name) const Pin * SdcNetwork::findPin(const Instance *instance, - const char *port_name) const + const char *port_name) const { Pin *pin = network_->findPin(instance, port_name); if (pin == nullptr) { @@ -967,11 +967,11 @@ SdcNetwork::findPin(const Instance *instance, string escaped1 = escapeBrackets(port_name, this); pin = network_->findPin(instance, escaped1.c_str()); if (pin == nullptr) { - // Try escaping base foo\[0\][1] + // Try escaping base foo\[0\][1] string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); string escaped2; stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index); - pin = network_->findPin(instance, escaped2.c_str()); + pin = network_->findPin(instance, escaped2.c_str()); } } else { @@ -986,7 +986,7 @@ SdcNetwork::findPin(const Instance *instance, // Top level ports are not considered pins by get_pins. PinSeq SdcNetwork::findPinsMatching(const Instance *instance, - const PatternMatch *pattern) const + const PatternMatch *pattern) const { PinSeq matches; if (stringEq(pattern->pattern(), "*")) { @@ -996,8 +996,8 @@ SdcNetwork::findPinsMatching(const Instance *instance, Instance *child = child_iter->next(); InstancePinIterator *pin_iter = pinIterator(child); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - matches.push_back(pin); + const Pin *pin = pin_iter->next(); + matches.push_back(pin); } delete pin_iter; } @@ -1005,18 +1005,18 @@ SdcNetwork::findPinsMatching(const Instance *instance, } else visitMatches(instance, pattern, - [&](const Instance *instance, - const PatternMatch *tail) - { - return visitPinTail(instance, tail, matches); - }); + [&](const Instance *instance, + const PatternMatch *tail) + { + return visitPinTail(instance, tail, matches); + }); return matches; } bool SdcNetwork::visitPinTail(const Instance *instance, - const PatternMatch *tail, - PinSeq &matches) const + const PatternMatch *tail, + PinSeq &matches) const { bool found_match = false; if (instance != network_->topInstance()) { @@ -1026,35 +1026,35 @@ SdcNetwork::visitPinTail(const Instance *instance, Port *port = port_iter->next(); const char *port_name = network_->name(port); if (network_->hasMembers(port)) { - bool bus_matches = tail->match(port_name); + bool bus_matches = tail->match(port_name); if (!bus_matches) { string escaped_name = escapeDividers(port_name, network_); - bus_matches = tail->match(escaped_name); + bus_matches = tail->match(escaped_name); } - PortMemberIterator *member_iter = network_->memberIterator(port); - while (member_iter->hasNext()) { - Port *member_port = member_iter->next(); - const Pin *pin = network_->findPin(instance, member_port); - if (pin) { - if (bus_matches) { - matches.push_back(pin); - found_match = true; - } - else { - const char *member_name = network_->name(member_port); - bool member_matches = tail->match(member_name); + PortMemberIterator *member_iter = network_->memberIterator(port); + while (member_iter->hasNext()) { + Port *member_port = member_iter->next(); + const Pin *pin = network_->findPin(instance, member_port); + if (pin) { + if (bus_matches) { + matches.push_back(pin); + found_match = true; + } + else { + const char *member_name = network_->name(member_port); + bool member_matches = tail->match(member_name); if (!member_matches) { string escaped_name = escapeDividers(member_name, network_); member_matches = tail->match(escaped_name); } if (member_matches) { - matches.push_back(pin); - found_match = true; - } - } - } - } - delete member_iter; + matches.push_back(pin); + found_match = true; + } + } + } + } + delete member_iter; } else { bool port_matches = tail->match(port_name); @@ -1078,8 +1078,8 @@ SdcNetwork::visitPinTail(const Instance *instance, Instance * SdcNetwork::makeInstance(LibertyCell *cell, - const char *name, - Instance *parent) + const char *name, + Instance *parent) { string escaped_name = escapeDividers(name, this); return network_edit_->makeInstance(cell, escaped_name.c_str(), parent); @@ -1087,7 +1087,7 @@ SdcNetwork::makeInstance(LibertyCell *cell, Net * SdcNetwork::makeNet(const char *name, - Instance *parent) + Instance *parent) { string escaped_name = escapeDividers(name, this); return network_edit_->makeNet(escaped_name.c_str(), parent); @@ -1104,9 +1104,9 @@ SdcNetwork::makeNet(const char *name, // a\/b\/c void SdcNetwork::parsePath(const char *path, - // Return values. - Instance *&inst, - const char *&path_tail) const + // Return values. + Instance *&inst, + const char *&path_tail) const { int divider_count, path_length; scanPath(path, divider_count, path_length); @@ -1121,10 +1121,10 @@ SdcNetwork::parsePath(const char *path, // Scan the path for unescaped dividers. void SdcNetwork::scanPath(const char *path, - // Return values. - // Unescaped divider count. - int ÷r_count, - int &path_length) const + // Return values. + // Unescaped divider count. + int ÷r_count, + int &path_length) const { divider_count = 0; path_length = 0; @@ -1133,8 +1133,8 @@ SdcNetwork::scanPath(const char *path, if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. if (s[1] != '\0') { - s++; - path_length++; + s++; + path_length++; } } else if (ch == divider_) @@ -1145,11 +1145,11 @@ SdcNetwork::scanPath(const char *path, void SdcNetwork::parsePath(const char *path, - int divider_count, - int path_length, - // Return values. - Instance *&inst, - const char *&path_tail) const + int divider_count, + int path_length, + // Return values. + Instance *&inst, + const char *&path_tail) const { Instance *parent = topInstance(); // Leave room to escape all the dividers and '\0'. @@ -1163,9 +1163,9 @@ SdcNetwork::parsePath(const char *path, 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]; - s++; + *p++ = ch; + *p++ = s[1]; + s++; } } else if (ch == divider_) { @@ -1173,16 +1173,16 @@ SdcNetwork::parsePath(const char *path, *p = '\0'; Instance *child = findChild(parent, inst_path); if (child) { - // Found an instance for the sub-path up to this divider. - parent = inst = child; - // Reset the instance path. - p = inst_path; - path_tail = s + 1; + // Found an instance for the sub-path up to this divider. + parent = inst = child; + // Reset the instance path. + p = inst_path; + path_tail = s + 1; } else { - // No match for sub-path. Escape the divider and keep looking. - *p++ = escape_; - *p++ = divider_; + // No match for sub-path. Escape the divider and keep looking. + *p++ = escape_; + *p++ = divider_; } } else @@ -1203,10 +1203,10 @@ SdcNetwork::parsePath(const char *path, // a\/b\/c bool SdcNetwork::visitMatches(const Instance *parent, - const PatternMatch *pattern, - const std::function - visit_tail) const + const PatternMatch *pattern, + const std::function + visit_tail) const { int divider_count, path_length; scanPath(pattern->pattern(), divider_count, path_length); @@ -1222,9 +1222,9 @@ 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]; - s++; + *p++ = ch; + *p++ = s[1]; + s++; } } else if (ch == divider_) { @@ -1234,21 +1234,18 @@ SdcNetwork::visitMatches(const Instance *parent, InstanceSeq matches; network_->findChildrenMatching(parent, &matcher, matches); if (has_brkts && matches.empty()) { - // Look for matches after escaping brackets. + // Look for matches after escaping brackets. string escaped_brkts = escapeBrackets(inst_path, this); - const PatternMatch escaped_pattern(escaped_brkts, pattern); - network_->findChildrenMatching(parent, &escaped_pattern, matches); + const PatternMatch escaped_pattern(escaped_brkts, pattern); + network_->findChildrenMatching(parent, &escaped_pattern, matches); } if (!matches.empty()) { - // Found instance matches for the sub-path up to this divider. - const PatternMatch tail_pattern(s + 1, pattern); - InstanceSeq::Iterator match_iter(matches); - while (match_iter.hasNext()) { - const Instance *match = match_iter.next(); - // Recurse to save the iterator state so we can iterate over - // multiple nested partial matches. - found_match |= visitMatches(match, &tail_pattern, visit_tail); - } + // Found instance matches for the sub-path up to this divider. + const PatternMatch tail_pattern(s + 1, pattern); + for (const Instance *match : matches) + // Recurse to save the iterator state so we can iterate over + // multiple nested partial matches. + found_match |= visitMatches(match, &tail_pattern, visit_tail); } // Escape the divider and keep looking. *p++ = escape_; @@ -1256,7 +1253,7 @@ SdcNetwork::visitMatches(const Instance *parent, } else { if (ch == '[' || ch == ']') - has_brkts = true; + has_brkts = true; *p++ = ch; } if (p - inst_path + 1 > inst_path_length) @@ -1281,15 +1278,15 @@ SdcNetwork::visitMatches(const Instance *parent, static string escapeDividers(const char *token, - const Network *network) + const Network *network) { return escapeChars(token, network->pathDivider(), '\0', - network->pathEscape()); + network->pathEscape()); } static string escapeBrackets(const char *token, - const Network *network) + const Network *network) { return escapeChars(token, '[', ']', network->pathEscape()); } diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index 94cc2f3f4..5a1dc8e0e 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -90,17 +90,17 @@ staToVerilog(const char *sta_name) if (ch == verilog_escape) { char next_ch = s[1]; if (next_ch == verilog_escape) { - escaped_name += ch; - escaped_name += next_ch; - s++; + escaped_name += ch; + escaped_name += next_ch; + s++; } else - // Skip escape. - escaped = true; + // Skip escape. + escaped = true; } else { if ((!(isalnum(ch) || ch == '_'))) - escaped = true; + escaped = true; escaped_name += ch; } } @@ -128,19 +128,19 @@ staToVerilog2(const char *sta_name) if (ch == verilog_escape) { char next_ch = s[1]; if (next_ch == verilog_escape) { - escaped_name += ch; - escaped_name += next_ch; - s++; + escaped_name += ch; + escaped_name += next_ch; + s++; } else - // Skip escape. - escaped = true; + // Skip escape. + escaped = true; } else { bool is_brkt = (ch == bus_brkt_left || ch == bus_brkt_right); if ((!(isalnum(ch) || ch == '_') && !is_brkt) - || is_brkt) - escaped = true; + || is_brkt) + escaped = true; escaped_name += ch; } } @@ -199,7 +199,7 @@ verilogToSta(const string *verilog_name) || ch == divider || ch == verilog_escape) // Escape bus brackets, dividers and escapes. - sta_name += verilog_escape; + sta_name += verilog_escape; sta_name += ch; } return sta_name; diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 9d7f58447..dd8666643 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -30,16 +30,14 @@ #include "Debug.hh" #include "Error.hh" #include "Mutex.hh" -#include "Set.hh" #include "MinMax.hh" #include "Network.hh" #include "Wireload.hh" #include "Liberty.hh" #include "Sdc.hh" #include "Parasitics.hh" -#include "MakeConcreteParasitics.hh" #include "ConcreteParasiticsPvt.hh" -#include "Corner.hh" +#include "Scene.hh" // Multiple inheritance is used to share elmore and pi model base // classes, but care is taken to make sure there are no loops in the @@ -85,15 +83,15 @@ ConcreteParasitic::isParasiticNetwork() const void ConcreteParasitic::piModel(float &, - float &, - float &) const + float &, + float &) const { } void ConcreteParasitic::setPiModel(float, - float, - float) + float, + float) { } @@ -110,15 +108,15 @@ ConcreteParasitic::setIsReduced(bool) void ConcreteParasitic::findElmore(const Pin *, - float &, - bool &exists) const + float &, + bool &exists) const { exists = false; } void ConcreteParasitic::setElmore(const Pin *, - float) + float) { } @@ -130,16 +128,16 @@ ConcreteParasitic::findPoleResidue(const Pin *) const void ConcreteParasitic::setPoleResidue(const Pin *, - ComplexFloatSeq *, - ComplexFloatSeq *) + ComplexFloatSeq *, + ComplexFloatSeq *) { } //////////////////////////////////////////////////////////////// ConcretePi::ConcretePi(float c2, - float rpi, - float c1) : + float rpi, + float c1) : c2_(c2), rpi_(rpi), c1_(c1), @@ -155,8 +153,8 @@ ConcretePi::capacitance() const void ConcretePi::setPiModel(float c2, - float rpi, - float c1) + float rpi, + float c1) { c2_ = c2; rpi_ = rpi; @@ -165,8 +163,8 @@ ConcretePi::setPiModel(float c2, void ConcretePi::piModel(float &c2, - float &rpi, - float &c1) const + float &rpi, + float &c1) const { c2 = c2_; rpi = rpi_; @@ -182,8 +180,8 @@ ConcretePi::setIsReduced(bool reduced) //////////////////////////////////////////////////////////////// ConcretePiElmore::ConcretePiElmore(float c2, - float rpi, - float c1) : + float rpi, + float c1) : ConcretePi(c2, rpi, c1) { } @@ -196,16 +194,16 @@ ConcretePiElmore::capacitance() const void ConcretePiElmore::piModel(float &c2, - float &rpi, - float &c1) const + float &rpi, + float &c1) const { ConcretePi::piModel(c2, rpi, c1); } void ConcretePiElmore::setPiModel(float c2, - float rpi, - float c1) + float rpi, + float c1) { ConcretePi::setPiModel(c2, rpi, c1); } @@ -224,8 +222,8 @@ ConcretePiElmore::setIsReduced(bool reduced) void ConcretePiElmore::findElmore(const Pin *load_pin, - float &elmore, - bool &exists) const + float &elmore, + bool &exists) const { auto itr = loads_.find(load_pin); if (itr == loads_.end()) @@ -238,7 +236,7 @@ ConcretePiElmore::findElmore(const Pin *load_pin, void ConcretePiElmore::setElmore(const Pin *load_pin, - float elmore) + float elmore) { loads_[load_pin] = elmore; } @@ -261,8 +259,7 @@ ConcretePiElmore::unannotatedLoads(const Pin *drvr_pin, //////////////////////////////////////////////////////////////// -ConcretePoleResidue:: -ConcretePoleResidue() : +ConcretePoleResidue::ConcretePoleResidue() : poles_(nullptr), residues_(nullptr) { @@ -282,8 +279,8 @@ ConcretePoleResidue::poleResidueCount() const void ConcretePoleResidue::poleResidue(int index, - ComplexFloat &pole, - ComplexFloat &residue) const + ComplexFloat &pole, + ComplexFloat &residue) const { pole = (*poles_)[index]; residue = (*residues_)[index]; @@ -291,7 +288,7 @@ ConcretePoleResidue::poleResidue(int index, void ConcretePoleResidue::setPoleResidue(ComplexFloatSeq *poles, - ComplexFloatSeq *residues) + ComplexFloatSeq *residues) { poles_ = poles; residues_ = residues; @@ -307,8 +304,8 @@ ConcretePoleResidue::unannotatedLoads(const Pin *, //////////////////////////////////////////////////////////////// ConcretePiPoleResidue::ConcretePiPoleResidue(float c2, - float rpi, - float c1) : + float rpi, + float c1) : ConcretePi(c2, rpi, c1) { } @@ -321,16 +318,16 @@ ConcretePiPoleResidue::capacitance() const void ConcretePiPoleResidue::piModel(float &c2, - float &rpi, - float &c1) const + float &rpi, + float &c1) const { ConcretePi::piModel(c2, rpi, c1); } void ConcretePiPoleResidue::setPiModel(float c2, - float rpi, - float c1) + float rpi, + float c1) { ConcretePi::setPiModel(c2, rpi, c1); } @@ -359,8 +356,8 @@ ConcretePiPoleResidue::findPoleResidue(const Pin *load_pin) const void ConcretePiPoleResidue::setPoleResidue(const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues) + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) { ConcretePoleResidue &pole_residue = load_pole_residue_[load_pin]; pole_residue.setPoleResidue(poles, residues); @@ -453,9 +450,9 @@ ConcreteParasiticNode::net(const Network *network) const ConcreteParasiticDevice::ConcreteParasiticDevice(size_t id, - float value, - ConcreteParasiticNode *node1, - ConcreteParasiticNode *node2) : + float value, + ConcreteParasiticNode *node1, + ConcreteParasiticNode *node2) : id_(id), value_(value), node1_(node1), @@ -502,6 +499,15 @@ ConcreteParasiticNetwork::ConcreteParasiticNetwork(const Net *net, { } +ConcreteParasiticNetwork::ConcreteParasiticNetwork(ConcreteParasiticNetwork &¶sitic): + net_(parasitic.net_), + sub_nodes_(parasitic.sub_nodes_), + pin_nodes_(parasitic.pin_nodes_), + max_node_id_(parasitic.max_node_id_), + includes_pin_caps_(parasitic.includes_pin_caps_) +{ +} + ConcreteParasiticNetwork::~ConcreteParasiticNetwork() { deleteDevices(); @@ -603,7 +609,7 @@ ConcreteParasiticNetwork::findParasiticNode(const Pin *pin) const ConcreteParasiticNode * ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, - int id, + int id, const Network *network) { ConcreteParasiticNode *node; @@ -679,12 +685,12 @@ ConcreteParasiticNetwork::unannotatedLoads(ParasiticNode *node, visited_nodes.insert(node); ParasiticResistorSeq &resistors = resistor_map[node]; for (ParasiticResistor *resistor : resistors) { - if (loop_resistors.find(resistor) == loop_resistors.end()) { + if (!loop_resistors.contains(resistor)) { ParasiticNode *onode = parasitics->otherNode(resistor, node); // One commercial extractor creates resistors with identical from/to nodes. if (onode != node - && resistor != from_res) { - if (visited_nodes.find(onode) == visited_nodes.end()) + && resistor != from_res) { + if (!visited_nodes.contains(onode)) unannotatedLoads(onode, resistor, loads, visited_nodes, loop_resistors, resistor_map, parasitics); else @@ -700,7 +706,7 @@ ConcreteParasiticNetwork::unannotatedLoads(ParasiticNode *node, void ConcreteParasiticNetwork::disconnectPin(const Pin *pin, - const Net *net, + const Net *net, const Network *network) { auto pin_node = pin_nodes_.find(pin); @@ -737,31 +743,27 @@ bool NetIdPairLess::operator()(const NetIdPair &net_id1, const NetIdPair &net_id2) const { - const Net *net1 = net_id1.first; - const Net *net2 = net_id2.first; - int id1 = net_id1.second; - int id2 = net_id2.second; + const auto& [net1, id1] = net_id1; + const auto& [net2, id2] = net_id2; return net_less_(net1, net2) || (net1 == net2 - && id1 < id2); + && id1 < id2); } //////////////////////////////////////////////////////////////// -Parasitics * -makeConcreteParasitics(StaState *sta) -{ - return new ConcreteParasitics(sta); -} - -ConcreteParasitics::ConcreteParasitics(StaState *sta) : - Parasitics(sta) +ConcreteParasitics::ConcreteParasitics(std::string name, + std::string filename, + StaState *sta) : + Parasitics(sta), + name_(name), + filename_(filename) { } ConcreteParasitics::~ConcreteParasitics() { - clear(); + deleteParasitics(); } bool @@ -777,78 +779,38 @@ ConcreteParasitics::clear() deleteParasitics(); } -int -ConcreteParasitics::parasiticAnalysisPtIndex(const ParasiticAnalysisPt *ap, - const RiseFall *rf) const -{ - return ap->index() * RiseFall::index_count + rf->index(); -} - void ConcreteParasitics::deleteParasitics() { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - for (const auto [drvr, parasitics] : drvr_parasitic_map_) { - if (parasitics) { - for (int i = 0; i < ap_rf_count; i++) - delete parasitics[i]; - delete [] parasitics; - } + for (auto &[drvr, parasitics] : drvr_parasitic_map_) { + for (size_t i = 0; i < min_max_rise_fall_count; i++) + delete parasitics[i]; } drvr_parasitic_map_.clear(); - for (const auto [net, parasitics] : parasitic_network_map_) { - if (parasitics) { - for (int i = 0; i < ap_count; i++) - delete parasitics[i]; - delete [] parasitics; - } - } parasitic_network_map_.clear(); } -void -ConcreteParasitics::deleteParasitics(const Pin *drvr_pin, - const ParasiticAnalysisPt *ap) -{ - ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; - if (parasitics) { - for (auto rf : RiseFall::range()) { - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - delete parasitics[ap_rf_index]; - parasitics[ap_rf_index] = nullptr; - } - } -} - void ConcreteParasitics::deleteParasitics(const Pin *drvr_pin) { - ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; - if (parasitics) { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - for (int i = 0; i < ap_rf_count; i++) { + auto itr = drvr_parasitic_map_.find(drvr_pin); + if (itr != drvr_parasitic_map_.end()) { + const MinMaxRiseFallParasitics ¶sitics = itr->second; + for (size_t i = 0; i < min_max_rise_fall_count; i++) delete parasitics[i]; - parasitics[i] = nullptr; - } + drvr_parasitic_map_.erase(itr); } } void -ConcreteParasitics::deleteParasitics(const Net *net, - const ParasiticAnalysisPt *ap) +ConcreteParasitics::deleteParasitics(const Net *net) { PinSet *drivers = network_->drivers(net); for (auto drvr_pin : *drivers) - deleteParasitics(drvr_pin, ap); + deleteParasitics(drvr_pin); - ConcreteParasiticNetwork **parasitics = parasitic_network_map_[net]; - if (parasitics) { - delete parasitics[ap->index()]; - parasitics[ap->index()] = nullptr; - } + parasitic_network_map_.erase(net); } float @@ -867,30 +829,24 @@ ConcreteParasitics::isReducedParasiticNetwork(const Parasitic *parasitic) const void ConcreteParasitics::setIsReducedParasiticNetwork(Parasitic *parasitic, - bool is_reduced) + bool is_reduced) { ConcreteParasitic *cparasitic = static_cast(parasitic); cparasitic->setIsReduced(is_reduced); } void -ConcreteParasitics::disconnectPinBefore(const Pin *pin, - const Network *network) +ConcreteParasitics::disconnectPinBefore(const Pin *pin) { if (haveParasitics()) { deleteReducedParasitics(pin); const Net *net = findParasiticNet(pin); if (net) { - ConcreteParasiticNetwork **parasitics = parasitic_network_map_[net]; - if (parasitics) { - int ap_count = corners_->parasiticAnalysisPtCount(); - for (int i = 0; i < ap_count; i++) { - ConcreteParasiticNetwork *parasitic = parasitics[i]; - if (parasitic) - parasitic->disconnectPin(pin, net, network); - } - } + ConcreteParasiticNetwork *parasitic = + static_cast(findParasiticNetwork(pin)); + if (parasitic) + parasitic->disconnectPin(pin, net, network_); } } } @@ -899,7 +855,7 @@ void ConcreteParasitics::deletePinBefore(const Pin *pin) { // Actions are the same. - disconnectPinBefore(pin, network_); + disconnectPinBefore(pin); } void @@ -910,14 +866,13 @@ ConcreteParasitics::loadPinCapacitanceChanged(const Pin *pin) } void -ConcreteParasitics::deleteReducedParasitics(const Net *net, - const ParasiticAnalysisPt *ap) +ConcreteParasitics::deleteReducedParasitics(const Net *net) { if (!drvr_parasitic_map_.empty()) { PinSet *drivers = network_->drivers(net); if (drivers) { for (auto drvr_pin : *drivers) - deleteDrvrReducedParasitics(drvr_pin, ap); + deleteDrvrReducedParasitics(drvr_pin); } } } @@ -930,7 +885,7 @@ ConcreteParasitics::deleteReducedParasitics(const Pin *pin) PinSet *drivers = network_->drivers(pin); if (drivers) { for (auto drvr_pin : *drivers) - deleteDrvrReducedParasitics(drvr_pin); + deleteDrvrReducedParasitics(drvr_pin); } } } @@ -938,29 +893,7 @@ ConcreteParasitics::deleteReducedParasitics(const Pin *pin) void ConcreteParasitics::deleteDrvrReducedParasitics(const Pin *drvr_pin) { - LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; - if (parasitics) { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - for (int i = 0; i < ap_rf_count; i++) - delete parasitics[i]; - delete [] parasitics; - } - drvr_parasitic_map_[drvr_pin] = nullptr; -} - -void -ConcreteParasitics::deleteDrvrReducedParasitics(const Pin *drvr_pin, - const ParasiticAnalysisPt *ap) -{ - LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; - if (parasitics) { - int ap_index = ap->index(); - delete parasitics[ap_index]; - parasitics[ap_index] = nullptr; - } + deleteParasitics(drvr_pin); } //////////////////////////////////////////////////////////////// @@ -972,59 +905,61 @@ ConcreteParasitics::isPiElmore(const Parasitic *parasitic) const return cparasitic && cparasitic->isPiElmore(); } +size_t +minMaxRiseFallIndex(const MinMax *min_max, + const RiseFall *rf) +{ + return min_max->index() * RiseFall::index_count + rf->index(); +} + Parasitic * ConcreteParasitics::findPiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap) const + const RiseFall *rf, + const MinMax *min_max) const { LockGuard lock(lock_); - if (!drvr_parasitic_map_.empty()) { - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); - if (parasitics) { - ConcreteParasitic *parasitic = parasitics[ap_rf_index]; - if (parasitic && parasitic->isPiElmore()) - return parasitic; - } + auto itr = drvr_parasitic_map_.find(drvr_pin); + if (itr != drvr_parasitic_map_.end()) { + const MinMaxRiseFallParasitics ¶sitics = itr->second; + ConcreteParasitic *parasitic = parasitics[minMaxRiseFallIndex(min_max, rf)]; + if (parasitic && parasitic->isPiElmore()) + return parasitic; } return nullptr; } Parasitic * ConcreteParasitics::makePiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, - float rpi, - float c1) + const RiseFall *rf, + const MinMax *min_max, + float c2, + float rpi, + float c1) { LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); - if (parasitics == nullptr) { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - parasitics = new ConcreteParasitic*[ap_rf_count]; - for (int i = 0; i < ap_rf_count; i++) - parasitics[i] = nullptr; - drvr_parasitic_map_[drvr_pin] = parasitics; - } - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - ConcreteParasitic *parasitic = parasitics[ap_rf_index]; + auto itr = drvr_parasitic_map_.find(drvr_pin); ConcretePiElmore *pi_elmore = nullptr; - if (parasitic) { - if (parasitic->isPiElmore()) { + size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf); + if (itr != drvr_parasitic_map_.end()) { + MinMaxRiseFallParasitics ¶sitics = itr->second; + ConcreteParasitic *parasitic = parasitics[mm_rf_index]; + if (parasitic && parasitic->isPiElmore()) { pi_elmore = dynamic_cast(parasitic); pi_elmore->setPiModel(c2, rpi, c1); + pi_elmore->loads().clear(); } else { delete parasitic; pi_elmore = new ConcretePiElmore(c2, rpi, c1); - parasitics[ap_rf_index] = pi_elmore; + parasitics[mm_rf_index] = pi_elmore; } } else { + MinMaxRiseFallParasitics ¶sitics = drvr_parasitic_map_[drvr_pin]; + for (size_t i = 0; i < min_max_rise_fall_count; i++) + parasitics[i] = nullptr; pi_elmore = new ConcretePiElmore(c2, rpi, c1); - parasitics[ap_rf_index] = pi_elmore; + parasitics[mm_rf_index] = pi_elmore; } return pi_elmore; } @@ -1040,9 +975,9 @@ ConcreteParasitics::isPiModel(const Parasitic *parasitic) const void ConcreteParasitics::piModel(const Parasitic *parasitic, - float &c2, - float &rpi, - float &c1) const + float &c2, + float &rpi, + float &c1) const { const ConcreteParasitic *cparasitic = static_cast(parasitic); cparasitic->piModel(c2, rpi, c1); @@ -1050,9 +985,9 @@ ConcreteParasitics::piModel(const Parasitic *parasitic, void ConcreteParasitics::setPiModel(Parasitic *parasitic, - float c2, - float rpi, - float c1) + float c2, + float rpi, + float c1) { ConcreteParasitic *cparasitic = static_cast(parasitic); cparasitic->setPiModel(c2, rpi, c1); @@ -1062,9 +997,9 @@ ConcreteParasitics::setPiModel(Parasitic *parasitic, void ConcreteParasitics::findElmore(const Parasitic *parasitic, - const Pin *load_pin, - float &elmore, - bool &exists) const + const Pin *load_pin, + float &elmore, + bool &exists) const { const ConcreteParasitic *cparasitic = static_cast(parasitic); cparasitic->findElmore(load_pin, elmore, exists); @@ -1072,8 +1007,8 @@ ConcreteParasitics::findElmore(const Parasitic *parasitic, void ConcreteParasitics::setElmore(Parasitic *parasitic, - const Pin *load_pin, - float elmore) + const Pin *load_pin, + float elmore) { ConcreteParasitic *cparasitic = static_cast(parasitic); cparasitic->setElmore(load_pin, elmore); @@ -1090,68 +1025,60 @@ ConcreteParasitics::isPiPoleResidue(const Parasitic* parasitic) const Parasitic * ConcreteParasitics::findPiPoleResidue(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap) const + const RiseFall *rf, + const MinMax *min_max) const { - if (!drvr_parasitic_map_.empty()) { - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); - if (parasitics) { - ConcreteParasitic *parasitic = parasitics[ap_rf_index]; - if (parasitic == nullptr && rf == RiseFall::fall()) { - ap_rf_index = parasiticAnalysisPtIndex(ap, RiseFall::rise()); - parasitic = parasitics[ap_rf_index]; - } - if (parasitic && parasitic->isPiPoleResidue()) - return parasitic; - } + LockGuard lock(lock_); + auto itr = drvr_parasitic_map_.find(drvr_pin); + if (itr != drvr_parasitic_map_.end()) { + const MinMaxRiseFallParasitics ¶sitics = itr->second; + size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf); + ConcreteParasitic *parasitic = parasitics[mm_rf_index]; + if (parasitic && parasitic->isPiPoleResidue()) + return parasitic; } return nullptr; } Parasitic * ConcreteParasitics::makePiPoleResidue(const Pin *drvr_pin, - const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, - float rpi, - float c1) + const RiseFall *rf, + const MinMax *min_max, + float c2, + float rpi, + float c1) { LockGuard lock(lock_); - ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); - if (parasitics == nullptr) { - int ap_count = corners_->parasiticAnalysisPtCount(); - int ap_rf_count = ap_count * RiseFall::index_count; - parasitics = new ConcreteParasitic*[ap_rf_count]; - for (int i = 0; i < ap_rf_count; i++) - parasitics[i] = nullptr; - drvr_parasitic_map_[drvr_pin] = parasitics; - } - int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); - ConcreteParasitic *parasitic = parasitics[ap_rf_index]; + auto itr = drvr_parasitic_map_.find(drvr_pin); ConcretePiPoleResidue *pi_pole_residue = nullptr; - if (parasitic) { - if (parasitic->isPiElmore()) { + size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf); + if (itr != drvr_parasitic_map_.end()) { + MinMaxRiseFallParasitics ¶sitics = itr->second; + ConcreteParasitic *parasitic = parasitics[mm_rf_index]; + if (parasitic && parasitic->isPoleResidue()) { pi_pole_residue = dynamic_cast(parasitic); pi_pole_residue->setPiModel(c2, rpi, c1); + pi_pole_residue->loadResidues().clear(); } else { delete parasitic; pi_pole_residue = new ConcretePiPoleResidue(c2, rpi, c1); - parasitics[ap_rf_index] = pi_pole_residue; + parasitics[mm_rf_index] = pi_pole_residue; } } else { + MinMaxRiseFallParasitics ¶sitics = drvr_parasitic_map_[drvr_pin]; + for (size_t i = 0; i < min_max_rise_fall_count; i++) + parasitics[i] = nullptr; pi_pole_residue = new ConcretePiPoleResidue(c2, rpi, c1); - parasitics[ap_rf_index] = pi_pole_residue; + parasitics[mm_rf_index] = pi_pole_residue; } return pi_pole_residue; } Parasitic * ConcreteParasitics::findPoleResidue(const Parasitic *parasitic, - const Pin *load_pin) const + const Pin *load_pin) const { const ConcreteParasitic *cparasitic = static_cast(parasitic); @@ -1160,9 +1087,9 @@ ConcreteParasitics::findPoleResidue(const Parasitic *parasitic, void ConcreteParasitics::setPoleResidue(Parasitic *parasitic, - const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues) + const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) { ConcreteParasitic *cparasitic = static_cast(parasitic); @@ -1189,9 +1116,9 @@ ConcreteParasitics::poleResidueCount(const Parasitic *parasitic) const void ConcreteParasitics::poleResidue(const Parasitic *parasitic, - int pole_index, - ComplexFloat &pole, - ComplexFloat &residue) const + int pole_index, + ComplexFloat &pole, + ComplexFloat &residue) const { const ConcretePoleResidue *pr_parasitic = static_cast(parasitic); @@ -1208,39 +1135,29 @@ ConcreteParasitics::isParasiticNetwork(const Parasitic *parasitic) const } Parasitic * -ConcreteParasitics::findParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) const +ConcreteParasitics::findParasiticNetwork(const Net *net) { - if (!parasitic_network_map_.empty()) { - LockGuard lock(lock_); - if (!parasitic_network_map_.empty()) { - ConcreteParasiticNetwork **parasitics=parasitic_network_map_.findKey(net); - if (parasitics) { - ConcreteParasiticNetwork *parasitic = parasitics[ap->index()]; - if (parasitic == nullptr) - parasitic = parasitics[ap->indexMax()]; - return parasitic; - } - } - } - return nullptr; + LockGuard lock(lock_); + auto itr = parasitic_network_map_.find(net); + if (itr != parasitic_network_map_.end()) + return &itr->second; + else + return nullptr; } Parasitic * -ConcreteParasitics::findParasiticNetwork(const Pin *pin, - const ParasiticAnalysisPt *ap) const +ConcreteParasitics::findParasiticNetwork(const Pin *pin) { if (!parasitic_network_map_.empty()) { LockGuard lock(lock_); if (!parasitic_network_map_.empty()) { // Only call findParasiticNet if parasitics exist. const Net *net = findParasiticNet(pin); - ConcreteParasiticNetwork **parasitics=parasitic_network_map_.findKey(net); - if (parasitics) { - ConcreteParasiticNetwork *parasitic = parasitics[ap->index()]; - if (parasitic == nullptr) - parasitic = parasitics[ap->indexMax()]; - return parasitic; + + if (!parasitic_network_map_.empty()) { + auto itr = parasitic_network_map_.find(net); + if (itr != parasitic_network_map_.end()) + return &itr->second; } } } @@ -1249,74 +1166,25 @@ ConcreteParasitics::findParasiticNetwork(const Pin *pin, Parasitic * ConcreteParasitics::makeParasiticNetwork(const Net *net, - bool includes_pin_caps, - const ParasiticAnalysisPt *ap) + bool includes_pin_caps) { LockGuard lock(lock_); - ConcreteParasiticNetwork **parasitics = parasitic_network_map_.findKey(net); - if (parasitics == nullptr) { - int ap_count = corners_->parasiticAnalysisPtCount(); - parasitics = new ConcreteParasiticNetwork*[ap_count]; - for (int i = 0; i < ap_count; i++) - parasitics[i] = nullptr; - parasitic_network_map_[net] = parasitics; - } - int ap_index = ap->index(); - ConcreteParasiticNetwork *parasitic = parasitics[ap_index]; - if (parasitic) { - delete parasitic; - if (net) { - for (const Pin *drvr_pin : *network_->drivers(net)) - deleteParasitics(drvr_pin); - } - } - parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_); - parasitics[ap_index] = parasitic; - return parasitic; -} - -void -ConcreteParasitics::deleteParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) -{ - if (!parasitic_network_map_.empty()) { - LockGuard lock(lock_); - ConcreteParasiticNetwork **parasitics = parasitic_network_map_.findKey(net); - if (parasitics) { - int ap_index = ap->index(); - delete parasitics[ap_index]; - parasitics[ap_index] = nullptr; - - int ap_count = corners_->parasiticAnalysisPtCount(); - bool have_parasitics = false; - for (int i = 0; i < ap_count; i++) { - if (parasitics[i]) { - have_parasitics = true; - break; - } - } - if (!have_parasitics) { - delete [] parasitics; - parasitic_network_map_.erase(net); - } - } + auto itr = parasitic_network_map_.find(net); + if (itr != parasitic_network_map_.end()) { + parasitic_network_map_.erase(itr); + for (const Pin *drvr_pin : *network_->drivers(net)) + deleteParasitics(drvr_pin); } + parasitic_network_map_.emplace(net, ConcreteParasiticNetwork(net, includes_pin_caps, + network_)); + return ¶sitic_network_map_.find(net)->second; } void -ConcreteParasitics::deleteParasiticNetworks(const Net *net) +ConcreteParasitics::deleteParasiticNetwork(const Net *net) { - if (!parasitic_network_map_.empty()) { - LockGuard lock(lock_); - ConcreteParasiticNetwork **parasitics = parasitic_network_map_.findKey(net); - if (parasitics) { - int ap_count = corners_->parasiticAnalysisPtCount(); - for (int i = 0; i < ap_count; i++) - delete parasitics[i]; - delete [] parasitics; - parasitic_network_map_.erase(net); - } - } + LockGuard lock(lock_); + parasitic_network_map_.erase(net); } const Net * @@ -1351,8 +1219,8 @@ ConcreteParasitics::findParasiticNode(Parasitic *parasitic, ParasiticNode * ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic, - const Net *net, - int id, + const Net *net, + int id, const Network *network) { ConcreteParasiticNetwork *cparasitic = @@ -1371,7 +1239,7 @@ ConcreteParasitics::findParasiticNode(const Parasitic *parasitic, ParasiticNode * ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic, - const Pin *pin, + const Pin *pin, const Network *network) { ConcreteParasiticNetwork *cparasitic = @@ -1381,7 +1249,7 @@ ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic, void ConcreteParasitics::incrCap(ParasiticNode *node, - float cap) + float cap) { ConcreteParasiticNode *cnode = static_cast(node); cnode->incrCapacitance(cap); @@ -1407,8 +1275,8 @@ void ConcreteParasitics::makeResistor(Parasitic *parasitic, size_t index, float res, - ParasiticNode *node1, - ParasiticNode *node2) + ParasiticNode *node1, + ParasiticNode *node2) { ConcreteParasiticNode *cnode1 = static_cast(node1); ConcreteParasiticNode *cnode2 = static_cast(node2); diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index 9a06058d6..bf5ca5479 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -25,9 +25,9 @@ #pragma once #include +#include +#include -#include "Map.hh" -#include "Set.hh" #include "MinMax.hh" #include "Parasitics.hh" @@ -36,24 +36,31 @@ namespace sta { class ConcreteParasitic; class ConcreteParasiticNetwork; -typedef Map ConcreteParasiticMap; -typedef Map ConcreteParasiticNetworkMap; +constexpr size_t min_max_rise_fall_count = MinMax::index_count * RiseFall::index_count; +// Min/maxrise/fall reduced parasitics for each driver pin. +// When min parastitic network != max parasitic network only +// the min values are populated for the min parasitics and +// max values for max parasitics. +using MinMaxRiseFallParasitics = std::array; +using ConcreteParasiticMap = std::map; +using ConcreteParasiticNetworkMap = std::map; // This class acts as a BUILDER for parasitics. class ConcreteParasitics : public Parasitics { public: - ConcreteParasitics(StaState *sta); + ConcreteParasitics(std::string name, + std::string filename, + StaState *sta); virtual ~ConcreteParasitics(); + const std::string &name() const override { return name_; }; + const std::string &filename() const override { return filename_; }; bool haveParasitics() override; void clear() override; void deleteParasitics() override; - void deleteParasitics(const Net *net, - const ParasiticAnalysisPt *ap) override; - void deleteParasitics(const Pin *drvr_pin, - const ParasiticAnalysisPt *ap) override; - void deleteParasitics(const Pin *drvr_pin); + void deleteParasitics(const Net *net) override; + void deleteParasitics(const Pin *drvr_pin) override; bool isReducedParasiticNetwork(const Parasitic *parasitic) const override; void setIsReducedParasiticNetwork(Parasitic *parasitic, @@ -64,10 +71,10 @@ public: bool isPiElmore(const Parasitic *parasitic) const override; Parasitic *findPiElmore(const Pin *drvr_pin, const RiseFall *rf, - const ParasiticAnalysisPt *ap) const override; + const MinMax *min_max) const override; Parasitic *makePiElmore(const Pin *drvr_pin, const RiseFall *rf, - const ParasiticAnalysisPt *ap, + const MinMax *min_max, float c2, float rpi, float c1) override; @@ -93,13 +100,15 @@ public: bool isPiPoleResidue(const Parasitic* parasitic) const override; Parasitic *findPiPoleResidue(const Pin *drvr_pin, const RiseFall *rf, - const ParasiticAnalysisPt *ap) const override; + const MinMax *min_max) const override; Parasitic *findPoleResidue(const Parasitic *parasitic, const Pin *load_pin) const override; Parasitic *makePiPoleResidue(const Pin *drvr_pin, const RiseFall *rf, - const ParasiticAnalysisPt *ap, - float c2, float rpi, float c1) override; + const MinMax *min_max, + float c2, + float rpi, + float c1) override; void setPoleResidue(Parasitic *parasitic, const Pin *load_pin, ComplexFloatSeq *poles, ComplexFloatSeq *residues) override; @@ -111,16 +120,11 @@ public: ComplexFloat &residue) const override; bool isParasiticNetwork(const Parasitic *parasitic) const override; - Parasitic *findParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) const override; - Parasitic *findParasiticNetwork(const Pin *pin, - const ParasiticAnalysisPt *ap) const override; + Parasitic *findParasiticNetwork(const Net *net) override; + Parasitic *findParasiticNetwork(const Pin *pin) override; Parasitic *makeParasiticNetwork(const Net *net, - bool includes_pin_caps, - const ParasiticAnalysisPt *ap) override; - void deleteParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) override; - void deleteParasiticNetworks(const Net *net) override; + bool includes_pin_caps) override; + void deleteParasiticNetwork(const Net *net) override; const Net *net(const Parasitic *parasitic) const override; bool includesPinCaps(const Parasitic *parasitic) const override; ParasiticNode *findParasiticNode(Parasitic *parasitic, @@ -171,23 +175,20 @@ public: PinSet unannotatedLoads(const Parasitic *parasitic, const Pin *drvr_pin) const override; - void disconnectPinBefore(const Pin *pin, - const Network *network) override; + void disconnectPinBefore(const Pin *pin) override; void deletePinBefore(const Pin *pin) override; void loadPinCapacitanceChanged(const Pin *pin) override; - void deleteReducedParasitics(const Net *net, - const ParasiticAnalysisPt *ap) override; + void deleteReducedParasitics(const Net *net) override; void deleteDrvrReducedParasitics(const Pin *drvr_pin) override; protected: - int parasiticAnalysisPtIndex(const ParasiticAnalysisPt *ap, - const RiseFall *rf) const; Parasitic *ensureRspf(const Pin *drvr_pin); void makeAnalysisPtAfter(); void deleteReducedParasitics(const Pin *pin); - void deleteDrvrReducedParasitics(const Pin *drvr_pin, - const ParasiticAnalysisPt *ap); + + std::string name_; + std::string filename_; // Driver pin to array of parasitics indexed by analysis pt index // and transition. diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh index 72c44a9c9..597512dc3 100644 --- a/parasitics/ConcreteParasiticsPvt.hh +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -35,26 +35,27 @@ class ConcretePoleResidue; class ConcreteParasiticDevice; class ConcreteParasiticNode; -typedef std::pair NetIdPair; +using NetIdPair = std::pair; + class NetIdPairLess { public: NetIdPairLess(const Network *network); bool operator()(const NetIdPair &net_id1, - const NetIdPair &net_id2) const; + const NetIdPair &net_id2) const; private: const NetIdLess net_less_; }; -typedef std::map ConcreteElmoreLoadMap; -typedef std::map ConcretePoleResidueMap; -typedef std::map ConcreteParasiticSubNodeMap; -typedef std::map ConcreteParasiticPinNodeMap; -typedef std::set ParasiticNodeSet; -typedef std::set ParasiticResistorSet; -typedef std::vector ParasiticResistorSeq; +using ConcreteElmoreLoadMap = std::map; +using ConcretePoleResidueMap = std::map; +using ConcreteParasiticSubNodeMap = std::map; +using ConcreteParasiticPinNodeMap = std::map; +using ParasiticNodeSet = std::set; +using ParasiticResistorSet = std::set; +using ParasiticResistorSeq = std::vector; // Empty base class definitions so casts are not required on returned // objects. @@ -76,22 +77,22 @@ public: virtual bool isPoleResidue() const; virtual bool isParasiticNetwork() const; virtual void piModel(float &c2, - float &rpi, - float &c1) const; + float &rpi, + float &c1) const; virtual void setPiModel(float c2, - float rpi, - float c1); + float rpi, + float c1); virtual bool isReducedParasiticNetwork() const; virtual void setIsReduced(bool reduced); virtual void findElmore(const Pin *load_pin, - float &elmore, - bool &exists) const; + float &elmore, + bool &exists) const; virtual void setElmore(const Pin *load_pin, - float elmore); + float elmore); virtual Parasitic *findPoleResidue(const Pin *load_pin) const; virtual void setPoleResidue(const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues); + ComplexFloatSeq *poles, + ComplexFloatSeq *residues); virtual PinSet unannotatedLoads(const Pin *drvr_pin, const Parasitics *parasitics) const = 0; }; @@ -101,15 +102,15 @@ class ConcretePi { public: ConcretePi(float c2, - float rpi, - float c1); + float rpi, + float c1); float capacitance() const; void piModel(float &c2, - float &rpi, - float &c1) const; + float &rpi, + float &c1) const; void setPiModel(float c2, - float rpi, - float c1); + float rpi, + float c1); bool isReducedParasiticNetwork() const { return is_reduced_; } void setIsReduced(bool reduced); @@ -122,12 +123,12 @@ protected: // Pi model for a driver pin and the elmore delay to each load. class ConcretePiElmore : public ConcretePi, - public ConcreteParasitic + public ConcreteParasitic { public: ConcretePiElmore(float c2, - float rpi, - float c1); + float rpi, + float c1); bool isPiElmore() const override { return true; } bool isPiModel() const override { return true; } float capacitance() const override; @@ -147,6 +148,7 @@ public: PinSet unannotatedLoads(const Pin *drvr_pin, const Parasitics *parasitics) const override; void deleteLoad(const Pin *load_pin); + ConcreteElmoreLoadMap &loads() { return loads_; } private: ConcreteElmoreLoadMap loads_; @@ -177,42 +179,44 @@ private: // Pi model for a driver pin and the pole/residues to each load. class ConcretePiPoleResidue : public ConcretePi, - public ConcreteParasitic + public ConcreteParasitic { public: ConcretePiPoleResidue(float c2, - float rpi, - float c1); + float rpi, + float c1); virtual bool isPiPoleResidue() const override { return true; } virtual bool isPiModel() const override { return true; } virtual float capacitance() const override; virtual void piModel(float &c2, - float &rpi, - float &c1) const override; + float &rpi, + float &c1) const override; virtual void setPiModel(float c2, - float rpi, - float c1) override; + float rpi, + float c1) override; virtual bool isReducedParasiticNetwork() const override; virtual void setIsReduced(bool reduced) override; virtual Parasitic *findPoleResidue(const Pin *load_pin) const override; virtual void setPoleResidue(const Pin *load_pin, - ComplexFloatSeq *poles, - ComplexFloatSeq *residues) override; + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) override; virtual PinSet unannotatedLoads(const Pin *drvr_pin, const Parasitics *parasitics) const override; void deleteLoad(const Pin *load_pin); + ConcretePoleResidueMap &loadResidues() { return load_pole_residue_; } private: ConcretePoleResidueMap load_pole_residue_; }; class ConcreteParasiticNetwork : public ParasiticNetwork, - public ConcreteParasitic + public ConcreteParasitic { public: ConcreteParasiticNetwork(const Net *net, bool includes_pin_caps, const Network *network); + ConcreteParasiticNetwork(ConcreteParasiticNetwork &¶sitic); virtual ~ConcreteParasiticNetwork(); virtual bool isParasiticNetwork() const { return true; } const Net *net() const { return net_; } @@ -221,7 +225,7 @@ public: int id, const Network *network) const; ConcreteParasiticNode *ensureParasiticNode(const Net *net, - int id, + int id, const Network *network); ConcreteParasiticNode *findParasiticNode(const Pin *pin) const; ConcreteParasiticNode *ensureParasiticNode(const Pin *pin, @@ -229,7 +233,7 @@ public: virtual float capacitance() const; ParasiticNodeSeq nodes() const; void disconnectPin(const Pin *pin, - const Net *net, + const Net *net, const Network *network); ParasiticResistorSeq resistors() const { return resistors_; } void addResistor(ParasiticResistor *resistor); diff --git a/parasitics/EstimateParasitics.cc b/parasitics/EstimateParasitics.cc index 5f656cfbb..415a7f1cb 100644 --- a/parasitics/EstimateParasitics.cc +++ b/parasitics/EstimateParasitics.cc @@ -42,20 +42,21 @@ EstimateParasitics::EstimateParasitics(StaState *sta) : // loads when driven by a different pin. void EstimateParasitics::estimatePiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const Wireload *wireload, - float fanout, - float net_pin_cap, - const Corner *corner, - const MinMax *min_max, - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) + const RiseFall *rf, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) { - const OperatingConditions *op_cond = sdc_->operatingConditions(min_max); + const Sdc *sdc = scene->sdc(); + const OperatingConditions *op_cond = sdc->operatingConditions(min_max); float wireload_cap, wireload_res; wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res); @@ -65,22 +66,22 @@ EstimateParasitics::estimatePiElmore(const Pin *drvr_pin, switch (tree) { case WireloadTree::worst_case: estimatePiElmoreWorst(drvr_pin, wireload_cap, wireload_res, - fanout, net_pin_cap, rf, corner, min_max, - c2, rpi, c1, elmore_res, - elmore_cap, elmore_use_load_cap); + fanout, net_pin_cap, rf, scene, min_max, + c2, rpi, c1, elmore_res, + elmore_cap, elmore_use_load_cap); break; case WireloadTree::balanced: case WireloadTree::unknown: estimatePiElmoreBalanced(drvr_pin, wireload_cap, wireload_res, - fanout, net_pin_cap, rf, corner, min_max, - c2, rpi, c1, elmore_res, - elmore_cap, elmore_use_load_cap); + fanout, net_pin_cap, rf, scene, min_max, + c2, rpi, c1, elmore_res, + elmore_cap, elmore_use_load_cap); break; case WireloadTree::best_case: estimatePiElmoreBest(drvr_pin, wireload_cap, net_pin_cap, - rf, corner, min_max, - c2, rpi, c1, elmore_res, elmore_cap, - elmore_use_load_cap); + rf, scene, min_max, + c2, rpi, c1, elmore_res, elmore_cap, + elmore_use_load_cap); break; } } @@ -88,17 +89,17 @@ EstimateParasitics::estimatePiElmore(const Pin *drvr_pin, // No wire resistance, so load is lumped capacitance. void EstimateParasitics::estimatePiElmoreBest(const Pin *, - float wireload_cap, - float net_pin_cap, - const RiseFall *, - const Corner *, - const MinMax *, - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) const + float wireload_cap, + float net_pin_cap, + const RiseFall *, + const Scene *, + const MinMax *, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) const { c2 = wireload_cap + net_pin_cap; rpi = 0.0; @@ -112,22 +113,23 @@ EstimateParasitics::estimatePiElmoreBest(const Pin *, // the resistor. void EstimateParasitics::estimatePiElmoreWorst(const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float, - float net_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) + float wireload_cap, + float wireload_res, + float, + float net_pin_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) { + const Sdc *sdc = scene->sdc(); float drvr_pin_cap = 0.0; - drvr_pin_cap = sdc_->pinCapacitance(drvr_pin, rf, corner, min_max); + drvr_pin_cap = sdc->pinCapacitance(drvr_pin, rf, scene, min_max); c2 = drvr_pin_cap; rpi = wireload_res; c1 = net_pin_cap - drvr_pin_cap + wireload_cap; @@ -141,19 +143,19 @@ EstimateParasitics::estimatePiElmoreWorst(const Pin *drvr_pin, // Use O'Brien/Savarino reduction to rspf (pi elmore) model. void EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout, - float net_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) + float wireload_cap, + float wireload_res, + float fanout, + float net_pin_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) { if (wireload_res == 0.0 || fanout == 0.0) { @@ -172,7 +174,8 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, double y1 = 0.0; double y2 = 0.0; double y3 = 0.0; - y1 = sdc_->pinCapacitance(drvr_pin, rf, corner, min_max); + const Sdc *sdc = scene->sdc(); + y1 = sdc->pinCapacitance(drvr_pin, rf, scene, min_max); PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin); while (load_iter->hasNext()) { @@ -181,11 +184,11 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, double cap = 0.0; // Bidirects don't count themselves as loads. if (load_pin == drvr_pin) - cap = sdc_->portExtCap(port, rf, corner, min_max); + cap = sdc->portExtCap(port, rf, min_max); else if (network_->isLeaf(load_pin)) - cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max) + cap_fanout; + cap = sdc->pinCapacitance(load_pin, rf, scene, min_max) + cap_fanout; else if (network_->isTopLevelPort(load_pin)) - cap = sdc_->portExtCap(port, rf, corner, min_max) + cap_fanout; + cap = sdc->portExtCap(port, rf, min_max) + cap_fanout; double y2_ = res_fanout * cap * cap; y1 += cap; y2 += -y2_; @@ -224,7 +227,7 @@ selectWireload(Network *network) static float instanceArea(Instance *inst, - Network *network) + Network *network) { float area = 0.0; LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); diff --git a/parasitics/EstimateParasitics.hh b/parasitics/EstimateParasitics.hh index 689183eaa..a6adf42ea 100644 --- a/parasitics/EstimateParasitics.hh +++ b/parasitics/EstimateParasitics.hh @@ -32,7 +32,7 @@ namespace sta { -class Corner; +class Scene; class StaState; class EstimateParasitics : public StaState @@ -41,58 +41,58 @@ public: EstimateParasitics(StaState *sta); // Helper function for wireload estimation. void estimatePiElmore(const Pin *drvr_pin, - const RiseFall *rf, - const Wireload *wireload, - float fanout, - float net_pin_cap, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap); + const RiseFall *rf, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap); protected: void estimatePiElmoreBest(const Pin *drvr_pin, - float net_pin_cap, - float wireload_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &c2, - float &rpi, - float &c1, - float &elmore_res, - float &elmore_cap, - bool &elmore_use_load_cap) const; + float net_pin_cap, + float wireload_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) const; void estimatePiElmoreWorst(const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout, - float net_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &c2, float &rpi, float &c1, - float &elmore_res, float &elmore_cap, - bool &elmore_use_load_cap); + float wireload_cap, + float wireload_res, + float fanout, + float net_pin_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &c2, float &rpi, float &c1, + float &elmore_res, float &elmore_cap, + bool &elmore_use_load_cap); void estimatePiElmoreBalanced(const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout, - float net_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &c2, float &rpi, float &c1, - float &elmore_res, float &elmore_cap, - bool &elmore_use_load_cap); + float wireload_cap, + float wireload_res, + float fanout, + float net_pin_cap, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &c2, float &rpi, float &c1, + float &elmore_res, float &elmore_cap, + bool &elmore_use_load_cap); }; } // namespace diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index 400e9239f..cd7da4e3a 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -32,7 +32,7 @@ #include "Network.hh" #include "PortDirection.hh" #include "Sdc.hh" -#include "Corner.hh" +#include "Scene.hh" #include "ReduceParasitics.hh" #include "EstimateParasitics.hh" @@ -182,26 +182,24 @@ Parasitic * Parasitics::reduceToPiElmore(const Parasitic *parasitic, const Pin *drvr_pin, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap) + const Scene *scene, + const MinMax *min_max) { - return sta::reduceToPiElmore(parasitic, drvr_pin, rf, ap->couplingCapFactor(), - corner, cnst_min_max, ap, this); + return sta::reduceToPiElmore(parasitic, drvr_pin, rf, + coupling_cap_factor_, + scene, min_max, this); } Parasitic * Parasitics::reduceToPiPoleResidue2(const Parasitic *parasitic, const Pin *drvr_pin, const RiseFall *rf, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap) + const Scene *scene, + const MinMax *min_max) { return sta::reduceToPiPoleResidue2(parasitic, drvr_pin, rf, - ap->couplingCapFactor(), - corner, cnst_min_max, - ap, this); + coupling_cap_factor_, + scene, min_max, this); } //////////////////////////////////////////////////////////////// @@ -212,27 +210,27 @@ Parasitics::estimatePiElmore(const Pin *drvr_pin, const Wireload *wireload, float fanout, float net_pin_cap, - const Corner *corner, + const Scene *scene, const MinMax *min_max) { 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, - corner, min_max, + scene, min_max, c2, rpi, c1, elmore_res, elmore_cap, elmore_use_load_cap); if (c1 > 0.0 || c2 > 0.0) { - ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max); - Parasitic *parasitic = makePiElmore(drvr_pin, rf, ap, c2, rpi, c1); + Parasitic *parasitic = makePiElmore(drvr_pin, rf, min_max, c2, rpi, c1); + const Sdc *sdc = scene->sdc(); NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (network_->isLoad(pin)) { float load_cap = 0.0; if (elmore_use_load_cap) - load_cap = sdc_->pinCapacitance(pin, rf, corner, min_max); + load_cap = sdc->pinCapacitance(pin, rf, scene, min_max); float elmore = elmore_res * (elmore_cap + load_cap); setElmore(parasitic, pin, elmore); } @@ -248,16 +246,17 @@ Parasitics::estimatePiElmore(const Pin *drvr_pin, Parasitic * Parasitics::makeWireloadNetwork(const Pin *drvr_pin, - const Wireload *wireload, - float fanout, - const MinMax *min_max, - const ParasiticAnalysisPt *ap) + const Wireload *wireload, + float fanout, + const Scene *scene, + const MinMax *min_max) { Parasitic *parasitic = nullptr; const Net *net = findParasiticNet(drvr_pin); if (net) { - parasitic = makeParasiticNetwork(net, false, ap); - const OperatingConditions *op_cond = sdc_->operatingConditions(min_max); + parasitic = makeParasiticNetwork(net, false); + const Sdc *sdc = scene->sdc(); + const OperatingConditions *op_cond = sdc->operatingConditions(min_max); float wireload_cap, wireload_res; wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res); @@ -287,23 +286,23 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin, // the resistor. void Parasitics::makeWireloadNetworkWorst(Parasitic *parasitic, - const Pin *drvr_pin, + const Pin *drvr_pin, const Net *net, - float wireload_cap, - float wireload_res, - float /* fanout */) + float wireload_cap, + float wireload_res, + float /* fanout */) { ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_); size_t resistor_index = 1; ParasiticNode *load_node = ensureParasiticNode(parasitic, net, 0, network_); makeResistor(parasitic, resistor_index++, wireload_res, drvr_node, load_node); - parasitics_->incrCap(load_node, wireload_cap); + incrCap(load_node, wireload_cap); 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)) { + && network_->isLoad(load_pin)) { ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_); makeResistor(parasitic, resistor_index++, 0.0, load_node, load_node1); } @@ -313,20 +312,20 @@ Parasitics::makeWireloadNetworkWorst(Parasitic *parasitic, // No wire resistance, so load is lumped capacitance. void Parasitics::makeWireloadNetworkBest(Parasitic *parasitic, - const Pin *drvr_pin, - float wireload_cap, - float /* wireload_res */, - float /* fanout */) + const Pin *drvr_pin, + float wireload_cap, + float /* wireload_res */, + float /* fanout */) { ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_); - parasitics_->incrCap(drvr_node, wireload_cap); + incrCap(drvr_node, wireload_cap); 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)) { + && network_->isLoad(load_pin)) { ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_); makeResistor(parasitic, resistor_index++, 0.0, drvr_node, load_node1); } @@ -337,10 +336,10 @@ Parasitics::makeWireloadNetworkBest(Parasitic *parasitic, // connecting it to the driver. void Parasitics::makeWireloadNetworkBalanced(Parasitic *parasitic, - const Pin *drvr_pin, - float wireload_cap, - float wireload_res, - float fanout) + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout) { float fanout_cap = wireload_cap / fanout; float fanout_res = wireload_res / fanout; @@ -351,34 +350,28 @@ Parasitics::makeWireloadNetworkBalanced(Parasitic *parasitic, while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); if (load_pin != drvr_pin - && network_->isLoad(load_pin)) { + && network_->isLoad(load_pin)) { ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_); makeResistor(parasitic, resistor_index++, fanout_res, drvr_node, load_node1); - parasitics_->incrCap(load_node1, fanout_cap); + incrCap(load_node1, fanout_cap); } } } -//////////////////////////////////////////////////////////////// - -ParasiticAnalysisPt::ParasiticAnalysisPt(const char *name, - int index, - int index_max) : - name_(name), - index_(index), - index_max_(index_max), - coupling_cap_factor_(1.0) -{ -} - void -ParasiticAnalysisPt::setCouplingCapFactor(float factor) +Parasitics::setCouplingCapFactor(float factor) { coupling_cap_factor_ = factor; } //////////////////////////////////////////////////////////////// +ParasiticNodeLess::ParasiticNodeLess() : + parasitics_(nullptr), + network_(nullptr) +{ +} + ParasiticNodeLess::ParasiticNodeLess(const Parasitics *parasitics, const Network *network) : parasitics_(parasitics), @@ -386,12 +379,6 @@ ParasiticNodeLess::ParasiticNodeLess(const Parasitics *parasitics, { } -ParasiticNodeLess::ParasiticNodeLess(const ParasiticNodeLess &less) : - parasitics_(less.parasitics_), - network_(less.network_) -{ -} - bool ParasiticNodeLess::operator()(const ParasiticNode *node1, const ParasiticNode *node2) const diff --git a/parasitics/Parasitics.i b/parasitics/Parasitics.i index 2854d38eb..548b50147 100644 --- a/parasitics/Parasitics.i +++ b/parasitics/Parasitics.i @@ -38,31 +38,33 @@ using sta::Pin; %inline %{ bool -read_spef_cmd(const char *filename, - Instance *instance, - const Corner *corner, +read_spef_cmd(const char *name, + const char *filename, + Instance *instance, + Scene *scene, const MinMaxAll *min_max, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce) + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce) { - return Sta::sta()->readSpef(filename, instance, corner, min_max, - pin_cap_included, keep_coupling_caps, + return Sta::sta()->readSpef(name, filename, instance, + scene, min_max, + pin_cap_included, keep_coupling_caps, coupling_cap_factor, reduce); } void -report_parasitic_annotation_cmd(bool report_unannotated, - const Corner *corner) +report_parasitic_annotation_cmd(const char *spef_name, + bool report_unannotated) { - Sta::sta()->reportParasiticAnnotation(report_unannotated, corner); + Sta::sta()->reportParasiticAnnotation(spef_name, report_unannotated); } FloatSeq find_pi_elmore(Pin *drvr_pin, - RiseFall *rf, - MinMax *min_max) + RiseFall *rf, + MinMax *min_max) { float c2, rpi, c1; bool exists; @@ -78,9 +80,9 @@ find_pi_elmore(Pin *drvr_pin, float find_elmore(Pin *drvr_pin, - Pin *load_pin, - RiseFall *rf, - MinMax *min_max) + Pin *load_pin, + RiseFall *rf, + MinMax *min_max) { float elmore = 0.0; bool exists; @@ -90,21 +92,21 @@ find_elmore(Pin *drvr_pin, void set_pi_model_cmd(Pin *drvr_pin, - RiseFall *rf, - MinMaxAll *min_max, - float c2, - float rpi, - float c1) + RiseFall *rf, + MinMaxAll *min_max, + float c2, + float rpi, + float c1) { Sta::sta()->makePiElmore(drvr_pin, rf, min_max, c2, rpi, c1); } void set_elmore_cmd(Pin *drvr_pin, - Pin *load_pin, - RiseFall *rf, - MinMaxAll *min_max, - float elmore) + Pin *load_pin, + RiseFall *rf, + MinMaxAll *min_max, + float elmore) { Sta::sta()->setElmore(drvr_pin, load_pin, rf, min_max, elmore); } diff --git a/parasitics/Parasitics.tcl b/parasitics/Parasitics.tcl index 15e8e81d4..2b4ce36ee 100644 --- a/parasitics/Parasitics.tcl +++ b/parasitics/Parasitics.tcl @@ -25,7 +25,8 @@ namespace eval sta { define_cmd_args "read_spef" \ - {[-corner corner]\ + {[-name spef_name] + [-corner corner]\ [-min]\ [-max]\ [-path path]\ @@ -33,16 +34,19 @@ define_cmd_args "read_spef" \ [-keep_capacitive_coupling]\ [-coupling_reduction_factor factor]\ [-reduce]\ - [-delete_after_reduce]\ filename} +# -scene/-min/-max are for compatibilty, Deprecated 11/21/2025 proc_redirect read_spef { parse_key_args "read_spef" args \ - keys {-path -coupling_reduction_factor -corner -name} \ + keys {-name -path -coupling_reduction_factor -corner} \ flags {-min -max -increment -pin_cap_included -keep_capacitive_coupling -reduce} - check_argc_eq1 "read_spef" $args - set reduce [info exists flags(-reduce)] + check_argc_eq1 "read_spef" $args + set name "" + if [info exists keys(-name)] { + set name $keys(-name) + } set instance [top_instance] if [info exists keys(-path)] { set path $keys(-path) @@ -51,7 +55,7 @@ proc_redirect read_spef { sta_error 276 "path instance '$path' not found." } } - set corner [parse_corner_or_all keys] + set scene [parse_scene_or_null keys] set min_max [parse_min_max_all_flags flags] set coupling_reduction_factor 1.0 if [info exists keys(-coupling_reduction_factor)] { @@ -60,22 +64,28 @@ proc_redirect read_spef { } set keep_coupling_caps [info exists flags(-keep_capacitive_coupling)] set pin_cap_included [info exists flags(-pin_cap_included)] + set reduce [info exists flags(-reduce)] set filename [file nativename [lindex $args 0]] - return [read_spef_cmd $filename $instance $corner $min_max \ - $pin_cap_included $keep_coupling_caps \ + return [read_spef_cmd $name $filename $instance $scene $min_max \ + $pin_cap_included $keep_coupling_caps \ $coupling_reduction_factor $reduce] } -define_cmd_args "report_parasitic_annotation" {[-report_unannotated]} +define_cmd_args "report_parasitic_annotation" {[-name spef_name]\ + [-report_unannotated]} proc_redirect report_parasitic_annotation { parse_key_args "report_parasitic_annotation" args \ - keys {} flags {-report_unannotated} + keys {-name} flags {-report_unannotated} check_argc_eq0 "report_parasitic_annotation" $args + set spef_name "" + if { [info exists keys(-name)] } { + set spef_name $keys(-name) + } set report_unannotated [info exists flags(-report_unannotated)] - report_parasitic_annotation_cmd $report_unannotated [sta::cmd_corner] + report_parasitic_annotation_cmd $spef_name $report_unannotated } # set_pi_model [-min] [-max] drvr_pin c2 rpi c1 diff --git a/parasitics/ReduceParasitics.cc b/parasitics/ReduceParasitics.cc index 7f2f4ef1d..1650f8e3e 100644 --- a/parasitics/ReduceParasitics.cc +++ b/parasitics/ReduceParasitics.cc @@ -24,23 +24,26 @@ #include "ReduceParasitics.hh" +#include +#include + #include "Error.hh" #include "Debug.hh" #include "MinMax.hh" #include "Liberty.hh" #include "Network.hh" #include "Sdc.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Parasitics.hh" namespace sta { using std::max; -typedef Map ParasiticNodeValueMap; -typedef Map ResistorCurrentMap; -typedef Set ParasiticResistorSet; -typedef Set ParasiticNodeSet; +typedef std::map ParasiticNodeValueMap; +typedef std::map ResistorCurrentMap; +typedef std::set ParasiticResistorSet; +typedef std::set ParasiticNodeSet; class ReduceToPi : public StaState { @@ -48,43 +51,42 @@ class ReduceToPi : public StaState ReduceToPi(StaState *sta); void reduceToPi(const Parasitic *parasitic_network, const Pin *drvr_pin, - ParasiticNode *drvr_node, - float coupling_cap_factor, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - float &c2, - float &rpi, - float &c1); + ParasiticNode *drvr_node, + float coupling_cap_factor, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1); bool pinCapsOneValue() { return pin_caps_one_value_; } protected: void reducePiDfs(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, + ParasiticNode *node, + ParasiticResistor *from_res, double src_resistance, - double &y1, - double &y2, - double &y3, - double &dwn_cap, + double &y1, + double &y2, + double &y3, + double &dwn_cap, double &max_resistance); void visit(ParasiticNode *node); bool isVisited(ParasiticNode *node); void leave(ParasiticNode *node); void setDownstreamCap(ParasiticNode *node, - float cap); + float cap); float downstreamCap(ParasiticNode *node); float pinCapacitance(ParasiticNode *node); bool isLoopResistor(ParasiticResistor *resistor); void markLoopResistor(ParasiticResistor *resistor); + Parasitics *parasitics_; bool includes_pin_caps_; float coupling_cap_multiplier_; const RiseFall *rf_; - const Corner *corner_; + const Scene *scene_; const MinMax *min_max_; - const ParasiticAnalysisPt *ap_; ParasiticNodeResistorMap resistor_map_; ParasiticNodeCapacitorMap capacitor_map_; @@ -98,7 +100,7 @@ ReduceToPi::ReduceToPi(StaState *sta) : StaState(sta), coupling_cap_multiplier_(1.0), rf_(nullptr), - corner_(nullptr), + scene_(nullptr), min_max_(nullptr), pin_caps_one_value_(true) { @@ -111,22 +113,21 @@ ReduceToPi::ReduceToPi(StaState *sta) : void ReduceToPi::reduceToPi(const Parasitic *parasitic_network, const Pin *drvr_pin, - ParasiticNode *drvr_node, - float coupling_cap_factor, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - float &c2, - float &rpi, - float &c1) + ParasiticNode *drvr_node, + float coupling_cap_factor, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1) { - includes_pin_caps_ = parasitics_->includesPinCaps(parasitic_network), coupling_cap_multiplier_ = coupling_cap_factor; rf_ = rf; - corner_ = corner; + scene_ = scene; min_max_ = min_max; - ap_ = ap; + parasitics_ = scene_->parasitics(min_max); + includes_pin_caps_ = parasitics_->includesPinCaps(parasitic_network), resistor_map_ = parasitics_->parasiticNodeResistorMap(parasitic_network); capacitor_map_ = parasitics_->parasiticNodeCapacitorMap(parasitic_network); @@ -154,13 +155,13 @@ ReduceToPi::reduceToPi(const Parasitic *parasitic_network, // Find admittance moments. void ReduceToPi::reducePiDfs(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - double src_resistance, - double &y1, - double &y2, - double &y3, - double &dwn_cap, + ParasiticNode *node, + ParasiticResistor *from_res, + double src_resistance, + double &y1, + double &y2, + double &y3, + double &dwn_cap, double &max_resistance) { double coupling_cap = 0.0; @@ -220,14 +221,15 @@ ReduceToPi::pinCapacitance(ParasiticNode *node) if (pin) { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(port); + const Sdc *sdc = scene_->sdc(); if (lib_port) { if (!includes_pin_caps_) { - pin_cap = sdc_->pinCapacitance(pin, rf_, corner_, min_max_); - pin_caps_one_value_ &= lib_port->capacitanceIsOneValue(); + pin_cap = sdc->pinCapacitance(pin, rf_, scene_, min_max_); + pin_caps_one_value_ &= lib_port->capacitanceIsOneValue(); } } else if (network_->isTopLevelPort(pin)) - pin_cap = sdc_->portExtCap(port, rf_, corner_, min_max_); + pin_cap = sdc->portExtCap(port, rf_, min_max_); } return pin_cap; } @@ -241,7 +243,7 @@ ReduceToPi::visit(ParasiticNode *node) bool ReduceToPi::isVisited(ParasiticNode *node) { - return visited_nodes_.hasKey(node); + return visited_nodes_.contains(node); } void @@ -253,7 +255,7 @@ ReduceToPi::leave(ParasiticNode *node) bool ReduceToPi::isLoopResistor(ParasiticResistor *resistor) { - return loop_resistors_.hasKey(resistor); + return loop_resistors_.contains(resistor); } void @@ -264,7 +266,7 @@ ReduceToPi::markLoopResistor(ParasiticResistor *resistor) void ReduceToPi::setDownstreamCap(ParasiticNode *node, - float cap) + float cap) { node_values_[node] = cap; } @@ -286,27 +288,25 @@ class ReduceToPiElmore : public ReduceToPi ParasiticNode *drvr_node, float coupling_cap_factor, const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); void reduceElmoreDfs(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - double elmore, - Parasitic *pi_elmore); + ParasiticNode *node, + ParasiticResistor *from_res, + double elmore, + Parasitic *pi_elmore); }; Parasitic * reduceToPiElmore(const Parasitic *parasitic_network, - const Pin *drvr_pin, + const Pin *drvr_pin, const RiseFall *rf, float coupling_cap_factor, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - StaState *sta) + const Scene *scene, + const MinMax *min_max, + StaState *sta) { - Parasitics *parasitics = sta->parasitics(); + Parasitics *parasitics = scene->parasitics(min_max); ParasiticNode *drvr_node = parasitics->findParasiticNode(parasitic_network, drvr_pin); if (drvr_node) { @@ -316,8 +316,7 @@ reduceToPiElmore(const Parasitic *parasitic_network, min_max->to_string().c_str()); ReduceToPiElmore reducer(sta); return reducer.makePiElmore(parasitic_network, drvr_pin, drvr_node, - coupling_cap_factor, rf, corner, - min_max, ap); + coupling_cap_factor, rf, scene, min_max); } return nullptr; } @@ -329,19 +328,18 @@ ReduceToPiElmore::ReduceToPiElmore(StaState *sta) : Parasitic * ReduceToPiElmore::makePiElmore(const Parasitic *parasitic_network, - const Pin *drvr_pin, - ParasiticNode *drvr_node, - float coupling_cap_factor, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap) + const Pin *drvr_pin, + ParasiticNode *drvr_node, + float coupling_cap_factor, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { float c2, rpi, c1; reduceToPi(parasitic_network, drvr_pin, drvr_node, coupling_cap_factor, - rf, corner, min_max, ap, c2, rpi, c1); - Parasitic *pi_elmore = parasitics_->makePiElmore(drvr_pin, rf, ap, - c2, rpi, c1); + rf, scene, min_max, c2, rpi, c1); + Parasitic *pi_elmore = parasitics_->makePiElmore(drvr_pin, rf, min_max, + c2, rpi, c1); parasitics_->setIsReducedParasiticNetwork(pi_elmore, true); reduceElmoreDfs(drvr_pin, drvr_node, 0, 0.0, pi_elmore); return pi_elmore; @@ -351,10 +349,10 @@ ReduceToPiElmore::makePiElmore(const Parasitic *parasitic_network, // set by reducePiDfs. void ReduceToPiElmore::reduceElmoreDfs(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - double elmore, - Parasitic *pi_elmore) + ParasiticNode *node, + ParasiticResistor *from_res, + double elmore, + Parasitic *pi_elmore) { const Pin *pin = parasitics_->pin(node); if (from_res && pin) { @@ -389,42 +387,41 @@ class ReduceToPiPoleResidue2 : public ReduceToPi ~ReduceToPiPoleResidue2(); void findPolesResidues(const Parasitic *parasitic_network, Parasitic *pi_pole_residue, - const Pin *drvr_pin, - ParasiticNode *drvr_node); + const Pin *drvr_pin, + ParasiticNode *drvr_node); Parasitic *makePiPoleResidue2(const Parasitic *parasitic_network, const Pin *drvr_pin, ParasiticNode *drvr_node, float coupling_cap_factor, const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap); + const Scene *scene, + const MinMax *min_max); private: void findMoments(const Pin *drvr_pin, - ParasiticNode *drvr_node, - int moment_count); + ParasiticNode *drvr_node, + int moment_count); void findMoments(const Pin *drvr_pin, - ParasiticNode *node, - double from_volt, - ParasiticResistor *from_res, - int moment_index); + ParasiticNode *node, + double from_volt, + ParasiticResistor *from_res, + int moment_index); double findBranchCurrents(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - int moment_index); + ParasiticNode *node, + ParasiticResistor *from_res, + int moment_index); double moment(ParasiticNode *node, - int moment_index); + int moment_index); void setMoment(ParasiticNode *node, - double moment, - int moment_index); + double moment, + int moment_index); double current(ParasiticResistor *res); void setCurrent(ParasiticResistor *res, - double i); + double i); void findPolesResidues(Parasitic *pi_pole_residue, - const Pin *drvr_pin, - const Pin *load_pin, - ParasiticNode *load_node); + const Pin *drvr_pin, + const Pin *load_pin, + ParasiticNode *load_node); // Resistor/capacitor currents. ResistorCurrentMap currents_; @@ -449,15 +446,14 @@ ReduceToPiPoleResidue2::ReduceToPiPoleResidue2(StaState *sta) : // Design Automation Conference, 1996, pg 611-616. Parasitic * reduceToPiPoleResidue2(const Parasitic *parasitic_network, - const Pin *drvr_pin, + const Pin *drvr_pin, const RiseFall *rf, - float coupling_cap_factor, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - StaState *sta) + float coupling_cap_factor, + const Scene *scene, + const MinMax *min_max, + StaState *sta) { - Parasitics *parasitics = sta->parasitics(); + Parasitics *parasitics = scene->parasitics(min_max); ParasiticNode *drvr_node = parasitics->findParasiticNode(parasitic_network, drvr_pin); if (drvr_node) { @@ -466,28 +462,27 @@ reduceToPiPoleResidue2(const Parasitic *parasitic_network, ReduceToPiPoleResidue2 reducer(sta); return reducer.makePiPoleResidue2(parasitic_network, drvr_pin, drvr_node, coupling_cap_factor, rf, - corner, min_max, ap); + scene, min_max); } return nullptr; } Parasitic * ReduceToPiPoleResidue2::makePiPoleResidue2(const Parasitic *parasitic_network, - const Pin *drvr_pin, - ParasiticNode *drvr_node, - float coupling_cap_factor, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap) + const Pin *drvr_pin, + ParasiticNode *drvr_node, + float coupling_cap_factor, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { float c2, rpi, c1; reduceToPi(parasitic_network, drvr_pin, drvr_node, - coupling_cap_factor, rf, corner, min_max, ap, - c2, rpi, c1); + coupling_cap_factor, rf, scene, min_max, + c2, rpi, c1); Parasitic *pi_pole_residue = parasitics_->makePiPoleResidue(drvr_pin, - rf, ap, - c2, rpi, c1); + rf, min_max, + c2, rpi, c1); parasitics_->setIsReducedParasiticNetwork(pi_pole_residue, true); findPolesResidues(parasitic_network, pi_pole_residue, drvr_pin, drvr_node); return pi_pole_residue; @@ -501,8 +496,8 @@ ReduceToPiPoleResidue2::~ReduceToPiPoleResidue2() void ReduceToPiPoleResidue2::findPolesResidues(const Parasitic *parasitic_network, Parasitic *pi_pole_residue, - const Pin *drvr_pin, - ParasiticNode *drvr_node) + const Pin *drvr_pin, + ParasiticNode *drvr_node) { moments_ = new ParasiticNodeValueMap[4]; findMoments(drvr_pin, drvr_node, 4); @@ -514,7 +509,7 @@ ReduceToPiPoleResidue2::findPolesResidues(const Parasitic *parasitic_network, ParasiticNode *load_node = parasitics_->findParasiticNode(parasitic_network, pin); if (load_node) { - findPolesResidues(pi_pole_residue, drvr_pin, pin, load_node); + findPolesResidues(pi_pole_residue, drvr_pin, pin, load_node); } } } @@ -523,8 +518,8 @@ ReduceToPiPoleResidue2::findPolesResidues(const Parasitic *parasitic_network, void ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, - ParasiticNode *drvr_node, - int moment_count) + ParasiticNode *drvr_node, + int moment_count) { // Driver model thevenin resistance. double rd = 0.0; @@ -541,9 +536,9 @@ ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, double ReduceToPiPoleResidue2::findBranchCurrents(const Pin *drvr_pin, - ParasiticNode *node, - ParasiticResistor *from_res, - int moment_index) + ParasiticNode *node, + ParasiticResistor *from_res, + int moment_index) { visit(node); double branch_i = 0.0; @@ -577,10 +572,10 @@ ReduceToPiPoleResidue2::findBranchCurrents(const Pin *drvr_pin, void ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, - ParasiticNode *node, - double from_volt, - ParasiticResistor *from_res, - int moment_index) + ParasiticNode *node, + double from_volt, + ParasiticResistor *from_res, + int moment_index) { visit(node); ParasiticResistorSeq &resistors = resistor_map_[node]; @@ -607,7 +602,7 @@ ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, double ReduceToPiPoleResidue2::moment(ParasiticNode *node, - int moment_index) + int moment_index) { // Zero'th moments are all 1. if (moment_index == 0) @@ -620,8 +615,8 @@ ReduceToPiPoleResidue2::moment(ParasiticNode *node, void ReduceToPiPoleResidue2::setMoment(ParasiticNode *node, - double moment, - int moment_index) + double moment, + int moment_index) { // Zero'th moments are all 1. if (moment_index > 0) { @@ -638,16 +633,16 @@ ReduceToPiPoleResidue2::current(ParasiticResistor *res) void ReduceToPiPoleResidue2::setCurrent(ParasiticResistor *res, - double i) + double i) { currents_[res] = i; } void ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue, - const Pin *, - const Pin *load_pin, - ParasiticNode *load_node) + const Pin *, + const Pin *load_pin, + ParasiticNode *load_node) { double m1 = moment(load_node, 1); double m2 = moment(load_node, 2); diff --git a/parasitics/ReduceParasitics.hh b/parasitics/ReduceParasitics.hh index 84a51e640..283822f7d 100644 --- a/parasitics/ReduceParasitics.hh +++ b/parasitics/ReduceParasitics.hh @@ -29,7 +29,7 @@ namespace sta { -class Corner; +class Scene; class Parasitic; class ParasiticAnalysisPt; class StaState; @@ -37,24 +37,22 @@ class StaState; // Reduce parasitic network to pi elmore model for drvr_pin. Parasitic * reduceToPiElmore(const Parasitic *parasitic_network, - const Pin *drvr_pin, + const Pin *drvr_pin, const RiseFall *rf, - float coupling_cap_factor, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - StaState *sta); + float coupling_cap_factor, + const Scene *scene, + const MinMax *min_max, + StaState *sta); // Reduce parasitic network to pi and 2nd order pole/residue models // for drvr_pin. Parasitic * reduceToPiPoleResidue2(const Parasitic *parasitic_network, - const Pin *drvr_pin, + const Pin *drvr_pin, const RiseFall *rf, - float coupling_cap_factor, - const Corner *corner, - const MinMax *min_max, - const ParasiticAnalysisPt *ap, - StaState *sta); + float coupling_cap_factor, + const Scene *scene, + const MinMax *min_max, + StaState *sta); } // namespace diff --git a/parasitics/ReportParasiticAnnotation.cc b/parasitics/ReportParasiticAnnotation.cc index d27ac0f40..dc327468a 100644 --- a/parasitics/ReportParasiticAnnotation.cc +++ b/parasitics/ReportParasiticAnnotation.cc @@ -24,14 +24,14 @@ #include "ReportParasiticAnnotation.hh" +#include "ContainerHelpers.hh" #include "Report.hh" #include "Network.hh" #include "NetworkCmp.hh" #include "PortDirection.hh" #include "Graph.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" #include "ArcDelayCalc.hh" namespace sta { @@ -39,8 +39,9 @@ namespace sta { class ReportParasiticAnnotation : public StaState { public: - ReportParasiticAnnotation(bool report_unannotated, - const Corner *corner, + ReportParasiticAnnotation(Parasitics *parasitics, + bool report_unannotated, + const Scene *scene, StaState *sta); void report(); @@ -50,31 +51,34 @@ class ReportParasiticAnnotation : public StaState void findCounts(Instance *inst); void findCounts(Net *net); + Parasitics *parasitics_; bool report_unannotated_; - const Corner *corner_; + const Scene *scene_; const MinMax *min_max_; - const ParasiticAnalysisPt *parasitic_ap_; PinSeq unannotated_; PinSeq partially_annotated_; }; void -reportParasiticAnnotation(bool report_unannotated, - const Corner *corner, +reportParasiticAnnotation(Parasitics *parasitics, + bool report_unannotated, + const Scene *scene, StaState *sta) { - ReportParasiticAnnotation report_annotation(report_unannotated, corner, sta); + ReportParasiticAnnotation report_annotation(parasitics, report_unannotated, + scene, sta); report_annotation.report(); } -ReportParasiticAnnotation::ReportParasiticAnnotation(bool report_unannotated, - const Corner *corner, +ReportParasiticAnnotation::ReportParasiticAnnotation(Parasitics *parasitics, + bool report_unannotated, + const Scene *scene, StaState *sta) : StaState(sta), + parasitics_(parasitics), report_unannotated_(report_unannotated), - corner_(corner), - min_max_(MinMax::max()), - parasitic_ap_(corner_->findParasiticAnalysisPt(min_max_)) + scene_(scene), + min_max_(MinMax::max()) { } @@ -102,7 +106,7 @@ ReportParasiticAnnotation::reportAnnotationCounts() for (const Pin *drvr_pin : partially_annotated_) { report_->reportLine(" %s", network_->pathName(drvr_pin)); - Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap_); + Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin); if (parasitic) { PinSet unannotated_loads = parasitics_->unannotatedLoads(parasitic, drvr_pin); for (const Pin *load_pin : unannotated_loads) @@ -115,7 +119,6 @@ ReportParasiticAnnotation::reportAnnotationCounts() void ReportParasiticAnnotation::findCounts() { - DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_); VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); @@ -123,9 +126,10 @@ ReportParasiticAnnotation::findCounts() PortDirection *dir = network_->direction(pin); if (vertex->isDriver(network_) && !dir->isInternal()) { - Parasitic *parasitic = parasitics_->findParasiticNetwork(pin, parasitic_ap_); + Parasitic *parasitic = parasitics_->findParasiticNetwork(pin); if (parasitic == nullptr) - parasitic = arc_delay_calc_->findParasitic(pin, RiseFall::rise(), dcalc_ap); + 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) diff --git a/parasitics/ReportParasiticAnnotation.hh b/parasitics/ReportParasiticAnnotation.hh index eb446c05e..a3c7e6a87 100644 --- a/parasitics/ReportParasiticAnnotation.hh +++ b/parasitics/ReportParasiticAnnotation.hh @@ -26,12 +26,14 @@ namespace sta { +class Parasitics; class StaState; -class Corner; +class Scene; void -reportParasiticAnnotation(bool report_unannotated, - const Corner *corner, +reportParasiticAnnotation(Parasitics *parasitics, + bool report_unannotated, + const Scene *scene, StaState *sta); } // namespace diff --git a/parasitics/SpefNamespace.cc b/parasitics/SpefNamespace.cc index b9178a1f0..e9c1ab7d5 100644 --- a/parasitics/SpefNamespace.cc +++ b/parasitics/SpefNamespace.cc @@ -32,7 +32,7 @@ namespace sta { char * spefToSta(const char *token, char spef_divider, - char path_divider, + char path_divider, char path_escape) { const char spef_escape = '\\'; @@ -44,21 +44,21 @@ spefToSta(const char *token, if (ch == spef_escape) { char next_ch = s[1]; if (next_ch == spef_divider) { - // Translate spef escape to network escape. - *t++ = path_escape; - // Translate spef divider to network divider. - *t++ = path_divider; + // Translate spef escape to network escape. + *t++ = path_escape; + // Translate spef divider to network divider. + *t++ = path_divider; } else if (next_ch == '[' - || next_ch == ']' - || next_ch == spef_escape) { - // Translate spef escape to network escape. - *t++ = path_escape; - *t++ = next_ch; + || next_ch == ']' + || next_ch == spef_escape) { + // Translate spef escape to network escape. + *t++ = path_escape; + *t++ = next_ch; } else - // No need to escape other characters. - *t++ = next_ch; + // No need to escape other characters. + *t++ = next_ch; s++; } else if (ch == spef_divider) @@ -75,7 +75,7 @@ spefToSta(const char *token, char * staToSpef(const char *token, char spef_divider, - char path_divider, + char path_divider, char path_escape) { const char spef_escape = '\\'; @@ -87,20 +87,20 @@ staToSpef(const char *token, if (ch == path_escape) { char next_ch = s[1]; if (next_ch == path_divider) { - // Translate network escape to spef escape. - *t++ = spef_escape; - // Translate network divider to spef divider. - *t++ = spef_divider; + // Translate network escape to spef escape. + *t++ = spef_escape; + // Translate network divider to spef divider. + *t++ = spef_divider; } else if (next_ch == '[' - || next_ch == ']') { - // Translate network escape to spef escape. - *t++ = spef_escape; - *t++ = next_ch; + || next_ch == ']') { + // Translate network escape to spef escape. + *t++ = spef_escape; + *t++ = next_ch; } else - // No need to escape other characters. - *t++ = next_ch; + // No need to escape other characters. + *t++ = next_ch; s++; } else if (ch == path_divider) diff --git a/parasitics/SpefNamespace.hh b/parasitics/SpefNamespace.hh index 15f47dede..17b150463 100644 --- a/parasitics/SpefNamespace.hh +++ b/parasitics/SpefNamespace.hh @@ -30,12 +30,12 @@ namespace sta { // Caller owns the result string. char * spefToSta(const char *token, char spef_divider, - char path_escape, char path_divider); + char path_escape, char path_divider); // Translate from sta namespace to spf/spef namespace. // Caller owns the result string. char * staToSpef(const char *token, char spef_divider, - char path_divider, char path_escape); + char path_divider, char path_escape); } // namespace diff --git a/parasitics/SpefParse.yy b/parasitics/SpefParse.yy index b8fbb0c29..39ad246f2 100755 --- a/parasitics/SpefParse.yy +++ b/parasitics/SpefParse.yy @@ -41,7 +41,7 @@ void sta::SpefParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(164,reader->filename(), + reader->report()->fileError(164,reader->filename().c_str(), loc.begin.line,"%s",msg.c_str()); } %} diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index e81d691fc..b27efe6c4 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -29,14 +29,13 @@ #include "Report.hh" #include "Debug.hh" #include "StringUtil.hh" -#include "Map.hh" #include "Transition.hh" #include "Liberty.hh" #include "Network.hh" #include "PortDirection.hh" #include "Sdc.hh" #include "Parasitics.hh" -#include "Corner.hh" +#include "Scene.hh" #include "ArcDelayCalc.hh" #include "SpefReaderPvt.hh" #include "SpefNamespace.hh" @@ -47,42 +46,42 @@ namespace sta { using std::string; bool -readSpefFile(const char *filename, - Instance *instance, - ParasiticAnalysisPt *ap, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce, - const Corner *corner, - const MinMaxAll *min_max, +readSpefFile(const std::string &filename, + Instance *instance, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce, + const Scene *scene, + const MinMaxAll *min_max, + Parasitics *parasitics, StaState *sta) { - SpefReader reader(filename, instance, ap, - pin_cap_included, keep_coupling_caps, coupling_cap_factor, - reduce, corner, min_max, 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; } -SpefReader::SpefReader(const char *filename, - Instance *instance, - ParasiticAnalysisPt *ap, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce, - const Corner *corner, - const MinMaxAll *min_max, - StaState *sta) : +SpefReader::SpefReader(const std::string &filename, + Instance *instance, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce, + const Scene *scene, + const MinMaxAll *min_max, + Parasitics *parasitics, + StaState *sta) : StaState(sta), filename_(filename), instance_(instance), - ap_(ap), pin_cap_included_(pin_cap_included), keep_coupling_caps_(keep_coupling_caps), + coupling_cap_factor_(coupling_cap_factor), reduce_(reduce), - corner_(corner), + scene_(scene), min_max_(min_max), // defaults divider_('\0'), @@ -96,9 +95,10 @@ SpefReader::SpefReader(const char *filename, res_scale_(1.0), induct_scale_(1.0), design_flow_(nullptr), + parasitics_(parasitics), parasitic_(nullptr) { - ap->setCouplingCapFactor(coupling_cap_factor); + parasitics->setCouplingCapFactor(coupling_cap_factor); } SpefReader::~SpefReader() @@ -114,7 +114,7 @@ bool SpefReader::read() { bool success; - gzstream::igzstream stream(filename_); + gzstream::igzstream stream(filename_.c_str()); if (stream.is_open()) { Stats stats(debug_, report_); SpefScanner scanner(&stream, filename_, this, report_); @@ -126,7 +126,7 @@ SpefReader::read() stats.report("Read spef"); } else - throw FileNotReadable(filename_); + throw FileNotReadable(filename_.c_str()); return success; } @@ -147,11 +147,11 @@ SpefReader::setBusBrackets(char left, char right) { if (!((left == '[' && right == ']') - || (left == '{' && right == '}') - || (left == '(' && right == ')') - || (left == '<' && right == '>') - || (left == ':' && right == '\0') - || (left == '.' && right == '\0'))) + || (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; @@ -190,7 +190,7 @@ char * SpefReader::translated(const char *token) { return spefToSta(token, divider_, network_->pathDivider(), - network_->pathEscape()); + network_->pathEscape()); } void @@ -198,13 +198,13 @@ SpefReader::warn(int id, const char *fmt, ...) { va_list args; va_start(args, fmt); - report_->vfileWarn(id, filename_, scanner_->line(), fmt, args); + report_->vfileWarn(id, filename_.c_str(), scanner_->line(), fmt, args); va_end(args); } void SpefReader::setTimeScale(float scale, - const char *units) + const char *units) { if (stringEq(units, "NS")) time_scale_ = scale * 1E-9F; @@ -217,7 +217,7 @@ SpefReader::setTimeScale(float scale, void SpefReader::setCapScale(float scale, - const char *units) + const char *units) { if (stringEq(units, "PF")) cap_scale_ = scale * 1E-12F; @@ -230,7 +230,7 @@ SpefReader::setCapScale(float scale, void SpefReader::setResScale(float scale, - const char *units) + const char *units) { if (stringEq(units, "OHM")) res_scale_ = scale; @@ -243,7 +243,7 @@ SpefReader::setResScale(float scale, void SpefReader::setInductScale(float scale, - const char *units) + const char *units) { if (stringEq(units, "HENRY")) induct_scale_ = scale; @@ -258,7 +258,7 @@ SpefReader::setInductScale(float scale, void SpefReader::makeNameMapEntry(const char *index, - const char *name) + const char *name) { int i = atoi(index + 1); name_map_[i] = name; @@ -330,7 +330,7 @@ SpefReader::findPin(char *name) else { pin = findPortPinRelative(name); if (pin == nullptr) - warn(1649, "pin %s not found.", name); + warn(1649, "pin %s not found.", name); } } return pin; @@ -351,10 +351,10 @@ SpefReader::findNet(const char *name) void SpefReader::rspfBegin(Net *net, - SpefTriple *total_cap) + SpefTriple *total_cap) { if (net) - parasitics_->deleteParasitics(net, ap_); + parasitics_->deleteParasitics(net); // Net total capacitance is ignored. delete total_cap; } @@ -366,14 +366,16 @@ SpefReader::rspfFinish() void SpefReader::rspfDrvrBegin(Pin *drvr_pin, - SpefRspfPi *pi) + SpefRspfPi *pi) { if (drvr_pin) { float c2 = pi->c2()->value(triple_index_) * cap_scale_; 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(), ap_, + parasitic_ = parasitics_->makePiElmore(drvr_pin, + RiseFall::rise(), + MinMax::max(), c2, rpi, c1); } delete pi; @@ -381,7 +383,7 @@ SpefReader::rspfDrvrBegin(Pin *drvr_pin, void SpefReader::rspfLoad(Pin *load_pin, - SpefTriple *rc) + SpefTriple *rc) { if (parasitic_ && load_pin) { float elmore = rc->value(triple_index_) * time_scale_; @@ -399,12 +401,12 @@ SpefReader::rspfDrvrFinish() // Net cap (total_cap) is ignored. void SpefReader::dspfBegin(Net *net, - SpefTriple *total_cap) + SpefTriple *total_cap) { if (net) { if (network_->isTopInstance(instance_)) { - parasitics_->deleteReducedParasitics(net, ap_); - parasitic_ = parasitics_->makeParasiticNetwork(net, pin_cap_included_, ap_); + parasitics_->deleteReducedParasitics(net); + parasitic_ = parasitics_->makeParasiticNetwork(net, pin_cap_included_); } else { Net *parasitic_owner = net; @@ -415,10 +417,10 @@ SpefReader::dspfBegin(Net *net, parasitic_owner = network_->net(hpin); } delete term_iter; - parasitic_ = parasitics_->findParasiticNetwork(parasitic_owner, ap_); + parasitic_ = parasitics_->findParasiticNetwork(parasitic_owner); if (parasitic_ == nullptr) parasitic_ = parasitics_->makeParasiticNetwork(parasitic_owner, - pin_cap_included_, ap_); + pin_cap_included_); } net_ = net; } @@ -433,8 +435,8 @@ void SpefReader::dspfFinish() { if (parasitic_ && reduce_) { - arc_delay_calc_->reduceParasitic(parasitic_, net_, corner_, min_max_); - parasitics_->deleteParasiticNetwork(net_, ap_); + arc_delay_calc_->reduceParasitic(parasitic_, net_, scene_, min_max_); + parasitics_->deleteParasiticNetwork(net_); } parasitic_ = nullptr; net_ = nullptr; @@ -442,7 +444,7 @@ SpefReader::dspfFinish() ParasiticNode * SpefReader::findParasiticNode(char *name, - bool local_only) + bool local_only) { if (name && parasitic_) { char *delim = strrchr(name, delimiter_); @@ -459,7 +461,7 @@ SpefReader::findParasiticNode(char *name, if (local_only && !network_->isConnected(net_, pin)) warn(1651, "%s not connected to net %s.", - name1, sdc_network_->pathName(net_)); + name1, sdc_network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, pin, network_); } else { @@ -513,7 +515,7 @@ SpefReader::findParasiticNode(char *name, void SpefReader::makeCapacitor(int, char *node_name, - SpefTriple *cap) + SpefTriple *cap) { ParasiticNode *node = findParasiticNode(node_name, true); if (node) { @@ -526,9 +528,9 @@ SpefReader::makeCapacitor(int, char *node_name, void SpefReader::makeCapacitor(int id, - char *node_name1, - char *node_name2, - SpefTriple *cap) + char *node_name1, + char *node_name2, + SpefTriple *cap) { ParasiticNode *node1 = findParasiticNode(node_name1, false); ParasiticNode *node2 = findParasiticNode(node_name2, false); @@ -537,7 +539,7 @@ SpefReader::makeCapacitor(int id, if (keep_coupling_caps_) parasitics_->makeCapacitor(parasitic_, id, cap1, node1, node2); else { - float scaled_cap = cap1 * ap_->couplingCapFactor(); + float scaled_cap = cap1 * coupling_cap_factor_; if (node1 && parasitics_->net(node1, network_) == net_) parasitics_->incrCap(node1, scaled_cap); if (node2 && parasitics_->net(node2, network_) == net_) @@ -551,9 +553,9 @@ SpefReader::makeCapacitor(int id, void SpefReader::makeResistor(int id, - char *node_name1, - char *node_name2, - SpefTriple *res) + char *node_name1, + char *node_name2, + SpefTriple *res) { ParasiticNode *node1 = findParasiticNode(node_name1, true); ParasiticNode *node2 = findParasiticNode(node_name2, true); @@ -569,8 +571,8 @@ SpefReader::makeResistor(int id, //////////////////////////////////////////////////////////////// SpefRspfPi::SpefRspfPi(SpefTriple *c2, - SpefTriple *r1, - SpefTriple *c1) : + SpefTriple *r1, + SpefTriple *c1) : c2_(c2), r1_(r1), c1_(c1) @@ -593,8 +595,8 @@ SpefTriple::SpefTriple(float value) : } SpefTriple::SpefTriple(float value1, - float value2, - float value3) : + float value2, + float value3) : is_triple_(true) { values_[0] = value1; diff --git a/parasitics/SpefReader.hh b/parasitics/SpefReader.hh index 3e3913488..a3cae7717 100644 --- a/parasitics/SpefReader.hh +++ b/parasitics/SpefReader.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "Zlib.hh" #include "MinMax.hh" #include "ParasiticsClass.hh" @@ -32,25 +34,24 @@ namespace sta { class ParasiticAnalysisPt; class Instance; -class Corner; +class Scene; class OperatingConditions; class StaState; // Read a file single value parasitics into analysis point ap. // In a Spef file with triplet values the first value is used. -// Constraint min/max cnst_min_max and operating condition op_cond -// are used for parasitic network reduction. +// Min/max and operating condition op_cond are used for parasitic network reduction. // Return true if successful. bool -readSpefFile(const char *filename, - Instance *instance, - ParasiticAnalysisPt *ap, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce, - const Corner *corner, - const MinMaxAll *min_max, - StaState *sta); +readSpefFile(const std::string &filename, + Instance *instance, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce, + const Scene *scene, + const MinMaxAll *min_max, + Parasitics *parasirics, + StaState *sta); } // namespace diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index 033a34311..f96480c4e 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -38,23 +38,23 @@ class Report; class MinMaxAll; class SpefRspfPi; class SpefTriple; -class Corner; +class Scene; class SpefScanner; -typedef std::map SpefNameMap; +using SpefNameMap = std::map; class SpefReader : public StaState { public: - SpefReader(const char *filename, - Instance *instance, - ParasiticAnalysisPt *ap, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce, - const Corner *corner, - const MinMaxAll *min_max, + SpefReader(const std::string &filename, + Instance *instance, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce, + const Scene *scene, + const MinMaxAll *min_max, + Parasitics *parasitics, StaState *sta); virtual ~SpefReader(); bool read(); @@ -62,7 +62,7 @@ public: void setDivider(char divider); char delimiter() const { return delimiter_; } void setDelimiter(char delimiter); - const char *filename() const { return filename_; } + const std::string &filename() const { return filename_; } // Translate from spf/spef namespace to sta namespace. char *translated(const char *token); void warn(int id, @@ -70,43 +70,43 @@ public: ...) __attribute__((format (printf, 3, 4))); void setBusBrackets(char left, - char right); + char right); void setTimeScale(float scale, - const char *units); + const char *units); void setCapScale(float scale, - const char *units); + const char *units); void setResScale(float scale, - const char *units); + const char *units); void setInductScale(float scale, - const char *units); + const char *units); void makeNameMapEntry(const char *index, - const char *name); + const char *name); const char *nameMapLookup(const char *index); void setDesignFlow(StringSeq *flow_keys); Pin *findPin(char *name); Net *findNet(const char *name); void rspfBegin(Net *net, - SpefTriple *total_cap); + SpefTriple *total_cap); void rspfFinish(); void rspfDrvrBegin(Pin *drvr_pin, - SpefRspfPi *pi); + SpefRspfPi *pi); void rspfLoad(Pin *load_pin, - SpefTriple *rc); + SpefTriple *rc); void rspfDrvrFinish(); void dspfBegin(Net *net, - SpefTriple *total_cap); + SpefTriple *total_cap); void dspfFinish(); void makeCapacitor(int id, - char *node_name, - SpefTriple *cap); + char *node_name, + SpefTriple *cap); void makeCapacitor(int id, - char *node_name1, - char *node_name2, - SpefTriple *cap); + char *node_name1, + char *node_name2, + SpefTriple *cap); void makeResistor(int id, - char *node_name1, - char *node_name2, - SpefTriple *res); + char *node_name1, + char *node_name2, + SpefTriple *res); PortDirection *portDirection(char *spef_dir); private: @@ -117,14 +117,14 @@ private: ParasiticNode *findParasiticNode(char *name, bool local_only); - const char *filename_; + const std::string filename_; SpefScanner *scanner_; Instance *instance_; - const ParasiticAnalysisPt *ap_; bool pin_cap_included_; bool keep_coupling_caps_; + bool coupling_cap_factor_; bool reduce_; - const Corner *corner_; + const Scene *scene_; const MinMaxAll *min_max_; // Normally no need to keep device names. char divider_; @@ -140,6 +140,7 @@ private: float induct_scale_; SpefNameMap name_map_; StringSeq *design_flow_; + Parasitics *parasitics_; Parasitic *parasitic_; }; @@ -148,8 +149,8 @@ class SpefTriple public: SpefTriple(float value); SpefTriple(float value1, - float value2, - float value3); + float value2, + float value3); float value(int index) const; bool isTriple() const { return is_triple_; } diff --git a/power/Power.cc b/power/Power.cc index 3265fe967..6cc8795f2 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -28,6 +28,7 @@ #include // abs #include "cudd.h" +#include "ContainerHelpers.hh" #include "Stats.hh" #include "Debug.hh" #include "EnumNameMap.hh" @@ -46,16 +47,17 @@ #include "Network.hh" #include "Clock.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Graph.hh" -#include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Path.hh" #include "search/Levelize.hh" #include "search/Sim.hh" #include "Search.hh" #include "Bfs.hh" #include "ClkNetwork.hh" +#include "ReportPower.hh" // Related liberty not supported: // library @@ -96,20 +98,21 @@ static EnumNameMap pwr_activity_origin_map = Power::Power(StaState *sta) : StaState(sta), + scene_(nullptr), global_activity_(), - input_activity_(), // default set in ensureActivities() + input_activity_(), // default set in ensureActivities. seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()), activities_valid_(false), bdd_(sta), instance_powers_(InstanceIdLess(network_)), - instance_powers_valid_(false), - corner_(nullptr) + instance_powers_valid_(false) { } void Power::clear() { + scene_ = nullptr; global_activity_.init(); input_activity_.init(); user_activity_map_.clear(); @@ -117,7 +120,6 @@ Power::clear() activity_map_.clear(); activities_valid_ = false; instance_powers_.clear(); - corner_ = nullptr; } void @@ -207,7 +209,7 @@ Power::userActivity(const Pin *pin) bool Power::hasUserActivity(const Pin *pin) { - return user_activity_map_.hasKey(pin); + return user_activity_map_.contains(pin); } void @@ -231,7 +233,7 @@ Power::activity(const Pin *pin) bool Power::hasActivity(const Pin *pin) { - return activity_map_.hasKey(pin); + return activity_map_.contains(pin); } // Sequential internal pins may not be in the netlist so their @@ -249,7 +251,7 @@ bool Power::hasSeqActivity(const Instance *reg, LibertyPort *output) { - return seq_activity_map_.hasKey(SeqPin(reg, output)); + return seq_activity_map_.contains(SeqPin(reg, output)); } PwrActivity & @@ -267,21 +269,157 @@ SeqPinHash::SeqPinHash(const Network *network) : size_t SeqPinHash::operator()(const SeqPin &pin) const { - return hashSum(network_->id(pin.first), pin.second->id()); + const auto& [inst, port] = pin; + return hashSum(network_->id(inst), port->id()); } bool SeqPinEqual::operator()(const SeqPin &pin1, const SeqPin &pin2) const { - return pin1.first == pin2.first - && pin1.second == pin2.second; + const auto& [inst1, port1] = pin1; + const auto& [inst2, port2] = pin2; + return inst1 == inst2 + && port1 == port2; } //////////////////////////////////////////////////////////////// void -Power::power(const Corner *corner, +Power::reportDesign(const Scene *scene, + int digits) +{ + 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); +} + +void +Power::reportInsts(const InstanceSeq &insts, + const Scene *scene, + int digits) +{ + InstPowers inst_pwrs = sortInstsByPower(insts, scene); + ReportPower report_power(this); + report_power.reportInsts(inst_pwrs, digits); +} + +void +Power::reportHighestInsts(size_t count, + const Scene *scene, + int digits) +{ + InstPowers inst_pwrs = highestInstPowers(count, scene); + ReportPower report_power(this); + report_power.reportInsts(inst_pwrs, digits); +} + +void +Power::reportDesignJson(const Scene *scene, + int digits) +{ + PowerResult total, sequential, combinational, clock, macro, pad; + power(scene, total, sequential, combinational, clock, macro, pad); + + report_->reportLine("{"); + 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("}"); +} + +void +Power::reportInstsJson(const InstanceSeq &insts, + const Scene *scene, + int digits) +{ + InstPowers inst_pwrs = sortInstsByPower(insts, scene); + + report_->reportLine("["); + bool first = true; + for (const InstPower &inst_pwr : inst_pwrs) { + if (!first) { + report_->reportLine(","); + } + first = false; + reportPowerInstJson(inst_pwr.first, inst_pwr.second, digits); + } + report_->reportLine("]"); +} + +void +Power::reportPowerRowJson(const char *name, + const PowerResult &power, + int digits, + const char *separator) +{ + float internal = power.internal(); + 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); + std::string line = " }"; + if (separator && separator[0] != '\0') + line += separator; + report_->reportLineString(line); +} + +void +Power::reportPowerInstJson(const Instance *inst, + const PowerResult &power, + int digits) +{ + float internal = power.internal(); + 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("}"); +} + +static bool +instPowerGreater(const InstPower &pwr1, + const InstPower &pwr2) +{ + return pwr1.second.total() > pwr2.second.total(); +} + +InstPowers +Power::sortInstsByPower(const InstanceSeq &insts, + const Scene *scene) +{ + // Collect instance powers. + InstPowers inst_pwrs; + for (const Instance *inst : 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; +} + +//////////////////////////////////////////////////////////////// + +void +Power::power(const Scene *scene, // Return values. PowerResult &total, PowerResult &sequential, @@ -297,8 +435,9 @@ Power::power(const Corner *corner, macro.clear(); pad.clear(); - ensureActivities(); - ensureInstPowers(corner); + ensureActivities(scene); + ensureInstPowers(); + ClkNetwork *clk_network = scene_->mode()->clkNetwork(); for (auto [inst, inst_power] : instance_powers_) { LibertyCell *cell = network_->libertyCell(inst); if (cell) { @@ -308,7 +447,7 @@ Power::power(const Corner *corner, macro.incr(inst_power); else if (cell->isPad()) pad.incr(inst_power); - else if (inClockNetwork(inst)) + else if (inClockNetwork(inst, clk_network)) clock.incr(inst_power); else if (cell->hasSequentials()) sequential.incr(inst_power); @@ -320,13 +459,14 @@ Power::power(const Corner *corner, } bool -Power::inClockNetwork(const Instance *inst) +Power::inClockNetwork(const Instance *inst, + const ClkNetwork *clk_network) { 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)) { + && !clk_network->isClock(pin)) { delete pin_iter; return false; } @@ -337,13 +477,13 @@ Power::inClockNetwork(const Instance *inst) PowerResult Power::power(const Instance *inst, - const Corner *corner) + const Scene *scene) { - ensureActivities(); - ensureInstPowers(corner); + ensureActivities(scene); + ensureInstPowers(); if (network_->isHierarchical(inst)) { PowerResult result; - powerInside(inst, corner, result); + powerInside(inst, scene, result); return result; } else @@ -352,66 +492,89 @@ Power::power(const Instance *inst, void Power::powerInside(const Instance *hinst, - const Corner *corner, + const Scene *scene, PowerResult &result) { InstanceChildIterator *child_iter = network_->childIterator(hinst); while (child_iter->hasNext()) { Instance *child = child_iter->next(); if (network_->isHierarchical(child)) - powerInside(child, corner, result); + powerInside(child, scene, result); else result.incr(instance_powers_[child]); } delete child_iter; } -typedef std::pair InstPower; +//////////////////////////////////////////////////////////////// -InstanceSeq -Power::highestPowerInstances(size_t count, - const Corner *corner) +InstPowers +Power::highestInstPowers(size_t count, + const Scene *scene) { - vector inst_pwrs; + InstPowers inst_pwrs; LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); while (inst_iter->hasNext()) { Instance *inst = inst_iter->next(); - PowerResult pwr = power(inst, corner); - inst_pwrs.push_back({inst, pwr.total()}); + PowerResult pwr = power(inst, scene); + inst_pwrs.push_back(std::make_pair(inst, pwr)); } delete inst_iter; - sort(inst_pwrs.begin(), inst_pwrs.end(), [](InstPower &inst_pwr1, - InstPower &inst_pwr2) { - return inst_pwr1.second > inst_pwr2.second; - }); - - InstanceSeq insts; - for (size_t i = 0; i < count; i++) - insts.push_back(inst_pwrs[i].first); - return insts; + sort(inst_pwrs, instPowerGreater); + if (inst_pwrs.size() > count) + inst_pwrs.resize(count); + return inst_pwrs; } //////////////////////////////////////////////////////////////// -class ActivitySrchPred : public SearchPredNonLatch2 +class ActivitySrchPred : public SearchPred { public: - explicit ActivitySrchPred(const StaState *sta); - virtual bool searchThru(Edge *edge); + ActivitySrchPred(const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; }; ActivitySrchPred::ActivitySrchPred(const StaState *sta) : - SearchPredNonLatch2(sta) + SearchPred(sta) +{ +} + +bool +ActivitySrchPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); + return !sdc->isDisabledConstraint(from_pin); } bool -ActivitySrchPred::searchThru(Edge *edge) +ActivitySrchPred::searchThru(Edge *edge, + const Mode *mode) const { + const Sdc *sdc = mode->sdc(); const TimingRole *role = edge->role(); - return SearchPredNonLatch2::searchThru(edge) - && role != TimingRole::regClkToQ(); + return !(edge->role()->isTimingCheck() + || sdc->isDisabledConstraint(edge) + || sdc->isDisabledCondDefault(edge) + || edge->isBidirectInstPath() + || edge->isDisabledLoop() + || role == TimingRole::regClkToQ() + || role->isLatchDtoQ()); +} + +bool +ActivitySrchPred::searchTo(const Vertex *, + const Mode *) const +{ + return true; } //////////////////////////////////////////////////////////////// @@ -420,44 +583,52 @@ class PropActivityVisitor : public VertexVisitor, StaState { public: PropActivityVisitor(Power *power, + const Mode *mode, BfsFwdIterator *bfs); virtual VertexVisitor *copy() const; virtual void visit(Vertex *vertex); InstanceSet &visitedRegs() { return visited_regs_; } void init(); float maxChange() const { return max_change_; } + const Pin *maxChangePin() const { return max_change_pin_; } private: bool setActivityCheck(const Pin *pin, PwrActivity &activity); - static constexpr float change_tolerance_ = .001; + static constexpr float change_tolerance_ = .01; InstanceSet visited_regs_; float max_change_; - Power *power_; + const Pin *max_change_pin_; BfsFwdIterator *bfs_; + Power *power_; + const Mode *mode_; }; PropActivityVisitor::PropActivityVisitor(Power *power, + const Mode *mode, BfsFwdIterator *bfs) : StaState(power), visited_regs_(network_), max_change_(0.0), + max_change_pin_(nullptr), + bfs_(bfs), power_(power), - bfs_(bfs) + mode_(mode) { } VertexVisitor * PropActivityVisitor::copy() const { - return new PropActivityVisitor(power_, bfs_); + return new PropActivityVisitor(power_, mode_, bfs_); } void PropActivityVisitor::init() { max_change_ = 0.0; + max_change_pin_ = nullptr; } void @@ -547,11 +718,25 @@ PropActivityVisitor::visit(Vertex *vertex) } } } - bfs_->enqueueAdjacentVertices(vertex); + bfs_->enqueueAdjacentVertices(vertex, mode_); } } } +static float +percentChange(float value, + float prev) +{ + if (prev == 0.0) { + if (value == 0.0) + return 0.0; + else + return 1.0; + } + else + return abs(value - prev) / prev; +} + // Return true if the activity changed. bool PropActivityVisitor::setActivityCheck(const Pin *pin, @@ -562,20 +747,26 @@ PropActivityVisitor::setActivityCheck(const Pin *pin, if (activity.density() > max_density) activity.setDensity(max_density); PwrActivity &prev_activity = power_->activity(pin); - float density_delta = abs(activity.density() - prev_activity.density()); - float duty_delta = abs(activity.duty() - prev_activity.duty()); - if (density_delta > change_tolerance_ - || duty_delta > change_tolerance_ - || activity.origin() != prev_activity.origin()) { - max_change_ = max(max_change_, density_delta); - max_change_ = max(max_change_, duty_delta); - power_->setActivity(pin, activity); - return true; + 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; + max_change_pin_ = pin; } - else - return false; + if (duty_delta > max_change_) { + max_change_ = duty_delta; + max_change_pin_ = pin; + } + bool changed = density_delta > change_tolerance_ + || duty_delta > change_tolerance_ + || activity.origin() != prev_activity.origin();; + power_->setActivity(pin, activity); + return changed; } +//////////////////////////////////////////////////////////////// + void Power::clockGatePins(const Instance *inst, // Return values. @@ -708,9 +899,15 @@ Power::evalBddActivity(DdNode *bdd, //////////////////////////////////////////////////////////////// void -Power::ensureActivities() +Power::ensureActivities(const Scene *scene) { Stats stats(debug_, report_); + if (scene != scene_) { + scene_ = scene; + activities_valid_ = false; + instance_powers_.clear(); + } + if (!activities_valid_) { // No need to propagate activites if global activity is set. if (!global_activity_.isSet()) { @@ -721,7 +918,7 @@ Power::ensureActivities() // Initialize default input activity (after sdc is defined) // unless it has been set by command. if (input_activity_.origin() == PwrActivityOrigin::unknown) { - float min_period = clockMinPeriod(); + float min_period = clockMinPeriod(scene_->mode()->sdc()); float density = 0.1 / (min_period != 0.0 ? min_period : units_->timeUnit()->scale()); @@ -730,7 +927,7 @@ Power::ensureActivities() ActivitySrchPred activity_srch_pred(this); BfsFwdIterator bfs(BfsIndex::other, &activity_srch_pred, this); seedActivities(bfs); - PropActivityVisitor visitor(this, &bfs); + PropActivityVisitor visitor(this, scene_->mode(), &bfs); // Propagate activities through combinational logic. bfs.visit(levelize_->maxLevel(), &visitor); // Propagate activiities through registers. @@ -745,8 +942,10 @@ Power::ensureActivities() // combinational logic. bfs.visit(levelize_->maxLevel(), &visitor); regs = std::move(visitor.visitedRegs()); - debugPrint(debug_, "power_activity", 1, "Pass %d change %.2f", - pass, visitor.maxChange()); + debugPrint(debug_, "power_activity", 1, "Pass %d change %.2f %s", + pass, + visitor.maxChange(), + network_->pathName(visitor.maxChangePin())); pass++; } } @@ -761,7 +960,7 @@ Power::seedActivities(BfsFwdIterator &bfs) for (Vertex *vertex : levelize_->roots()) { const Pin *pin = vertex->pin(); // Clock activities are baked in. - if (!sdc_->isLeafPinClock(pin) + if (!scene_->mode()->sdc()->isLeafPinClock(pin) && !network_->direction(pin)->isInternal()) { debugPrint(debug_, "power_activity", 3, "seed %s", vertex->to_string(this).c_str()); @@ -771,7 +970,7 @@ Power::seedActivities(BfsFwdIterator &bfs) // Default inputs without explicit activities to the input default. setActivity(pin, input_activity_); Vertex *vertex = graph_->pinDrvrVertex(pin); - bfs.enqueueAdjacentVertices(vertex); + bfs.enqueueAdjacentVertices(vertex, scene_->mode()); } } } @@ -851,8 +1050,8 @@ Power::seedRegOutputActivities(const Instance *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; + && clk_func->op() == FuncExpr::Op::not_ + && clk_func->left()->op() == FuncExpr::Op::port; if (clk_invert) out_density = in_density * (1 - clk_duty); else @@ -869,17 +1068,16 @@ Power::seedRegOutputActivities(const Instance *reg, //////////////////////////////////////////////////////////////// void -Power::ensureInstPowers(const Corner *corner) +Power::ensureInstPowers() { - if (!instance_powers_valid_ - || corner != corner_) { - findInstPowers(corner); + if (!instance_powers_valid_) { + findInstPowers(); instance_powers_valid_ = true; } } void -Power::findInstPowers(const Corner *corner) +Power::findInstPowers() { Stats stats(debug_, report_); LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); @@ -887,24 +1085,23 @@ Power::findInstPowers(const Corner *corner) Instance *inst = inst_iter->next(); LibertyCell *cell = network_->libertyCell(inst); if (cell) { - PowerResult inst_power = power(inst, cell, corner); + PowerResult inst_power = power(inst, cell, scene_); instance_powers_[inst] = inst_power; } } delete inst_iter; - corner_ = corner; stats.report("Find power"); } PowerResult Power::power(const Instance *inst, LibertyCell *cell, - const Corner *corner) + const Scene *scene) { PowerResult result; - findInternalPower(inst, cell, corner, result); - findSwitchingPower(inst, cell, corner, result); - findLeakagePower(inst, cell, corner, result); + findInternalPower(inst, cell, scene, result); + findSwitchingPower(inst, cell, scene, result); + findLeakagePower(inst, cell, scene, result); return result; } @@ -928,26 +1125,25 @@ Power::findInstClk(const Instance *inst) void Power::findInternalPower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { const Pin *to_pin = pin_iter->next(); LibertyPort *to_port = network_->libertyPort(to_pin); if (to_port) { float load_cap = to_port->direction()->isAnyOutput() - ? graph_delay_calc_->loadCap(to_pin, dcalc_ap) + ? 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, corner, result); + load_cap, scene, result); if (to_port->direction()->isAnyInput()) findInputInternalPower(to_pin, to_port, inst, cell, activity, - load_cap, corner, result); + load_cap, scene, result); } } delete pin_iter; @@ -960,23 +1156,22 @@ Power::findInputInternalPower(const Pin *pin, LibertyCell *cell, PwrActivity &activity, float load_cap, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result) { const MinMax *min_max = MinMax::max(); - LibertyCell *corner_cell = cell->cornerCell(corner, min_max); - const LibertyPort *corner_port = port->cornerPort(corner, min_max); - if (corner_cell && corner_port) { - const InternalPowerSeq &internal_pwrs = corner_cell->internalPowers(corner_port); + LibertyCell *scene_cell = cell->sceneCell(scene, min_max); + const LibertyPort *scene_port = port->scenePort(scene, min_max); + if (scene_cell && scene_port) { + const InternalPowerSeq &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(), units_->capacitanceUnit()->asString(load_cap)); debugPrint(debug_, "power", 2, " when act/ns duty energy power"); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); - const Pvt *pvt = dcalc_ap->operatingConditions(); + const Pvt *pvt = scene->sdc()->operatingConditions(MinMax::max()); Vertex *vertex = graph_->pinLoadVertex(pin); float internal = 0.0; for (InternalPower *pwr : internal_pwrs) { @@ -984,7 +1179,7 @@ Power::findInputInternalPower(const Pin *pin, float energy = 0.0; int rf_count = 0; for (const RiseFall *rf : RiseFall::range()) { - float slew = getSlew(vertex, rf, corner); + float slew = getSlew(vertex, rf, scene); if (!delayInf(slew)) { float table_energy = pwr->power(rf, pvt, slew, load_cap); energy += table_energy; @@ -996,9 +1191,9 @@ Power::findInputInternalPower(const Pin *pin, float duty = 1.0; // fallback default FuncExpr *when = pwr->when(); if (when) { - const LibertyPort *out_corner_port = findExprOutPort(when); - if (out_corner_port) { - LibertyPort *out_port = findLinkPort(cell, out_corner_port); + const LibertyPort *out_scene_port = findExprOutPort(when); + if (out_scene_port) { + LibertyPort *out_port = findLinkPort(cell, out_scene_port); if (out_port) { FuncExpr *func = out_port->function(); if (func && func->hasPort(port)) @@ -1029,14 +1224,18 @@ Power::findInputInternalPower(const Pin *pin, float Power::getSlew(Vertex *vertex, const RiseFall *rf, - const Corner *corner) + const Scene *scene) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); + + const MinMax *min_max = MinMax::max(); const Pin *pin = vertex->pin(); - if (clk_network_->isIdealClock(pin)) - return clk_network_->idealClkSlew(pin, rf, MinMax::max()); - else - return delayAsFloat(graph_->slew(vertex, rf, dcalc_ap->index())); + const ClkNetwork *clk_network = scene->mode()->clkNetwork(); + if (clk_network->isIdealClock(pin)) + return clk_network->idealClkSlew(pin, rf, min_max); + else { + DcalcAPIndex slew_index = scene->dcalcAnalysisPtIndex(min_max); + return delayAsFloat(graph_->slew(vertex, rf, slew_index)); + } } float @@ -1047,8 +1246,9 @@ Power::getMinRfSlew(const Pin *pin) if (vertex) { const MinMax *min_max = MinMax::min(); Slew mm_slew = min_max->initValue(); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - DcalcAPIndex ap_index = dcalc_ap->index(); + 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; @@ -1065,19 +1265,19 @@ Power::findExprOutPort(FuncExpr *expr) { LibertyPort *port; switch (expr->op()) { - case FuncExpr::op_port: + case FuncExpr::Op::port: port = expr->port(); if (port && port->direction()->isAnyOutput()) return expr->port(); return nullptr; - case FuncExpr::op_not: + 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: + case FuncExpr::Op::or_: + case FuncExpr::Op::and_: + case FuncExpr::Op::xor_: port = findExprOutPort(expr->left()); if (port) return port; @@ -1085,8 +1285,8 @@ Power::findExprOutPort(FuncExpr *expr) if (port) return port; return nullptr; - case FuncExpr::op_one: - case FuncExpr::op_zero: + case FuncExpr::Op::one: + case FuncExpr::Op::zero: return nullptr; } return nullptr; @@ -1098,7 +1298,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, LibertyCell *cell, PwrActivity &to_activity, float load_cap, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result) { @@ -1106,17 +1306,17 @@ Power::findOutputInternalPower(const LibertyPort *to_port, network_->pathName(inst), to_port->name(), units_->capacitanceUnit()->asString(load_cap)); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); - const Pvt *pvt = dcalc_ap->operatingConditions(); - LibertyCell *corner_cell = cell->cornerCell(dcalc_ap); - const LibertyPort *to_corner_port = to_port->cornerPort(dcalc_ap); + const MinMax *min_max = MinMax::max(); + const Pvt *pvt = scene->sdc()->operatingConditions(min_max); + LibertyCell *scene_cell = cell->sceneCell(scene, min_max); + const LibertyPort *to_scene_port = to_port->scenePort(scene, min_max); FuncExpr *func = to_port->function(); map pg_duty_sum; - for (InternalPower *pwr : corner_cell->internalPowers(to_corner_port)) { - const LibertyPort *from_corner_port = pwr->relatedPort(); - if (from_corner_port) { - const Pin *from_pin = findLinkPin(inst, from_corner_port); + for (InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { + const LibertyPort *from_scene_port = pwr->relatedPort(); + if (from_scene_port) { + const Pin *from_pin = findLinkPin(inst, from_scene_port); float from_density = findActivity(from_pin).density(); float duty = findInputDuty(inst, func, pwr); const char *related_pg_pin = pwr->relatedPgPin(); @@ -1128,17 +1328,17 @@ Power::findOutputInternalPower(const LibertyPort *to_port, debugPrint(debug_, "power", 2, " when act/ns duty wgt energy power"); float internal = 0.0; - for (InternalPower *pwr : corner_cell->internalPowers(to_corner_port)) { + for (InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { FuncExpr *when = pwr->when(); const char *related_pg_pin = pwr->relatedPgPin(); float duty = findInputDuty(inst, func, pwr); Vertex *from_vertex = nullptr; bool positive_unate = true; - const LibertyPort *from_corner_port = pwr->relatedPort(); + const LibertyPort *from_scene_port = pwr->relatedPort(); const Pin *from_pin = nullptr; - if (from_corner_port) { - positive_unate = isPositiveUnate(corner_cell, from_corner_port, to_corner_port); - from_pin = findLinkPin(inst, from_corner_port); + if (from_scene_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); } @@ -1148,7 +1348,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, // Use unateness to find from_rf. const RiseFall *from_rf = positive_unate ? to_rf : to_rf->opposite(); float slew = from_vertex - ? getSlew(from_vertex, from_rf, corner) + ? getSlew(from_vertex, from_rf, scene) : 0.0; if (!delayInf(slew)) { float table_energy = pwr->power(to_rf, pvt, slew, load_cap); @@ -1169,7 +1369,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, } float port_internal = weight * energy * to_activity.density(); debugPrint(debug_, "power", 2, "%3s -> %-3s %6s %.3f %.3f %.3f %9.2e %9.2e %s", - from_corner_port ? from_corner_port->name() : "-" , + from_scene_port ? from_scene_port->name() : "-" , to_port->name(), when ? when->to_string().c_str() : "", to_activity.density() * 1e-9, @@ -1189,10 +1389,10 @@ Power::findInputDuty(const Instance *inst, InternalPower *pwr) { - const LibertyPort *from_corner_port = pwr->relatedPort(); - if (from_corner_port) { + const LibertyPort *from_scene_port = pwr->relatedPort(); + if (from_scene_port) { LibertyPort *from_port = findLinkPort(network_->libertyCell(inst), - from_corner_port); + from_scene_port); const Pin *from_pin = network_->findPin(inst, from_port); if (from_pin) { FuncExpr *when = pwr->when(); @@ -1203,7 +1403,7 @@ Power::findInputDuty(const Instance *inst, } else if (when) return evalActivity(when, inst).duty(); - else if (search_->isClock(from_vertex)) + else if (scene_->mode()->clkNetwork()->isClock(from_vertex->pin())) return 0.5; return 0.5; } @@ -1211,20 +1411,20 @@ Power::findInputDuty(const Instance *inst, return 0.0; } -// Hack to find cell port that corresponds to corner_port. +// Hack to find cell port that corresponds to scene_port. LibertyPort * Power::findLinkPort(const LibertyCell *cell, - const LibertyPort *corner_port) + const LibertyPort *scene_port) { - return cell->findLibertyPort(corner_port->name()); + return cell->findLibertyPort(scene_port->name()); } Pin * Power::findLinkPin(const Instance *inst, - const LibertyPort *corner_port) + const LibertyPort *scene_port) { const LibertyCell *cell = network_->libertyCell(inst); - LibertyPort *port = findLinkPort(cell, corner_port); + LibertyPort *port = findLinkPort(cell, scene_port); return network_->findPin(inst, port); } @@ -1248,23 +1448,22 @@ isPositiveUnate(const LibertyCell *cell, void Power::findSwitchingPower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); - LibertyCell *corner_cell = cell->cornerCell(dcalc_ap); + LibertyCell *scene_cell = cell->sceneCell(scene, MinMax::max()); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { const Pin *to_pin = pin_iter->next(); const LibertyPort *to_port = network_->libertyPort(to_pin); if (to_port) { float load_cap = to_port->direction()->isAnyOutput() - ? graph_delay_calc_->loadCap(to_pin, dcalc_ap) + ? graph_delay_calc_->loadCap(to_pin, scene, MinMax::max()) : 0.0; PwrActivity activity = findActivity(to_pin); if (to_port->direction()->isAnyOutput()) { - float volt = portVoltage(corner_cell, to_port, dcalc_ap); + 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(), @@ -1285,17 +1484,17 @@ Power::findSwitchingPower(const Instance *inst, void Power::findLeakagePower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result) { - LibertyCell *corner_cell = cell->cornerCell(corner, MinMax::max()); + LibertyCell *scene_cell = cell->sceneCell(scene, MinMax::max()); float cond_leakage = 0.0; bool found_cond = false; float uncond_leakage = 0.0; bool found_uncond = false; float cond_duty_sum = 0.0; - for (LeakagePower *leak : *corner_cell->leakagePowers()) { + for (LeakagePower *leak : *scene_cell->leakagePowers()) { FuncExpr *when = leak->when(); if (when) { PwrActivity cond_activity = evalActivity(when, inst); @@ -1345,34 +1544,34 @@ Power::findLeakagePower(const Instance *inst, // External. PwrActivity -Power::pinActivity(const Pin *pin) +Power::pinActivity(const Pin *pin, + const Scene *scene) { - ensureActivities(); + ensureActivities(scene); return findActivity(pin); } PwrActivity Power::findActivity(const Pin *pin) { + const Mode *mode = scene_->mode(); Vertex *vertex = graph_->pinLoadVertex(pin); - if (vertex && vertex->isConstant()) - return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant); - else if (vertex && search_->isClock(vertex)) { - if (activity_map_.hasKey(pin)) { - PwrActivity &activity = activity_map_[pin]; - if (activity.origin() != PwrActivityOrigin::unknown) - return activity; - } + if (vertex && mode->clkNetwork()->isClock(pin)) { + PwrActivity *activity = findKeyValuePtr(activity_map_, pin); + if (activity + && activity->origin() != PwrActivityOrigin::unknown) + return *activity; const Clock *clk = findClk(pin); float duty = clockDuty(clk); return PwrActivity(2.0 / clk->period(), duty, PwrActivityOrigin::clock); } else if (global_activity_.isSet()) return global_activity_; - else if (activity_map_.hasKey(pin)) { - PwrActivity &activity = activity_map_[pin]; - if (activity.origin() != PwrActivityOrigin::unknown) - return activity; + else { + PwrActivity *activity = findKeyValuePtr(activity_map_, pin); + if (activity + && activity->origin() != PwrActivityOrigin::unknown) + return *activity; } return PwrActivity(0.0, 0.0, PwrActivityOrigin::unknown); } @@ -1412,15 +1611,17 @@ Power::findSeqActivity(const Instance *inst, float Power::portVoltage(LibertyCell *cell, const LibertyPort *port, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - return pgNameVoltage(cell, port->relatedPowerPin(), dcalc_ap); + return pgNameVoltage(cell, port->relatedPowerPin(), scene, min_max); } float Power::pgNameVoltage(LibertyCell *cell, const char *pg_port_name, - const DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { if (pg_port_name) { LibertyPort *pg_port = cell->findLibertyPort(pg_port_name); @@ -1435,7 +1636,7 @@ Power::pgNameVoltage(LibertyCell *cell, } } - const Pvt *pvt = dcalc_ap->operatingConditions(); + Pvt *pvt = scene->sdc()->operatingConditions(min_max); if (pvt == nullptr) pvt = cell->libertyLibrary()->defaultOperatingConditions(); if (pvt) @@ -1502,7 +1703,7 @@ Power::reportActivityAnnotation(bool report_unannotated, PinSeq annotated_pins; for (auto const& [pin, activity] : user_activity_map_) annotated_pins.push_back(pin); - sort(annotated_pins.begin(), annotated_pins.end(), PinPathNameLess(sdc_network_)); + sort(annotated_pins, PinPathNameLess(sdc_network_)); report_->reportLine("Annotated pins:"); for (const Pin *pin : annotated_pins) { const PwrActivity &activity = user_activity_map_[pin]; @@ -1523,8 +1724,7 @@ Power::reportActivityAnnotation(bool report_unannotated, } delete inst_iter; - sort(unannotated_pins.begin(), unannotated_pins.end(), - PinPathNameLess(sdc_network_)); + sort(unannotated_pins, PinPathNameLess(sdc_network_)); report_->reportLine("Unannotated pins:"); for (const Pin *pin : unannotated_pins) { report_->reportLine(" %s", sdc_network_->pathName(pin)); @@ -1543,7 +1743,7 @@ Power::findUnannotatedPins(const Instance *inst, if (!network_->direction(pin)->isInternal() && !network_->direction(pin)->isPowerGround() && !(liberty_port && liberty_port->isPwrGnd()) - && user_activity_map_.find(pin) == user_activity_map_.end()) + && !user_activity_map_.contains(pin)) unannotated_pins.push_back(pin); } delete pin_iter; @@ -1581,12 +1781,12 @@ Power::pinCount() } float -Power::clockMinPeriod() +Power::clockMinPeriod(const Sdc *sdc) { - ClockSeq *clks = sdc_->clocks(); - if (clks && !clks->empty()) { + const ClockSeq &clks = sdc->clocks(); + if (!clks.empty()) { float min_period = INF; - for (const Clock *clk : *clks) + for (const Clock *clk : clks) min_period = min(min_period, clk->period()); return min_period; } @@ -1599,7 +1799,7 @@ Power::deleteInstanceBefore(const Instance *) { activities_valid_ = false; instance_powers_.clear(); - corner_ = nullptr; + scene_ = nullptr; } void @@ -1607,7 +1807,7 @@ Power::deletePinBefore(const Pin *) { activities_valid_ = false; instance_powers_.clear(); - corner_ = nullptr; + scene_ = nullptr; } //////////////////////////////////////////////////////////////// diff --git a/power/Power.hh b/power/Power.hh index abed2f407..839302532 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -25,9 +25,10 @@ #pragma once #include +#include +#include #include "StaConfig.hh" // CUDD -#include "UnorderedMap.hh" #include "Network.hh" #include "SdcClass.hh" #include "PowerClass.hh" @@ -40,13 +41,13 @@ struct DdManager; namespace sta { class Sta; -class Corner; -class DcalcAnalysisPt; +class Scene; class PropActivityVisitor; class BfsFwdIterator; class Vertex; +class ClkNetwork; -typedef std::pair SeqPin; +using SeqPin = std::pair; class SeqPinHash { @@ -65,9 +66,9 @@ public: const SeqPin &pin2) const; }; -typedef UnorderedMap PwrActivityMap; -typedef UnorderedMap PwrSeqActivityMap; +using PwrActivityMap = std::unordered_map; +using PwrSeqActivityMap = std::unordered_map; // The Power class has access to Sta components directly for // convenience but also requires access to the Sta class member functions. @@ -77,7 +78,25 @@ public: Power(StaState *sta); void clear(); void activitiesInvalid(); - void power(const Corner *corner, + void reportDesign(const Scene *scene, + int digits); + void reportInsts(const InstanceSeq &insts, + const Scene *scene, + int digits); + void reportHighestInsts(size_t count, + const Scene *scene, + int digits); + void reportDesignJson(const Scene *scene, + int digits); + void reportInstsJson(const InstanceSeq &insts, + const Scene *scene, + int digits); + InstPowers highestInstPowers(size_t count, + const Scene *scene); + InstPowers sortInstsByPower(const InstanceSeq &insts, + const Scene *scene); + + void power(const Scene *scene, // Return values. PowerResult &total, PowerResult &sequential, @@ -86,7 +105,8 @@ public: PowerResult ¯o, PowerResult &pad); PowerResult power(const Instance *inst, - const Corner *corner); + const Scene *scene); + void setGlobalActivity(float activity, float duty); void unsetGlobalActivity(); @@ -97,7 +117,8 @@ public: float activity, float duty); void unsetInputPortActivity(const Port *input_port); - PwrActivity pinActivity(const Pin *pin); + PwrActivity pinActivity(const Pin *pin, + const Scene *scene); void setUserActivity(const Pin *pin, float activity, float duty, @@ -105,19 +126,18 @@ public: void unsetUserActivity(const Pin *pin); void reportActivityAnnotation(bool report_unannotated, bool report_annotated); - float clockMinPeriod(); - InstanceSeq highestPowerInstances(size_t count, - const Corner *corner); + float clockMinPeriod(const Sdc *sdc); void deleteInstanceBefore(const Instance *inst); void deletePinBefore(const Pin *pin); protected: PwrActivity &activity(const Pin *pin); - bool inClockNetwork(const Instance *inst); + bool inClockNetwork(const Instance *inst, + const ClkNetwork *clk_network); void powerInside(const Instance *hinst, - const Corner *corner, + const Scene *scene, PowerResult &result); - void ensureActivities(); + void ensureActivities(const Scene *scene); bool hasUserActivity(const Pin *pin); PwrActivity &userActivity(const Pin *pin); void setSeqActivity(const Instance *reg, @@ -131,15 +151,22 @@ protected: void setActivity(const Pin *pin, PwrActivity &activity); PwrActivity findActivity(const Pin *pin); + void reportPowerRowJson(const char *name, + const PowerResult &power, + int digits, + const char *separator); + void reportPowerInstJson(const Instance *inst, + const PowerResult &power, + int digits); - void ensureInstPowers(const Corner *corner); - void findInstPowers(const Corner *corner); + void ensureInstPowers(); + void findInstPowers(); PowerResult power(const Instance *inst, LibertyCell *cell, - const Corner *corner); + const Scene *scene); void findInternalPower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); void findInputInternalPower(const Pin *to_pin, @@ -148,7 +175,7 @@ protected: LibertyCell *cell, PwrActivity &to_activity, float load_cap, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); void findOutputInternalPower(const LibertyPort *to_port, @@ -156,22 +183,22 @@ protected: LibertyCell *cell, PwrActivity &to_activity, float load_cap, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); void findLeakagePower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); void findSwitchingPower(const Instance *inst, LibertyCell *cell, - const Corner *corner, + const Scene *scene, // Return values. PowerResult &result); float getSlew(Vertex *vertex, const RiseFall *rf, - const Corner *corner); + const Scene *scene); float getMinRfSlew(const Pin *pin); const Clock *findInstClk(const Instance *inst); const Clock *findClk(const Pin *to_pin); @@ -180,10 +207,12 @@ protected: LibertyPort *port); float portVoltage(LibertyCell *cell, const LibertyPort *port, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); float pgNameVoltage(LibertyCell *cell, const char *pg_port_name, - const DcalcAnalysisPt *dcalc_ap); + const Scene *scene, + const MinMax *min_max); void seedActivities(BfsFwdIterator &bfs); void seedRegOutputActivities(const Instance *reg, Sequential *seq, @@ -209,9 +238,9 @@ protected: LibertyPort *from_port, const Instance *inst); LibertyPort *findLinkPort(const LibertyCell *cell, - const LibertyPort *corner_port); + const LibertyPort *scene_port); Pin *findLinkPin(const Instance *inst, - const LibertyPort *corner_port); + const LibertyPort *scene_port); void clockGatePins(const Instance *inst, // Return values. const Pin *&enable, @@ -226,6 +255,7 @@ protected: size_t pinCount(); private: + const Scene *scene_; // Port/pin activities set by set_pin_activity. // set_pin_activity -global PwrActivity global_activity_; @@ -240,9 +270,8 @@ private: Bdd bdd_; std::map instance_powers_; bool instance_powers_valid_; - const Corner *corner_; - static constexpr int max_activity_passes_ = 100; + static constexpr int max_activity_passes_ = 50; friend class PropActivityVisitor; }; diff --git a/power/Power.i b/power/Power.i index cdc59840b..9ff3438ae 100644 --- a/power/Power.i +++ b/power/Power.i @@ -27,6 +27,7 @@ %{ #include "Sta.hh" #include "Sdc.hh" +#include "Mode.hh" #include "power/Power.hh" #include "power/VcdReader.hh" #include "power/SaifReader.hh" @@ -37,9 +38,54 @@ using namespace sta; %inline %{ +void +report_power_design(const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerDesign(scene, digits); +} + +void +report_power_insts(const InstanceSeq insts, + const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerInsts(insts, scene, digits); +} + +void +report_power_highest_insts(size_t count, + const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerHighestInsts(count, scene, digits); +} + +void +report_power_design_json(const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerDesignJson(scene, digits); +} + +void +report_power_insts_json(const InstanceSeq insts, + const Scene *scene, + int digits) +{ + Sta *sta = Sta::sta(); + sta->reportPowerInstsJson(insts, scene, digits); +} + +//////////////////////////////////////////////////////////////// + static void pushPowerResultFloats(PowerResult &power, - FloatSeq &powers) + FloatSeq &powers) { powers.push_back(power.internal()); powers.push_back(power.switching()); @@ -47,11 +93,12 @@ pushPowerResultFloats(PowerResult &power, powers.push_back(power.total()); } +// Use lassign to retrieve power values. FloatSeq -design_power(const Corner *corner) +design_power(const Scene *scene) { PowerResult total, sequential, combinational, clock, macro, pad; - Sta::sta()->power(corner, total, sequential, combinational, clock, macro, pad); + Sta::sta()->power(scene, total, sequential, combinational, clock, macro, pad); FloatSeq powers; pushPowerResultFloats(total, powers); pushPowerResultFloats(sequential, powers); @@ -64,10 +111,10 @@ design_power(const Corner *corner) FloatSeq instance_power(Instance *inst, - const Corner *corner) + const Scene *scene) { Sta *sta = Sta::sta(); - PowerResult power = sta->power(inst, corner); + PowerResult power = sta->power(inst, scene); FloatSeq powers; powers.push_back(power.internal()); powers.push_back(power.switching()); @@ -78,7 +125,7 @@ instance_power(Instance *inst, void set_power_global_activity(float activity, - float duty) + float duty) { Power *power = Sta::sta()->power(); power->setGlobalActivity(activity, duty); @@ -93,7 +140,7 @@ unset_power_global_activity() void set_power_input_activity(float activity, - float duty) + float duty) { Power *power = Sta::sta()->power(); return power->setInputActivity(activity, duty); @@ -108,8 +155,8 @@ unset_power_input_activity() void set_power_input_port_activity(const Port *input_port, - float activity, - float duty) + float activity, + float duty) { Power *power = Sta::sta()->power(); return power->setInputPortActivity(input_port, activity, duty); @@ -124,8 +171,8 @@ unset_power_input_port_activity(const Port *input_port) void set_power_pin_activity(const Pin *pin, - float activity, - float duty) + float activity, + float duty) { Power *power = Sta::sta()->power(); return power->setUserActivity(pin, activity, duty, PwrActivityOrigin::user); @@ -139,29 +186,27 @@ unset_power_pin_activity(const Pin *pin) } float -clock_min_period() -{ - Power *power = Sta::sta()->power(); - return power->clockMinPeriod(); -} - -InstanceSeq -highest_power_instances(size_t count, - const Corner *corner) +clock_min_period(const char *mode_name) { - Power *power = Sta::sta()->power(); - return power->highestPowerInstances(count, corner); + Sta *sta = Sta::sta(); + Power *power = sta->power(); + const Mode *mode = sta->findMode(mode_name); + if (mode) + return power->clockMinPeriod(mode->sdc()); + else + return 0.0; } //////////////////////////////////////////////////////////////// void read_vcd_file(const char *filename, - const char *scope) + const char *scope, + const char *mode_name) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); - readVcdActivities(filename, scope, sta); + readVcdActivities(filename, scope, mode_name, sta); } //////////////////////////////////////////////////////////////// diff --git a/power/Power.tcl b/power/Power.tcl index 977695016..f56681d68 100644 --- a/power/Power.tcl +++ b/power/Power.tcl @@ -33,7 +33,7 @@ namespace eval sta { define_cmd_args "report_power" \ { [-instances instances]\ [-highest_power_instances count]\ - [-corner corner]\ + [-scene scene]\ [-digits digits]\ [-format format]\ [> filename] [>> filename] } @@ -42,7 +42,8 @@ proc_redirect report_power { global sta_report_default_digits parse_key_args "report_power" args \ - keys {-instances -highest_power_instances -corner -digits -format} flags {} + keys {-instances -highest_power_instances -corner -scene -format -digits}\ + flags {} check_argc_eq0 "report_power" $args @@ -55,7 +56,7 @@ proc_redirect report_power { } else { set digits $sta_report_default_digits } - set corner [parse_corner keys] + set scene [parse_scene keys] if { [info exists keys(-format)] } { set format $keys(-format) @@ -69,24 +70,24 @@ proc_redirect report_power { if { [info exists keys(-instances)] } { set insts [get_instances_error "-instances" $keys(-instances)] if { $format == "json" } { - report_power_insts_json $insts $corner $digits + report_power_insts_json $insts $scene $digits } else { - report_power_insts $insts $corner $digits + report_power_insts $insts $scene $digits } } elseif { [info exists keys(-highest_power_instances)] } { set count $keys(-highest_power_instances) check_positive_integer "-highest_power_instances" $count - set insts [highest_power_instances $count $corner] + set insts [highest_power_instances $count $scene] if { $format == "json" } { - report_power_insts_json $insts $corner $digits + report_power_insts_json $insts $scene $digits } else { - report_power_insts $insts $corner $digits + report_power_insts $insts $scene $digits } } else { if { $format == "json" } { - report_power_design_json $corner $digits + report_power_design_json $scene $digits } else { - report_power_design $corner $digits + report_power_design $scene $digits } } } @@ -101,223 +102,14 @@ proc liberty_libraries_exist {} { return $have_liberty } -proc report_power_design { corner digits } { - set power_result [design_power $corner] - set totals [lrange $power_result 0 3] - set sequential [lrange $power_result 4 7] - set combinational [lrange $power_result 8 11] - set clock [lrange $power_result 12 15] - set macro [lrange $power_result 16 19] - set pad [lrange $power_result 20 end] - lassign $totals design_internal design_switching design_leakage design_total - - set field_width [max [expr $digits + 6] 10] - report_power_title5 "Group" "Internal" "Switching" "Leakage" "Total" $field_width - report_power_title5_units " " "Power" "Power" "Power" "Power" "(Watts)" $field_width - report_title_dashes5 $field_width - report_power_row "Sequential" $sequential $design_total $field_width $digits - report_power_row "Combinational" $combinational $design_total $field_width $digits - report_power_row "Clock" $clock $design_total $field_width $digits - report_power_row "Macro" $macro $design_total $field_width $digits - report_power_row "Pad" $pad $design_total $field_width $digits - report_title_dashes5 $field_width - report_power_row "Total" $power_result $design_total $field_width $digits - - report_line "[format %-20s {}][power_col_percent $design_internal $design_total $field_width][power_col_percent $design_switching $design_total $field_width][power_col_percent $design_leakage $design_total $field_width]" -} - -proc report_power_design_json { corner digits } { - set power_result [design_power $corner] - set totals [lrange $power_result 0 3] - set sequential [lrange $power_result 4 7] - set combinational [lrange $power_result 8 11] - set clock [lrange $power_result 12 15] - set macro [lrange $power_result 16 19] - set pad [lrange $power_result 20 end] - - report_line "\{" - report_power_row_json "Sequential" $sequential $digits "," - report_power_row_json "Combinational" $combinational $digits "," - report_power_row_json "Clock" $clock $digits "," - report_power_row_json "Macro" $macro $digits "," - report_power_row_json "Pad" $pad $digits "," - report_power_row_json "Total" $totals $digits "" - report_line "\}" -} - -proc report_power_row_json { name row_result digits separator } { - lassign $row_result internal switching leakage total - report_line " \"$name\": \{" - report_line " \"internal\": [format %.${digits}e $internal]," - report_line " \"switching\": [format %.${digits}e $switching]," - report_line " \"leakage\": [format %.${digits}e $leakage]," - report_line " \"total\": [format %.${digits}e $total]" - report_line " \}$separator" -} - -proc max { x y } { - if { $x >= $y } { - return $x - } else { - return $y - } -} - -proc report_power_title5 { title1 title2 title3 title4 title5 field_width } { - report_line "[format %-20s $title1] [format %${field_width}s $title2] [format %${field_width}s $title3] [format %${field_width}s $title4] [format %${field_width}s $title5]" -} - -proc report_power_title5_units { title1 title2 title3 title4 title5 units field_width } { - report_line "[format %-20s $title1] [format %${field_width}s $title2] [format %${field_width}s $title3] [format %${field_width}s $title4] [format %${field_width}s $title5] $units" -} - -proc report_power_title4 { title1 title2 title3 title4 field_width } { - report_line " [format %${field_width}s $title1] [format %${field_width}s $title2] [format %${field_width}s $title3] [format %${field_width}s $title4]" -} - -proc report_power_title4_units { title1 title2 title3 title4 units field_width } { - report_line " [format %${field_width}s $title1] [format %${field_width}s $title2] [format %${field_width}s $title3] [format %${field_width}s $title4] $units" -} - -proc report_title_dashes5 { field_width } { - set count [expr 20 + ($field_width + 1) * 4] - report_title_dashes $count -} - -proc report_title_dashes4 { field_width } { - set count [expr ($field_width + 1) * 4] - report_title_dashes $count -} - -proc report_title_dashes { count } { - set line "" - for {set i 0} {$i < $count} {incr i} { - set line "-$line" - } - report_line $line -} - -proc report_power_row { type row_result design_total field_width digits } { - lassign $row_result internal switching leakage total - if { $design_total == 0.0 || [is_nan $design_total] } { - set percent 0.0 - } else { - set percent [expr $total / $design_total * 100] - } - report_line "[format %-20s $type][power_col $internal $field_width $digits][power_col $switching $field_width $digits][power_col $leakage $field_width $digits][power_col $total $field_width $digits] [format %5.1f $percent]%" -} - -proc is_nan { str } { - return [string match "*NaN" $str] -} - -proc power_col { pwr field_width digits } { - if { [is_nan $pwr] } { - format " %${field_width}s" $pwr - } else { - format " %$field_width.${digits}e" $pwr - } -} - -proc power_col_percent { col_total total field_width } { - if { $total == 0.0 || [is_nan $total]} { - set percent 0.0 - } else { - set percent [expr $col_total / $total * 100] - } - format "%$field_width.1f%%" $percent -} - -proc report_power_line { type pwr digits } { - if { [is_nan $pwr] } { - report_line [format "%-16s %s" $type $pwr] - } else { - report_line [format "%-16s %.${digits}e" $type $pwr] - } -} - -proc report_power_insts { insts corner digits } { - set inst_pwrs {} - foreach inst $insts { - set power_result [instance_power $inst $corner] - lappend inst_pwrs [list $inst $power_result] - } - set inst_pwrs [lsort -command inst_pwr_cmp $inst_pwrs] - - set field_width [max [expr $digits + 6] 10] - - report_power_title4 "Internal" "Switching" "Leakage" "Total" $field_width - report_power_title4_units "Power" "Power" "Power" "Power" "(Watts)" $field_width - report_title_dashes4 $field_width - - foreach inst_pwr $inst_pwrs { - set inst [lindex $inst_pwr 0] - set power [lindex $inst_pwr 1] - report_power_inst $inst $power $field_width $digits - } -} - -proc report_power_insts_json { insts corner digits } { - set inst_pwrs {} - foreach inst $insts { - set power_result [instance_power $inst $corner] - lappend inst_pwrs [list $inst $power_result] - } - set inst_pwrs [lsort -command inst_pwr_cmp $inst_pwrs] - - report_line "\[" - set first 1 - foreach inst_pwr $inst_pwrs { - set inst [lindex $inst_pwr 0] - set power [lindex $inst_pwr 1] - if { !$first } { - report_line "," - } - set first 0 - report_power_inst_json $inst $power $digits - } - report_line "\]" -} - -proc report_power_inst_json { inst power digits } { - lassign $power internal switching leakage total - set inst_name [get_full_name $inst] - report_line "\{" - report_line " \"name\": \"$inst_name\"," - report_line " \"internal\": [format %.${digits}e $internal]," - report_line " \"switching\": [format %.${digits}e $switching]," - report_line " \"leakage\": [format %.${digits}e $leakage]," - report_line " \"total\": [format %.${digits}e $total]" - report_line "\}" -} - -proc inst_pwr_cmp { inst_pwr1 inst_pwr2 } { - set pwr1 [lindex $inst_pwr1 1] - set pwr2 [lindex $inst_pwr2 1] - lassign $pwr1 internal1 switching1 leakage1 total1 - lassign $pwr2 internal2 switching2 leakage2 total2 - if { $total1 < $total2 } { - return 1 - } elseif { $total1 == $total2 } { - return 0 - } else { - return -1 - } -} - -proc report_power_inst { inst power_result field_width digits } { - lassign $power_result internal switching leakage total - report_line "[power_col $internal $field_width $digits][power_col $switching $field_width $digits][power_col $leakage $field_width $digits][power_col $total $field_width $digits] [get_full_name $inst]" -} - ################################################################ define_cmd_args "set_power_activity" { [-global]\ - [-input]\ - [-input_ports ports]\ - [-pins pins]\ - [-activity activity | -density density]\ - [-duty duty]\ + [-input]\ + [-input_ports ports]\ + [-pins pins]\ + [-activity activity | -density density]\ + [-duty duty]\ [-clock clock]} proc set_power_activity { args } { @@ -343,7 +135,7 @@ proc set_power_activity { args } { sta_error 307 "-activity requires a clock to be defined" } } - set density [expr $activity / [clock_min_period]] + set density [expr $activity / [clock_min_period [sta::cmd_mode]]] } if { [info exists keys(-density)] } { @@ -373,7 +165,7 @@ proc set_power_activity { args } { set ports [get_ports_error "input_ports" $keys(-input_ports)] foreach port $ports { if { [get_property $port "direction"] == "input" } { - if { [is_clock_src [sta::get_port_pin $port]] } { + if { [is_clock_src [sta::get_port_pin $port]] } { sta_warn 310 "activity cannot be set on clock ports." } else { set_power_input_port_activity $port $density $duty @@ -414,7 +206,7 @@ proc unset_power_activity { args } { set ports [get_ports_error "input_ports" $keys(-input_ports)] foreach port $ports { if { [get_property $port "direction"] == "input" } { - if { [is_clock_src [sta::get_port_pin $port]] } { + if { [is_clock_src [sta::get_port_pin $port]] } { sta_warn 303 "activity cannot be set on clock ports." } else { unset_power_input_port_activity $port @@ -451,11 +243,11 @@ proc read_power_activities { args } { ################################################################ -define_cmd_args "read_vcd" { [-scope scope] filename } +define_cmd_args "read_vcd" { [-scope scope] [-mode mode_name] filename } proc read_vcd { args } { parse_key_args "read_vcd" args \ - keys {-scope} flags {} + keys {-scope -mode_name} flags {} check_argc_eq1 "read_vcd" $args set filename [file nativename [lindex $args 0]] @@ -463,7 +255,11 @@ proc read_vcd { args } { if { [info exists keys(-scope)] } { set scope $keys(-scope) } - read_vcd_file $filename $scope + set mode_name [sta::cmd_mode] + if { [info exists keys(-mode)] } { + set mode_name $keys(-mode) + } + read_vcd_file $filename $scope $mode_name } ################################################################ @@ -499,9 +295,9 @@ proc_redirect report_activity_annotation { ################################################################ proc power_find_nan { } { - set corner [cmd_corner] + set scene [cmd_scene] foreach inst [network_leaf_instances] { - set power_result [instance_power $inst $corner] + set power_result [instance_power $inst $scene] lassign $power_result internal switching leakage total if { [is_nan $internal] || [is_nan $switching] || [is_nan $leakage] } { report_line "[get_full_name $inst] $internal $switching $leakage" @@ -510,5 +306,9 @@ proc power_find_nan { } { } } +proc is_nan { str } { + return [string match "*NaN" $str] +} + # sta namespace end. } diff --git a/power/ReportPower.cc b/power/ReportPower.cc new file mode 100644 index 000000000..3cd77090b --- /dev/null +++ b/power/ReportPower.cc @@ -0,0 +1,247 @@ +// 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 "ReportPower.hh" + +#include +#include + +#include "Report.hh" +#include "Network.hh" +#include "StringUtil.hh" + +namespace sta { + +ReportPower::ReportPower(StaState *sta) : + StaState(sta) +{ +} + +void +ReportPower::reportDesign(PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult &clock, + PowerResult ¯o, + PowerResult &pad, + int digits) +{ + float design_internal = total.internal(); + 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); + 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", ""); + 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); +} + +void +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); + reportTitleDashes4(field_width); + + for (const InstPower &inst_pwr : inst_pwrs) { + reportInst(inst_pwr.first, inst_pwr.second, field_width, digits); + } +} + +void +ReportPower::reportInst(const Instance *inst, + const PowerResult &power, + int field_width, + int digits) +{ + float internal = power.internal(); + 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); +} + +std::string +ReportPower::powerCol(float pwr, + int field_width, + int digits) +{ + if (std::isnan(pwr)) + return stdstrPrint(" %*s", field_width, "NaN"); + else + return stdstrPrint(" %*.*e", field_width, digits, pwr); +} + +std::string +ReportPower::powerColPercent(float col_total, + float total, + int field_width) +{ + float percent = 0.0; + if (total != 0.0 && !std::isnan(total)) { + percent = col_total / total * 100.0; + } + return stdstrPrint("%*.*f%%", field_width, 1, percent); +} + +void +ReportPower::reportTitle5(const char *title1, + 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); +} + +void +ReportPower::reportTitle5Units(const char *title1, + 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); +} + +void +ReportPower::reportTitleDashes5(int field_width) +{ + int count = 20 + (field_width + 1) * 4; + std::string dashes(count, '-'); + report_->reportLineString(dashes); +} + +void +ReportPower::reportRow(const char *type, + const PowerResult &power, + float design_total, + int field_width, + int digits) +{ + float internal = power.internal(); + 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); + 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); +} + +void +ReportPower::reportTitle4(const char *title1, + const char *title2, + const char *title3, + const char *title4, + int field_width) +{ + report_->reportLine(" %*s %*s %*s %*s", + field_width, title1, + field_width, title2, + field_width, title3, + field_width, title4); +} + +void +ReportPower::reportTitle4Units(const char *title1, + const char *title2, + const char *title3, + const char *title4, + 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); +} + +void +ReportPower::reportTitleDashes4(int field_width) +{ + int count = (field_width + 1) * 4; + std::string dashes(count, '-'); + report_->reportLineString(dashes); +} + +} // namespace diff --git a/power/ReportPower.hh b/power/ReportPower.hh new file mode 100644 index 000000000..c069a8b2e --- /dev/null +++ b/power/ReportPower.hh @@ -0,0 +1,91 @@ +// 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 "StaState.hh" +#include "NetworkClass.hh" +#include "PowerClass.hh" + +namespace sta { + +class ReportPower : public StaState +{ +public: + ReportPower(StaState *sta); + void reportDesign(PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult &clock, + PowerResult ¯o, + PowerResult &pad, + int digits); + void reportInsts(const InstPowers &inst_pwrs, + int digits); + +private: + std::string powerCol(float pwr, + int field_width, + int digits); + std::string powerColPercent(float col_total, + float total, + int field_width); + void reportTitle5(const char *title1, + const char *title2, + const char *title3, + const char *title4, + const char *title5, + int field_width); + void reportTitle5Units(const char *title1, + const char *title2, + const char *title3, + const char *title4, + const char *title5, + const char *units, + int field_width); + void reportTitleDashes5(int field_width); + void reportRow(const char *type, + const PowerResult &power, + float design_total, + int field_width, + int digits); + void reportTitle4(const char *title1, + const char *title2, + const char *title3, + const char *title4, + int field_width); + void reportTitle4Units(const char *title1, + const char *title2, + const char *title3, + const char *title4, + const char *units, + int field_width); + void reportTitleDashes4(int field_width); + void reportInst(const Instance *inst, + const PowerResult &power, + int field_width, + int digits); +}; + +} // namespace diff --git a/power/SaifReader.cc b/power/SaifReader.cc index ecd14277b..3a758ba2d 100644 --- a/power/SaifReader.cc +++ b/power/SaifReader.cc @@ -63,7 +63,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()) @@ -173,7 +173,7 @@ SaifReader::setNetDurations(const char *net_name, if (pin && !sdc_network_->isHierarchical(pin) && !sdc_network_->direction(pin)->isInternal() - && !(liberty_port && liberty_port->isPwrGnd())) { + && !(liberty_port && liberty_port->isPwrGnd())) { double t1 = durations[static_cast(SaifState::T1)]; float duty = t1 / duration_; double tc = durations[static_cast(SaifState::TC)]; diff --git a/power/SaifReaderPvt.hh b/power/SaifReaderPvt.hh index b5a57bdb6..cfbb7dd2d 100644 --- a/power/SaifReaderPvt.hh +++ b/power/SaifReaderPvt.hh @@ -46,7 +46,7 @@ class SaifScanner; enum class SaifState { T0, T1, TX, TZ, TB, TC, IG }; -typedef std::array(SaifState::IG)+1> SaifStateDurations; +using SaifStateDurations = std::array(SaifState::IG)+1>; class SaifReader : public StaState { diff --git a/power/VcdParse.hh b/power/VcdParse.hh index 267aff3cb..0fdb23014 100644 --- a/power/VcdParse.hh +++ b/power/VcdParse.hh @@ -33,8 +33,8 @@ namespace sta { -typedef int64_t VcdTime; -typedef std::vector VcdScope; +using VcdTime = int64_t; +using VcdScope = std::vector; enum class VcdVarType { wire, diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 2adebfdad..420e1449c 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -35,6 +35,7 @@ #include "VerilogNamespace.hh" #include "ParseBus.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Power.hh" #include "Sta.hh" @@ -123,8 +124,8 @@ typedef unordered_map VcdIdCountsMap; class VcdCountReader : public VcdReader { public: - VcdCountReader(const char *scope, - Network *sdc_network, + VcdCountReader(const std::string &scope, + const Network *sdc_network, Report *report, Debug *debug); VcdTime timeMax() const { return time_max_; } @@ -161,28 +162,29 @@ class VcdCountReader : public VcdReader size_t width, size_t bit_idx); - const char *scope_; - Network *sdc_network_; - Report *report_; - Debug *debug_; + const std::string scope_; double time_scale_; VcdTime time_min_; VcdTime time_max_; VcdIdCountsMap vcd_count_map_; + + const Network *sdc_network_; + Report *report_; + Debug *debug_; }; -VcdCountReader::VcdCountReader(const char *scope, - Network *sdc_network, +VcdCountReader::VcdCountReader(const std::string &scope, + const Network *sdc_network, Report *report, Debug *debug) : scope_(scope), - sdc_network_(sdc_network), - report_(report), - debug_(debug), time_scale_(1.0), time_min_(0), - time_max_(0) + time_max_(0), + sdc_network_(sdc_network), + report_(report), + debug_(debug) { } @@ -229,7 +231,7 @@ VcdCountReader::makeVar(const VcdScope &scope, path_name += context; first = false; } - size_t scope_length = strlen(scope_); + size_t scope_length = scope_.size(); // string::starts_with in c++20 if (scope_length == 0 || path_name.substr(0, scope_length) == scope_) { @@ -361,8 +363,9 @@ VcdCountReader::varAppendBusValue(const string &id, class ReadVcdActivities : public StaState { public: - ReadVcdActivities(const char *filename, - const char *scope, + ReadVcdActivities(const std::string &filename, + const std::string &scope, + const Sdc *sdc, Sta *sta); void readActivities(); @@ -371,32 +374,38 @@ class ReadVcdActivities : public StaState void checkClkPeriod(const Pin *pin, double transition_count); - const char *filename_; + const std::string filename_; + + std::set annotated_pins_; VcdCountReader vcd_reader_; VcdParse vcd_parse_; - + const Sdc *sdc_; Power *power_; - std::set annotated_pins_; static constexpr double sim_clk_period_tolerance_ = .1; }; void -readVcdActivities(const char *filename, - const char *scope, +readVcdActivities(const std::string &filename, + const std::string &scope, + const std::string &mode_name, Sta *sta) { - ReadVcdActivities reader(filename, scope, sta); + const Mode *mode = sta->findMode(mode_name); + const Sdc *sdc = mode->sdc(); + ReadVcdActivities reader(filename, scope, sdc, sta); reader.readActivities(); } -ReadVcdActivities::ReadVcdActivities(const char *filename, - const char *scope, +ReadVcdActivities::ReadVcdActivities(const std::string &filename, + const std::string &scope, + const Sdc *sdc, Sta *sta) : StaState(sta), filename_(filename), vcd_reader_(scope, sdc_network_, report_, debug_), vcd_parse_(report_, debug_), + sdc_(sdc), power_(sta->power()) { } @@ -404,11 +413,11 @@ ReadVcdActivities::ReadVcdActivities(const char *filename, void ReadVcdActivities::readActivities() { - ClockSeq *clks = sdc_->clocks(); - if (clks->empty()) + const ClockSeq &clks = sdc_->clocks(); + if (clks.empty()) report_->error(820, "No clocks have been defined."); - vcd_parse_.read(filename_, &vcd_reader_); + vcd_parse_.read(filename_.c_str(), &vcd_reader_); if (vcd_reader_.timeMax() > 0) setActivities(); @@ -462,7 +471,7 @@ ReadVcdActivities::checkClkPeriod(const Pin *pin, double sim_period = (time_max - time_min) * time_scale / (transition_count / 2.0); for (Clock *clk : *clks) { if (transition_count == 0) - report_->warn(1452, "clock %s pin %s has no vcd transitions.", + report_->warn(1453, "clock %s pin %s has no vcd transitions.", clk->name(), sdc_network_->pathName(pin)); else { diff --git a/power/VcdReader.hh b/power/VcdReader.hh index a5a67ef34..ca1d6d2e2 100644 --- a/power/VcdReader.hh +++ b/power/VcdReader.hh @@ -24,13 +24,16 @@ #pragma once +#include + namespace sta { class Sta; void -readVcdActivities(const char *filename, - const char *scope, +readVcdActivities(const std::string &filename, + const std::string &scope, + const std::string &mode_name, Sta *sta); } // namespace diff --git a/sdc/Clock.cc b/sdc/Clock.cc index 8e7aadd14..3960f670a 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -26,6 +26,7 @@ #include +#include "ContainerHelpers.hh" #include "Error.hh" #include "StringUtil.hh" #include "MinMax.hh" @@ -41,7 +42,7 @@ static bool isPowerOfTwo(int i); Clock::Clock(const char *name, - int index, + int index, const Network *network) : name_(stringCopy(name)), pins_(network), @@ -71,11 +72,11 @@ Clock::Clock(const char *name, void Clock::initClk(PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - const char *comment, - const Network *network) + bool add_to_pins, + float period, + FloatSeq *waveform, + const char *comment, + const Network *network) { is_generated_ = false; setPins(pins, network); @@ -96,7 +97,7 @@ Clock::isVirtual() const void Clock::setPins(PinSet *pins, - const Network *network) + const Network *network) { if (pins) pins_ = *pins; @@ -108,11 +109,8 @@ void Clock::makeLeafPins(const Network *network) { leaf_pins_.clear(); - PinSet::Iterator pin_iter(pins_); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + for (const Pin *pin : pins_) findLeafDriverPins(pin, network, &leaf_pins_); - } } void @@ -181,9 +179,9 @@ Clock::setClkEdgeTime(const RiseFall *rf) const Pin * Clock::defaultPin() const { - PinSet::ConstIterator pin_iter(leaf_pins_); - if (pin_iter.hasNext()) - return pin_iter.next(); + auto itr = leaf_pins_.begin(); + if (itr != leaf_pins_.end()) + return *itr; else return nullptr; } @@ -202,17 +200,17 @@ Clock::setIsPropagated(bool propagated) void Clock::slew(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &slew, - bool &exists) const + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const { slews_.value(rf, min_max, slew, exists); } float Clock::slew(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { float slew; bool exists; @@ -224,16 +222,16 @@ Clock::slew(const RiseFall *rf, void Clock::setSlew(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const MinMaxAll *min_max, + float slew) { slews_.setValue(rf, min_max, slew); } void Clock::setSlew(const RiseFall *rf, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { slews_.setValue(rf, min_max, slew); } @@ -246,29 +244,29 @@ Clock::removeSlew() void Clock::setSlewLimit(const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew) + const PathClkOrData clk_data, + const MinMax *min_max, + float slew) { slew_limits_[int(clk_data)].setValue(rf, min_max, slew); } void Clock::slewLimit(const RiseFall *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - // Return values. - float &slew, - bool &exists) const + const PathClkOrData clk_data, + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const { slew_limits_[int(clk_data)].value(rf, min_max, slew, exists); } void Clock::uncertainty(const SetupHold *setup_hold, - // Return values. - float &uncertainty, - bool &exists) const + // Return values. + float &uncertainty, + bool &exists) const { if (uncertainties_) uncertainties_->value(setup_hold, uncertainty, exists); @@ -280,7 +278,7 @@ Clock::uncertainty(const SetupHold *setup_hold, void Clock::setUncertainty(const SetupHoldAll *setup_hold, - float uncertainty) + float uncertainty) { if (uncertainties_ == nullptr) uncertainties_ = new ClockUncertainties; @@ -289,7 +287,7 @@ Clock::setUncertainty(const SetupHoldAll *setup_hold, void Clock::setUncertainty(const SetupHold *setup_hold, - float uncertainty) + float uncertainty) { if (uncertainties_ == nullptr) uncertainties_ = new ClockUncertainties; @@ -318,19 +316,19 @@ Clock::waveformInvalid() void Clock::initGeneratedClk(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, - bool is_propagated, - const char *comment, - const Network *network) + 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, + bool is_propagated, + const char *comment, + const Network *network) { is_generated_ = true; setPins(pins, network); @@ -431,7 +429,7 @@ Clock::generate(const Clock *src_clk) void Clock::generateScaledClk(const Clock *src_clk, - float scale) + float scale) { period_ = src_clk->period() * scale; if (duty_cycle_ != 0.0) { @@ -440,11 +438,8 @@ Clock::generateScaledClk(const Clock *src_clk, waveform_->push_back(rise + period_ * duty_cycle_ / 100.0F); } else { - FloatSeq::ConstIterator wave_iter(src_clk->waveform()); - while (wave_iter.hasNext()) { - float time = wave_iter.next(); + for (float time : *src_clk->waveform()) waveform_->push_back(time * scale); - } } } @@ -499,22 +494,20 @@ Clock::masterClkEdgeTr(const RiseFall *rf) const void Clock::srcPinVertices(VertexSet &src_vertices, - const Network *network, - Graph *graph) + const Network *network, + Graph *graph) { if (network->isHierarchical(src_pin_)) { // Use the clocks on a non-hierarchical pin on the same net. PinSet leaf_pins(network); findLeafDriverPins(src_pin_, network, &leaf_pins); - PinSet::Iterator pin_iter(leaf_pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + for (const Pin *pin : leaf_pins) { Vertex *vertex, *bidirect_drvr_vertex; graph->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) - src_vertices.insert(vertex); + src_vertices.insert(vertex); if (bidirect_drvr_vertex) - src_vertices.insert(bidirect_drvr_vertex); + src_vertices.insert(bidirect_drvr_vertex); } } else { @@ -535,7 +528,7 @@ Clock::isDivideByOneCombinational() const //////////////////////////////////////////////////////////////// ClockEdge::ClockEdge(Clock *clock, - const RiseFall *rf) : + const RiseFall *rf) : clock_(clock), rf_(rf), name_(stringPrint("%s %s", clock_->name(), rf_->to_string().c_str())), @@ -597,7 +590,7 @@ clkCmp(const Clock *clk1, int clkEdgeCmp(const ClockEdge *clk_edge1, - const ClockEdge *clk_edge2) + const ClockEdge *clk_edge2) { if (clk_edge1 == nullptr && clk_edge2) return -1; @@ -619,7 +612,7 @@ clkEdgeCmp(const ClockEdge *clk_edge1, bool clkEdgeLess(const ClockEdge *clk_edge1, - const ClockEdge *clk_edge2) + const ClockEdge *clk_edge2) { return clkEdgeCmp(clk_edge1, clk_edge2) < 0; } @@ -627,7 +620,7 @@ clkEdgeLess(const ClockEdge *clk_edge1, //////////////////////////////////////////////////////////////// InterClockUncertainty::InterClockUncertainty(const Clock *src, - const Clock *target) : + const Clock *target) : src_(src), target_(target) { @@ -642,20 +635,20 @@ InterClockUncertainty::empty() const void InterClockUncertainty::uncertainty(const RiseFall *src_rf, - const RiseFall *tgt_rf, - const SetupHold *setup_hold, - float &uncertainty, - bool &exists) const + const RiseFall *tgt_rf, + const SetupHold *setup_hold, + float &uncertainty, + bool &exists) const { uncertainties_[src_rf->index()].value(tgt_rf, setup_hold, - uncertainty, exists); + uncertainty, exists); } void InterClockUncertainty::setUncertainty(const RiseFallBoth *src_rf, - const RiseFallBoth *tgt_rf, - const SetupHoldAll *setup_hold, - float uncertainty) + const RiseFallBoth *tgt_rf, + const SetupHoldAll *setup_hold, + float uncertainty) { for (auto src_rf_index : src_rf->rangeIndex()) uncertainties_[src_rf_index].setValue(tgt_rf, setup_hold, uncertainty); @@ -663,8 +656,8 @@ InterClockUncertainty::setUncertainty(const RiseFallBoth *src_rf, void InterClockUncertainty::removeUncertainty(const RiseFallBoth *src_rf, - const RiseFallBoth *tgt_rf, - const SetupHoldAll *setup_hold) + const RiseFallBoth *tgt_rf, + const SetupHoldAll *setup_hold) { for (auto src_rf_index : src_rf->rangeIndex()) uncertainties_[src_rf_index].removeValue(tgt_rf, setup_hold); @@ -678,18 +671,18 @@ InterClockUncertainty::uncertainties(const RiseFall *src_rf) const bool InterClockUncertaintyLess::operator()(const InterClockUncertainty *inter1, - const InterClockUncertainty *inter2)const + const InterClockUncertainty *inter2)const { return inter1->src()->index() < inter2->src()->index() || (inter1->src() == inter2->src() - && inter1->target()->index() < inter2->target()->index()); + && inter1->target()->index() < inter2->target()->index()); } //////////////////////////////////////////////////////////////// bool ClockNameLess::operator()(const Clock *clk1, - const Clock *clk2) + const Clock *clk2) { return stringLess(clk1->name(), clk2->name()); } @@ -727,26 +720,7 @@ int compare(const ClockSet *set1, const ClockSet *set2) { - size_t size1 = set1 ? set1->size() : 0; - size_t size2 = set2 ? set2->size() : 0; - if (size1 == size2) { - ClockSet::ConstIterator iter1(set1); - ClockSet::ConstIterator iter2(set2); - while (iter1.hasNext() && iter2.hasNext()) { - Clock *clk1 = iter1.next(); - Clock *clk2 = iter2.next(); - int id1 = clk1->index(); - int id2 = clk2->index(); - if (id1 < id2) - return -1; - else if (id1 > id2) - return 1; - } - // Sets are equal. - return 0; - } - else - return (size1 > size2) ? 1 : -1; + return sta::compare(set1, set2, ClockIndexLess()); } } // namespace diff --git a/sdc/ClockGroups.cc b/sdc/ClockGroups.cc index 850611e56..7a67fca7c 100644 --- a/sdc/ClockGroups.cc +++ b/sdc/ClockGroups.cc @@ -24,16 +24,17 @@ #include "ClockGroups.hh" +#include "ContainerHelpers.hh" #include "StringUtil.hh" namespace sta { ClockGroups::ClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment) : + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + const char *comment) : SdcCmdComment(comment), name_(stringCopy(name)), logically_exclusive_(logically_exclusive), @@ -46,7 +47,7 @@ ClockGroups::ClockGroups(const char *name, ClockGroups::~ClockGroups() { stringDelete(name_); - groups_.deleteContentsClear(); + deleteContents(groups_); } void diff --git a/sdc/ClockInsertion.cc b/sdc/ClockInsertion.cc index 4c2455182..ee317e4b0 100644 --- a/sdc/ClockInsertion.cc +++ b/sdc/ClockInsertion.cc @@ -27,7 +27,7 @@ namespace sta { ClockInsertion::ClockInsertion(const Clock *clk, - const Pin *pin) : + const Pin *pin) : clk_(clk), pin_(pin) { @@ -35,9 +35,9 @@ ClockInsertion::ClockInsertion(const Clock *clk, void ClockInsertion::setDelay(const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay) + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) { for (auto el_index : early_late->rangeIndex()) delays_[el_index].setValue(rf, min_max, delay); @@ -45,8 +45,8 @@ ClockInsertion::setDelay(const RiseFallBoth *rf, float ClockInsertion::delay(const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late) + const MinMax *min_max, + const EarlyLate *early_late) { float insertion; bool exists; @@ -59,11 +59,11 @@ ClockInsertion::delay(const RiseFall *rf, void ClockInsertion::delay(const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - // Return values. - float &insertion, - bool &exists) + const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, + bool &exists) { delays_[early_late->index()].value(rf, min_max, insertion, exists); @@ -73,9 +73,9 @@ ClockInsertion::delay(const RiseFall *rf, void ClockInsertion::setDelay(const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - float delay) + const MinMax *min_max, + const EarlyLate *early_late, + float delay) { delays_[early_late->index()].setValue(rf, min_max, delay); } diff --git a/sdc/ClockLatency.cc b/sdc/ClockLatency.cc index 22fe82ba9..d9267663b 100644 --- a/sdc/ClockLatency.cc +++ b/sdc/ClockLatency.cc @@ -27,7 +27,7 @@ namespace sta { ClockLatency::ClockLatency(const Clock *clk, - const Pin *pin) : + const Pin *pin) : clk_(clk), pin_(pin) { @@ -35,15 +35,15 @@ ClockLatency::ClockLatency(const Clock *clk, void ClockLatency::setDelay(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay) + const MinMaxAll *min_max, + float delay) { delays_.setValue(rf, min_max, delay); } float ClockLatency::delay(const RiseFall *rf, - const MinMax *min_max) + const MinMax *min_max) { float latency; bool exists; @@ -56,10 +56,10 @@ ClockLatency::delay(const RiseFall *rf, void ClockLatency::delay(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) { delays_.value(rf, min_max, latency, exists); @@ -69,8 +69,8 @@ ClockLatency::delay(const RiseFall *rf, void ClockLatency::setDelay(const RiseFall *rf, - const MinMax *min_max, - float delay) + const MinMax *min_max, + float delay) { delays_.setValue(rf, min_max, delay); } diff --git a/sdc/CycleAccting.cc b/sdc/CycleAccting.cc index 6ac5f8756..9df530e5b 100644 --- a/sdc/CycleAccting.cc +++ b/sdc/CycleAccting.cc @@ -27,6 +27,7 @@ #include // ceil #include // max +#include "ContainerHelpers.hh" #include "Debug.hh" #include "Fuzzy.hh" #include "Units.hh" @@ -49,7 +50,7 @@ CycleAcctings::~CycleAcctings() void CycleAcctings::clear() { - cycle_acctings_.deleteContentsClear(); + deleteContents(cycle_acctings_); } // Determine cycle accounting "on demand". @@ -60,7 +61,7 @@ CycleAcctings::cycleAccting(const ClockEdge *src, if (src == nullptr) src = tgt; CycleAccting probe(src, tgt); - CycleAccting *acct = cycle_acctings_.findKey(&probe); + CycleAccting *acct = findKey(cycle_acctings_, &probe); if (acct == nullptr) { acct = new CycleAccting(src, tgt); if (src == sdc_->defaultArrivalClockEdge()) @@ -78,20 +79,20 @@ CycleAcctings::reportClkToClkMaxCycleWarnings(Report *report) // Find cycle acctings that exceed max cycle count. Eliminate // duplicate warnings between different src/tgt clk edges. ClockPairSet clk_warnings; - for (Clock *src_clk : *sdc_->clocks()) { + for (Clock *src_clk : sdc_->clocks()) { for (const RiseFall *src_rf : RiseFall::range()) { ClockEdge *src = src_clk->edge(src_rf); - for (Clock *tgt_clk : *sdc_->clocks()) { + for (Clock *tgt_clk : sdc_->clocks()) { for (const RiseFall *tgt_rf : RiseFall::range()) { ClockEdge *tgt = tgt_clk->edge(tgt_rf); CycleAccting probe(src, tgt); - CycleAccting *acct = cycle_acctings_.findKey(&probe); + CycleAccting *acct = findKey(cycle_acctings_, &probe); if (acct && acct->maxCyclesExceeded()) { // Canonicalize the warning wrt src/tgt. ClockPair clk_pair1(src_clk, tgt_clk); ClockPair clk_pair2(tgt_clk, src_clk); - if (!clk_warnings.hasKey(clk_pair1) - && !clk_warnings.hasKey(clk_pair2)) { + if (!clk_warnings.contains(clk_pair1) + && !clk_warnings.contains(clk_pair2)) { report->warn(1010, "No common period was found between clocks %s and %s.", src_clk->name(), tgt_clk->name()); @@ -107,7 +108,7 @@ CycleAcctings::reportClkToClkMaxCycleWarnings(Report *report) //////////////////////////////////////////////////////////////// CycleAccting::CycleAccting(const ClockEdge *src, - const ClockEdge *tgt) : + const ClockEdge *tgt) : src_(src), tgt_(tgt), max_cycles_exceeded_(false) @@ -157,121 +158,121 @@ CycleAccting::findDelays(StaState *sta) double tgt_time = tgt_cycle_start + tgt_->time(); double tgt_opp_time = tgt_cycle_start + tgt_opp_time1; for (src_cycle = firstCycle(src_); - ; - src_cycle++) { - double src_cycle_start = src_cycle * src_period; - double src_time = src_cycle_start + src_->time(); - - // Make sure both setup and hold required are determined. - 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", + ; + src_cycle++) { + double src_cycle_start = src_cycle * src_period; + double src_time = src_cycle_start + src_->time(); + + // Make sure both setup and hold required are determined. + 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", 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 = %s, required = %s", time_unit->asString(delay_[hold_index]), time_unit->asString(required_[hold_index])); - debugPrint(debug, "cycle_acct", 1, + debugPrint(debug, "cycle_acct", 1, " converged at src cycles = %d tgt cycles = %d", src_cycle, tgt_cycle); - return; - } + return; + } - 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", + 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", 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, " %s tgt cycle %d %s + %s = %s", tgt_->name(), tgt_cycle, time_unit->asString(tgt_cycle_start), time_unit->asString(tgt_->time()), time_unit->asString(tgt_time)); - // For setup checks, target has to be AFTER source. - if (fuzzyGreater(tgt_time, src_time)) { - tgt_past_src = true; - double delay = tgt_time - src_time; - if (fuzzyLess(delay, delay_[setup_index])) { - double required = tgt_time - src_cycle_start; - setSetupAccting(src_cycle, tgt_cycle, delay, required); - debugPrint(debug, "cycle_acct", 2, + // For setup checks, target has to be AFTER source. + if (fuzzyGreater(tgt_time, src_time)) { + tgt_past_src = true; + double delay = tgt_time - src_time; + if (fuzzyLess(delay, delay_[setup_index])) { + 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", time_unit->asString(delay_[setup_index]), time_unit->asString(required_[setup_index])); - } - } - - // Data check setup checks are zero cycle. - if (fuzzyLessEqual(tgt_time, src_time)) { - double setup_delay = src_time - tgt_time; - if (fuzzyLess(setup_delay, delay_[data_check_setup_index])) { - double setup_required = tgt_time - src_cycle_start; - setAccting(TimingRole::dataCheckSetup(), src_cycle, tgt_cycle, - setup_delay, setup_required); - double hold_required = tgt_time - (src_cycle_start + src_period); - double hold_delay = (src_period + src_time) - tgt_time; - setAccting(TimingRole::dataCheckHold(), - src_cycle + 1, tgt_cycle, hold_delay, hold_required); - } - } - - // Latch setup cycle accting for the enable is the data clk edge - // closest to the disable (opposite) edge. - if (fuzzyGreater(tgt_opp_time, src_time)) { - double delay = tgt_opp_time - src_time; - if (fuzzyLess(delay, delay_[latch_setup_index])) { - double latch_tgt_time = tgt_time; - int latch_tgt_cycle = tgt_cycle; - // Enable time is the edge before the disable. - if (tgt_time > tgt_opp_time) { - latch_tgt_time -= tgt_period; - latch_tgt_cycle--; - } - double required = latch_tgt_time - src_cycle_start; - setAccting(TimingRole::latchSetup(), - src_cycle, latch_tgt_cycle, delay, required); - debugPrint(debug, "cycle_acct", 2, + } + } + + // Data check setup checks are zero cycle. + if (fuzzyLessEqual(tgt_time, src_time)) { + double setup_delay = src_time - tgt_time; + if (fuzzyLess(setup_delay, delay_[data_check_setup_index])) { + double setup_required = tgt_time - src_cycle_start; + setAccting(TimingRole::dataCheckSetup(), src_cycle, tgt_cycle, + setup_delay, setup_required); + double hold_required = tgt_time - (src_cycle_start + src_period); + double hold_delay = (src_period + src_time) - tgt_time; + setAccting(TimingRole::dataCheckHold(), + src_cycle + 1, tgt_cycle, hold_delay, hold_required); + } + } + + // Latch setup cycle accting for the enable is the data clk edge + // closest to the disable (opposite) edge. + if (fuzzyGreater(tgt_opp_time, src_time)) { + double delay = tgt_opp_time - src_time; + if (fuzzyLess(delay, delay_[latch_setup_index])) { + double latch_tgt_time = tgt_time; + int latch_tgt_cycle = tgt_cycle; + // Enable time is the edge before the disable. + if (tgt_time > tgt_opp_time) { + latch_tgt_time -= tgt_period; + latch_tgt_cycle--; + } + double required = latch_tgt_time - src_cycle_start; + setAccting(TimingRole::latchSetup(), + src_cycle, latch_tgt_cycle, delay, required); + debugPrint(debug, "cycle_acct", 2, " latch setup min delay = %s, required = %s", time_unit->asString(delay_[latch_setup_index]), time_unit->asString(required_[latch_setup_index])); - } - } - - // For hold checks, target has to be BEFORE source. - if (fuzzyLessEqual(tgt_time, src_time)) { - double delay = src_time - tgt_time; - src_past_tgt = true; - if (fuzzyLess(delay, delay_[hold_index])) { - double required = tgt_time - src_cycle_start; - setHoldAccting(src_cycle, tgt_cycle, delay, required); - debugPrint(debug, "cycle_acct", 2, + } + } + + // For hold checks, target has to be BEFORE source. + if (fuzzyLessEqual(tgt_time, src_time)) { + double delay = src_time - tgt_time; + src_past_tgt = true; + if (fuzzyLess(delay, delay_[hold_index])) { + 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", time_unit->asString(delay_[hold_index]), time_unit->asString(required_[hold_index])); - } - } - - // Gated clock hold checks are in the same cycle as the - // setup check. - if (fuzzyLessEqual(tgt_opp_time, src_time)) { - double delay = src_time - tgt_time; - if (fuzzyLess(delay, delay_[gclk_hold_index])) { - double required = tgt_time - src_cycle_start; - setAccting(TimingRole::gatedClockHold(), - src_cycle, tgt_cycle, delay, required); - debugPrint(debug, "cycle_acct", 2, + } + } + + // Gated clock hold checks are in the same cycle as the + // setup check. + if (fuzzyLessEqual(tgt_opp_time, src_time)) { + double delay = src_time - tgt_time; + if (fuzzyLess(delay, delay_[gclk_hold_index])) { + double required = tgt_time - src_cycle_start; + setAccting(TimingRole::gatedClockHold(), + src_cycle, tgt_cycle, delay, required); + debugPrint(debug, "cycle_acct", 2, " gated clk hold min delay = %s, required = %s", time_unit->asString(delay_[gclk_hold_index]), time_unit->asString(required_[gclk_hold_index])); - } - } + } + } } tgt_cycle++; } @@ -297,9 +298,9 @@ CycleAccting::firstCycle(const ClockEdge *clk_edge) const void CycleAccting::setSetupAccting(int src_cycle, - int tgt_cycle, - float delay, - float req) + int tgt_cycle, + float delay, + float req) { setAccting(TimingRole::setup(), src_cycle, tgt_cycle, delay, req); setAccting(TimingRole::outputSetup(), src_cycle, tgt_cycle, delay, req); @@ -309,9 +310,9 @@ CycleAccting::setSetupAccting(int src_cycle, void CycleAccting::setHoldAccting(int src_cycle, - int tgt_cycle, - float delay, - float req) + int tgt_cycle, + float delay, + float req) { setAccting(TimingRole::hold(), src_cycle, tgt_cycle, delay, req); setAccting(TimingRole::outputHold(), src_cycle, tgt_cycle, delay, req); @@ -321,10 +322,10 @@ CycleAccting::setHoldAccting(int src_cycle, void CycleAccting::setAccting(const TimingRole *role, - int src_cycle, - int tgt_cycle, - float delay, - float req) + int src_cycle, + int tgt_cycle, + float delay, + float req) { int index = role->index(); src_cycle_[index] = src_cycle; @@ -351,9 +352,9 @@ CycleAccting::findDefaultArrivalSrcDelays() void CycleAccting::setDefaultSetupAccting(int src_cycle, - int tgt_cycle, - float delay, - float req) + int tgt_cycle, + float delay, + float req) { setSetupAccting(src_cycle, tgt_cycle, delay, req); setAccting(TimingRole::latchSetup(), src_cycle, tgt_cycle, delay, req); @@ -362,9 +363,9 @@ CycleAccting::setDefaultSetupAccting(int src_cycle, void CycleAccting::setDefaultHoldAccting(int src_cycle, - int tgt_cycle, - float delay, - float req) + int tgt_cycle, + float delay, + float req) { setHoldAccting(src_cycle, tgt_cycle, delay, req); setAccting(TimingRole::dataCheckHold(), src_cycle, tgt_cycle, delay, req); @@ -404,14 +405,14 @@ CycleAccting::targetTimeOffset(const TimingRole *check_role) bool CycleAcctingLess::operator()(const CycleAccting *acct1, - const CycleAccting *acct2) const + const CycleAccting *acct2) const { int src_index1 = acct1->src()->index(); int src_index2 = acct2->src()->index(); return src_index1 < src_index2 || (src_index1 == src_index2 - && acct1->target()->index() < acct2->target()->index()); + && acct1->target()->index() < acct2->target()->index()); } size_t @@ -422,7 +423,7 @@ CycleAcctingHash::operator()(const CycleAccting *acct) const bool CycleAcctingEqual::operator()(const CycleAccting *acct1, - const CycleAccting *acct2) const + const CycleAccting *acct2) const { return acct1->src() == acct2->src() && acct1->target() == acct2->target(); diff --git a/sdc/DataCheck.cc b/sdc/DataCheck.cc index 15a839ba2..23d3abd27 100644 --- a/sdc/DataCheck.cc +++ b/sdc/DataCheck.cc @@ -30,8 +30,8 @@ namespace sta { DataCheck::DataCheck(Pin *from, - Pin *to, - Clock *clk) : + Pin *to, + Clock *clk) : from_(from), to_(to), clk_(clk) @@ -40,21 +40,21 @@ DataCheck::DataCheck(Pin *from, void DataCheck::margin(const RiseFall *from_rf, - const RiseFall *to_rf, - const SetupHold *setup_hold, - // Return values. - float &margin, - bool &exists) const + const RiseFall *to_rf, + const SetupHold *setup_hold, + // Return values. + float &margin, + bool &exists) const { return margins_[from_rf->index()].value(to_rf, setup_hold, - margin, exists); + margin, exists); } void DataCheck::setMargin(const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, - float margin) + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + float margin) { for (auto from_rf_index : from_rf->rangeIndex()) margins_[from_rf_index].setValue(to_rf, setup_hold, margin); @@ -62,8 +62,8 @@ DataCheck::setMargin(const RiseFallBoth *from_rf, void DataCheck::removeMargin(const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold) + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold) { for (int from_rf_index : from_rf->rangeIndex()) margins_[from_rf_index].removeValue(to_rf, setup_hold); @@ -81,9 +81,9 @@ DataCheck::empty() const void DataCheck::marginIsOneValue(const SetupHold *setup_hold, - // Return values. - float &value, - bool &one_value) const + // Return values. + float &value, + bool &one_value) const { float value1, value2; if (margins_[RiseFall::riseIndex()].isOneValue(setup_hold, value1) @@ -105,7 +105,7 @@ DataCheckLess::DataCheckLess(const Network *network) : bool DataCheckLess::operator()(const DataCheck *check1, - const DataCheck *check2) const + const DataCheck *check2) const { const Pin *from1 = check1->from(); const Pin *from2 = check2->from(); @@ -115,9 +115,9 @@ DataCheckLess::operator()(const DataCheck *check1, const Clock *clk2 = check2->clk(); return network_->id(from1) < network_->id(from2) || (from1 == from2 - && (network_->id(to1) < network_->id(to2) - || (to1 == to2 - && clkCmp(clk1, clk2) < 0))); + && (network_->id(to1) < network_->id(to2) + || (to1 == to2 + && clkCmp(clk1, clk2) < 0))); } } // namespace diff --git a/sdc/DeratingFactors.cc b/sdc/DeratingFactors.cc index 34609412d..3da41c415 100644 --- a/sdc/DeratingFactors.cc +++ b/sdc/DeratingFactors.cc @@ -45,9 +45,9 @@ DeratingFactors::DeratingFactors() void DeratingFactors::setFactor(PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor) + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor) { for (auto rf1 : rf->range()) factors_[int(clk_data)].setValue(rf1, early_late, factor); @@ -55,10 +55,10 @@ DeratingFactors::setFactor(PathClkOrData clk_data, void DeratingFactors::factor(PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const { factors_[int(clk_data)].value(rf, early_late, factor, exists); } @@ -72,8 +72,8 @@ DeratingFactors::clear() void DeratingFactors::isOneValue(const EarlyLate *early_late, - bool &is_one_value, - float &value) const + bool &is_one_value, + float &value) const { bool is_one_value0, is_one_value1; float value0, value1; @@ -87,9 +87,9 @@ DeratingFactors::isOneValue(const EarlyLate *early_late, void DeratingFactors::isOneValue(PathClkOrData clk_data, - const EarlyLate *early_late, - bool &is_one_value, - float &value) const + const EarlyLate *early_late, + bool &is_one_value, + float &value) const { is_one_value = factors_[int(clk_data)].isOneValue(early_late, value); } @@ -110,32 +110,32 @@ DeratingFactorsGlobal::DeratingFactorsGlobal() void DeratingFactorsGlobal::setFactor(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor) { factors_[index(type)].setFactor(clk_data, rf, early_late, factor); } void DeratingFactorsGlobal::factor(TimingDerateType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const { factors_[index(type)].factor(clk_data, rf, early_late, factor, exists); } void DeratingFactorsGlobal::factor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const { factors_[index(type)].factor(clk_data, rf, early_late, factor, exists); } @@ -162,21 +162,21 @@ DeratingFactorsCell::DeratingFactorsCell() void DeratingFactorsCell::setFactor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float factor) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float factor) { factors_[index(type)].setFactor(clk_data, rf, early_late, factor); } void DeratingFactorsCell::factor(TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late, - float &factor, - bool &exists) const + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late, + float &factor, + bool &exists) const { factors_[index(type)].factor(clk_data, rf, early_late, factor, exists); } @@ -196,8 +196,8 @@ DeratingFactorsCell::factors(TimingDerateCellType type) void DeratingFactorsCell::isOneValue(const EarlyLate *early_late, - bool &is_one_value, - float &value) const + bool &is_one_value, + float &value) const { bool is_one_value1, is_one_value2; float value1, value2; diff --git a/sdc/DisabledPorts.cc b/sdc/DisabledPorts.cc index 20f2cc36e..99c99350f 100644 --- a/sdc/DisabledPorts.cc +++ b/sdc/DisabledPorts.cc @@ -113,14 +113,14 @@ DisabledPorts::removeDisabledFromTo(LibertyPort *from, bool DisabledPorts::isDisabled(LibertyPort *from, LibertyPort *to, - const TimingRole *role) + const TimingRole *role) { LibertyPortPair from_to(from, to); // set_disable_timing instance does not disable timing checks. return (all_ && !role->isTimingCheck()) - || (from_ && from_->hasKey(from)) - || (to_ && to_->hasKey(to)) - || (from_to_ && from_to_->hasKey(from_to)); + || (from_ && from_->contains(from)) + || (to_ && to_->contains(to)) + || (from_to_ && from_to_->contains(from_to)); } //////////////////////////////////////////////////////////////// @@ -157,7 +157,7 @@ bool DisabledCellPorts::isDisabled(TimingArcSet *arc_set) const { return arc_sets_ - && arc_sets_->hasKey(arc_set); + && arc_sets_->contains(arc_set); } class DisabledCellPortsLess @@ -165,7 +165,7 @@ class DisabledCellPortsLess public: DisabledCellPortsLess(); bool operator()(const DisabledCellPorts *disable1, - const DisabledCellPorts *disable2); + const DisabledCellPorts *disable2); }; DisabledCellPortsLess::DisabledCellPortsLess() @@ -174,14 +174,14 @@ DisabledCellPortsLess::DisabledCellPortsLess() bool DisabledCellPortsLess::operator()(const DisabledCellPorts *disable1, - const DisabledCellPorts *disable2) + const DisabledCellPorts *disable2) { return stringLess(disable1->cell()->name(), - disable2->cell()->name()); + disable2->cell()->name()); } DisabledCellPortsSeq -sortByName(DisabledCellPortsMap *cell_map) +sortByName(const DisabledCellPortsMap *cell_map) { DisabledCellPortsSeq disables; for (auto cell_disable : *cell_map) { @@ -203,9 +203,9 @@ DisabledInstancePorts::DisabledInstancePorts(Instance *inst) : class DisabledInstPortsLess { public: - explicit DisabledInstPortsLess(const Network *network); + DisabledInstPortsLess(const Network *network); bool operator()(const DisabledInstancePorts *disable1, - const DisabledInstancePorts *disable2); + const DisabledInstancePorts *disable2); private: const Network *network_; @@ -221,7 +221,7 @@ DisabledInstPortsLess::operator()(const DisabledInstancePorts *disable1, const DisabledInstancePorts *disable2) { return stringLess(network_->pathName(disable1->instance()), - network_->pathName(disable2->instance())); + network_->pathName(disable2->instance())); } DisabledInstancePortsSeq @@ -243,12 +243,12 @@ class LibertyPortPairNameLess { public: bool operator()(const LibertyPortPair &pair1, - const LibertyPortPair &pair2); + const LibertyPortPair &pair2); }; bool LibertyPortPairNameLess::operator()(const LibertyPortPair &pair1, - const LibertyPortPair &pair2) + const LibertyPortPair &pair2) { const char *from1 = pair1.first->name(); const char *from2 = pair2.first->name(); diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 163dcf653..70d131c98 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -26,6 +26,7 @@ #include +#include "ContainerHelpers.hh" #include "MinMax.hh" #include "TimingRole.hh" #include "Units.hh" @@ -41,20 +42,20 @@ using std::string; static bool thrusIntersectPts(ExceptionThruSeq *thrus1, - ExceptionThruSeq *thrus2, + ExceptionThruSeq *thrus2, const Network *network); static void insertPinPairsThruHierPin(const Pin *hpin, - const Network *network, - PinPairSet *pairs); + const Network *network, + PinPairSet *pairs); static void insertPinPairsThruNet(const Net *net, - const Network *network, - PinPairSet *pairs); + const Network *network, + PinPairSet *pairs); static void deletePinPairsThruHierPin(const Pin *hpin, - const Network *network, - PinPairSet *pairs); + const Network *network, + PinPairSet *pairs); //////////////////////////////////////////////////////////////// @@ -66,20 +67,20 @@ EmptyExpceptionPt::what() const noexcept void checkFromThrusTo(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) + ExceptionThruSeq *thrus, + ExceptionTo *to) { bool found_empty = ((from && !from->hasObjects()) - || (to - && (!to->hasObjects() - && to->transition() - == RiseFallBoth::riseFall() - && (to->endTransition() - == RiseFallBoth::riseFall())))); + || (to + && (!to->hasObjects() + && to->transition() + == RiseFallBoth::riseFall() + && (to->endTransition() + == RiseFallBoth::riseFall())))); if (thrus) { for (ExceptionThru *thru : *thrus) { if (!thru->hasObjects()) - found_empty = true; + found_empty = true; } } if (found_empty) @@ -87,12 +88,12 @@ checkFromThrusTo(ExceptionFrom *from, } ExceptionPath::ExceptionPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - int priority, - const char *comment) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + const char *comment) : SdcCmdComment(comment), from_(from), thrus_(thrus), @@ -111,7 +112,7 @@ ExceptionPath::~ExceptionPath() delete from_; delete to_; if (thrus_) { - thrus_->deleteContents(); + deleteContents(*thrus_); delete thrus_; } } @@ -156,7 +157,7 @@ ExceptionPath::firstPt() bool ExceptionPath::matchesFirstPt(const RiseFall *to_rf, - const MinMax *min_max) + const MinMax *min_max) { ExceptionPt *first_pt = firstPt(); return first_pt->transition()->matches(to_rf) @@ -165,7 +166,7 @@ ExceptionPath::matchesFirstPt(const RiseFall *to_rf, bool ExceptionPath::matches(const MinMax *min_max, - bool) const + bool) const { return min_max_->matches(min_max); } @@ -190,8 +191,8 @@ ExceptionPath::setPriority(int priority) // priority over an exception without this type of qualifier. int ExceptionPath::fromThruToPriority(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) + ExceptionThruSeq *thrus, + ExceptionTo *to) { int priority = 0; if (from && (from->hasPins() || from->hasInstances())) @@ -244,44 +245,47 @@ ExceptionPath::mergeablePts(ExceptionPath *exception) const bool ExceptionPath::mergeablePts(ExceptionPath *exception2, - ExceptionPt *missing_pt2, - ExceptionPt *&missing_pt) const + ExceptionPt *missing_pt2, + ExceptionPt *&missing_pt) const { missing_pt = nullptr; ExceptionFrom *from2 = exception2->from(); if ((from_ && from2 && !(from_->transition() == from2->transition() - && (from2 == missing_pt2 - || from_->equal(from2)))) + && (from2 == missing_pt2 + || from_->equal(from2)))) || (from_ && from2 == nullptr) || (from_ == nullptr && from2)) return false; if (from2 == missing_pt2) missing_pt = from_; - ExceptionThruSeq::Iterator thru_iter(thrus_); - ExceptionThruSeq::Iterator thru_iter2(exception2->thrus()); - while (thru_iter.hasNext() - && thru_iter2.hasNext()) { - ExceptionThru *thru = thru_iter.next(); - ExceptionThru *thru2 = thru_iter2.next(); - if (!(thru->transition() == thru2->transition() - && (thru2 == missing_pt2 - || thru->equal(thru)))) + ExceptionThruSeq *thrus2 = exception2->thrus(); + if (thrus_ && thrus2) { + ExceptionThruSeq::iterator thru_iter1 = thrus_->begin(); + ExceptionThruSeq::iterator thru_iter2 = thrus2->begin(); + while (thru_iter1 != thrus_->end() + && thru_iter2 != thrus2->end()) { + ExceptionThru *thru1 = *thru_iter1++; + ExceptionThru *thru2 = *thru_iter2++; + if (!(thru1->transition() == thru2->transition() + && (thru2 == missing_pt2 + || thru1->equal(thru2)))) + return false; + if (thru2 == missing_pt2) + missing_pt = thru1; + } + if (thru_iter1 != thrus_->end() + || thru_iter2 != thrus2->end()) return false; - if (thru2 == missing_pt2) - missing_pt = thru; } - if (thru_iter.hasNext() - || thru_iter2.hasNext()) - return false; ExceptionTo *to2 = exception2->to(); if ((to_ && to2 && !(to_->transition() == to2->transition() - && to_->endTransition() == to2->endTransition() - && (to2 == missing_pt2 - || to_->equal(to2)))) + && to_->endTransition() == to2->endTransition() + && (to2 == missing_pt2 + || to_->equal(to2)))) || (to_ && to2 == nullptr) || (to_ == nullptr && to2)) return false; @@ -300,16 +304,19 @@ ExceptionPath::intersectsPts(ExceptionPath *exception, if (((from_ == nullptr && from2 == nullptr) || (from_ && from2 && from_->intersectsPts(from2, network))) && ((thrus_ == nullptr && thrus2 == nullptr) - || (thrus_ && thrus2 && thrus_->size() == thrus2->size())) + || (thrus_ && thrus2 && thrus_->size() == thrus2->size())) && ((to_ == nullptr && to2 == nullptr) - || (to_ && to2 && to_->intersectsPts(to2, network)))) { - ExceptionThruSeq::Iterator thrus_iter1(thrus_); - ExceptionThruSeq::Iterator thrus_iter2(thrus2); - while (thrus_iter1.hasNext() && thrus_iter2.hasNext()) { - ExceptionThru *thru1 = thrus_iter1.next(); - ExceptionThru *thru2 = thrus_iter2.next(); - if (!thru1->intersectsPts(thru2, network)) - return false; + || (to_ && to2 && to_->intersectsPts(to2, network)))) { + if (thrus_ && thrus2) { + ExceptionThruSeq::iterator thru_iter1 = thrus_->begin(); + ExceptionThruSeq::iterator thru_iter2 = thrus2->begin(); + while (thru_iter1 != thrus_->end() + && thru_iter2 != thrus2->end()) { + ExceptionThru *thru1 = *thru_iter1++; + ExceptionThru *thru2 = *thru_iter2++; + if (!thru1->intersectsPts(thru2, network)) + return false; + } } return true; } @@ -390,9 +397,9 @@ ExceptionPath::makeStates() bool ExceptionPath::resetMatch(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, const Network *network) { // Only the reset expception points need to match. @@ -400,58 +407,59 @@ ExceptionPath::resetMatch(ExceptionFrom *from, // exceptions that match the -from even if they are more specific. // -from return ((from && from_ - && thrus == nullptr - && to == nullptr - && from_->intersectsPts(from, network)) - // -thru - || (from == nullptr - && thrus && thrus_ - && to == nullptr - && thrusIntersectPts(thrus_, thrus, network)) - // -to - || (from == nullptr - && thrus == nullptr - && to && to_ - && to_->intersectsPts(to, network)) - // -from -thru - || (from && from_ - && thrus && thrus_ - && to == nullptr - && from_->intersectsPts(from, network) - && thrusIntersectPts(thrus_, thrus, network)) - // -from -to - || (from && from_ - && thrus == nullptr - && to && to_ - && from_->intersectsPts(from, network) - && to_->intersectsPts(to, network)) - // -thru -to - || (from == nullptr - && thrus && thrus_ - && to && to_ - && thrusIntersectPts(thrus_, thrus, network) - && to_->intersectsPts(to, network)) - // -from -thru -to - || (from && from_ - && thrus && thrus_ - && to && to_ - && from_->intersectsPts(from, network) - && thrusIntersectPts(thrus_, thrus, network) - && to_->intersectsPts(to, network))) + && thrus == nullptr + && to == nullptr + && from_->intersectsPts(from, network)) + // -thru + || (from == nullptr + && thrus && thrus_ + && to == nullptr + && thrusIntersectPts(thrus_, thrus, network)) + // -to + || (from == nullptr + && thrus == nullptr + && to && to_ + && to_->intersectsPts(to, network)) + // -from -thru + || (from && from_ + && thrus && thrus_ + && to == nullptr + && from_->intersectsPts(from, network) + && thrusIntersectPts(thrus_, thrus, network)) + // -from -to + || (from && from_ + && thrus == nullptr + && to && to_ + && from_->intersectsPts(from, network) + && to_->intersectsPts(to, network)) + // -thru -to + || (from == nullptr + && thrus && thrus_ + && to && to_ + && thrusIntersectPts(thrus_, thrus, network) + && to_->intersectsPts(to, network)) + // -from -thru -to + || (from && from_ + && thrus && thrus_ + && to && to_ + && from_->intersectsPts(from, network) + && thrusIntersectPts(thrus_, thrus, network) + && to_->intersectsPts(to, network))) && (min_max == MinMaxAll::all() - || min_max_ == min_max); + || min_max_ == min_max); } static bool thrusIntersectPts(ExceptionThruSeq *thrus1, - ExceptionThruSeq *thrus2, + ExceptionThruSeq *thrus2, const Network *network) { - ExceptionThruSeq::Iterator thrus_iter1(thrus1); - ExceptionThruSeq::Iterator thrus_iter2(thrus2); - while (thrus_iter1.hasNext() && thrus_iter2.hasNext()) { - ExceptionThru *thru1 = thrus_iter1.next(); - ExceptionThru *thru2 = thrus_iter2.next(); + ExceptionThruSeq::iterator thru_iter1 = thrus1->begin(); + ExceptionThruSeq::iterator thru_iter2 = thrus2->begin(); + while (thru_iter1 != thrus1->end() + && thru_iter2 != thrus2->end()) { + ExceptionThru *thru1 = *thru_iter1++; + ExceptionThru *thru2 = *thru_iter2++; if (!thru1->intersectsPts(thru2, network)) return false; } @@ -475,17 +483,17 @@ ExceptionPath::deleteInstance(const Instance *inst, //////////////////////////////////////////////////////////////// PathDelay::PathDelay(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, - bool own_pts, - const char *comment) : + float delay, + bool own_pts, + const char *comment) : ExceptionPath(from, thrus, to, min_max->asMinMaxAll(), own_pts, - pathDelayPriority() + fromThruToPriority(from, thrus, to), - comment), + pathDelayPriority() + fromThruToPriority(from, thrus, to), + comment), ignore_clk_latency_(ignore_clk_latency), break_path_(break_path), delay_(delay) @@ -494,12 +502,12 @@ PathDelay::PathDelay(ExceptionFrom *from, ExceptionPath * PathDelay::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new PathDelay(from, thrus, to, min_max_->asMinMax(), - ignore_clk_latency_, break_path_, delay_, + ignore_clk_latency_, break_path_, delay_, own_pts, comment_); } @@ -523,8 +531,8 @@ PathDelay::asString(const Network *network) const { const char *from_thru_to = fromThruToString(network); const char *result = stringPrintTmp("PathDelay %.3fns%s", - delay_ * 1E+9F, - from_thru_to); + delay_ * 1E+9F, + from_thru_to); return result; } @@ -565,33 +573,33 @@ PathDelay::overrides(ExceptionPath *exception) const //////////////////////////////////////////////////////////////// FalsePath::FalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - const char *comment) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + const char *comment) : ExceptionPath(from, thrus, to, min_max, own_pts, - falsePathPriority() + fromThruToPriority(from, thrus, to), - comment) + falsePathPriority() + fromThruToPriority(from, thrus, to), + comment) { } FalsePath::FalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool own_pts, - int priority, - const char *comment) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + const char *comment) : ExceptionPath(from, thrus, to, min_max, own_pts, priority, comment) { } ExceptionPath * FalsePath::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new FalsePath(from, thrus, to, min_max_, own_pts, comment_); } @@ -631,10 +639,10 @@ FalsePath::overrides(ExceptionPath *exception) const //////////////////////////////////////////////////////////////// LoopPath::LoopPath(ExceptionThruSeq *thrus, - bool own_pts) : + bool own_pts) : FalsePath(nullptr, thrus, nullptr, MinMaxAll::all(), own_pts, - falsePathPriority() + fromThruToPriority(nullptr, thrus, nullptr), - nullptr) + falsePathPriority() + fromThruToPriority(nullptr, thrus, nullptr), + nullptr) { } @@ -653,16 +661,16 @@ LoopPath::mergeable(ExceptionPath *) const //////////////////////////////////////////////////////////////// MultiCyclePath::MultiCyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - bool own_pts, - const char *comment) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + bool own_pts, + const char *comment) : ExceptionPath(from, thrus, to, min_max, own_pts, - multiCyclePathPriority() + fromThruToPriority(from, thrus, to), - comment), + multiCyclePathPriority() + fromThruToPriority(from, thrus, to), + comment), use_end_clk_(use_end_clk), path_multiplier_(path_multiplier) { @@ -670,12 +678,12 @@ MultiCyclePath::MultiCyclePath(ExceptionFrom *from, ExceptionPath * MultiCyclePath::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new MultiCyclePath(from, thrus, to, min_max_, use_end_clk_, - path_multiplier_, own_pts, comment_); + path_multiplier_, own_pts, comment_); } int @@ -714,7 +722,7 @@ MultiCyclePath::tighterThan(ExceptionPath *exception) const bool MultiCyclePath::matches(const MinMax *min_max, - bool exactly) const + bool exactly) const { return min_max_->matches(min_max) // set_multicycle_path -setup determines hold check accounting, @@ -727,9 +735,9 @@ MultiCyclePath::asString(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); + (use_end_clk_) ? "-end" : "-start", + path_multiplier_, + from_thru_to); return result; } @@ -758,12 +766,12 @@ MultiCyclePath::overrides(ExceptionPath *exception) const //////////////////////////////////////////////////////////////// FilterPath::FilterPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) : + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) : ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts, - filterPathPriority() + fromThruToPriority(from, thrus, to), - nullptr) + filterPathPriority() + fromThruToPriority(from, thrus, to), + nullptr) { } @@ -775,9 +783,9 @@ FilterPath::typeString() const ExceptionPath * FilterPath::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new FilterPath(from, thrus, to, own_pts); } @@ -810,9 +818,9 @@ FilterPath::overrides(ExceptionPath *) const bool FilterPath::resetMatch(ExceptionFrom *, - ExceptionThruSeq *, - ExceptionTo *, - const MinMaxAll *, + ExceptionThruSeq *, + ExceptionTo *, + const MinMaxAll *, const Network *) { return false; @@ -821,15 +829,15 @@ FilterPath::resetMatch(ExceptionFrom *, //////////////////////////////////////////////////////////////// GroupPath::GroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts, - const char *comment) : + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts, + const char *comment) : ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts, - groupPathPriority() + fromThruToPriority(from, thrus, to), - comment), + groupPathPriority() + fromThruToPriority(from, thrus, to), + comment), name_(stringCopy(name)), is_default_(is_default) { @@ -848,12 +856,12 @@ GroupPath::typeString() const ExceptionPath * GroupPath::clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) { return new GroupPath(name_, is_default_, from, thrus, to, own_pts, - comment_); + comment_); } int @@ -889,7 +897,7 @@ GroupPath::overrides(ExceptionPath *exception) const const int ExceptionPt::as_string_max_objects_ = 20; ExceptionPt::ExceptionPt(const RiseFallBoth *rf, - bool own_pts) : + bool own_pts) : rf_(rf), own_pts_(own_pts), hash_(0) @@ -905,10 +913,10 @@ ExceptionPt::hash() const } ExceptionFromTo::ExceptionFromTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, const Network *network) : ExceptionPt(rf, own_pts), pins_(pins), @@ -979,8 +987,8 @@ ExceptionFromTo::allPins(const Network *network) for (const Instance *inst : *insts_) { InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - pins.insert(pin); + const Pin *pin = pin_iter->next(); + pins.insert(pin); } delete pin_iter; } @@ -1015,30 +1023,33 @@ ExceptionFromTo::findHash(const Network *network) bool ExceptionFromTo::equal(ExceptionFromTo *from_to) const { - return PinSet::equal(from_to->pins_, pins_) - && ClockSet::equal(from_to->clks_, clks_) - && InstanceSet::equal(from_to->insts_, insts_) + return ((from_to->pins_ == nullptr && pins_ == nullptr) + || (from_to->pins_ && pins_ && *from_to->pins_ == *pins_)) + && ((from_to->clks_ == nullptr && clks_ == nullptr) + || (from_to->clks_ && clks_ && *from_to->clks_ == *clks_)) + && ((from_to->insts_ == nullptr && insts_ == nullptr) + || (from_to->insts_ && insts_ && *from_to->insts_ == *insts_)) && from_to->transition() == rf_; } int ExceptionFromTo::compare(ExceptionPt *pt2, - const Network *network) const + const Network *network) const { int priority_cmp = typePriority() - pt2->typePriority(); if (priority_cmp == 0) { - int pin_cmp = PinSet::compare(pins_, pt2->pins(), network); + int pin_cmp = sta::compare(pins_, pt2->pins(), network); if (pin_cmp == 0) { int clk_cmp = sta::compare(clks_, pt2->clks()); if (clk_cmp == 0) { - int inst_cmp = InstanceSet::compare(insts_, pt2->instances(), network); - if (inst_cmp == 0) - return rf_->index() - pt2->transition()->index(); - else - return inst_cmp; + int inst_cmp = sta::compare(insts_, pt2->instances(), network); + if (inst_cmp == 0) + return rf_->index() - pt2->transition()->index(); + else + return inst_cmp; } else - return clk_cmp; + return clk_cmp; } else return pin_cmp; @@ -1092,7 +1103,7 @@ ExceptionFromTo::addPin(const Pin *pin, { if (pins_ == nullptr) pins_ = new PinSet(network); - if (!pins_->hasKey(pin)) { + if (!pins_->contains(pin)) { pins_->insert(pin); // Incrementally update hash. hash_ += network->id(pin) * hash_pin; @@ -1104,7 +1115,7 @@ ExceptionFromTo::addClock(Clock *clk) { if (clks_ == nullptr) clks_ = new ClockSet; - if (!clks_->hasKey(clk)) { + if (!clks_->contains(clk)) { clks_->insert(clk); // Incrementally update hash. hash_ += clk->index() * hash_clk; @@ -1117,7 +1128,7 @@ ExceptionFromTo::addInstance(const Instance *inst, { if (insts_ == nullptr) insts_ = new InstanceSet(network); - if (!insts_->hasKey(inst)) { + if (!insts_->contains(inst)) { insts_->insert(inst); // Incrementally update hash. hash_ += network->id(inst) * hash_inst; @@ -1186,7 +1197,7 @@ ExceptionFromTo::asString(const Network *network) const PinSeq pins = sortByPathName(pins_, network); for (const Pin *pin : pins) { if (!first) - str += ", "; + str += ", "; str += network->pathName(pin); first = false; obj_count++; @@ -1198,7 +1209,7 @@ ExceptionFromTo::asString(const Network *network) const ClockSeq clks = sortByName(clks_); for (Clock *clk : clks) { if (!first) - str += ", "; + str += ", "; str += clk->name(); first = false; obj_count++; @@ -1210,7 +1221,7 @@ ExceptionFromTo::asString(const Network *network) const InstanceSeq insts = sortByPathName(insts_, network); for (const Instance *inst : insts) { if (!first) - str += ", "; + str += ", "; str += network->pathName(inst); first = false; obj_count++; @@ -1244,10 +1255,10 @@ ExceptionFromTo::objectCount() const //////////////////////////////////////////////////////////////// ExceptionFrom::ExceptionFrom(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, const Network *network) : ExceptionFromTo(pins, clks, insts, rf, own_pts, network) { @@ -1275,14 +1286,35 @@ ExceptionFrom::clone(const Network *network) return new ExceptionFrom(pins, clks, insts, rf_, true, network); } +bool +clkSetIntersects(ClockSet *set1, + ClockSet *set2) +{ + if (set1 && set2) { + auto iter1 = set1->begin(); + auto end1 = set1->end(); + auto iter2 = set2->begin(); + auto end2 = set2->end(); + while (iter1 != end1 && iter2 != end2) { + if (ClockIndexLess()(*iter1, *iter2)) + iter1++; + else if (ClockIndexLess()(*iter2, *iter1)) + iter2++; + else + return true; + } + } + return false; +} + bool ExceptionFrom::intersectsPts(ExceptionFrom *from, const Network *network) const { return from->transition() == rf_ - && ((pins_ && PinSet::intersects(pins_, from->pins(), network)) - || (clks_ && ClockSet::intersects(clks_, from->clks(), ClockIndexLess())) - || (insts_ && InstanceSet::intersects(insts_, from->instances(), network))); + && ((pins_ && intersects(pins_, from->pins(), network)) + || (clks_ && clkSetIntersects(clks_, from->clks())) + || (insts_ && intersects(insts_, from->instances(), network))); } const char * @@ -1299,11 +1331,11 @@ ExceptionFrom::cmdKeyword() const //////////////////////////////////////////////////////////////// ExceptionTo::ExceptionTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - const RiseFallBoth *end_rf, - bool own_pts, + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + const RiseFallBoth *end_rf, + bool own_pts, const Network *network) : ExceptionFromTo(pins, clks, insts, rf, own_pts, network), end_rf_(end_rf) @@ -1346,16 +1378,16 @@ ExceptionTo::intersectsPts(ExceptionTo *to, { return to->transition() == rf_ && to->endTransition() == end_rf_ - && ((pins_ && PinSet::intersects(pins_, to->pins(), network)) - || (clks_ && ClockSet::intersects(clks_, to->clks(), ClockIndexLess())) - || (insts_ && InstanceSet::intersects(insts_, to->instances(), network))); + && ((pins_ && intersects(pins_, to->pins(), network)) + || (clks_ && clkSetIntersects(clks_, to->clks())) + || (insts_ && intersects(insts_, to->instances(), network))); } bool ExceptionTo::matchesFilter(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - const Network *network) const + const ClockEdge *clk_edge, + const RiseFall *end_rf, + const Network *network) const { // "report -to reg" matches clock pins. return matches(pin, clk_edge, end_rf, true, network); @@ -1363,9 +1395,9 @@ ExceptionTo::matchesFilter(const Pin *pin, bool ExceptionTo::matches(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - const Network *network) const + const ClockEdge *clk_edge, + const RiseFall *end_rf, + const Network *network) const { // "exception -to reg" does not match reg clock pins. return matches(pin, clk_edge, end_rf, false, network); @@ -1373,71 +1405,71 @@ ExceptionTo::matches(const Pin *pin, bool ExceptionTo::matches(const Pin *pin, - const ClockEdge *clk_edge, - const RiseFall *end_rf, - bool inst_matches_reg_clk_pin, - const Network *network) const + const ClockEdge *clk_edge, + const RiseFall *end_rf, + bool inst_matches_reg_clk_pin, + const Network *network) const { return (pins_ - && pins_->hasKey(const_cast(pin)) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)) + && pins_->contains(const_cast(pin)) + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)) || (clk_edge - && clks_ - && clks_->hasKey(const_cast(clk_edge->clock())) - && rf_->matches(clk_edge->transition()) - && end_rf_->matches(end_rf)) + && clks_ + && clks_->contains(clk_edge->clock()) + && rf_->matches(clk_edge->transition()) + && end_rf_->matches(end_rf)) || (insts_ - && (inst_matches_reg_clk_pin - || !network->isRegClkPin(pin)) - && insts_->hasKey(network->instance(pin)) - && (network->direction(pin)->isAnyInput() + && (inst_matches_reg_clk_pin + || !network->isRegClkPin(pin)) + && insts_->contains(network->instance(pin)) + && (network->direction(pin)->isAnyInput() || network->direction(pin)->isInternal()) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)) + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)) || (pins_ == nullptr - && clks_ == nullptr - && insts_ == nullptr - && end_rf_->matches(end_rf)); + && clks_ == nullptr + && insts_ == nullptr + && end_rf_->matches(end_rf)); } bool ExceptionTo::matches(const Pin *pin, - const RiseFall *end_rf, - const Network *network) const + const RiseFall *end_rf, + const Network *network) const { return (pins_ - && pins_->hasKey(const_cast(pin)) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)) + && pins_->contains(const_cast(pin)) + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)) || (insts_ - && insts_->hasKey(network->instance(pin)) - && (network->direction(pin)->isAnyInput() + && insts_->contains(network->instance(pin)) + && (network->direction(pin)->isAnyInput() || network->direction(pin)->isInternal()) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)); + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)); } bool ExceptionTo::matches(const Pin *pin, - const RiseFall *end_rf) const + const RiseFall *end_rf) const { return (pins_ - && pins_->hasKey(const_cast(pin)) - && rf_->matches(end_rf) - && end_rf_->matches(end_rf)) + && pins_->contains(const_cast(pin)) + && rf_->matches(end_rf) + && end_rf_->matches(end_rf)) || (pins_ == nullptr - && clks_ == nullptr - && insts_ == nullptr - && end_rf_->matches(end_rf)); + && clks_ == nullptr + && insts_ == nullptr + && end_rf_->matches(end_rf)); } bool ExceptionTo::matches(const Clock *clk) const { return clks_ - && clks_->hasKey(const_cast(clk)); + && clks_->contains(const_cast(clk)); } const char * @@ -1453,7 +1485,7 @@ ExceptionTo::cmdKeyword() const int ExceptionTo::compare(ExceptionPt *pt2, - const Network *network) const + const Network *network) const { ExceptionTo *to2 = dynamic_cast(pt2); int cmp = ExceptionFromTo::compare(pt2, network); @@ -1466,11 +1498,11 @@ ExceptionTo::compare(ExceptionPt *pt2, //////////////////////////////////////////////////////////////// ExceptionThru::ExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf, - bool own_pts, - const Network *network) : + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf, + bool own_pts, + const Network *network) : ExceptionPt(rf, own_pts), pins_(pins), edges_(nullptr), @@ -1521,7 +1553,7 @@ ExceptionThru::makePinEdges(const Network *network) // but before the pin has been deleted from the netlist. void ExceptionThru::deletePinEdges(const Pin *pin, - Network *network) + Network *network) { // Incrementally delete only edges through (hier) or from/to (leaf) the pin. if (edges_ && network->net(pin)) { @@ -1530,12 +1562,12 @@ ExceptionThru::deletePinEdges(const Pin *pin, // deletePinPairsThruHierPin. PinSet *drvrs = network->drivers(pin); if (drvrs) { - // Some edges originating at drvrs may not actually go through pin, so - // still must use deletePinPairsThruHierPin to identify specific edges. - if (edges_) { - for (const EdgePins &edge_pins : *edges_) { + // Some edges originating at drvrs may not actually go through pin, so + // still must use deletePinPairsThruHierPin to identify specific edges. + if (edges_) { + for (const EdgePins &edge_pins : *edges_) { const Pin *p_first = edge_pins.first; - if (drvrs->hasKey(p_first)) { + if (drvrs->contains(p_first)) { deletePinPairsThruHierPin(pin, network, edges_); break; } @@ -1545,13 +1577,13 @@ ExceptionThru::deletePinEdges(const Pin *pin, } else { // erase prevents range iteration. - EdgePinsSet::Iterator edge_iter(edges_); - while (edge_iter.hasNext()) { - const EdgePins &edge_pins = edge_iter.next(); + for (auto itr = edges_->begin(); itr != edges_->end(); /* no incr */) { + const EdgePins &edge_pins = *itr; if (edge_pins.first == pin - || edge_pins.second == pin) { - edges_->erase(edge_pins); - } + || edge_pins.second == pin) + itr = edges_->erase(itr); + else + itr++; } } } @@ -1559,7 +1591,7 @@ ExceptionThru::deletePinEdges(const Pin *pin, void ExceptionThru::makeHpinEdges(const Pin *pin, - const Network *network) + const Network *network) { if (edges_ == nullptr) edges_ = new EdgePinsSet(network); @@ -1580,7 +1612,7 @@ ExceptionThru::makeNetEdges(const Network *network) void ExceptionThru::makeNetEdges(const Net *net, - const Network *network) + const Network *network) { if (edges_ == nullptr) edges_ = new EdgePinsSet(network); @@ -1595,8 +1627,8 @@ ExceptionThru::makeInstEdges(const Network *network) if (network->isHierarchical(inst)) { InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - makeHpinEdges(pin, network); + Pin *pin = pin_iter->next(); + makeHpinEdges(pin, network); } delete pin_iter; } @@ -1605,7 +1637,7 @@ ExceptionThru::makeInstEdges(const Network *network) void ExceptionThru::makeInstEdges(Instance *inst, - Network *network) + Network *network) { if (network->isHierarchical(inst)) { InstancePinIterator *pin_iter = network->pinIterator(inst); @@ -1621,7 +1653,7 @@ ExceptionThru::makeInstEdges(Instance *inst, // but before the inst has been deleted from the netlist. void ExceptionThru::deleteInstEdges(Instance *inst, - Network *network) + Network *network) { // Incrementally delete edges through each hier pin. if (edges_) { @@ -1654,7 +1686,7 @@ ExceptionThru::asString(const Network *network) const PinSeq pins = sortByPathName(pins_, network); for (const Pin *pin : pins) { if (!first) - str += ", "; + str += ", "; str += network->pathName(pin); first = false; obj_count++; @@ -1666,7 +1698,7 @@ ExceptionThru::asString(const Network *network) const NetSeq nets = sortByPathName(nets_, network); for (const Net *net : nets) { if (!first) - str += ", "; + str += ", "; str += network->pathName(net); first = false; obj_count++; @@ -1678,7 +1710,7 @@ ExceptionThru::asString(const Network *network) const InstanceSeq insts = sortByPathName(insts_, network); for (const Instance *inst : insts) { if (!first) - str += ", "; + str += ", "; str += network->pathName(inst); first = false; obj_count++; @@ -1700,7 +1732,7 @@ ExceptionThru::asString(const Network *network) const ExceptionThruSeq * exceptionThrusClone(ExceptionThruSeq *thrus, - const Network *network) + const Network *network) { if (thrus) { ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; @@ -1744,7 +1776,7 @@ ExceptionThru::addPin(const Pin *pin, { if (pins_ == nullptr) pins_ = new PinSet(network); - if (!pins_->hasKey(pin)) { + if (!pins_->contains(pin)) { pins_->insert(pin); // Incrementally update hash. hash_ += network->id(pin) * hash_pin; @@ -1757,7 +1789,7 @@ ExceptionThru::addNet(const Net *net, { if (nets_ == nullptr) nets_ = new NetSet(network); - if (!nets_->hasKey(net)) { + if (!nets_->contains(net)) { nets_->insert(net); // Incrementally update hash. hash_ += network->id(net) * hash_net; @@ -1770,7 +1802,7 @@ ExceptionThru::addInstance(const Instance *inst, { if (insts_ == nullptr) insts_ = new InstanceSet(network); - if (!insts_->hasKey(inst)) { + if (!insts_->contains(inst)) { insts_->insert(inst); // Incrementally update hash. hash_ += network->id(inst) * hash_inst; @@ -1849,8 +1881,8 @@ ExceptionThru::allPins(const Network *network) for (const Instance *inst : *insts_) { InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - pins.insert(pin); + Pin *pin = pin_iter->next(); + pins.insert(pin); } delete pin_iter; } @@ -1859,8 +1891,8 @@ ExceptionThru::allPins(const Network *network) for (const Net *net : *nets_) { NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - pins.insert(pin); + const Pin *pin = pin_iter->next(); + pins.insert(pin); } delete pin_iter; } @@ -1870,15 +1902,15 @@ ExceptionThru::allPins(const Network *network) bool ExceptionThru::matches(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const Network *network) + const Pin *to_pin, + const RiseFall *to_rf, + const Network *network) { EdgePins edge_pins(from_pin, to_pin); - return ((pins_ && to_pin && pins_->hasKey(to_pin)) - || (edges_ && from_pin && to_pin && edges_->hasKey(edge_pins)) - || (nets_ && to_pin && nets_->hasKey(network->net(to_pin))) - || (insts_ && to_pin && insts_->hasKey(network->instance(to_pin)))) + return ((pins_ && to_pin && pins_->contains(to_pin)) + || (edges_ && from_pin && to_pin && edges_->contains(edge_pins)) + || (nets_ && to_pin && nets_->contains(network->net(to_pin))) + || (insts_ && to_pin && insts_->contains(network->instance(to_pin)))) && rf_->matches(to_rf); } @@ -1911,30 +1943,33 @@ bool ExceptionThru::equal(ExceptionThru *thru) const { // Edges_ are derived from pins_ so matching pins is sufficient. - return PinSet::equal(thru->pins_, pins_) - && NetSet::equal(thru->nets_, nets_) - && InstanceSet::equal(thru->insts_, insts_) + return ((thru->pins_ == nullptr && pins_ == nullptr) + || (thru->pins_ && pins_ && *thru->pins_ == *pins_)) + && ((thru->nets_ == nullptr && nets_ == nullptr) + || (thru->nets_ && nets_ && *thru->nets_ == *nets_)) + && ((thru->insts_ == nullptr && insts_ == nullptr) + || (thru->insts_ && insts_ && *thru->insts_ == *insts_)) && rf_ == thru->rf_; } int ExceptionThru::compare(ExceptionPt *pt2, - const Network *network) const + const Network *network) const { int priority_cmp = typePriority() - pt2->typePriority(); if (priority_cmp == 0) { - int pin_cmp = PinSet::compare(pins_, pt2->pins(), network); + int pin_cmp = sta::compare(pins_, pt2->pins(), network); if (pin_cmp == 0) { - int net_cmp = NetSet::compare(nets_, pt2->nets(), network); + int net_cmp = sta::compare(nets_, pt2->nets(), network); if (net_cmp == 0) { - int inst_cmp = InstanceSet::compare(insts_, pt2->instances(), network); - if (inst_cmp == 0) - return rf_->index() - pt2->transition()->index(); - else - return inst_cmp; + int inst_cmp = sta::compare(insts_, pt2->instances(), network); + if (inst_cmp == 0) + return rf_->index() - pt2->transition()->index(); + else + return inst_cmp; } else - return net_cmp; + return net_cmp; } else return pin_cmp; @@ -1998,9 +2033,9 @@ ExceptionThru::intersectsPts(ExceptionThru *thru, const Network *network) const { return thru->transition() == rf_ - && ((pins_ && PinSet::intersects(pins_, thru->pins(), network)) - || (nets_ && NetSet::intersects(nets_, thru->nets(), network)) - || (insts_ && InstanceSet::intersects(insts_, thru->instances(), network))); + && ((pins_ && intersects(pins_, thru->pins(), network)) + || (nets_ && intersects(nets_, thru->nets(), network)) + || (insts_ && intersects(insts_, thru->instances(), network))); } size_t @@ -2018,7 +2053,7 @@ ExceptionThru::objectCount() const void ExceptionThru::connectPinAfter(PinSet *drvrs, - Network *network) + Network *network) { // - Tricky to detect exactly what needs to be updated. In theory, // at most, only edges starting/ending (pin is leaf) or spanning @@ -2042,7 +2077,7 @@ ExceptionThru::connectPinAfter(PinSet *drvrs, for (const Pin *thru_pin : *pins_) { if (network->isHierarchical(thru_pin)) { PinSet *thru_pin_drvrs = network->drivers(thru_pin); - if (PinSet::intersects(drvrs, thru_pin_drvrs, network)) + if (intersects(drvrs, thru_pin_drvrs, network)) makePinEdges(thru_pin, network); } } @@ -2054,7 +2089,7 @@ ExceptionThru::connectPinAfter(PinSet *drvrs, while (inst_pin_iter->hasNext()) { Pin *inst_pin = inst_pin_iter->next(); PinSet *inst_pin_drvrs = network->drivers(inst_pin); - if (PinSet::intersects(drvrs, inst_pin_drvrs, network)) + if (intersects(drvrs, inst_pin_drvrs, network)) makePinEdges(inst_pin, network); } delete inst_pin_iter; @@ -2064,7 +2099,7 @@ ExceptionThru::connectPinAfter(PinSet *drvrs, if (nets_) { for (const Net *net : *nets_) { PinSet *net_drvrs = network->drivers(net); - if (PinSet::intersects(drvrs, net_drvrs, network)) + if (intersects(drvrs, net_drvrs, network)) makeNetEdges(net, network); } } @@ -2073,7 +2108,7 @@ ExceptionThru::connectPinAfter(PinSet *drvrs, void ExceptionThru::makePinEdges(const Pin *pin, - const Network *network) + const Network *network) { if (network->isHierarchical(pin)) makeHpinEdges(pin, network); @@ -2096,14 +2131,14 @@ ExceptionPtIterator::ExceptionPtIterator(const ExceptionPath *exception) : to_done_(false) { if (exception->thrus()) - thru_iter_.init(exception->thrus()); + thru_iter_ = exception->thrus()->begin(); } bool ExceptionPtIterator::hasNext() { return (!from_done_ && exception_->from()) - || thru_iter_.hasNext() + || (exception_->thrus() && thru_iter_ != exception_->thrus()->end()) || (!to_done_ && exception_->to()); } @@ -2115,8 +2150,9 @@ ExceptionPtIterator::next() from_done_ = true; return exception_->from(); } - else if (thru_iter_.hasNext()) - return thru_iter_.next(); + else if (exception_->thrus() + && thru_iter_ != exception_->thrus()->end()) + return *thru_iter_++; else { to_done_ = true; return exception_->to(); @@ -2126,7 +2162,7 @@ ExceptionPtIterator::next() //////////////////////////////////////////////////////////////// ExpandedExceptionVisitor::ExpandedExceptionVisitor(ExceptionPath *exception, - const Network *network) : + const Network *network) : exception_(exception), network_(network) { @@ -2182,8 +2218,8 @@ ExpandedExceptionVisitor::expandThrus(ExceptionFrom *expanded_from) void ExpandedExceptionVisitor::expandThru(ExceptionFrom *expanded_from, - size_t next_thru_idx, - ExceptionThruSeq *expanded_thrus) + size_t next_thru_idx, + ExceptionThruSeq *expanded_thrus) { ExceptionThruSeq *thrus = exception_->thrus(); if (next_thru_idx < thrus->size()) { @@ -2191,32 +2227,32 @@ ExpandedExceptionVisitor::expandThru(ExceptionFrom *expanded_from, const RiseFallBoth *rf = thru->transition(); if (thru->pins()) { for (const Pin *pin : *thru->pins()) { - PinSet pins(network_); - pins.insert(pin); - ExceptionThru expanded_thru(&pins, nullptr, nullptr, rf, false, network_); - expanded_thrus->push_back(&expanded_thru); - expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); - expanded_thrus->pop_back(); + PinSet pins(network_); + pins.insert(pin); + ExceptionThru expanded_thru(&pins, nullptr, nullptr, rf, false, network_); + expanded_thrus->push_back(&expanded_thru); + expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); + expanded_thrus->pop_back(); } } if (thru->nets()) { for (const Net *net : *thru->nets()) { - NetSet nets(network_); - nets.insert(net); - ExceptionThru expanded_thru(nullptr, &nets, nullptr, rf, false, network_); - expanded_thrus->push_back(&expanded_thru); - expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); - expanded_thrus->pop_back(); + NetSet nets(network_); + nets.insert(net); + ExceptionThru expanded_thru(nullptr, &nets, nullptr, rf, false, network_); + expanded_thrus->push_back(&expanded_thru); + expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); + expanded_thrus->pop_back(); } } if (thru->instances()) { for (const Instance *inst : *thru->instances()) { - InstanceSet insts(network_); - insts.insert(inst); - ExceptionThru expanded_thru(nullptr, nullptr, &insts, rf, false, network_); - expanded_thrus->push_back(&expanded_thru); - expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); - expanded_thrus->pop_back(); + InstanceSet insts(network_); + insts.insert(inst); + ExceptionThru expanded_thru(nullptr, nullptr, &insts, rf, false, network_); + expanded_thrus->push_back(&expanded_thru); + expandThru(expanded_from, next_thru_idx + 1, expanded_thrus); + expanded_thrus->pop_back(); } } } @@ -2227,7 +2263,7 @@ ExpandedExceptionVisitor::expandThru(ExceptionFrom *expanded_from, void ExpandedExceptionVisitor::expandTo(ExceptionFrom *expanded_from, - ExceptionThruSeq *expanded_thrus) + ExceptionThruSeq *expanded_thrus) { ExceptionTo *to = exception_->to(); if (to) { @@ -2265,8 +2301,8 @@ ExpandedExceptionVisitor::expandTo(ExceptionFrom *expanded_from, //////////////////////////////////////////////////////////////// ExceptionState::ExceptionState(ExceptionPath *exception, - ExceptionThru *next_thru, - int index) : + ExceptionThru *next_thru, + int index) : exception_(exception), next_thru_(next_thru), next_state_(nullptr), @@ -2282,10 +2318,10 @@ ExceptionState::setNextState(ExceptionState *next_state) bool ExceptionState::matchesNextThru(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - const Network *network) const + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + const Network *network) const { // Don't advance the state if the exception is complete (no next_thru_). return next_thru_ @@ -2306,22 +2342,33 @@ ExceptionState::hash() const return hashSum(exception_->hash(), index_); } -bool -exceptionStateLess(const ExceptionState *state1, - const ExceptionState *state2) -{ - const ExceptionPath *except1 = state1->exception(); - const ExceptionPath *except2 = state2->exception(); - return except1->id() < except2->id() - || (except1 == except2 - && state1->index() < state2->index()); +int +exceptionStateCmp(const ExceptionState *state1, + const ExceptionState *state2) +{ + size_t id1 = state1->exception()->id(); + size_t id2 = state2->exception()->id(); + if (id1 < id2) + return -1; + else if (id1 > id2) + return 1; + else { + size_t state_index1 = state1->index(); + size_t state_index2 = state2->index(); + if (state_index1 < state_index2) + return -1; + else if (state_index1 > state_index2) + return 1; + else + return 0; + } } bool ExceptionStateLess::operator()(const ExceptionState *state1, const ExceptionState *state2) const { - return exceptionStateLess(state1, state2); + return exceptionStateCmp(state1, state2) < 0; } //////////////////////////////////////////////////////////////// @@ -2345,7 +2392,7 @@ ExceptionPathLess::operator()(const ExceptionPath *except1, ExceptionPt *pt2 = pt_iter2.next(); int cmp = pt1->compare(pt2, network_); if (cmp != 0) - return cmp < 0; + return cmp < 0; } // Lesser has fewer exception pts. return !pt_iter1.hasNext() && pt_iter2.hasNext(); @@ -2360,18 +2407,18 @@ class InsertPinPairsThru : public HierPinThruVisitor { public: InsertPinPairsThru(PinPairSet *pairs, - const Network *network); + const Network *network); protected: virtual void visit(const Pin *drvr, - const Pin *load); + const Pin *load); PinPairSet *pairs_; const Network *network_; }; InsertPinPairsThru::InsertPinPairsThru(PinPairSet *pairs, - const Network *network) : + const Network *network) : HierPinThruVisitor(), pairs_(pairs), network_(network) @@ -2380,7 +2427,7 @@ InsertPinPairsThru::InsertPinPairsThru(PinPairSet *pairs, void InsertPinPairsThru::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { PinPair pair(drvr, load); pairs_->insert(pair); @@ -2388,8 +2435,8 @@ InsertPinPairsThru::visit(const Pin *drvr, static void insertPinPairsThruHierPin(const Pin *hpin, - const Network *network, - PinPairSet *pairs) + const Network *network, + PinPairSet *pairs) { InsertPinPairsThru visitor(pairs, network); visitDrvrLoadsThruHierPin(hpin, network, &visitor); @@ -2397,8 +2444,8 @@ insertPinPairsThruHierPin(const Pin *hpin, static void insertPinPairsThruNet(const Net *net, - const Network *network, - PinPairSet *pairs) + const Network *network, + PinPairSet *pairs) { InsertPinPairsThru visitor(pairs, network); visitDrvrLoadsThruNet(net, network, &visitor); @@ -2408,7 +2455,7 @@ class DeletePinPairsThru : public HierPinThruVisitor { public: DeletePinPairsThru(PinPairSet *pairs, - const Network *network); + const Network *network); protected: virtual void visit(const Pin *drvr, @@ -2419,7 +2466,7 @@ class DeletePinPairsThru : public HierPinThruVisitor }; DeletePinPairsThru::DeletePinPairsThru(PinPairSet *pairs, - const Network *network) : + const Network *network) : HierPinThruVisitor(), pairs_(pairs), network_(network) @@ -2428,7 +2475,7 @@ DeletePinPairsThru::DeletePinPairsThru(PinPairSet *pairs, void DeletePinPairsThru::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { PinPair pair(drvr, load); pairs_->erase(pair); @@ -2436,8 +2483,8 @@ DeletePinPairsThru::visit(const Pin *drvr, static void deletePinPairsThruHierPin(const Pin *hpin, - const Network *network, - PinPairSet *pairs) + const Network *network, + PinPairSet *pairs) { DeletePinPairsThru visitor(pairs, network); visitDrvrLoadsThruHierPin(hpin, network, &visitor); diff --git a/sdc/InputDrive.cc b/sdc/InputDrive.cc index 845bb5cfc..2c647562a 100644 --- a/sdc/InputDrive.cc +++ b/sdc/InputDrive.cc @@ -46,25 +46,25 @@ InputDrive::~InputDrive() void InputDrive::setSlew(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const MinMaxAll *min_max, + float slew) { slews_.setValue(rf, min_max, slew); } void InputDrive::setDriveResistance(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res) + const MinMaxAll *min_max, + float res) { drive_resistances_.setValue(rf, min_max, res); } void InputDrive::driveResistance(const RiseFall *rf, - const MinMax *min_max, - float &res, - bool &exists) const + const MinMax *min_max, + float &res, + bool &exists) const { drive_resistances_.value(rf, min_max, res, exists); } @@ -88,27 +88,27 @@ InputDrive::driveResistanceMinMaxEqual(const RiseFall *rf) const void InputDrive::setDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max) + const LibertyCell *cell, + const LibertyPort *from_port, + float *from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max) { for (auto rf_index : rf->rangeIndex()) { for (auto mm_index : min_max->rangeIndex()) { InputDriveCell *drive = drive_cells_[rf_index][mm_index]; if (drive) { - drive->setLibrary(library); - drive->setCell(cell); - drive->setFromPort(from_port); - drive->setFromSlews(from_slews); - drive->setToPort(to_port); + drive->setLibrary(library); + drive->setCell(cell); + drive->setFromPort(from_port); + drive->setFromSlews(from_slews); + drive->setToPort(to_port); } else { - drive = new InputDriveCell(library, cell, from_port, - from_slews, to_port); - drive_cells_[rf_index][mm_index] = drive; + drive = new InputDriveCell(library, cell, from_port, + from_slews, to_port); + drive_cells_[rf_index][mm_index] = drive; } } } @@ -116,12 +116,12 @@ InputDrive::setDriveCell(const LibertyLibrary *library, void InputDrive::driveCell(const RiseFall *rf, - const MinMax *min_max, + const MinMax *min_max, // Return values. - const LibertyCell *&cell, - const LibertyPort *&from_port, - float *&from_slews, - const LibertyPort *&to_port) const + const LibertyCell *&cell, + const LibertyPort *&from_port, + float *&from_slews, + const LibertyPort *&to_port) const { InputDriveCell *drive = drive_cells_[rf->index()][min_max->index()]; if (drive) { @@ -140,14 +140,14 @@ InputDrive::driveCell(const RiseFall *rf, InputDriveCell * InputDrive::driveCell(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { return drive_cells_[rf->index()][min_max->index()]; } bool InputDrive::hasDriveCell(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { return drive_cells_[rf->index()][min_max->index()] != nullptr; } @@ -170,9 +170,9 @@ InputDrive::driveCellsEqual() const void InputDrive::slew(const RiseFall *rf, - const MinMax *min_max, - float &slew, - bool &exists) const + const MinMax *min_max, + float &slew, + bool &exists) const { slews_.value(rf, min_max, slew, exists); } @@ -180,10 +180,10 @@ InputDrive::slew(const RiseFall *rf, //////////////////////////////////////////////////////////////// InputDriveCell::InputDriveCell(const LibertyLibrary *library, - const LibertyCell *cell, - const LibertyPort *from_port, - float *from_slews, - const LibertyPort *to_port) : + const LibertyCell *cell, + const LibertyPort *from_port, + float *from_slews, + const LibertyPort *to_port) : library_(library), cell_(cell), from_port_(from_port), diff --git a/sdc/PinPair.cc b/sdc/PinPair.cc index 6178a9dd3..e88801da3 100644 --- a/sdc/PinPair.cc +++ b/sdc/PinPair.cc @@ -35,7 +35,7 @@ PinPairLess::PinPairLess(const Network *network) : bool PinPairLess::operator()(const PinPair &pair1, - const PinPair &pair2) const + const PinPair &pair2) const { const Pin *pair1_pin1 = pair1.first; const Pin *pair1_pin2 = pair1.second; @@ -52,7 +52,7 @@ PinPairLess::operator()(const PinPair &pair1, bool PinPairEqual::operator()(const PinPair &pair1, - const PinPair &pair2) const + const PinPair &pair2) const { return pair1.first == pair2.first && pair1.second == pair2.second; @@ -73,7 +73,7 @@ PinPairHash::operator()(const PinPair &pair) const } PinPairSet::PinPairSet(const Network *network) : - Set(PinPairLess(network)) + std::set(PinPairLess(network)) { } diff --git a/sdc/PortDelay.cc b/sdc/PortDelay.cc index e23812162..073c8e26d 100644 --- a/sdc/PortDelay.cc +++ b/sdc/PortDelay.cc @@ -30,7 +30,7 @@ namespace sta { PortDelay::PortDelay(const Pin *pin, - const ClockEdge *clk_edge, + const ClockEdge *clk_edge, const Network *network) : pin_(pin), clk_edge_(clk_edge), @@ -92,9 +92,9 @@ PortDelay::refTransition() const } InputDelay::InputDelay(const Pin *pin, - const ClockEdge *clk_edge, - int index, - const Network *network) : + const ClockEdge *clk_edge, + int index, + const Network *network) : PortDelay(pin, clk_edge, network), index_(index) { @@ -102,8 +102,8 @@ InputDelay::InputDelay(const Pin *pin, } OutputDelay::OutputDelay(const Pin *pin, - const ClockEdge *clk_edge, - const Network *network) : + const ClockEdge *clk_edge, + const Network *network) : PortDelay(pin, clk_edge, network) { if (network) diff --git a/sdc/PortExtCap.cc b/sdc/PortExtCap.cc index 5dfd2180e..48287fec1 100644 --- a/sdc/PortExtCap.cc +++ b/sdc/PortExtCap.cc @@ -26,60 +26,66 @@ namespace sta { -PortExtCap::PortExtCap(const Port *port) : - port_(port) +PortExtCap::PortExtCap() : + port_(nullptr) { } void PortExtCap::pinCap(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists) + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const { pin_cap_.value(rf, min_max, cap, exists); } void -PortExtCap::setPinCap(float cap, - const RiseFall *rf, - const MinMax *min_max) +PortExtCap::setPinCap(const Port *port, + float cap, + const RiseFall *rf, + const MinMax *min_max) { + port_ = port; pin_cap_.setValue(rf, min_max, cap); } void PortExtCap::wireCap(const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists) + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const { wire_cap_.value(rf, min_max, cap, exists); } void -PortExtCap::setWireCap(float cap, - const RiseFall *rf, - const MinMax *min_max) +PortExtCap::setWireCap(const Port *port, + float cap, + const RiseFall *rf, + const MinMax *min_max) { + port_ = port; wire_cap_.setValue(rf, min_max, cap); } void -PortExtCap::setFanout(int fanout, - const MinMax *min_max) +PortExtCap::setFanout(const Port *port, + int fanout, + const MinMax *min_max) { + port_ = port; fanout_.setValue(min_max, fanout); } void PortExtCap::fanout(const MinMax *min_max, - // Return values. - int &fanout, - bool &exists) + // Return values. + int &fanout, + bool &exists) const { fanout_.value(min_max, fanout, exists); } diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index daca91869..0c11320af 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -25,7 +25,9 @@ #include "Sdc.hh" #include +#include +#include "ContainerHelpers.hh" #include "Stats.hh" #include "Debug.hh" #include "Mutex.hh" @@ -55,7 +57,7 @@ #include "DeratingFactors.hh" #include "HpinDrvrLoad.hh" #include "search/Levelize.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Graph.hh" namespace sta { @@ -64,25 +66,25 @@ using std::swap; bool ClockPairLess::operator()(const ClockPair &pair1, - const ClockPair &pair2) const + const ClockPair &pair2) const { - int first1 = pair1.first->index(); - int second1 = pair1.second->index(); + const auto& [clk1_1, clk2_1] = pair1; + int first1 = clk1_1->index(); + int second1 = clk2_1->index(); if (first1 > second1) std::swap(first1, second1); - int first2 = pair2.first->index(); - int second2 = pair2.second->index(); + const auto& [clk1_2, clk2_2] = pair2; + int first2 = clk1_2->index(); + int second2 = clk2_2->index(); if (first2 > second2) std::swap(first2, second2); return (first1 < first2) || (first1 == first2 - && second1 < second2); + && second1 < second2); } -//////////////////////////////////////////////////////////////// - -typedef Vector ClockPairSeq; -typedef Set PvtSet; +using ClockPairSeq = std::vector; +using PvtSet = std::set; static ExceptionThruSeq * clone(ExceptionThruSeq *thrus, @@ -90,15 +92,18 @@ clone(ExceptionThruSeq *thrus, //////////////////////////////////////////////////////////////// -Sdc::Sdc(StaState *sta) : +Sdc::Sdc(Mode *mode, + StaState *sta) : StaState(sta), + mode_(mode), derating_factors_(nullptr), clk_index_(0), - clock_pin_map_(PinIdHash(network_)), - clock_leaf_pin_map_(PinIdHash(network_)), + clock_pin_map_(10, PinIdHash(network_)), + clock_leaf_pin_map_(10, PinIdHash(network_)), clk_hpin_disables_(network_), propagated_clk_pins_(network_), clk_latencies_(network_), + edge_clk_latency_map_(network_), clk_insertions_(network_), clk_sense_map_(network_), clk_gating_check_(nullptr), @@ -114,6 +119,10 @@ Sdc::Sdc(StaState *sta) : output_delay_ref_pin_map_(PinIdLess(network_)), output_delay_leaf_pin_map_(PinIdLess(network_)), + port_ext_cap_map_(network_), + net_wire_cap_map_(network_), + drvr_pin_wire_cap_map_(network_), + disabled_pins_(network_), disabled_ports_(network_), disabled_wire_edges_(network_), @@ -125,12 +134,10 @@ Sdc::Sdc(StaState *sta) : path_delay_internal_from_(network_), path_delay_internal_from_break_(network_), path_delay_internal_to_(network_), - path_delay_internal_to_break_(network_) + path_delay_internal_to_break_(network_), + filter_(nullptr) { - sdc_ = this; initVariables(); - if (corners_) - makeCornersAfter(corners_); setWireload(nullptr, MinMaxAll::all()); setWireloadSelection(nullptr, MinMaxAll::all()); setOperatingConditions(nullptr, MinMaxAll::all()); @@ -157,7 +164,6 @@ Sdc::~Sdc() void Sdc::clear() { - removeLibertyAnnotations(); deleteConstraints(); propagated_clk_pins_.clear(); clocks_.clear(); @@ -165,7 +171,7 @@ Sdc::clear() clock_pin_map_.clear(); clock_leaf_pin_map_.clear(); clk_latencies_.clear(); - edge_clk_latency_.clear(); + edge_clk_latency_map_.clear(); clk_insertions_.clear(); pin_clk_uncertainty_map_.clear(); @@ -246,53 +252,53 @@ Sdc::initVariables() void Sdc::deleteConstraints() { - clocks_.deleteContents(); + deleteContents(clocks_);; delete default_arrival_clk_; - clock_pin_map_.deleteContents(); - clock_leaf_pin_map_.deleteContents(); - clk_latencies_.deleteContents(); - clk_insertions_.deleteContents(); + deleteContents(clock_pin_map_); + deleteContents(clock_leaf_pin_map_); + deleteContents(clk_latencies_); + deleteContents(clk_insertions_); - clk_groups_name_map_.deleteContents(); + deleteContents(clk_groups_name_map_); clearClkGroupExclusions(); - pin_clk_uncertainty_map_.deleteContents(); - inter_clk_uncertainties_.deleteContents(); + deleteContents(pin_clk_uncertainty_map_); + deleteContents(inter_clk_uncertainties_); delete clk_gating_check_; clk_gating_check_ = nullptr; - clk_gating_check_map_.deleteContents(); - inst_clk_gating_check_map_.deleteContents(); - pin_clk_gating_check_map_.deleteContents(); - input_drive_map_.deleteContents(); - disabled_cell_ports_.deleteContents(); - disabled_inst_ports_.deleteContents(); - pin_min_pulse_width_map_.deleteContentsClear(); - inst_min_pulse_width_map_.deleteContentsClear(); - clk_min_pulse_width_map_.deleteContentsClear(); + deleteContents(clk_gating_check_map_); + deleteContents(inst_clk_gating_check_map_); + deleteContents(pin_clk_gating_check_map_); + deleteContents(input_drive_map_); + deleteContents(disabled_cell_ports_); + deleteContents(disabled_inst_ports_); + deleteContents(pin_min_pulse_width_map_); + deleteContents(inst_min_pulse_width_map_); + deleteContents(clk_min_pulse_width_map_); for (auto [pin, checks] : data_checks_from_map_) { - checks->deleteContents(); + deleteContents(*checks); delete checks; } - for (auto [pin, checks] : data_checks_to_map_) - delete checks; + deleteContents(data_checks_to_map_); - input_delays_.deleteContents(); - input_delay_pin_map_.deleteContents(); - input_delay_leaf_pin_map_.deleteContents(); - input_delay_ref_pin_map_.deleteContents(); - input_delay_internal_pin_map_.deleteContents(); + deleteContents(input_delays_); + deleteContents(input_delay_pin_map_); + deleteContents(input_delay_leaf_pin_map_); + deleteContents(input_delay_ref_pin_map_); + deleteContents(input_delay_internal_pin_map_); - output_delays_.deleteContents(); - output_delay_pin_map_.deleteContents(); - output_delay_ref_pin_map_.deleteContents(); - output_delay_leaf_pin_map_.deleteContents(); + deleteContents(output_delays_); + deleteContents(output_delay_pin_map_); + deleteContents(output_delay_ref_pin_map_); + deleteContents(output_delay_leaf_pin_map_); - clk_hpin_disables_.deleteContentsClear(); + deleteContents(clk_hpin_disables_); clk_hpin_disables_valid_ = false; clearCycleAcctings(); deleteExceptions(); + deleteFilter(); clearGroupPathMap(); deleteDeratingFactors(); @@ -301,66 +307,30 @@ Sdc::deleteConstraints() clk_sense_map_.clear(); for (int mm_index : MinMax::rangeIndex()) - instance_pvt_maps_[mm_index].deleteContentsClear(); + deleteContents(instance_pvt_maps_[mm_index]); } void -Sdc::removeNetLoadCaps() +Sdc::searchPreamble() { - if (!net_wire_cap_maps_.empty()) { - for (int corner_index = 0; corner_index < corners_->count(); corner_index++) { - net_wire_cap_maps_[corner_index].clear(); - drvr_pin_wire_cap_maps_[corner_index].clear(); - port_ext_cap_maps_[corner_index].deleteContentsClear(); - } - } + ensureClkHpinDisables(); + ensureClkGroupExclusions(); } void -Sdc::removeLibertyAnnotations() +Sdc::removeNetLoadCaps() { - for (auto [cell, disable] : disabled_cell_ports_) { - if (disable->all()) - cell->setIsDisabledConstraint(false); - - if (disable->from()) { - for (LibertyPort *from : *disable->from()) - from->setIsDisabledConstraint(false); - } - - if (disable->to()) { - for (LibertyPort *to : *disable->to()) - to->setIsDisabledConstraint(false); - } - - if (disable->timingArcSets()) { - for (TimingArcSet *arc_set : *disable->timingArcSets()) - arc_set->setIsDisabledConstraint(false); - } - - - if (disable->fromTo()) { - for (const LibertyPortPair &pair : *disable->fromTo()) { - const LibertyPort *from = pair.first; - const LibertyPort *to = pair.second; - for (TimingArcSet *arc_set : cell->timingArcSets(from, to)) - arc_set->setIsDisabledConstraint(false); - } - } - } - - for (LibertyPort *port : disabled_lib_ports_) - port->setIsDisabledConstraint(false); + net_wire_cap_map_.clear(); + drvr_pin_wire_cap_map_.clear(); + port_ext_cap_map_.clear(); } void Sdc::deleteNetBefore(const Net *net) { - for (int corner_index = 0; corner_index < corners_->count(); corner_index++) { - net_wire_cap_maps_[corner_index].erase(net); - for (const Pin *pin : *network_->drivers(net)) - drvr_pin_wire_cap_maps_[corner_index].erase(pin); - } + net_wire_cap_map_.erase(net); + for (const Pin *pin : *network_->drivers(net)) + drvr_pin_wire_cap_map_.erase(pin); } // see Sdc::isConstrained @@ -383,82 +353,73 @@ Sdc::deleteInstanceBefore(const Instance *inst) } void -Sdc::makeCornersBefore() +Sdc::makeSceneBefore() { removeNetLoadCaps(); } -void -Sdc::makeCornersAfter(Corners *corners) -{ - corners_ = corners; - port_ext_cap_maps_.resize(corners_->count(), PortExtCapMap(PortIdLess(network_))); - net_wire_cap_maps_.resize(corners_->count(), NetWireCapMap(NetIdLess(network_))); - drvr_pin_wire_cap_maps_.resize(corners_->count(), PinWireCapMap(PinIdLess(network_))); -} - //////////////////////////////////////////////////////////////// bool Sdc::isConstrained(const Pin *pin) const { Port *port = network_->isTopLevelPort(pin) ? network_->port(pin) : nullptr; - return clock_pin_map_.hasKey(pin) - || propagated_clk_pins_.hasKey(pin) + return clock_pin_map_.contains(pin) + || propagated_clk_pins_.contains(pin) || hasClockLatency(pin) || hasClockInsertion(pin) - || pin_clk_uncertainty_map_.hasKey(pin) - || pin_clk_gating_check_map_.hasKey(pin) - || data_checks_from_map_.hasKey(pin) - || data_checks_to_map_.hasKey(pin) - || input_delay_pin_map_.hasKey(pin) - || output_delay_pin_map_.hasKey(pin) - || pin_cap_limit_map_.hasKey(pin) - || disabled_pins_.hasKey(pin) - || disabled_ports_.hasKey(port) - || disabled_clk_gating_checks_pin_.hasKey(pin) - || first_from_pin_exceptions_.hasKey(pin) - || first_thru_pin_exceptions_.hasKey(pin) - || first_to_pin_exceptions_.hasKey(pin) - || input_drive_map_.hasKey(port) - || logic_value_map_.hasKey(pin) - || case_value_map_.hasKey(pin) - || pin_latch_borrow_limit_map_.hasKey(pin) - || pin_min_pulse_width_map_.hasKey(pin) - || (port && (port_slew_limit_map_.hasKey(port) - || port_cap_limit_map_.hasKey(port) - || port_fanout_limit_map_.hasKey(port) + || pin_clk_uncertainty_map_.contains(pin) + || pin_clk_gating_check_map_.contains(pin) + || data_checks_from_map_.contains(pin) + || data_checks_to_map_.contains(pin) + || input_delay_pin_map_.contains(pin) + || output_delay_pin_map_.contains(pin) + || pin_cap_limit_map_.contains(pin) + || disabled_pins_.contains(pin) + || disabled_ports_.contains(port) + || disabled_clk_gating_checks_pin_.contains(pin) + || first_from_pin_exceptions_.contains(pin) + || first_thru_pin_exceptions_.contains(pin) + || first_to_pin_exceptions_.contains(pin) + || input_drive_map_.contains(port) + || logic_value_map_.contains(pin) + || case_value_map_.contains(pin) + || pin_latch_borrow_limit_map_.contains(pin) + || pin_min_pulse_width_map_.contains(pin) + || (port && (port_slew_limit_map_.contains(port) + || port_cap_limit_map_.contains(port) + || port_fanout_limit_map_.contains(port) || hasPortExtCap(port))); } bool Sdc::isConstrained(const Instance *inst) const { - return instance_pvt_maps_[MinMax::minIndex()].hasKey(inst) - || instance_pvt_maps_[MinMax::maxIndex()].hasKey(inst) - || inst_derating_factors_.hasKey(inst) - || inst_clk_gating_check_map_.hasKey(inst) - || disabled_inst_ports_.hasKey(inst) - || first_from_inst_exceptions_.hasKey(inst) - || first_thru_inst_exceptions_.hasKey(inst) - || first_to_inst_exceptions_.hasKey(inst) - || inst_latch_borrow_limit_map_.hasKey(inst) - || inst_min_pulse_width_map_.hasKey(inst); + return instance_pvt_maps_[MinMax::minIndex()].contains(inst) + || instance_pvt_maps_[MinMax::maxIndex()].contains(inst) + || inst_derating_factors_.contains(inst) + || inst_clk_gating_check_map_.contains(inst) + || disabled_inst_ports_.contains(inst) + || first_from_inst_exceptions_.contains(inst) + || first_thru_inst_exceptions_.contains(inst) + || first_to_inst_exceptions_.contains(inst) + || inst_latch_borrow_limit_map_.contains(inst) + || inst_min_pulse_width_map_.contains(inst); } bool Sdc::isConstrained(const Net *net) const { - return net_derating_factors_.hasKey(net) + return net_derating_factors_.contains(net) || hasNetWireCap(net) - || net_res_map_.hasKey(net) - || first_thru_net_exceptions_.hasKey(net); + || net_res_map_.contains(net) + || first_thru_net_exceptions_.contains(net); } //////////////////////////////////////////////////////////////// PortSeq -Sdc::allInputs(bool no_clks) +Sdc::allInputs(bool no_clks) const { PortSeq ports; Instance *top_inst = network_->topInstance(); @@ -476,7 +437,7 @@ Sdc::allInputs(bool no_clks) } PortSeq -Sdc::allOutputs() +Sdc::allOutputs() const { PortSeq ports; Instance *top_inst = network_->topInstance(); @@ -494,7 +455,7 @@ Sdc::allOutputs() void Sdc::portMembers(const Port *port, - PortSeq &ports) + PortSeq &ports) const { if (network_->isBus(port)) { PortMemberIterator *member_iter = network_->memberIterator(port); @@ -516,7 +477,7 @@ Sdc::setAnalysisType(AnalysisType analysis_type) void Sdc::setOperatingConditions(OperatingConditions *op_cond, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { for (auto mm_index : min_max->rangeIndex()) operating_conditions_[mm_index] = op_cond; @@ -524,7 +485,7 @@ Sdc::setOperatingConditions(OperatingConditions *op_cond, void Sdc::setOperatingConditions(OperatingConditions *op_cond, - const MinMax *min_max) + const MinMax *min_max) { int mm_index = min_max->index(); operating_conditions_[mm_index] = op_cond; @@ -539,16 +500,16 @@ Sdc::operatingConditions(const MinMax *min_max) const const Pvt * Sdc::pvt(const Instance *inst, - const MinMax *min_max) const + const MinMax *min_max) const { const InstancePvtMap &pvt_map = instance_pvt_maps_[min_max->index()]; - return pvt_map.findKey(inst); + return findKey(pvt_map, inst); } void Sdc::setPvt(const Instance *inst, const MinMaxAll *min_max, - const Pvt &pvt) + const Pvt &pvt) { for (auto mm_index : min_max->rangeIndex()) { InstancePvtMap &pvt_map = instance_pvt_maps_[mm_index]; @@ -560,7 +521,7 @@ void Sdc::voltage(const MinMax *min_max, // Return values. float &voltage, - bool &exists) + bool &exists) const { voltages_.value(min_max, voltage, exists); } @@ -570,11 +531,14 @@ Sdc::voltage(const Net *net, const MinMax *min_max, // Return values. float &voltage, - bool &exists) + bool &exists) const { exists = false; - if (net_voltage_map_.hasKey(net)) - net_voltage_map_[net].value(min_max, voltage, exists); + auto itr = net_voltage_map_.find(net); + if (itr != net_voltage_map_.end()) { + const MinMaxFloatValues &values = itr->second; + values.value(min_max, voltage, exists); + } } void @@ -596,10 +560,10 @@ Sdc::setVoltage(const Net *net, void Sdc::setTimingDerate(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { if (derating_factors_ == nullptr) derating_factors_ = new DeratingFactorsGlobal; @@ -608,12 +572,12 @@ Sdc::setTimingDerate(TimingDerateType type, void Sdc::setTimingDerate(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - DeratingFactorsNet *factors = net_derating_factors_.findKey(net); + DeratingFactorsNet *factors = findKey(net_derating_factors_, net); if (factors == nullptr) { factors = new DeratingFactorsNet; net_derating_factors_[net] = factors; @@ -623,13 +587,13 @@ Sdc::setTimingDerate(const Net *net, void Sdc::setTimingDerate(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - DeratingFactorsCell *factors = inst_derating_factors_.findKey(inst); + DeratingFactorsCell *factors = findKey(inst_derating_factors_, inst); if (factors == nullptr) { factors = new DeratingFactorsCell; inst_derating_factors_[inst] = factors; @@ -639,13 +603,13 @@ Sdc::setTimingDerate(const Instance *inst, void Sdc::setTimingDerate(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - DeratingFactorsCell *factors = cell_derating_factors_.findKey(cell); + DeratingFactorsCell *factors = findKey(cell_derating_factors_, cell); if (factors == nullptr) { factors = new DeratingFactorsCell; cell_derating_factors_[cell] = factors; @@ -655,13 +619,13 @@ Sdc::setTimingDerate(const LibertyCell *cell, float Sdc::timingDerateInstance(const Pin *pin, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late) const + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late) const { const Instance *inst = network_->instance(pin); - DeratingFactorsCell *factors = inst_derating_factors_.findKey(inst); + DeratingFactorsCell *factors = findKey(inst_derating_factors_, inst); if (factors) { float factor; bool exists; @@ -672,7 +636,7 @@ Sdc::timingDerateInstance(const Pin *pin, const LibertyCell *cell = network_->libertyCell(inst); if (cell) { - DeratingFactorsCell *factors = cell_derating_factors_.findKey(cell); + DeratingFactorsCell *factors = findKey(cell_derating_factors_, cell); float factor; bool exists; if (factors) { @@ -694,12 +658,12 @@ Sdc::timingDerateInstance(const Pin *pin, float Sdc::timingDerateNet(const Pin *pin, - PathClkOrData clk_data, - const RiseFall *rf, - const EarlyLate *early_late) const + PathClkOrData clk_data, + const RiseFall *rf, + const EarlyLate *early_late) const { const Net *net = network_->net(pin); - DeratingFactorsNet *factors = net_derating_factors_.findKey(net); + DeratingFactorsNet *factors = findKey(net_derating_factors_, net); if (factors) { float factor; bool exists; @@ -712,7 +676,7 @@ Sdc::timingDerateNet(const Pin *pin, float factor; bool exists; derating_factors_->factor(TimingDerateType::net_delay, clk_data, rf, - early_late, factor, exists); + early_late, factor, exists); if (exists) return factor; } @@ -738,9 +702,9 @@ Sdc::swapDeratingFactors(Sdc *sdc1, void Sdc::deleteDeratingFactors() { - net_derating_factors_.deleteContents(); - inst_derating_factors_.deleteContents(); - cell_derating_factors_.deleteContents(); + deleteContents(net_derating_factors_); + deleteContents(inst_derating_factors_); + deleteContents(cell_derating_factors_); delete derating_factors_; derating_factors_ = nullptr; @@ -750,32 +714,32 @@ Sdc::deleteDeratingFactors() void Sdc::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 MinMaxAll *min_max) + const LibertyCell *cell, + const Port *port, + const LibertyPort *from_port, + float *from_slews, + const LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max) { ensureInputDrive(port)->setDriveCell(library, cell, from_port, from_slews, - to_port, rf, min_max); + to_port, rf, min_max); } void Sdc::setInputSlew(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) { ensureInputDrive(port)->setSlew(rf, min_max, slew); } void Sdc::setDriveResistance(const Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res) { ensureInputDrive(port)->setDriveResistance(rf, min_max, res); } @@ -783,7 +747,7 @@ Sdc::setDriveResistance(const Port *port, InputDrive * Sdc::ensureInputDrive(const Port *port) { - InputDrive *drive = input_drive_map_.findKey(port); + InputDrive *drive = findKey(input_drive_map_, port); if (drive == nullptr) { drive = new InputDrive; input_drive_map_[port] = drive; @@ -795,10 +759,10 @@ Sdc::ensureInputDrive(const Port *port) void Sdc::setSlewLimit(Clock *clk, - const RiseFallBoth *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float slew) + const RiseFallBoth *rf, + const PathClkOrData clk_data, + const MinMax *min_max, + float slew) { clk->setSlewLimit(rf, clk_data, min_max, slew); have_clk_slew_limits_ = true; @@ -811,33 +775,32 @@ Sdc::haveClkSlewLimits() const } void -Sdc::slewLimit(Clock *clk, +Sdc::slewLimit(const Clock *clk, const RiseFall *rf, - const PathClkOrData clk_data, - const MinMax *min_max, - float &slew, - bool &exists) + const PathClkOrData clk_data, + const MinMax *min_max, + float &slew, + bool &exists) const { clk->slewLimit(rf, clk_data, min_max, slew, exists); } void Sdc::slewLimit(Port *port, - const MinMax *min_max, - float &slew, - bool &exists) + const MinMax *min_max, + float &slew, + bool &exists) const { slew = INF; MinMaxFloatValues values; - port_slew_limit_map_.findKey(port, values, exists); - if (exists) - values.value(min_max, slew, exists); + findKeyValue(port_slew_limit_map_, port, values, exists); + values.value(min_max, slew, exists); } void Sdc::setSlewLimit(Port *port, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { MinMaxFloatValues &values = port_slew_limit_map_[port]; values.setValue(min_max, slew); @@ -845,21 +808,21 @@ Sdc::setSlewLimit(Port *port, void Sdc::slewLimit(Cell *cell, - const MinMax *min_max, - float &slew, - bool &exists) + const MinMax *min_max, + float &slew, + bool &exists) const { slew = INF; MinMaxFloatValues values; - cell_slew_limit_map_.findKey(cell, values, exists); + findKeyValue(cell_slew_limit_map_, cell, values, exists); if (exists) values.value(min_max, slew, exists); } void Sdc::setSlewLimit(Cell *cell, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { MinMaxFloatValues &values = cell_slew_limit_map_[cell]; values.setValue(min_max, slew); @@ -867,22 +830,22 @@ Sdc::setSlewLimit(Cell *cell, void Sdc::capacitanceLimit(Cell *cell, - const MinMax *min_max, - float &cap, - bool &exists) + const MinMax *min_max, + float &cap, + bool &exists) const { cap = 0.0; exists = false; MinMaxFloatValues values; - cell_cap_limit_map_.findKey(cell, values, exists); + findKeyValue(cell_cap_limit_map_, cell, values, exists); if (exists) values.value(min_max, cap, exists); } void Sdc::setCapacitanceLimit(Cell *cell, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { MinMaxFloatValues &values = cell_cap_limit_map_[cell]; values.setValue(min_max, cap); @@ -890,22 +853,22 @@ Sdc::setCapacitanceLimit(Cell *cell, void Sdc::capacitanceLimit(Port *port, - const MinMax *min_max, - float &cap, - bool &exists) + const MinMax *min_max, + float &cap, + bool &exists) const { cap = 0.0; exists = false; MinMaxFloatValues values; - port_cap_limit_map_.findKey(port, values, exists); + findKeyValue(port_cap_limit_map_, port, values, exists); if (exists) values.value(min_max, cap, exists); } void Sdc::setCapacitanceLimit(Port *port, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { MinMaxFloatValues &values = port_cap_limit_map_[port]; values.setValue(min_max, cap); @@ -913,22 +876,22 @@ Sdc::setCapacitanceLimit(Port *port, void Sdc::capacitanceLimit(Pin *pin, - const MinMax *min_max, - float &cap, - bool &exists) + const MinMax *min_max, + float &cap, + bool &exists) const { cap = 0.0; exists = false; MinMaxFloatValues values; - pin_cap_limit_map_.findKey(pin, values, exists); + findKeyValue(pin_cap_limit_map_, pin, values, exists); if (exists) values.value(min_max, cap, exists); } void Sdc::setCapacitanceLimit(Pin *pin, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { MinMaxFloatValues &values = pin_cap_limit_map_[pin]; values.setValue(min_max, cap); @@ -936,21 +899,21 @@ Sdc::setCapacitanceLimit(Pin *pin, void Sdc::fanoutLimit(Cell *cell, - const MinMax *min_max, - float &fanout, - bool &exists) + const MinMax *min_max, + float &fanout, + bool &exists) const { fanout = min_max->initValue(); MinMaxFloatValues values; - cell_fanout_limit_map_.findKey(cell, values, exists); + findKeyValue(cell_fanout_limit_map_, cell, values, exists); if (exists) values.value(min_max, fanout, exists); } void Sdc::setFanoutLimit(Cell *cell, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout) { MinMaxFloatValues &values = cell_fanout_limit_map_[cell]; values.setValue(min_max, fanout); @@ -958,21 +921,21 @@ Sdc::setFanoutLimit(Cell *cell, void Sdc::fanoutLimit(Port *port, - const MinMax *min_max, - float &fanout, - bool &exists) + const MinMax *min_max, + float &fanout, + bool &exists) const { fanout = 0.0; MinMaxFloatValues values; - port_fanout_limit_map_.findKey(port, values, exists); + findKeyValue(port_fanout_limit_map_, port, values, exists); if (exists) values.value(min_max, fanout, exists); } void Sdc::setFanoutLimit(Port *port, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout) { MinMaxFloatValues &values = port_fanout_limit_map_[port]; values.setValue(min_max, fanout); @@ -994,13 +957,13 @@ Sdc::maxArea() const Clock * Sdc::makeClock(const char *name, - PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - const char *comment) + PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, + const char *comment) { - Clock *clk = clock_name_map_.findKey(name); + Clock *clk = findKey(clock_name_map_, name); if (!add_to_pins) deletePinClocks(clk, pins); if (clk) @@ -1024,20 +987,20 @@ Sdc::makeClock(const char *name, Clock * Sdc::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, - const char *comment) -{ - Clock *clk = clock_name_map_.findKey(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, + const char *comment) +{ + Clock *clk = findKey(clock_name_map_, name); if (!add_to_pins) deletePinClocks(clk, pins); if (clk) @@ -1048,11 +1011,11 @@ Sdc::makeGeneratedClock(const char *name, clock_name_map_[clk->name()] = clk; } clk->initGeneratedClk(pins, add_to_pins, src_pin, master_clk, - divide_by, multiply_by, duty_cycle, - invert, combinational, - edges, edge_shifts, + divide_by, multiply_by, duty_cycle, + invert, combinational, + edges, edge_shifts, variables_->propagateAllClocks(), - comment, network_); + comment, network_); makeClkPinMappings(clk); clearCycleAcctings(); invalidateGeneratedClks(); @@ -1074,14 +1037,14 @@ Sdc::invalidateGeneratedClks() const // is not the clock being defined and has no pins it is removed. void Sdc::deletePinClocks(Clock *defining_clk, - PinSet *pins) + PinSet *pins) { // Find all the clocks defined on pins to avoid finding the clock's // vertex pins multiple times. ClockSet clks; if (pins) { for (const Pin *pin : *pins) { - ClockSet *pin_clks = clock_pin_map_.findKey(pin); + ClockSet *pin_clks = findKey(clock_pin_map_, pin); if (pin_clks) { for (Clock *clk : *pin_clks) clks.insert(clk); @@ -1094,12 +1057,12 @@ Sdc::deletePinClocks(Clock *defining_clk, clk->deletePin(pin); if (clk != defining_clk) { if (clk->pins().empty()) - removeClock(clk); + removeClock(clk); else { - clk->makeLeafPins(network_); - // One of the remaining clock pins may use a vertex pin that - // was deleted above. - makeClkPinMappings(clk); + clk->makeLeafPins(network_); + // One of the remaining clock pins may use a vertex pin that + // was deleted above. + makeClkPinMappings(clk); } } } @@ -1109,23 +1072,23 @@ void Sdc::deleteClkPinMappings(Clock *clk) { for (const Pin *pin : clk->pins()) { - ClockSet *pin_clks = clock_pin_map_.findKey(pin); + ClockSet *pin_clks = findKey(clock_pin_map_, pin); if (pin_clks) { pin_clks->erase(clk); if (pin_clks->empty()) { - clock_pin_map_.erase(pin); - delete pin_clks; + clock_pin_map_.erase(pin); + delete pin_clks; } } } for (const Pin *pin : clk->leafPins()) { - ClockSet *pin_clks = clock_leaf_pin_map_.findKey(pin); + ClockSet *pin_clks = findKey(clock_leaf_pin_map_, pin); if (pin_clks) { pin_clks->erase(clk); if (pin_clks->empty()) { - clock_leaf_pin_map_.erase(pin); - delete pin_clks; + clock_leaf_pin_map_.erase(pin); + delete pin_clks; } } } @@ -1135,19 +1098,19 @@ void Sdc::makeClkPinMappings(Clock *clk) { for (const Pin *pin : clk->pins()) { - ClockSet *pin_clks = clock_pin_map_.findKey(pin); + ClockSet *pin_clks = findKey(clock_pin_map_, pin); if (pin_clks == nullptr) { pin_clks = new ClockSet; - clock_pin_map_.insert(pin, pin_clks); + clock_pin_map_[pin] = pin_clks; } pin_clks->insert(clk); } for (const Pin *pin : clk->leafPins()) { - ClockSet *pin_clks = clock_leaf_pin_map_.findKey(pin); + ClockSet *pin_clks = findKey(clock_leaf_pin_map_, pin); if (pin_clks == nullptr) { pin_clks = new ClockSet; - clock_leaf_pin_map_.insert(pin, pin_clks); + clock_leaf_pin_map_[pin] = pin_clks; } pin_clks->insert(clk); } @@ -1169,7 +1132,7 @@ Sdc::removeClock(Clock *clk) clearCycleAcctings(); deleteClkPinMappings(clk); - clocks_.eraseObject(clk); + clocks_.erase(std::find(clocks_.begin(), clocks_.end(), clk)); clock_name_map_.erase(clk->name()); delete clk; } @@ -1180,7 +1143,7 @@ Sdc::deleteMasterClkRefs(Clock *clk) { for (auto gclk : clocks_) { if (gclk->isGenerated() - && gclk->masterClk() == clk) { + && gclk->masterClk() == clk) { gclk->setMasterClk(nullptr); } } @@ -1189,7 +1152,7 @@ Sdc::deleteMasterClkRefs(Clock *clk) Clock * Sdc::findClock(const char *name) const { - return clock_name_map_.findKey(name); + return findKey(clock_name_map_, name); } bool @@ -1213,7 +1176,7 @@ Sdc::isLeafPinNonGeneratedClock(const Pin *pin) const if (clks) { for (Clock *clk : *clks) { if (!clk->isGenerated()) - return true; + return true; } return false; } @@ -1224,13 +1187,13 @@ Sdc::isLeafPinNonGeneratedClock(const Pin *pin) const ClockSet * Sdc::findLeafPinClocks(const Pin *pin) const { - return clock_leaf_pin_map_.findKey(pin); + return findKey(clock_leaf_pin_map_, pin); } ClockSet * Sdc::findClocks(const Pin *pin) const { - return clock_pin_map_.findKey(pin); + return findKey(clock_pin_map_, pin); } ClockSeq @@ -1245,18 +1208,20 @@ Sdc::findClocksMatching(PatternMatch *pattern) const else { for (auto clk : clocks_) { if (pattern->match(clk->name())) - matches.push_back(clk); + matches.push_back(clk); } } return matches; } -void -Sdc::sortedClocks(ClockSeq &clks) +ClockSeq +Sdc::sortedClocks() const { - for (auto clk : clocks_) + ClockSeq clks; + for (Clock *clk : clocks_) clks.push_back(clk); sort(clks, ClkNameLess()); + return clks; } ClockEdge * @@ -1271,8 +1236,8 @@ class ClkHpinDisable { public: ClkHpinDisable(const Clock *clk, - const Pin *from_pin, - const Pin *to_pin); + const Pin *from_pin, + const Pin *to_pin); const Clock *clk() const { return clk_; } const Pin *fromPin() const { return from_pin_; } const Pin *toPin() const { return to_pin_; } @@ -1284,8 +1249,8 @@ class ClkHpinDisable }; ClkHpinDisable::ClkHpinDisable(const Clock *clk, - const Pin *from_pin, - const Pin *to_pin) : + const Pin *from_pin, + const Pin *to_pin) : clk_(clk), from_pin_(from_pin), to_pin_(to_pin) @@ -1299,7 +1264,7 @@ ClkHpinDisableLess::ClkHpinDisableLess(const Network *network) : bool ClkHpinDisableLess::operator()(const ClkHpinDisable *disable1, - const ClkHpinDisable *disable2) const + const ClkHpinDisable *disable2) const { int clk_index1 = disable1->clk()->index(); int clk_index2 = disable2->clk()->index(); @@ -1318,10 +1283,10 @@ class FindClkHpinDisables : public HpinDrvrLoadVisitor { public: FindClkHpinDisables(Clock *clk, - const Network *network, - Sdc *sdc); + const Network *network, + Sdc *sdc); bool drvrLoadExists(const Pin *drvr, - const Pin *load); + const Pin *load); protected: virtual void visit(HpinDrvrLoad *drvr_load); @@ -1336,8 +1301,8 @@ class FindClkHpinDisables : public HpinDrvrLoadVisitor }; FindClkHpinDisables::FindClkHpinDisables(Clock *clk, - const Network *network, - Sdc *sdc) : + const Network *network, + Sdc *sdc) : HpinDrvrLoadVisitor(), clk_(clk), drvr_loads_(network), @@ -1362,8 +1327,8 @@ FindClkHpinDisables::visit(HpinDrvrLoad *drvr_load) void FindClkHpinDisables::makeClkHpinDisables(const Pin *clk_src, - const Pin *drvr, - const Pin *load) + const Pin *drvr, + const Pin *load) { ClockSet *clks = sdc_->findClocks(clk_src); if (clks) { @@ -1379,36 +1344,36 @@ FindClkHpinDisables::makeClkHpinDisables(const Pin *clk_src, bool FindClkHpinDisables::drvrLoadExists(const Pin *drvr, - const Pin *load) + const Pin *load) { PinPair probe(drvr, load); - return drvr_loads_.hasKey(probe); + return drvr_loads_.contains(probe); } void Sdc::ensureClkHpinDisables() { if (!clk_hpin_disables_valid_) { - clk_hpin_disables_.deleteContentsClear(); + deleteContents(clk_hpin_disables_); for (auto clk : clocks_) { for (const Pin *src : clk->pins()) { - if (network_->isHierarchical(src)) { - FindClkHpinDisables visitor1(clk, network_, this); - visitHpinDrvrLoads(src, network_, &visitor1); - PinSeq loads, drvrs; - PinSet visited_drvrs(network_); - FindNetDrvrLoads visitor2(nullptr, visited_drvrs, loads, drvrs, network_); - network_->visitConnectedPins(src, visitor2); - - // Disable fanouts from the src driver pins that do - // not go thru the hierarchical src pin. - for (const Pin *drvr : drvrs) { - for (const Pin *load : loads) { - if (!visitor1.drvrLoadExists(drvr, load)) - makeClkHpinDisable(clk, drvr, load); - } - } - } + if (network_->isHierarchical(src)) { + FindClkHpinDisables visitor1(clk, network_, this); + visitHpinDrvrLoads(src, network_, &visitor1); + PinSeq loads, drvrs; + PinSet visited_drvrs(network_); + FindNetDrvrLoads visitor2(nullptr, visited_drvrs, loads, drvrs, network_); + network_->visitConnectedPins(src, visitor2); + + // Disable fanouts from the src driver pins that do + // not go thru the hierarchical src pin. + for (const Pin *drvr : drvrs) { + for (const Pin *load : loads) { + if (!visitor1.drvrLoadExists(drvr, load)) + makeClkHpinDisable(clk, drvr, load); + } + } + } } } clk_hpin_disables_valid_ = true; @@ -1417,11 +1382,11 @@ Sdc::ensureClkHpinDisables() void Sdc::makeClkHpinDisable(const Clock *clk, - const Pin *drvr, - const Pin *load) + const Pin *drvr, + const Pin *load) { ClkHpinDisable probe(clk, drvr, load); - if (!clk_hpin_disables_.hasKey(&probe)) { + if (!clk_hpin_disables_.contains(&probe)) { ClkHpinDisable *disable = new ClkHpinDisable(clk, drvr, load); clk_hpin_disables_.insert(disable); } @@ -1439,12 +1404,12 @@ Sdc::clkHpinDisablesInvalid() // Check for disable by hierarchical clock pin between driver and load. bool Sdc::clkDisabledByHpinThru(const Clock *clk, - const Pin *from_pin, - const Pin *to_pin) + const Pin *from_pin, + const Pin *to_pin) const { - if (clk->leafPins().hasKey(from_pin)) { + if (clk->leafPins().contains(from_pin)) { ClkHpinDisable probe(clk, from_pin, to_pin); - return clk_hpin_disables_.hasKey(&probe); + return clk_hpin_disables_.contains(&probe); } else return false; @@ -1479,16 +1444,16 @@ Sdc::removePropagatedClock(Pin *pin) } bool -Sdc::isPropagatedClock(const Pin *pin) +Sdc::isPropagatedClock(const Pin *pin) const { - return propagated_clk_pins_.hasKey(pin); + return propagated_clk_pins_.contains(pin); } void Sdc::setClockSlew(Clock *clk, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) { clk->setSlew(rf, min_max, slew); } @@ -1499,18 +1464,50 @@ Sdc::removeClockSlew(Clock *clk) clk->removeSlew(); } +class MakeClkLatencyEdge : public HierPinThruVisitor +{ +public: + MakeClkLatencyEdge(ClockLatency *latency, + EdgeClockLatencyMap &edge_clk_latency_map); + +private: + void visit(const Pin *drvr, + const Pin *load) override; + + ClockLatency *latency_; + EdgeClockLatencyMap &edge_clk_latency_map_; +}; + +MakeClkLatencyEdge::MakeClkLatencyEdge(ClockLatency *latency, + EdgeClockLatencyMap &edge_clk_latency_map) : + latency_(latency), + edge_clk_latency_map_(edge_clk_latency_map) +{ +} + +void +MakeClkLatencyEdge::visit(const Pin *drvr, + const Pin *load) +{ + edge_clk_latency_map_[{drvr, load}] = latency_; +} + void Sdc::setClockLatency(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float delay) + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float delay) { ClockLatency probe(clk, pin); - ClockLatency *latency = clk_latencies_.findKey(&probe); + ClockLatency *latency = findKey(clk_latencies_, &probe); if (latency == nullptr) { latency = new ClockLatency(clk, pin); clk_latencies_.insert(latency); + if (pin && network_->isHierarchical(pin)) { + MakeClkLatencyEdge visitor(latency, edge_clk_latency_map_); + visitDrvrLoadsThruHierPin(pin, network_, &visitor); + } } latency->setDelay(rf, min_max, delay); @@ -1521,12 +1518,41 @@ Sdc::setClockLatency(Clock *clk, removePropagatedClock(pin); } +ClockLatency * +Sdc::clockLatency(Edge *edge) const +{ + PinPair pins(edge->from(graph_)->pin(), edge->to(graph_)->pin()); + auto itr = edge_clk_latency_map_.find(pins); + if (itr == edge_clk_latency_map_.end()) + return nullptr; + else + return itr->second; +} + +void +Sdc::clockLatency(Edge *edge, + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const + +{ + ClockLatency *latencies = clockLatency(edge); + if (latencies) + latencies->delay(rf, min_max, latency, exists); + else { + latency = 0.0; + exists = false; + } +} + void Sdc::removeClockLatency(const Clock *clk, - const Pin *pin) + const Pin *pin) { ClockLatency probe(clk, pin); - ClockLatency *latency = clk_latencies_.findKey(&probe); + ClockLatency *latency = findKey(clk_latencies_, &probe); if (latency) deleteClockLatency(latency); } @@ -1557,29 +1583,29 @@ bool Sdc::hasClockLatency(const Pin *pin) const { ClockLatency probe(nullptr, pin); - return clk_latencies_.hasKey(&probe); + return clk_latencies_.contains(&probe); } void Sdc::clockLatency(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const { latency = 0.0; exists = false; if (pin && clk) { ClockLatency probe(clk, pin); - ClockLatency *latencies = clk_latencies_.findKey(&probe); + ClockLatency *latencies = findKey(clk_latencies_, &probe); if (latencies) latencies->delay(rf, min_max, latency, exists); } if (!exists) { ClockLatency probe(nullptr, pin); - ClockLatency *latencies = clk_latencies_.findKey(&probe); + ClockLatency *latencies = findKey(clk_latencies_, &probe); if (latencies) latencies->delay(rf, min_max, latency, exists); } @@ -1587,38 +1613,38 @@ Sdc::clockLatency(const Clock *clk, void Sdc::clockLatency(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const { latency = 0.0; exists = false; ClockLatency probe(clk, nullptr); - ClockLatency *latencies = clk_latencies_.findKey(&probe); + ClockLatency *latencies = findKey(clk_latencies_, &probe); if (latencies) latencies->delay(rf, min_max, latency, exists); } float Sdc::clockLatency(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max) const + const RiseFall *rf, + const MinMax *min_max) const { float latency; bool exists; clockLatency(clk, rf, min_max, - latency, exists); + latency, exists); return latency; } void Sdc::setClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold, - float uncertainty) + const SetupHoldAll *setup_hold, + float uncertainty) { - ClockUncertainties *uncertainties = pin_clk_uncertainty_map_.findKey(pin); + ClockUncertainties *uncertainties = findKey(pin_clk_uncertainty_map_, pin); if (uncertainties == nullptr) { uncertainties = new ClockUncertainties; pin_clk_uncertainty_map_[pin] = uncertainties; @@ -1628,9 +1654,9 @@ Sdc::setClockUncertainty(Pin *pin, void Sdc::removeClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold) { - ClockUncertainties *uncertainties = pin_clk_uncertainty_map_.findKey(pin); + ClockUncertainties *uncertainties = findKey(pin_clk_uncertainty_map_, pin); if (uncertainties) { uncertainties->removeValue(setup_hold); if (uncertainties->empty()) { @@ -1640,19 +1666,19 @@ Sdc::removeClockUncertainty(Pin *pin, } } -ClockUncertainties * -Sdc::clockUncertainties(const Pin *pin) +const ClockUncertainties * +Sdc::clockUncertainties(const Pin *pin) const { - return pin_clk_uncertainty_map_.findKey(pin); + return findKey(pin_clk_uncertainty_map_, pin); } void Sdc::clockUncertainty(const Pin *pin, - const SetupHold *setup_hold, - float &uncertainty, - bool &exists) + const SetupHold *setup_hold, + float &uncertainty, + bool &exists) { - ClockUncertainties *uncertainties = clockUncertainties(pin); + const ClockUncertainties *uncertainties = clockUncertainties(pin); if (uncertainties) uncertainties->value(setup_hold, uncertainty, exists); else { @@ -1663,19 +1689,19 @@ Sdc::clockUncertainty(const Pin *pin, void Sdc::clockUncertainty(const Clock *src_clk, - const RiseFall *src_rf, - const Clock *tgt_clk, - const RiseFall *tgt_rf, - const SetupHold *setup_hold, - float &uncertainty, - bool &exists) + const RiseFall *src_rf, + const Clock *tgt_clk, + const RiseFall *tgt_rf, + const SetupHold *setup_hold, + float &uncertainty, + bool &exists) const { InterClockUncertainty probe(src_clk, tgt_clk); InterClockUncertainty *uncertainties = - inter_clk_uncertainties_.findKey(&probe); + findKey(inter_clk_uncertainties_, &probe); if (uncertainties) uncertainties->uncertainty(src_rf, tgt_rf, setup_hold, - uncertainty, exists); + uncertainty, exists); else { uncertainty = 0.0; exists = false; @@ -1684,15 +1710,15 @@ Sdc::clockUncertainty(const Clock *src_clk, void Sdc::setClockUncertainty(Clock *from_clk, - const RiseFallBoth *from_rf, - Clock *to_clk, - const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold, - float uncertainty) + const RiseFallBoth *from_rf, + Clock *to_clk, + const RiseFallBoth *to_rf, + const SetupHoldAll *setup_hold, + float uncertainty) { InterClockUncertainty probe(from_clk, to_clk); InterClockUncertainty *uncertainties = - inter_clk_uncertainties_.findKey(&probe); + findKey(inter_clk_uncertainties_, &probe); if (uncertainties == nullptr) { uncertainties = new InterClockUncertainty(from_clk, to_clk); inter_clk_uncertainties_.insert(uncertainties); @@ -1702,14 +1728,14 @@ Sdc::setClockUncertainty(Clock *from_clk, void Sdc::removeClockUncertainty(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) { InterClockUncertainty probe(from_clk, to_clk); InterClockUncertainty *uncertainties = - inter_clk_uncertainties_.findKey(&probe); + findKey(inter_clk_uncertainties_, &probe); if (uncertainties) { uncertainties->removeUncertainty(from_rf, to_rf, setup_hold); if (uncertainties->empty()) { @@ -1733,7 +1759,7 @@ Sdc::deleteInterClockUncertaintiesReferencing(Clock *clk) iter != inter_clk_uncertainties_.cend(); ) { InterClockUncertainty *uncertainties = *iter; if (uncertainties->src() == clk - || uncertainties->target() == clk) { + || uncertainties->target() == clk) { iter = inter_clk_uncertainties_.erase(iter); delete uncertainties; } @@ -1746,14 +1772,14 @@ Sdc::deleteInterClockUncertaintiesReferencing(Clock *clk) void Sdc::setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay) + const Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) { ClockInsertion probe(clk, pin); - ClockInsertion *insertion = clk_insertions_.findKey(&probe); + ClockInsertion *insertion = findKey(clk_insertions_, &probe); if (insertion == nullptr) { insertion = new ClockInsertion(clk, pin); clk_insertions_.insert(insertion); @@ -1763,14 +1789,14 @@ Sdc::setClockInsertion(const Clock *clk, void Sdc::setClockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - float delay) + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + float delay) { ClockInsertion probe(clk, pin); - ClockInsertion *insertion = clk_insertions_.findKey(&probe); + ClockInsertion *insertion = findKey(clk_insertions_, &probe); if (insertion == nullptr) { insertion = new ClockInsertion(clk, pin); clk_insertions_.insert(insertion); @@ -1780,10 +1806,10 @@ Sdc::setClockInsertion(const Clock *clk, void Sdc::removeClockInsertion(const Clock *clk, - const Pin *pin) + const Pin *pin) { ClockInsertion probe(clk, pin); - ClockInsertion *insertion = clk_insertions_.findKey(&probe); + ClockInsertion *insertion = findKey(clk_insertions_, &probe); if (insertion != nullptr) deleteClockInsertion(insertion); } @@ -1819,9 +1845,9 @@ Sdc::deleteClockInsertionsReferencing(Clock *clk) float Sdc::clockInsertion(const Clock *clk, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late) const + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late) const { float insertion; bool exists; @@ -1833,31 +1859,31 @@ bool Sdc::hasClockInsertion(const Pin *pin) const { ClockInsertion probe(nullptr, pin); - return clk_insertions_.hasKey(&probe); + return clk_insertions_.contains(&probe); } void Sdc::clockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - // Return values. - float &insertion, - bool &exists) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, + bool &exists) const { ClockInsertion *insert = nullptr; if (clk && pin) { ClockInsertion probe(clk, pin); - insert = clk_insertions_.findKey(&probe); + insert = findKey(clk_insertions_, &probe); } if (insert == nullptr && pin) { ClockInsertion probe(nullptr, pin); - insert = clk_insertions_.findKey(&probe); + insert = findKey(clk_insertions_, &probe); } if (insert == nullptr && clk) { ClockInsertion probe(clk, nullptr); - insert = clk_insertions_.findKey(&probe); + insert = findKey(clk_insertions_, &probe); } if (insert) @@ -1916,24 +1942,24 @@ ClockInsertionkLess::operator()(const ClockInsertion *insert1, ClockGroups * Sdc::makeClockGroups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment) + 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(); else { - ClockGroups *groups = clk_groups_name_map_.findKey(name); + ClockGroups *groups = findKey(clk_groups_name_map_, name); if (groups) removeClockGroups(groups); } ClockGroups *groups = new ClockGroups(name, logically_exclusive, - physically_exclusive, - asynchronous, allow_paths, comment); + physically_exclusive, + asynchronous, allow_paths, comment); clk_groups_name_map_[groups->name()] = groups; stringDelete(gen_name); return groups; @@ -1949,13 +1975,13 @@ Sdc::makeClockGroupsName() i++; stringDelete(name); name = stringPrint("group%d", i); - } while (clk_groups_name_map_.hasKey(name)); + } while (clk_groups_name_map_.contains(name)); return name; } void Sdc::makeClockGroup(ClockGroups *clk_groups, - ClockSet *clks) + ClockSet *clks) { clk_groups->makeClockGroup(clks); } @@ -1973,7 +1999,7 @@ void Sdc::makeClkGroupExclusions(ClockGroups *clk_groups) { if (!(clk_groups->asynchronous() - && clk_groups->allowPaths())) { + && clk_groups->allowPaths())) { ClockGroupSet *groups = clk_groups->groups(); if (groups->size() == 1) makeClkGroupExclusions1(groups); @@ -1987,13 +2013,12 @@ Sdc::makeClkGroupExclusions(ClockGroups *clk_groups) void Sdc::makeClkGroupExclusions1(ClockGroupSet *groups) { - ClockGroupSet::Iterator group_iter1(groups); - ClockGroup *group1 = group_iter1.next(); - for (auto clk1 : *group1) { + ClockGroup *group1 = *groups->begin(); + for (Clock *clk1 : *group1) { for (Clock *clk2 : clocks_) { if (clk2 != clk1 - && !group1->hasKey(clk2)) - clk_group_exclusions_.insert(ClockPair(clk1, clk2)); + && !group1->contains(clk2)) + clk_group_exclusions_.insert(ClockPair(clk1, clk2)); } } makeClkGroupSame(group1); @@ -2005,14 +2030,14 @@ Sdc::makeClkGroupExclusions(ClockGroupSet *groups) for (auto group1 : *groups) { for (auto group2 : *groups) { if (group1 != group2) { - for (auto clk1 : *group1) { - for (auto clk2 : *group2) { - // ClockPair is symmetric so only add one clk1/clk2 pair. - if (clk1->index() < clk2->index()) { - clk_group_exclusions_.insert(ClockPair(clk1, clk2)); - } - } - } + for (auto clk1 : *group1) { + for (auto clk2 : *group2) { + // ClockPair is symmetric so only add one clk1/clk2 pair. + if (clk1->index() < clk2->index()) { + clk_group_exclusions_.insert(ClockPair(clk1, clk2)); + } + } + } } } makeClkGroupSame(group1); @@ -2025,9 +2050,9 @@ Sdc::makeClkGroupSame(ClockGroup *group) for (auto clk1 : *group) { for (auto clk2 : *group) { if (clk1->index() <= clk2->index()) { - ClockPair clk_pair(clk1, clk2); - if (!clk_group_same_.hasKey(clk_pair)) - clk_group_same_.insert(clk_pair); + ClockPair clk_pair(clk1, clk2); + if (!clk_group_same_.contains(clk_pair)) + clk_group_same_.insert(clk_pair); } } } @@ -2042,11 +2067,11 @@ Sdc::clearClkGroupExclusions() bool Sdc::sameClockGroup(const Clock *clk1, - const Clock *clk2) + const Clock *clk2) const { if (clk1 && clk2) { ClockPair clk_pair(clk1, clk2); - bool excluded = clk_group_exclusions_.hasKey(clk_pair); + bool excluded = clk_group_exclusions_.contains(clk_pair); return !excluded; } else @@ -2055,16 +2080,16 @@ Sdc::sameClockGroup(const Clock *clk1, bool Sdc::sameClockGroupExplicit(const Clock *clk1, - const Clock *clk2) + const Clock *clk2) { ClockPair clk_pair(clk1, clk2); - return clk_group_same_.hasKey(clk_pair); + return clk_group_same_.contains(clk_pair); } void Sdc::removeClockGroups(const char *name) { - ClockGroups *clk_groups = clk_groups_name_map_.findKey(name); + ClockGroups *clk_groups = findKey(clk_groups_name_map_, name); if (clk_groups) removeClockGroups(clk_groups); } @@ -2073,14 +2098,14 @@ void Sdc::removeClockGroupsLogicallyExclusive(const char *name) { if (name) { - ClockGroups *groups = clk_groups_name_map_.findKey(name); + ClockGroups *groups = findKey(clk_groups_name_map_, name); if (groups && groups->logicallyExclusive()) removeClockGroups(groups); } else { for (const auto [name, groups] : clk_groups_name_map_) { if (groups->logicallyExclusive()) - removeClockGroups(groups); + removeClockGroups(groups); } } } @@ -2089,14 +2114,14 @@ void Sdc::removeClockGroupsPhysicallyExclusive(const char *name) { if (name) { - ClockGroups *groups = clk_groups_name_map_.findKey(name); + ClockGroups *groups = findKey(clk_groups_name_map_, name); if (groups && groups->physicallyExclusive()) removeClockGroups(groups); } else { for (const auto [name, groups] : clk_groups_name_map_) { if (groups->physicallyExclusive()) - removeClockGroups(groups); + removeClockGroups(groups); } } } @@ -2105,14 +2130,14 @@ void Sdc::removeClockGroupsAsynchronous(const char *name) { if (name) { - ClockGroups *groups = clk_groups_name_map_.findKey(name); + ClockGroups *groups = findKey(clk_groups_name_map_, name); if (groups && groups->asynchronous()) removeClockGroups(groups); } else { for (const auto [name, groups] : clk_groups_name_map_) { if (groups->asynchronous()) - removeClockGroups(groups); + removeClockGroups(groups); } } } @@ -2139,8 +2164,8 @@ Sdc::clockGroupsDeleteClkRefs(Clock *clk) void Sdc::setClockSense(PinSet *pins, - ClockSet *clks, - ClockSense sense) + ClockSet *clks, + ClockSense sense) { if (clks && clks->empty()) { delete clks; @@ -2149,7 +2174,7 @@ Sdc::setClockSense(PinSet *pins, for (const Pin *pin : *pins) { if (clks) { for (const Clock *clk : *clks) - setClockSense(pin, clk, sense); + setClockSense(pin, clk, sense); } else setClockSense(pin, nullptr, sense); @@ -2164,7 +2189,7 @@ Sdc::setClockSense(const Pin *pin, ClockSense sense) { PinClockPair probe(pin, clk); - if (clk_sense_map_.hasKey(probe)) + if (clk_sense_map_.contains(probe)) clk_sense_map_[probe] = sense; else { PinClockPair pin_clk(pin, clk); @@ -2174,15 +2199,15 @@ Sdc::setClockSense(const Pin *pin, bool Sdc::clkStopPropagation(const Pin *pin, - const Clock *clk) const + const Clock *clk) const { PinClockPair pin_clk(pin, clk); ClockSense sense; bool exists; - clk_sense_map_.findKey(pin_clk, sense, exists); + findKeyValue(clk_sense_map_, pin_clk, sense, exists); if (!exists) { PinClockPair pin_clk1(pin, nullptr); - clk_sense_map_.findKey(pin_clk1, sense, exists); + findKeyValue(clk_sense_map_, pin_clk1, sense, exists); } return exists && sense == ClockSense::stop; @@ -2190,32 +2215,32 @@ Sdc::clkStopPropagation(const Pin *pin, bool Sdc::clkStopSense(const Pin *to_pin, - const Clock *clk, - const RiseFall *from_rf, - const RiseFall *to_rf) const + const Clock *clk, + const RiseFall *from_rf, + const RiseFall *to_rf) const { PinClockPair pin_clk(to_pin, clk); ClockSense sense; bool exists; - clk_sense_map_.findKey(pin_clk, sense, exists); + findKeyValue(clk_sense_map_, pin_clk, sense, exists); if (!exists) { PinClockPair pin(to_pin, nullptr); - clk_sense_map_.findKey(pin, sense, exists); + findKeyValue(clk_sense_map_, pin, sense, exists); } return exists && (sense == ClockSense::stop - || (sense == ClockSense::positive - && from_rf != to_rf) - || (sense == ClockSense::negative - && from_rf == to_rf)); + || (sense == ClockSense::positive + && from_rf != to_rf) + || (sense == ClockSense::negative + && from_rf == to_rf)); } bool Sdc::clkStopPropagation(const Clock *clk, - const Pin *from_pin, - const RiseFall *from_rf, - const Pin *to_pin, - const RiseFall *to_rf) const + const Pin *from_pin, + const RiseFall *from_rf, + const Pin *to_pin, + const RiseFall *to_rf) const { return clkStopPropagation(from_pin, clk) || clkStopSense(to_pin, clk, from_rf, to_rf); @@ -2228,25 +2253,23 @@ PinClockPairLess::PinClockPairLess(const Network *network) : bool PinClockPairLess::operator()(const PinClockPair &pin_clk1, - const PinClockPair &pin_clk2) const + const PinClockPair &pin_clk2) const { - const Pin *pin1 = pin_clk1.first; - const Pin *pin2 = pin_clk2.first; - const Clock *clk1 = pin_clk1.second; - const Clock *clk2 = pin_clk2.second; + const auto& [pin1, clk1] = pin_clk1; + const auto& [pin2, clk2] = pin_clk2; return pin1 < pin2 || (pin1 == pin2 - && ((clk1 == nullptr && clk2) - || (clk1 && clk2 - && clk1->index() < clk2->index()))); + && ((clk1 == nullptr && clk2) + || (clk1 && clk2 + && clk1->index() < clk2->index()))); } //////////////////////////////////////////////////////////////// void Sdc::setClockGatingCheck(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const SetupHold *setup_hold, + float margin) { if (clk_gating_check_ == nullptr) clk_gating_check_ = new ClockGatingCheck; @@ -2255,11 +2278,11 @@ Sdc::setClockGatingCheck(const RiseFallBoth *rf, void Sdc::setClockGatingCheck(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin) { - ClockGatingCheck *check = clk_gating_check_map_.findKey(clk); + ClockGatingCheck *check = findKey(clk_gating_check_map_, clk); if (check == nullptr) { check = new ClockGatingCheck(); clk_gating_check_map_[clk] = check; @@ -2269,12 +2292,12 @@ Sdc::setClockGatingCheck(Clock *clk, void Sdc::setClockGatingCheck(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) { - ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst); + ClockGatingCheck *check = findKey(inst_clk_gating_check_map_, inst); if (check == nullptr) { check = new ClockGatingCheck(); inst_clk_gating_check_map_[inst] = check; @@ -2285,12 +2308,12 @@ Sdc::setClockGatingCheck(Instance *inst, void Sdc::setClockGatingCheck(const Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) { - ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(pin); + ClockGatingCheck *check = findKey(pin_clk_gating_check_map_, pin); if (check == nullptr) { check = new ClockGatingCheck(); pin_clk_gating_check_map_[pin] = check; @@ -2301,11 +2324,11 @@ Sdc::setClockGatingCheck(const Pin *pin, void Sdc::clockGatingMarginEnablePin(const Pin *enable_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, float &margin) + const RiseFall *enable_rf, + const SetupHold *setup_hold, + bool &exists, float &margin) const { - ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(enable_pin); + ClockGatingCheck *check = findKey(pin_clk_gating_check_map_, enable_pin); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else @@ -2314,12 +2337,12 @@ Sdc::clockGatingMarginEnablePin(const Pin *enable_pin, void Sdc::clockGatingMarginInstance(Instance *inst, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, - float &margin) + const RiseFall *enable_rf, + const SetupHold *setup_hold, + bool &exists, + float &margin) const { - ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst); + ClockGatingCheck *check = findKey(inst_clk_gating_check_map_, inst); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else @@ -2328,12 +2351,12 @@ Sdc::clockGatingMarginInstance(Instance *inst, void Sdc::clockGatingMarginClkPin(const Pin *clk_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, - float &margin) + const RiseFall *enable_rf, + const SetupHold *setup_hold, + bool &exists, + float &margin) const { - ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(clk_pin); + ClockGatingCheck *check = findKey(pin_clk_gating_check_map_, clk_pin); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else @@ -2342,12 +2365,12 @@ Sdc::clockGatingMarginClkPin(const Pin *clk_pin, void Sdc::clockGatingMarginClk(const Clock *clk, - const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, - float &margin) + const RiseFall *enable_rf, + const SetupHold *setup_hold, + bool &exists, + float &margin) const { - ClockGatingCheck *check = clk_gating_check_map_.findKey(clk); + ClockGatingCheck *check = findKey(clk_gating_check_map_, clk); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else @@ -2356,9 +2379,9 @@ Sdc::clockGatingMarginClk(const Clock *clk, void Sdc::clockGatingMargin(const RiseFall *enable_rf, - const SetupHold *setup_hold, - bool &exists, - float &margin) + const SetupHold *setup_hold, + bool &exists, + float &margin) const { if (clk_gating_check_) clk_gating_check_->margins()->value(enable_rf, setup_hold, margin, exists); @@ -2368,17 +2391,17 @@ Sdc::clockGatingMargin(const RiseFall *enable_rf, LogicValue Sdc::clockGatingActiveValue(const Pin *clk_pin, - const Pin *enable_pin) + const Pin *enable_pin) const { ClockGatingCheck *check; - check = pin_clk_gating_check_map_.findKey(enable_pin); + check = findKey(pin_clk_gating_check_map_, enable_pin); if (check) return check->activeValue(); Instance *inst = network_->instance(enable_pin); - check = inst_clk_gating_check_map_.findKey(inst); + check = findKey(inst_clk_gating_check_map_, inst); if (check) return check->activeValue(); - check = pin_clk_gating_check_map_.findKey(clk_pin); + check = findKey(pin_clk_gating_check_map_, clk_pin); if (check) return check->activeValue(); return LogicValue::unknown; @@ -2389,7 +2412,7 @@ Sdc::clockGatingActiveValue(const Pin *clk_pin, // Determine cycle accounting "on demand". CycleAccting * Sdc::cycleAccting(const ClockEdge *src, - const ClockEdge *tgt) + const ClockEdge *tgt) { LockGuard lock(cycle_acctings_lock_); return cycle_acctings_.cycleAccting(src, tgt); @@ -2411,29 +2434,29 @@ Sdc::clearCycleAcctings() void Sdc::setDataCheck(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, - float margin) + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin) { DataCheck *check = nullptr; - DataCheckSet *checks = data_checks_from_map_.findKey(from); + DataCheckSet *checks = findKey(data_checks_from_map_, from); if (checks == nullptr) { checks = new DataCheckSet(DataCheckLess(network_)); data_checks_from_map_[from] = checks; } else { DataCheck probe(from, to, clk); - check = checks->findKey(&probe); + check = findKey(*checks, &probe); } if (check == nullptr) check = new DataCheck(from, to, clk); check->setMargin(from_rf, to_rf, setup_hold, margin); checks->insert(check); - checks = data_checks_to_map_.findKey(to); + checks = findKey(data_checks_to_map_, to); if (checks == nullptr) { checks = new DataCheckSet(DataCheckLess(network_)); data_checks_to_map_[to] = checks; @@ -2443,24 +2466,24 @@ Sdc::setDataCheck(Pin *from, void Sdc::removeDataCheck(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) { DataCheck probe(from, to, clk); - DataCheckSet *checks = data_checks_from_map_.findKey(from); + DataCheckSet *checks = findKey(data_checks_from_map_, from); if (checks) { - DataCheck *check = checks->findKey(&probe); + DataCheck *check = findKey(*checks, &probe); if (check) { check->removeMargin(from_rf, to_rf, setup_hold); if (check->empty()) { - checks->erase(check); - checks = data_checks_to_map_.findKey(to); - if (checks) - checks->erase(check); - delete check; + checks->erase(check); + checks = findKey(data_checks_to_map_, to); + if (checks) + checks->erase(check); + delete check; } } } @@ -2469,27 +2492,27 @@ Sdc::removeDataCheck(Pin *from, DataCheckSet * Sdc::dataChecksFrom(const Pin *from) const { - return data_checks_from_map_.findKey(from); + return findKey(data_checks_from_map_, from); } DataCheckSet * Sdc::dataChecksTo(const Pin *to) const { - return data_checks_to_map_.findKey(to); + return findKey(data_checks_to_map_, to); } //////////////////////////////////////////////////////////////// void Sdc::setLatchBorrowLimit(const Pin *pin, - float limit) + float limit) { pin_latch_borrow_limit_map_[pin] = limit; } void Sdc::setLatchBorrowLimit(const Instance *inst, - float limit) + float limit) { inst_latch_borrow_limit_map_[inst] = limit; } @@ -2509,20 +2532,20 @@ Sdc::deleteLatchBorrowLimitsReferencing(Clock *clk) void Sdc::latchBorrowLimit(const Pin *data_pin, - const Pin *enable_pin, - const Clock *clk, - // Return values. - float &limit, - bool &exists) + const Pin *enable_pin, + const Clock *clk, + // Return values. + float &limit, + bool &exists) { - pin_latch_borrow_limit_map_.findKey(data_pin, limit, exists); + findKeyValue(pin_latch_borrow_limit_map_, data_pin, limit, exists); if (!exists) { - pin_latch_borrow_limit_map_.findKey(enable_pin, limit, exists); + findKeyValue(pin_latch_borrow_limit_map_, enable_pin, limit, exists); if (!exists) { Instance *inst = network_->instance(data_pin); - inst_latch_borrow_limit_map_.findKey(inst, limit, exists); + findKeyValue(inst_latch_borrow_limit_map_, inst, limit, exists); if (!exists) - clk_latch_borrow_limit_map_.findKey(clk, limit, exists); + findKeyValue(clk_latch_borrow_limit_map_, clk, limit, exists); } } } @@ -2531,7 +2554,7 @@ Sdc::latchBorrowLimit(const Pin *data_pin, void Sdc::setMinPulseWidth(const RiseFallBoth *rf, - float min_width) + float min_width) { for (auto rf1 : rf->range()) min_pulse_width_.setValue(rf1, min_width); @@ -2539,10 +2562,10 @@ Sdc::setMinPulseWidth(const RiseFallBoth *rf, void Sdc::setMinPulseWidth(const Pin *pin, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - RiseFallValues *widths = pin_min_pulse_width_map_.findKey(pin); + RiseFallValues *widths = findKey(pin_min_pulse_width_map_, pin); if (widths == nullptr) { widths = new RiseFallValues; pin_min_pulse_width_map_[pin] = widths; @@ -2553,10 +2576,10 @@ Sdc::setMinPulseWidth(const Pin *pin, void Sdc::setMinPulseWidth(const Instance *inst, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - RiseFallValues *widths = inst_min_pulse_width_map_.findKey(inst); + RiseFallValues *widths = findKey(inst_min_pulse_width_map_, inst); if (widths == nullptr) { widths = new RiseFallValues; inst_min_pulse_width_map_[inst] = widths; @@ -2567,10 +2590,10 @@ Sdc::setMinPulseWidth(const Instance *inst, void Sdc::setMinPulseWidth(const Clock *clk, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - RiseFallValues *widths = clk_min_pulse_width_map_.findKey(clk); + RiseFallValues *widths = findKey(clk_min_pulse_width_map_, clk); if (widths == nullptr) { widths = new RiseFallValues; clk_min_pulse_width_map_[clk] = widths; @@ -2581,21 +2604,21 @@ Sdc::setMinPulseWidth(const Clock *clk, void Sdc::minPulseWidth(const Pin *pin, - const Clock *clk, - const RiseFall *hi_low, - float &min_width, - bool &exists) const + const Clock *clk, + const RiseFall *hi_low, + float &min_width, + bool &exists) const { - RiseFallValues *widths = pin_min_pulse_width_map_.findKey(pin); + RiseFallValues *widths = findKey(pin_min_pulse_width_map_, pin); if (widths) widths->value(hi_low, min_width, exists); else { if (pin) { const Instance *inst = network_->instance(pin); - widths = inst_min_pulse_width_map_.findKey(inst); + widths = findKey(inst_min_pulse_width_map_, inst); } if (widths == nullptr) - widths = clk_min_pulse_width_map_.findKey(clk); + widths = findKey(clk_min_pulse_width_map_, clk); if (widths) widths->value(hi_low, min_width, exists); else @@ -2606,7 +2629,7 @@ Sdc::minPulseWidth(const Pin *pin, void Sdc::deleteMinPulseWidthReferencing(Clock *clk) { - RiseFallValues *widths = clk_min_pulse_width_map_.findKey(clk); + RiseFallValues *widths = findKey(clk_min_pulse_width_map_, clk); if (widths) { delete widths; clk_min_pulse_width_map_.erase(clk); @@ -2616,22 +2639,22 @@ Sdc::deleteMinPulseWidthReferencing(Clock *clk) //////////////////////////////////////////////////////////////// InputDrive * -Sdc::findInputDrive(Port *port) +Sdc::findInputDrive(Port *port) const { - return input_drive_map_.findKey(port); + return findKey(input_drive_map_, port); } void Sdc::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, - float delay) + 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) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; InputDelay *input_delay = findInputDelay(pin, clk_edge); @@ -2648,7 +2671,7 @@ Sdc::setInputDelay(const Pin *pin, } if (ref_pin) { - InputDelaySet *ref_inputs = input_delay_ref_pin_map_.findKey(ref_pin); + InputDelaySet *ref_inputs = findKey(input_delay_ref_pin_map_, ref_pin); if (ref_inputs == nullptr) { ref_inputs = new InputDelaySet; input_delay_ref_pin_map_[ref_pin] = ref_inputs; @@ -2663,12 +2686,12 @@ Sdc::setInputDelay(const Pin *pin, InputDelay * Sdc::makeInputDelay(const Pin *pin, - const ClockEdge *clk_edge) + const ClockEdge *clk_edge) { InputDelay *input_delay = new InputDelay(pin, clk_edge, input_delay_index_++, - network_); + network_); input_delays_.insert(input_delay); - InputDelaySet *inputs = input_delay_pin_map_.findKey(pin); + InputDelaySet *inputs = findKey(input_delay_pin_map_, pin); if (inputs == nullptr) { inputs = new InputDelaySet; input_delay_pin_map_[pin] = inputs; @@ -2686,8 +2709,8 @@ Sdc::makeInputDelay(const Pin *pin, if (!network_->isTopLevelPort(lpin)) { InputDelaySet *internal_inputs = input_delay_internal_pin_map_[lpin]; if (internal_inputs == nullptr) { - internal_inputs = new InputDelaySet; - input_delay_internal_pin_map_[pin] = internal_inputs; + internal_inputs = new InputDelaySet; + input_delay_internal_pin_map_[pin] = internal_inputs; } internal_inputs->insert(input_delay); } @@ -2697,13 +2720,13 @@ Sdc::makeInputDelay(const Pin *pin, InputDelay * Sdc::findInputDelay(const Pin *pin, - const ClockEdge *clk_edge) + const ClockEdge *clk_edge) { - InputDelaySet *inputs = input_delay_pin_map_.findKey(pin); + InputDelaySet *inputs = findKey(input_delay_pin_map_, pin); if (inputs) { for (InputDelay *input_delay : *inputs) { if (input_delay->clkEdge() == clk_edge) - return input_delay; + return input_delay; } } return nullptr; @@ -2711,10 +2734,10 @@ Sdc::findInputDelay(const Pin *pin, void Sdc::removeInputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max) + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; InputDelay *input_delay = findInputDelay(pin, clk_edge); @@ -2728,50 +2751,56 @@ Sdc::removeInputDelay(const Pin *pin, void Sdc::deleteInputDelays(const Pin *pin, - InputDelay *except) + InputDelay *except) { InputDelaySet *input_delays = input_delay_pin_map_[pin]; - InputDelaySet::Iterator iter(input_delays); - while (iter.hasNext()) { - InputDelay *input_delay = iter.next(); - if (input_delay != except) + for (auto itr = input_delays->begin(); itr != input_delays->end(); /* no incr */) { + InputDelay *input_delay = *itr; + if (input_delay != except) { + itr = input_delays->erase(itr); deleteInputDelay(input_delay); + } + else + itr++; } } InputDelaySet * Sdc::refPinInputDelays(const Pin *ref_pin) const { - return input_delay_ref_pin_map_.findKey(ref_pin); + return findKey(input_delay_ref_pin_map_, ref_pin); } InputDelaySet * -Sdc::inputDelaysLeafPin(const Pin *leaf_pin) +Sdc::inputDelaysLeafPin(const Pin *leaf_pin) const { - return input_delay_leaf_pin_map_.findKey(leaf_pin); + return findKey(input_delay_leaf_pin_map_, leaf_pin); } bool Sdc::hasInputDelay(const Pin *leaf_pin) const { - InputDelaySet *input_delays = input_delay_leaf_pin_map_.findKey(leaf_pin); + InputDelaySet *input_delays = findKey(input_delay_leaf_pin_map_, leaf_pin); return input_delays && !input_delays->empty(); } bool Sdc::isInputDelayInternal(const Pin *pin) const { - return input_delay_internal_pin_map_.hasKey(pin); + return input_delay_internal_pin_map_.contains(pin); } void Sdc::deleteInputDelaysReferencing(const Clock *clk) { - InputDelaySet::Iterator iter(input_delays_); - while (iter.hasNext()) { - InputDelay *input_delay = iter.next(); - if (input_delay->clock() == clk) + for (auto itr = input_delays_.begin(); itr != input_delays_.end(); ) { + InputDelay *input_delay = *itr; + if (input_delay->clock() == clk) { + itr = input_delays_.erase(itr); deleteInputDelay(input_delay); + } + else + itr++; } } @@ -2813,15 +2842,15 @@ Sdc::swapPortDelays(Sdc *sdc1, void Sdc::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, - float delay) + 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) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; OutputDelay *output_delay = findOutputDelay(pin, clk_edge); @@ -2838,7 +2867,7 @@ Sdc::setOutputDelay(const Pin *pin, } if (ref_pin) { - OutputDelaySet *ref_outputs = output_delay_ref_pin_map_.findKey(ref_pin); + OutputDelaySet *ref_outputs = findKey(output_delay_ref_pin_map_, ref_pin); if (ref_outputs == nullptr) { ref_outputs = new OutputDelaySet; output_delay_ref_pin_map_[ref_pin] = ref_outputs; @@ -2853,13 +2882,13 @@ Sdc::setOutputDelay(const Pin *pin, OutputDelay * Sdc::findOutputDelay(const Pin *pin, - const ClockEdge *clk_edge) + const ClockEdge *clk_edge) { - OutputDelaySet *outputs = output_delay_pin_map_.findKey(pin); + OutputDelaySet *outputs = findKey(output_delay_pin_map_, pin); if (outputs) { for (OutputDelay *output_delay : *outputs) { if (output_delay->clkEdge() == clk_edge) - return output_delay; + return output_delay; } } return nullptr; @@ -2867,11 +2896,11 @@ Sdc::findOutputDelay(const Pin *pin, OutputDelay * Sdc::makeOutputDelay(const Pin *pin, - const ClockEdge *clk_edge) + const ClockEdge *clk_edge) { OutputDelay *output_delay = new OutputDelay(pin, clk_edge, network_); output_delays_.insert(output_delay); - OutputDelaySet *outputs = output_delay_pin_map_.findKey(pin); + OutputDelaySet *outputs = findKey(output_delay_pin_map_, pin); if (outputs == nullptr) { outputs = new OutputDelaySet; output_delay_pin_map_[pin] = outputs; @@ -2891,10 +2920,10 @@ Sdc::makeOutputDelay(const Pin *pin, void Sdc::removeOutputDelay(const Pin *pin, - const RiseFallBoth *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMaxAll *min_max) + const RiseFallBoth *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMaxAll *min_max) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; OutputDelay *output_delay = findOutputDelay(pin, clk_edge); @@ -2906,37 +2935,43 @@ Sdc::removeOutputDelay(const Pin *pin, void Sdc::deleteOutputDelays(const Pin *pin, - OutputDelay *except) + OutputDelay *except) { OutputDelaySet *output_delays = output_delay_pin_map_[pin]; - OutputDelaySet::Iterator iter(output_delays); - while (iter.hasNext()) { - OutputDelay *output_delay = iter.next(); - if (output_delay != except) + for (auto itr = output_delays->begin(); itr != output_delays->end(); ) { + OutputDelay *output_delay = *itr; + if (output_delay != except) { + itr = output_delays->erase(itr); deleteOutputDelay(output_delay); + } + else + itr++; } } OutputDelaySet * -Sdc::outputDelaysLeafPin(const Pin *leaf_pin) +Sdc::outputDelaysLeafPin(const Pin *leaf_pin) const { - return output_delay_leaf_pin_map_.findKey(leaf_pin); + return findKey(output_delay_leaf_pin_map_, leaf_pin); } bool Sdc::hasOutputDelay(const Pin *leaf_pin) const { - return output_delay_leaf_pin_map_.hasKey(leaf_pin); + return output_delay_leaf_pin_map_.contains(leaf_pin); } void Sdc::deleteOutputDelaysReferencing(const Clock *clk) { - OutputDelaySet::Iterator iter(output_delays_); - while (iter.hasNext()) { - OutputDelay *output_delay = iter.next(); - if (output_delay->clock() == clk) + for (auto itr = output_delays_.begin(); itr != output_delays_.end(); ) { + OutputDelay *output_delay = *itr; + if (output_delay->clock() == clk) { + itr = output_delays_.erase(itr); deleteOutputDelay(output_delay); + } + else + itr++; } } @@ -2961,64 +2996,54 @@ Sdc::deleteOutputDelay(OutputDelay *output_delay) void Sdc::setPortExtPinCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float cap) + const RiseFall *rf, + const MinMax *min_max, + float cap) { - PortExtCap *port_cap = ensurePortExtPinCap(port, corner); - port_cap->setPinCap(cap, rf, min_max); + PortExtCap &port_cap = port_ext_cap_map_[port]; + port_cap.setPinCap(port, cap, rf, min_max); } void Sdc::setPortExtWireCap(const Port *port, - bool subtract_pin_cap, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float cap) + const RiseFall *rf, + const MinMax *min_max, + float cap) { - PortExtCap *port_cap = ensurePortExtPinCap(port, corner); - if (subtract_pin_cap) { - Pin *pin = network_->findPin(network_->name(port)); - cap -= connectedPinCap(pin, rf, corner, min_max); - if (cap < 0.0) - cap = 0.0; - } - port_cap->setWireCap(cap, rf, min_max); + PortExtCap &port_cap = port_ext_cap_map_[port]; + port_cap.setWireCap(port, cap, rf, min_max); } -PortExtCap * -Sdc::portExtCap(const Port *port, - const Corner *corner) const +const PortExtCap * +Sdc::portExtCap(const Port *port) const { - return port_ext_cap_maps_[corner->index()].findKey(port); + auto itr = port_ext_cap_map_.find(port); + if (itr != port_ext_cap_map_.end()) + return &itr->second; + else + return nullptr; } bool Sdc::hasPortExtCap(const Port *port) const { - for (int corner_index = 0; corner_index < corners_->count(); corner_index++) { - if (port_ext_cap_maps_[corner_index].hasKey(port)) - return true; - } - return false; + auto itr = port_ext_cap_map_.find(port); + return itr != port_ext_cap_map_.end(); } void Sdc::portExtCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - bool &has_pin_cap, - float &wire_cap, - bool &has_wire_cap, - int &fanout, - bool &has_fanout) const -{ - PortExtCap *port_cap = port_ext_cap_maps_[corner->index()].findKey(port); + const RiseFall *rf, + const MinMax *min_max, + // Return values. + float &pin_cap, + bool &has_pin_cap, + float &wire_cap, + bool &has_wire_cap, + int &fanout, + bool &has_fanout) const +{ + const PortExtCap *port_cap = portExtCap(port); if (port_cap) { port_cap->pinCap(rf, min_max, pin_cap, has_pin_cap); port_cap->wireCap(rf, min_max, wire_cap, has_wire_cap); @@ -3036,17 +3061,16 @@ Sdc::portExtCap(const Port *port, float Sdc::portExtCap(const Port *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) const + const RiseFall *rf, + const MinMax *min_max) const { float pin_cap, wire_cap; int fanout; bool has_pin_cap, has_wire_cap, has_fanout; - portExtCap(port, rf, corner, min_max, - pin_cap, has_pin_cap, - wire_cap, has_wire_cap, - fanout, has_fanout); + portExtCap(port, rf, min_max, + pin_cap, has_pin_cap, + wire_cap, has_wire_cap, + fanout, has_fanout); float cap = 0.0; if (has_pin_cap) cap += pin_cap; @@ -3056,22 +3080,20 @@ Sdc::portExtCap(const Port *port, } bool -Sdc::drvrPinHasWireCap(const Pin *pin, - const Corner *corner) +Sdc::drvrPinHasWireCap(const Pin *pin) const { - return drvr_pin_wire_cap_maps_[corner->index()].hasKey(pin); + return drvr_pin_wire_cap_map_.contains(pin); } void Sdc::drvrPinWireCap(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &cap, - bool &exists, + const MinMax *min_max, + // Return values. + float &cap, + bool &exists, bool &subtract_pin_cap) const { - NetWireCaps *net_caps = drvr_pin_wire_cap_maps_[corner->index()].findKey(pin); + NetWireCaps *net_caps = findKey(drvr_pin_wire_cap_map_, pin); if (net_caps) { net_caps->value(min_max, cap, exists); subtract_pin_cap = net_caps->subtractPinCap(min_max); @@ -3085,47 +3107,42 @@ Sdc::drvrPinWireCap(const Pin *pin, void Sdc::setNetWireCap(const Net *net, - bool subtract_pin_cap, - const Corner *corner, - const MinMax *min_max, - float wire_cap) + bool subtract_pin_cap, + const MinMax *min_max, + float wire_cap) { - NetWireCaps &net_caps = net_wire_cap_maps_[corner->index()][net]; + NetWireCaps &net_caps = net_wire_cap_map_[net]; net_caps.setValue(min_max, wire_cap); net_caps.setSubtractPinCap(subtract_pin_cap, min_max); for (const Pin *pin : *network_->drivers(net)) - drvr_pin_wire_cap_maps_[corner->index()][pin] = &net_caps; + drvr_pin_wire_cap_map_[pin] = &net_caps; } bool Sdc::hasNetWireCap(const Net *net) const { - for (int i = 0; i < corners_->count(); i++) { - if (net_wire_cap_maps_[i].hasKey(net)) - return true; - } - return false; + return net_wire_cap_map_.contains(net); } //////////////////////////////////////////////////////////////// void Sdc::connectedCap(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, bool &has_net_load) const { - netCaps(pin, rf, corner, min_max, pin_cap, wire_cap, fanout, has_net_load); + netCaps(pin, rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); float net_wire_cap; bool subtract_pin_cap; - drvrPinWireCap(pin, corner, min_max, net_wire_cap, has_net_load, subtract_pin_cap); + drvrPinWireCap(pin, min_max, net_wire_cap, has_net_load, subtract_pin_cap); if (subtract_pin_cap) pin_cap = 0.0; if (has_net_load) @@ -3134,14 +3151,14 @@ Sdc::connectedCap(const Pin *pin, float Sdc::connectedPinCap(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) { float pin_cap, wire_cap, fanout; bool has_net_load; - connectedCap(pin, rf, corner, min_max, - pin_cap, wire_cap, fanout, has_net_load); + connectedCap(pin, rf, scene, min_max, + pin_cap, wire_cap, fanout, has_net_load); return pin_cap; } @@ -3149,18 +3166,18 @@ class FindNetCaps : public PinVisitor { public: FindNetCaps(const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load, - const Sdc *sdc); + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load, + const Sdc *sdc); virtual void operator()(const Pin *pin); protected: const RiseFall *rf_; - const Corner *corner_; + const Scene *scene_; const MinMax *min_max_; float &pin_cap_; float &wire_cap_; @@ -3170,16 +3187,16 @@ class FindNetCaps : public PinVisitor }; FindNetCaps::FindNetCaps(const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load, - const Sdc *sdc) : + const Scene *scene, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load, + const Sdc *sdc) : PinVisitor(), rf_(rf), - corner_(corner), + scene_(scene), min_max_(min_max), pin_cap_(pin_cap), wire_cap_(wire_cap), @@ -3192,40 +3209,40 @@ FindNetCaps::FindNetCaps(const RiseFall *rf, void FindNetCaps::operator()(const Pin *pin) { - sdc_->pinCaps(pin, rf_, corner_, min_max_, - pin_cap_, wire_cap_, fanout_); + sdc_->pinCaps(pin, rf_, scene_, min_max_, + pin_cap_, wire_cap_, fanout_); } // Capacitances for all pins connected to drvr_pin's net. void Sdc::netCaps(const Pin *drvr_pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, bool &has_net_load) const { pin_cap = 0.0; wire_cap = 0.0; fanout = 0.0; has_net_load = false; - FindNetCaps visitor(rf, corner, min_max, pin_cap, - wire_cap, fanout, has_net_load, this); + FindNetCaps visitor(rf, scene, min_max, pin_cap, + wire_cap, fanout, has_net_load, this); network_->visitConnectedPins(drvr_pin, visitor); } void Sdc::pinCaps(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout) const + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout) const { if (network_->isTopLevelPort(pin)) { Port *port = network_->port(pin); @@ -3233,17 +3250,17 @@ Sdc::pinCaps(const Pin *pin, float port_pin_cap, port_wire_cap; int port_fanout; bool has_pin_cap, has_wire_cap, has_fanout; - portExtCap(port, rf, corner, min_max, - port_pin_cap, has_pin_cap, - port_wire_cap, has_wire_cap, - port_fanout, has_fanout); + portExtCap(port, rf, min_max, + port_pin_cap, has_pin_cap, + port_wire_cap, has_wire_cap, + port_fanout, has_fanout); if (has_pin_cap) pin_cap += port_pin_cap; if (has_wire_cap) wire_cap += port_wire_cap; if (is_output) { if (has_fanout) - fanout += port_fanout; + fanout += port_fanout; // Output port counts as a fanout. fanout++; } @@ -3252,38 +3269,40 @@ Sdc::pinCaps(const Pin *pin, LibertyPort *port = network_->libertyPort(pin); if (port) { Instance *inst = network_->instance(pin); - pin_cap += portCapacitance(inst, port, rf, corner, min_max); + pin_cap += portCapacitance(inst, port, rf, scene, min_max); if (port->direction()->isAnyInput()) - fanout++; + fanout++; } } } float Sdc::portCapacitance(Instance *inst, - LibertyPort *port, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) const + LibertyPort *port, + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) const { const Pvt *inst_pvt = nullptr; if (inst) inst_pvt = pvt(inst, min_max); - LibertyPort *corner_port = port->cornerPort(corner, min_max); + LibertyPort *scene_port = port->scenePort(scene, min_max); + if (scene_port == nullptr) + scene_port = port; OperatingConditions *op_cond = operatingConditions(min_max); - return corner_port->capacitance(rf, min_max, op_cond, inst_pvt); + return scene_port->capacitance(rf, min_max, op_cond, inst_pvt); } float Sdc::pinCapacitance(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) + const RiseFall *rf, + const Scene *scene, + const MinMax *min_max) const { LibertyPort *port = network_->libertyPort(pin); if (port) { Instance *inst = network_->instance(pin); - return portCapacitance(inst, port, rf, corner, min_max); + return portCapacitance(inst, port, rf, scene, min_max); } else return 0.0; @@ -3293,8 +3312,8 @@ Sdc::pinCapacitance(const Pin *pin, void Sdc::setResistance(const Net *net, - const MinMaxAll *min_max, - float res) + const MinMaxAll *min_max, + float res) { MinMaxFloatValues &values = net_res_map_[net]; values.setValue(min_max, res); @@ -3302,36 +3321,34 @@ Sdc::setResistance(const Net *net, void Sdc::resistance(const Net *net, - const MinMax *min_max, - float &res, - bool &exists) + const MinMax *min_max, + float &res, + bool &exists) const { res = 0.0; MinMaxFloatValues values; - net_res_map_.findKey(net, values, exists); + findKeyValue(net_res_map_, net, values, exists); if (exists) values.value(min_max, res, exists); } void Sdc::setPortExtFanout(const Port *port, - const Corner *corner, - const MinMax *min_max, - int fanout) + const MinMax *min_max, + int fanout) { - PortExtCap *port_cap = ensurePortExtPinCap(port, corner); - port_cap->setFanout(fanout, min_max); + PortExtCap &port_cap = port_ext_cap_map_[port]; + port_cap.setFanout(port, fanout, min_max); } void Sdc::portExtFanout(const Port *port, - const Corner *corner, - const MinMax *min_max, - // Return values. - int &fanout, - bool &exists) + const MinMax *min_max, + // Return values. + int &fanout, + bool &exists) const { - PortExtCap *port_cap = portExtCap(port, corner); + const PortExtCap *port_cap = portExtCap(port); if (port_cap) port_cap->fanout(min_max, fanout, exists); else { @@ -3342,95 +3359,62 @@ Sdc::portExtFanout(const Port *port, int Sdc::portExtFanout(Port *port, - const Corner *corner, - const MinMax *min_max) + const MinMax *min_max) const { int fanout; bool exists; - portExtFanout(port, corner, min_max, fanout, exists); + portExtFanout(port, min_max, fanout, exists); if (exists) return fanout; else return 0.0; } -PortExtCap * -Sdc::ensurePortExtPinCap(const Port *port, - const Corner *corner) -{ - PortExtCap *port_cap = port_ext_cap_maps_[corner->index()].findKey(port); - if (port_cap == nullptr) { - port_cap = new PortExtCap(port); - port_ext_cap_maps_[corner->index()][port] = port_cap; - } - return port_cap; -} - void Sdc::swapPortExtCaps(Sdc *sdc1, Sdc *sdc2) { - for (int corner_index = 0; corner_index < sdc1->corners()->count(); corner_index++) { - swap(sdc1->port_ext_cap_maps_[corner_index], sdc2->port_ext_cap_maps_[corner_index]); - swap(sdc1->net_wire_cap_maps_[corner_index], sdc2->net_wire_cap_maps_[corner_index]); - } + swap(sdc1->port_ext_cap_map_, sdc2->port_ext_cap_map_); + swap(sdc1->net_wire_cap_map_, sdc2->net_wire_cap_map_); } //////////////////////////////////////////////////////////////// void Sdc::disable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); if (disabled_cell == nullptr) { disabled_cell = new DisabledCellPorts(cell); disabled_cell_ports_[cell] = disabled_cell; } - if (from && to) { + if (from && to) disabled_cell->setDisabledFromTo(from, to); - for (TimingArcSet *arc_set : cell->timingArcSets(from, to)) - arc_set->setIsDisabledConstraint(true); - } - else if (from) { + else if (from) disabled_cell->setDisabledFrom(from); - from->setIsDisabledConstraint(true); - } - else if (to) { + else if (to) disabled_cell->setDisabledTo(to); - to->setIsDisabledConstraint(true); - } - else { + else disabled_cell->setDisabledAll(); - cell->setIsDisabledConstraint(true); - } } void Sdc::removeDisable(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); if (disabled_cell) { - if (from && to) { + if (from && to) disabled_cell->removeDisabledFromTo(from, to); - for (TimingArcSet *arc_set : cell->timingArcSets(from, to)) - arc_set->setIsDisabledConstraint(false); - } - else if (from) { + else if (from) disabled_cell->removeDisabledFrom(from); - from->setIsDisabledConstraint(false); - } - else if (to) { + else if (to) disabled_cell->removeDisabledTo(to); - to->setIsDisabledConstraint(false); - } - else { + else disabled_cell->removeDisabledAll(); - cell->setIsDisabledConstraint(false); - } } } @@ -3438,38 +3422,33 @@ void Sdc::disable(TimingArcSet *arc_set) { LibertyCell *cell = arc_set->libertyCell(); - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); if (disabled_cell == nullptr) { disabled_cell = new DisabledCellPorts(cell); disabled_cell_ports_[cell] = disabled_cell; } disabled_cell->setDisabled(arc_set); - arc_set->setIsDisabledConstraint(true); } void Sdc::removeDisable(TimingArcSet *arc_set) { LibertyCell *cell = arc_set->libertyCell(); - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); - if (disabled_cell) { + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); + if (disabled_cell) disabled_cell->removeDisabled(arc_set); - arc_set->setIsDisabledConstraint(false); - } } void Sdc::disable(LibertyPort *port) { disabled_lib_ports_.insert(port); - port->setIsDisabledConstraint(true); } void Sdc::removeDisable(LibertyPort *port) { disabled_lib_ports_.erase(port); - port->setIsDisabledConstraint(false); } void @@ -3486,10 +3465,10 @@ Sdc::removeDisable(Port *port) void Sdc::disable(Instance *inst, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); + DisabledInstancePorts *disabled_inst = findKey(disabled_inst_ports_, inst); if (disabled_inst == nullptr) { disabled_inst = new DisabledInstancePorts(inst); disabled_inst_ports_[inst] = disabled_inst; @@ -3506,10 +3485,10 @@ Sdc::disable(Instance *inst, void Sdc::removeDisable(Instance *inst, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); + DisabledInstancePorts *disabled_inst = findKey(disabled_inst_ports_, inst); if (disabled_inst) { if (from && to) disabled_inst->removeDisabledFromTo(from, to); @@ -3523,57 +3502,76 @@ Sdc::removeDisable(Instance *inst, } void -Sdc::disable(Pin *from, - Pin *to) +Sdc::disableWire(const Pin *from, + const Pin *to) { PinPair pair(from, to); disabled_wire_edges_.insert(pair); } void -Sdc::removeDisable(Pin *from, - Pin *to) +Sdc::removeDisableWire(Pin *from, + Pin *to) { PinPair probe(from, to); disabled_wire_edges_.erase(probe); } +bool +Sdc::isDisabledWire(const Pin *from, + const Pin *to) const +{ + PinPair pair(from, to); + return disabled_wire_edges_.contains(pair); +} + void Sdc::disable(Edge *edge) { disabled_edges_.insert(edge); - edge->setIsDisabledConstraint(true); } void Sdc::removeDisable(Edge *edge) { disabled_edges_.erase(edge); - edge->setIsDisabledConstraint(false); } bool -Sdc::isDisabled(Edge *edge) +Sdc::isDisabled(const Edge *edge) const { - return disabled_edges_.hasKey(edge); + return disabled_edges_.contains(const_cast(edge)); +} + +bool +Sdc::isDisabledConstraint(const Edge *edge) const +{ + Pin *from_pin = edge->from(graph_)->pin(); + Pin *to_pin = edge->to(graph_)->pin(); + const Instance *inst = network_->instance(from_pin); + TimingArcSet *arc_set = edge->timingArcSet(); + return isDisabled(inst, from_pin, to_pin, edge->role()) + || isDisabled(edge) + || isDisabledWire(from_pin, to_pin) + || isDisabled(arc_set); } class DisableEdgesThruHierPin : public HierPinThruVisitor { public: DisableEdgesThruHierPin(PinPairSet *pairs, - Graph *graph); + Graph *graph); protected: virtual void visit(const Pin *drvr, - const Pin *load); + const Pin *load); PinPairSet *pairs_; Graph *graph_; }; DisableEdgesThruHierPin::DisableEdgesThruHierPin(PinPairSet *pairs, - Graph *graph) : + Graph *graph) : HierPinThruVisitor(), pairs_(pairs), graph_(graph) @@ -3582,7 +3580,7 @@ DisableEdgesThruHierPin::DisableEdgesThruHierPin(PinPairSet *pairs, void DisableEdgesThruHierPin::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { PinPair pair(drvr, load); pairs_->insert(pair); @@ -3604,7 +3602,7 @@ class RemoveDisableEdgesThruHierPin : public HierPinThruVisitor { public: RemoveDisableEdgesThruHierPin(PinPairSet *pairs, - Graph *graph); + Graph *graph); protected: virtual void visit(const Pin *drvr, @@ -3615,7 +3613,7 @@ class RemoveDisableEdgesThruHierPin : public HierPinThruVisitor }; RemoveDisableEdgesThruHierPin::RemoveDisableEdgesThruHierPin(PinPairSet *pairs, - Graph *graph) : + Graph *graph) : HierPinThruVisitor(), pairs_(pairs), graph_(graph) @@ -3624,7 +3622,7 @@ RemoveDisableEdgesThruHierPin::RemoveDisableEdgesThruHierPin(PinPairSet *pairs, void RemoveDisableEdgesThruHierPin::visit(const Pin *drvr, - const Pin *load) + const Pin *load) { PinPair pair(drvr, load); pairs_->erase(pair); @@ -3643,36 +3641,36 @@ Sdc::removeDisable(Pin *pin) } bool -Sdc::isDisabled(const Pin *pin) const +Sdc::isDisabledConstraint(const Pin *pin) const { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(pin); - return disabled_pins_.hasKey(pin) - || disabled_ports_.hasKey(port) - || disabled_lib_ports_.hasKey(lib_port); + return disabled_pins_.contains(pin) + || disabled_ports_.contains(port) + || disabled_lib_ports_.contains(lib_port); } bool Sdc::isDisabled(const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const TimingRole *role) const + const Pin *from_pin, + const Pin *to_pin, + const TimingRole *role) const { if (role == TimingRole::wire()) { // Hierarchical thru pin disables. PinPair pair(from_pin, to_pin); - return disabled_wire_edges_.hasKey(pair); + return disabled_wire_edges_.contains(pair); } else { LibertyCell *cell = network_->libertyCell(inst); LibertyPort *from_port = network_->libertyPort(from_pin); LibertyPort *to_port = network_->libertyPort(to_pin); - DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledInstancePorts *disabled_inst = findKey(disabled_inst_ports_, inst); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); return (disabled_inst - && disabled_inst->isDisabled(from_port, to_port, role)) + && disabled_inst->isDisabled(from_port, to_port, role)) || (disabled_cell - && disabled_cell->isDisabled(from_port, to_port, role)); + && disabled_cell->isDisabled(from_port, to_port, role)); } } @@ -3681,7 +3679,7 @@ Sdc::isDisabled(TimingArcSet *arc_set) const { LibertyCell *cell = arc_set->libertyCell(); if (cell) { - DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + DisabledCellPorts *disabled_cell = findKey(disabled_cell_ports_, cell); return disabled_cell && disabled_cell->isDisabled(arc_set); } @@ -3695,12 +3693,26 @@ Sdc::disabledInstancePorts() const return &disabled_inst_ports_; } -DisabledCellPortsMap * -Sdc::disabledCellPorts() +const DisabledCellPortsMap * +Sdc::disabledCellPorts() const { return &disabled_cell_ports_; } +//////////////////////////////////////////////////////////////// + +bool +Sdc::isConstrainedEnd(const Pin *pin) const +{ + // All output pins are considered constrained because + // they may be downstream from a set_min/max_delay -from that + // does not have a set_output_delay. + return (network_->isTopLevelPort(pin) + && network_->direction(pin)->isAnyOutput()) + || output_delay_leaf_pin_map_.contains(pin) + || data_checks_to_map_.contains(pin); +} + void Sdc::disableClockGatingCheck(Instance *inst) { @@ -3726,15 +3738,15 @@ Sdc::removeDisableClockGatingCheck(Pin *pin) } bool -Sdc::isDisableClockGatingCheck(const Instance *inst) +Sdc::isDisableClockGatingCheck(const Instance *inst) const { - return disabled_clk_gating_checks_inst_.hasKey(inst); + return disabled_clk_gating_checks_inst_.contains(inst); } bool -Sdc::isDisableClockGatingCheck(const Pin *pin) +Sdc::isDisableClockGatingCheck(const Pin *pin) const { - return disabled_clk_gating_checks_pin_.hasKey(pin); + return disabled_clk_gating_checks_pin_.contains(pin); } //////////////////////////////////////////////////////////////// @@ -3748,15 +3760,15 @@ Sdc::setLogicValue(const Pin *pin, void Sdc::logicValue(const Pin *pin, - LogicValue &value, - bool &exists) + LogicValue &value, + bool &exists) const { - logic_value_map_.findKey(pin, value, exists); + findKeyValue(logic_value_map_, pin, value, exists); } void Sdc::setCaseAnalysis(const Pin *pin, - LogicValue value) + LogicValue value) { case_value_map_[pin] = value; } @@ -3769,26 +3781,26 @@ Sdc::removeCaseAnalysis(const Pin *pin) void Sdc::caseLogicValue(const Pin *pin, - LogicValue &value, - bool &exists) + LogicValue &value, + bool &exists) const { - case_value_map_.findKey(pin, value, exists); + findKeyValue(case_value_map_, pin, value, exists); } bool -Sdc::hasLogicValue(const Pin *pin) +Sdc::hasLogicValue(const Pin *pin) const { - return case_value_map_.hasKey(pin) - || logic_value_map_.hasKey(pin); + return case_value_map_.contains(pin) + || logic_value_map_.contains(pin); } //////////////////////////////////////////////////////////////// ExceptionFrom * Sdc::makeExceptionFrom(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_rf) + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_rf) const { if ((from_pins && !from_pins->empty()) || (from_clks && !from_clks->empty()) @@ -3817,9 +3829,9 @@ Sdc::isExceptionStartpoint(const Pin *pin) const ExceptionThru * Sdc::makeExceptionThru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf) + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf) const { if ((pins && !pins->empty()) || (nets && !nets->empty()) @@ -3831,10 +3843,10 @@ Sdc::makeExceptionThru(PinSet *pins, ExceptionTo * Sdc::makeExceptionTo(PinSet *pins, - ClockSet *clks, - InstanceSet *insts, - const RiseFallBoth *rf, - const RiseFallBoth *end_rf) + ClockSet *clks, + InstanceSet *insts, + const RiseFallBoth *rf, + const RiseFallBoth *end_rf) const { if ((pins && !pins->empty()) || (clks && !clks->empty()) @@ -3849,7 +3861,7 @@ Sdc::makeExceptionTo(PinSet *pins, // Valid endpoints include gated clock enables which are not // known until clock arrivals are determined. bool -Sdc::isExceptionEndpoint(const Pin *pin) +Sdc::isExceptionEndpoint(const Pin *pin) const { Net *net = network_->net(pin); bool has_checks = false; @@ -3860,7 +3872,7 @@ Sdc::isExceptionEndpoint(const Pin *pin) LibertyCell *cell = port->libertyCell(); for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { if (arc_set->role()->isTimingCheck()) { - has_checks = true; + has_checks = true; break; } } @@ -3876,49 +3888,51 @@ Sdc::isExceptionEndpoint(const Pin *pin) && !network_->isHierarchical(pin); } +//////////////////////////////////////////////////////////////// + void Sdc::makeFalsePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const char *comment) { checkFromThrusTo(from, thrus, to); FalsePath *exception = new FalsePath(from, thrus, to, min_max, true, - comment); + comment); addException(exception); } void Sdc::makeMulticyclePath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + const char *comment) { checkFromThrusTo(from, thrus, to); MultiCyclePath *exception = new MultiCyclePath(from, thrus, to, - min_max, use_end_clk, - path_multiplier, true, - comment); + min_max, use_end_clk, + path_multiplier, true, + comment); addException(exception); } void Sdc::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, - const char *comment) + float delay, + const char *comment) { checkFromThrusTo(from, thrus, to); PathDelay *exception = new PathDelay(from, thrus, to, min_max, - ignore_clk_latency, break_path, + ignore_clk_latency, break_path, delay, true, comment); addException(exception); } @@ -3931,7 +3945,7 @@ Sdc::recordPathDelayInternalFrom(ExceptionPath *exception) && from->hasPins()) { for (const Pin *pin : *from->pins()) { if (!isExceptionStartpoint(pin)) { - path_delay_internal_from_.insert(pin); + path_delay_internal_from_.insert(pin); if (exception->breakPath()) path_delay_internal_from_break_.insert(pin); } @@ -3948,8 +3962,8 @@ Sdc::unrecordPathDelayInternalFrom(ExceptionPath *exception) && !path_delay_internal_from_.empty()) { for (const Pin *pin : *from->pins()) { if (!isExceptionStartpoint(pin) - && !pathDelayFrom(pin)) { - path_delay_internal_from_.erase(pin); + && !pathDelayFrom(pin)) { + path_delay_internal_from_.erase(pin); if (exception->breakPath()) path_delay_internal_from_break_.erase(pin); } @@ -3957,28 +3971,14 @@ Sdc::unrecordPathDelayInternalFrom(ExceptionPath *exception) } } -template -const ExceptionPathSet * -findExceptions(const UnorderedMap &map, - const OBJ *obj) -{ - const auto itr = map.find(obj); - if (itr != map.end()) - return &itr->second; - else - return nullptr; -} - bool Sdc::pathDelayFrom(const Pin *pin) { - - const ExceptionPathSet *exceptions = - findExceptions(first_from_pin_exceptions_, pin); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_from_pin_exceptions_, pin); if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->isPathDelay()) - return true; + return true; } } return false; @@ -3987,13 +3987,13 @@ Sdc::pathDelayFrom(const Pin *pin) bool Sdc::isPathDelayInternalFrom(const Pin *pin) const { - return path_delay_internal_from_.hasKey(pin); + return path_delay_internal_from_.contains(pin); } bool Sdc::isPathDelayInternalFromBreak(const Pin *pin) const { - return path_delay_internal_from_break_.hasKey(pin); + return path_delay_internal_from_break_.contains(pin); } const PinSet & @@ -4010,8 +4010,8 @@ Sdc::recordPathDelayInternalTo(ExceptionPath *exception) && to->hasPins()) { for (const Pin *pin : *to->pins()) { if (!(hasLibertyCheckTo(pin) - || network_->isTopLevelPort(pin))) { - path_delay_internal_to_.insert(pin); + || network_->isTopLevelPort(pin))) { + path_delay_internal_to_.insert(pin); if (exception->breakPath()) path_delay_internal_to_break_.insert(pin); } @@ -4028,9 +4028,9 @@ Sdc::unrecordPathDelayInternalTo(ExceptionPath *exception) && !path_delay_internal_to_.empty()) { for (const Pin *pin : *to->pins()) { if (!(hasLibertyCheckTo(pin) - || network_->isTopLevelPort(pin)) - && !pathDelayTo(pin)) { - path_delay_internal_to_.erase(pin); + || network_->isTopLevelPort(pin)) + && !pathDelayTo(pin)) { + path_delay_internal_to_.erase(pin); if (exception->breakPath()) path_delay_internal_to_break_.erase(pin); } @@ -4047,8 +4047,8 @@ Sdc::hasLibertyCheckTo(const Pin *pin) LibertyPort *port = network_->libertyPort(pin); if (port) { for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { - if (arc_set->role()->isTimingCheckBetween()) - return true; + if (arc_set->role()->isTimingCheckBetween()) + return true; } } } @@ -4058,12 +4058,11 @@ Sdc::hasLibertyCheckTo(const Pin *pin) bool Sdc::pathDelayTo(const Pin *pin) { - const ExceptionPathSet *exceptions = - findExceptions(first_to_pin_exceptions_, pin); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_pin_exceptions_, pin); if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->isPathDelay()) - return true; + return true; } } return false; @@ -4072,13 +4071,13 @@ Sdc::pathDelayTo(const Pin *pin) bool Sdc::isPathDelayInternalTo(const Pin *pin) const { - return path_delay_internal_to_.hasKey(pin); + return path_delay_internal_to_.contains(pin); } bool Sdc::isPathDelayInternalToBreak(const Pin *pin) const { - return path_delay_internal_to_break_.hasKey(pin); + return path_delay_internal_to_break_.contains(pin); } //////////////////////////////////////////////////////////////// @@ -4090,7 +4089,7 @@ Sdc::clearGroupPathMap() // Delete group_path name strings. for (auto [name, groups] : group_path_map_) { stringDelete(name); - groups->deleteContents(); + deleteContents(*groups); delete groups; } group_path_map_.clear(); @@ -4098,18 +4097,18 @@ Sdc::clearGroupPathMap() void Sdc::makeGroupPath(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment) + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const char *comment) { checkFromThrusTo(from, thrus, to); if (name && is_default) report_->critical(1490, "group path name and is_default are mutually exclusive."); else if (name) { GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to, - true, comment); + true, comment); // Clone the group_path because it may get merged and hence deleted // by addException. ExceptionFrom *from1 = group_path->from() @@ -4119,12 +4118,12 @@ Sdc::makeGroupPath(const char *name, ExceptionPath *clone = group_path->clone(from1, thrus1, to1, true); addException(clone); // A named group path can have multiple exceptions. - GroupPathSet *groups = group_path_map_.findKey(name); + GroupPathSet *groups = findKey(group_path_map_, name); if (groups == nullptr) { groups = new GroupPathSet(network_); group_path_map_[stringCopy(name)] = groups; } - if (groups->hasKey(group_path)) + if (groups->contains(group_path)) // Exact copy of existing group path. delete group_path; else @@ -4133,23 +4132,23 @@ Sdc::makeGroupPath(const char *name, else { // is_default GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to, - true, comment); + true, comment); addException(group_path); } } bool -Sdc::isGroupPathName(const char *group_name) +Sdc::isGroupPathName(const char *group_name) const { - return group_path_map_.hasKey(group_name); + return group_path_map_.contains(group_name); } //////////////////////////////////////////////////////////////// FilterPath * Sdc::makeFilterPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) + ExceptionThruSeq *thrus, + ExceptionTo *to) { checkFromThrusTo(from, thrus, to); FilterPath *exception = new FilterPath(from, thrus, to, true); @@ -4159,6 +4158,22 @@ Sdc::makeFilterPath(ExceptionFrom *from, return exception; } +void +Sdc::makeFilter(ExceptionFrom *from, + ExceptionThruSeq *thrus) +{ + filter_ = makeFilterPath(from, thrus, nullptr); +} + +void +Sdc::deleteFilter() +{ + if (filter_) { + deleteException(filter_); + filter_ = nullptr; + } +} + //////////////////////////////////////////////////////////////// void @@ -4184,10 +4199,10 @@ Sdc::makeLoopExceptions(GraphLoop *loop) while (in_edge_iter.hasNext()) { Edge *in_edge = in_edge_iter.next(); if (in_edge != edge) { - Pin *loop_input_pin = in_edge->from(graph_)->pin(); - makeLoopException(loop_input_pin, to_pin, from_pin); - // Prevent sub-loops by blocking paths on the main loop also. - makeLoopException(from_pin, to_pin, loop_input_pin); + Pin *loop_input_pin = in_edge->from(graph_)->pin(); + makeLoopException(loop_input_pin, to_pin, from_pin); + // Prevent sub-loops by blocking paths on the main loop also. + makeLoopException(from_pin, to_pin, loop_input_pin); } } } @@ -4195,8 +4210,8 @@ Sdc::makeLoopExceptions(GraphLoop *loop) void Sdc::makeLoopException(const Pin *loop_input_pin, - const Pin *loop_pin, - const Pin *loop_prev_pin) + const Pin *loop_pin, + const Pin *loop_prev_pin) { ExceptionThruSeq *thrus = new ExceptionThruSeq; makeLoopExceptionThru(loop_input_pin, thrus); @@ -4215,13 +4230,13 @@ Sdc::makeLoopPath(ExceptionThruSeq *thrus) void Sdc::makeLoopExceptionThru(const Pin *pin, - ExceptionThruSeq *thrus) + ExceptionThruSeq *thrus) { debugPrint(debug_, "levelize", 2, " %s", network_->pathName(pin)); PinSet *pins = new PinSet(network_); pins->insert(pin); ExceptionThru *thru = makeExceptionThru(pins, nullptr, nullptr, - RiseFallBoth::riseFall()); + RiseFallBoth::riseFall()); thrus->push_back(thru); } @@ -4268,7 +4283,7 @@ Sdc::addException(ExceptionPath *exception) InstanceSet *insts1 = from->instances() ? new InstanceSet(*from->instances()) : nullptr; ExceptionFrom *from1 = new ExceptionFrom(pins1, nullptr, insts1, - from->transition(), true, network_); + from->transition(), true, network_); ExceptionThruSeq *thrus1 = exceptionThrusClone(exception->thrus(), network_); ExceptionTo *to = exception->to(); ExceptionTo *to1 = to ? to->clone(network_) : nullptr; @@ -4279,7 +4294,7 @@ Sdc::addException(ExceptionPath *exception) ClockSet *clks2 = new ClockSet(*from->clks()); ExceptionFrom *from2 = new ExceptionFrom(nullptr, clks2, nullptr, - from->transition(), true, network_); + from->transition(), true, network_); ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_); ExceptionTo *to2 = to ? to->clone(network_) : nullptr; ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); @@ -4305,7 +4320,7 @@ Sdc::addException1(ExceptionPath *exception) PinSet *pins1 = to->pins() ? new PinSet(*to->pins()) : nullptr; InstanceSet *insts1 = to->instances() ? new InstanceSet(*to->instances()) : nullptr; ExceptionTo *to1 = new ExceptionTo(pins1, nullptr, insts1, to->transition(), - to->endTransition(), true, network_); + to->endTransition(), true, network_); ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true); debugPrint(debug_, "exception_merge", 1, " split exception for %s", exception1->asString(network_)); @@ -4315,7 +4330,7 @@ Sdc::addException1(ExceptionPath *exception) ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_); ClockSet *clks2 = new ClockSet(*to->clks()); ExceptionTo *to2 = new ExceptionTo(nullptr, clks2, nullptr, to->transition(), - to->endTransition(), true, network_); + to->endTransition(), true, network_); ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); debugPrint(debug_, "exception_merge", 1, " split exception for %s", exception2->asString(network_)); @@ -4401,7 +4416,7 @@ Sdc::deleteMatchingExceptions(ExceptionPath *exception) void Sdc::findMatchingExceptions(ExceptionPath *exception, - ExceptionPathSet &matches) + ExceptionPathSet &matches) { if (exception->from()) findMatchingExceptionsFirstFrom(exception, matches); @@ -4413,7 +4428,7 @@ Sdc::findMatchingExceptions(ExceptionPath *exception, void Sdc::findMatchingExceptionsFirstFrom(ExceptionPath *exception, - ExceptionPathSet &matches) + ExceptionPathSet &matches) { ExceptionFrom *from = exception->from(); findMatchingExceptionsPins(exception, from->pins(), @@ -4428,30 +4443,30 @@ Sdc::findMatchingExceptionsFirstFrom(ExceptionPath *exception, void Sdc::findMatchingExceptionsFirstThru(ExceptionPath *exception, - ExceptionPathSet &matches) + ExceptionPathSet &matches) { ExceptionThru *thru = (*exception->thrus())[0]; findMatchingExceptionsPins(exception, thru->pins(), - first_thru_pin_exceptions_, - matches); + first_thru_pin_exceptions_, + matches); findMatchingExceptionsInsts(exception, thru->instances(), - first_thru_inst_exceptions_, - matches); + first_thru_inst_exceptions_, + matches); if (!first_thru_net_exceptions_.empty() && thru->nets()) { for (const Net *net : *thru->nets()) { // Potential matches includes exceptions that match net that are not // the first exception point. const ExceptionPathSet *potential_matches = - findExceptions(first_thru_net_exceptions_, net); + findKeyValuePtr(first_thru_net_exceptions_, net); if (potential_matches) { - for (ExceptionPath *match : *potential_matches) { - ExceptionThru *match_thru = (*match->thrus())[0]; - if (match_thru->nets()->hasKey(net) - && match->overrides(exception) - && match->intersectsPts(exception, network_)) - matches.insert(match); - } + for (ExceptionPath *match : *potential_matches) { + ExceptionThru *match_thru = (*match->thrus())[0]; + if (match_thru->nets()->contains(net) + && match->overrides(exception) + && match->intersectsPts(exception, network_)) + matches.insert(match); + } } } } @@ -4459,30 +4474,30 @@ Sdc::findMatchingExceptionsFirstThru(ExceptionPath *exception, void Sdc::findMatchingExceptionsFirstTo(ExceptionPath *exception, - ExceptionPathSet &matches) + ExceptionPathSet &matches) { ExceptionTo *to = exception->to(); findMatchingExceptionsPins(exception, to->pins(), first_to_pin_exceptions_, - matches); + matches); findMatchingExceptionsInsts(exception, to->instances(), - first_to_inst_exceptions_, - matches); + first_to_inst_exceptions_, + matches); findMatchingExceptionsClks(exception, to->clks(), first_to_clk_exceptions_, - matches); + matches); } void Sdc::findMatchingExceptionsClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map, - ExceptionPathSet &matches) + ClockSet *clks, + ClockExceptionsMap &exception_map, + ExceptionPathSet &matches) { if (clks) { ExceptionPathSet clks_matches; for (Clock *clk : *clks) { auto itr = exception_map.find(clk); if (itr != exception_map.end()) - clks_matches.insert(itr->second.begin(), itr->second.end()); + clks_matches.insert(itr->second.begin(), itr->second.end()); } findMatchingExceptions(exception, &clks_matches, matches); } @@ -4490,16 +4505,16 @@ Sdc::findMatchingExceptionsClks(ExceptionPath *exception, void Sdc::findMatchingExceptionsPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map, - ExceptionPathSet &matches) + PinSet *pins, + PinExceptionsMap &exception_map, + ExceptionPathSet &matches) { if (pins) { ExceptionPathSet pins_matches; for (const Pin *pin : *pins) { auto itr = exception_map.find(pin); if (itr != exception_map.end()) - pins_matches.insert(itr->second.begin(), itr->second.end()); + pins_matches.insert(itr->second.begin(), itr->second.end()); } findMatchingExceptions(exception, &pins_matches, matches); } @@ -4507,16 +4522,16 @@ Sdc::findMatchingExceptionsPins(ExceptionPath *exception, void Sdc::findMatchingExceptionsInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map, - ExceptionPathSet &matches) + InstanceSet *insts, + InstanceExceptionsMap &exception_map, + ExceptionPathSet &matches) { if (insts) { ExceptionPathSet inst_matches; for (const Instance *inst : *insts) { auto itr = exception_map.find(inst); if (itr != exception_map.end()) - inst_matches.insert(itr->second.begin(), itr->second.end()); + inst_matches.insert(itr->second.begin(), itr->second.end()); } findMatchingExceptions(exception, &inst_matches, matches); } @@ -4524,22 +4539,22 @@ Sdc::findMatchingExceptionsInsts(ExceptionPath *exception, void Sdc::findMatchingExceptions(ExceptionPath *exception, - ExceptionPathSet *potential_matches, - ExceptionPathSet &matches) + ExceptionPathSet *potential_matches, + ExceptionPathSet &matches) { if (potential_matches) { for (ExceptionPath *match : *potential_matches) { if (match->overrides(exception) - && match->intersectsPts(exception, network_)) - matches.insert(match); + && match->intersectsPts(exception, network_)) + matches.insert(match); } } } void Sdc::expandExceptionExcluding(ExceptionPath *exception, - ExceptionPath *excluding, - ExceptionPathSet &expansions) + ExceptionPath *excluding, + ExceptionPathSet &expansions) { ExceptionFrom *from = exception->from(); ExceptionThruSeq *thrus = exception->thrus(); @@ -4550,10 +4565,10 @@ Sdc::expandExceptionExcluding(ExceptionPath *exception, if (from_cpy->hasObjects()) { ExceptionThruSeq *thrus_cpy = nullptr; if (thrus) - thrus_cpy = clone(thrus, network_); + thrus_cpy = clone(thrus, network_); ExceptionTo *to_cpy = nullptr; if (to) - to_cpy = to->clone(network_); + to_cpy = to->clone(network_); ExceptionPath *expand = exception->clone(from_cpy,thrus_cpy,to_cpy,true); expansions.insert(expand); } @@ -4561,36 +4576,37 @@ Sdc::expandExceptionExcluding(ExceptionPath *exception, delete from_cpy; } if (thrus) { - ExceptionThruSeq::Iterator thru_iter(thrus); - ExceptionThruSeq::Iterator thru_iter2(excluding->thrus()); - while (thru_iter.hasNext() - && thru_iter2.hasNext()) { - ExceptionThru *thru = thru_iter.next(); - ExceptionThru *thru2 = thru_iter2.next(); + ExceptionThruSeq *excluding_thrus = excluding->thrus(); + ExceptionThruSeq::iterator thru_iter = thrus->begin(); + ExceptionThruSeq::iterator thru_iter2 = excluding_thrus->begin(); + while (thru_iter != thrus->end() + && thru_iter2 != excluding_thrus->end()) { + ExceptionThru *thru = *thru_iter++; + ExceptionThru *thru2 = *thru_iter2++; ExceptionThru *thru_cpy = thru->clone(network_); thru_cpy->deleteObjects(thru2, network_); if (thru_cpy->hasObjects()) { - ExceptionFrom *from_cpy = nullptr; - if (from) - from_cpy = from->clone(network_); - ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; + ExceptionFrom *from_cpy = nullptr; + if (from) + from_cpy = from->clone(network_); + ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; for (ExceptionThru *thru1 : *thrus) { - if (thru1 == thru) - thrus_cpy->push_back(thru_cpy); - else { - ExceptionThru *thru_cpy = thru->clone(network_); - thrus_cpy->push_back(thru_cpy); - } - } - ExceptionTo *to_cpy = nullptr; - if (to) - to_cpy = to->clone(network_); - ExceptionPath *expand = exception->clone(from_cpy, thrus_cpy, to_cpy, - true); - expansions.insert(expand); + if (thru1 == thru) + thrus_cpy->push_back(thru_cpy); + else { + ExceptionThru *thru_cpy = thru->clone(network_); + thrus_cpy->push_back(thru_cpy); + } + } + ExceptionTo *to_cpy = nullptr; + if (to) + to_cpy = to->clone(network_); + ExceptionPath *expand = exception->clone(from_cpy, thrus_cpy, to_cpy, + true); + expansions.insert(expand); } else - delete thru_cpy; + delete thru_cpy; } } if (to) { @@ -4599,10 +4615,10 @@ Sdc::expandExceptionExcluding(ExceptionPath *exception, if (to_cpy->hasObjects()) { ExceptionFrom *from_cpy = nullptr; if (from) - from_cpy = from->clone(network_); + from_cpy = from->clone(network_); ExceptionThruSeq *thrus_cpy = nullptr; if (thrus) - thrus_cpy = clone(thrus, network_); + thrus_cpy = clone(thrus, network_); ExceptionPath *expand = exception->clone(from_cpy,thrus_cpy,to_cpy,true); expansions.insert(expand); } @@ -4662,7 +4678,7 @@ Sdc::recordMergeHashes(ExceptionPath *exception) void Sdc::recordMergeHash(ExceptionPath *exception, - ExceptionPt *missing_pt) + ExceptionPt *missing_pt) { size_t hash = exception->hash(missing_pt); debugPrint(debug_, "exception_merge", 3, @@ -4695,7 +4711,7 @@ Sdc::recordExceptionFirstFrom(ExceptionPath *exception) ExceptionFrom *from = exception->from(); recordExceptionPins(exception, from->pins(), first_from_pin_exceptions_); recordExceptionInsts(exception, from->instances(), - first_from_inst_exceptions_); + first_from_inst_exceptions_); recordExceptionClks(exception, from->clks(), first_from_clk_exceptions_); } @@ -4721,7 +4737,7 @@ Sdc::recordExceptionFirstThru(ExceptionPath *exception) ExceptionThru *thru = (*exception->thrus())[0]; recordExceptionPins(exception, thru->pins(), first_thru_pin_exceptions_); recordExceptionInsts(exception, thru->instances(), - first_thru_inst_exceptions_); + first_thru_inst_exceptions_); recordExceptionEdges(exception, thru->edges(), first_thru_edge_exceptions_); for (ExceptionThru *thru : *exception->thrus()) recordExceptionNets(exception, thru->nets(), first_thru_net_exceptions_); @@ -4738,8 +4754,8 @@ Sdc::recordExceptionFirstTo(ExceptionPath *exception) void Sdc::recordExceptionClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map) + ClockSet *clks, + ClockExceptionsMap &exception_map) { if (clks) { for (Clock *clk : *clks) { @@ -4751,8 +4767,8 @@ Sdc::recordExceptionClks(ExceptionPath *exception, void Sdc::recordExceptionEdges(ExceptionPath *exception, - EdgePinsSet *edges, - EdgeExceptionsMap &exception_map) + EdgePinsSet *edges, + EdgeExceptionsMap &exception_map) { if (edges) { for (const EdgePins &edge : *edges) { @@ -4764,8 +4780,8 @@ Sdc::recordExceptionEdges(ExceptionPath *exception, void Sdc::recordExceptionPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map) + PinSet *pins, + PinExceptionsMap &exception_map) { if (pins) { for (const Pin *pin : *pins) { @@ -4777,8 +4793,8 @@ Sdc::recordExceptionPins(ExceptionPath *exception, void Sdc::recordExceptionHpin(ExceptionPath *exception, - Pin *pin, - PinExceptionsMap &exception_map) + Pin *pin, + PinExceptionsMap &exception_map) { ExceptionPathSet &set = exception_map[pin]; set.insert(exception); @@ -4786,8 +4802,8 @@ Sdc::recordExceptionHpin(ExceptionPath *exception, void Sdc::recordExceptionInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map) + InstanceSet *insts, + InstanceExceptionsMap &exception_map) { if (insts) { for (const Instance *inst : *insts) { @@ -4799,8 +4815,8 @@ Sdc::recordExceptionInsts(ExceptionPath *exception, void Sdc::recordExceptionNets(ExceptionPath *exception, - NetSet *nets, - NetExceptionsMap &exception_map) + NetSet *nets, + NetExceptionsMap &exception_map) { if (nets) { for (const Net *net : *nets) { @@ -4843,31 +4859,31 @@ Sdc::findMergeMatch(ExceptionPath *exception) if (itr != exception_merge_hash_.end()) { ExceptionPathSet &matches = itr->second; for (ExceptionPath *match : matches) { - ExceptionPt *match_missing_pt; - if (match != exception - // Exceptions are not merged if their priorities are - // different. This allows exceptions to be pruned during - // search at the endpoint. - && exception->mergeable(match) - && match->mergeablePts(exception, missing_pt, match_missing_pt)) { - debugPrint(debug_, "exception_merge", 1, "merge %s", + ExceptionPt *match_missing_pt; + if (match != exception + // Exceptions are not merged if their priorities are + // different. This allows exceptions to be pruned during + // 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", + debugPrint(debug_, "exception_merge", 1, " with %s", match->asString(network_)); - // Unrecord the exception that is being merged away. - unrecordException(exception); - unrecordMergeHashes(match); - missing_pt->mergeInto(match_missing_pt, network_); - recordMergeHashes(match); - // First point maps only change if the exception point that - // is being merged is the first exception point. - if (first_pt) - recordExceptionFirstPts(match); + // Unrecord the exception that is being merged away. + unrecordException(exception); + unrecordMergeHashes(match); + missing_pt->mergeInto(match_missing_pt, network_); + recordMergeHashes(match); + // First point maps only change if the exception point that + // is being merged is the first exception point. + if (first_pt) + recordExceptionFirstPts(match); // Have to wait until after exception point merge to delete // the exception. - delete exception; - return match; - } + delete exception; + return match; + } } } first_pt = false; @@ -4919,32 +4935,32 @@ Sdc::deleteExceptionsReferencing(Clock *clk) ExceptionFrom *from = exception->from(); if (from) { ClockSet *clks = from->clks(); - if (clks && clks->hasKey(clk)) { - itr = exceptions_.erase(itr); - unrecordException(exception); - deleted = true; - from->deleteClock(clk); - if (from->hasObjects()) - recordException(exception); - else - deleteException(exception); + if (clks && clks->contains(clk)) { + itr = exceptions_.erase(itr); + unrecordException(exception); + deleted = true; + from->deleteClock(clk); + if (from->hasObjects()) + recordException(exception); + else + deleteException(exception); } } if (!deleted) { ExceptionTo *to = exception->to(); if (to) { - ClockSet *clks = to->clks(); - if (clks && clks->hasKey(clk)) { - itr = exceptions_.erase(itr); - deleted = true; - unrecordException(exception); - to->deleteClock(clk); - if (to->hasObjects()) - recordException(exception); - else - deleteException(exception); - } + ClockSet *clks = to->clks(); + if (clks && clks->contains(clk)) { + itr = exceptions_.erase(itr); + deleted = true; + unrecordException(exception); + to->deleteClock(clk); + if (to->hasObjects()) + recordException(exception); + else + deleteException(exception); + } } } if (!deleted) @@ -4982,7 +4998,7 @@ Sdc::unrecordMergeHashes(ExceptionPath *exception) void Sdc::unrecordMergeHash(ExceptionPath *exception, - ExceptionPt *missing_pt) + ExceptionPt *missing_pt) { size_t hash = exception->hash(missing_pt); debugPrint(debug_, "exception_merge", 3, @@ -5012,16 +5028,16 @@ Sdc::unrecordExceptionFirstPts(ExceptionPath *exception) ExceptionThru *thru = (*thrus)[0]; unrecordExceptionPins(exception, thru->pins(), first_thru_pin_exceptions_); unrecordExceptionInsts(exception, thru->instances(), - first_thru_inst_exceptions_); + first_thru_inst_exceptions_); unrecordExceptionNets(exception, thru->nets(), first_thru_net_exceptions_); unrecordExceptionEdges(exception, thru->edges(), - first_thru_edge_exceptions_); + first_thru_edge_exceptions_); } else if (to) { unrecordExceptionPins(exception, to->pins(), first_to_pin_exceptions_); unrecordExceptionClks(exception, to->clks(), first_to_clk_exceptions_); unrecordExceptionInsts(exception, to->instances(), - first_to_inst_exceptions_); + first_to_inst_exceptions_); } } @@ -5043,14 +5059,14 @@ Sdc::unrecordExceptionPins(ExceptionPath *exception) void Sdc::unrecordExceptionClks(ExceptionPath *exception, - ClockSet *clks, - ClockExceptionsMap &exception_map) + ClockSet *clks, + ClockExceptionsMap &exception_map) { if (clks) { for (Clock *clk : *clks) { auto itr = exception_map.find(clk); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5059,14 +5075,14 @@ Sdc::unrecordExceptionClks(ExceptionPath *exception, void Sdc::unrecordExceptionPins(ExceptionPath *exception, - PinSet *pins, - PinExceptionsMap &exception_map) + PinSet *pins, + PinExceptionsMap &exception_map) { if (pins) { for (const Pin *pin : *pins) { auto itr = exception_map.find(pin); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5075,14 +5091,14 @@ Sdc::unrecordExceptionPins(ExceptionPath *exception, void Sdc::unrecordExceptionInsts(ExceptionPath *exception, - InstanceSet *insts, - InstanceExceptionsMap &exception_map) + InstanceSet *insts, + InstanceExceptionsMap &exception_map) { if (insts) { for (const Instance *inst : *insts) { auto itr = exception_map.find(inst); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5091,14 +5107,14 @@ Sdc::unrecordExceptionInsts(ExceptionPath *exception, void Sdc::unrecordExceptionEdges(ExceptionPath *exception, - EdgePinsSet *edges, - EdgeExceptionsMap &exception_map) + EdgePinsSet *edges, + EdgeExceptionsMap &exception_map) { if (edges) { for (const EdgePins &edge : *edges) { auto itr = exception_map.find(edge); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5107,14 +5123,14 @@ Sdc::unrecordExceptionEdges(ExceptionPath *exception, void Sdc::unrecordExceptionNets(ExceptionPath *exception, - NetSet *nets, - NetExceptionsMap &exception_map) + NetSet *nets, + NetExceptionsMap &exception_map) { if (nets) { for (const Net *net : *nets) { auto itr = exception_map.find(net); if (itr != exception_map.end()) { - ExceptionPathSet &set = itr->second; + ExceptionPathSet &set = itr->second; set.erase(exception); } } @@ -5123,8 +5139,8 @@ Sdc::unrecordExceptionNets(ExceptionPath *exception, void Sdc::unrecordExceptionHpin(ExceptionPath *exception, - Pin *pin, - PinExceptionsMap &exception_map) + Pin *pin, + PinExceptionsMap &exception_map) { auto itr = exception_map.find(pin); if (itr != exception_map.end()) { @@ -5139,19 +5155,19 @@ class ExpandException : public ExpandedExceptionVisitor { public: ExpandException(ExceptionPath *exception, - ExceptionPathSet &expansions, - Network *network); + ExceptionPathSet &expansions, + Network *network); virtual void visit(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to); + ExceptionThruSeq *thrus, + ExceptionTo *to); private: ExceptionPathSet &expansions_; }; ExpandException::ExpandException(ExceptionPath *exception, - ExceptionPathSet &expansions, - Network *network) : + ExceptionPathSet &expansions, + Network *network) : ExpandedExceptionVisitor(exception, network), expansions_(expansions) { @@ -5159,8 +5175,8 @@ ExpandException::ExpandException(ExceptionPath *exception, void ExpandException::visit(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to) + ExceptionThruSeq *thrus, + ExceptionTo *to) { ExceptionFrom *from_clone = nullptr; if (from) @@ -5177,7 +5193,7 @@ ExpandException::visit(ExceptionFrom *from, if (to) to_clone = to->clone(network_); ExceptionPath *expand = exception_->clone(from_clone, thrus_clone, - to_clone, true); + to_clone, true); expansions_.insert(expand); } @@ -5185,7 +5201,7 @@ ExpandException::visit(ExceptionFrom *from, // point in each from/thru/to. void Sdc::expandException(ExceptionPath *exception, - ExceptionPathSet &expansions) + ExceptionPathSet &expansions) { ExpandException expander(exception, expansions, network_); expander.visitExpansions(); @@ -5195,9 +5211,9 @@ Sdc::expandException(ExceptionPath *exception, void Sdc::resetPath(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max) { checkFromThrusTo(from, thrus, to); // erase prevents range iteration. @@ -5212,13 +5228,13 @@ Sdc::resetPath(ExceptionFrom *from, deleteException(match); for (ExceptionPath *expand : expansions) { - if (expand->resetMatch(from, thrus, to, min_max, network_)) { - unrecordPathDelayInternalFrom(expand); - unrecordPathDelayInternalTo(expand); - delete expand; - } - else - addException(expand); + if (expand->resetMatch(from, thrus, to, min_max, network_)) { + unrecordPathDelayInternalFrom(expand); + unrecordPathDelayInternalTo(expand); + delete expand; + } + else + addException(expand); } } else @@ -5230,57 +5246,56 @@ Sdc::resetPath(ExceptionFrom *from, bool Sdc::exceptionFromStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + ExceptionStateSet *&states) { return exceptionFromStates(pin, rf, clk, clk_rf, min_max, true, states); } bool Sdc::exceptionFromStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - bool include_filter, - ExceptionStateSet *&states) const + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) { bool srch_from = true; if (pin) { if (srch_from) { const ExceptionPathSet *exceptions = - findExceptions(first_from_pin_exceptions_, pin); + findKeyValuePtr(first_from_pin_exceptions_, pin); srch_from &= exceptionFromStates(exceptions, pin, rf, min_max, - include_filter, states); + include_filter, states); } if (srch_from) { const ExceptionPathSet *exceptions = - findExceptions(first_thru_pin_exceptions_, pin); + findKeyValuePtr(first_thru_pin_exceptions_, pin); srch_from &= exceptionFromStates(exceptions, pin, rf, min_max, - include_filter, states); + include_filter, states); } if (srch_from - && (!first_from_inst_exceptions_.empty() + && (!first_from_inst_exceptions_.empty() || !first_thru_inst_exceptions_.empty())) { Instance *inst = network_->instance(pin); const ExceptionPathSet *exceptions = - findExceptions(first_from_inst_exceptions_, inst); + findKeyValuePtr(first_from_inst_exceptions_, inst); srch_from &= exceptionFromStates(exceptions, pin, rf, min_max, - include_filter, states); + include_filter, states); const ExceptionPathSet *exceptions2 = - findExceptions(first_thru_inst_exceptions_, inst); + findKeyValuePtr(first_thru_inst_exceptions_, inst); srch_from &= exceptionFromStates(exceptions2, pin, rf, min_max, - include_filter, states); + include_filter, states); } } if (srch_from && clk) { - const ExceptionPathSet *exceptions = - findExceptions(first_from_clk_exceptions_, clk); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_from_clk_exceptions_, clk); srch_from &= exceptionFromStates(exceptions, pin, clk_rf, min_max, - include_filter, states); + include_filter, states); } if (!srch_from) { delete states; @@ -5291,40 +5306,40 @@ Sdc::exceptionFromStates(const Pin *pin, bool Sdc::exceptionFromStates(const ExceptionPathSet *exceptions, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - bool include_filter, - ExceptionStateSet *&states) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) const { if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->matches(min_max, false) - && (exception->from() == nullptr - || exception->from()->transition()->matches(rf)) - && (include_filter || !exception->isFilter())) { - ExceptionState *state = exception->firstState(); - if (state->matchesNextThru(nullptr, pin, rf, min_max, network_)) - // -from clk -thru reg/clk - state = state->nextState(); - // If the exception is -from and has no -to transition it is - // complete out of the gate. - if (state->isComplete() - && exception->isFalse()) { - // Leave the completed false path state as a marker on the tag, - // but flush all other exception states because they are lower - // priority. - if (states == nullptr) - states = new ExceptionStateSet(); - states->clear(); - states->insert(state); - // No need to examine other exceptions from this - // pin/clock/instance. - return false; - } - if (states == nullptr) - states = new ExceptionStateSet(); - states->insert(state); + && (exception->from() == nullptr + || exception->from()->transition()->matches(rf)) + && (include_filter || !exception->isFilter())) { + ExceptionState *state = exception->firstState(); + if (state->matchesNextThru(nullptr, pin, rf, min_max, network_)) + // -from clk -thru reg/clk + state = state->nextState(); + // If the exception is -from and has no -to transition it is + // complete out of the gate. + if (state->isComplete() + && exception->isFalse()) { + // Leave the completed false path state as a marker on the tag, + // but flush all other exception states because they are lower + // priority. + if (states == nullptr) + states = new ExceptionStateSet(); + states->clear(); + states->insert(state); + // No need to examine other exceptions from this + // pin/clock/instance. + return false; + } + if (states == nullptr) + states = new ExceptionStateSet(); + states->insert(state); } } } @@ -5333,50 +5348,48 @@ Sdc::exceptionFromStates(const ExceptionPathSet *exceptions, void Sdc::exceptionFromClkStates(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const + const RiseFall *rf, + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + ExceptionStateSet *&states) { if (pin) { const ExceptionPathSet *exceptions = - findExceptions(first_from_pin_exceptions_, pin); + findKeyValuePtr(first_from_pin_exceptions_, pin); exceptionFromStates(exceptions, nullptr, rf, min_max, true, states); if (!first_from_inst_exceptions_.empty()) { Instance *inst = network_->instance(pin); const ExceptionPathSet *exceptions = - findExceptions(first_from_inst_exceptions_, inst); + findKeyValuePtr(first_from_inst_exceptions_, inst); exceptionFromStates(exceptions, pin, rf, min_max, true, states); } - const ExceptionPathSet *exceptions2 = - findExceptions(first_thru_pin_exceptions_, pin); + const ExceptionPathSet *exceptions2 = findKeyValuePtr(first_thru_pin_exceptions_, pin); exceptionThruStates(exceptions2, rf, min_max, states); } - const ExceptionPathSet *exceptions = - findExceptions(first_from_clk_exceptions_, clk); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_from_clk_exceptions_, clk); exceptionFromStates(exceptions, pin, clk_rf, min_max, true, states); } void Sdc::filterRegQStates(const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const + const RiseFall *to_rf, + const MinMax *min_max, + ExceptionStateSet *&states) const { if (!first_from_pin_exceptions_.empty()) { auto itr = first_from_pin_exceptions_.find(to_pin); if (itr != first_from_pin_exceptions_.end()) { const ExceptionPathSet &exceptions = itr->second; for (ExceptionPath *exception : exceptions) { - // Hack for filter -from reg/Q. - if (exception->isFilter() - && exception->matchesFirstPt(to_rf, min_max)) { - ExceptionState *state = exception->firstState(); - if (states == nullptr) - states = new ExceptionStateSet(); - states->insert(state); - } + // Hack for filter -from reg/Q. + if (exception->isFilter() + && exception->matchesFirstPt(to_rf, min_max)) { + ExceptionState *state = exception->firstState(); + if (states == nullptr) + states = new ExceptionStateSet(); + states->insert(state); + } } } } @@ -5384,13 +5397,13 @@ Sdc::filterRegQStates(const Pin *to_pin, void Sdc::exceptionThruStates(const Pin *from_pin, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - ExceptionStateSet *&states) const + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + ExceptionStateSet *&states) { const ExceptionPathSet *exceptions = - findExceptions(first_thru_pin_exceptions_, to_pin); + findKeyValuePtr(first_thru_pin_exceptions_, to_pin); exceptionThruStates(exceptions, to_rf, min_max, states); if (!first_thru_edge_exceptions_.empty()) { @@ -5403,28 +5416,28 @@ Sdc::exceptionThruStates(const Pin *from_pin, } if (!first_thru_inst_exceptions_.empty() && (network_->direction(to_pin)->isAnyOutput() - || network_->isLatchData(to_pin))) { + || network_->isLatchData(to_pin))) { const Instance *to_inst = network_->instance(to_pin); const ExceptionPathSet *exceptions = - findExceptions(first_thru_inst_exceptions_, to_inst); + findKeyValuePtr(first_thru_inst_exceptions_, to_inst); exceptionThruStates(exceptions, to_rf, min_max, states); } } void Sdc::exceptionThruStates(const ExceptionPathSet *exceptions, - const RiseFall *to_rf, - const MinMax *min_max, - // Return value. - ExceptionStateSet *&states) const + const RiseFall *to_rf, + const MinMax *min_max, + // Return value. + ExceptionStateSet *&states) const { if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->matchesFirstPt(to_rf, min_max)) { - ExceptionState *state = exception->firstState(); - if (states == nullptr) - states = new ExceptionStateSet(); - states->insert(state); + ExceptionState *state = exception->firstState(); + if (states == nullptr) + states = new ExceptionStateSet(); + states->insert(state); } } } @@ -5434,81 +5447,79 @@ Sdc::exceptionThruStates(const ExceptionPathSet *exceptions, void Sdc::exceptionTo(ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) { if (!first_to_inst_exceptions_.empty()) { Instance *inst = network_->instance(pin); - const ExceptionPathSet *exceptions = - findExceptions(first_to_inst_exceptions_, inst); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_inst_exceptions_, inst); exceptionTo(exceptions, type, pin, rf, - clk_edge, min_max, match_min_max_exactly, - hi_priority_exception, hi_priority); + clk_edge, min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); } if (!first_to_pin_exceptions_.empty()) { - const ExceptionPathSet *exceptions = - findExceptions(first_to_pin_exceptions_, pin); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_pin_exceptions_, pin); exceptionTo(exceptions, type, pin, rf, - clk_edge, min_max, match_min_max_exactly, - hi_priority_exception, hi_priority); + clk_edge, min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); } if (clk_edge && !first_to_clk_exceptions_.empty()) { const ExceptionPathSet *exceptions = - findExceptions(first_to_clk_exceptions_, clk_edge->clock()); + findKeyValuePtr(first_to_clk_exceptions_, clk_edge->clock()); exceptionTo(exceptions, type, pin, rf, clk_edge, - min_max, match_min_max_exactly, - hi_priority_exception, hi_priority); + min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); } } void Sdc::exceptionTo(const ExceptionPathSet *to_exceptions, - ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const + ExceptionPathType type, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const { if (to_exceptions) { for (ExceptionPath *exception : *to_exceptions) { exceptionTo(exception, type, pin, rf, clk_edge, - min_max, match_min_max_exactly, - hi_priority_exception, hi_priority); + min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); } } } void Sdc::exceptionTo(ExceptionPath *exception, - ExceptionPathType type, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - // Return values. - ExceptionPath *&hi_priority_exception, - int &hi_priority) const + ExceptionPathType type, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const { if ((type == ExceptionPathType::any || exception->type() == type) && exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, - match_min_max_exactly, false)) { + match_min_max_exactly, false)) { int priority = exception->priority(min_max); if (hi_priority_exception == nullptr - || priority > hi_priority - || (priority == hi_priority - && exception->tighterThan(hi_priority_exception))) { + || priority > hi_priority + || (priority == hi_priority + && exception->tighterThan(hi_priority_exception))) { hi_priority = priority; hi_priority_exception = exception; } @@ -5517,40 +5528,40 @@ Sdc::exceptionTo(ExceptionPath *exception, bool Sdc::exceptionMatchesTo(ExceptionPath *exception, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin) const { ExceptionTo *to = exception->to(); return exception->matches(min_max, match_min_max_exactly) && ((to == nullptr - && !require_to_pin) - || (to - && to->matches(pin, clk_edge, rf, network_))); + && !require_to_pin) + || (to + && to->matches(pin, clk_edge, rf, network_))); } bool Sdc::isCompleteTo(ExceptionState *state, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin) const { return state->nextThru() == nullptr && exceptionMatchesTo(state->exception(), pin, rf, clk_edge, - min_max, match_min_max_exactly, require_to_pin); + min_max, match_min_max_exactly, require_to_pin); } bool Sdc::isCompleteTo(ExceptionState *state, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max) const { ExceptionPath *exception = state->exception(); ExceptionTo *to = exception->to(); @@ -5564,44 +5575,42 @@ Sdc::isCompleteTo(ExceptionState *state, void Sdc::groupPathsTo(const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return value. - ExceptionPathSeq &group_paths) const + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + // Return value. + ExceptionPathSeq &group_paths) { if (!first_to_inst_exceptions_.empty()) { Instance *inst = network_->instance(pin); - const ExceptionPathSet *exceptions = - findExceptions(first_to_inst_exceptions_, inst); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_inst_exceptions_, inst); groupPathsTo(exceptions, pin, rf, clk_edge, min_max, group_paths); } if (!first_to_pin_exceptions_.empty()) { - const ExceptionPathSet *exceptions = - findExceptions(first_to_pin_exceptions_, pin); + const ExceptionPathSet *exceptions = findKeyValuePtr(first_to_pin_exceptions_, pin); groupPathsTo(exceptions, pin, rf, clk_edge, min_max, group_paths); } if (clk_edge && !first_to_clk_exceptions_.empty()) { const ExceptionPathSet *exceptions = - findExceptions(first_to_clk_exceptions_, clk_edge->clock()); + findKeyValuePtr(first_to_clk_exceptions_, clk_edge->clock()); groupPathsTo(exceptions, pin, rf, clk_edge, min_max, group_paths); } } void Sdc::groupPathsTo(const ExceptionPathSet *to_exceptions, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return value. - ExceptionPathSeq &group_paths) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + // Return value. + ExceptionPathSeq &group_paths) const { if (to_exceptions) { for (ExceptionPath *exception : *to_exceptions) { if (exception->isGroupPath() - && exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, true, false)) - group_paths.push_back(exception); + && exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, true, false)) + group_paths.push_back(exception); } } } @@ -5609,14 +5618,14 @@ Sdc::groupPathsTo(const ExceptionPathSet *to_exceptions, //////////////////////////////////////////////////////////////// Wireload * -Sdc::wireload(const MinMax *min_max) +Sdc::wireload(const MinMax *min_max) const { return wireload_[min_max->index()]; } void Sdc::setWireload(Wireload *wireload, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { for (auto mm_index : min_max->rangeIndex()) wireload_[mm_index] = wireload; @@ -5628,12 +5637,6 @@ Sdc::setWireloadMode(WireloadMode mode) wireload_mode_ = mode; } -WireloadMode -Sdc::wireloadMode() -{ - return wireload_mode_; -} - const WireloadSelection * Sdc::wireloadSelection(const MinMax *min_max) { @@ -5644,8 +5647,8 @@ Sdc::wireloadSelection(const MinMax *min_max) if (lib) { WireloadSelection *default_sel = lib->defaultWireloadSelection(); if (default_sel) { - sel = default_sel; - setWireloadSelection(default_sel, MinMaxAll::all()); + sel = default_sel; + setWireloadSelection(default_sel, MinMaxAll::all()); } } } @@ -5654,7 +5657,7 @@ Sdc::wireloadSelection(const MinMax *min_max) void Sdc::setWireloadSelection(WireloadSelection *selection, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { for (auto mm_index : min_max->rangeIndex()) wireload_selection_[mm_index] = selection; @@ -5699,12 +5702,12 @@ Sdc::deletePinBefore(const Pin *pin) ExceptionPt *first_pt = exception->firstPt(); ExceptionThruSeq *thrus = exception->thrus(); if (thrus) { - for (ExceptionThru *thru : *exception->thrus()) { + for (ExceptionThru *thru : *exception->thrus()) { thru->deletePinBefore(pin, network_); - if (thru == first_pt) - recordExceptionEdges(exception, thru->edges(), - first_thru_edge_exceptions_); - } + if (thru == first_pt) + recordExceptionEdges(exception, thru->edges(), + first_thru_edge_exceptions_); + } } } first_from_pin_exceptions_.erase(pin); @@ -5712,9 +5715,7 @@ Sdc::deletePinBefore(const Pin *pin) first_to_pin_exceptions_.erase(pin); pin_exceptions_.erase(pin); } - - for (int corner_index = 0; corner_index < corners_->count(); corner_index++) - drvr_pin_wire_cap_maps_[corner_index].erase(pin); + drvr_pin_wire_cap_map_.erase(pin); } void @@ -5732,8 +5733,8 @@ Sdc::clkHpinDisablesChanged(const Pin *pin) // hierarchical output - load pins outside the hierarchical instance void findLeafLoadPins(const Pin *pin, - const Network *network, - PinSet *leaf_pins) + const Network *network, + PinSet *leaf_pins) { if (network->isHierarchical(pin)) { PortDirection *dir = network->direction(pin); @@ -5745,9 +5746,9 @@ findLeafLoadPins(const Pin *pin, const Pin *pin1 = pin_iter->next(); bool is_inside = network->isInside(pin1, hinst); if (((is_input && is_inside) - || (is_output && !is_inside)) - && network->isLoad(pin1)) - leaf_pins->insert(pin1); + || (is_output && !is_inside)) + && network->isLoad(pin1)) + leaf_pins->insert(pin1); } delete pin_iter; } @@ -5761,8 +5762,8 @@ findLeafLoadPins(const Pin *pin, // hierarchical output - driver pins inside the hierarchical instance void findLeafDriverPins(const Pin *pin, - const Network *network, - PinSet *leaf_pins) + const Network *network, + PinSet *leaf_pins) { if (network->isHierarchical(pin)) { PortDirection *dir = network->direction(pin); @@ -5774,9 +5775,9 @@ findLeafDriverPins(const Pin *pin, const Pin *pin1 = pin_iter->next(); bool is_inside = network->isInside(pin1, hinst); if (((is_input && !is_inside) - || (is_output && is_inside)) - && network->isDriver(pin1)) - leaf_pins->insert(pin1); + || (is_output && is_inside)) + && network->isDriver(pin1)) + leaf_pins->insert(pin1); } delete pin_iter; } diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 2c66e43da..e6b95667d 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -23,8 +23,11 @@ // This notice may not be removed or altered from any source distribution. %module sdc +%import %{ +#include + #include "Sdc.hh" #include "Wireload.hh" #include "Clock.hh" @@ -89,18 +92,22 @@ private: void write_sdc_cmd(const char *filename, - bool leaf, - bool compatible, - int digits, + bool leaf, + bool compatible, + int digits, bool gzip, - bool no_timestamp) + bool no_timestamp) { - Sta::sta()->writeSdc(filename, leaf, compatible, digits, gzip, no_timestamp); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + sta->writeSdc(sdc, filename, leaf, compatible, digits, gzip, no_timestamp); } void set_analysis_type_cmd(const char *analysis_type) { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); AnalysisType type; if (stringEq(analysis_type, "single")) type = AnalysisType::single; @@ -109,29 +116,35 @@ set_analysis_type_cmd(const char *analysis_type) else if (stringEq(analysis_type, "on_chip_variation")) type = AnalysisType::ocv; else { - Sta::sta()->report()->warn(2121, "unknown analysis type"); + sta->report()->warn(2121, "unknown analysis type"); type = AnalysisType::single; } - Sta::sta()->setAnalysisType(type); + sta->setAnalysisType(type, sdc); } OperatingConditions * operating_conditions(const MinMax *min_max) { - return Sta::sta()->operatingConditions(min_max); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->operatingConditions(min_max, sdc); } void set_operating_conditions_cmd(OperatingConditions *op_cond, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Sta::sta()->setOperatingConditions(op_cond, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setOperatingConditions(op_cond, min_max, sdc); } const char * operating_condition_analysis_type() { - switch (Sta::sta()->sdc()->analysisType()){ + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + switch (sdc->analysisType()) { case AnalysisType::single: return "single"; case AnalysisType::bc_wc: @@ -145,573 +158,692 @@ operating_condition_analysis_type() void set_instance_pvt(Instance *inst, - const MinMaxAll *min_max, - float process, - float voltage, - float temperature) + const MinMaxAll *min_max, + float process, + float voltage, + float temperature) { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); Pvt pvt(process, voltage, temperature); - Sta::sta()->setPvt(inst, min_max, pvt); + sta->setPvt(inst, min_max, pvt, sdc); } float port_ext_pin_cap(const Port *port, - const Corner *corner, - const MinMax *min_max) + const MinMax *min_max) { + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); float pin_cap, wire_cap; int fanout; - Sta::sta()->portExtCaps(port, corner, min_max, pin_cap, wire_cap, fanout); + sta->portExtCaps(port, min_max, sdc, pin_cap, wire_cap, fanout); return pin_cap; } void set_port_ext_pin_cap(const Port *port, const RiseFallBoth *rf, - const Corner *corner, const MinMaxAll *min_max, float cap) { - Sta::sta()->setPortExtPinCap(port, rf, corner, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setPortExtPinCap(port, rf, min_max, cap, sdc); } float port_ext_wire_cap(const Port *port, - const Corner *corner, const MinMax *min_max) { + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); float pin_cap, wire_cap; int fanout; - Sta::sta()->portExtCaps(port, corner, min_max, pin_cap, wire_cap, fanout); + sta->portExtCaps(port, min_max, sdc, pin_cap, wire_cap, fanout); return wire_cap; } void set_port_ext_wire_cap(const Port *port, - bool subtract_pin_cap, const RiseFallBoth *rf, - const Corner *corner, const MinMaxAll *min_max, float cap) { - Sta::sta()->setPortExtWireCap(port, subtract_pin_cap, rf, corner, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setPortExtWireCap(port, rf, min_max, cap, sdc); } void set_port_ext_fanout_cmd(const Port *port, - int fanout, - const Corner *corner, - const MinMaxAll *min_max) + int fanout, + const MinMaxAll *min_max) { - Sta::sta()->setPortExtFanout(port, fanout, corner, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setPortExtFanout(port, fanout, min_max, sdc); } float port_ext_fanout(const Port *port, - const Corner *corner, const MinMax *min_max) { + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); float pin_cap, wire_cap; int fanout; - Sta::sta()->portExtCaps(port, corner, min_max, pin_cap, wire_cap, fanout); + Sta::sta()->portExtCaps(port, min_max, sdc, pin_cap, wire_cap, fanout); return fanout; } void set_net_wire_cap(const Net *net, - bool subtract_pin_cap, - const Corner *corner, - const MinMaxAll *min_max, - float cap) + bool subtract_pin_cap, + const MinMaxAll *min_max, + float cap) { - Sta::sta()->setNetWireCap(net, subtract_pin_cap, corner, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setNetWireCap(net, subtract_pin_cap, min_max, cap, sdc); } void set_wire_load_mode_cmd(const char *mode_name) { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); WireloadMode mode = stringWireloadMode(mode_name); if (mode == WireloadMode::unknown) - Sta::sta()->report()->warn(2122, "unknown wire load mode"); + sta->report()->warn(2122, "unknown wire load mode"); else - Sta::sta()->setWireloadMode(mode); + sta->setWireloadMode(mode, sdc); } void -set_net_resistance(Net *net, - const MinMaxAll *min_max, - float res) +set_wire_load_cmd(Wireload *wireload, + const MinMaxAll *min_max) { - Sta::sta()->setResistance(net, min_max, res); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setWireload(wireload, min_max, sdc); } void -set_wire_load_cmd(Wireload *wireload, - const MinMaxAll *min_max) +set_wire_load_selection_group_cmd(WireloadSelection *selection, + const MinMaxAll *min_max) { - Sta::sta()->setWireload(wireload, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setWireloadSelection(selection, min_max, sdc); } void -set_wire_load_selection_group_cmd(WireloadSelection *selection, - const MinMaxAll *min_max) +set_net_resistance(Net *net, + const MinMaxAll *min_max, + float res) { - Sta::sta()->setWireloadSelection(selection, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setResistance(net, min_max, res, sdc); } void make_clock(const char *name, - PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - char *comment) + PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, + char *comment) { - Sta::sta()->makeClock(name, pins, add_to_pins, period, waveform, comment); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->makeClock(name, pins, add_to_pins, period, waveform, comment, mode); } void make_generated_clock(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, - char *comment) -{ - Sta::sta()->makeGeneratedClock(name, pins, add_to_pins, - src_pin, master_clk, - divide_by, multiply_by, duty_cycle, invert, - combinational, edges, edge_shifts, - comment); + 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) +{ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->makeGeneratedClock(name, pins, add_to_pins, + src_pin, master_clk, + divide_by, multiply_by, duty_cycle, invert, + combinational, edges, edge_shifts, + comment, mode); } void remove_clock_cmd(Clock *clk) { - Sta::sta()->removeClock(clk); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClock(clk, sdc); } void set_propagated_clock_cmd(Clock *clk) { - Sta::sta()->setPropagatedClock(clk); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->setPropagatedClock(clk, mode); } void set_propagated_clock_pin_cmd(Pin *pin) { - Sta::sta()->setPropagatedClock(pin); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->setPropagatedClock(pin, mode); } void unset_propagated_clock_cmd(Clock *clk) { - Sta::sta()->removePropagatedClock(clk); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->removePropagatedClock(clk, mode); } void unset_propagated_clock_pin_cmd(Pin *pin) { - Sta::sta()->removePropagatedClock(pin); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->removePropagatedClock(pin, mode); } void set_clock_slew_cmd(Clock *clk, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) { - Sta::sta()->setClockSlew(clk, rf, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockSlew(clk, rf, min_max, slew, sdc); } void unset_clock_slew_cmd(Clock *clk) { - Sta::sta()->removeClockSlew(clk); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockSlew(clk, sdc); } void set_clock_latency_cmd(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - MinMaxAll *min_max, float delay) + Pin *pin, + const RiseFallBoth *rf, + MinMaxAll *min_max, float delay) { - Sta::sta()->setClockLatency(clk, pin, rf, min_max, delay); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockLatency(clk, pin, rf, min_max, delay, sdc); } void set_clock_insertion_cmd(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay) + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) { - Sta::sta()->setClockInsertion(clk, pin, rf, min_max, early_late, delay); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockInsertion(clk, pin, rf, min_max, early_late, delay, sdc); } void unset_clock_latency_cmd(Clock *clk, - Pin *pin) + Pin *pin) { - Sta::sta()->removeClockLatency(clk, pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockLatency(clk, pin, sdc); } void unset_clock_insertion_cmd(Clock *clk, - Pin *pin) + Pin *pin) { - Sta::sta()->removeClockInsertion(clk, pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockInsertion(clk, pin, sdc); } void set_clock_uncertainty_clk(Clock *clk, - const SetupHoldAll *setup_hold, - float uncertainty) + const SetupHoldAll *setup_hold, + float uncertainty) { - Sta::sta()->setClockUncertainty(clk, setup_hold, uncertainty); + Sta *sta = Sta::sta(); + sta->setClockUncertainty(clk, setup_hold, uncertainty); } void unset_clock_uncertainty_clk(Clock *clk, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold) { - Sta::sta()->removeClockUncertainty(clk, setup_hold); + Sta *sta = Sta::sta(); + sta->removeClockUncertainty(clk, setup_hold); } void set_clock_uncertainty_pin(Pin *pin, - const MinMaxAll *min_max, - float uncertainty) + const MinMaxAll *min_max, + float uncertainty) { - Sta::sta()->setClockUncertainty(pin, min_max, uncertainty); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockUncertainty(pin, min_max, uncertainty, sdc); } void unset_clock_uncertainty_pin(Pin *pin, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Sta::sta()->removeClockUncertainty(pin, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockUncertainty(pin, min_max, sdc); } void set_inter_clock_uncertainty(Clock *from_clk, - const RiseFallBoth *from_tr, - Clock *to_clk, - const RiseFallBoth *to_tr, - const MinMaxAll *min_max, - float uncertainty) + const RiseFallBoth *from_tr, + Clock *to_clk, + const RiseFallBoth *to_tr, + const MinMaxAll *min_max, + float uncertainty) { - Sta::sta()->setClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, - uncertainty); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, + uncertainty, sdc); } void unset_inter_clock_uncertainty(Clock *from_clk, - const RiseFallBoth *from_tr, - Clock *to_clk, - const RiseFallBoth *to_tr, - const MinMaxAll *min_max) + const RiseFallBoth *from_tr, + Clock *to_clk, + const RiseFallBoth *to_tr, + const MinMaxAll *min_max) { - Sta::sta()->removeClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, sdc); } void set_clock_gating_check_cmd(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const SetupHold *setup_hold, + float margin) { - Sta::sta()->setClockGatingCheck(rf, setup_hold, margin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockGatingCheck(rf, setup_hold, margin, sdc); } void set_clock_gating_check_clk_cmd(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin) { - Sta::sta()->setClockGatingCheck(clk, rf, setup_hold, margin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockGatingCheck(clk, rf, setup_hold, margin, sdc); } void set_clock_gating_check_pin_cmd(Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) { - Sta::sta()->setClockGatingCheck(pin, rf, setup_hold, margin, active_value); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockGatingCheck(pin, rf, setup_hold, margin, active_value, sdc); } void set_clock_gating_check_instance_cmd(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) { - Sta::sta()->setClockGatingCheck(inst, rf, setup_hold, margin, active_value); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setClockGatingCheck(inst, rf, setup_hold, margin, active_value, sdc); } void set_data_check_cmd(Pin *from, - const RiseFallBoth *from_rf, - Pin *to, - const RiseFallBoth *to_rf, - Clock *clk, - const SetupHoldAll *setup_hold, - float margin) + const RiseFallBoth *from_rf, + Pin *to, + const RiseFallBoth *to_rf, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin) { - Sta::sta()->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold, margin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold, margin, sdc); } void unset_data_check_cmd(Pin *from, - const RiseFallBoth *from_tr, - Pin *to, - const RiseFallBoth *to_tr, - Clock *clk, - const SetupHoldAll *setup_hold) + const RiseFallBoth *from_tr, + Pin *to, + const RiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold) { - Sta::sta()->removeDataCheck(from, from_tr, to, to_tr, clk, setup_hold); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDataCheck(from, from_tr, to, to_tr, clk, setup_hold, sdc); } void set_input_delay_cmd(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - MinMaxAll *min_max, - bool add, - float delay) + RiseFallBoth *rf, + Clock *clk, + RiseFall *clk_rf, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + MinMaxAll *min_max, + bool add, + float delay) { - Sta::sta()->setInputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included, network_latency_included, - min_max, add, delay); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setInputDelay(pin, rf, clk, clk_rf, ref_pin, + source_latency_included, network_latency_included, + min_max, add, delay, sdc); } void unset_input_delay_cmd(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - MinMaxAll *min_max) + RiseFallBoth *rf, + Clock *clk, + RiseFall *clk_rf, + MinMaxAll *min_max) { - Sta::sta()->removeInputDelay(pin, rf, clk, clk_rf, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeInputDelay(pin, rf, clk, clk_rf, min_max, sdc); } void set_output_delay_cmd(Pin *pin, - const RiseFallBoth *rf, - Clock *clk, - const RiseFall *clk_rf, - Pin *ref_pin, - bool source_latency_included, - bool network_latency_included, - const MinMaxAll *min_max, - bool add, - float delay) + const RiseFallBoth *rf, + Clock *clk, + const RiseFall *clk_rf, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay) { - Sta::sta()->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included, network_latency_included, - min_max, add, delay); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, + source_latency_included, network_latency_included, + min_max, add, delay, sdc); } void unset_output_delay_cmd(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - MinMaxAll *min_max) + RiseFallBoth *rf, + Clock *clk, + RiseFall *clk_rf, + MinMaxAll *min_max) { - Sta::sta()->removeOutputDelay(pin, rf, clk, clk_rf, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeOutputDelay(pin, rf, clk, clk_rf, min_max, sdc); } void disable_cell(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - Sta::sta()->disable(cell, from, to); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(cell, from, to, sdc); } void unset_disable_cell(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - Sta::sta()->removeDisable(cell, from, to); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(cell, from, to, sdc); } void disable_lib_port(LibertyPort *port) { - Sta::sta()->disable(port); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(port, sdc); } void unset_disable_lib_port(LibertyPort *port) { - Sta::sta()->removeDisable(port); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(port, sdc); } void disable_port(Port *port) { - Sta::sta()->disable(port); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(port, sdc); } void unset_disable_port(Port *port) { - Sta::sta()->removeDisable(port); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(port, sdc); } void disable_instance(Instance *instance, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - Sta::sta()->disable(instance, from, to); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(instance, from, to, sdc); } void unset_disable_instance(Instance *instance, - LibertyPort *from, - LibertyPort *to) + LibertyPort *from, + LibertyPort *to) { - Sta::sta()->removeDisable(instance, from, to); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(instance, from, to, sdc); } void disable_pin(Pin *pin) { - Sta::sta()->disable(pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(pin, sdc); } void unset_disable_pin(Pin *pin) { - Sta::sta()->removeDisable(pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(pin, sdc); } void disable_edge(Edge *edge) { - Sta::sta()->disable(edge); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(edge, sdc); } void unset_disable_edge(Edge *edge) { - Sta::sta()->removeDisable(edge); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(edge, sdc); } void disable_timing_arc_set(TimingArcSet *arc_set) { - Sta::sta()->disable(arc_set); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disable(arc_set, sdc); } void unset_disable_timing_arc_set(TimingArcSet *arc_set) { - Sta::sta()->removeDisable(arc_set); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisable(arc_set, sdc); } void disable_clock_gating_check_inst(Instance *inst) { - Sta::sta()->disableClockGatingCheck(inst); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disableClockGatingCheck(inst, sdc); } void disable_clock_gating_check_pin(Pin *pin) { - Sta::sta()->disableClockGatingCheck(pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->disableClockGatingCheck(pin, sdc); } void unset_disable_clock_gating_check_inst(Instance *inst) { - Sta::sta()->removeDisableClockGatingCheck(inst); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisableClockGatingCheck(inst, sdc); } void unset_disable_clock_gating_check_pin(Pin *pin) { - Sta::sta()->removeDisableClockGatingCheck(pin); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeDisableClockGatingCheck(pin, sdc); } EdgeSeq disabled_edges_sorted() { - return Sta::sta()->disabledEdgesSorted(); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return sta->disabledEdgesSorted(mode); } bool timing_arc_disabled(Edge *edge, - TimingArc *arc) + TimingArc *arc) { - Graph *graph = Sta::sta()->graph(); - return !searchThru(edge, arc, graph); + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + return !searchThru(edge, arc, mode); } void make_false_path(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const char *comment) { - Sta::sta()->makeFalsePath(from, thrus, to, min_max, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->makeFalsePath(from, thrus, to, min_max, comment, sdc); } void make_multicycle_path(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment) + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + const char *comment) { - Sta::sta()->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, - path_multiplier, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, + path_multiplier, comment, sdc); } void make_path_delay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, - bool break_path, - float delay, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, + bool break_path, + float delay, const char *comment) { - Sta::sta()->makePathDelay(from, thrus, to, min_max, - ignore_clk_latency, break_path, - delay, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->makePathDelay(from, thrus, to, min_max, + ignore_clk_latency, break_path, + delay, comment, sdc); } void reset_path_cmd(ExceptionFrom * - from, ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max) + from, ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max) { - Sta::sta()->resetPath(from, thrus, to, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->resetPath(from, thrus, to, min_max, sdc); // from/to and thru are owned and deleted by the caller. // ExceptionThruSeq thrus arg is made by TclListSeqExceptionThru // in the swig converter so it is deleted here. @@ -720,404 +852,494 @@ reset_path_cmd(ExceptionFrom * void make_group_path(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment) + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const char *comment) { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); if (name[0] == '\0') name = nullptr; - Sta::sta()->makeGroupPath(name, is_default, from, thrus, to, comment); + sta->makeGroupPath(name, is_default, from, thrus, to, comment, sdc); } bool is_path_group_name(const char *name) { - return Sta::sta()->isPathGroupName(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return Sta::sta()->isPathGroupName(name, sdc); } ExceptionFrom * make_exception_from(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_tr) + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_rf) { - return Sta::sta()->makeExceptionFrom(from_pins, from_clks, from_insts, - from_tr); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return sta->makeExceptionFrom(from_pins, from_clks, from_insts, + from_rf, sdc); } void delete_exception_from(ExceptionFrom *from) { - Sta::sta()->deleteExceptionFrom(from); + Sta *sta = Sta::sta(); + sta->deleteExceptionFrom(from); } void check_exception_from_pins(ExceptionFrom *from, - const char *file, - int line) + const char *file, + int line) { - Sta::sta()->checkExceptionFromPins(from, file, line); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + sta->checkExceptionFromPins(from, file, line, sdc); } ExceptionThru * make_exception_thru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf) + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf) { - return Sta::sta()->makeExceptionThru(pins, nets, insts, rf); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->makeExceptionThru(pins, nets, insts, rf, sdc); } void delete_exception_thru(ExceptionThru *thru) { - Sta::sta()->deleteExceptionThru(thru); + Sta *sta = Sta::sta(); + sta->deleteExceptionThru(thru); } ExceptionTo * make_exception_to(PinSet *to_pins, - ClockSet *to_clks, - InstanceSet *to_insts, - const RiseFallBoth *rf, - RiseFallBoth *end_rf) + ClockSet *to_clks, + InstanceSet *to_insts, + const RiseFallBoth *rf, + RiseFallBoth *end_rf) { - return Sta::sta()->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf, sdc); } void delete_exception_to(ExceptionTo *to) { - Sta::sta()->deleteExceptionTo(to); + Sta *sta = Sta::sta(); + sta->deleteExceptionTo(to); } void check_exception_to_pins(ExceptionTo *to, - const char *file, - int line) + const char *file, + int line) { - Sta::sta()->checkExceptionToPins(to, file, line); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + sta->checkExceptionToPins(to, file, line, sdc); } +//////////////////////////////////////////////////////////////// + ClockGroups * make_clock_groups(const char *name, - bool logically_exclusive, - bool physically_exclusive, - bool asynchronous, - bool allow_paths, - const char *comment) + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + const char *comment) { - return Sta::sta()->makeClockGroups(name, logically_exclusive, - physically_exclusive, asynchronous, - allow_paths, comment); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return sta->makeClockGroups(name, logically_exclusive, + physically_exclusive, asynchronous, + allow_paths, comment, sdc); } void clock_groups_make_group(ClockGroups *clk_groups, - ClockSet *clks) + ClockSet *clks) { - Sta::sta()->makeClockGroup(clk_groups, clks); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->makeClockGroup(clk_groups, clks, sdc); } void unset_clock_groups_logically_exclusive(const char *name) { - Sta::sta()->removeClockGroupsLogicallyExclusive(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsLogicallyExclusive(name, sdc); } void unset_clock_groups_physically_exclusive(const char *name) { - Sta::sta()->removeClockGroupsPhysicallyExclusive(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsPhysicallyExclusive(name, sdc); } void unset_clock_groups_asynchronous(const char *name) { - Sta::sta()->removeClockGroupsAsynchronous(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->removeClockGroupsAsynchronous(name, sdc); } // Debugging function. bool same_clk_group(Clock *clk1, - Clock *clk2) + Clock *clk2) { Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); + Sdc *sdc = sta->cmdSdc(); return sdc->sameClockGroupExplicit(clk1, clk2); } void set_clock_sense_cmd(PinSet *pins, - ClockSet *clks, - bool positive, - bool negative, - bool stop_propagation) + ClockSet *clks, + bool positive, + bool negative, + bool stop_propagation) { Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); if (positive) - sta->setClockSense(pins, clks, ClockSense::positive); + sta->setClockSense(pins, clks, ClockSense::positive, sdc); else if (negative) - sta->setClockSense(pins, clks, ClockSense::negative); + sta->setClockSense(pins, clks, ClockSense::negative, sdc); else if (stop_propagation) - sta->setClockSense(pins, clks, ClockSense::stop); + sta->setClockSense(pins, clks, ClockSense::stop, sdc); else sta->report()->critical(2123, "unknown clock sense"); } void set_input_slew_cmd(Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) { - Sta::sta()->setInputSlew(port, rf, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setInputSlew(port, rf, min_max, slew, sdc); } void set_drive_cell_cmd(LibertyLibrary *library, - LibertyCell *cell, - Port *port, - LibertyPort *from_port, - float from_slew_rise, - float from_slew_fall, - LibertyPort *to_port, - const RiseFallBoth *rf, - const MinMaxAll *min_max) + LibertyCell *cell, + Port *port, + LibertyPort *from_port, + float from_slew_rise, + float from_slew_fall, + LibertyPort *to_port, + const RiseFallBoth *rf, + const MinMaxAll *min_max) { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); float from_slews[RiseFall::index_count]; from_slews[RiseFall::riseIndex()] = from_slew_rise; from_slews[RiseFall::fallIndex()] = from_slew_fall; - Sta::sta()->setDriveCell(library, cell, port, from_port, from_slews, - to_port, rf, min_max); + sta->setDriveCell(library, cell, port, from_port, from_slews, + to_port, rf, min_max, sdc); } void set_drive_resistance_cmd(Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res) + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res) { - Sta::sta()->setDriveResistance(port, rf, min_max, res); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setDriveResistance(port, rf, min_max, res, sdc); } void set_slew_limit_clk(Clock *clk, - const RiseFallBoth *rf, - PathClkOrData clk_data, - const MinMax *min_max, - float slew) + const RiseFallBoth *rf, + PathClkOrData clk_data, + const MinMax *min_max, + float slew) { - Sta::sta()->setSlewLimit(clk, rf, clk_data, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setSlewLimit(clk, rf, clk_data, min_max, slew, sdc); } void set_slew_limit_port(Port *port, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { - Sta::sta()->setSlewLimit(port, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setSlewLimit(port, min_max, slew, sdc); } void set_slew_limit_cell(Cell *cell, - const MinMax *min_max, - float slew) + const MinMax *min_max, + float slew) { - Sta::sta()->setSlewLimit(cell, min_max, slew); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setSlewLimit(cell, min_max, slew, sdc); } void set_port_capacitance_limit(Port *port, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { - Sta::sta()->setCapacitanceLimit(port, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setCapacitanceLimit(port, min_max, cap, sdc); } void set_pin_capacitance_limit(Pin *pin, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { - Sta::sta()->setCapacitanceLimit(pin, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setCapacitanceLimit(pin, min_max, cap, sdc); } void set_cell_capacitance_limit(Cell *cell, - const MinMax *min_max, - float cap) + const MinMax *min_max, + float cap) { - Sta::sta()->setCapacitanceLimit(cell, min_max, cap); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setCapacitanceLimit(cell, min_max, cap, sdc); } void set_latch_borrow_limit_pin(Pin *pin, - float limit) + float limit) { - Sta::sta()->setLatchBorrowLimit(pin, limit); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setLatchBorrowLimit(pin, limit, sdc); } void set_latch_borrow_limit_inst(Instance *inst, - float limit) + float limit) { - Sta::sta()->setLatchBorrowLimit(inst, limit); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setLatchBorrowLimit(inst, limit, sdc); } void set_latch_borrow_limit_clk(Clock *clk, float limit) { - Sta::sta()->setLatchBorrowLimit(clk, limit); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setLatchBorrowLimit(clk, limit, sdc); } void set_min_pulse_width_global(const RiseFallBoth *rf, - float min_width) + float min_width) { - Sta::sta()->setMinPulseWidth(rf, min_width); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMinPulseWidth(rf, min_width, sdc); } void set_min_pulse_width_pin(Pin *pin, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - Sta::sta()->setMinPulseWidth(pin, rf, min_width); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMinPulseWidth(pin, rf, min_width, sdc); } void set_min_pulse_width_clk(Clock *clk, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - Sta::sta()->setMinPulseWidth(clk, rf, min_width); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMinPulseWidth(clk, rf, min_width, sdc); } void set_min_pulse_width_inst(Instance *inst, - const RiseFallBoth *rf, - float min_width) + const RiseFallBoth *rf, + float min_width) { - Sta::sta()->setMinPulseWidth(inst, rf, min_width); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMinPulseWidth(inst, rf, min_width, sdc); } void set_max_area_cmd(float area) { - Sta::sta()->setMaxArea(area); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setMaxArea(area, sdc); } void set_port_fanout_limit(Port *port, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout) { - Sta::sta()->setFanoutLimit(port, min_max, fanout); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setFanoutLimit(port, min_max, fanout, sdc); } void set_cell_fanout_limit(Cell *cell, - const MinMax *min_max, - float fanout) + const MinMax *min_max, + float fanout) { - Sta::sta()->setFanoutLimit(cell, min_max, fanout); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setFanoutLimit(cell, min_max, fanout, sdc); } void set_logic_value_cmd(Pin *pin, - LogicValue value) + LogicValue value) { - Sta::sta()->setLogicValue(pin, value); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + sta->setLogicValue(pin, value, mode); } void set_case_analysis_cmd(Pin *pin, - LogicValue value) + LogicValue value) { - Sta::sta()->setCaseAnalysis(pin, value); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + sta->setCaseAnalysis(pin, value, mode); } void unset_case_analysis_cmd(Pin *pin) { - Sta::sta()->removeCaseAnalysis(pin); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + sta->removeCaseAnalysis(pin, mode); } void set_timing_derate_cmd(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - Sta::sta()->setTimingDerate(type, clk_data, rf, early_late, derate); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setTimingDerate(type, clk_data, rf, early_late, derate, sdc); } void set_timing_derate_net_cmd(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - Sta::sta()->setTimingDerate(net, clk_data, rf, early_late, derate); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setTimingDerate(net, clk_data, rf, early_late, derate, sdc); } void set_timing_derate_inst_cmd(const Instance *inst, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - Sta::sta()->setTimingDerate(inst, type, clk_data, rf, early_late, derate); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setTimingDerate(inst, type, clk_data, rf, early_late, derate, sdc); } void set_timing_derate_cell_cmd(const LibertyCell *cell, - TimingDerateCellType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) + TimingDerateCellType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) { - Sta::sta()->setTimingDerate(cell, type, clk_data, rf, early_late, derate); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setTimingDerate(cell, type, clk_data, rf, early_late, derate, sdc); } void unset_timing_derate_cmd() { - Sta::sta()->unsetTimingDerate(); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->unsetTimingDerate(sdc); } Clock * find_clock(const char *name) { - return Sta::sta()->sdc()->findClock(name); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return sdc->findClock(name); } bool is_clock_src(const Pin *pin) { - return Sta::sta()->isClockSrc(pin); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sta->isClockSrc(pin, sdc); } Clock * default_arrival_clock() { - return Sta::sta()->sdc()->defaultArrivalClock(); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + return sdc->defaultArrivalClock(); } ClockSeq find_clocks_matching(const char *pattern, - bool regexp, - bool nocase) + bool regexp, + bool nocase) { Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); + const Sdc *sdc = sta->cmdSdc(); PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); return sdc->findClocksMatching(&matcher); } @@ -1131,96 +1353,127 @@ update_generated_clks() bool is_clock(Pin *pin) { - return Sta::sta()->isClock(pin); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + return sta->isClock(pin, mode); } +// variable sta_clock_through_tristate_enabled bool -is_ideal_clock(Pin *pin) +clk_thru_tristate_enabled() { - return Sta::sta()->isIdealClock(pin); + return Sta::sta()->clkThruTristateEnabled(); } -bool -is_clock_search(const Pin *pin) +// variable sta_clock_through_tristate_enabled +void +set_clk_thru_tristate_enabled(bool enabled) { - Sta *sta = Sta::sta(); - Graph *graph = sta->graph(); - Search *search = sta->search(); - Vertex *vertex, *bidirect_drvr_vertex; - graph->pinVertices(pin, vertex, bidirect_drvr_vertex); - return search->isClock(vertex); + Sta::sta()->setClkThruTristateEnabled(enabled); } -bool -is_genclk_src(const Pin *pin) +PortSeq +all_inputs_cmd(bool no_clocks) { Sta *sta = Sta::sta(); - Graph *graph = sta->graph(); - Search *search = sta->search(); - Vertex *vertex, *bidirect_drvr_vertex; - graph->pinVertices(pin, vertex, bidirect_drvr_vertex); - return search->isGenClkSrc(vertex); + const Sdc *sdc = sta->cmdSdc(); + sta->ensureLinked(); + return sdc->allInputs(no_clocks); } -bool -pin_is_constrained(Pin *pin) +PortSeq +all_outputs_cmd() { - return Sta::sta()->sdc()->isConstrained(pin); + Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); + sta->ensureLinked(); + return sdc->allOutputs(); } -bool -instance_is_constrained(Instance *inst) -{ - return Sta::sta()->sdc()->isConstrained(inst); -} +//////////////////////////////////////////////////////////////// -bool -net_is_constrained(Net *net) -{ - return Sta::sta()->sdc()->isConstrained(net); -} +// all_register variants -bool -clk_thru_tristate_enabled() +InstanceSet +find_register_instances(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { - return Sta::sta()->clkThruTristateEnabled(); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + InstanceSet insts = sta->findRegisterInstances(clks, clk_tr, + edge_triggered, + latches, mode); + delete clks; + return insts; } -void -set_clk_thru_tristate_enabled(bool enabled) +PinSet +find_register_data_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { - Sta::sta()->setClkThruTristateEnabled(enabled); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterDataPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; } -void -remove_constraints() +PinSet +find_register_clk_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { - Sta::sta()->removeConstraints(); + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterClkPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; } -PortSeq -all_inputs_cmd(bool no_clocks) +PinSet +find_register_async_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { Sta *sta = Sta::sta(); - sta->ensureLinked(); - return sta->sdc()->allInputs(no_clocks); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterAsyncPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; } -PortSeq -all_outputs_cmd() +PinSet +find_register_output_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { Sta *sta = Sta::sta(); - sta->ensureLinked(); - return sta->sdc()->allOutputs(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterOutputPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; } -template Vector +//////////////////////////////////////////////////////////////// + +template std::vector filter_objects(const char *property, - const char *op, - const char *pattern, - Vector *objects) + const char *op, + const char *pattern, + std::vector *objects) { - Vector filtered_objects; + std::vector filtered_objects; if (objects) { Sta *sta = Sta::sta(); Properties &properties = sta->properties(); @@ -1230,7 +1483,7 @@ filter_objects(const char *property, bool not_pattern_match = stringEq(op, "!~"); for (T *object : *objects) { PropertyValue value(properties.getProperty(object, property)); - string prop_str = value.to_string(sta->network()); + std::string prop_str = value.to_string(sta->network()); const char *prop = prop_str.c_str(); if (!prop_str.empty() && ((exact_match && stringEq(prop, pattern)) @@ -1246,81 +1499,81 @@ filter_objects(const char *property, PortSeq filter_ports(const char *property, - const char *op, - const char *pattern, - PortSeq *ports) + const char *op, + const char *pattern, + PortSeq *ports) { return filter_objects(property, op, pattern, ports); } InstanceSeq filter_insts(const char *property, - const char *op, - const char *pattern, - InstanceSeq *insts) + const char *op, + const char *pattern, + InstanceSeq *insts) { return filter_objects(property, op, pattern, insts); } PinSeq filter_pins(const char *property, - const char *op, - const char *pattern, - PinSeq *pins) + const char *op, + const char *pattern, + PinSeq *pins) { return filter_objects(property, op, pattern, pins); } ClockSeq filter_clocks(const char *property, - const char *op, - const char *pattern, - ClockSeq *clocks) + const char *op, + const char *pattern, + ClockSeq *clocks) { return filter_objects(property, op, pattern, clocks); } LibertyCellSeq filter_lib_cells(const char *property, - const char *op, - const char *pattern, - LibertyCellSeq *cells) + const char *op, + const char *pattern, + LibertyCellSeq *cells) { return filter_objects(property, op, pattern, cells); } LibertyPortSeq filter_lib_pins(const char *property, - const char *op, - const char *pattern, - LibertyPortSeq *pins) + const char *op, + const char *pattern, + LibertyPortSeq *pins) { return filter_objects(property, op, pattern, pins); } LibertyLibrarySeq filter_liberty_libraries(const char *property, - const char *op, - const char *pattern, - LibertyLibrarySeq *libs) + const char *op, + const char *pattern, + LibertyLibrarySeq *libs) { return filter_objects(property, op, pattern, libs); } NetSeq filter_nets(const char *property, - const char *op, - const char *pattern, - NetSeq *nets) + const char *op, + const char *pattern, + NetSeq *nets) { return filter_objects(property, op, pattern, nets); } EdgeSeq filter_timing_arcs(const char *property, - const char *op, - const char *pattern, - EdgeSeq *edges) + const char *op, + const char *pattern, + EdgeSeq *edges) { return filter_objects(property, op, pattern, edges); } @@ -1330,8 +1583,10 @@ filter_timing_arcs(const char *property, StringSeq group_path_names() { + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); StringSeq pg_names; - for (auto const& [name, group] : Sta::sta()->sdc()->groupPaths()) + for (auto const& [name, group] : sdc->groupPaths()) pg_names.push_back(name); return pg_names; } @@ -1342,7 +1597,9 @@ void set_voltage_global(const MinMax *min_max, float voltage) { - Sta::sta()->setVoltage(min_max, voltage); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setVoltage(min_max, voltage, sdc); } void @@ -1350,7 +1607,9 @@ set_voltage_net(const Net *net, const MinMax *min_max, float voltage) { - Sta::sta()->setVoltage(net, min_max, voltage); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setVoltage(net, min_max, voltage, sdc); } //////////////////////////////////////////////////////////////// @@ -1359,7 +1618,7 @@ char pin_case_logic_value(const Pin *pin) { Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); + const Sdc *sdc = sta->cmdSdc(); LogicValue value = LogicValue::unknown; bool exists; sdc->caseLogicValue(pin, value, exists); @@ -1370,7 +1629,7 @@ char pin_logic_value(const Pin *pin) { Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); + const Sdc *sdc = sta->cmdSdc(); LogicValue value = LogicValue::unknown; bool exists; sdc->logicValue(pin, value, exists); diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index 112ea504e..1e59be57e 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -34,16 +34,27 @@ namespace eval sta { -define_cmd_args "read_sdc" {[-echo] filename} +define_cmd_args "read_sdc" {[-echo] [-mode mode_name] filename} proc_redirect read_sdc { - parse_key_args "read_sdc" args keys {} flags {-echo} + parse_key_args "read_sdc" args keys {-mode} flags {-echo} check_argc_eq1 "read_sdc" $args set echo [info exists flags(-echo)] set filename [file nativename [lindex $args 0]] - set prev_filename [info script] - include_file $filename $echo 0 + set mode_name {} + if { [info exists keys(-mode)] } { + set mode_name $keys(-mode) + } + set prev_mode [cmd_mode_name] + try { + set_mode_cmd $mode_name + include_file $filename $echo 0 + } finally { + if { $prev_mode != "default" } { + set_mode_cmd $prev_mode + } + } } ################################################################ @@ -107,7 +118,7 @@ proc set_hierarchy_separator { separator } { proc check_path_divider { divider } { set sdc_dividers "/@^#.|" if { !([string length $divider] == 1 - && [string first $divider $sdc_dividers] != -1)} { + && [string first $divider $sdc_dividers] != -1)} { sta_error 342 "hierarchy separator must be one of '$sdc_dividers'." } } @@ -237,11 +248,11 @@ proc all_registers { args } { } if {[info exists flags(-edge_triggered)] \ - && ![info exists flags(-level_sensitive)]} { + && ![info exists flags(-level_sensitive)]} { set edge_triggered 1 set level_sensitive 0 } elseif {[info exists flags(-level_sensitive)] \ - && ![info exists flags(-edge_triggered)]} { + && ![info exists flags(-edge_triggered)]} { set level_sensitive 1 set edge_triggered 0 } else { @@ -257,23 +268,23 @@ proc all_registers { args } { } if [info exists flags(-cells)] { return [find_register_instances $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } elseif [info exists flags(-data_pins)] { return [find_register_data_pins $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } elseif [info exists flags(-clock_pins)] { return [find_register_clk_pins $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } elseif [info exists flags(-async_pins)] { return [find_register_async_pins $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } elseif [info exists flags(-output_pins)] { return [find_register_output_pins $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } else { # -cells is the default. return [find_register_instances $clks $clk_rf \ - $edge_triggered $level_sensitive] + $edge_triggered $level_sensitive] } } @@ -368,19 +379,19 @@ proc get_cells { args } { parse_port_pin_net_arg $keys(-of_objects) pins nets foreach pin $pins { if { [$pin is_top_level_port] } { - set net [get_nets [get_name $pin]] - if { $net != "NULL" } { - lappend nets $net - } + set net [get_nets [get_name $pin]] + if { $net != "NULL" } { + lappend nets $net + } } else { - lappend insts [$pin instance] + lappend insts [$pin instance] } } foreach net $nets { set pin_iter [$net pin_iterator] while { [$pin_iter has_next] } { - set pin [$pin_iter next] - lappend insts [$pin instance] + set pin [$pin_iter next] + lappend insts [$pin instance] } $pin_iter finish } @@ -388,23 +399,23 @@ proc get_cells { args } { check_argc_eq0or1 "get_cells" $args foreach pattern $patterns { if { [is_object $pattern] } { - if { [object_type $pattern] != "Instance" } { - sta_error 326 "object '$pattern' is not an instance." - } - set insts [concat $insts $pattern] + if { [object_type $pattern] != "Instance" } { + sta_error 326 "object '$pattern' is not an instance." + } + set insts [concat $insts $pattern] } else { - if { $divider != $hierarchy_separator } { - regsub $divider $pattern $hierarchy_separator pattern - } - if { $hierarchical } { - set matches [find_instances_hier_matching $pattern $regexp $nocase] - } else { - set matches [find_instances_matching $pattern $regexp $nocase] - } - if { $matches == {} && !$quiet} { - sta_warn 349 "instance '$pattern' not found." - } - set insts [concat $insts $matches] + if { $divider != $hierarchy_separator } { + regsub $divider $pattern $hierarchy_separator pattern + } + if { $hierarchical } { + set matches [find_instances_hier_matching $pattern $regexp $nocase] + } else { + set matches [find_instances_matching $pattern $regexp $nocase] + } + if { $matches == {} && !$quiet} { + sta_warn 349 "instance '$pattern' not found." + } + set insts [concat $insts $matches] } } } @@ -437,17 +448,17 @@ proc get_clocks { args } { foreach pattern $patterns { if { [is_object $pattern] } { if { [object_type $pattern] != "Clock" } { - sta_error 327 "object '$pattern' is not an clock." + sta_error 327 "object '$pattern' is not an clock." } set clocks [concat $clocks $pattern] } else { set matches [find_clocks_matching $pattern $regexp $nocase] if { $matches != {} } { - set clocks [concat $clocks $matches] + set clocks [concat $clocks $matches] } else { - if {![info exists flags(-quiet)]} { - sta_warn 351 "clock '$pattern' not found." - } + if {![info exists flags(-quiet)]} { + sta_warn 351 "clock '$pattern' not found." + } } } } @@ -500,35 +511,35 @@ proc get_lib_cells { args } { set quiet [info exists flags(-quiet)] foreach pattern $patterns { if { [is_object $pattern] } { - if { [object_type $pattern] != "LibertyCell" } { - sta_error 328 "object '$pattern' is not a liberty cell." - } - set cells [concat $cells $pattern] + if { [object_type $pattern] != "LibertyCell" } { + sta_error 328 "object '$pattern' is not a liberty cell." + } + set cells [concat $cells $pattern] } else { - if { ![regexp $cell_regexp $pattern ignore lib_name cell_pattern]} { - set lib_name "*" - set cell_pattern $pattern - } - # Allow wildcards in the library name (incompatible). - set libs [get_libs -quiet $lib_name] - if { $libs == {} } { - if {!$quiet} { - sta_warn 353 "library '$lib_name' not found." - } - } else { - foreach lib $libs { - set matches [$lib find_liberty_cells_matching $cell_pattern \ - $regexp $nocase] - if {$matches != {}} { - set cells [concat $cells $matches] - } - } - if { $cells == {} } { - if {!$quiet} { - sta_warn 354 "cell '$cell_pattern' not found." - } - } - } + if { ![regexp $cell_regexp $pattern ignore lib_name cell_pattern]} { + set lib_name "*" + set cell_pattern $pattern + } + # Allow wildcards in the library name (incompatible). + set libs [get_libs -quiet $lib_name] + if { $libs == {} } { + if {!$quiet} { + sta_warn 353 "library '$lib_name' not found." + } + } else { + foreach lib $libs { + set matches [$lib find_liberty_cells_matching $cell_pattern \ + $regexp $nocase] + if {$matches != {}} { + set cells [concat $cells $matches] + } + } + if { $cells == {} } { + if {!$quiet} { + sta_warn 354 "cell '$cell_pattern' not found." + } + } + } } } } @@ -579,60 +590,60 @@ proc get_lib_pins { args } { set libcells [get_libcells_error "objects" $keys(-of_objects)] foreach libcell $libcells { foreach port [$libcell find_liberty_ports_matching * 0 1] { - # Filter pg ports. - if { ![$port is_pwr_gnd] } { - lappend ports $port - } + # Filter pg ports. + if { ![$port is_pwr_gnd] } { + lappend ports $port + } } } } else { foreach pattern $patterns { if { [is_object $pattern] } { - if { [object_type $pattern] != "LibertyPort" } { - sta_error 329 "object '$pattern' is not a liberty pin." - } - set ports [concat $ports $pattern] + if { [object_type $pattern] != "LibertyPort" } { + sta_error 329 "object '$pattern' is not a liberty pin." + } + set ports [concat $ports $pattern] } else { - # match library/cell/port - set libs {} - if { [regexp $port_regexp1 $pattern ignore lib_name cell_name port_pattern] } { - set libs [get_libs -quiet $lib_name] - # match cell/port - } elseif { [regexp $port_regexp2 $pattern ignore cell_name port_pattern] } { - set libs [get_libs *] - } else { - if { !$quiet } { - sta_warn 355 "library/cell/port '$pattern' not found." - } - return {} - } - if { $libs != {} } { - set found_match 0 - set cells {} - foreach lib $libs { - set cells [$lib find_liberty_cells_matching $cell_name $regexp $nocase] - foreach cell $cells { - set matches [$cell find_liberty_ports_matching $port_pattern \ - $regexp $nocase] - foreach match $matches { - # Filter pg ports. - if { ![$match is_pwr_gnd] } { - lappend ports $match - set found_match 1 - } - } - } - } - if { !$found_match } { - if { !$quiet } { - sta_warn 356 "port '$port_pattern' not found." - } - } - } else { - if { !$quiet } { - sta_warn 357 "library '$lib_name' not found." - } - } + # match library/cell/port + set libs {} + if { [regexp $port_regexp1 $pattern ignore lib_name cell_name port_pattern] } { + set libs [get_libs -quiet $lib_name] + # match cell/port + } elseif { [regexp $port_regexp2 $pattern ignore cell_name port_pattern] } { + set libs [get_libs *] + } else { + if { !$quiet } { + sta_warn 355 "library/cell/port '$pattern' not found." + } + return {} + } + if { $libs != {} } { + set found_match 0 + set cells {} + foreach lib $libs { + set cells [$lib find_liberty_cells_matching $cell_name $regexp $nocase] + foreach cell $cells { + set matches [$cell find_liberty_ports_matching $port_pattern \ + $regexp $nocase] + foreach match $matches { + # Filter pg ports. + if { ![$match is_pwr_gnd] } { + lappend ports $match + set found_match 1 + } + } + } + } + if { !$found_match } { + if { !$quiet } { + sta_warn 356 "port '$port_pattern' not found." + } + } + } else { + if { !$quiet } { + sta_warn 357 "library '$lib_name' not found." + } + } } } } @@ -672,17 +683,17 @@ proc get_libs { args } { foreach pattern $patterns { if { [is_object $pattern] } { if { [object_type $pattern] != "LibertyLibrary" } { - sta_error 330 "object '$pattern' is not a liberty library." + sta_error 330 "object '$pattern' is not a liberty library." } set libs [concat $libs $pattern] } else { set matches [find_liberty_libraries_matching $pattern $regexp $nocase] if {$matches != {}} { - set libs [concat $libs $matches] + set libs [concat $libs $matches] } else { - if {![info exists flags(-quiet)]} { - sta_warn 359 "library '$pattern' not found." - } + if {![info exists flags(-quiet)]} { + sta_warn 359 "library '$pattern' not found." + } } } } @@ -710,8 +721,8 @@ proc find_liberty_libraries_matching { pattern regexp nocase } { set lib [$lib_iter next] set lib_name [get_name $lib] if { (!$regexp && [string match $pattern2 $lib_name]) \ - || ($regexp && $nocase && [regexp -nocase $pattern2 $lib_name]) \ - || ($regexp && !$nocase && [regexp $pattern2 $lib_name]) } { + || ($regexp && $nocase && [regexp -nocase $pattern2 $lib_name]) \ + || ($regexp && !$nocase && [regexp $pattern2 $lib_name]) } { lappend matches $lib } } @@ -758,8 +769,8 @@ proc get_nets { args } { foreach inst $insts { set pin_iter [$inst pin_iterator] while { [$pin_iter has_next] } { - set pin [$pin_iter next] - lappend nets [$pin net] + set pin [$pin_iter next] + lappend nets [$pin net] } $pin_iter finish } @@ -770,20 +781,20 @@ proc get_nets { args } { check_argc_eq0or1 "get_nets" $args foreach pattern $patterns { if { [is_object $pattern] } { - if { [object_type $pattern] != "Net" } { - sta_error 331 "object '$pattern' is not a net." - } - set nets [concat $nets $pattern] + if { [object_type $pattern] != "Net" } { + sta_error 331 "object '$pattern' is not a net." + } + set nets [concat $nets $pattern] } else { - if { $hierarchical } { - set matches [find_nets_hier_matching $pattern $regexp $nocase] - } else { - set matches [find_nets_matching $pattern $regexp $nocase] - } - set nets [concat $nets $matches] - if { $matches == {} && !$quiet } { - sta_warn 361 "net '$pattern' not found." - } + if { $hierarchical } { + set matches [find_nets_hier_matching $pattern $regexp $nocase] + } else { + set matches [find_nets_matching $pattern $regexp $nocase] + } + set nets [concat $nets $matches] + if { $matches == {} && !$quiet } { + sta_warn 361 "net '$pattern' not found." + } } } } @@ -821,22 +832,22 @@ proc get_pins { args } { foreach inst $insts { set pin_iter [$inst pin_iterator] while { [$pin_iter has_next] } { - set pin [$pin_iter next] - # Filter pg ports. - if { ![$pin is_pwr_gnd] } { - lappend pins $pin - } + set pin [$pin_iter next] + # Filter pg ports. + if { ![$pin is_pwr_gnd] } { + lappend pins $pin + } } $pin_iter finish } foreach net $nets { set pin_iter [$net pin_iterator] while { [$pin_iter has_next] } { - set pin [$pin_iter next] - # Filter pg ports. - if { ![$pin is_pwr_gnd] } { - lappend pins $pin - } + set pin [$pin_iter next] + # Filter pg ports. + if { ![$pin is_pwr_gnd] } { + lappend pins $pin + } } $pin_iter finish } @@ -856,25 +867,25 @@ proc get_pins { args } { set patterns [string map {\\ \\\\} $patterns] foreach pattern $patterns { if { [is_object $pattern] } { - if { [object_type $pattern] != "Pin" } { - sta_error 332 "object '$pattern' is not a pin." - } - set pins [concat $pins $pattern] + if { [object_type $pattern] != "Pin" } { + sta_error 332 "object '$pattern' is not a pin." + } + set pins [concat $pins $pattern] } else { - if { $hierarchical } { - set matches [find_pins_hier_matching $pattern $regexp $nocase] - } else { - set matches [find_pins_matching $pattern $regexp $nocase] - } - foreach match $matches { - # Filter pg ports. - if { ![$match is_pwr_gnd] } { - lappend pins $match - } - } - if { $matches == {} && !$quiet } { - sta_warn 363 "pin '$pattern' not found." - } + if { $hierarchical } { + set matches [find_pins_hier_matching $pattern $regexp $nocase] + } else { + set matches [find_pins_matching $pattern $regexp $nocase] + } + foreach match $matches { + # Filter pg ports. + if { ![$match is_pwr_gnd] } { + lappend pins $match + } + } + if { $matches == {} && !$quiet } { + sta_warn 363 "pin '$pattern' not found." + } } } } @@ -918,18 +929,18 @@ proc get_ports { args } { check_argc_eq0or1 "get_ports" $args foreach pattern $patterns { if { [is_object $pattern] } { - if { [object_type $pattern] != "Port" } { - sta_error 333 "object '$pattern' is not a port." - } - set ports [concat $ports $pattern] + if { [object_type $pattern] != "Port" } { + sta_error 333 "object '$pattern' is not a port." + } + set ports [concat $ports $pattern] } else { set matches [find_ports_matching $pattern $regexp $nocase] if { $matches != {} } { - set ports [concat $ports $matches] + set ports [concat $ports $matches] } else { - if {![info exists flags(-quiet)]} { - sta_warn 366 "port '$pattern' not found." - } + if {![info exists flags(-quiet)]} { + sta_warn 366 "port '$pattern' not found." + } } } } @@ -996,10 +1007,10 @@ proc create_clock { args } { check_float "-waveform edge" $edge set edge [time_ui_sta $edge] if { !$first_edge && $edge < $prev_edge } { - sta_error 372 "non-increasing clock -waveform edge times." + sta_error 372 "non-increasing clock -waveform edge times." } if { $edge > [expr $period * 2] } { - sta_error 373 "-waveform time greater than two periods." + sta_error 373 "-waveform time greater than two periods." } lappend waveform $edge set prev_edge $edge @@ -1116,8 +1127,8 @@ proc create_generated_clock { args } { if {[info exists keys(-duty_cycle)]} { set duty_cycle $keys(-duty_cycle) if {![string is double $duty_cycle] \ - || $duty_cycle < 0.0 || $duty_cycle > 100.0} { - sta_error 384 "-duty_cycle is not a float between 0 and 100." + || $duty_cycle < 0.0 || $duty_cycle > 100.0} { + sta_error 384 "-duty_cycle is not a float between 0 and 100." } } } elseif {[info exists keys(-edges)]} { @@ -1129,16 +1140,16 @@ proc create_generated_clock { args } { foreach edge $edges { check_cardinal "-edges" $edge if { $edge <= $prev_edge } { - sta_error 386 "edges times are not monotonically increasing." + sta_error 386 "edges times are not monotonically increasing." } } if [info exists keys(-edge_shift)] { foreach shift $keys(-edge_shift) { - check_float "-edge_shift" $shift - lappend edge_shifts [time_ui_sta $shift] + check_float "-edge_shift" $shift + lappend edge_shifts [time_ui_sta $shift] } if { [llength $edge_shifts] != [llength $edges] } { - sta_error 387 "-edge_shift length does not match -edges length." + sta_error 387 "-edge_shift length does not match -edges length." } } } elseif { $combinational } { @@ -1150,8 +1161,8 @@ proc create_generated_clock { args } { set invert 0 if {[info exists flags(-invert)]} { if {!([info exists keys(-divide_by)] \ - || [info exists keys(-multiply_by)] \ - || [info exists flags(-combinational)])} { + || [info exists keys(-multiply_by)] \ + || [info exists flags(-combinational)])} { sta_error 389 "cannot specify -invert without -multiply_by, -divide_by or -combinational." } set invert 1 @@ -1206,8 +1217,8 @@ define_cmd_args "group_path" \ proc group_path { args } { parse_key_args "group_path" args \ keys {-name -weight -critical_range \ - -from -rise_from -fall_from \ - -to -rise_to -fall_to -comment} \ + -from -rise_from -fall_from \ + -to -rise_to -fall_to -comment} \ flags {-default} 0 set cmd "group_path" @@ -1311,11 +1322,11 @@ proc set_clock_gating_check1 { args rf setup_hold margin active_value } { } foreach pin $pins { set_clock_gating_check_pin_cmd $pin $rf $setup_hold \ - $margin $active_value + $margin $active_value } foreach inst $insts { set_clock_gating_check_instance_cmd $inst $rf $setup_hold \ - $margin $active_value + $margin $active_value } } } @@ -1330,7 +1341,7 @@ proc set_clock_groups { args } { parse_key_args "set_clock_groups" args \ keys {-name -comment} \ flags {-logically_exclusive -physically_exclusive \ - -asynchronous -allow_paths} 0 + -asynchronous -allow_paths} 0 if {[info exists keys(-name)]} { set name $keys(-name) @@ -1352,22 +1363,22 @@ proc set_clock_groups { args } { set comment [parse_comment_key keys] set clk_groups [make_clock_groups $name $logically_exclusive \ - $physically_exclusive $asynchronous $allow_paths \ - $comment] + $physically_exclusive $asynchronous $allow_paths \ + $comment] while { $args != "" } { set arg [lindex $args 0] if {[string match $arg "-group"]} { set group_clks [get_clocks_warn "clocks" [lindex $args 1]] if { $group_clks != {} } { - clock_groups_make_group $clk_groups $group_clks + clock_groups_make_group $clk_groups $group_clks } set args [lrange $args 2 end] } else { if {[is_keyword_arg $arg]} { - sta_warn 402 "unknown keyword argument $arg." + sta_warn 402 "unknown keyword argument $arg." } else { - sta_warn 403 "extra positional argument $arg." + sta_warn 403 "extra positional argument $arg." } set args [lrange $args 1 end] } @@ -1379,7 +1390,7 @@ proc set_clock_groups { args } { define_cmd_args "unset_clock_groups" \ {[-logically_exclusive] [-physically_exclusive]\ [-asynchronous] [-name names] [-all]} - + proc unset_clock_groups { args } { unset_clk_groups_cmd "unset_clock_groups" $args } @@ -1424,11 +1435,11 @@ proc unset_clk_groups_cmd { cmd cmd_args } { } else { foreach name $names { if { $logically_exclusive } { - unset_clock_groups_logically_exclusive $name + unset_clock_groups_logically_exclusive $name } elseif { $physically_exclusive } { - unset_clock_groups_physically_exclusive $name + unset_clock_groups_physically_exclusive $name } elseif { $asynchronous } { - unset_clock_groups_asynchronous $name + unset_clock_groups_asynchronous $name } } } @@ -1474,7 +1485,7 @@ proc set_clock_latency { args } { foreach pin $pins { # Source only allowed on clocks and clock pins. if { ![is_clock_src $pin] } { - sta_error 409 "-source '[get_full_name $pin]' is not a clock pin." + sta_error 409 "-source '[get_full_name $pin]' is not a clock pin." } set_clock_insertion_cmd $pin_clk $pin $rf $min_max $early_late $delay } @@ -1522,7 +1533,7 @@ proc unset_clk_latency_cmd { cmd cmd_args } { foreach pin $pins { # Source only allowed on clocks and clock pins. if { ![is_clock_pin $pin] } { - sta_error 412 "-source '[$pin path_name]' is not a clock pin." + sta_error 412 "-source '[$pin path_name]' is not a clock pin." } unset_clock_insertion_cmd $pin_clk $pin } @@ -1583,8 +1594,8 @@ proc set_clock_sense_cmd1 { cmd cmd_args } { set negative [info exists flags(-negative)] set stop_propagation [info exists flags(-stop_propagation)] if { ($positive && ($negative || $stop_propagation || $pulse)) \ - || ($negative && ($positive || $stop_propagation || $pulse)) \ - || ($stop_propagation && ($positive || $negative || $pulse)) + || ($negative && ($positive || $stop_propagation || $pulse)) \ + || ($stop_propagation && ($positive || $negative || $pulse)) || ($pulse && ($positive || $negative || $stop_propagation)) } { sta_warn 417 "-positive, -negative, -stop_propagation and -pulse are mutually exclusive." } @@ -1703,7 +1714,7 @@ proc set_clock_uncertainty { args } { } if { $from_key != "none" && $to_key == "none" \ - || $from_key == "none" && $to_key != "none" } { + || $from_key == "none" && $to_key != "none" } { sta_error 421 "-from/-to must be used together." } elseif { $from_key != "none" && $to_key != "none" } { # Inter-clock uncertainty. @@ -1715,15 +1726,15 @@ proc set_clock_uncertainty { args } { foreach from_clk $from_clks { foreach to_clk $to_clks { - set_inter_clock_uncertainty $from_clk $from_rf \ - $to_clk $to_rf $min_max $uncertainty + set_inter_clock_uncertainty $from_clk $from_rf \ + $to_clk $to_rf $min_max $uncertainty } } } else { # Single clock uncertainty. check_argc_eq2 "set_clock_uncertainty" $args if { [info exists flags(-rise)] \ - || [info exists flags(-fall)] } { + || [info exists flags(-fall)] } { sta_error 422 "-rise, -fall options not allowed for single clock uncertainty." } set objects [lindex $args 1] @@ -1789,7 +1800,7 @@ proc unset_clk_uncertainty_cmd { cmd cmd_args } { } if { $from_key != "none" && $to_key == "none" \ - || $from_key == "none" && $to_key != "none" } { + || $from_key == "none" && $to_key != "none" } { sta_error 423 "-from/-to must be used together." } elseif { $from_key != "none" && $to_key != "none" } { # Inter-clock uncertainty. @@ -1801,15 +1812,15 @@ proc unset_clk_uncertainty_cmd { cmd cmd_args } { foreach from_clk $from_clks { foreach to_clk $to_clks { - unset_inter_clock_uncertainty $from_clk $from_rf \ - $to_clk $to_rf $min_max + unset_inter_clock_uncertainty $from_clk $from_rf \ + $to_clk $to_rf $min_max } } } else { # Single clock uncertainty. check_argc_eq1 $cmd $cmd_args if { [info exists keys(-rise)] \ - || [info exists keys(-fall)] } { + || [info exists keys(-fall)] } { sta_error 424 "-rise, -fall options not allowed for single clock uncertainty." } set objects [lindex $cmd_args 0] @@ -1964,7 +1975,7 @@ proc set_disable_timing { args } { libcells libports insts ports pins edges timing_arc_sets if { ([info exists keys(-from)] || [info exists keys(-to)]) \ - && ($libports != {} || $pins != {} || $ports != {}) } { + && ($libports != {} || $pins != {} || $ports != {}) } { sta_warn 429 "-from/-to keywords ignored for lib_pin, port and pin arguments." } @@ -2010,7 +2021,7 @@ proc set_disable_timing_instance { inst from to } { } else { foreach from_port $from_ports { foreach to_port $to_ports { - disable_instance $inst $from_port $to_port + disable_instance $inst $from_port $to_port } } } @@ -2770,10 +2781,10 @@ proc set_driving_cell { args } { } else { set library "NULL" if { [is_object $cell_name] } { - if { [object_type $cell_name] != "LibertyCell" } { - sta_error 334 "object '$cell_name' is not a liberty cell." - } - set cell $cell_name + if { [object_type $cell_name] != "LibertyCell" } { + sta_error 334 "object '$cell_name' is not a liberty cell." + } + set cell $cell_name } else { set cell [find_liberty_cell $cell_name] } @@ -2906,11 +2917,11 @@ proc set_input_transition { args } { # set_load port same as -pin_load # set_load net overrides parasitics define_cmd_args "set_load" \ - {[-corner corner] [-rise] [-fall] [-max] [-min] [-subtract_pin_load]\ + {[-rise] [-fall] [-max] [-min] [-subtract_pin_load]\ [-pin_load] [-wire_load] capacitance objects} proc set_load { args } { - parse_key_args "set_load" args keys {-corner} \ + parse_key_args "set_load" args keys {} \ flags {-rise -fall -min -max -subtract_pin_load -pin_load -wire_load}\ check_argc_eq2 "set_load" $args @@ -2918,7 +2929,6 @@ proc set_load { args } { set pin_load [info exists flags(-pin_load)] set wire_load [info exists flags(-wire_load)] set subtract_pin_load [info exists flags(-subtract_pin_load)] - set corner [parse_corner_or_all keys] set min_max [parse_min_max_all_check_flags flags] set rf [parse_rise_fall_flags flags] @@ -2926,7 +2936,6 @@ proc set_load { args } { check_positive_float "capacitance" $cap set cap [capacitance_ui_sta $cap] parse_port_net_args [lindex $args 1] ports nets - if { $ports != {} } { if { $subtract_pin_load } { sta_warn 486 "-subtract_pin_load not allowed for port objects." @@ -2934,11 +2943,11 @@ proc set_load { args } { # -pin_load is the default. if { $pin_load || (!$pin_load && !$wire_load) } { foreach port $ports { - set_port_ext_pin_cap $port $rf $corner $min_max $cap + set_port_ext_pin_cap $port $rf $min_max $cap } } elseif { $wire_load } { foreach port $ports { - set_port_ext_wire_cap $port 0 $rf $corner $min_max $cap + set_port_ext_wire_cap $port $rf $min_max $cap } } } @@ -2953,7 +2962,7 @@ proc set_load { args } { sta_warn 466 "-rise/-fall not allowed for net objects." } foreach net $nets { - set_net_wire_cap $net $subtract_pin_load $corner $min_max $cap + set_net_wire_cap $net $subtract_pin_load $min_max $cap } } } @@ -3103,10 +3112,10 @@ proc set_max_transition { args } { ################################################################ define_cmd_args "set_port_fanout_number" \ - {[-corner corner] [-max] [-min] fanout ports} + {[-max] [-min] fanout ports} proc set_port_fanout_number { args } { - parse_key_args "set_port_fanout_number" args keys {-corner} flags {-max -min} + parse_key_args "set_port_fanout_number" args keys {} flags {-max -min} set min_max [parse_min_max_all_check_flags flags] check_argc_eq2 "set_port_fanout_number" $args @@ -3114,9 +3123,8 @@ proc set_port_fanout_number { args } { set fanout [lindex $args 0] check_positive_integer "fanout" $fanout set ports [get_ports_error "ports" [lindex $args 1]] - set corner [parse_corner_or_all keys] foreach port $ports { - set_port_ext_fanout_cmd $port $fanout $corner $min_max + set_port_ext_fanout_cmd $port $fanout $min_max } } @@ -3628,20 +3636,6 @@ proc set_max_leakage_power { power {unit {}} } { # ################################################################ -define_cmd_args "define_corners" { corner1 [corner2]... } - -proc define_corners { args } { - if { [get_libs -quiet *] != {} } { - sta_error 482 "define_corners must be called before read_liberty." - } - if { [llength $args] == 0 } { - sta_error 577 "define_corners must define at least one corner." - } - define_corners_cmd $args -} - -################################################################ - define_cmd_args "set_pvt"\ {insts [-min] [-max] [-process process] [-voltage voltage]\ [-temperature temperature]} diff --git a/sdc/SdcGraph.cc b/sdc/SdcGraph.cc deleted file mode 100644 index afd14c70e..000000000 --- a/sdc/SdcGraph.cc +++ /dev/null @@ -1,396 +0,0 @@ -// 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 "Stats.hh" -#include "PortDirection.hh" -#include "Network.hh" -#include "Graph.hh" -#include "DisabledPorts.hh" -#include "PortDelay.hh" -#include "ClockLatency.hh" -#include "Sdc.hh" - -namespace sta { - -static void -annotateGraphDisabledWireEdge(const Pin *from_pin, - const Pin *to_pin, - Graph *graph); - -// Annotate constraints to the timing graph. -void -Sdc::annotateGraph() -{ - Stats stats(debug_, report_); - // All output pins are considered constrained because - // they may be downstream from a set_min/max_delay -from that - // does not have a set_output_delay. - annotateGraphConstrainOutputs(); - annotateDisables(); - annotateGraphOutputDelays(); - annotateGraphDataChecks(); - annotateHierClkLatency(); - stats.report("Annotate constraints to graph"); -} - -void -Sdc::annotateGraphConstrainOutputs() -{ - Instance *top_inst = network_->topInstance(); - InstancePinIterator *pin_iter = network_->pinIterator(top_inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyOutput()) - annotateGraphConstrained(pin); - } - delete pin_iter; -} - -void -Sdc::annotateDisables() -{ - PinSet::Iterator pin_iter(disabled_pins_); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - annotateGraphDisabled(pin); - } - - if (!disabled_lib_ports_.empty()) { - VertexIterator vertex_iter(graph_); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - Pin *pin = vertex->pin(); - LibertyPort *port = network_->libertyPort(pin); - if (disabled_lib_ports_.hasKey(port)) - annotateGraphDisabled(pin); - } - } - - Instance *top_inst = network_->topInstance(); - PortSet::Iterator port_iter(disabled_ports_); - while (port_iter.hasNext()) { - const Port *port = port_iter.next(); - Pin *pin = network_->findPin(top_inst, port); - annotateGraphDisabled(pin); - } - - for (const PinPair &pair : disabled_wire_edges_) - annotateGraphDisabledWireEdge(pair.first, pair.second, graph_); - - for (Edge *edge : disabled_edges_) - edge->setIsDisabledConstraint(true); - - DisabledInstancePortsMap::Iterator disable_inst_iter(disabled_inst_ports_); - while (disable_inst_iter.hasNext()) { - DisabledInstancePorts *disabled_inst = disable_inst_iter.next(); - setEdgeDisabledInstPorts(disabled_inst); - } -} - -class DisableHpinEdgeVisitor : public HierPinThruVisitor -{ -public: - DisableHpinEdgeVisitor(Graph *graph); - virtual void visit(const Pin *from_pin, - const Pin *to_pin); - -protected: - bool annotate_; - Graph *graph_; -}; - -DisableHpinEdgeVisitor::DisableHpinEdgeVisitor(Graph *graph) : - HierPinThruVisitor(), - graph_(graph) -{ -} - -void -DisableHpinEdgeVisitor::visit(const Pin *from_pin, - const Pin *to_pin) -{ - annotateGraphDisabledWireEdge(from_pin, to_pin, graph_); -} - -static void -annotateGraphDisabledWireEdge(const Pin *from_pin, - const Pin *to_pin, - Graph *graph) -{ - Vertex *from_vertex = graph->pinDrvrVertex(from_pin); - Vertex *to_vertex = graph->pinLoadVertex(to_pin); - if (from_vertex && to_vertex) { - VertexOutEdgeIterator edge_iter(from_vertex, graph); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->isWire() - && edge->to(graph) == to_vertex) - edge->setIsDisabledConstraint(true); - } - } -} - -void -Sdc::annotateGraphDisabled(const Pin *pin) -{ - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - vertex->setIsDisabledConstraint(true); - if (bidirect_drvr_vertex) - bidirect_drvr_vertex->setIsDisabledConstraint(true); -} - -void -Sdc::setEdgeDisabledInstPorts(DisabledInstancePorts *disabled_inst) -{ - setEdgeDisabledInstPorts(disabled_inst, disabled_inst->instance()); -} - -void -Sdc::setEdgeDisabledInstPorts(DisabledPorts *disabled_port, - Instance *inst) -{ - if (disabled_port->all()) { - InstancePinIterator *pin_iter = network_->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - // set_disable_timing instance does not disable timing checks. - setEdgeDisabledInstFrom(pin, false); - } - delete pin_iter; - } - - // Disable from pins. - LibertyPortSet::Iterator from_iter(disabled_port->from()); - while (from_iter.hasNext()) { - LibertyPort *from_port = from_iter.next(); - Pin *from_pin = network_->findPin(inst, from_port); - if (from_pin) - setEdgeDisabledInstFrom(from_pin, true); - } - - // Disable to pins. - LibertyPortSet::Iterator to_iter(disabled_port->to()); - while (to_iter.hasNext()) { - LibertyPort *to_port = to_iter.next(); - Pin *to_pin = network_->findPin(inst, to_port); - if (to_pin) { - if (network_->direction(to_pin)->isAnyOutput()) { - Vertex *vertex = graph_->pinDrvrVertex(to_pin); - if (vertex) { - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - edge->setIsDisabledConstraint(true); - } - } - } - } - } - - // Disable from/to pins. - if (disabled_port->fromTo()) { - for (const LibertyPortPair &from_to : *disabled_port->fromTo()) { - const LibertyPort *from_port = from_to.first; - const LibertyPort *to_port = from_to.second; - Pin *from_pin = network_->findPin(inst, from_port); - Pin *to_pin = network_->findPin(inst, to_port); - if (from_pin && network_->direction(from_pin)->isAnyInput() - && to_pin) { - Vertex *from_vertex = graph_->pinLoadVertex(from_pin); - Vertex *to_vertex = graph_->pinDrvrVertex(to_pin); - if (from_vertex && to_vertex) { - VertexOutEdgeIterator edge_iter(from_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->to(graph_) == to_vertex) - edge->setIsDisabledConstraint(true); - } - } - } - } - } -} - -void -Sdc::setEdgeDisabledInstFrom(Pin *from_pin, - bool disable_checks) -{ - if (network_->direction(from_pin)->isAnyInput()) { - Vertex *from_vertex = graph_->pinLoadVertex(from_pin); - if (from_vertex) { - VertexOutEdgeIterator edge_iter(from_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (disable_checks - || !edge->role()->isTimingCheck()) - edge->setIsDisabledConstraint(true); - } - } - } -} - -void -Sdc::annotateGraphOutputDelays() -{ - for (OutputDelay *output_delay : output_delays_) { - for (const Pin *lpin : output_delay->leafPins()) - annotateGraphConstrained(lpin); - } -} - -void -Sdc::annotateGraphDataChecks() -{ - DataChecksMap::Iterator data_checks_iter(data_checks_to_map_); - while (data_checks_iter.hasNext()) { - DataCheckSet *checks = data_checks_iter.next(); - DataCheckSet::Iterator check_iter(checks); - // There may be multiple data checks on a single pin, - // but we only need to mark it as constrained once. - if (check_iter.hasNext()) { - DataCheck *check = check_iter.next(); - annotateGraphConstrained(check->to()); - } - } -} - -void -Sdc::annotateGraphConstrained(const PinSet *pins) -{ - PinSet::ConstIterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - annotateGraphConstrained(pin); - } -} - -void -Sdc::annotateGraphConstrained(const InstanceSet *insts) -{ - InstanceSet::ConstIterator inst_iter(insts); - while (inst_iter.hasNext()) { - const Instance *inst = inst_iter.next(); - annotateGraphConstrained(inst); - } -} - -void -Sdc::annotateGraphConstrained(const Instance *inst) -{ - InstancePinIterator *pin_iter = network_->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyInput()) - annotateGraphConstrained(pin); - } - delete pin_iter; -} - -void -Sdc::annotateGraphConstrained(const Pin *pin) -{ - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - // Pin may be hierarchical and have no vertex. - if (vertex) - vertex->setIsConstrained(true); - if (bidirect_drvr_vertex) - bidirect_drvr_vertex->setIsConstrained(true); -} - -void -Sdc::annotateHierClkLatency() -{ - ClockLatencies::Iterator latency_iter(clk_latencies_); - while (latency_iter.hasNext()) { - ClockLatency *latency = latency_iter.next(); - const Pin *pin = latency->pin(); - if (pin && network_->isHierarchical(pin)) - annotateHierClkLatency(pin, latency); - } -} - -void -Sdc::annotateHierClkLatency(const Pin *hpin, - ClockLatency *latency) -{ - EdgesThruHierPinIterator edge_iter(hpin, network_, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - edge_clk_latency_[edge] = latency; - } -} - -ClockLatency * -Sdc::clockLatency(Edge *edge) const -{ - return edge_clk_latency_.findKey(edge); -} - -void -Sdc::clockLatency(Edge *edge, - const RiseFall *rf, - const MinMax *min_max, - // Return values. - float &latency, - bool &exists) const -{ - ClockLatency *latencies = edge_clk_latency_.findKey(edge); - if (latencies) - latencies->delay(rf, min_max, latency, exists); - else { - latency = 0.0; - exists = false; - } -} - -//////////////////////////////////////////////////////////////// - -void -Sdc::removeGraphAnnotations() -{ - VertexIterator vertex_iter(graph_); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - vertex->setIsDisabledConstraint(false); - vertex->setIsConstrained(false); - - VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - edge->setIsDisabledConstraint(false); - } - } - edge_clk_latency_.clear(); -} - -void -Sdc::searchPreamble() -{ - ensureClkHpinDisables(); - ensureClkGroupExclusions(); -} - -} // namespace diff --git a/sdc/Variables.cc b/sdc/Variables.cc index 1cb10bd0a..c97bbba11 100644 --- a/sdc/Variables.cc +++ b/sdc/Variables.cc @@ -32,7 +32,6 @@ Variables::Variables() : propagate_gated_clock_enable_(true), preset_clr_arcs_enabled_(false), cond_default_arcs_enabled_(true), - bidirect_net_paths_enabled_(false), bidirect_inst_paths_enabled_(false), recovery_removal_checks_enabled_(true), gated_clk_checks_enabled_(true), @@ -80,12 +79,6 @@ Variables::setBidirectInstPathsEnabled(bool enabled) bidirect_inst_paths_enabled_ = enabled; } -void -Variables::setBidirectNetPathsEnabled(bool enabled) -{ - bidirect_net_paths_enabled_ = enabled; -} - void Variables::setRecoveryRemovalChecksEnabled(bool enabled) { diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index 32c2450cd..e317a8a4d 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -27,7 +27,10 @@ #include #include #include +#include +#include +#include "ContainerHelpers.hh" #include "Zlib.hh" #include "Report.hh" #include "Error.hh" @@ -54,7 +57,7 @@ #include "Sdc.hh" #include "Fuzzy.hh" #include "StaState.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Variables.hh" #include "WriteSdcPvt.hh" @@ -62,8 +65,8 @@ namespace sta { using std::string; -typedef Set ClockSenseSet; -typedef Vector ClockSenseSeq; +typedef std::set ClockSenseSet; +typedef std::vector ClockSenseSeq; static const char * transRiseFallFlag(const RiseFall *rf); @@ -94,7 +97,7 @@ class WriteGetPort : public WriteSdcObject { public: WriteGetPort(const Port *port, - const WriteSdc *writer); + const WriteSdc *writer); virtual void write() const; private: @@ -119,9 +122,9 @@ class WriteGetPinAndClkKey : public WriteSdcObject { public: WriteGetPinAndClkKey(const Pin *pin, - bool map_hpin_to_drvr, - const Clock *clk, - const WriteSdc *writer); + bool map_hpin_to_drvr, + const Clock *clk, + const WriteSdc *writer); virtual void write() const; private: @@ -132,9 +135,9 @@ class WriteGetPinAndClkKey : public WriteSdcObject }; WriteGetPinAndClkKey::WriteGetPinAndClkKey(const Pin *pin, - bool map_hpin_to_drvr, - const Clock *clk, - const WriteSdc *writer) : + bool map_hpin_to_drvr, + const Clock *clk, + const WriteSdc *writer) : pin_(pin), map_hpin_to_drvr_(map_hpin_to_drvr), clk_(clk), @@ -154,8 +157,8 @@ class WriteGetPin : public WriteSdcObject { public: WriteGetPin(const Pin *pin, - bool map_hpin_to_drvr, - const WriteSdc *writer); + bool map_hpin_to_drvr, + const WriteSdc *writer); virtual void write() const; private: @@ -165,7 +168,7 @@ class WriteGetPin : public WriteSdcObject }; WriteGetPin::WriteGetPin(const Pin *pin, - bool map_hpin_to_drvr, + bool map_hpin_to_drvr, const WriteSdc *writer) : pin_(pin), map_hpin_to_drvr_(map_hpin_to_drvr), @@ -183,7 +186,7 @@ class WriteGetNet : public WriteSdcObject { public: WriteGetNet(const Net *net, - const WriteSdc *writer); + const WriteSdc *writer); virtual void write() const; private: @@ -192,7 +195,7 @@ class WriteGetNet : public WriteSdcObject }; WriteGetNet::WriteGetNet(const Net *net, - const WriteSdc *writer) : + const WriteSdc *writer) : net_(net), writer_(writer) { @@ -208,7 +211,7 @@ class WriteGetInstance : public WriteSdcObject { public: WriteGetInstance(const Instance *inst, - const WriteSdc *writer); + const WriteSdc *writer); virtual void write() const; private: @@ -217,7 +220,7 @@ class WriteGetInstance : public WriteSdcObject }; WriteGetInstance::WriteGetInstance(const Instance *inst, - const WriteSdc *writer) : + const WriteSdc *writer) : inst_(inst), writer_(writer) { @@ -233,7 +236,7 @@ class WriteGetLibCell : public WriteSdcObject { public: WriteGetLibCell(const LibertyCell *cell, - const WriteSdc *writer); + const WriteSdc *writer); virtual void write() const; private: @@ -242,7 +245,7 @@ class WriteGetLibCell : public WriteSdcObject }; WriteGetLibCell::WriteGetLibCell(const LibertyCell *cell, - const WriteSdc *writer) : + const WriteSdc *writer) : cell_(cell), writer_(writer) { @@ -258,7 +261,7 @@ class WriteGetClock : public WriteSdcObject { public: WriteGetClock(const Clock *clk, - const WriteSdc *writer); + const WriteSdc *writer); virtual void write() const; private: @@ -267,7 +270,7 @@ class WriteGetClock : public WriteSdcObject }; WriteGetClock::WriteGetClock(const Clock *clk, - const WriteSdc *writer) : + const WriteSdc *writer) : clk_(clk), writer_(writer) { @@ -282,29 +285,30 @@ WriteGetClock::write() const //////////////////////////////////////////////////////////////// void -writeSdc(Instance *instance, - const char *filename, - const char *creator, - bool map_hpins, - bool native, - int digits, +writeSdc(const Sdc *sdc, + Instance *instance, + const char *filename, + const char *creator, + bool map_hpins, + bool native, + int digits, bool gzip, - bool no_timestamp, - Sdc *sdc) + bool no_timestamp) { - WriteSdc writer(instance, creator, map_hpins, native, - digits, no_timestamp, sdc); + WriteSdc writer(sdc, instance, creator, map_hpins, native, + digits, no_timestamp); writer.write(filename, gzip); } -WriteSdc::WriteSdc(Instance *instance, - const char *creator, - bool map_hpins, - bool native, - int digits, - bool no_timestamp, - Sdc *sdc) : +WriteSdc::WriteSdc(const Sdc *sdc, + Instance *instance, + const char *creator, + bool map_hpins, + bool native, + int digits, + bool no_timestamp) : StaState(sdc), + sdc_(sdc), instance_(instance), creator_(creator), map_hpins_(map_hpins), @@ -412,7 +416,7 @@ void WriteSdc::writeClock(Clock *clk) const { gzprintf(stream_, "create_clock -name %s", - clk->name()); + clk->name()); if (clk->addToPins()) gzprintf(stream_, " -add"); gzprintf(stream_, " -period "); @@ -435,7 +439,7 @@ void WriteSdc::writeGeneratedClock(Clock *clk) const { gzprintf(stream_, "create_generated_clock -name %s", - clk->name()); + clk->name()); if (clk->addToPins()) gzprintf(stream_, " -add"); gzprintf(stream_, " -source "); @@ -517,8 +521,8 @@ WriteSdc::writeClockUncertainty(const Clock *clk) const void WriteSdc::writeClockUncertainty(const Clock *clk, - const char *setup_hold, - float value) const + const char *setup_hold, + float value) const { gzprintf(stream_, "set_clock_uncertainty %s", setup_hold); writeTime(value); @@ -528,18 +532,13 @@ WriteSdc::writeClockUncertainty(const Clock *clk, void WriteSdc::writeClockUncertaintyPins() const { - PinClockUncertaintyMap::Iterator iter(sdc_->pin_clk_uncertainty_map_); - while (iter.hasNext()) { - const Pin *pin; - ClockUncertainties *uncertainties; - iter.next(pin, uncertainties); + for (const auto [pin, uncertainties] : sdc_->pin_clk_uncertainty_map_) writeClockUncertaintyPin(pin, uncertainties); - } } void WriteSdc::writeClockUncertaintyPin(const Pin *pin, - ClockUncertainties *uncertainties) + ClockUncertainties *uncertainties) const { float setup; @@ -559,8 +558,8 @@ WriteSdc::writeClockUncertaintyPin(const Pin *pin, } void WriteSdc::writeClockUncertaintyPin(const Pin *pin, - const char *setup_hold, - float value) const + const char *setup_hold, + float value) const { gzprintf(stream_, "set_clock_uncertainty %s", setup_hold); writeTime(value); @@ -572,25 +571,23 @@ WriteSdc::writeClockUncertaintyPin(const Pin *pin, void WriteSdc::writeClockLatencies() const { - ClockLatencies::Iterator latency_iter(sdc_->clockLatencies()); - while (latency_iter.hasNext()) { - ClockLatency *latency = latency_iter.next(); + for (ClockLatency *latency : *sdc_->clockLatencies()) { const Pin *pin = latency->pin(); const Clock *clk = latency->clock(); if (pin && clk) { WriteGetPinAndClkKey write_pin(pin, true, clk, this); writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(), - write_pin); + write_pin); } else if (pin) { WriteGetPin write_pin(pin, true, this); writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(), - write_pin); + write_pin); } else if (clk) { WriteGetClock write_clk(clk, this); writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(), - write_clk); + write_clk); } } } @@ -618,27 +615,25 @@ WriteSdc::writeClockInsertions() const void WriteSdc::writeClockInsertion(ClockInsertion *insert, - WriteSdcObject &write_obj) const + WriteSdcObject &write_obj) const { RiseFallMinMax *early_values = insert->delays(EarlyLate::early()); RiseFallMinMax *late_values = insert->delays(EarlyLate::late()); if (early_values->equal(late_values)) writeRiseFallMinMaxTimeCmd("set_clock_latency -source", - late_values, write_obj); + late_values, write_obj); else { writeRiseFallMinMaxTimeCmd("set_clock_latency -source -early", - early_values, write_obj); + early_values, write_obj); writeRiseFallMinMaxTimeCmd("set_clock_latency -source -late", - late_values, write_obj); + late_values, write_obj); } } void WriteSdc::writePropagatedClkPins() const { - PinSet::Iterator pin_iter(sdc_->propagated_clk_pins_); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + for (const Pin *pin : sdc_->propagated_clk_pins_) { gzprintf(stream_, "set_propagated_clock "); writeGetPin(pin, true); gzprintf(stream_, "\n"); @@ -648,12 +643,8 @@ WriteSdc::writePropagatedClkPins() const void WriteSdc::writeInterClockUncertainties() const { - InterClockUncertaintySet::Iterator - uncertainty_iter(sdc_->inter_clk_uncertainties_); - while (uncertainty_iter.hasNext()) { - InterClockUncertainty *uncertainty = uncertainty_iter.next(); + for (InterClockUncertainty *uncertainty : sdc_->inter_clk_uncertainties_) writeInterClockUncertainty(uncertainty); - } } void @@ -680,24 +671,24 @@ writeInterClockUncertainty(InterClockUncertainty *uncertainty) const else { for (auto src_rf : RiseFall::range()) { for (auto tgt_rf : RiseFall::range()) { - for (auto setup_hold : SetupHold::range()) { - float value; - bool exists; - 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"); - writeGetClock(uncertainty->src()); - gzprintf(stream_, " -%s_to ", - tgt_rf == RiseFall::rise() ? "rise" : "fall"); - writeGetClock(uncertainty->target()); - gzprintf(stream_, " %s ", - setupHoldFlag(setup_hold)); - writeTime(value); - gzprintf(stream_, "\n"); - } - } + for (auto setup_hold : SetupHold::range()) { + float value; + bool exists; + 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"); + writeGetClock(uncertainty->src()); + gzprintf(stream_, " -%s_to ", + tgt_rf == RiseFall::rise() ? "rise" : "fall"); + writeGetClock(uncertainty->target()); + gzprintf(stream_, " %s ", + setupHoldFlag(setup_hold)); + writeTime(value); + gzprintf(stream_, "\n"); + } + } } } } @@ -714,11 +705,8 @@ WriteSdc::writeInputDelays() const PortDelayLess port_delay_less(sdc_network_); sort(delays, port_delay_less); - PortDelaySeq::Iterator delay_iter(delays); - while (delay_iter.hasNext()) { - PortDelay *input_delay = delay_iter.next(); + for (PortDelay *input_delay : delays) writePortDelay(input_delay, true, "set_input_delay"); - } } void @@ -737,20 +725,20 @@ WriteSdc::writeOutputDelays() const void WriteSdc::writePortDelay(PortDelay *port_delay, - bool is_input_delay, - const char *sdc_cmd) const + bool is_input_delay, + const char *sdc_cmd) const { RiseFallMinMax *delays = port_delay->delays(); float rise_min, rise_max, fall_min, fall_max; bool rise_min_exists, rise_max_exists, fall_min_exists, fall_max_exists; delays->value(RiseFall::rise(), MinMax::min(), - rise_min, rise_min_exists); + rise_min, rise_min_exists); delays->value(RiseFall::rise(), MinMax::max(), - rise_max, rise_max_exists); + rise_max, rise_max_exists); delays->value(RiseFall::fall(), MinMax::min(), - fall_min, fall_min_exists); + fall_min, fall_min_exists); delays->value(RiseFall::fall(), MinMax::max(), - fall_max, fall_max_exists); + fall_max, fall_max_exists); // Try to compress the four port delays. if (rise_min_exists && rise_max_exists @@ -760,52 +748,52 @@ WriteSdc::writePortDelay(PortDelay *port_delay, && fall_min == rise_min && fall_max == rise_min) writePortDelay(port_delay, is_input_delay, rise_min, - RiseFallBoth::riseFall(), MinMaxAll::all(), sdc_cmd); + RiseFallBoth::riseFall(), MinMaxAll::all(), sdc_cmd); else if (rise_min_exists - && rise_max_exists - && rise_max == rise_min - && fall_min_exists - && fall_max_exists - && fall_min == fall_max) { + && rise_max_exists + && rise_max == rise_min + && fall_min_exists + && fall_max_exists + && fall_min == fall_max) { writePortDelay(port_delay, is_input_delay, rise_min, - RiseFallBoth::rise(), MinMaxAll::all(), sdc_cmd); + RiseFallBoth::rise(), MinMaxAll::all(), sdc_cmd); writePortDelay(port_delay, is_input_delay, fall_min, - RiseFallBoth::fall(), MinMaxAll::all(), sdc_cmd); + RiseFallBoth::fall(), MinMaxAll::all(), sdc_cmd); } else if (rise_min_exists - && fall_min_exists - && rise_min == fall_min - && rise_max_exists - && fall_max_exists - && rise_max == fall_max) { + && fall_min_exists + && rise_min == fall_min + && rise_max_exists + && fall_max_exists + && rise_max == fall_max) { writePortDelay(port_delay, is_input_delay, rise_min, - RiseFallBoth::riseFall(), MinMaxAll::min(), sdc_cmd); + RiseFallBoth::riseFall(), MinMaxAll::min(), sdc_cmd); writePortDelay(port_delay, is_input_delay, rise_max, - RiseFallBoth::riseFall(), MinMaxAll::max(), sdc_cmd); + RiseFallBoth::riseFall(), MinMaxAll::max(), sdc_cmd); } else { if (rise_min_exists) writePortDelay(port_delay, is_input_delay, rise_min, - RiseFallBoth::rise(), MinMaxAll::min(), sdc_cmd); + RiseFallBoth::rise(), MinMaxAll::min(), sdc_cmd); if (rise_max_exists) writePortDelay(port_delay, is_input_delay, rise_max, - RiseFallBoth::rise(), MinMaxAll::max(), sdc_cmd); + RiseFallBoth::rise(), MinMaxAll::max(), sdc_cmd); if (fall_min_exists) writePortDelay(port_delay, is_input_delay, fall_min, - RiseFallBoth::fall(), MinMaxAll::min(), sdc_cmd); + RiseFallBoth::fall(), MinMaxAll::min(), sdc_cmd); if (fall_max_exists) writePortDelay(port_delay, is_input_delay, fall_max, - RiseFallBoth::fall(), MinMaxAll::max(), sdc_cmd); + RiseFallBoth::fall(), MinMaxAll::max(), sdc_cmd); } } void WriteSdc::writePortDelay(PortDelay *port_delay, - bool is_input_delay, - float delay, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const char *sdc_cmd) const + bool is_input_delay, + float delay, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const char *sdc_cmd) const { gzprintf(stream_, "%s ", sdc_cmd); writeTime(delay); @@ -816,8 +804,8 @@ WriteSdc::writePortDelay(PortDelay *port_delay, gzprintf(stream_, " -clock_fall"); } gzprintf(stream_, "%s%s -add_delay ", - transRiseFallFlag(rf), - minMaxFlag(min_max)); + transRiseFallFlag(rf), + minMaxFlag(min_max)); const Pin *ref_pin = port_delay->refPin(); if (ref_pin) { gzprintf(stream_, "-reference_pin "); @@ -833,7 +821,7 @@ class PinClockPairNameLess public: PinClockPairNameLess(const Network *network); bool operator()(const PinClockPair &pin_clk1, - const PinClockPair &pin_clk2) const; + const PinClockPair &pin_clk2) const; private: PinPathNameLess pin_less_; @@ -846,7 +834,7 @@ PinClockPairNameLess::PinClockPairNameLess(const Network *network) : bool PinClockPairNameLess::operator()(const PinClockPair &pin_clk1, - const PinClockPair &pin_clk2) const + const PinClockPair &pin_clk2) const { const Pin *pin1 = pin_clk1.first; const Pin *pin2 = pin_clk2.first; @@ -854,15 +842,15 @@ PinClockPairNameLess::operator()(const PinClockPair &pin_clk1, const Clock *clk2 = pin_clk2.second; return pin_less_(pin1, pin2) || (pin1 == pin2 - && ((clk1 == nullptr && clk2) - || (clk1 && clk2 - && clk1->index() < clk2->index()))); + && ((clk1 == nullptr && clk2) + || (clk1 && clk2 + && clk1->index() < clk2->index()))); } void WriteSdc::writeClockSenses() const { - Vector pin_clks; + std::vector pin_clks; for (const auto& [pin_clk, sense] : sdc_->clk_sense_map_) pin_clks.push_back(pin_clk); @@ -872,7 +860,7 @@ WriteSdc::writeClockSenses() const for (auto pin_clk : pin_clks) { ClockSense sense; bool exists; - sdc_->clk_sense_map_.findKey(pin_clk, sense, exists); + findKeyValue(sdc_->clk_sense_map_, pin_clk, sense, exists); if (exists) writeClockSense(pin_clk, sense); } @@ -880,7 +868,7 @@ WriteSdc::writeClockSenses() const void WriteSdc::writeClockSense(PinClockPair &pin_clk, - ClockSense sense) const + ClockSense sense) const { const char *flag = nullptr; if (sense == ClockSense::positive) @@ -904,13 +892,13 @@ class ClockGroupLess { public: bool operator()(const ClockGroup *clk_group1, - const ClockGroup *clk_group2) const; + const ClockGroup *clk_group2) const; }; bool ClockGroupLess::operator()(const ClockGroup *clk_group1, - const ClockGroup *clk_group2) const + const ClockGroup *clk_group2) const { size_t size1 = clk_group1->size(); size_t size2 = clk_group2->size(); @@ -929,17 +917,17 @@ ClockGroupLess::operator()(const ClockGroup *clk_group1, clks2.push_back(clk2); sort(clks2, ClockNameLess()); - ClockSeq::Iterator clk_iter3(clks1); - ClockSeq::Iterator clk_iter4(clks2); - while (clk_iter3.hasNext() - && clk_iter4.hasNext()) { - Clock *clk1 = clk_iter3.next(); - Clock *clk2 = clk_iter4.next(); + ClockSeq::iterator clk_iter3 = clks1.begin(); + ClockSeq::iterator clk_iter4 = clks2.begin(); + while (clk_iter3 != clks1.end() + && clk_iter4 != clks2.end()) { + Clock *clk1 = *clk_iter3++; + Clock *clk2 = *clk_iter4++; int cmp = strcmp(clk1->name(), clk2->name()); if (cmp < 0) - return true; + return true; else if (cmp > 0) - return false; + return false; } return false; } @@ -964,17 +952,12 @@ WriteSdc::writeClockGroups(ClockGroups *clk_groups) const gzprintf(stream_, "-asynchronous \\\n"); if (clk_groups->allowPaths()) gzprintf(stream_, "-allow_paths \\\n"); - Vector groups; - ClockGroupSet::Iterator group_iter1(clk_groups->groups()); - while (group_iter1.hasNext()) { - ClockGroup *clk_group = group_iter1.next(); + std::vector groups; + for (ClockGroup *clk_group : *clk_groups->groups()) groups.push_back(clk_group); - } sort(groups, ClockGroupLess()); bool first = true; - Vector::Iterator group_iter2(groups); - while (group_iter2.hasNext()) { - ClockGroup *clk_group = group_iter2.next(); + for (ClockGroup *clk_group : groups) { if (!first) gzprintf(stream_, "\\\n"); gzprintf(stream_, " -group "); @@ -1012,31 +995,31 @@ WriteSdc::writeDisabledCells() const 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()); - writeGetLibCell(cell); - gzprintf(stream_, "\n"); + 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()); + writeGetLibCell(cell); + gzprintf(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()); - writeGetLibCell(cell); - gzprintf(stream_, "\n"); + gzprintf(stream_, "set_disable_timing -from {%s} ", + from_port->name()); + writeGetLibCell(cell); + gzprintf(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()); - writeGetLibCell(cell); - gzprintf(stream_, "\n"); + gzprintf(stream_, "set_disable_timing -to {%s} ", + to_port->name()); + writeGetLibCell(cell); + gzprintf(stream_, "\n"); } } if (disable->timingArcSets()) { @@ -1086,31 +1069,31 @@ WriteSdc::writeDisabledInstances() const 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()); - writeGetInstance(inst); - gzprintf(stream_, "\n"); + 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()); + writeGetInstance(inst); + gzprintf(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()); - writeGetInstance(inst); - gzprintf(stream_, "\n"); + gzprintf(stream_, "set_disable_timing -from {%s} ", + from_port->name()); + writeGetInstance(inst); + gzprintf(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()); - writeGetInstance(inst); - gzprintf(stream_, "\n"); + gzprintf(stream_, "set_disable_timing -to {%s} ", + to_port->name()); + writeGetInstance(inst); + gzprintf(stream_, "\n"); } } } @@ -1146,7 +1129,7 @@ WriteSdc::writeDisabledEdges() const void WriteSdc::findMatchingEdges(Edge *edge, - EdgeSet &matches) const + EdgeSet &matches) const { Vertex *from_vertex = edge->from(graph_); Vertex *to_vertex = edge->to(graph_); @@ -1161,11 +1144,11 @@ WriteSdc::findMatchingEdges(Edge *edge, bool WriteSdc::edgeSenseIsUnique(Edge *edge, - EdgeSet &matches) const + EdgeSet &matches) const { for (Edge *match : matches) { if (match != edge - && match->sense() == edge->sense()) + && match->sense() == edge->sense()) return false; } return true; @@ -1201,7 +1184,7 @@ WriteSdc::writeExceptions() const sort(exceptions, ExceptionPathLess(network_)); for (ExceptionPath *exception : exceptions) { if (!exception->isFilter() - && !exception->isLoop()) + && !exception->isLoop()) writeException(exception); } } @@ -1237,12 +1220,12 @@ WriteSdc::writeExceptionCmd(ExceptionPath *exception) const if (min_max == MinMaxAll::min()) { // For hold MCPs default is -start. if (exception->useEndClk()) - gzprintf(stream_, " -end"); + gzprintf(stream_, " -end"); } else { // For setup MCPs default is -end. if (!exception->useEndClk()) - gzprintf(stream_, " -start"); + gzprintf(stream_, " -start"); } } else if (exception->isPathDelay()) { @@ -1268,7 +1251,7 @@ WriteSdc::writeExceptionValue(ExceptionPath *exception) const { if (exception->isMultiCycle()) gzprintf(stream_, " %d", - exception->pathMultiplier()); + exception->pathMultiplier()); else if (exception->isPathDelay()) { gzprintf(stream_, " "); writeTime(exception->delay()); @@ -1293,8 +1276,8 @@ WriteSdc::writeExceptionTo(ExceptionTo *to) const void WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to, - const char *from_to_key, - bool map_hpin_to_drvr) const + const char *from_to_key, + bool map_hpin_to_drvr) const { const RiseFallBoth *rf = from_to->transition(); const char *rf_prefix = "-"; @@ -1314,7 +1297,7 @@ WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to, PinSeq pins = sortByPathName(from_to->pins(), sdc_network_); for (const Pin *pin : pins) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + gzprintf(stream_, "\\\n "); writeGetPin(pin, map_hpin_to_drvr); first = false; } @@ -1325,7 +1308,7 @@ 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 "); + gzprintf(stream_, "\\\n "); writeGetInstance(inst); first = false; } @@ -1365,7 +1348,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 "); + gzprintf(stream_, "\\\n "); writeGetNet(net); first = false; } @@ -1374,7 +1357,7 @@ WriteSdc::writeExceptionThru(ExceptionThru *thru) const InstanceSeq insts = sortByPathName(thru->instances(), sdc_network_); for (const Instance *inst : insts) { if (multi_objs && !first) - gzprintf(stream_, "\\\n "); + gzprintf(stream_, "\\\n "); writeGetInstance(inst); first = false; } @@ -1385,28 +1368,28 @@ WriteSdc::writeExceptionThru(ExceptionThru *thru) const void WriteSdc::mapThruHpins(ExceptionThru *thru, - PinSeq &pins) const + PinSeq &pins) const { if (thru->pins()) { for (const Pin *pin : *thru->pins()) { // Map hierarical pins to load pins outside of outputs or inside of inputs. if (network_->isHierarchical(pin)) { - Instance *hinst = network_->instance(pin); - bool hpin_is_output = network_->direction(pin)->isAnyOutput(); - PinConnectedPinIterator *cpin_iter = network_->connectedPinIterator(pin); - while (cpin_iter->hasNext()) { - const Pin *cpin = cpin_iter->next(); - if (network_->isLoad(cpin) - && ((hpin_is_output - && !network_->isInside(network_->instance(cpin), hinst)) - || (!hpin_is_output - && network_->isInside(network_->instance(cpin), hinst)))) - pins.push_back(cpin); - } - delete cpin_iter; + Instance *hinst = network_->instance(pin); + bool hpin_is_output = network_->direction(pin)->isAnyOutput(); + PinConnectedPinIterator *cpin_iter = network_->connectedPinIterator(pin); + while (cpin_iter->hasNext()) { + const Pin *cpin = cpin_iter->next(); + if (network_->isLoad(cpin) + && ((hpin_is_output + && !network_->isInside(network_->instance(cpin), hinst)) + || (!hpin_is_output + && network_->isInside(network_->instance(cpin), hinst)))) + pins.push_back(cpin); + } + delete cpin_iter; } else - pins.push_back(pin); + pins.push_back(pin); } } } @@ -1416,7 +1399,7 @@ WriteSdc::mapThruHpins(ExceptionThru *thru, void WriteSdc::writeDataChecks() const { - Vector checks; + std::vector checks; for (const auto [pin, checks1] : sdc_->data_checks_to_map_) { for (DataCheck *check : *checks1) checks.push_back(check); @@ -1435,18 +1418,18 @@ WriteSdc::writeDataCheck(DataCheck *check) const check->marginIsOneValue(setup_hold, margin, one_value); if (one_value) writeDataCheck(check, RiseFallBoth::riseFall(), - RiseFallBoth::riseFall(), setup_hold, margin); + RiseFallBoth::riseFall(), setup_hold, margin); else { for (auto from_rf : RiseFall::range()) { - for (auto to_rf : RiseFall::range()) { - float margin; - bool margin_exists; - check->margin(from_rf, to_rf, setup_hold, margin, margin_exists); - if (margin_exists) { - writeDataCheck(check, from_rf->asRiseFallBoth(), - to_rf->asRiseFallBoth(), setup_hold, margin); - } - } + for (auto to_rf : RiseFall::range()) { + float margin; + bool margin_exists; + check->margin(from_rf, to_rf, setup_hold, margin, margin_exists); + if (margin_exists) { + writeDataCheck(check, from_rf->asRiseFallBoth(), + to_rf->asRiseFallBoth(), setup_hold, margin); + } + } } } } @@ -1454,10 +1437,10 @@ WriteSdc::writeDataCheck(DataCheck *check) const void WriteSdc::writeDataCheck(DataCheck *check, - const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHold *setup_hold, - float margin) const + const RiseFallBoth *from_rf, + const RiseFallBoth *to_rf, + const SetupHold *setup_hold, + float margin) const { const char *from_key = "-from"; if (from_rf == RiseFallBoth::rise()) @@ -1474,7 +1457,7 @@ WriteSdc::writeDataCheck(DataCheck *check, gzprintf(stream_, " %s ", to_key); writeGetPin(check->to(), false); gzprintf(stream_, "%s ", - setupHoldFlag(setup_hold)); + setupHoldFlag(setup_hold)); writeTime(margin); gzprintf(stream_, "\n"); } @@ -1513,14 +1496,13 @@ WriteSdc::writeWireload() const WireloadMode wireload_mode = sdc_->wireloadMode(); if (wireload_mode != WireloadMode::unknown) gzprintf(stream_, "set_wire_load_mode \"%s\"\n", - wireloadModeString(wireload_mode)); + wireloadModeString(wireload_mode)); } void WriteSdc::writeNetLoads() const { - int corner_index = 0; // missing corner arg - for (const auto [net, caps] : sdc_->net_wire_cap_maps_[corner_index]) { + for (const auto [net, caps] : sdc_->net_wire_cap_map_) { float min_cap, max_cap; bool min_exists, max_exists; caps.value(MinMax::min(), min_cap, min_exists); @@ -1539,8 +1521,8 @@ WriteSdc::writeNetLoads() const void WriteSdc::writeNetLoad(const Net *net, - const MinMaxAll *min_max, - float cap) const + const MinMaxAll *min_max, + float cap) const { gzprintf(stream_, "set_load "); gzprintf(stream_, "%s ", minMaxFlag(min_max)); @@ -1564,18 +1546,17 @@ WriteSdc::writePortLoads() const void WriteSdc::writePortLoads(const Port *port) const { - const Corner *corner = corners_->findCorner(0); // missing corner arg - PortExtCap *ext_cap = sdc_->portExtCap(port, corner); + const PortExtCap *ext_cap = sdc_->portExtCap(port); if (ext_cap) { WriteGetPort write_port(port, this); writeRiseFallMinMaxCapCmd("set_load -pin_load", - ext_cap->pinCap(), - write_port); + ext_cap->pinCap(), + write_port); writeRiseFallMinMaxCapCmd("set_load -wire_load", - ext_cap->wireCap(), - write_port); + ext_cap->wireCap(), + write_port); writeMinMaxIntValuesCmd("set_port_fanout_number", - ext_cap->fanout(), write_port); + ext_cap->fanout(), write_port); } } @@ -1588,33 +1569,33 @@ WriteSdc::writeDriveResistances() const InputDrive *drive = sdc_->findInputDrive(port); if (drive) { for (auto rf : RiseFall::range()) { - if (drive->driveResistanceMinMaxEqual(rf)) { - float res; - bool exists; - drive->driveResistance(rf, MinMax::max(), res, exists); - gzprintf(stream_, "set_drive %s ", - transRiseFallFlag(rf)); - writeResistance(res); - gzprintf(stream_, " "); - writeGetPort(port); - gzprintf(stream_, "\n"); - } - else { - for (auto min_max : MinMax::range()) { - float res; - bool exists; - drive->driveResistance(rf, min_max, res, exists); - if (exists) { - gzprintf(stream_, "set_drive %s %s ", - transRiseFallFlag(rf), - minMaxFlag(min_max)); - writeResistance(res); - gzprintf(stream_, " "); - writeGetPort(port); - gzprintf(stream_, "\n"); - } - } - } + if (drive->driveResistanceMinMaxEqual(rf)) { + float res; + bool exists; + drive->driveResistance(rf, MinMax::max(), res, exists); + gzprintf(stream_, "set_drive %s ", + transRiseFallFlag(rf)); + writeResistance(res); + gzprintf(stream_, " "); + writeGetPort(port); + gzprintf(stream_, "\n"); + } + else { + for (auto min_max : MinMax::range()) { + float res; + bool exists; + drive->driveResistance(rf, min_max, res, exists); + if (exists) { + gzprintf(stream_, "set_drive %s %s ", + transRiseFallFlag(rf), + minMaxFlag(min_max)); + writeResistance(res); + gzprintf(stream_, " "); + writeGetPort(port); + gzprintf(stream_, "\n"); + } + } + } } } } @@ -1630,47 +1611,47 @@ WriteSdc::writeDrivingCells() const InputDrive *drive = sdc_->findInputDrive(port); if (drive) { InputDriveCell *drive_rise_min = drive->driveCell(RiseFall::rise(), - MinMax::min()); + MinMax::min()); InputDriveCell *drive_rise_max = drive->driveCell(RiseFall::rise(), - MinMax::max()); + MinMax::max()); InputDriveCell *drive_fall_min = drive->driveCell(RiseFall::fall(), - MinMax::min()); + MinMax::min()); InputDriveCell *drive_fall_max = drive->driveCell(RiseFall::fall(), - MinMax::max()); + MinMax::max()); if (drive_rise_min - && drive_rise_max - && drive_fall_min - && drive_fall_max - && drive_rise_min->equal(drive_rise_max) - && drive_rise_min->equal(drive_fall_min) - && drive_rise_min->equal(drive_fall_max)) - // Only write one set_driving_cell if possible. - writeDrivingCell(port, drive_rise_min, nullptr, nullptr); + && drive_rise_max + && drive_fall_min + && drive_fall_max + && drive_rise_min->equal(drive_rise_max) + && drive_rise_min->equal(drive_fall_min) + && drive_rise_min->equal(drive_fall_max)) + // Only write one set_driving_cell if possible. + writeDrivingCell(port, drive_rise_min, nullptr, nullptr); else { - if (drive_rise_min - && drive_rise_max - && drive_rise_min->equal(drive_rise_max)) - writeDrivingCell(port, drive_rise_min, RiseFall::rise(), nullptr); - else { - if (drive_rise_min) - writeDrivingCell(port, drive_rise_min, RiseFall::rise(), - MinMax::min()); - if (drive_rise_max) - writeDrivingCell(port, drive_rise_max, RiseFall::rise(), - MinMax::max()); - } - if (drive_fall_min - && drive_fall_max - && drive_fall_min->equal(drive_fall_max)) - writeDrivingCell(port, drive_fall_min, RiseFall::fall(), nullptr); - else { - if (drive_fall_min) - writeDrivingCell(port, drive_fall_min, RiseFall::fall(), - MinMax::min()); - if (drive_fall_max) - writeDrivingCell(port, drive_fall_max, RiseFall::fall(), - MinMax::max()); - } + if (drive_rise_min + && drive_rise_max + && drive_rise_min->equal(drive_rise_max)) + writeDrivingCell(port, drive_rise_min, RiseFall::rise(), nullptr); + else { + if (drive_rise_min) + writeDrivingCell(port, drive_rise_min, RiseFall::rise(), + MinMax::min()); + if (drive_rise_max) + writeDrivingCell(port, drive_rise_max, RiseFall::rise(), + MinMax::max()); + } + if (drive_fall_min + && drive_fall_max + && drive_fall_min->equal(drive_fall_max)) + writeDrivingCell(port, drive_fall_min, RiseFall::fall(), nullptr); + else { + if (drive_fall_min) + writeDrivingCell(port, drive_fall_min, RiseFall::fall(), + MinMax::min()); + if (drive_fall_max) + writeDrivingCell(port, drive_fall_max, RiseFall::fall(), + MinMax::max()); + } } } } @@ -1679,9 +1660,9 @@ WriteSdc::writeDrivingCells() const void WriteSdc::writeDrivingCell(Port *port, - InputDriveCell *drive_cell, - const RiseFall *rf, - const MinMax *min_max) const + InputDriveCell *drive_cell, + const RiseFall *rf, + const MinMax *min_max) const { const LibertyCell *cell = drive_cell->cell(); const LibertyPort *from_port = drive_cell->fromPort(); @@ -1699,10 +1680,10 @@ WriteSdc::writeDrivingCell(Port *port, gzprintf(stream_, " -lib_cell %s", cell->name()); if (from_port) gzprintf(stream_, " -from_pin {%s}", - from_port->name()); + from_port->name()); gzprintf(stream_, - " -pin {%s} -input_transition_rise ", - to_port->name()); + " -pin {%s} -input_transition_rise ", + to_port->name()); writeTime(from_slews[RiseFall::riseIndex()]); gzprintf(stream_, " -input_transition_fall "); writeTime(from_slews[RiseFall::fallIndex()]); @@ -1740,21 +1721,21 @@ WriteSdc::writeNetResistances() const sdc_->resistance(net, MinMax::min(), min_res, min_exists); sdc_->resistance(net, MinMax::max(), max_res, max_exists); if (min_exists && max_exists - && min_res == max_res) + && min_res == max_res) writeNetResistance(net, MinMaxAll::all(), min_res); else { if (min_exists) - writeNetResistance(net, MinMaxAll::min(), min_res); + writeNetResistance(net, MinMaxAll::min(), min_res); if (max_exists) - writeNetResistance(net, MinMaxAll::max(), max_res); + writeNetResistance(net, MinMaxAll::max(), max_res); } } } void WriteSdc::writeNetResistance(const Net *net, - const MinMaxAll *min_max, - float res) const + const MinMaxAll *min_max, + float res) const { gzprintf(stream_, "set_resistance "); writeResistance(res); @@ -1844,8 +1825,8 @@ WriteSdc::caseAnalysisValueStr(const Pin *pin) const } void -WriteSdc::sortedLogicValuePins(LogicValueMap &value_map, - PinSeq &pins) const +WriteSdc::sortedLogicValuePins(const LogicValueMap &value_map, + PinSeq &pins) const { for (const auto [pin, value] : value_map) pins.push_back(pin); @@ -1866,7 +1847,7 @@ WriteSdc::writeDeratings() const WriteGetNet write_net(net, this); for (auto early_late : EarlyLate::range()) { writeDerating(factors, TimingDerateType::net_delay, early_late, - &write_net); + &write_net); } } @@ -1888,32 +1869,32 @@ WriteSdc::writeDerating(DeratingFactorsGlobal *factors) const bool delay_is_one_value, check_is_one_value, net_is_one_value; float delay_value, check_value, net_value; factors->factors(TimingDerateType::cell_delay)->isOneValue(early_late, - delay_is_one_value, - delay_value); + delay_is_one_value, + delay_value); factors->factors(TimingDerateType::net_delay)->isOneValue(early_late, - net_is_one_value, - net_value); + net_is_one_value, + net_value); DeratingFactors *cell_check_factors = factors->factors(TimingDerateType::cell_check); cell_check_factors->isOneValue(early_late, check_is_one_value, check_value); if (delay_is_one_value - && net_is_one_value - && delay_value == net_value - && (!cell_check_factors->hasValue() - || (check_is_one_value && check_value == 1.0))) { + && net_is_one_value + && delay_value == net_value + && (!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)); - writeFloat(delay_value); - gzprintf(stream_, "\n"); + gzprintf(stream_, "set_timing_derate %s ", earlyLateFlag(early_late)); + writeFloat(delay_value); + gzprintf(stream_, "\n"); } } else { for (int type_index = 0; - type_index < timing_derate_type_count; - type_index++) { - TimingDerateType type = static_cast(type_index); - DeratingFactors *type_factors = factors->factors(type); - writeDerating(type_factors, type, early_late, nullptr); + type_index < timing_derate_type_count; + type_index++) { + TimingDerateType type = static_cast(type_index); + DeratingFactors *type_factors = factors->factors(type); + writeDerating(type_factors, type, early_late, nullptr); } } } @@ -1921,7 +1902,7 @@ WriteSdc::writeDerating(DeratingFactorsGlobal *factors) const void WriteSdc::writeDerating(DeratingFactorsCell *factors, - WriteSdcObject *write_obj) const + WriteSdcObject *write_obj) const { for (auto early_late : EarlyLate::range()) { DeratingFactors *delay_factors=factors->factors(TimingDerateCellType::cell_delay); @@ -1933,9 +1914,9 @@ WriteSdc::writeDerating(DeratingFactorsCell *factors, void WriteSdc::writeDerating(DeratingFactors *factors, - TimingDerateType type, - const MinMax *early_late, - WriteSdcObject *write_obj) const + TimingDerateType type, + const MinMax *early_late, + WriteSdcObject *write_obj) const { const char *type_key = timingDerateTypeKeyword(type); bool is_one_value; @@ -1944,57 +1925,57 @@ WriteSdc::writeDerating(DeratingFactors *factors, if (is_one_value) { if (value != 1.0) { gzprintf(stream_, "set_timing_derate %s %s ", - type_key, - earlyLateFlag(early_late)); + type_key, + earlyLateFlag(early_late)); writeFloat(value); if (write_obj) { - gzprintf(stream_, " "); - write_obj->write(); + gzprintf(stream_, " "); + write_obj->write(); } gzprintf(stream_, "\n"); } } else { for (int clk_data_index = 0; - clk_data_index < path_clk_or_data_count; - clk_data_index++) { + clk_data_index < path_clk_or_data_count; + clk_data_index++) { PathClkOrData clk_data = static_cast(clk_data_index); static const char *clk_data_keys[] = {"-clock", "-data"}; const char *clk_data_key = clk_data_keys[clk_data_index]; 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); - writeFloat(value); - if (write_obj) { - gzprintf(stream_, " "); - write_obj->write(); - } - gzprintf(stream_, "\n"); - } + if (value != 1.0) { + gzprintf(stream_, "set_timing_derate %s %s %s ", + type_key, + earlyLateFlag(early_late), + clk_data_key); + writeFloat(value); + if (write_obj) { + gzprintf(stream_, " "); + write_obj->write(); + } + gzprintf(stream_, "\n"); + } } else { - for (auto rf : RiseFall::range()) { - float factor; - 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)); - writeFloat(factor); - if (write_obj) { - gzprintf(stream_, " "); - write_obj->write(); - } - gzprintf(stream_, "\n"); - } - } + for (auto rf : RiseFall::range()) { + float factor; + 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)); + writeFloat(factor); + if (write_obj) { + gzprintf(stream_, " "); + write_obj->write(); + } + gzprintf(stream_, "\n"); + } + } } } } @@ -2077,7 +2058,7 @@ WriteSdc::writeMinPulseWidths() const void WriteSdc::writeMinPulseWidths(RiseFallValues *min_widths, - WriteSdcObject &write_obj) const + WriteSdcObject &write_obj) const { bool hi_exists, low_exists; float hi, low; @@ -2096,8 +2077,8 @@ WriteSdc::writeMinPulseWidths(RiseFallValues *min_widths, void WriteSdc::writeMinPulseWidth(const char *hi_low, - float value, - WriteSdcObject &write_obj) const + float value, + WriteSdcObject &write_obj) const { gzprintf(stream_, "set_min_pulse_width %s", hi_low); writeTime(value); @@ -2172,44 +2153,43 @@ void WriteSdc::writeClkSlewLimits() const { const MinMax *min_max = MinMax::max(); - ClockSeq clks; - sdc_->sortedClocks(clks); + ClockSeq clks = sdc_->sortedClocks(); for (const Clock *clk : clks) { float rise_clk_limit, fall_clk_limit, rise_data_limit, fall_data_limit; bool rise_clk_exists, fall_clk_exists, rise_data_exists, fall_data_exists; clk->slewLimit(RiseFall::rise(), PathClkOrData::clk, min_max, - rise_clk_limit, rise_clk_exists); + rise_clk_limit, rise_clk_exists); clk->slewLimit(RiseFall::fall(), PathClkOrData::clk, min_max, - fall_clk_limit, fall_clk_exists); + fall_clk_limit, fall_clk_exists); clk->slewLimit(RiseFall::rise(), PathClkOrData::data, min_max, - rise_data_limit, rise_data_exists); + rise_data_limit, rise_data_exists); clk->slewLimit(RiseFall::fall(), PathClkOrData::data, min_max, - fall_data_limit, fall_data_exists); + fall_data_limit, fall_data_exists); if (rise_clk_exists && fall_clk_exists - && rise_data_exists && fall_data_exists - && fall_clk_limit == rise_clk_limit - && rise_data_limit == rise_clk_limit - && fall_data_limit == rise_clk_limit) + && rise_data_exists && fall_data_exists + && fall_clk_limit == rise_clk_limit + && rise_data_limit == rise_clk_limit + && fall_data_limit == rise_clk_limit) writeClkSlewLimit("", "", clk, rise_clk_limit); else { if (rise_clk_exists && fall_clk_exists - && fall_clk_limit == rise_clk_limit) - writeClkSlewLimit("-clock_path ", "", clk, rise_clk_limit); + && fall_clk_limit == rise_clk_limit) + writeClkSlewLimit("-clock_path ", "", clk, rise_clk_limit); else { - if (rise_clk_exists) - writeClkSlewLimit("-clock_path ", "-rise ", clk, rise_clk_limit); - if (fall_clk_exists) - writeClkSlewLimit("-clock_path ", "-fall ", clk, fall_clk_limit); + if (rise_clk_exists) + writeClkSlewLimit("-clock_path ", "-rise ", clk, rise_clk_limit); + if (fall_clk_exists) + writeClkSlewLimit("-clock_path ", "-fall ", clk, fall_clk_limit); } if (rise_data_exists && fall_data_exists - && fall_data_limit == rise_data_limit) - writeClkSlewLimit("-data_path ", "", clk, rise_data_limit); + && fall_data_limit == rise_data_limit) + writeClkSlewLimit("-data_path ", "", clk, rise_data_limit); else { - if (rise_data_exists) - writeClkSlewLimit("-data_path ", "-rise ", clk, rise_data_limit); - if (fall_data_exists) { - writeClkSlewLimit("-data_path ", "-fall ", clk, fall_data_limit); - } + if (rise_data_exists) + writeClkSlewLimit("-data_path ", "-rise ", clk, rise_data_limit); + if (fall_data_exists) { + writeClkSlewLimit("-data_path ", "-fall ", clk, fall_data_limit); + } } } } @@ -2217,9 +2197,9 @@ WriteSdc::writeClkSlewLimits() const void WriteSdc::writeClkSlewLimit(const char *clk_data, - const char *rise_fall, - const Clock *clk, - float limit) const + const char *rise_fall, + const Clock *clk, + float limit) const { gzprintf(stream_, "set_max_transition %s%s", clk_data, rise_fall); writeTime(limit); @@ -2237,7 +2217,7 @@ WriteSdc::writeCapLimits() const void WriteSdc::writeCapLimits(const MinMax *min_max, - const char *cmd) const + const char *cmd) const { float cap; bool exists; @@ -2295,7 +2275,7 @@ WriteSdc::writeFanoutLimits() const void WriteSdc::writeFanoutLimits(const MinMax *min_max, - const char *cmd) const + const char *cmd) const { float fanout; bool exists; @@ -2311,11 +2291,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); - writeFloat(fanout); - gzprintf(stream_, " "); - writeGetPort(port); - gzprintf(stream_, "\n"); + gzprintf(stream_, "%s ", cmd); + writeFloat(fanout); + gzprintf(stream_, " "); + writeGetPort(port); + gzprintf(stream_, "\n"); } } delete port_iter; @@ -2359,7 +2339,7 @@ WriteSdc::writeGetTimingArcs(Edge *edge) const void WriteSdc::writeGetTimingArcs(Edge *edge, - const char *filter) const + const char *filter) const { gzprintf(stream_, "[%s -from ", getTimingArcsCmd()); Vertex *from_vertex = edge->from(graph_); @@ -2384,8 +2364,8 @@ void WriteSdc::writeGetLibCell(const LibertyCell *cell) const { gzprintf(stream_, "[get_lib_cells {%s/%s}]", - cell->libertyLibrary()->name(), - cell->name()); + cell->libertyLibrary()->name(), + cell->name()); } void @@ -2394,9 +2374,9 @@ 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()); + lib->name(), + cell->name(), + port->name()); } void @@ -2413,8 +2393,8 @@ WriteSdc::writeGetClocks(ClockSet *clks) const void WriteSdc::writeGetClocks(ClockSet *clks, - bool multiple, - bool &first) const + bool multiple, + bool &first) const { ClockSeq clks1 = sortByName(clks); for (const Clock *clk : clks1) { @@ -2429,7 +2409,7 @@ void WriteSdc::writeGetClock(const Clock *clk) const { gzprintf(stream_, "[get_clocks {%s}]", - clk->name()); + clk->name()); } void @@ -2440,19 +2420,19 @@ WriteSdc::writeGetPort(const Port *port) const void WriteSdc::writeGetPins(const PinSet *pins, - bool map_hpin_to_drvr) const + bool map_hpin_to_drvr) const { if (map_hpins_) { PinSet leaf_pins(network_);; for (const Pin *pin : *pins) { if (network_->isHierarchical(pin)) { - if (map_hpin_to_drvr) - findLeafDriverPins(const_cast(pin), network_, &leaf_pins); - else - findLeafLoadPins(const_cast(pin), network_, &leaf_pins); + if (map_hpin_to_drvr) + findLeafDriverPins(const_cast(pin), network_, &leaf_pins); + else + findLeafLoadPins(const_cast(pin), network_, &leaf_pins); } else - leaf_pins.insert(pin); + leaf_pins.insert(pin); } PinSeq pins1 = sortByPathName(&leaf_pins, sdc_network_); writeGetPins1(&pins1); @@ -2491,7 +2471,7 @@ WriteSdc::writeGetPin(const Pin *pin) const void WriteSdc::writeGetPin(const Pin *pin, - bool map_hpin_to_drvr) const + bool map_hpin_to_drvr) const { if (map_hpins_ && network_->isHierarchical(pin)) { PinSet pins(network_); @@ -2562,101 +2542,101 @@ WriteSdc::writeCommentSeparator() const void WriteSdc::writeRiseFallMinMaxTimeCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - WriteSdcObject &write_object) const + const RiseFallMinMax *values, + WriteSdcObject &write_object) const { writeRiseFallMinMaxCmd(sdc_cmd, values, units_->timeUnit()->scale(), - write_object); + write_object); } void WriteSdc::writeRiseFallMinMaxCapCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - WriteSdcObject &write_object) const + const RiseFallMinMax *values, + WriteSdcObject &write_object) const { writeRiseFallMinMaxCmd(sdc_cmd, values, units_->capacitanceUnit()->scale(), - write_object); + write_object); } void WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - float scale, - WriteSdcObject &write_object) const + const RiseFallMinMax *values, + float scale, + WriteSdcObject &write_object) const { float fall_min, fall_max, rise_min, rise_max; bool fall_min_exists, fall_max_exists, rise_min_exists, rise_max_exists; values->value(RiseFall::fall(), MinMax::min(), - fall_min, fall_min_exists); + fall_min, fall_min_exists); values->value(RiseFall::fall(), MinMax::max(), - fall_max, fall_max_exists); + fall_max, fall_max_exists); values->value(RiseFall::rise(), MinMax::min(), - rise_min, rise_min_exists); + rise_min, rise_min_exists); values->value(RiseFall::rise(), MinMax::max(), - rise_max, rise_max_exists); + rise_max, rise_max_exists); if (fall_min_exists && fall_max_exists && rise_min_exists && rise_max_exists) { if (fall_min == rise_min - && rise_max == rise_min - && fall_max == rise_min) { + && rise_max == rise_min + && fall_max == rise_min) { // rise/fall/min/max match. writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, - RiseFallBoth::riseFall(), MinMaxAll::all(), - write_object); + RiseFallBoth::riseFall(), MinMaxAll::all(), + write_object); } else if (rise_min == fall_min - && rise_max == fall_max) { + && rise_max == fall_max) { // rise/fall match. writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, - RiseFallBoth::riseFall(), MinMaxAll::min(), - write_object); + RiseFallBoth::riseFall(), MinMaxAll::min(), + write_object); writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale, - RiseFallBoth::riseFall(), MinMaxAll::max(), - write_object); + RiseFallBoth::riseFall(), MinMaxAll::max(), + write_object); } else if (rise_min == rise_max - && fall_min == fall_max) { + && fall_min == fall_max) { // min/max match. writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, - RiseFallBoth::rise(), MinMaxAll::all(), - write_object); + RiseFallBoth::rise(), MinMaxAll::all(), + write_object); writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale, - RiseFallBoth::fall(), MinMaxAll::all(), - write_object); + RiseFallBoth::fall(), MinMaxAll::all(), + write_object); } } else { if (rise_min_exists) writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, - RiseFallBoth::rise(), MinMaxAll::min(), - write_object); + RiseFallBoth::rise(), MinMaxAll::min(), + write_object); if (rise_max_exists) writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale, - RiseFallBoth::rise(), MinMaxAll::max(), - write_object); + RiseFallBoth::rise(), MinMaxAll::max(), + write_object); if (fall_min_exists) writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale, - RiseFallBoth::fall(), MinMaxAll::min(), - write_object); + RiseFallBoth::fall(), MinMaxAll::min(), + write_object); if (fall_max_exists) writeRiseFallMinMaxCmd(sdc_cmd, fall_max, scale, - RiseFallBoth::fall(), MinMaxAll::max(), - write_object); + RiseFallBoth::fall(), MinMaxAll::max(), + write_object); } } void WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, - float value, - float scale, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const + float value, + float scale, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const { gzprintf(stream_, "%s%s%s ", - sdc_cmd, - transRiseFallFlag(rf), - minMaxFlag(min_max)); + sdc_cmd, + transRiseFallFlag(rf), + minMaxFlag(min_max)); writeFloat(value / scale); gzprintf(stream_, " "); write_object.write(); @@ -2674,9 +2654,9 @@ WriteSdc::writeClockKey(const Clock *clk) const void WriteSdc::writeMinMaxFloatValuesCmd(const char *sdc_cmd, - MinMaxFloatValues *values, - float scale, - WriteSdcObject &write_object) const + const MinMaxFloatValues *values, + float scale, + WriteSdcObject &write_object) const { float min, max; bool min_exists, max_exists; @@ -2697,14 +2677,14 @@ WriteSdc::writeMinMaxFloatValuesCmd(const char *sdc_cmd, void WriteSdc::writeMinMaxFloatCmd(const char *sdc_cmd, - float value, - float scale, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const + float value, + float scale, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const { gzprintf(stream_, "%s%s ", - sdc_cmd, - minMaxFlag(min_max)); + sdc_cmd, + minMaxFlag(min_max)); writeFloat(value / scale); gzprintf(stream_, " "); write_object.write(); @@ -2713,8 +2693,8 @@ WriteSdc::writeMinMaxFloatCmd(const char *sdc_cmd, void WriteSdc::writeMinMaxIntValuesCmd(const char *sdc_cmd, - MinMaxIntValues *values, - WriteSdcObject &write_object) const + const MinMaxIntValues *values, + WriteSdcObject &write_object) const { int min, max; bool min_exists, max_exists; @@ -2735,13 +2715,13 @@ WriteSdc::writeMinMaxIntValuesCmd(const char *sdc_cmd, void WriteSdc::writeMinMaxIntCmd(const char *sdc_cmd, - int value, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const + int value, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const { gzprintf(stream_, "%s%s ", - sdc_cmd, - minMaxFlag(min_max)); + sdc_cmd, + minMaxFlag(min_max)); gzprintf(stream_, "%d ", value); write_object.write(); gzprintf(stream_, "\n"); @@ -2793,7 +2773,7 @@ WriteSdc::writeResistance(float res) const void WriteSdc::writeFloatSeq(FloatSeq *floats, - float scale) const + float scale) const { gzprintf(stream_, "{"); bool first = true; diff --git a/sdc/WriteSdcPvt.hh b/sdc/WriteSdcPvt.hh index 90392d9cb..7fd42340d 100644 --- a/sdc/WriteSdcPvt.hh +++ b/sdc/WriteSdcPvt.hh @@ -36,13 +36,13 @@ class WriteSdcObject; class WriteSdc : public StaState { public: - WriteSdc(Instance *instance, - const char *creator, - bool map_hpins, - bool native, - int digits, - bool no_timestamp, - Sdc *sdc); + WriteSdc(const Sdc *sdc, + Instance *instance, + const char *creator, + bool map_hpins, + bool native, + int digits, + bool no_timestamp); virtual ~WriteSdc(); void write(const char *filename, bool gzip); @@ -61,49 +61,49 @@ public: void writeDisabledEdges() const; void writeDisabledEdge(Edge *edge) const; void findMatchingEdges(Edge *edge, - EdgeSet &matches) const; + EdgeSet &matches) const; bool edgeSenseIsUnique(Edge *edge, - EdgeSet &matches) const; + EdgeSet &matches) const; void writeDisabledEdgeSense(Edge *edge) const; void writeClocks() const; void writeClock(Clock *clk) const; void writeGeneratedClock(Clock *clk) const; void writeClockPins(const Clock *clk) const; void writeFloatSeq(FloatSeq *floats, - float scale) const; + float scale) const; void writeIntSeq(IntSeq *ints) const; void writeClockSlews(const Clock *clk) const; void writeClockUncertainty(const Clock *clk) const; void writeClockUncertainty(const Clock *clk, - const char *setup_hold, - float value) const; + const char *setup_hold, + float value) const; void writeClockUncertaintyPins() const; void writeClockUncertaintyPin(const Pin *pin, - ClockUncertainties *uncertainties) const; + ClockUncertainties *uncertainties) const; void writeClockUncertaintyPin(const Pin *pin, - const char *setup_hold, - float value) const; + const char *setup_hold, + float value) const; void writeClockLatencies() const; void writeClockInsertions() const; void writeClockInsertion(ClockInsertion *insert, - WriteSdcObject &write_obj) const; + WriteSdcObject &write_obj) const; void writeInterClockUncertainties() const; void writeInterClockUncertainty(InterClockUncertainty *uncertainty) const; void writePropagatedClkPins() const; void writeInputDelays() const; void writeOutputDelays() const; void writePortDelay(PortDelay *port_delay, - bool is_input_delay, - const char *sdc_cmd) const; + bool is_input_delay, + const char *sdc_cmd) const; void writePortDelay(PortDelay *port_delay, - bool is_input_delay, - float delay, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const char *sdc_cmd) const; + bool is_input_delay, + float delay, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const char *sdc_cmd) const; void writeClockSenses() const; void writeClockSense(PinClockPair &pin_clk, - ClockSense sense) const; + ClockSense sense) const; void writeClockGroups() const; void writeClockGroups(ClockGroups *clk_groups) const; void writeExceptions() const; @@ -113,25 +113,25 @@ public: void writeExceptionFrom(ExceptionFrom *from) const; void writeExceptionTo(ExceptionTo *to) const; void writeExceptionFromTo(ExceptionFromTo *from_to, - const char *from_to_key, - bool map_hpin_to_drvr) const; + const char *from_to_key, + bool map_hpin_to_drvr) const; void writeExceptionThru(ExceptionThru *thru) const; void mapThruHpins(ExceptionThru *thru, - PinSeq &pins) const; + PinSeq &pins) const; void writeDataChecks() const; void writeDataCheck(DataCheck *check) const; void writeDataCheck(DataCheck *check, - const RiseFallBoth *from_rf, - const RiseFallBoth *to_rf, - const SetupHold *setup_hold, - float margin) const; + const RiseFallBoth *from_rf, + const RiseFallBoth *to_rf, + const SetupHold *setup_hold, + float margin) const; void writeEnvironment() const; void writeOperatingConditions() const; void writeWireload() const; virtual void writeNetLoads() const; void writeNetLoad(const Net *net, - const MinMaxAll *min_max, - float cap) const; + const MinMaxAll *min_max, + float cap) const; void writePortLoads() const; void writePortLoads(const Port *port) const; void writePortFanout(const Port *port) const; @@ -139,45 +139,45 @@ public: void writeDrivingCells() const; void writeInputTransitions() const; void writeDrivingCell(Port *port, - InputDriveCell *drive_cell, - const RiseFall *rf, - const MinMax *min_max) const; + InputDriveCell *drive_cell, + const RiseFall *rf, + const MinMax *min_max) const; void writeConstants() const; virtual void writeConstant(const Pin *pin) const; const char *setConstantCmd(const Pin *pin) const; void writeCaseAnalysis() const; virtual void writeCaseAnalysis(const Pin *pin) const; const char *caseAnalysisValueStr(const Pin *pin) const; - void sortedLogicValuePins(LogicValueMap &value_map, - PinSeq &pins) const; + void sortedLogicValuePins(const LogicValueMap &value_map, + PinSeq &pins) const; void writeNetResistances() const; void writeNetResistance(const Net *net, - const MinMaxAll *min_max, - float res) const; + const MinMaxAll *min_max, + float res) const; void writeDesignRules() const; void writeMinPulseWidths() const; void writeMinPulseWidths(RiseFallValues *min_widths, - WriteSdcObject &write_obj) const; + WriteSdcObject &write_obj) const; void writeMinPulseWidth(const char *hi_low, - float value, - WriteSdcObject &write_obj) const; + float value, + WriteSdcObject &write_obj) const; void writeSlewLimits() const; void writeCapLimits() const; void writeCapLimits(const MinMax *min_max, - const char *cmd) const; + const char *cmd) const; void writeMaxArea() const; void writeFanoutLimits() const; void writeFanoutLimits(const MinMax *min_max, - const char *cmd) const; + const char *cmd) const; void writeLatchBorowLimits() const; void writeDeratings() const; void writeDerating(DeratingFactorsGlobal *factors) const; void writeDerating(DeratingFactorsCell *factors, - WriteSdcObject *write_obj) const; + WriteSdcObject *write_obj) const; void writeDerating(DeratingFactors *factors, - TimingDerateType type, - const MinMax *early_late, - WriteSdcObject *write_obj) const; + TimingDerateType type, + const MinMax *early_late, + WriteSdcObject *write_obj) const; void writeVoltages() const; const char *pathName(const Pin *pin) const; @@ -189,23 +189,23 @@ public: void writeGetTimingArcsOfOjbects(const LibertyCell *cell) const; void writeGetTimingArcs(Edge *edge) const; void writeGetTimingArcs(Edge *edge, - const char *filter) const; + const char *filter) const; const char *getTimingArcsCmd() const; void writeGetLibCell(const LibertyCell *cell) const; void writeGetLibPin(const LibertyPort *port) const; void writeGetClock(const Clock *clk) const; void writeGetClocks(ClockSet *clks) const; void writeGetClocks(ClockSet *clks, - bool multiple, - bool &first) const; + bool multiple, + bool &first) const; virtual void writeGetPort(const Port *port) const; virtual void writeGetNet(const Net *net) const; virtual void writeGetInstance(const Instance *inst) const; virtual void writeGetPin(const Pin *pin) const; void writeGetPin(const Pin *pin, - bool map_hpin_to_drvr) const; + bool map_hpin_to_drvr) const; void writeGetPins(const PinSet *pins, - bool map_hpin_to_drvr) const; + bool map_hpin_to_drvr) const; void writeGetPins1(PinSeq *pins) const; void writeClockKey(const Clock *clk) const; float scaleTime(float time) const; @@ -218,41 +218,41 @@ public: void writeClkSlewLimits() const; void writeClkSlewLimit(const char *clk_data, - const char *rise_fall, - const Clock *clk, - float limit) const; + const char *rise_fall, + const Clock *clk, + float limit) const; void writeRiseFallMinMaxTimeCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - WriteSdcObject &write_object) const; + const RiseFallMinMax *values, + WriteSdcObject &write_object) const; void writeRiseFallMinMaxCapCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - WriteSdcObject &write_object) const; + const RiseFallMinMax *values, + WriteSdcObject &write_object) const; void writeRiseFallMinMaxCmd(const char *sdc_cmd, - const RiseFallMinMax *values, - float scale, - WriteSdcObject &write_object) const; + const RiseFallMinMax *values, + float scale, + WriteSdcObject &write_object) const; void writeRiseFallMinMaxCmd(const char *sdc_cmd, - float value, - float scale, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const; + float value, + float scale, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const; void writeMinMaxFloatValuesCmd(const char *sdc_cmd, - MinMaxFloatValues *values, - float scale, - WriteSdcObject &write_object) const; + const MinMaxFloatValues *values, + float scale, + WriteSdcObject &write_object) const; void writeMinMaxFloatCmd(const char *sdc_cmd, - float value, - float scale, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const; + float value, + float scale, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const; void writeMinMaxIntValuesCmd(const char *sdc_cmd, - MinMaxIntValues *values, - WriteSdcObject &write_object) const; + const MinMaxIntValues *values, + WriteSdcObject &write_object) const; void writeMinMaxIntCmd(const char *sdc_cmd, - int value, - const MinMaxAll *min_max, - WriteSdcObject &write_object) const; + int value, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const; void writeSetupHoldFlag(const MinMaxAll *min_max) const; void writeVariables() const; void writeCmdComment(SdcCmdComment *cmd) const; @@ -260,6 +260,7 @@ public: gzFile stream() const { return stream_; } protected: + const Sdc *sdc_; Instance *instance_; const char *creator_; bool map_hpins_; diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc index 649853e9c..63ecbcc66 100644 --- a/sdf/ReportAnnotation.cc +++ b/sdf/ReportAnnotation.cc @@ -33,45 +33,46 @@ #include "Graph.hh" #include "GraphCmp.hh" #include "Sdc.hh" -#include "DcalcAnalysisPt.hh" namespace sta { class ReportAnnotated : public StaState { public: - ReportAnnotated(bool report_cells, - bool report_nets, - bool report_in_ports, - bool report_out_ports, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool constant_arcs, - StaState *sta); - ReportAnnotated(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool constant_arcs, - StaState *sta); + ReportAnnotated(const Scene *scene, + bool report_cells, + bool report_nets, + bool report_in_ports, + bool report_out_ports, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool constant_arcs, + StaState *sta); + ReportAnnotated(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool constant_arcs, + StaState *sta); void reportDelayAnnotation(); void reportCheckAnnotation(); protected: - enum CountIndex { + enum class CountIndex { count_internal_net = TimingRole::index_max, count_input_net, count_output_net, - count_index_max }; + static const int count_index_max = static_cast(CountIndex::count_output_net) + 1; static int count_delay; void init(); @@ -81,28 +82,30 @@ class ReportAnnotated : public StaState void reportCheckCounts(); void reportArcs(); void reportArcs(const char *header, - bool report_annotated, - PinSet &pins); + bool report_annotated, + PinSet &pins); void reportArcs(Vertex *vertex, - bool report_annotated, - int &i); + bool report_annotated, + int &i); void reportPeriodArcs(const Pin *pin, bool report_annotated, int &i); void reportCount(const char *title, - int index, - int &total, - int &annotated_total); + int index, + int &total, + int &annotated_total); void reportCheckCount(const TimingRole *role, - int &total, - int &annotated_total); + int &total, + int &annotated_total); int roleIndex(const TimingRole *role, - const Pin *from_pin, - const Pin *to_pin); + const Pin *from_pin, + const Pin *to_pin); + bool delayAnnotated(Edge *edge); + const Scene *scene_; int max_lines_; - bool list_annotated_; - bool list_unannotated_; + bool report_annotated_; + bool report_unannotated_; bool report_constant_arcs_; int edge_count_[count_index_max]; @@ -118,45 +121,48 @@ class ReportAnnotated : public StaState int ReportAnnotated::count_delay; void -reportAnnotatedDelay(bool report_cells, - bool report_nets, - bool from_in_ports, - bool to_out_ports, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta) +reportAnnotatedDelay(const Scene *scene, + bool report_cells, + bool report_nets, + bool from_in_ports, + bool to_out_ports, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta) { - ReportAnnotated report_annotated(report_cells, report_nets, - from_in_ports, to_out_ports, - max_lines, list_annotated, list_unannotated, - report_constant_arcs, sta); - report_annotated.reportDelayAnnotation(); + 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(); } -ReportAnnotated::ReportAnnotated(bool report_cells, - bool report_nets, - bool report_in_ports, - bool report_out_ports, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta) : +ReportAnnotated::ReportAnnotated(const Scene *scene, + bool report_cells, + bool report_nets, + bool report_in_ports, + bool report_out_ports, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta) : StaState(sta), + scene_(scene), max_lines_(max_lines), - list_annotated_(list_annotated), - list_unannotated_(list_unannotated), + report_annotated_(report_annotated), + report_unannotated_(report_unannotated), report_constant_arcs_(report_constant_arcs), unannotated_pins_(sta->network()), annotated_pins_(sta->network()) { init(); report_role_[TimingRole::sdfIopath()->index()] = report_cells; - report_role_[count_internal_net] = report_nets; - report_role_[count_input_net] = report_in_ports; - report_role_[count_output_net] = report_out_ports; + report_role_[static_cast(CountIndex::count_internal_net)] = report_nets; + report_role_[static_cast(CountIndex::count_input_net)] = report_in_ports; + report_role_[static_cast(CountIndex::count_output_net)] = report_out_ports; } void @@ -177,11 +183,11 @@ ReportAnnotated::reportDelayCounts() int total = 0; int annotated_total = 0; reportCount("cell arcs", count_delay, total, annotated_total); - reportCount("internal net arcs", count_internal_net, total, annotated_total); - reportCount("net arcs from primary inputs", count_input_net, - total, annotated_total); - reportCount("net arcs to primary outputs", count_output_net, - 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), + total, annotated_total); report_->reportLine("----------------------------------------------------------------"); report_->reportLine("%-28s %10u %10u %10u", " ", @@ -193,47 +199,50 @@ ReportAnnotated::reportDelayCounts() //////////////////////////////////////////////////////////////// void -reportAnnotatedCheck(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta) +reportAnnotatedCheck(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta) { - ReportAnnotated report_annotated(report_setup, report_hold, - report_recovery, report_removal, - report_nochange, report_width, - report_period, report_max_skew, - max_lines, list_annotated, list_unannotated, - report_constant_arcs, sta); - report_annotated.reportCheckAnnotation(); + 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(); } -ReportAnnotated::ReportAnnotated(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta) : +ReportAnnotated::ReportAnnotated(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta) : StaState(sta), + scene_(scene), max_lines_(max_lines), - list_annotated_(list_annotated), - list_unannotated_(list_unannotated), + report_annotated_(report_annotated), + report_unannotated_(report_unannotated), report_constant_arcs_(report_constant_arcs), unannotated_pins_(sta->network()), annotated_pins_(sta->network()) @@ -285,8 +294,8 @@ ReportAnnotated::reportCheckCounts() void ReportAnnotated::reportCheckCount(const TimingRole *role, - int &total, - int &annotated_total) + int &total, + int &annotated_total) { int index = role->index(); if (edge_count_[index] > 0) { @@ -314,14 +323,15 @@ ReportAnnotated::init() void ReportAnnotated::findCounts() { + const Sdc *sdc = scene_->sdc(); VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *from_vertex = vertex_iter.next(); 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(); @@ -331,50 +341,64 @@ 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]++; if (from_logic_value_exists || to_logic_value_exists) - edge_constant_count_[index]++; + edge_constant_count_[index]++; if (report_role_[index]) { - if (graph_->delayAnnotated(edge)) { - edge_annotated_count_[index]++; - if (from_logic_value_exists || to_logic_value_exists) - edge_constant_annotated_count_[index]++; - if (list_annotated_) - annotated_pins_.insert(from_pin); - } - else { - if (list_unannotated_) - unannotated_pins_.insert(from_pin); - } + if (delayAnnotated(edge)) { + edge_annotated_count_[index]++; + if (from_logic_value_exists || to_logic_value_exists) + edge_constant_annotated_count_[index]++; + if (report_annotated_) + annotated_pins_.insert(from_pin); + } + else { + if (report_unannotated_) + unannotated_pins_.insert(from_pin); + } } } findPeriodCount(from_pin); } } +bool +ReportAnnotated::delayAnnotated(Edge *edge) +{ + TimingArcSet *arc_set = edge->timingArcSet(); + for (TimingArc *arc : arc_set->arcs()) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene_->dcalcAnalysisPtIndex(min_max); + if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) + return false; + } + } + return true; +} + int ReportAnnotated::roleIndex(const TimingRole *role, - const Pin *from_pin, - const Pin *to_pin) + const Pin *from_pin, + const Pin *to_pin) { if (role == TimingRole::wire()) { if (network_->isTopLevelPort(from_pin)) - return count_input_net; + return static_cast(CountIndex::count_input_net); else if (network_->isTopLevelPort(to_pin)) - return count_output_net; + return static_cast(CountIndex::count_output_net); else - return count_internal_net; + return static_cast(CountIndex::count_internal_net); } else if (role->sdfRole() == TimingRole::sdfIopath()) 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(); } @@ -394,17 +418,17 @@ ReportAnnotated::findPeriodCount(Pin *pin) if (report_role_[period_index]) { port->minPeriod(value, exists); if (exists) { - edge_count_[period_index]++; - graph_->periodCheckAnnotation(pin, ap_index, value, annotated); - if (annotated) { - edge_annotated_count_[period_index]++; - if (list_annotated_) - annotated_pins_.insert(pin); - } - else { - if (list_unannotated_) - unannotated_pins_.insert(pin); - } + edge_count_[period_index]++; + graph_->periodCheckAnnotation(pin, ap_index, value, annotated); + if (annotated) { + edge_annotated_count_[period_index]++; + if (report_annotated_) + annotated_pins_.insert(pin); + } + else { + if (report_unannotated_) + unannotated_pins_.insert(pin); + } } } } @@ -412,9 +436,9 @@ ReportAnnotated::findPeriodCount(Pin *pin) void ReportAnnotated::reportCount(const char *title, - int index, - int &total, - int &annotated_total) + int index, + int &total, + int &annotated_total) { if (report_role_[index]) { int count = edge_count_[index]; @@ -441,16 +465,16 @@ ReportAnnotated::reportCount(const char *title, void ReportAnnotated::reportArcs() { - if (list_annotated_) + if (report_annotated_) reportArcs("Annotated Arcs", true, annotated_pins_); - if (list_unannotated_) + if (report_unannotated_) reportArcs("Unannotated Arcs", false, unannotated_pins_); } void ReportAnnotated::reportArcs(const char *header, - bool report_annotated, - PinSet &pins) + bool report_annotated, + PinSet &pins) { report_->reportBlankLine(); report_->reportLineString(header); @@ -470,31 +494,31 @@ ReportAnnotated::reportArcs(const char *header, void ReportAnnotated::reportArcs(Vertex *vertex, - bool report_annotated, - int &i) + bool report_annotated, + int &i) { const Pin *from_pin = vertex->pin(); VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext() - && (max_lines_ == 0 || i < max_lines_)) { + && (max_lines_ == 0 || i < max_lines_)) { Edge *edge = edge_iter.next(); const TimingRole *role = edge->role(); const Pin *to_pin = edge->to(graph_)->pin(); - if (graph_->delayAnnotated(edge) == report_annotated - && report_role_[roleIndex(role, from_pin, to_pin)]) { + if (delayAnnotated(edge) == report_annotated + && report_role_[roleIndex(role, from_pin, to_pin)]) { const char *role_name; if (role->isTimingCheck()) - role_name = role->to_string().c_str(); + role_name = role->to_string().c_str(); else if (role->isWire()) { - if (network_->isTopLevelPort(from_pin)) - role_name = "primary input net"; - else if (network_->isTopLevelPort(to_pin)) - role_name = "primary output net"; - else - role_name = "internal net"; + if (network_->isTopLevelPort(from_pin)) + role_name = "primary input net"; + else if (network_->isTopLevelPort(to_pin)) + role_name = "primary output net"; + else + role_name = "internal net"; } else - role_name = "delay"; + role_name = "delay"; const char *cond = edge->timingArcSet()->sdfCond(); report_->reportLine(" %-18s %s -> %s %s", role_name, @@ -516,19 +540,19 @@ ReportAnnotated::reportPeriodArcs(const Pin *pin, DcalcAPIndex ap_index = 0; int period_index = TimingRole::period()->index(); if (report_role_[period_index] - && (max_lines_ == 0 || i < max_lines_)) { + && (max_lines_ == 0 || i < max_lines_)) { float value; bool exists, annotated; port->minPeriod(value, exists); if (exists) { - edge_count_[period_index]++; - graph_->periodCheckAnnotation(pin, ap_index, value, annotated); - if (annotated == report_annotated) { - report_->reportLine(" %-18s %s", + edge_count_[period_index]++; + graph_->periodCheckAnnotation(pin, ap_index, value, annotated); + if (annotated == report_annotated) { + report_->reportLine(" %-18s %s", "period", network_->pathName(pin)); - i++; - } + i++; + } } } } diff --git a/sdf/ReportAnnotation.hh b/sdf/ReportAnnotation.hh index eb1b5ff5b..1973fe742 100644 --- a/sdf/ReportAnnotation.hh +++ b/sdf/ReportAnnotation.hh @@ -27,30 +27,33 @@ namespace sta { class StaState; +class Scene; void -reportAnnotatedDelay(bool report_cells, - bool report_nets, - bool report_in_ports, - bool report_out_ports, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta); +reportAnnotatedDelay(const Scene *scene, + bool report_cells, + bool report_nets, + bool report_in_ports, + bool report_out_ports, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta); void -reportAnnotatedCheck(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - int max_lines, - bool list_annotated, - bool list_unannotated, - bool report_constant_arcs, - StaState *sta); +reportAnnotatedCheck(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + int max_lines, + bool report_annotated, + bool report_unannotated, + bool report_constant_arcs, + StaState *sta); } // namespace diff --git a/sdf/Sdf.i b/sdf/Sdf.i index 8e35fd0eb..395e0f7e0 100644 --- a/sdf/Sdf.i +++ b/sdf/Sdf.i @@ -53,7 +53,7 @@ using sta::reportAnnotatedCheck; bool read_sdf_file(const char *filename, const char *path, - Corner *corner, + Scene *scene, bool unescaped_dividers, bool incremental_only, MinMaxAllNull *cond_use) @@ -63,70 +63,74 @@ read_sdf_file(const char *filename, sta->ensureGraph(); if (stringEq(path, "")) path = NULL; - bool success = readSdf(filename, path, corner, unescaped_dividers, incremental_only, - cond_use, sta); + bool success = readSdf(filename, path, scene, unescaped_dividers, + incremental_only, cond_use, sta); sta->search()->arrivalsInvalid(); return success; } void -report_annotated_delay_cmd(bool report_cells, - bool report_nets, - bool report_in_ports, - bool report_out_ports, - unsigned max_lines, - bool list_annotated, - bool list_not_annotated, - bool report_constant_arcs) +report_annotated_delay_cmd(const Scene *scene, + bool report_cells, + bool report_nets, + bool report_in_ports, + bool report_out_ports, + unsigned max_lines, + bool report_annotated, + bool report_not_annotated, + bool report_constant_arcs) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); sta->ensureGraph(); - reportAnnotatedDelay(report_cells, report_nets, - report_in_ports, report_out_ports, - max_lines, list_annotated, list_not_annotated, - report_constant_arcs, sta); + reportAnnotatedDelay(scene, report_cells, report_nets, + report_in_ports, report_out_ports, + max_lines, report_annotated, + report_not_annotated, + report_constant_arcs, sta); } void -report_annotated_check_cmd(bool report_setup, - bool report_hold, - bool report_recovery, - bool report_removal, - bool report_nochange, - bool report_width, - bool report_period, - bool report_max_skew, - unsigned max_lines, - bool list_annotated, - bool list_not_annotated, - bool report_constant_arcs) +report_annotated_check_cmd(const Scene *scene, + bool report_setup, + bool report_hold, + bool report_recovery, + bool report_removal, + bool report_nochange, + bool report_width, + bool report_period, + bool report_max_skew, + unsigned max_lines, + bool report_annotated, + bool report_not_annotated, + bool report_constant_arcs) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); sta->ensureGraph(); - reportAnnotatedCheck(report_setup, report_hold, - report_recovery, report_removal, - report_nochange, report_width, - report_period, report_max_skew, - max_lines, list_annotated, list_not_annotated, - report_constant_arcs, sta); + reportAnnotatedCheck(scene, report_setup, report_hold, + report_recovery, report_removal, + report_nochange, report_width, + report_period, report_max_skew, + max_lines, report_annotated, + report_not_annotated, + report_constant_arcs, sta); } void write_sdf_cmd(char *filename, - Corner *corner, - char divider, - bool include_typ, + Scene *scene, + char divider, + bool include_typ, int digits, - bool gzip, - bool no_timestamp, - bool no_version) + bool gzip, + bool no_timestamp, + bool no_version) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); - sta->writeSdf(filename, corner, divider, include_typ, digits, gzip, - no_timestamp, no_version); + sta->writeSdf(filename, scene, divider, include_typ, digits, gzip, + no_timestamp, no_version); } %} // inline diff --git a/sdf/Sdf.tcl b/sdf/Sdf.tcl index 32ce32d31..ab6572c21 100644 --- a/sdf/Sdf.tcl +++ b/sdf/Sdf.tcl @@ -25,13 +25,13 @@ namespace eval sta { define_cmd_args "read_sdf" \ - {[-path path] [-corner corner]\ + {[-path path] [-scene scene]\ [-cond_use min|max|min_max]\ [-unescaped_dividers] filename} proc_redirect read_sdf { parse_key_args "read_sdf" args \ - keys {-path -corner -cond_use -analysis_type} \ + keys {-path -corner -scene -cond_use -analysis_type} \ flags {-unescaped_dividers -incremental_only} check_argc_eq1 "read_sdf" $args @@ -40,7 +40,7 @@ proc_redirect read_sdf { if [info exists keys(-path)] { set path $keys(-path) } - set corner [parse_corner keys] + set scene [parse_scene keys] set cond_use "NULL" if [info exists keys(-cond_use)] { @@ -50,31 +50,32 @@ proc_redirect read_sdf { set cond_use "NULL" } if { $cond_use == "min_max" \ - && { [operating_condition_analysis_type] == "single" }} { + && { [operating_condition_analysis_type] == "single" }} { sta_error 621 "-cond_use min_max cannot be used with analysis type single." } } set unescaped_dividers [info exists flags(-unescaped_dividers)] set incremental_only [info exists flags(-incremental_only)] - read_sdf_file $filename $path $corner $unescaped_dividers \ + read_sdf_file $filename $path $scene $unescaped_dividers \ $incremental_only $cond_use } ################################################################ define_cmd_args "report_annotated_delay" \ - {[-cell] [-net] [-from_in_ports] [-to_out_ports] [-max_lines lines]\ + {[-cell] [-net] [-from_in_ports] [-to_out_ports]\ + [-scene scene] [-max_lines lines]\ [-report_annotated] [-report_unannotated] [-constant_arcs]} proc_redirect report_annotated_delay { - parse_key_args "report_annotated_delay" args keys {-max_lines} \ + parse_key_args "report_annotated_delay" args keys {-scene -corner -max_lines} \ flags {-cell -net -from_in_ports -to_out_ports \ -report_annotated -report_unannotated -constant_arcs \ -list_not_annotated -list_annotated} if { [info exists flags(-cell)] || [info exists flags(-net)] \ - || [info exists flags(-from_in_ports)] \ - || [info exists flags(-to_out_ports)] } { + || [info exists flags(-from_in_ports)] \ + || [info exists flags(-to_out_ports)] } { set report_cells [info exists flags(-cell)] set report_nets [info exists flags(-net)] set report_in_nets [info exists flags(-from_in_ports)] @@ -86,6 +87,7 @@ proc_redirect report_annotated_delay { set report_out_nets 1 } + set scene [parse_scene keys] set max_lines 0 if { [info exists keys(-max_lines)] } { set max_lines $keys(-max_lines) @@ -105,26 +107,27 @@ proc_redirect report_annotated_delay { set report_unannotated 1 } - report_annotated_delay_cmd $report_cells $report_nets \ + report_annotated_delay_cmd $scene $report_cells $report_nets \ $report_in_nets $report_out_nets \ $max_lines $report_annotated $report_unannotated \ [info exists flags(-constant_arcs)] } define_cmd_args "report_annotated_check" \ - {[-setup] [-hold] [-recovery] [-removal] [-nochange] [-width] [-period]\ - [-max_skew] [-max_lines lines] [-report_annotated] [-report_unannotated]\ - [-constant_arcs]} + {[-setup] [-hold] [-recovery] [-removal] [-nochange]\ + [-width] [-period] [-max_skew]\ + [-scene scene] [-max_lines lines]\ + [-report_annotated] [-report_unannotated] [-constant_arcs]} proc_redirect report_annotated_check { - parse_key_args "report_annotated_check" args keys {-max_lines} \ + parse_key_args "report_annotated_check" args keys {-scene -max_lines} \ flags {-setup -hold -recovery -removal -nochange -width -period \ - -max_skew -report_annotated -report_unannotated -constant_arcs \ + -max_skew -report_annotated -report_unannotated -constant_arcs \ -list_annotated -list_not_annotated} if { [info exists flags(-setup)] || [info exists flags(-hold)] \ - || [info exists flags(-recovery)] || [info exists flags(-removal)] \ - || [info exists flags(-nochange)] || [info exists flags(-width)] \ - || [info exists flags(-period)] || [info exists flags(-max_skew)] } { + || [info exists flags(-recovery)] || [info exists flags(-removal)] \ + || [info exists flags(-nochange)] || [info exists flags(-width)] \ + || [info exists flags(-period)] || [info exists flags(-max_skew)] } { set report_setup [info exists flags(-setup)] set report_hold [info exists flags(-hold)] set report_recovery [info exists flags(-recovery)] @@ -144,6 +147,7 @@ proc_redirect report_annotated_check { set report_max_skew 1 } + set scene [parse_scene keys] set max_lines 0 if { [info exists keys(-max_lines)] } { set max_lines $keys(-max_lines) @@ -163,7 +167,7 @@ proc_redirect report_annotated_check { set report_unannotated 1 } - report_annotated_check_cmd $report_setup $report_hold \ + report_annotated_check_cmd $scene $report_setup $report_hold \ $report_recovery $report_removal $report_nochange \ $report_width $report_period $report_max_skew \ $max_lines $report_annotated $report_unannotated \ @@ -171,15 +175,15 @@ proc_redirect report_annotated_check { } define_cmd_args "write_sdf" \ - {[-corner corner] [-divider /|.] [-include_typ]\ + {[-scene scene] [-divider /|.] [-include_typ]\ [-digits digits] [-gzip] [-no_timestamp] [-no_version] filename} proc_redirect write_sdf { parse_key_args "write_sdf" args \ - keys {-corner -divider -digits -significant_digits} \ + keys {-corner -scene -divider -digits -significant_digits} \ flags {-include_typ -gzip -no_timestamp -no_version} check_argc_eq1 "write_sdf" $args - set corner [parse_corner keys] + set scene [parse_scene keys] set filename [file nativename [lindex $args 0]] set divider "/" if [info exists keys(-divider)] { @@ -198,7 +202,7 @@ proc_redirect write_sdf { set no_timestamp [info exists flags(-no_timestamp)] set no_version [info exists flags(-no_version)] set gzip [info exists flags(-gzip)] - write_sdf_cmd $filename $corner $divider $include_typ $digits $gzip \ + write_sdf_cmd $filename $scene $divider $include_typ $digits $gzip \ $no_timestamp $no_version } diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 00279e498..8f6952969 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -27,6 +27,7 @@ #include #include +#include "ContainerHelpers.hh" #include "Zlib.hh" #include "Error.hh" #include "Debug.hh" @@ -37,8 +38,7 @@ #include "Network.hh" #include "SdcNetwork.hh" #include "Graph.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" +#include "Scene.hh" #include "Sdc.hh" #include "sdf/SdfReaderPvt.hh" #include "sdf/SdfScanner.hh" @@ -52,8 +52,8 @@ class SdfTriple { public: SdfTriple(float *min, - float *typ, - float *max); + float *typ, + float *max); ~SdfTriple(); float **values() { return values_; } bool hasValue() const; @@ -66,8 +66,8 @@ class SdfPortSpec { public: SdfPortSpec(const Transition *tr, - const std::string *port, - const std::string *cond); + const std::string *port, + const std::string *cond); ~SdfPortSpec(); const string *port() const { return port_; } const Transition *transition() const { return tr_; } @@ -82,32 +82,32 @@ class SdfPortSpec bool readSdf(const char *filename, const char *path, - Corner *corner, + Scene *scene, bool unescaped_dividers, bool incremental_only, MinMaxAll *cond_use, StaState *sta) { - int arc_min_index = corner->findDcalcAnalysisPt(MinMax::min())->index(); - int arc_max_index = corner->findDcalcAnalysisPt(MinMax::max())->index(); + 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, - sta->sdc()->analysisType(), + arc_min_index, arc_max_index, + scene->sdc()->analysisType(), unescaped_dividers, incremental_only, - cond_use, sta); + cond_use, sta); bool success = reader.read(); return success; } SdfReader::SdfReader(const char *filename, - const char *path, + const char *path, int arc_min_index, - int arc_max_index, - AnalysisType analysis_type, - bool unescaped_dividers, - bool is_incremental_only, + int arc_max_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool is_incremental_only, MinMaxAll *cond_use, - StaState *sta) : + StaState *sta) : StaState(sta), filename_(filename), path_(path), @@ -125,7 +125,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_); @@ -162,7 +162,7 @@ SdfReader::setDivider(char divider) void SdfReader::setTimescale(float multiplier, - const string *units) + const string *units) { if (multiplier == 1.0 || multiplier == 10.0 @@ -183,8 +183,8 @@ SdfReader::setTimescale(float multiplier, void SdfReader::interconnect(const string *from_pin_name, - const string *to_pin_name, - SdfTripleSeq *triples) + const string *to_pin_name, + SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_)) { @@ -194,29 +194,29 @@ SdfReader::interconnect(const string *from_pin_name, // Assume the pins are non-hierarchical and on the same net. Edge *edge = findWireEdge(from_pin, to_pin); if (edge) - setEdgeDelays(edge, triples, "INTERCONNECT"); + setEdgeDelays(edge, triples, "INTERCONNECT"); else { - bool from_is_hier = network_->isHierarchical(from_pin); - 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.", + bool from_is_hier = network_->isHierarchical(from_pin); + 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()); - if (to_is_hier) - sdfError(183, "pin %s is a hierarchical pin.", + if (to_is_hier) + sdfError(183, "pin %s is a hierarchical pin.", to_pin_name->c_str()); - } - else - sdfWarn(184, "INTERCONNECT from %s to %s not found.", + } + else + sdfWarn(184, "INTERCONNECT from %s to %s not found.", from_pin_name->c_str(), to_pin_name->c_str()); } } else { if (from_pin == nullptr) - sdfWarn(185, "pin %s not found.", from_pin_name->c_str()); + sdfWarn(185, "pin %s not found.", from_pin_name->c_str()); if (to_pin == nullptr) - sdfWarn(186, "pin %s not found.", to_pin_name->c_str()); + sdfWarn(186, "pin %s not found.", to_pin_name->c_str()); } } delete from_pin_name; @@ -226,7 +226,7 @@ SdfReader::interconnect(const string *from_pin_name, void SdfReader::port(const string *to_pin_name, - SdfTripleSeq *triples) + SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_)) { @@ -239,9 +239,9 @@ SdfReader::port(const string *to_pin_name, Vertex *vertex = graph_->pinLoadVertex(to_pin); VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->sdfRole()->isWire()) - setEdgeDelays(edge, triples, "PORT"); + Edge *edge = edge_iter.next(); + if (edge->role()->sdfRole()->isWire()) + setEdgeDelays(edge, triples, "PORT"); } } } @@ -251,7 +251,7 @@ SdfReader::port(const string *to_pin_name, Edge * SdfReader::findWireEdge(Pin *from_pin, - Pin *to_pin) + Pin *to_pin) { Vertex *to_vertex, *to_vertex_bidirect_drvr; graph_->pinVertices(to_pin, to_vertex, to_vertex_bidirect_drvr); @@ -271,8 +271,8 @@ SdfReader::findWireEdge(Pin *from_pin, void SdfReader::setEdgeDelays(Edge *edge, - SdfTripleSeq *triples, - const char *sdf_cmd) + SdfTripleSeq *triples, + const char *sdf_cmd) { // Rise/fall triples. size_t triple_count = triples->size(); @@ -282,9 +282,9 @@ SdfReader::setEdgeDelays(Edge *edge, for (TimingArc *arc : arc_set->arcs()) { size_t triple_index; if (triple_count == 1) - triple_index = 0; + triple_index = 0; else - triple_index = arc->toEdge()->sdfTripleIndex(); + triple_index = arc->toEdge()->sdfTripleIndex(); SdfTriple *triple = (*triples)[triple_index]; setEdgeArcDelays(edge, arc, triple); } @@ -312,10 +312,10 @@ SdfReader::setInstance(const string *instance_name) else { instance_ = findInstance(instance_name); if (instance_) { - 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.", + 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()); @@ -344,10 +344,10 @@ SdfReader::cellFinish() void SdfReader::iopath(SdfPortSpec *from_edge, - const string *to_port_name, - SdfTripleSeq *triples, - const string *cond, - bool condelse) + const string *to_port_name, + SdfTripleSeq *triples, + const string *cond, + bool condelse) { if (instance_) { const string *from_port_name = from_edge->port(); @@ -360,8 +360,8 @@ SdfReader::iopath(SdfPortSpec *from_edge, // Do not report an error if the pin is not found because the // instance may not have the pin. if (from_pin && to_pin) { - Vertex *to_vertex = graph_->pinDrvrVertex(to_pin); - if (to_vertex) { + Vertex *to_vertex = graph_->pinDrvrVertex(to_pin); + if (to_vertex) { size_t triple_count = triples->size(); bool matched = false; // Fanin < fanout, so search for driver from load. @@ -433,7 +433,7 @@ SdfReader::findPort(const Cell *cell, void SdfReader::timingCheck(const TimingRole *role, SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, + SdfPortSpec *clk_edge, SdfTriple *triple) { if (instance_) { @@ -453,10 +453,10 @@ SdfReader::timingCheck(const TimingRole *role, void SdfReader::timingCheck1(const TimingRole *role, Port *data_port, - SdfPortSpec *data_edge, + SdfPortSpec *data_edge, Port *clk_port, - SdfPortSpec *clk_edge, - SdfTriple *triple) + SdfPortSpec *clk_edge, + SdfTriple *triple) { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_) @@ -507,12 +507,12 @@ SdfReader::timingCheck1(const TimingRole *role, // Return true if matched. bool SdfReader::annotateCheckEdges(Pin *data_pin, - SdfPortSpec *data_edge, - Pin *clk_pin, - SdfPortSpec *clk_edge, - const TimingRole *sdf_role, - SdfTriple *triple, - bool match_generic) + SdfPortSpec *data_edge, + Pin *clk_pin, + SdfPortSpec *clk_edge, + const TimingRole *sdf_role, + SdfTriple *triple, + bool match_generic) { bool matched = false; const string *cond_start = data_edge->cond(); @@ -529,21 +529,21 @@ SdfReader::annotateCheckEdges(Pin *data_pin, const char *lib_cond_start = arc_set->sdfCondStart(); const char *lib_cond_end = arc_set->sdfCondEnd(); bool cond_matches = condMatch(cond_start, lib_cond_start) - && condMatch(cond_end, lib_cond_end); + && condMatch(cond_end, lib_cond_end); if (((!match_generic && edge_role->sdfRole() == sdf_role) - || (match_generic - && edge_role->genericRole() == sdf_role->genericRole())) - && cond_matches) { - TimingArcSet *arc_set = edge->timingArcSet(); + || (match_generic + && edge_role->genericRole() == sdf_role->genericRole())) + && cond_matches) { + TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { - if (((data_edge->transition() == Transition::riseFall()) - || (arc->toEdge() == data_edge->transition())) - && ((clk_edge->transition() == Transition::riseFall()) - || (arc->fromEdge() == clk_edge->transition()))) { - setEdgeArcDelays(edge, arc, triple); - } - } - matched = true; + if (((data_edge->transition() == Transition::riseFall()) + || (arc->toEdge() == data_edge->transition())) + && ((clk_edge->transition() == Transition::riseFall()) + || (arc->fromEdge() == clk_edge->transition()))) { + setEdgeArcDelays(edge, arc, triple); + } + } + matched = true; } } } @@ -552,7 +552,7 @@ SdfReader::annotateCheckEdges(Pin *data_pin, void SdfReader::timingCheckWidth(SdfPortSpec *edge, - SdfTriple *triple) + SdfTriple *triple) { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_) @@ -578,9 +578,9 @@ SdfReader::timingCheckWidth(SdfPortSpec *edge, void SdfReader::timingCheckSetupHold(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *setup_triple, - SdfTriple *hold_triple) + SdfPortSpec *clk_edge, + SdfTriple *setup_triple, + SdfTriple *hold_triple) { timingCheckSetupHold1(data_edge, clk_edge, setup_triple, hold_triple, TimingRole::setup(), TimingRole::hold()); @@ -588,9 +588,9 @@ SdfReader::timingCheckSetupHold(SdfPortSpec *data_edge, void SdfReader::timingCheckRecRem(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *rec_triple, - SdfTriple *rem_triple) + SdfPortSpec *clk_edge, + SdfTriple *rec_triple, + SdfTriple *rem_triple) { timingCheckSetupHold1(data_edge, clk_edge, rec_triple, rem_triple, TimingRole::recovery(), TimingRole::removal()); @@ -621,7 +621,7 @@ SdfReader::timingCheckSetupHold1(SdfPortSpec *data_edge, void SdfReader::timingCheckPeriod(SdfPortSpec *edge, - SdfTriple *triple) + SdfTriple *triple) { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_) @@ -633,19 +633,19 @@ SdfReader::timingCheckPeriod(SdfPortSpec *edge, // Edge specifier is ignored for period checks. Pin *pin = network_->findPin(instance_, port_name->c_str()); if (pin) { - float **values = triple->values(); - float *value_ptr = values[triple_min_index_]; - if (value_ptr) { - float value = *value_ptr; - graph_->setPeriodCheckAnnotation(pin, arc_delay_min_index_, value); - } - if (triple_max_index_ != null_index_) { - value_ptr = values[triple_max_index_]; - if (value_ptr) { - float value = *value_ptr; - graph_->setPeriodCheckAnnotation(pin, arc_delay_max_index_, value); - } - } + float **values = triple->values(); + float *value_ptr = values[triple_min_index_]; + if (value_ptr) { + float value = *value_ptr; + graph_->setPeriodCheckAnnotation(pin, arc_delay_min_index_, value); + } + if (triple_max_index_ != null_index_) { + value_ptr = values[triple_max_index_]; + if (value_ptr) { + float value = *value_ptr; + graph_->setPeriodCheckAnnotation(pin, arc_delay_max_index_, value); + } + } } } } @@ -655,9 +655,9 @@ SdfReader::timingCheckPeriod(SdfPortSpec *edge, void SdfReader::timingCheckNochange(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *before_triple, - SdfTriple *after_triple) + SdfPortSpec *clk_edge, + SdfTriple *before_triple, + SdfTriple *after_triple) { notSupported("NOCHANGE"); delete data_edge; @@ -684,7 +684,7 @@ SdfReader::device(SdfTripleSeq *triples) void SdfReader::device(const string *to_port_name, - SdfTripleSeq *triples) + SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_) @@ -702,7 +702,7 @@ SdfReader::device(const string *to_port_name, void SdfReader::setDevicePinDelays(Pin *to_pin, - SdfTripleSeq *triples) + SdfTripleSeq *triples) { Vertex *vertex = graph_->pinDrvrVertex(to_pin); if (vertex) { @@ -717,8 +717,8 @@ SdfReader::setDevicePinDelays(Pin *to_pin, void SdfReader::setEdgeArcDelays(Edge *edge, - TimingArc *arc, - SdfTriple *triple) + TimingArc *arc, + SdfTriple *triple) { setEdgeArcDelays(edge, arc, triple, triple_min_index_, arc_delay_min_index_); setEdgeArcDelays(edge, arc, triple, triple_max_index_, arc_delay_max_index_); @@ -726,10 +726,10 @@ SdfReader::setEdgeArcDelays(Edge *edge, void SdfReader::setEdgeArcDelays(Edge *edge, - TimingArc *arc, - SdfTriple *triple, - int triple_index, - int arc_delay_index) + TimingArc *arc, + SdfTriple *triple, + int triple_index, + int arc_delay_index) { if (triple_index != null_index_) { float **values = triple->values(); @@ -737,9 +737,9 @@ SdfReader::setEdgeArcDelays(Edge *edge, if (value_ptr) { ArcDelay delay; if (in_incremental_) - delay = *value_ptr + graph_->arcDelay(edge, arc, arc_delay_index); + delay = *value_ptr + graph_->arcDelay(edge, arc, arc_delay_index); else - delay = *value_ptr; + delay = *value_ptr; graph_->setArcDelay(edge, arc, arc_delay_index, delay); graph_->setArcDelayAnnotated(edge, arc, arc_delay_index, true); edge->setDelayAnnotationIsIncremental(is_incremental_only_); @@ -749,8 +749,8 @@ SdfReader::setEdgeArcDelays(Edge *edge, void SdfReader::setEdgeArcDelaysCondUse(Edge *edge, - TimingArc *arc, - SdfTriple *triple) + TimingArc *arc, + SdfTriple *triple) { float **values = triple->values(); float *value_min = values[triple_min_index_]; @@ -769,18 +769,18 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, max = MinMax::max(); } setEdgeArcDelaysCondUse(edge, arc, value_min, triple_min_index_, - arc_delay_min_index_, min); + arc_delay_min_index_, min); setEdgeArcDelaysCondUse(edge, arc, value_max, triple_max_index_, - arc_delay_max_index_, max); + arc_delay_max_index_, max); } void SdfReader::setEdgeArcDelaysCondUse(Edge *edge, - TimingArc *arc, - float *value, - int triple_index, - int arc_delay_index, - const MinMax *min_max) + TimingArc *arc, + float *value, + int triple_index, + int arc_delay_index, + const MinMax *min_max) { if (value && triple_index != null_index_) { @@ -790,7 +790,7 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, 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)) - delay = prev_value; + delay = prev_value; } graph_->setArcDelay(edge, arc, arc_delay_index, delay); graph_->setArcDelayAnnotated(edge, arc, arc_delay_index, true); @@ -800,7 +800,7 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, bool SdfReader::condMatch(const string *sdf_cond, - const char *lib_cond) + const char *lib_cond) { // If the sdf is not conditional it matches any library condition. if (sdf_cond == nullptr) @@ -814,11 +814,11 @@ SdfReader::condMatch(const string *sdf_cond, ch1 = *c1++; ch2 = *c2++; while (ch1 && isspace(ch1)) - ch1 = *c1++; + ch1 = *c1++; while (ch2 && isspace(ch2)) - ch2 = *c2++; + ch2 = *c2++; if (ch1 != ch2) - return false; + return false; } while (ch1 && ch2); return (ch1 == '\0' && ch2 == '\0'); } @@ -828,8 +828,8 @@ SdfReader::condMatch(const string *sdf_cond, SdfPortSpec * SdfReader::makePortSpec(const Transition *tr, - const string *port, - const string *cond) + const string *port, + const string *cond) { return new SdfPortSpec(tr, port, cond); } @@ -866,11 +866,7 @@ SdfReader::makeTripleSeq() void SdfReader::deleteTripleSeq(SdfTripleSeq *triples) { - SdfTripleSeq::Iterator iter(triples); - while (iter.hasNext()) { - SdfTriple *triple = iter.next(); - delete triple; - } + deleteContents(triples); delete triples; } @@ -890,8 +886,8 @@ SdfReader::makeTriple(float value) SdfTriple * SdfReader::makeTriple(float *min, - float *typ, - float *max) + float *typ, + float *max) { if (min) *min *= timescale_; if (typ) *typ *= timescale_; @@ -930,18 +926,18 @@ SdfReader::unescaped(const string *token) char next_ch = (*token)[i + 1]; if (next_ch == divider_) { // Escaped divider. - // Translate sdf escape to network escape. - *unescaped += path_escape; - // Translate sdf divider to network divider. - *unescaped += path_divider; + // Translate sdf escape to network escape. + *unescaped += path_escape; + // Translate sdf divider to network divider. + *unescaped += path_divider; } else if (next_ch == '[' - || next_ch == ']' - || next_ch == escape_) { - // Escaped bus bracket or escape. - // Translate sdf escape to network escape. - *unescaped += path_escape; - *unescaped += next_ch; + || next_ch == ']' + || next_ch == escape_) { + // Escaped bus bracket or escape. + // Translate sdf escape to network escape. + *unescaped += path_escape; + *unescaped += next_ch; } else // Escaped non-divider character. @@ -1060,8 +1056,8 @@ SdfPortSpec::~SdfPortSpec() //////////////////////////////////////////////////////////////// SdfTriple::SdfTriple(float *min, - float *typ, - float *max) + float *typ, + float *max) { values_[0] = min; values_[1] = typ; diff --git a/sdf/SdfReader.hh b/sdf/SdfReader.hh index a2b4e064f..fd17bb84e 100644 --- a/sdf/SdfReader.hh +++ b/sdf/SdfReader.hh @@ -27,7 +27,7 @@ namespace sta { class MinMaxAll; -class Corner; +class Scene; class StaState; // If unescaped_dividers is true, path names in the SDF do not have to @@ -54,7 +54,7 @@ class StaState; bool readSdf(const char *filename, const char *path, - Corner *corner, + Scene *scene, bool unescaped_dividers, bool incremental_only, MinMaxAll *cond_use, diff --git a/sdf/SdfReaderPvt.hh b/sdf/SdfReaderPvt.hh index 067493915..52bc1cf38 100644 --- a/sdf/SdfReaderPvt.hh +++ b/sdf/SdfReaderPvt.hh @@ -24,7 +24,8 @@ #pragma once -#include "Vector.hh" +#include + #include "TimingRole.hh" #include "Transition.hh" #include "LibertyClass.hh" @@ -40,20 +41,20 @@ class SdfTriple; class SdfPortSpec; class SdfScanner; -typedef Vector SdfTripleSeq; +using SdfTripleSeq = std::vector; class SdfReader : public StaState { public: SdfReader(const char *filename, - const char *path, - int arc_min_index, - int arc_max_index, - AnalysisType analysis_type, - bool unescaped_dividers, - bool is_incremental_only, + const char *path, + int arc_min_index, + int arc_max_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool is_incremental_only, MinMaxAll *cond_use, - StaState *sta); + StaState *sta); ~SdfReader(); bool read(); @@ -61,53 +62,53 @@ public: void setTimescale(float multiplier, const std::string *units); void setPortDeviceDelay(Edge *edge, - SdfTripleSeq *triples, - bool from_trans); + SdfTripleSeq *triples, + bool from_trans); void setEdgeArcDelays(Edge *edge, - TimingArc *arc, - SdfTriple *triple); + TimingArc *arc, + SdfTriple *triple); void setEdgeArcDelays(Edge *edge, - TimingArc *arc, - SdfTriple *triple, - int triple_index, - int arc_delay_index); + TimingArc *arc, + SdfTriple *triple, + int triple_index, + int arc_delay_index); void setEdgeArcDelaysCondUse(Edge *edge, - TimingArc *arc, - SdfTriple *triple); + TimingArc *arc, + SdfTriple *triple); void setEdgeArcDelaysCondUse(Edge *edge, - TimingArc *arc, - float *value, - int triple_index, - int arc_delay_index, - const MinMax *min_max); + TimingArc *arc, + float *value, + int triple_index, + int arc_delay_index, + const MinMax *min_max); void setInstance(const std::string *instance_name); void setInstanceWildcard(); void cellFinish(); void setCell(const std::string *cell_name); void interconnect(const std::string *from_pin_name, - const std::string *to_pin_name, - SdfTripleSeq *triples); + const std::string *to_pin_name, + SdfTripleSeq *triples); void iopath(SdfPortSpec *from_edge, - const std::string *to_port_name, - SdfTripleSeq *triples, - const std::string *cond, - bool condelse); + const std::string *to_port_name, + SdfTripleSeq *triples, + const std::string *cond, + bool condelse); void timingCheck(const TimingRole *role, - SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *triple); + SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *triple); void timingCheckWidth(SdfPortSpec *edge, - SdfTriple *triple); + SdfTriple *triple); void timingCheckPeriod(SdfPortSpec *edge, - SdfTriple *triple); + SdfTriple *triple); void timingCheckSetupHold(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *setup_triple, - SdfTriple *hold_triple); + SdfPortSpec *clk_edge, + SdfTriple *setup_triple, + SdfTriple *hold_triple); void timingCheckRecRem(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *rec_triple, - SdfTriple *rem_triple); + SdfPortSpec *clk_edge, + SdfTriple *rec_triple, + SdfTriple *rem_triple); void timingCheckSetupHold1(SdfPortSpec *data_edge, SdfPortSpec *clk_edge, SdfTriple *setup_triple, @@ -115,26 +116,26 @@ public: const TimingRole *setup_role, const TimingRole *hold_role); void timingCheckNochange(SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, - SdfTriple *before_triple, - SdfTriple *after_triple); + SdfPortSpec *clk_edge, + SdfTriple *before_triple, + SdfTriple *after_triple); void port(const std::string *to_pin_name, - SdfTripleSeq *triples); + SdfTripleSeq *triples); void device(SdfTripleSeq *triples); void device(const std::string *to_pin_name, - SdfTripleSeq *triples); + SdfTripleSeq *triples); SdfTriple *makeTriple(); SdfTriple *makeTriple(float value); SdfTriple *makeTriple(float *min, - float *typ, - float *max); + float *typ, + float *max); void deleteTriple(SdfTriple *triple); SdfTripleSeq *makeTripleSeq(); void deleteTripleSeq(SdfTripleSeq *triples); SdfPortSpec *makePortSpec(const Transition *tr, - const std::string *port, - const std::string *cond); + const std::string *port, + const std::string *cond); SdfPortSpec *makeCondPortSpec(const std::string *cond_port); std::string *unescaped(const std::string *token); std::string *makePath(const std::string *head, @@ -156,17 +157,17 @@ public: private: int readSdfFile1(Network *network, - Graph *graph, - const char *filename); + Graph *graph, + const char *filename); Edge *findCheckEdge(Pin *from_pin, - Pin *to_pin, - const TimingRole *sdf_role, - const std::string *cond_start, - const std::string *cond_end); + Pin *to_pin, + const TimingRole *sdf_role, + const std::string *cond_start, + const std::string *cond_end); Edge *findWireEdge(Pin *from_pin, - Pin *to_pin); + Pin *to_pin); bool condMatch(const std::string *sdf_cond, - const char *lib_cond); + const char *lib_cond); void timingCheck1(const TimingRole *role, Port *data_port, SdfPortSpec *data_edge, @@ -174,19 +175,19 @@ private: SdfPortSpec *clk_edge, SdfTriple *triple); bool annotateCheckEdges(Pin *data_pin, - SdfPortSpec *data_edge, - Pin *clk_pin, - SdfPortSpec *clk_edge, - const TimingRole *sdf_role, - SdfTriple *triple, - bool match_generic); + SdfPortSpec *data_edge, + Pin *clk_pin, + SdfPortSpec *clk_edge, + const TimingRole *sdf_role, + SdfTriple *triple, + bool match_generic); Pin *findPin(const std::string *name); Instance *findInstance(const std::string *name); void setEdgeDelays(Edge *edge, - SdfTripleSeq *triples, - const char *sdf_cmd); + SdfTripleSeq *triples, + const char *sdf_cmd); void setDevicePinDelays(Pin *to_pin, - SdfTripleSeq *triples); + SdfTripleSeq *triples); Port *findPort(const Cell *cell, const std::string *port_name); diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 2c2bfa82f..23b055cb4 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -39,11 +39,9 @@ #include "MinMaxValues.hh" #include "Network.hh" #include "Graph.hh" -#include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" #include "StaState.hh" -#include "Corner.hh" -#include "PathAnalysisPt.hh" +#include "Scene.hh" namespace sta { @@ -55,18 +53,18 @@ class SdfWriter : public StaState SdfWriter(StaState *sta); ~SdfWriter(); void write(const char *filename, - const Corner *corner, - char sdf_divider, - bool include_typ, + const Scene *scene, + char sdf_divider, + bool include_typ, int digits, - bool gzip, - bool no_timestamp, - bool no_version); + bool gzip, + bool no_timestamp, + bool no_version); protected: void writeHeader(LibertyLibrary *default_lib, - bool no_timestamp, - bool no_version); + bool no_timestamp, + bool no_version); void writeTrailer(); void writeInterconnects(); void writeInstInterconnects(Instance *inst); @@ -76,33 +74,33 @@ class SdfWriter : public StaState void writeInstHeader(const Instance *inst); void writeInstTrailer(); void writeIopaths(const Instance *inst, - bool &inst_header); + bool &inst_header); void writeIopathHeader(); void writeIopathTrailer(); void writeTimingChecks(const Instance *inst, - bool &inst_header); + bool &inst_header); void ensureTimingCheckheaders(bool &check_header, - const Instance *inst, - bool &inst_header); + const Instance *inst, + bool &inst_header); void writeCheck(Edge *edge, - const char *sdf_check); + const char *sdf_check); void writeCheck(Edge *edge, - TimingArc *arc, - const char *sdf_check, - bool use_data_edge, - bool use_clk_edge); + TimingArc *arc, + const char *sdf_check, + bool use_data_edge, + bool use_clk_edge); void writeEdgeCheck(Edge *edge, - const char *sdf_check, - int clk_rf_index, - TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]); + const char *sdf_check, + int clk_rf_index, + TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]); void writeTimingCheckHeader(); void writeTimingCheckTrailer(); void writeWidthCheck(const Pin *pin, - const RiseFall *hi_low, - float min_width, - float max_width); + const RiseFall *hi_low, + float min_width, + float max_width); void writePeriodCheck(const Pin *pin, - float min_period); + float min_period); const char *sdfEdge(const Transition *tr); void writeArcDelays(Edge *edge); void writeSdfTriple(RiseFallMinMax &delays, @@ -125,25 +123,25 @@ class SdfWriter : public StaState char *delay_format_; gzFile stream_; - const Corner *corner_; + const Scene *scene_; int arc_delay_min_index_; int arc_delay_max_index_; }; void writeSdf(const char *filename, - const Corner *corner, - char sdf_divider, + const Scene *scene, + char sdf_divider, bool include_typ, - int digits, - bool gzip, - bool no_timestamp, - bool no_version, - StaState *sta) + int digits, + bool gzip, + bool no_timestamp, + bool no_version, + StaState *sta) { SdfWriter writer(sta); - writer.write(filename, corner, sdf_divider, include_typ, digits, gzip, - no_timestamp, no_version); + writer.write(filename, scene, sdf_divider, include_typ, digits, gzip, + no_timestamp, no_version); } SdfWriter::SdfWriter(StaState *sta) : @@ -161,13 +159,13 @@ SdfWriter::~SdfWriter() void SdfWriter::write(const char *filename, - const Corner *corner, - char sdf_divider, + const Scene *scene, + char sdf_divider, bool include_typ, - int digits, - bool gzip, - bool no_timestamp, - bool no_version) + int digits, + bool gzip, + bool no_timestamp, + bool no_version) { sdf_divider_ = sdf_divider; include_typ_ = include_typ; @@ -177,17 +175,9 @@ SdfWriter::write(const char *filename, LibertyLibrary *default_lib = network_->defaultLibertyLibrary(); timescale_ = default_lib->units()->timeUnit()->scale(); - corner_ = corner; - const MinMax *min_max; - const DcalcAnalysisPt *dcalc_ap; - - min_max = MinMax::min(); - dcalc_ap = corner_->findDcalcAnalysisPt(min_max); - arc_delay_min_index_ = dcalc_ap->index(); - - min_max = MinMax::max(); - dcalc_ap = corner_->findDcalcAnalysisPt(min_max); - arc_delay_max_index_ = dcalc_ap->index(); + scene_ = scene; + arc_delay_min_index_ = scene->dcalcAnalysisPtIndex(MinMax::min()); + arc_delay_max_index_ = scene->dcalcAnalysisPtIndex(MinMax::max()); stream_ = gzopen(filename, gzip ? "wb" : "wT"); if (stream_ == nullptr) @@ -204,13 +194,13 @@ SdfWriter::write(const char *filename, void SdfWriter::writeHeader(LibertyLibrary *default_lib, - bool no_timestamp, - bool no_version) + 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())); + network_->cellName(network_->topInstance())); if (!no_timestamp) { time_t now; @@ -228,11 +218,11 @@ SdfWriter::writeHeader(LibertyLibrary *default_lib, gzprintf(stream_, " (DIVIDER %c)\n", sdf_divider_); LibertyLibrary *lib_min = default_lib; - const LibertySeq &libs_min = corner_->libertyLibraries(MinMax::min()); + const LibertySeq &libs_min = scene_->libertyLibraries(MinMax::min()); if (!libs_min.empty()) lib_min = libs_min[0]; LibertyLibrary *lib_max = default_lib; - const LibertySeq &libs_max = corner_->libertyLibraries(MinMax::max()); + const LibertySeq &libs_max = scene_->libertyLibraries(MinMax::max()); if (!libs_max.empty()) lib_max = libs_max[0]; @@ -284,7 +274,7 @@ SdfWriter::writeInterconnects() { gzprintf(stream_, " (CELL\n"); gzprintf(stream_, " (CELLTYPE \"%s\")\n", - network_->cellName(network_->topInstance())); + network_->cellName(network_->topInstance())); gzprintf(stream_, " (INSTANCE)\n"); gzprintf(stream_, " (DELAY\n"); gzprintf(stream_, " (ABSOLUTE\n"); @@ -369,7 +359,7 @@ SdfWriter::writeInstTrailer() void SdfWriter::writeIopaths(const Instance *inst, - bool &inst_header) + bool &inst_header) { bool iopath_header = false; InstancePinIterator *pin_iter = network_->pinIterator(inst); @@ -379,39 +369,39 @@ SdfWriter::writeIopaths(const Instance *inst, Vertex *from_vertex = graph_->pinLoadVertex(from_pin); VertexOutEdgeIterator edge_iter(from_vertex, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - const TimingRole *role = edge->role(); - if (role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::regClkToQ() - || role == TimingRole::regSetClr() - || role == TimingRole::latchEnToQ() - || role == TimingRole::latchDtoQ()) { - Vertex *to_vertex = edge->to(graph_); - Pin *to_pin = to_vertex->pin(); - if (!inst_header) { - writeInstHeader(inst); - inst_header = true; - } - if (!iopath_header) { - writeIopathHeader(); - iopath_header = true; - } - const char *sdf_cond = edge->timingArcSet()->sdfCond(); - if (sdf_cond) { - gzprintf(stream_, " (COND %s\n", sdf_cond); - gzprintf(stream_, " "); - } - string from_pin_name = sdfPortName(from_pin); - string to_pin_name = sdfPortName(to_pin); + Edge *edge = edge_iter.next(); + const TimingRole *role = edge->role(); + if (role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::regClkToQ() + || role == TimingRole::regSetClr() + || role == TimingRole::latchEnToQ() + || role == TimingRole::latchDtoQ()) { + Vertex *to_vertex = edge->to(graph_); + Pin *to_pin = to_vertex->pin(); + if (!inst_header) { + writeInstHeader(inst); + inst_header = true; + } + if (!iopath_header) { + writeIopathHeader(); + iopath_header = true; + } + const char *sdf_cond = edge->timingArcSet()->sdfCond(); + if (sdf_cond) { + gzprintf(stream_, " (COND %s\n", sdf_cond); + gzprintf(stream_, " "); + } + string from_pin_name = sdfPortName(from_pin); + string to_pin_name = sdfPortName(to_pin); gzprintf(stream_, " (IOPATH %s %s ", - from_pin_name.c_str(), - to_pin_name.c_str()); - writeArcDelays(edge); - if (sdf_cond) - gzprintf(stream_, ")"); - gzprintf(stream_, ")\n"); - } + from_pin_name.c_str(), + to_pin_name.c_str()); + writeArcDelays(edge); + if (sdf_cond) + gzprintf(stream_, ")"); + gzprintf(stream_, ")\n"); + } } } } @@ -455,9 +445,9 @@ SdfWriter::writeArcDelays(Edge *edge) writeSdfTriple(delays, RiseFall::rise()); // Merge rise/fall values if they are the same. if (!(fuzzyEqual(delays.value(RiseFall::rise(), MinMax::min()), - delays.value(RiseFall::fall(), MinMax::min())) - && fuzzyEqual(delays.value(RiseFall::rise(), MinMax::max()), - delays.value(RiseFall::fall(),MinMax::max())))) { + delays.value(RiseFall::fall(), MinMax::min())) + && fuzzyEqual(delays.value(RiseFall::rise(), MinMax::max()), + delays.value(RiseFall::fall(),MinMax::max())))) { gzprintf(stream_, " "); writeSdfTriple(delays, RiseFall::fall()); } @@ -506,7 +496,7 @@ SdfWriter::writeSdfDelay(double delay) void SdfWriter::writeTimingChecks(const Instance *inst, - bool &inst_header) + bool &inst_header) { bool check_header = false; @@ -517,40 +507,40 @@ SdfWriter::writeTimingChecks(const Instance *inst, if (vertex) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - const TimingRole *role = edge->role(); - const char *sdf_check = nullptr; - if (role == TimingRole::setup()) - sdf_check = "SETUP"; - else if (role == TimingRole::hold()) - sdf_check = "HOLD"; - else if (role == TimingRole::recovery()) - sdf_check = "RECOVERY"; - else if (role == TimingRole::removal()) - sdf_check = "REMOVAL"; - if (sdf_check) { - ensureTimingCheckheaders(check_header, inst, inst_header); - writeCheck(edge, sdf_check); - } + Edge *edge = edge_iter.next(); + const TimingRole *role = edge->role(); + const char *sdf_check = nullptr; + if (role == TimingRole::setup()) + sdf_check = "SETUP"; + else if (role == TimingRole::hold()) + sdf_check = "HOLD"; + else if (role == TimingRole::recovery()) + sdf_check = "RECOVERY"; + else if (role == TimingRole::removal()) + sdf_check = "REMOVAL"; + if (sdf_check) { + ensureTimingCheckheaders(check_header, inst, inst_header); + writeCheck(edge, sdf_check); + } } for (auto hi_low : RiseFall::range()) { - float min_width, max_width; - Edge *edge; - 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_)); - ensureTimingCheckheaders(check_header, inst, inst_header); - writeWidthCheck(pin, hi_low, min_width, max_width); - } + float min_width, max_width; + Edge *edge; + 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_)); + ensureTimingCheckheaders(check_header, inst, inst_header); + writeWidthCheck(pin, hi_low, min_width, max_width); + } } float min_period; bool exists; - graph_delay_calc_->minPeriod(pin, corner_, min_period, exists); + graph_delay_calc_->minPeriod(pin, scene_, min_period, exists); if (exists) { - ensureTimingCheckheaders(check_header, inst, inst_header); - writePeriodCheck(pin, min_period); + ensureTimingCheckheaders(check_header, inst, inst_header); + writePeriodCheck(pin, min_period); } } } @@ -562,8 +552,8 @@ SdfWriter::writeTimingChecks(const Instance *inst, void SdfWriter::ensureTimingCheckheaders(bool &check_header, - const Instance *inst, - bool &inst_header) + const Instance *inst, + bool &inst_header) { if (!inst_header) { writeInstHeader(inst); @@ -589,7 +579,7 @@ SdfWriter::writeTimingCheckTrailer() void SdfWriter::writeCheck(Edge *edge, - const char *sdf_check) + const char *sdf_check) { TimingArcSet *arc_set = edge->timingArcSet(); // Examine the arcs to see if the check requires clk or data edge specifiers. @@ -605,7 +595,7 @@ SdfWriter::writeCheck(Edge *edge, && arcs[RiseFall::fallIndex()][RiseFall::fallIndex()] == nullptr) writeEdgeCheck(edge, sdf_check, RiseFall::riseIndex(), arcs); else if (arcs[RiseFall::riseIndex()][RiseFall::riseIndex()] == nullptr - && arcs[RiseFall::riseIndex()][RiseFall::fallIndex()] == nullptr) + && arcs[RiseFall::riseIndex()][RiseFall::fallIndex()] == nullptr) writeEdgeCheck(edge, sdf_check, RiseFall::fallIndex(), arcs); else { // No special case; write all the checks with data and clock edge specifiers. @@ -616,9 +606,9 @@ SdfWriter::writeCheck(Edge *edge, void SdfWriter::writeEdgeCheck(Edge *edge, - const char *sdf_check, - int clk_rf_index, - TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]) + const char *sdf_check, + int clk_rf_index, + TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]) { // SDF requires edge specifiers on the data port to define separate // rise/fall check values. @@ -629,36 +619,36 @@ SdfWriter::writeEdgeCheck(Edge *edge, && 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_)) + 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_))) + 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); + sdf_check, false, true); else { if (arcs[clk_rf_index][RiseFall::riseIndex()]) writeCheck(edge, arcs[clk_rf_index][RiseFall::riseIndex()], - sdf_check, true, true); + sdf_check, true, true); if (arcs[clk_rf_index][RiseFall::fallIndex()]) writeCheck(edge, arcs[clk_rf_index][RiseFall::fallIndex()], - sdf_check, true, true); + sdf_check, true, true); } } void SdfWriter::writeCheck(Edge *edge, - TimingArc *arc, - const char *sdf_check, - bool use_data_edge, - bool use_clk_edge) + TimingArc *arc, + const char *sdf_check, + bool use_data_edge, + bool use_clk_edge) { TimingArcSet *arc_set = edge->timingArcSet(); Pin *from_pin = edge->from(graph_)->pin(); @@ -674,8 +664,8 @@ SdfWriter::writeCheck(Edge *edge, string to_pin_name = sdfPortName(to_pin); if (use_data_edge) { gzprintf(stream_, "(%s %s)", - sdfEdge(arc->toEdge()), - to_pin_name.c_str()); + sdfEdge(arc->toEdge()), + to_pin_name.c_str()); } else gzprintf(stream_, "%s", to_pin_name.c_str()); @@ -691,8 +681,8 @@ SdfWriter::writeCheck(Edge *edge, string from_pin_name = sdfPortName(from_pin); if (use_clk_edge) gzprintf(stream_, "(%s %s)", - sdfEdge(arc->fromEdge()), - from_pin_name.c_str()); + sdfEdge(arc->fromEdge()), + from_pin_name.c_str()); else gzprintf(stream_, "%s", from_pin_name.c_str()); @@ -710,21 +700,21 @@ SdfWriter::writeCheck(Edge *edge, void SdfWriter::writeWidthCheck(const Pin *pin, - const RiseFall *hi_low, - float min_width, - float max_width) + const RiseFall *hi_low, + float min_width, + float max_width) { string pin_name = sdfPortName(pin); gzprintf(stream_, " (WIDTH (%s %s) ", - sdfEdge(hi_low->asTransition()), - pin_name.c_str()); + sdfEdge(hi_low->asTransition()), + pin_name.c_str()); writeSdfTriple(min_width, max_width); gzprintf(stream_, ")\n"); } void SdfWriter::writePeriodCheck(const Pin *pin, - float min_period) + float min_period) { string pin_name = sdfPortName(pin); gzprintf(stream_, " (PERIOD %s ", pin_name.c_str()); @@ -766,7 +756,6 @@ SdfWriter::sdfPathName(const Instance *instance) { InstanceSeq inst_path; network_->path(instance, inst_path); - InstanceSeq::Iterator path_iter1(inst_path); string path_name; while (!inst_path.empty()) { const Instance *inst = inst_path.back(); @@ -791,8 +780,8 @@ SdfWriter::sdfName(const Instance *inst) // Ignore sta escapes. if (ch != network_escape_) { if (!(isalnum(ch) || ch == '_')) - // Insert escape. - sdf_name += sdf_escape_; + // Insert escape. + sdf_name += sdf_escape_; sdf_name += ch; } p++; diff --git a/sdf/SdfWriter.hh b/sdf/SdfWriter.hh index 382faa71b..3c992366f 100644 --- a/sdf/SdfWriter.hh +++ b/sdf/SdfWriter.hh @@ -27,17 +27,17 @@ namespace sta { class StaState; -class Corner; +class Scene; void writeSdf(const char *filename, - const Corner *corner, - char divider, + const Scene *scene, + char divider, bool include_typ, - int digits, - bool gzip, - bool no_timestamp, - bool no_version, - StaState *sta); + int digits, + bool gzip, + bool no_timestamp, + bool no_version, + StaState *sta); } // namespace diff --git a/search/Bdd.cc b/search/Bdd.cc index 0845c24c6..89d776555 100644 --- a/search/Bdd.cc +++ b/search/Bdd.cc @@ -49,17 +49,17 @@ Bdd::funcBdd(const FuncExpr *expr) DdNode *right = nullptr; DdNode *result = nullptr; switch (expr->op()) { - case FuncExpr::op_port: { + case FuncExpr::Op::port: { LibertyPort *port = expr->port(); result = ensureNode(port); break; } - case FuncExpr::op_not: + case FuncExpr::Op::not_: left = funcBdd(expr->left()); if (left) result = Cudd_Not(left); break; - case FuncExpr::op_or: + case FuncExpr::Op::or_: left = funcBdd(expr->left()); right = funcBdd(expr->right()); if (left && right) @@ -69,7 +69,7 @@ Bdd::funcBdd(const FuncExpr *expr) else if (right) result = right; break; - case FuncExpr::op_and: + case FuncExpr::Op::and_: left = funcBdd(expr->left()); right = funcBdd(expr->right()); if (left && right) @@ -79,7 +79,7 @@ Bdd::funcBdd(const FuncExpr *expr) else if (right) result = right; break; - case FuncExpr::op_xor: + case FuncExpr::Op::xor_: left = funcBdd(expr->left()); right = funcBdd(expr->right()); if (left && right) @@ -89,10 +89,10 @@ Bdd::funcBdd(const FuncExpr *expr) else if (right) result = right; break; - case FuncExpr::op_one: + case FuncExpr::Op::one: result = Cudd_ReadOne(cudd_mgr_); break; - case FuncExpr::op_zero: + case FuncExpr::Op::zero: result = Cudd_ReadLogicZero(cudd_mgr_); break; default: diff --git a/search/Bfs.cc b/search/Bfs.cc index ebea9fdbf..5ea3d470e 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -122,21 +122,14 @@ BfsIterator::empty() const void BfsIterator::enqueueAdjacentVertices(Vertex *vertex) { - enqueueAdjacentVertices(vertex, search_pred_, level_max_); + enqueueAdjacentVertices(vertex, search_pred_); } void BfsIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred) + const Mode *mode) { - enqueueAdjacentVertices(vertex, search_pred, level_max_); -} - -void -BfsIterator::enqueueAdjacentVertices(Vertex *vertex, - Level to_level) -{ - enqueueAdjacentVertices(vertex, search_pred_, to_level); + enqueueAdjacentVertices(vertex, search_pred_, mode); } int @@ -395,22 +388,37 @@ BfsFwdIterator::levelLess(Level level1, void BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, - Level to_level) + SearchPred *search_pred) { if (search_pred->searchFrom(vertex)) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); - if (to_vertex->level() <= to_level - && search_pred->searchThru(edge) + if (search_pred->searchThru(edge) && search_pred->searchTo(to_vertex)) enqueue(to_vertex); } } } +void +BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + const Mode *mode) +{ + if (search_pred->searchFrom(vertex, mode)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (search_pred->searchThru(edge, mode) + && search_pred->searchTo(to_vertex, mode)) + enqueue(to_vertex); + } + } +} + //////////////////////////////////////////////////////////////// BfsBkwdIterator::BfsBkwdIterator(BfsIndex bfs_index, @@ -449,20 +457,35 @@ BfsBkwdIterator::levelLess(Level level1, void BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, - Level to_level) + SearchPred *search_pred) { if (search_pred->searchTo(vertex)) { VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *from_vertex = edge->from(graph_); - if (from_vertex->level() >= to_level - && search_pred->searchFrom(from_vertex) + if (search_pred->searchFrom(from_vertex) && search_pred->searchThru(edge)) enqueue(from_vertex); } } } +void +BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + const Mode *mode) +{ + if (search_pred->searchTo(vertex, mode)) { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + if (search_pred->searchFrom(from_vertex, mode) + && search_pred->searchThru(edge, mode)) + enqueue(from_vertex); + } + } +} + } // namespace diff --git a/search/CheckCapacitanceLimits.cc b/search/CheckCapacitanceLimits.cc deleted file mode 100644 index 391ecda13..000000000 --- a/search/CheckCapacitanceLimits.cc +++ /dev/null @@ -1,356 +0,0 @@ -// 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 "CheckCapacitanceLimits.hh" - -#include "Fuzzy.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" -#include "InputDrive.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" -#include "StaState.hh" -#include "Corner.hh" -#include "PortDirection.hh" -#include "Sim.hh" -#include "Graph.hh" -#include "GraphDelayCalc.hh" - -namespace sta { - -class PinCapacitanceLimitSlackLess -{ -public: - PinCapacitanceLimitSlackLess(const Corner *corner, - const MinMax *min_max, - CheckCapacitanceLimits *check_capacitance_limit, - const StaState *sta); - bool operator()(const Pin *pin1, - const Pin *pin2) const; - -private: - const Corner *corner_; - const MinMax *min_max_; - CheckCapacitanceLimits *check_capacitance_limit_; - const StaState *sta_; - -}; - -PinCapacitanceLimitSlackLess::PinCapacitanceLimitSlackLess(const Corner *corner, - const MinMax *min_max, - CheckCapacitanceLimits *check_capacitance_limit, - const StaState *sta) : - corner_(corner), - min_max_(min_max), - check_capacitance_limit_(check_capacitance_limit), - sta_(sta) -{ -} - -bool -PinCapacitanceLimitSlackLess::operator()(const Pin *pin1, - const Pin *pin2) const -{ - const Corner *corner1, *corner2; - const RiseFall *rf1, *rf2; - float capacitance1, capacitance2; - float limit1, limit2, slack1, slack2; - check_capacitance_limit_->checkCapacitance(pin1, corner_, min_max_, - corner1, rf1, capacitance1, - limit1, slack1); - check_capacitance_limit_->checkCapacitance(pin2, corner_, min_max_, - corner2, rf2, capacitance2, - limit2, slack2); - return fuzzyLess(slack1, slack2) - || (fuzzyEqual(slack1, slack2) - // Break ties for the sake of regression stability. - && sta_->network()->pinLess(pin1, pin2)); -} - -//////////////////////////////////////////////////////////////// - -CheckCapacitanceLimits::CheckCapacitanceLimits(const Sta *sta) : - sta_(sta) -{ -} - -void -CheckCapacitanceLimits::checkCapacitance(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &limit1, - float &slack1) const -{ - corner1 = nullptr; - rf1 = nullptr; - capacitance1 = 0.0; - limit1 = 0.0; - slack1 = MinMax::min()->initValue(); - if (corner) - checkCapacitance1(pin, corner, min_max, - corner1, rf1, capacitance1, limit1, slack1); - else { - for (auto corner : *sta_->corners()) { - checkCapacitance1(pin, corner, min_max, - corner1, rf1, capacitance1, limit1, slack1); - } - } -} - -void -CheckCapacitanceLimits::checkCapacitance1(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &limit1, - float &slack1) const -{ - float limit; - bool limit_exists; - findLimit(pin, corner, min_max, limit, limit_exists); - if (limit_exists) { - for (auto rf : RiseFall::range()) { - checkCapacitance(pin, corner, min_max, rf, limit, - corner1, rf1, capacitance1, slack1, limit1); - } - } -} - -// Return the tightest limit. -void -CheckCapacitanceLimits::findLimit(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const -{ - const Network *network = sta_->network(); - Sdc *sdc = sta_->sdc(); - - // Default to top ("design") limit. - Cell *top_cell = network->cell(network->topInstance()); - sdc->capacitanceLimit(top_cell, min_max, - limit, exists); - - float limit1; - bool exists1; - if (network->isTopLevelPort(pin)) { - Port *port = network->port(pin); - sdc->capacitanceLimit(port, min_max, limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - InputDrive *drive = sdc->findInputDrive(port); - if (drive) { - for (auto rf : RiseFall::range()) { - const LibertyCell *cell; - const LibertyPort *from_port; - float *from_slews; - const LibertyPort *to_port; - drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); - if (to_port) { - const LibertyPort *corner_port = to_port->cornerPort(corner, min_max); - corner_port->capacitanceLimit(min_max, limit1, exists1); - if (!exists1 - && corner_port->direction()->isAnyOutput() - && min_max == MinMax::max()) - corner_port->libertyLibrary()->defaultMaxCapacitance(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } - } - } - else { - Cell *cell = network->cell(network->instance(pin)); - sdc->capacitanceLimit(cell, min_max, - limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - LibertyPort *port = network->libertyPort(pin); - if (port) { - LibertyPort *corner_port = port->cornerPort(corner, min_max); - corner_port->capacitanceLimit(min_max, limit1, exists1); - if (!exists1 - && port->direction()->isAnyOutput()) - corner_port->libertyLibrary()->defaultMaxCapacitance(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } -} - -void -CheckCapacitanceLimits::checkCapacitance(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - const RiseFall *rf, - float limit, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &slack1, - float &limit1) const -{ - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - GraphDelayCalc *dcalc = sta_->graphDelayCalc(); - float cap = dcalc->loadCap(pin, dcalc_ap); - - float slack = (min_max == MinMax::max()) - ? limit - cap : cap - limit; - if (slack < slack1 - // Break ties for the sake of regression stability. - || (fuzzyEqual(slack, slack1) - && rf->index() < rf1->index())) { - corner1 = corner; - rf1 = rf; - capacitance1 = cap; - slack1 = slack; - limit1 = limit; - } -} - -//////////////////////////////////////////////////////////////// - -PinSeq -CheckCapacitanceLimits::checkCapacitanceLimits(const Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) -{ - const Network *network = sta_->network(); - PinSeq cap_pins; - float min_slack = MinMax::min()->initValue(); - if (net) { - NetPinIterator *pin_iter = network->pinIterator(net); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - checkCapLimits(pin, violators, corner, min_max, cap_pins, min_slack); - } - delete pin_iter; - } - else { - LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); - while (inst_iter->hasNext()) { - Instance *inst = inst_iter->next(); - checkCapLimits(inst, violators, corner, min_max, cap_pins, min_slack); - } - delete inst_iter; - // Check top level ports. - checkCapLimits(network->topInstance(), violators, corner, min_max, - cap_pins, min_slack); - } - sort(cap_pins, PinCapacitanceLimitSlackLess(corner, min_max, this, sta_)); - // Keep the min slack pin unless all violators or net pins. - if (!cap_pins.empty() && !violators && net == nullptr) - cap_pins.resize(1); - return cap_pins; -} - -void -CheckCapacitanceLimits::checkCapLimits(const Instance *inst, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &cap_pins, - float &min_slack) -{ - const Network *network = sta_->network(); - InstancePinIterator *pin_iter = network->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - checkCapLimits(pin, violators, corner, min_max, cap_pins, min_slack); - } - delete pin_iter; -} - -void -CheckCapacitanceLimits::checkCapLimits(const Pin *pin, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &cap_pins, - float &min_slack) -{ - if (checkPin(pin)) { - const Corner *corner1; - const RiseFall *rf; - float capacitance, limit, slack; - checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); - if (!fuzzyInf(slack)) { - if (violators) { - if (slack < 0.0) - cap_pins.push_back(pin); - } - else { - if (cap_pins.empty() - || slack < min_slack) { - cap_pins.push_back(pin); - min_slack = slack; - } - } - } - } -} - -bool -CheckCapacitanceLimits::checkPin(const Pin *pin) -{ - const Network *network = sta_->network(); - const Sim *sim = sta_->sim(); - const Sdc *sdc = sta_->sdc(); - const Graph *graph = sta_->graph(); - Vertex *vertex = graph->pinLoadVertex(pin); - return network->isDriver(pin) - && !sim->logicZeroOne(pin) - && !sdc->isDisabled(pin) - && !(vertex && sta_->isIdealClock(pin)); -} - -} // namespace diff --git a/search/CheckCapacitanceLimits.hh b/search/CheckCapacitanceLimits.hh deleted file mode 100644 index cae737748..000000000 --- a/search/CheckCapacitanceLimits.hh +++ /dev/null @@ -1,105 +0,0 @@ -// 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 "MinMax.hh" -#include "Transition.hh" -#include "NetworkClass.hh" -#include "SdcClass.hh" -#include "Sta.hh" - -namespace sta { - -class StaState; -class Corner; - -class CheckCapacitanceLimits -{ -public: - CheckCapacitanceLimits(const Sta *sta); - // corner=nullptr checks all corners. - void checkCapacitance(const Pin *pin, - const Corner *corner1, - const MinMax *min_max, - // Return values. - // Corner is nullptr for no capacitance limit. - const Corner *&corner, - const RiseFall *&rf, - float &capacitance, - float &limit, - float &slack) const; - // Return pins with the min/max cap limit slack. - // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkCapacitanceLimits(const Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max); - -protected: - void checkCapacitance(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - const RiseFall *rf, - float limit, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &slack1, - float &limit1) const; - void checkCapacitance1(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - float &capacitance1, - float &limit1, - float &slack1) const; - void findLimit(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &limit, - bool &limit_exists) const; - void checkCapLimits(const Instance *inst, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &cap_pins, - float &min_slack); - void checkCapLimits(const Pin *pin, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &cap_pins, - float &min_slack); - bool checkPin(const Pin *pin); - - const Sta *sta_; -}; - -} // namespace diff --git a/search/CheckCapacitances.cc b/search/CheckCapacitances.cc new file mode 100644 index 000000000..c084ca8c3 --- /dev/null +++ b/search/CheckCapacitances.cc @@ -0,0 +1,369 @@ +// 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 "CheckCapacitances.hh" + +#include "ContainerHelpers.hh" +#include "Fuzzy.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Mode.hh" +#include "InputDrive.hh" +#include "GraphDelayCalc.hh" +#include "StaState.hh" +#include "Scene.hh" +#include "PortDirection.hh" +#include "Sim.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" +#include "ClkNetwork.hh" +#include "Transition.hh" +#include "BoundedHeap.hh" + +namespace sta { + +class CapacitanceCheckSlackLess +{ +public: + CapacitanceCheckSlackLess(const StaState *sta); + bool operator()(const CapacitanceCheck &check1, + const CapacitanceCheck &check2) const; + +private: + const StaState *sta_; +}; + +CapacitanceCheckSlackLess::CapacitanceCheckSlackLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +CapacitanceCheckSlackLess::operator()(const CapacitanceCheck &check1, + const CapacitanceCheck &check2) const +{ + return fuzzyLess(check1.slack(), check2.slack()) + || (fuzzyEqual(check1.slack(), check2.slack()) + // Break ties for the sake of regression stability. + && sta_->network()->pinLess(check1.pin(), check2.pin())); +} + +//////////////////////////////////////////////////////////////// + +CheckCapacitances::CheckCapacitances(const StaState *sta) : + sta_(sta) +{ +} + +void +CheckCapacitances::clear() +{ + checks_.clear(); +} + +CapacitanceCheck +CheckCapacitances::check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max) const +{ + return check(pin, false, scenes, min_max); +} + +CapacitanceCheck +CheckCapacitances::check(const Pin *pin, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) const +{ + CapacitanceCheck min_slack_check(nullptr, 0.0, min_max->initValue(), + MinMax::min()->initValue(), nullptr, nullptr); + GraphDelayCalc *dcalc = sta_->graphDelayCalc(); + + for (const Scene *scene : scenes) { + if (checkPin(pin, scene)) { + float limit; + bool limit_exists; + findLimit(pin, scene, min_max, limit, limit_exists); + if (limit_exists) { + for (const RiseFall *rf : RiseFall::range()) { + float cap = dcalc->loadCap(pin, scene, min_max); + float slack = (min_max == MinMax::max()) + ? limit - cap : cap - limit; + if ((!violators || fuzzyLess(slack, 0.0)) + && (min_slack_check.pin() == nullptr + || fuzzyLess(slack, min_slack_check.slack()) + // Break ties for the sake of regression stability. + || (fuzzyEqual(slack, min_slack_check.slack()) + && rf->index() < min_slack_check.rf()->index()))) + min_slack_check = CapacitanceCheck(pin, cap, limit, slack, scene, rf); + } + } + } + } + return min_slack_check; +} + +// Return the tightest limit. +void +CheckCapacitances::findLimit(const Pin *pin, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + const Network *network = sta_->network(); + Sdc *sdc = scene->sdc(); + + // Default to top ("design") limit. + Cell *top_cell = network->cell(network->topInstance()); + sdc->capacitanceLimit(top_cell, min_max, + limit, exists); + + float limit1; + bool exists1; + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->capacitanceLimit(port, min_max, limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + InputDrive *drive = sdc->findInputDrive(port); + if (drive) { + for (auto rf : RiseFall::range()) { + const LibertyCell *cell; + const LibertyPort *from_port; + float *from_slews; + const LibertyPort *to_port; + drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); + if (to_port) { + const LibertyPort *scene_port = to_port->scenePort(scene, min_max); + scene_port->capacitanceLimit(min_max, limit1, exists1); + if (!exists1 + && scene_port->direction()->isAnyOutput() + && min_max == MinMax::max()) + scene_port->libertyLibrary()->defaultMaxCapacitance(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } + } + } + else { + Cell *cell = network->cell(network->instance(pin)); + sdc->capacitanceLimit(cell, min_max, + limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + LibertyPort *port = network->libertyPort(pin); + if (port) { + LibertyPort *scene_port = port->scenePort(scene, min_max); + scene_port->capacitanceLimit(min_max, limit1, exists1); + if (!exists1 + && port->direction()->isAnyOutput()) + scene_port->libertyLibrary()->defaultMaxCapacitance(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } +} + +//////////////////////////////////////////////////////////////// + +CapacitanceCheckSeq & +CheckCapacitances::check(const Net *net, + size_t max_count, + bool violations, + const SceneSeq &scenes, + const MinMax *min_max) +{ + clear(); + if (violations) + return checkViolations(net, scenes, min_max); + else + return checkMaxCount(net, max_count, scenes, min_max); +} + +CapacitanceCheckSeq & +CheckCapacitances::checkViolations(const Net *net, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + if (net) { + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + CapacitanceCheck cap_check = check(pin, true, scenes, min_max); + if (!cap_check.isNull()) + checks_.push_back(cap_check); + } + delete pin_iter; + } + else { + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + checkCapLimits(inst, true, scenes, min_max); + } + delete inst_iter; + // Check top level ports. + checkCapLimits(network->topInstance(), true, scenes, min_max); + } + + sort(checks_, CapacitanceCheckSlackLess(sta_)); + return checks_; +} + +CapacitanceCheckSeq & +CheckCapacitances::checkMaxCount(const Net *net, + size_t max_count, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + CapacitanceCheckHeap heap(max_count, CapacitanceCheckSlackLess(sta_)); + + if (net) { + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + check(pin, scenes, min_max, heap); + } + delete pin_iter; + } + else { + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + checkCapLimits(inst, scenes, min_max, heap); + } + delete inst_iter; + // Check top level ports. + checkCapLimits(network->topInstance(), scenes, min_max, heap); + } + + checks_ = heap.extract(); + return checks_; +} + +void +CheckCapacitances::checkCapLimits(const Instance *inst, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + CapacitanceCheck cap_check = check(pin, violators, scenes, min_max); + if (!cap_check.isNull()) + checks_.push_back(cap_check); + } + delete pin_iter; +} + +void +CheckCapacitances::checkCapLimits(const Instance *inst, + const SceneSeq &scenes, + const MinMax *min_max, + CapacitanceCheckHeap &heap) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + check(pin, scenes, min_max, heap); + } + delete pin_iter; +} + +void +CheckCapacitances::check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max, + CapacitanceCheckHeap &heap) +{ + CapacitanceCheck cap_check = check(pin, false, scenes, min_max); + if (!cap_check.isNull()) + heap.insert(cap_check); +} + +bool +CheckCapacitances::checkPin(const Pin *pin, + const Scene *scene) const +{ + const Network *network = sta_->network(); + const Mode *mode = scene->mode(); + return network->isDriver(pin) + && !mode->sim()->isConstant(pin) + && !mode->sdc()->isDisabledConstraint(pin) + && !mode->clkNetwork()->isIdealClock(pin); +} + +//////////////////////////////////////////////////////////////// + +CapacitanceCheck::CapacitanceCheck() : + pin_(nullptr), + capacitance_(0.0), + limit_(INF), + slack_(-INF), + scene_(nullptr), + rf_(nullptr) +{ +} + +CapacitanceCheck::CapacitanceCheck(const Pin *pin, + float capacitance, + float limit, + float slack, + const Scene *scene, + const RiseFall *rf) : + pin_(pin), + capacitance_(capacitance), + limit_(limit), + slack_(slack), + scene_(scene), + rf_(rf) +{ +} + +} // namespace diff --git a/search/CheckCapacitances.hh b/search/CheckCapacitances.hh new file mode 100644 index 000000000..4d395cf53 --- /dev/null +++ b/search/CheckCapacitances.hh @@ -0,0 +1,128 @@ +// 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 + +#include "MinMax.hh" +#include "Transition.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "StaState.hh" +#include "BoundedHeap.hh" + +namespace sta { + +class StaState; +class Scene; +class RiseFall; +class CapacitanceCheckSlackLess; + +class CapacitanceCheck +{ +public: + CapacitanceCheck(); + CapacitanceCheck(const Pin *pin, + float capacitance, + float limit, + float slack, + const Scene *scene, + const RiseFall *rf); + bool isNull() { return pin_ == nullptr; } + const Pin *pin() const { return pin_; } + float capacitance() const { return capacitance_; } + float limit() const { return limit_; } + float slack() const { return slack_; } + const Scene *scene() const { return scene_; } + const RiseFall *rf() const { return rf_; } + +private: + const Pin *pin_; + float capacitance_; + float limit_; + float slack_; + const Scene *scene_; + const RiseFall *rf_; +}; + +using CapacitanceCheckSeq = std::vector; +using CapacitanceCheckHeap = BoundedHeap; + +class CheckCapacitances +{ +public: + CheckCapacitances(const StaState *sta); + void clear(); + // Return pins with the min/max cap limit slack. + // net=null check all nets + CapacitanceCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + // Return min slack check across scenes. + CapacitanceCheck check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max) const; + +protected: + CapacitanceCheck check(const Pin *pin, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) const; + void findLimit(const Pin *pin, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &limit, + bool &limit_exists) const; + void checkCapLimits(const Instance *inst, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void checkCapLimits(const Instance *inst, + const SceneSeq &scenes, + const MinMax *min_max, + CapacitanceCheckHeap &heap); + void check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max, + CapacitanceCheckHeap &heap); + CapacitanceCheckSeq &checkViolations(const Net *net, + const SceneSeq &scenes, + const MinMax *min_max); + CapacitanceCheckSeq &checkMaxCount(const Net *net, + size_t max_count, + const SceneSeq &scenes, + const MinMax *min_max); + bool checkPin(const Pin *pin, + const Scene *scene) const; + + const StaState *sta_; + CapacitanceCheckSeq checks_; +}; + +} // namespace + diff --git a/search/CheckFanoutLimits.cc b/search/CheckFanoutLimits.cc deleted file mode 100644 index 726fbc31e..000000000 --- a/search/CheckFanoutLimits.cc +++ /dev/null @@ -1,332 +0,0 @@ -// 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 "CheckFanoutLimits.hh" - -#include "Fuzzy.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" -#include "InputDrive.hh" -#include "Sim.hh" -#include "PortDirection.hh" -#include "Graph.hh" -#include "Search.hh" - -namespace sta { - -class PinFanoutLimitSlackLess -{ -public: - PinFanoutLimitSlackLess(const MinMax *min_max, - CheckFanoutLimits *check_fanout_limit, - const StaState *sta); - bool operator()(const Pin *pin1, - const Pin *pin2) const; - -private: - const MinMax *min_max_; - CheckFanoutLimits *check_fanout_limit_; - const StaState *sta_; - -}; - -PinFanoutLimitSlackLess::PinFanoutLimitSlackLess(const MinMax *min_max, - CheckFanoutLimits *check_fanout_limit, - const StaState *sta) : - min_max_(min_max), - check_fanout_limit_(check_fanout_limit), - sta_(sta) -{ -} - -bool -PinFanoutLimitSlackLess::operator()(const Pin *pin1, - const Pin *pin2) const -{ - float fanout1, fanout2; - float limit1, limit2, slack1, slack2; - check_fanout_limit_->checkFanout(pin1, min_max_, - fanout1, limit1, slack1); - check_fanout_limit_->checkFanout(pin2, min_max_, - fanout2, limit2, slack2); - return fuzzyLess(slack1, slack2) - || (fuzzyEqual(slack1, slack2) - // Break ties for the sake of regression stability. - && sta_->network()->pinLess(pin1, pin2)); -} - -//////////////////////////////////////////////////////////////// - -CheckFanoutLimits::CheckFanoutLimits(const Sta *sta) : - sta_(sta) -{ -} - -void -CheckFanoutLimits::checkFanout(const Pin *pin, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack) const -{ - fanout = 0.0; - limit = min_max->initValue(); - slack = MinMax::min()->initValue(); - - float limit1; - bool limit1_exists; - findLimit(pin, min_max, limit1, limit1_exists); - if (limit1_exists) - checkFanout(pin, min_max, limit1, - fanout, limit, slack); -} - -// return the tightest limit. -void -CheckFanoutLimits::findLimit(const Pin *pin, - const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const -{ - const Network *network = sta_->network(); - Sdc *sdc = sta_->sdc(); - - limit = min_max->initValue(); - exists = false; - - // Default to top ("design") limit. - // Applies to input ports as well as instance outputs. - Cell *top_cell = network->cell(network->topInstance()); - sdc->fanoutLimit(top_cell, min_max, - limit, exists); - - float limit1; - bool exists1; - if (network->isTopLevelPort(pin)) { - Port *port = network->port(pin); - sdc->fanoutLimit(port, min_max, limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - InputDrive *drive = sdc->findInputDrive(port); - if (drive) { - for (auto rf : RiseFall::range()) { - const LibertyCell *cell; - const LibertyPort *from_port; - float *from_slews; - const LibertyPort *to_port; - drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); - if (to_port) { - to_port->fanoutLimit(min_max, limit1, exists1); - if (!exists1 - && min_max == MinMax::max() - && to_port->direction()->isAnyOutput()) - to_port->libertyLibrary()->defaultMaxFanout(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } - } - } - else { - Cell *cell = network->cell(network->instance(pin)); - sdc->fanoutLimit(cell, min_max, - limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - LibertyPort *port = network->libertyPort(pin); - if (port) { - port->fanoutLimit(min_max, limit1, exists1); - if (!exists1 - && min_max == MinMax::max() - && port->direction()->isAnyOutput()) - port->libertyLibrary()->defaultMaxFanout(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } -} - -void -CheckFanoutLimits::checkFanout(const Pin *pin, - const MinMax *min_max, - float limit1, - // Return values. - float &fanout, - float &limit, - float &slack) const -{ - float fanout1 = fanoutLoad(pin); - float slack1 = (min_max == MinMax::max()) - ? limit1 - fanout1 - : fanout1 - limit1; - if (fuzzyLessEqual(slack1, slack)) { - fanout = fanout1; - slack = slack1; - limit = limit1; - } -} - -float -CheckFanoutLimits::fanoutLoad(const Pin *pin) const -{ - float fanout = 0; - const Network *network = sta_->network(); - NetConnectedPinIterator *pin_iter = network->connectedPinIterator(pin); - while (pin_iter->hasNext()) { - const Pin *fanout_pin = pin_iter->next(); - if (network->isLoad(fanout_pin) - && !network->isTopLevelPort(fanout_pin)) { - LibertyPort *port = network->libertyPort(fanout_pin); - if (port) { - float fanout_load; - bool exists; - port->fanoutLoad(fanout_load, exists); - if (!exists) { - LibertyLibrary *lib = port->libertyLibrary(); - lib->defaultFanoutLoad(fanout_load, exists); - } - if (exists) - fanout += fanout_load; - } - else - fanout += 1; - } - } - delete pin_iter; - return fanout; -} - -//////////////////////////////////////////////////////////////// - -PinSeq -CheckFanoutLimits::checkFanoutLimits(const Net *net, - bool violators, - const MinMax *min_max) -{ - const Network *network = sta_->network(); - PinSeq fanout_pins; - float min_slack = MinMax::min()->initValue(); - if (net) { - NetPinIterator *pin_iter = network->pinIterator(net); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - checkFanoutLimits(pin, violators, min_max, fanout_pins, min_slack); - } - delete pin_iter; - } - else { - LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); - while (inst_iter->hasNext()) { - const Instance *inst = inst_iter->next(); - checkFanoutLimits(inst, violators, min_max, fanout_pins, min_slack); - } - delete inst_iter; - // Check top level ports. - checkFanoutLimits(network->topInstance(), violators, min_max, - fanout_pins, min_slack); - } - sort(fanout_pins, PinFanoutLimitSlackLess(min_max, this, sta_)); - // Keep the min slack pin unless all violators or net pins. - if (!fanout_pins.empty() && !violators && net == nullptr) - fanout_pins.resize(1); - return fanout_pins; -} - -void -CheckFanoutLimits::checkFanoutLimits(const Instance *inst, - bool violators, - const MinMax *min_max, - PinSeq &fanout_pins, - float &min_slack) -{ - const Network *network = sta_->network(); - InstancePinIterator *pin_iter = network->pinIterator(inst); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - checkFanoutLimits(pin, violators, min_max, fanout_pins, min_slack); - } - delete pin_iter; -} - -void -CheckFanoutLimits::checkFanoutLimits(const Pin *pin, - bool violators, - const MinMax *min_max, - PinSeq &fanout_pins, - float &min_slack) -{ - if (checkPin(pin)) { - float fanout; - float limit, slack; - checkFanout(pin, min_max, fanout, limit, slack); - if (!fuzzyInf(slack)) { - if (violators) { - if (slack < 0.0) - fanout_pins.push_back(pin); - } - else { - if (fanout_pins.empty() - || slack < min_slack) { - fanout_pins.push_back(pin); - min_slack = slack; - } - } - } - } -} - -bool -CheckFanoutLimits::checkPin(const Pin *pin) -{ - const Network *network = sta_->network(); - const Sim *sim = sta_->sim(); - const Sdc *sdc = sta_->sdc(); - const Graph *graph = sta_->graph(); - Vertex *vertex = graph->pinDrvrVertex(pin); - return network->isDriver(pin) - && !sim->logicZeroOne(pin) - && !sdc->isDisabled(pin) - && !(vertex && sta_->isIdealClock(pin)); -} - -} // namespace diff --git a/search/CheckFanoutLimits.hh b/search/CheckFanoutLimits.hh deleted file mode 100644 index 0af63f3e3..000000000 --- a/search/CheckFanoutLimits.hh +++ /dev/null @@ -1,82 +0,0 @@ -// 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 "MinMax.hh" -#include "NetworkClass.hh" -#include "SdcClass.hh" -#include "Sta.hh" - -namespace sta { - -class StaState; - -class CheckFanoutLimits -{ -public: - CheckFanoutLimits(const Sta *sta); - void checkFanout(const Pin *pin, - const MinMax *min_max, - // Return values. - float &fanout, - float &limit, - float &slack) const; - // Return pins with the min/max fanout limit slack. - // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkFanoutLimits(const Net *net, - bool violators, - const MinMax *min_max); - -protected: - void checkFanout(const Pin *pin, - const MinMax *min_max, - float limit1, - // Return values. - float &fanout, - float &limit, - float &slack) const; - void findLimit(const Pin *pin, - const MinMax *min_max, - // Return values. - float &limit, - bool &limit_exists) const; - float fanoutLoad(const Pin *pin) const; - void checkFanoutLimits(const Instance *inst, - bool violators, - const MinMax *min_max, - PinSeq &fanout_pins, - float &min_slack); - void checkFanoutLimits(const Pin *pin, - bool violators, - const MinMax *min_max, - PinSeq &fanout_pins, - float &min_slack); - bool checkPin(const Pin *pin); - - const Sta *sta_; -}; - -} // namespace diff --git a/search/CheckFanouts.cc b/search/CheckFanouts.cc new file mode 100644 index 000000000..7b5a03d01 --- /dev/null +++ b/search/CheckFanouts.cc @@ -0,0 +1,334 @@ +// 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 "CheckFanouts.hh" + +#include "ContainerHelpers.hh" +#include "Fuzzy.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Mode.hh" +#include "InputDrive.hh" +#include "Sim.hh" +#include "PortDirection.hh" +#include "Graph.hh" +#include "Search.hh" +#include "ClkNetwork.hh" + +namespace sta { + +CheckFanouts::CheckFanouts(const Sta *sta) : + sta_(sta), + heap_(0, FanoutCheckSlackLess(sta)) +{ +} + +void +CheckFanouts::clear() +{ + checks_.clear(); + heap_.clear(); +} + +FanoutCheck +CheckFanouts::check(const Pin *pin, + const Mode *mode, + const MinMax *min_max) const +{ + FanoutCheck min_slack_check; + float fanout = fanoutLoad(pin); + if (checkPin(pin, mode)) { + float limit; + bool limit_exists; + findLimit(pin, mode->sdc(), min_max, limit, limit_exists); + if (limit_exists) { + float slack = (min_max == MinMax::max()) + ? limit - fanout + : fanout - limit; + return FanoutCheck(pin, fanout, limit, slack, mode); + } + } + return FanoutCheck(); +} + +// return the tightest limit. +void +CheckFanouts::findLimit(const Pin *pin, + const Sdc *sdc, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + const Network *network = sta_->network(); + + limit = min_max->initValue(); + exists = false; + + // Default to top ("design") limit. + // Applies to input ports as well as instance outputs. + Cell *top_cell = network->cell(network->topInstance()); + sdc->fanoutLimit(top_cell, min_max, + limit, exists); + + float limit1; + bool exists1; + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->fanoutLimit(port, min_max, limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + InputDrive *drive = sdc->findInputDrive(port); + if (drive) { + for (auto rf : RiseFall::range()) { + const LibertyCell *cell; + const LibertyPort *from_port; + float *from_slews; + const LibertyPort *to_port; + drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); + if (to_port) { + to_port->fanoutLimit(min_max, limit1, exists1); + if (!exists1 + && min_max == MinMax::max() + && to_port->direction()->isAnyOutput()) + to_port->libertyLibrary()->defaultMaxFanout(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } + } + } + else { + Cell *cell = network->cell(network->instance(pin)); + sdc->fanoutLimit(cell, min_max, + limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + LibertyPort *port = network->libertyPort(pin); + if (port) { + port->fanoutLimit(min_max, limit1, exists1); + if (!exists1 + && min_max == MinMax::max() + && port->direction()->isAnyOutput()) + port->libertyLibrary()->defaultMaxFanout(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } +} + +float +CheckFanouts::fanoutLoad(const Pin *pin) const +{ + float fanout = 0; + const Network *network = sta_->network(); + NetConnectedPinIterator *pin_iter = network->connectedPinIterator(pin); + while (pin_iter->hasNext()) { + const Pin *fanout_pin = pin_iter->next(); + if (network->isLoad(fanout_pin) + && !network->isTopLevelPort(fanout_pin)) { + LibertyPort *port = network->libertyPort(fanout_pin); + if (port) { + float fanout_load; + bool exists; + port->fanoutLoad(fanout_load, exists); + if (!exists) { + LibertyLibrary *lib = port->libertyLibrary(); + lib->defaultFanoutLoad(fanout_load, exists); + } + if (exists) + fanout += fanout_load; + } + else + fanout += 1; + } + } + delete pin_iter; + return fanout; +} + +//////////////////////////////////////////////////////////////// + +FanoutCheckSeq & +CheckFanouts::check(const Net *net, + size_t max_count, + bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + clear(); + if (!violators) + heap_.setMaxSize(max_count); + + if (net) + checkNet(net, violators, modes, min_max); + else + checkAll(violators, modes, min_max); + + if (violators) + sort(checks_, FanoutCheckSlackLess(sta_)); + else + checks_ = heap_.extract(); + return checks_; +} + +void +CheckFanouts::checkNet(const Net *net, + bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + if (net) { + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + checkPin(pin, violators, modes, min_max); + } + delete pin_iter; + } +} + +void +CheckFanouts::checkAll(bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + const Instance *inst = inst_iter->next(); + checkInst(inst, violators, modes, min_max); + } + delete inst_iter; + // Check top level ports. + checkInst(network->topInstance(), violators, modes, min_max); +} + +void +CheckFanouts::checkInst(const Instance *inst, + bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + checkPin(pin, violators, modes, min_max); + } + delete pin_iter; +} + +void +CheckFanouts::checkPin(const Pin *pin, + bool violators, + const ModeSeq &modes, + const MinMax *min_max) +{ + for (const Mode *mode : modes) { + if (checkPin(pin, mode)) { + FanoutCheck fanout_check = check(pin, mode, min_max); + if (!fanout_check.isNull()) { + if (violators) { + if (fanout_check.slack() < 0.0) + checks_.push_back(fanout_check); + } + else + heap_.insert(fanout_check); + } + } + } +} + +bool +CheckFanouts::checkPin(const Pin *pin, + const Mode *mode) const +{ + const Network *network = sta_->network(); + return network->isDriver(pin) + && !mode->sim()->isConstant(pin) + && !mode->sdc()->isDisabledConstraint(pin) + && !mode->clkNetwork()->isIdealClock(pin); +} + +//////////////////////////////////////////////////////////////// + +FanoutCheck::FanoutCheck() : + pin_(nullptr), + fanout_(0.0), + limit_(INF), + slack_(INF), + mode_(nullptr) +{ +} + +FanoutCheck::FanoutCheck(const Pin *pin, + float fanout, + float limit, + float slack, + const Mode *mode) : + pin_(pin), + fanout_(fanout), + limit_(limit), + slack_(slack), + mode_(mode) +{ +} + +//////////////////////////////////////////////////////////////// + +FanoutCheckSlackLess::FanoutCheckSlackLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +FanoutCheckSlackLess::operator()(const FanoutCheck &check1, + const FanoutCheck &check2) const +{ + return fuzzyLess(check1.slack(), check2.slack()) + || (fuzzyEqual(check1.slack(), check2.slack()) + // Break ties for the sake of regression stability. + && sta_->network()->pinLess(check1.pin(), check2.pin())); +} + +} // namespace diff --git a/search/CheckFanouts.hh b/search/CheckFanouts.hh new file mode 100644 index 000000000..0cd3d1c3b --- /dev/null +++ b/search/CheckFanouts.hh @@ -0,0 +1,126 @@ +// 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 + +#include "MinMax.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "Sta.hh" +#include "BoundedHeap.hh" + +namespace sta { + +class StaState; + +class FanoutCheck +{ +public: + FanoutCheck(); + FanoutCheck(const Pin *pin, + float fanout, + float limit, + float slack, + const Mode *mode); + bool isNull() { return pin_ == nullptr; } + const Pin *pin() const { return pin_; } + float fanout() const { return fanout_; } + float limit() const { return limit_; } + float slack() const { return slack_; } + const Mode *mode() const { return mode_; } + +private: + const Pin *pin_; + float fanout_; + float limit_; + float slack_; + const Mode *mode_; +}; + +class FanoutCheckSlackLess +{ +public: + FanoutCheckSlackLess(const StaState *sta); + bool operator()(const FanoutCheck &check1, + const FanoutCheck &check2) const; + +private: + const StaState *sta_; +}; + +using FanoutCheckSeq = std::vector; +using FanoutCheckHeap = BoundedHeap; + +class CheckFanouts +{ +public: + CheckFanouts(const Sta *sta); + void clear(); + // Return pins with the min/max fanout limit slack. + // net=null check all nets + FanoutCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const ModeSeq &modes, + const MinMax *min_max); + FanoutCheck check(const Pin *pin, + const Mode*mode, + const MinMax *min_max) const; + +protected: + void checkNet(const Net *net, + bool violators, + const ModeSeq &modes, + const MinMax *min_max); + void checkAll(bool violators, + const ModeSeq &modes, + const MinMax *min_max); + void checkInst(const Instance *inst, + bool violators, + const ModeSeq &modes, + const MinMax *min_max); + void checkPin(const Pin *pin, + bool violators, + const ModeSeq &modes, + const MinMax *min_max); + bool checkPin(const Pin *pin, + const Mode *mode) const; + + void findLimit(const Pin *pin, + const Sdc *sdc, + const MinMax *min_max, + // Return values. + float &limit, + bool &limit_exists) const; + float fanoutLoad(const Pin *pin) const; + + const Sta *sta_; + FanoutCheckSeq checks_; + FanoutCheckHeap heap_; +}; + +} // namespace + diff --git a/search/CheckMaxSkews.cc b/search/CheckMaxSkews.cc index 3496fb0c3..8e71b8a55 100644 --- a/search/CheckMaxSkews.cc +++ b/search/CheckMaxSkews.cc @@ -31,21 +31,10 @@ #include "Graph.hh" #include "Clock.hh" #include "Path.hh" -#include "PathAnalysisPt.hh" #include "Search.hh" namespace sta { -// Abstract base class. -class MaxSkewCheckVisitor -{ -public: - MaxSkewCheckVisitor() {} - virtual ~MaxSkewCheckVisitor() {} - virtual void visit(MaxSkewCheck &check, - const StaState *sta) = 0; -}; - CheckMaxSkews::CheckMaxSkews(StaState *sta) : sta_(sta) { @@ -53,141 +42,57 @@ CheckMaxSkews::CheckMaxSkews(StaState *sta) : CheckMaxSkews::~CheckMaxSkews() { - checks_.deleteContents(); } void CheckMaxSkews::clear() { - checks_.deleteContentsClear(); -} - -class MaxSkewChecksVisitor : public MaxSkewCheckVisitor -{ -public: - explicit MaxSkewChecksVisitor(MaxSkewCheckSeq &checks); - virtual void visit(MaxSkewCheck &check, - const StaState *sta); - -private: - MaxSkewCheckSeq &checks_; -}; - -MaxSkewChecksVisitor::MaxSkewChecksVisitor(MaxSkewCheckSeq &checks) : - MaxSkewCheckVisitor(), - checks_(checks) -{ -} - -void -MaxSkewChecksVisitor::visit(MaxSkewCheck &check, - const StaState *) -{ - checks_.push_back(new MaxSkewCheck(check)); -} - -class MaxSkewViolatorsVisititor : public MaxSkewCheckVisitor -{ -public: - explicit MaxSkewViolatorsVisititor(MaxSkewCheckSeq &checks); - virtual void visit(MaxSkewCheck &check, - const StaState *sta); - -private: - MaxSkewCheckSeq &checks_; -}; - -MaxSkewViolatorsVisititor:: -MaxSkewViolatorsVisititor(MaxSkewCheckSeq &checks) : - MaxSkewCheckVisitor(), - checks_(checks) -{ -} - -void -MaxSkewViolatorsVisititor::visit(MaxSkewCheck &check, - const StaState *sta) -{ - if (delayLess(check.slack(sta), 0.0, sta)) - checks_.push_back(new MaxSkewCheck(check)); + checks_.clear(); } MaxSkewCheckSeq & -CheckMaxSkews::violations() -{ - clear(); - MaxSkewViolatorsVisititor visitor(checks_); - visitMaxSkewChecks(&visitor); - sort(checks_, MaxSkewSlackLess(sta_)); - return checks_; -} - -class MaxSkewSlackVisitor : public MaxSkewCheckVisitor -{ -public: - MaxSkewSlackVisitor(); - virtual void visit(MaxSkewCheck &check, - const StaState *sta); - MaxSkewCheck *minSlackCheck(); - -private: - MaxSkewCheck *min_slack_check_; -}; - -MaxSkewSlackVisitor::MaxSkewSlackVisitor() : - MaxSkewCheckVisitor(), - min_slack_check_(nullptr) -{ -} - -void -MaxSkewSlackVisitor::visit(MaxSkewCheck &check, - const StaState *sta) -{ - MaxSkewSlackLess slack_less(sta); - if (min_slack_check_ == nullptr - || slack_less(&check, min_slack_check_)) { - delete min_slack_check_; - min_slack_check_ = new MaxSkewCheck(check); - } -} - -MaxSkewCheck * -MaxSkewSlackVisitor::minSlackCheck() -{ - return min_slack_check_; -} - -MaxSkewCheck * -CheckMaxSkews::minSlackCheck() +CheckMaxSkews::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes) { clear(); - MaxSkewSlackVisitor visitor; - visitMaxSkewChecks(&visitor); - MaxSkewCheck *check = visitor.minSlackCheck(); - // Save check for cleanup. - checks_.push_back(check); - return check; -} + scenes_ = Scene::sceneSet(scenes); -void -CheckMaxSkews::visitMaxSkewChecks(MaxSkewCheckVisitor *visitor) -{ Graph *graph = sta_->graph(); - VertexIterator vertex_iter(graph); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - visitMaxSkewChecks(vertex, visitor); + const Network *network = sta_->network(); + if (net) { + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + Vertex *vertex = graph->pinLoadVertex(pin); + check(vertex, violators); + } + delete pin_iter; + } + else { + VertexIterator vertex_iter(graph); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + check(vertex, violators); + } } + + // Sort checks by slack + sort(checks_, MaxSkewSlackLess(sta_)); + if (!violators && checks_.size() > max_count) + checks_.resize(max_count); + return checks_; } void -CheckMaxSkews:: visitMaxSkewChecks(Vertex *vertex, - MaxSkewCheckVisitor *visitor) +CheckMaxSkews::check(Vertex *vertex, + bool violators) { Graph *graph = sta_->graph(); Search *search = sta_->search(); const MinMax *clk_min_max = MinMax::max(); + MaxSkewCheck min_slack_check; VertexInEdgeIterator edge_iter(vertex, graph); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); @@ -195,35 +100,53 @@ CheckMaxSkews:: visitMaxSkewChecks(Vertex *vertex, Vertex *ref_vertex = edge->from(graph); TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { - const RiseFall *clk_rf = arc->fromEdge()->asRiseFall(); - const RiseFall *ref_rf = arc->toEdge()->asRiseFall(); - VertexPathIterator clk_path_iter(vertex, clk_rf, clk_min_max, search); - while (clk_path_iter.hasNext()) { - Path *clk_path = clk_path_iter.next(); - if (clk_path->isClock(search)) { - const PathAnalysisPt *clk_ap = clk_path->pathAnalysisPt(sta_); - PathAnalysisPt *ref_ap = clk_ap->tgtClkAnalysisPt(); - VertexPathIterator ref_path_iter(ref_vertex, ref_rf, ref_ap, sta_); - while (ref_path_iter.hasNext()) { - Path *ref_path = ref_path_iter.next(); - if (ref_path->isClock(search)) { - MaxSkewCheck check(clk_path, ref_path, arc, edge); - visitor->visit(check, sta_); - } - } - } - } + const RiseFall *clk_rf = arc->fromEdge()->asRiseFall(); + const RiseFall *ref_rf = arc->toEdge()->asRiseFall(); + VertexPathIterator clk_path_iter(vertex, clk_rf, clk_min_max, search); + while (clk_path_iter.hasNext()) { + Path *clk_path = clk_path_iter.next(); + if (clk_path->isClock(search)) { + const Scene *scene = clk_path->scene(sta_); + if (scenes_.contains(scene)) { + const MinMax *ref_min_max = clk_path->tgtClkMinMax(sta_); + VertexPathIterator ref_path_iter(ref_vertex, scene, ref_min_max, + ref_rf, sta_); + while (ref_path_iter.hasNext()) { + Path *ref_path = ref_path_iter.next(); + if (ref_path->isClock(search)) { + MaxSkewCheck skew_check(clk_path, ref_path, arc, edge); + Slack slack = skew_check.slack(sta_); + if ((min_slack_check.isNull() + || delayLess(slack, min_slack_check.slack(sta_), sta_)) + && (!violators || + delayLess(slack, 0.0, sta_))) + min_slack_check = skew_check; + } + } + } + } + } } } } + if (!min_slack_check.isNull()) + checks_.push_back(min_slack_check); } //////////////////////////////////////////////////////////////// +MaxSkewCheck::MaxSkewCheck() : + clk_path_(nullptr), + ref_path_(nullptr), + check_arc_(nullptr), + check_edge_(nullptr) +{ +} + MaxSkewCheck::MaxSkewCheck(Path *clk_path, - Path *ref_path, - TimingArc *check_arc, - Edge *check_edge) : + Path *ref_path, + TimingArc *check_arc, + Edge *check_edge) : clk_path_(clk_path), ref_path_(ref_path), check_arc_(check_arc), @@ -248,8 +171,10 @@ MaxSkewCheck::maxSkew(const StaState *sta) const { Search *search = sta->search(); return search->deratedDelay(ref_path_->vertex(sta), - check_arc_, check_edge_, false, - clk_path_->pathAnalysisPt(sta)); + check_arc_, check_edge_, false, + clk_path_->minMax(sta), + clk_path_->dcalcAnalysisPtIndex(sta), + ref_path_->scene(sta)->sdc()); } Delay @@ -272,15 +197,15 @@ MaxSkewSlackLess::MaxSkewSlackLess(const StaState *sta) : } bool -MaxSkewSlackLess::operator()(const MaxSkewCheck *check1, - const MaxSkewCheck *check2) const +MaxSkewSlackLess::operator()(const MaxSkewCheck &check1, + const MaxSkewCheck &check2) const { - Slack slack1 = check1->slack(sta_); - Slack slack2 = check2->slack(sta_); + Slack slack1 = check1.slack(sta_); + Slack slack2 = check2.slack(sta_); return delayLess(slack1, slack2, sta_) || (delayEqual(slack1, slack2) - // Break ties based on constrained pin names. - && sta_->network()->pinLess(check1->clkPin(sta_),check2->clkPin(sta_))); + // Break ties based on constrained pin names. + && sta_->network()->pinLess(check1.clkPin(sta_), check2.clkPin(sta_))); } } // namespace diff --git a/search/CheckMaxSkews.hh b/search/CheckMaxSkews.hh index 4f40ffbbc..0c7a58829 100644 --- a/search/CheckMaxSkews.hh +++ b/search/CheckMaxSkews.hh @@ -24,43 +24,26 @@ #pragma once +#include + #include "GraphClass.hh" #include "Delay.hh" #include "StaState.hh" #include "SearchClass.hh" #include "Path.hh" +#include "MinMax.hh" namespace sta { -class MaxSkewCheckVisitor; - -class CheckMaxSkews -{ -public: - explicit CheckMaxSkews(StaState *sta); - ~CheckMaxSkews(); - void clear(); - // All violating max skew checks. - MaxSkewCheckSeq &violations(); - // Max skew check with the least slack. - MaxSkewCheck *minSlackCheck(); - -protected: - void visitMaxSkewChecks(MaxSkewCheckVisitor *visitor); - void visitMaxSkewChecks(Vertex *vertex, - MaxSkewCheckVisitor *visitor); - - MaxSkewCheckSeq checks_; - StaState *sta_; -}; - class MaxSkewCheck { public: + MaxSkewCheck(); MaxSkewCheck(Path *clk_path, - Path *ref_path, - TimingArc *check_arc, - Edge *check_edge); + Path *ref_path, + TimingArc *check_arc, + Edge *check_edge); + bool isNull() const { return clk_path_ == nullptr; } const Path *clkPath() const { return clk_path_; } Pin *clkPin(const StaState *sta) const; const Path *refPath() const { return ref_path_; } @@ -77,12 +60,36 @@ private: Edge *check_edge_; }; +using MaxSkewCheckSeq = std::vector; + +class CheckMaxSkews +{ +public: + CheckMaxSkews(StaState *sta); + ~CheckMaxSkews(); + void clear(); + // Return max skew checks. + // net=null check all nets + MaxSkewCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes); + +protected: + void check(Vertex *vertex, + bool violators); + + SceneSet scenes_; + MaxSkewCheckSeq checks_; + StaState *sta_; +}; + class MaxSkewSlackLess { public: - explicit MaxSkewSlackLess(const StaState *sta); - bool operator()(const MaxSkewCheck *check1, - const MaxSkewCheck *check2) const; + MaxSkewSlackLess(const StaState *sta); + bool operator()(const MaxSkewCheck &check1, + const MaxSkewCheck &check2) const; protected: const StaState *sta_; diff --git a/search/CheckMinPeriods.cc b/search/CheckMinPeriods.cc index 063583823..7008d4138 100644 --- a/search/CheckMinPeriods.cc +++ b/search/CheckMinPeriods.cc @@ -29,184 +29,134 @@ #include "Sdc.hh" #include "Clock.hh" #include "Graph.hh" -#include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" #include "Search.hh" namespace sta { -// Abstract base class. -class MinPeriodCheckVisitor -{ -public: - MinPeriodCheckVisitor(const Corner *corner); - virtual ~MinPeriodCheckVisitor() {} - virtual void visit(MinPeriodCheck &check, - StaState *sta) = 0; - const Corner *corner() { return corner_; } - -protected: - const Corner *corner_; -}; - -MinPeriodCheckVisitor::MinPeriodCheckVisitor(const Corner *corner) : - corner_(corner) -{ -} - CheckMinPeriods::CheckMinPeriods(StaState *sta) : + heap_(0, MinPeriodSlackLess(sta)), sta_(sta) { } -CheckMinPeriods::~CheckMinPeriods() -{ - checks_.deleteContents(); -} - void CheckMinPeriods::clear() { - checks_.deleteContentsClear(); + checks_.clear(); + heap_.clear(); } -class MinPeriodViolatorsVisitor : public MinPeriodCheckVisitor -{ -public: - MinPeriodViolatorsVisitor(const Corner *corner, - MinPeriodCheckSeq &checks); - virtual void visit(MinPeriodCheck &check, - StaState *sta); - -private: - MinPeriodCheckSeq &checks_; -}; - -MinPeriodViolatorsVisitor::MinPeriodViolatorsVisitor(const Corner *corner, - MinPeriodCheckSeq &checks): - MinPeriodCheckVisitor(corner), - checks_(checks) +MinPeriodCheckSeq & +CheckMinPeriods::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes) { + clear(); + if (!violators) + heap_.setMaxSize(max_count); + + if (net) + checkNet(net, violators, scenes); + else + checkAll(violators, scenes); + + if (violators) + sort(checks_, MinPeriodSlackLess(sta_)); + else + checks_ = heap_.extract(); + return checks_; } void -MinPeriodViolatorsVisitor::visit(MinPeriodCheck &check, - StaState *sta) -{ - if (delayLess(check.slack(sta), 0.0, sta)) - checks_.push_back(check.copy()); -} - -MinPeriodCheckSeq & -CheckMinPeriods::violations(const Corner *corner) +CheckMinPeriods::checkNet(const Net *net, + bool violators, + const SceneSeq &scenes) { - clear(); - MinPeriodViolatorsVisitor visitor(corner, checks_); - visitMinPeriodChecks(&visitor); - sort(checks_, MinPeriodSlackLess(sta_)); - return checks_; + Graph *graph = sta_->graph(); + NetPinIterator *pin_iter = sta_->network()->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + Vertex *vertex = graph->pinLoadVertex(pin); + checkVertex(vertex, violators, scenes); + } + delete pin_iter; } void -CheckMinPeriods::visitMinPeriodChecks(MinPeriodCheckVisitor *visitor) +CheckMinPeriods::checkAll(bool violators, + const SceneSeq &scenes) { Graph *graph = sta_->graph(); VertexIterator vertex_iter(graph); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (isClkEnd(vertex, graph)) - visitMinPeriodChecks(vertex, visitor); + checkVertex(vertex, violators, scenes); } } void -CheckMinPeriods::visitMinPeriodChecks(Vertex *vertex, - MinPeriodCheckVisitor *visitor) -{ - Search *search = sta_->search(); - GraphDelayCalc *graph_dcalc = sta_->graphDelayCalc(); - const Corner *corner = visitor->corner(); - Pin *pin = vertex->pin(); - float min_period; - bool exists; - graph_dcalc->minPeriod(pin, corner, min_period, exists); - if (exists) { - const ClockSet clks = search->clocks(vertex); - ClockSet::ConstIterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); - MinPeriodCheck check(pin, clk, corner); - visitor->visit(check, sta_); +CheckMinPeriods::checkVertex(Vertex *vertex, + bool violators, + const SceneSeq &scenes) +{ + MinPeriodCheck min_check = check(vertex, scenes); + if (!min_check.isNull()) { + if (violators) { + if (delayLess(min_check.slack(sta_), 0.0, sta_)) + checks_.push_back(min_check); } + else + heap_.insert(min_check); } } -//////////////////////////////////////////////////////////////// - -class MinPeriodSlackVisitor : public MinPeriodCheckVisitor -{ -public: - MinPeriodSlackVisitor(const Corner *corner); - void visit(MinPeriodCheck &check, - StaState *sta) override; - MinPeriodCheck *minSlackCheck(); - -private: - MinPeriodCheck *min_slack_check_; -}; - -MinPeriodSlackVisitor::MinPeriodSlackVisitor(const Corner *corner) : - MinPeriodCheckVisitor(corner), - min_slack_check_(nullptr) -{ -} - -void -MinPeriodSlackVisitor::visit(MinPeriodCheck &check, - StaState *sta) +MinPeriodCheck +CheckMinPeriods::check(Vertex *vertex, + const SceneSeq &scenes) { - MinPeriodSlackLess slack_less(sta); - if (min_slack_check_ == nullptr) - min_slack_check_ = check.copy(); - else if (slack_less(&check, min_slack_check_)) { - delete min_slack_check_; - min_slack_check_ = check.copy(); + Search *search = sta_->search(); + GraphDelayCalc *graph_dcalc = sta_->graphDelayCalc(); + MinPeriodCheck min_slack_check; + Pin *pin = vertex->pin(); + for (const Scene *scene : scenes) { + const Mode *mode = scene->mode(); + if (isClkEnd(vertex, mode)) { + float min_period; + bool exists; + graph_dcalc->minPeriod(pin, scene, min_period, exists); + if (exists) { + const ClockSet clks = search->clocks(vertex, mode); + for (Clock *clk : clks) { + MinPeriodCheck check(pin, clk, scene); + Slack slack = check.slack(sta_); + if (min_slack_check.isNull() + || delayLess(slack, min_slack_check.slack(sta_), sta_)) + min_slack_check = check; + } + } + } } -} - -MinPeriodCheck * -MinPeriodSlackVisitor::minSlackCheck() -{ - return min_slack_check_; -} - -MinPeriodCheck * -CheckMinPeriods::minSlackCheck(const Corner *corner) -{ - clear(); - MinPeriodSlackVisitor visitor(corner); - visitMinPeriodChecks(&visitor); - MinPeriodCheck *check = visitor.minSlackCheck(); - // Save check for cleanup. - checks_.push_back(check); - return check; + return min_slack_check; } //////////////////////////////////////////////////////////////// MinPeriodCheck::MinPeriodCheck(Pin *pin, - Clock *clk, - const Corner *corner) : + Clock *clk, + const Scene *scene) : pin_(pin), clk_(clk), - corner_(corner) + scene_(scene) { } -MinPeriodCheck * -MinPeriodCheck::copy() +MinPeriodCheck::MinPeriodCheck() : + pin_(nullptr), + clk_(nullptr), + scene_(nullptr) { - return new MinPeriodCheck(pin_, clk_, corner_); } float @@ -221,7 +171,7 @@ MinPeriodCheck::minPeriod(const StaState *sta) const GraphDelayCalc *graph_dcalc = sta->graphDelayCalc(); float min_period; bool exists; - graph_dcalc->minPeriod(pin_, corner_, min_period, exists); + graph_dcalc->minPeriod(pin_, scene_, min_period, exists); return min_period; } @@ -233,26 +183,26 @@ MinPeriodCheck::slack(const StaState *sta) const //////////////////////////////////////////////////////////////// -MinPeriodSlackLess::MinPeriodSlackLess(StaState *sta) : +MinPeriodSlackLess::MinPeriodSlackLess(const StaState *sta) : sta_(sta) { } bool -MinPeriodSlackLess::operator()(const MinPeriodCheck *check1, - const MinPeriodCheck *check2) const +MinPeriodSlackLess::operator()(const MinPeriodCheck &check1, + const MinPeriodCheck &check2) const { - Slack slack1 = check1->slack(sta_); - Slack slack2 = check2->slack(sta_); - const Pin *pin1 = check1->pin(); - const Pin *pin2 = check2->pin(); + Slack slack1 = check1.slack(sta_); + Slack slack2 = check2.slack(sta_); + const Pin *pin1 = check1.pin(); + const Pin *pin2 = check2.pin(); return delayLess(slack1, slack2, sta_) // Break ties based on pin and clock names. || (delayEqual(slack1, slack2) - && (sta_->network()->pinLess(pin1, pin2) - || (pin1 == pin2 - && ClockNameLess()(check1->clk(), - check2->clk())))); + && (sta_->network()->pinLess(pin1, pin2) + || (pin1 == pin2 + && ClockNameLess()(check1.clk(), + check2.clk())))); } } // namespace diff --git a/search/CheckMinPeriods.hh b/search/CheckMinPeriods.hh index bcba3218b..a8e0b918e 100644 --- a/search/CheckMinPeriods.hh +++ b/search/CheckMinPeriods.hh @@ -30,37 +30,31 @@ #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" +#include "BoundedHeap.hh" namespace sta { -class MinPeriodCheckVisitor; - -class CheckMinPeriods +class MinPeriodSlackLess { public: - CheckMinPeriods(StaState *sta); - ~CheckMinPeriods(); - void clear(); - MinPeriodCheckSeq &violations(const Corner *corner); - // Min period check with the least slack. - MinPeriodCheck *minSlackCheck(const Corner *corner); - -protected: - void visitMinPeriodChecks(MinPeriodCheckVisitor *visitor); - void visitMinPeriodChecks(Vertex *vertex, - MinPeriodCheckVisitor *visitor); + MinPeriodSlackLess(const StaState *sta); + bool operator()(const MinPeriodCheck &check1, + const MinPeriodCheck &check2) const; - MinPeriodCheckSeq checks_; - StaState *sta_; +private: + const StaState *sta_; }; +using MinPeriodHeap = BoundedHeap; + class MinPeriodCheck { public: + MinPeriodCheck(); MinPeriodCheck(Pin *pin, - Clock *clk, - const Corner *corner); - MinPeriodCheck *copy(); + Clock *clk, + const Scene *scene); + bool isNull() { return pin_ == nullptr; } Pin *pin() const { return pin_; } Clock *clk() const { return clk_; } float period() const; @@ -70,18 +64,36 @@ public: private: Pin *pin_; Clock *clk_; - const Corner *corner_; + const Scene *scene_; }; -class MinPeriodSlackLess +using MinPeriodCheckSeq = std::vector; + +class CheckMinPeriods { public: - MinPeriodSlackLess(StaState *sta); - bool operator()(const MinPeriodCheck *check1, - const MinPeriodCheck *check2) const; + CheckMinPeriods(StaState *sta); + void clear(); + MinPeriodCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes); -private: - const StaState *sta_; +protected: + void checkNet(const Net *net, + bool violators, + const SceneSeq &scenes); + void checkAll(bool violators, + const SceneSeq &scenes); + void checkVertex(Vertex *vertex, + bool violators, + const SceneSeq &scenes); + MinPeriodCheck check(Vertex *vertex, + const SceneSeq &scenes); + + MinPeriodCheckSeq checks_; + MinPeriodHeap heap_; + StaState *sta_; }; } // namespace diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index 395327ea4..b13de4e35 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -24,6 +24,7 @@ #include "CheckMinPulseWidths.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" #include "TimingRole.hh" #include "Liberty.hh" @@ -31,13 +32,11 @@ #include "Graph.hh" #include "Clock.hh" #include "Sdc.hh" -#include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" #include "ClkInfo.hh" #include "Tag.hh" #include "Path.hh" -#include "Corner.hh" -#include "PathAnalysisPt.hh" +#include "Scene.hh" #include "SearchPred.hh" #include "PathEnd.hh" #include "Search.hh" @@ -47,238 +46,111 @@ namespace sta { static void minPulseWidth(const Path *path, - const StaState *sta, - // Return values. - float &min_width, - bool &exists); - -// Abstract base class. -class MinPulseWidthCheckVisitor -{ -public: - MinPulseWidthCheckVisitor() {} - virtual ~MinPulseWidthCheckVisitor() {} - virtual void visit(MinPulseWidthCheck &check, - const StaState *sta) = 0; -}; + const StaState *sta, + // Return values. + float &min_width, + bool &exists); CheckMinPulseWidths::CheckMinPulseWidths(StaState *sta) : + heap_(0, MinPulseWidthSlackLess(sta)), sta_(sta) { } -CheckMinPulseWidths::~CheckMinPulseWidths() -{ - checks_.deleteContents(); -} - void CheckMinPulseWidths::clear() { - checks_.deleteContentsClear(); -} - -//////////////////////////////////////////////////////////////// - -class MinPulseWidthChecksVisitor : public MinPulseWidthCheckVisitor -{ -public: - explicit MinPulseWidthChecksVisitor(const Corner *corner, - MinPulseWidthCheckSeq &checks); - virtual void visit(MinPulseWidthCheck &check, - const StaState *sta); - -private: - const Corner *corner_; - MinPulseWidthCheckSeq &checks_; -}; - -MinPulseWidthChecksVisitor:: -MinPulseWidthChecksVisitor(const Corner *corner, - MinPulseWidthCheckSeq &checks) : - corner_(corner), - checks_(checks) -{ -} - -void -MinPulseWidthChecksVisitor::visit(MinPulseWidthCheck &check, - const StaState *sta) -{ - if (corner_ == nullptr - || check.corner(sta) == corner_) { - MinPulseWidthCheck *copy = new MinPulseWidthCheck(check.openPath()); - checks_.push_back(copy); - } + checks_.clear(); + heap_.clear(); } MinPulseWidthCheckSeq & -CheckMinPulseWidths::check(const Corner *corner) +CheckMinPulseWidths::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes) { clear(); - MinPulseWidthChecksVisitor visitor(corner, checks_); - visitMinPulseWidthChecks(&visitor); - sort(checks_, MinPulseWidthSlackLess(sta_)); - return checks_; -} + if (!violators) + heap_.setMaxSize(max_count); -MinPulseWidthCheckSeq & -CheckMinPulseWidths::check(PinSeq *pins, - const Corner *corner) -{ - clear(); - Graph *graph = sta_->graph(); - MinPulseWidthChecksVisitor visitor(corner, checks_); - PinSeq::Iterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - Vertex *vertex = graph->pinLoadVertex(pin); - visitMinPulseWidthChecks(vertex, &visitor); - } - sort(checks_, MinPulseWidthSlackLess(sta_)); - return checks_; -} - -//////////////////////////////////////////////////////////////// - -class MinPulseWidthViolatorsVisitor : public MinPulseWidthCheckVisitor -{ -public: - explicit MinPulseWidthViolatorsVisitor(const Corner *corner, - MinPulseWidthCheckSeq &checks); - virtual void visit(MinPulseWidthCheck &check, - const StaState *sta); - -private: - const Corner *corner_; - MinPulseWidthCheckSeq &checks_; -}; - -MinPulseWidthViolatorsVisitor:: -MinPulseWidthViolatorsVisitor(const Corner *corner, - MinPulseWidthCheckSeq &checks) : - corner_(corner), - checks_(checks) -{ -} - -void -MinPulseWidthViolatorsVisitor::visit(MinPulseWidthCheck &check, - const StaState *sta) -{ - if (delayLess(check.slack(sta), 0.0, sta) - && (corner_ == nullptr - || check.corner(sta) == corner_)) { - MinPulseWidthCheck *copy = new MinPulseWidthCheck(check.openPath()); - checks_.push_back(copy); - } -} + if (net) + checkNet(net, violators, scenes); + else + checkAll(violators, scenes); -MinPulseWidthCheckSeq & -CheckMinPulseWidths::violations(const Corner *corner) -{ - clear(); - MinPulseWidthViolatorsVisitor visitor(corner, checks_); - visitMinPulseWidthChecks(&visitor); - sort(checks_, MinPulseWidthSlackLess(sta_)); + if (violators) + sort(checks_, MinPulseWidthSlackLess(sta_)); + else + checks_ = heap_.extract(); return checks_; } -//////////////////////////////////////////////////////////////// - -class MinPulseWidthSlackVisitor : public MinPulseWidthCheckVisitor -{ -public: - MinPulseWidthSlackVisitor(const Corner *corner); - virtual void visit(MinPulseWidthCheck &check, - const StaState *sta); - MinPulseWidthCheck *minSlackCheck(); - -private: - const Corner *corner_; - MinPulseWidthCheck *min_slack_check_; -}; - -MinPulseWidthSlackVisitor::MinPulseWidthSlackVisitor(const Corner *corner) : - corner_(corner), - min_slack_check_(nullptr) -{ -} - void -MinPulseWidthSlackVisitor::visit(MinPulseWidthCheck &check, - const StaState *sta) +CheckMinPulseWidths::checkNet(const Net *net, + bool violators, + const SceneSeq &scenes) { - MinPulseWidthSlackLess slack_less(sta); - if (corner_ == nullptr - || check.corner(sta) == corner_) { - if (min_slack_check_ == nullptr) - min_slack_check_ = check.copy(); - else if (slack_less(&check, min_slack_check_)) { - delete min_slack_check_; - min_slack_check_ = check.copy(); - } + Graph *graph = sta_->graph(); + NetPinIterator *pin_iter = sta_->network()->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + Vertex *vertex = graph->pinLoadVertex(pin); + checkVertex(vertex, violators, scenes); } -} - -MinPulseWidthCheck * -MinPulseWidthSlackVisitor::minSlackCheck() -{ - return min_slack_check_; -} - -MinPulseWidthCheck * -CheckMinPulseWidths::minSlackCheck(const Corner *corner) -{ - clear(); - MinPulseWidthSlackVisitor visitor(corner); - visitMinPulseWidthChecks(&visitor); - MinPulseWidthCheck *check = visitor.minSlackCheck(); - // Save check for cleanup. - checks_.push_back(check); - return check; + delete pin_iter; } void -CheckMinPulseWidths:: -visitMinPulseWidthChecks(MinPulseWidthCheckVisitor *visitor) +CheckMinPulseWidths::checkAll(bool violators, + const SceneSeq &scenes) { Graph *graph = sta_->graph(); - Debug *debug = sta_->debug(); VertexIterator vertex_iter(graph); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (isClkEnd(vertex, graph)) { - debugPrint(debug, "mpw", 1, "check mpw %s", - vertex->to_string(sta_).c_str()); - visitMinPulseWidthChecks(vertex, visitor); - } + checkVertex(vertex, violators, scenes); } } void -CheckMinPulseWidths:: -visitMinPulseWidthChecks(Vertex *vertex, - MinPulseWidthCheckVisitor *visitor) +CheckMinPulseWidths::checkVertex(Vertex *vertex, + bool violators, + const SceneSeq &scenes) { Search *search = sta_->search(); + Debug *debug = sta_->debug(); const MinMax *min_max = MinMax::max(); + SceneSet scene_set = Scene::sceneSet(scenes); VertexPathIterator path_iter(vertex, search); while (path_iter.hasNext()) { Path *path = path_iter.next(); - if (path->isClock(search) - && !path->tag(sta_)->clkInfo()->isGenClkSrcPath()) { - if (path->minMax(sta_) == min_max) { - float min_width; - bool exists; - minPulseWidth(path, sta_, min_width, exists); - if (exists) { - MinPulseWidthCheck check(path); - Path *close_path = check.closePath(sta_); - // Don't bother visiting if nobody is home. - if (close_path) - visitor->visit(check, sta_); - } + Vertex *path_vertex = path->vertex(sta_); + const Mode *mode = path->mode(sta_); + if (isClkEnd(path_vertex, mode) + && path->isClock(search) + && !path->tag(sta_)->clkInfo()->isGenClkSrcPath() + && scene_set.find(path->scene(sta_)) != scene_set.end() + && path->minMax(sta_) == min_max) { + float min_width; + bool exists; + minPulseWidth(path, sta_, min_width, exists); + if (exists) { + MinPulseWidthCheck check(path); + 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(), + path->transition(sta_) == RiseFall::rise() ? "(high)" : "(low)", + delayAsString(check.slack(sta_), sta_)); + if (violators) { + if (delayLess(check.slack(sta_), 0.0, sta_)) + checks_.push_back(check); + } + else + heap_.insert(check); + } } } } @@ -296,10 +168,13 @@ MinPulseWidthCheck::MinPulseWidthCheck(Path *open_path) : { } -MinPulseWidthCheck * -MinPulseWidthCheck::copy() +std::string +MinPulseWidthCheck::to_string(const StaState *sta) { - return new MinPulseWidthCheck(open_path_); + std::string result = sta->network()->pathName(pin(sta)); + result += " "; + result += (openTransition(sta) == RiseFall::rise()) ? "(high)" : "(low)"; + return result; } Pin * @@ -317,37 +192,39 @@ MinPulseWidthCheck::openTransition(const StaState *sta) const Path * MinPulseWidthCheck::closePath(const StaState *sta) const { - PathAnalysisPt *open_ap = open_path_->pathAnalysisPt(sta); - PathAnalysisPt *close_ap = open_ap->tgtClkAnalysisPt(); + Scene *scene = open_path_->scene(sta); + const MinMax *close_min_max = open_path_->tgtClkMinMax(sta); const RiseFall *open_rf = open_path_->transition(sta); const RiseFall *close_rf = open_rf->opposite(); Tag *open_tag = open_path_->tag(sta); const ClkInfo *open_clk_info = open_tag->clkInfo(); - const ClkInfo close_clk_info(open_clk_info->clkEdge()->opposite(), - open_clk_info->clkSrc(), - open_clk_info->isPropagated(), - open_clk_info->genClkSrc(), - open_clk_info->isGenClkSrcPath(), - open_clk_info->pulseClkSense(), - delay_zero, 0.0, nullptr, - open_clk_info->pathAPIndex(), - open_clk_info->crprClkPath(sta), - sta); - Tag close_tag(0, - close_rf->index(), - close_ap->index(), - &close_clk_info, - open_tag->isClock(), - open_tag->inputDelay(), - open_tag->isSegmentStart(), - open_tag->states(), - false, sta); + const ClkInfo close_clk_info(scene, + open_clk_info->clkEdge()->opposite(), + open_clk_info->clkSrc(), + open_clk_info->isPropagated(), + open_clk_info->genClkSrc(), + open_clk_info->isGenClkSrcPath(), + open_clk_info->pulseClkSense(), + delay_zero, 0.0, nullptr, + open_clk_info->minMax(), + open_clk_info->crprClkPath(sta), + sta); + Tag close_tag(scene, + 0, + close_rf, + close_min_max, + &close_clk_info, + open_tag->isClock(), + open_tag->inputDelay(), + 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()); - VertexPathIterator close_iter(open_path_->vertex(sta), close_rf, - close_ap, 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)) { @@ -434,27 +311,26 @@ MinPulseWidthCheck::minWidth(const StaState *sta) const // min_pulse_width timing group static void minPulseWidth(const Path *path, - const StaState *sta, - // Return values. - float &min_width, - bool &exists) + const StaState *sta, + // Return values. + float &min_width, + bool &exists) { Pin *pin = path->pin(sta); const Clock *clk = path->clock(sta); const RiseFall *rf = path->transition(sta); - Sdc *sdc = sta->sdc(); + const Sdc *sdc = path->sdc(sta); // set_min_pulse_width command. sdc->minPulseWidth(pin, clk, rf, min_width, exists); if (!exists) { - const PathAnalysisPt *path_ap = path->pathAnalysisPt(sta); - const DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt(); + DcalcAPIndex dcalc_ap = path->dcalcAnalysisPtIndex(sta); Vertex *vertex = path->vertex(sta); Graph *graph = sta->graph(); Edge *edge; TimingArc *arc; graph->minPulseWidthArc(vertex, rf, edge, arc); if (edge) { - min_width = delayAsFloat(graph->arcDelay(edge, arc, dcalc_ap->index())); + min_width = delayAsFloat(graph->arcDelay(edge, arc, dcalc_ap)); exists = true; } } @@ -477,10 +353,10 @@ MinPulseWidthCheck::slack(const StaState *sta) const return width(sta) - minWidth(sta); } -Corner * -MinPulseWidthCheck::corner(const StaState *sta) const +Scene * +MinPulseWidthCheck::scene(const StaState *sta) const { - return open_path_->pathAnalysisPt(sta)->corner(); + return open_path_->scene(sta); } //////////////////////////////////////////////////////////////// @@ -491,20 +367,20 @@ MinPulseWidthSlackLess::MinPulseWidthSlackLess(const StaState *sta) : } bool -MinPulseWidthSlackLess::operator()(const MinPulseWidthCheck *check1, - const MinPulseWidthCheck *check2) const +MinPulseWidthSlackLess::operator()(const MinPulseWidthCheck &check1, + const MinPulseWidthCheck &check2) const { - Slack slack1 = check1->slack(sta_); - Slack slack2 = check2->slack(sta_); - const Pin *pin1 = check1->pin(sta_); - const Pin *pin2 = check2->pin(sta_); + Slack slack1 = check1.slack(sta_); + Slack slack2 = check2.slack(sta_); + const Pin *pin1 = check1.pin(sta_); + const Pin *pin2 = check2.pin(sta_); return delayLess(slack1, slack2, sta_) || (delayEqual(slack1, slack2) - // Break ties for the sake of regression stability. - && (sta_->network()->pinLess(pin1, pin2) - || (pin1 == pin2 - && check1->openPath()->rfIndex(sta_) - < check2->openPath()->rfIndex(sta_)))); + // Break ties for the sake of regression stability. + && (sta_->network()->pinLess(pin1, pin2) + || (pin1 == pin2 + && check1.openPath()->rfIndex(sta_) + < check2.openPath()->rfIndex(sta_)))); } } // namespace diff --git a/search/CheckMinPulseWidths.hh b/search/CheckMinPulseWidths.hh index d250cc203..0acda2737 100644 --- a/search/CheckMinPulseWidths.hh +++ b/search/CheckMinPulseWidths.hh @@ -24,59 +24,45 @@ #pragma once +#include +#include +#include + #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" #include "Path.hh" +#include "BoundedHeap.hh" namespace sta { class RiseFall; -class MinPulseWidthCheck; -class MinPulseWidthCheckVisitor; -class CheckMinPulseWidths +class MinPulseWidthSlackLess { public: - CheckMinPulseWidths(StaState *sta); - ~CheckMinPulseWidths(); - void clear(); - // Min pulse width checks for pins. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &check(PinSeq *pins, - const Corner *corner); - // All min pulse width checks. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &check(const Corner *corner); - // All violating min pulse width checks. - // corner=nullptr checks all corners. - MinPulseWidthCheckSeq &violations(const Corner *corner); - // Min pulse width check with the least slack. - // corner=nullptr checks all corners. - MinPulseWidthCheck *minSlackCheck(const Corner *corner); - -protected: - void visitMinPulseWidthChecks(MinPulseWidthCheckVisitor *visitor); - void visitMinPulseWidthChecks(Vertex *vertex, - MinPulseWidthCheckVisitor *visitor); + MinPulseWidthSlackLess(const StaState *sta); + bool operator()(const MinPulseWidthCheck &check1, + const MinPulseWidthCheck &check2) const; - MinPulseWidthCheckSeq checks_; - StaState *sta_; +private: + const StaState *sta_; }; class MinPulseWidthCheck { public: - explicit MinPulseWidthCheck(); + MinPulseWidthCheck(); MinPulseWidthCheck(Path *open_path); - MinPulseWidthCheck *copy(); + std::string to_string(const StaState *sta); + bool isNull() const { return open_path_ == nullptr; } Pin *pin(const StaState *sta) const; const RiseFall *openTransition(const StaState *sta) const; Arrival width(const StaState *sta) const; float minWidth(const StaState *sta) const; Slack slack(const StaState *sta) const; Path *openPath() { return open_path_; } - Corner *corner(const StaState *sta) const; + Scene *scene(const StaState *sta) const; const Path *openPath() const { return open_path_; } Arrival openArrival(const StaState *sta) const; Path *closePath(const StaState *sta) const; @@ -93,15 +79,32 @@ protected: Path *open_path_; }; -class MinPulseWidthSlackLess +using MinPulseWidthCheckSeq = std::vector; +using MinPulseWidthCheckHeap = BoundedHeap; + +class CheckMinPulseWidths { public: - MinPulseWidthSlackLess(const StaState *sta); - bool operator()(const MinPulseWidthCheck *check1, - const MinPulseWidthCheck *check2) const; + CheckMinPulseWidths(StaState *sta); + void clear(); + MinPulseWidthCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes); -private: - const StaState *sta_; +protected: + void checkNet(const Net *net, + bool violators, + const SceneSeq &scenes); + void checkAll(bool violators, + const SceneSeq &scenes); + void checkVertex(Vertex *vertex, + bool violators, + const SceneSeq &scenes); + + MinPulseWidthCheckSeq checks_; + MinPulseWidthCheckHeap heap_; + StaState *sta_; }; } // namespace diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc deleted file mode 100644 index 2cc392ca2..000000000 --- a/search/CheckSlewLimits.cc +++ /dev/null @@ -1,422 +0,0 @@ -// 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 "CheckSlewLimits.hh" - -#include "Fuzzy.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" -#include "InputDrive.hh" -#include "Graph.hh" -#include "DcalcAnalysisPt.hh" -#include "GraphDelayCalc.hh" -#include "StaState.hh" -#include "Corner.hh" -#include "Path.hh" -#include "PortDirection.hh" -#include "Search.hh" -#include "ClkNetwork.hh" - -namespace sta { - -class PinSlewLimitSlackLess -{ -public: - PinSlewLimitSlackLess(const Corner *corner, - const MinMax *min_max, - CheckSlewLimits *check_slew_limit, - const StaState *sta); - bool operator()(const Pin *pin1, - const Pin *pin2) const; - -private: - const Corner *corner_; - const MinMax *min_max_; - CheckSlewLimits *check_slew_limit_; - const StaState *sta_; - -}; - -PinSlewLimitSlackLess::PinSlewLimitSlackLess(const Corner *corner, - const MinMax *min_max, - CheckSlewLimits *check_slew_limit, - const StaState *sta) : - corner_(corner), - min_max_(min_max), - check_slew_limit_(check_slew_limit), - sta_(sta) -{ -} - -bool -PinSlewLimitSlackLess::operator()(const Pin *pin1, - const Pin *pin2) const -{ - const Corner *corner1, *corner2; - const RiseFall *rf1, *rf2; - Slew slew1, slew2; - float limit1, limit2, slack1, slack2; - check_slew_limit_->checkSlew(pin1, corner_, min_max_, true, - corner1, rf1, slew1, limit1, slack1); - check_slew_limit_->checkSlew(pin2, corner_, min_max_, true, - corner2, rf2, slew2, limit2, slack2); - return fuzzyLess(slack1, slack2) - || (fuzzyEqual(slack1, slack2) - // Break ties for the sake of regression stability. - && sta_->network()->pinLess(pin1, pin2)); -} - -//////////////////////////////////////////////////////////////// - -CheckSlewLimits::CheckSlewLimits(const StaState *sta) : - sta_(sta) -{ -} - -void -CheckSlewLimits::checkSlewLimits(const Pin *pin, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &slew_pins, - float &min_slack) -{ - const Corner *corner1; - const RiseFall *rf; - Slew slew; - float limit, slack; - checkSlew(pin, corner, min_max, true, corner1, rf, slew, limit, slack); - if (!fuzzyInf(slack)) { - if (violators) { - if (slack < 0.0) - slew_pins.push_back(pin); - } - else { - if (slew_pins.empty() - || slack < min_slack) { - slew_pins.push_back(pin); - min_slack = slack; - } - } - } -} - -void -CheckSlewLimits::checkSlew(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const -{ - corner1 = nullptr; - rf1 = nullptr; - slew1 = 0.0; - limit1 = 0.0; - slack1 = MinMax::min()->initValue(); - - Vertex *vertex, *bidirect_drvr_vertex; - sta_->graph()->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - checkSlew1(pin, vertex, corner, min_max, check_clks, - corner1, rf1, slew1, limit1, slack1); - if (bidirect_drvr_vertex) - checkSlew1(pin, bidirect_drvr_vertex, corner, min_max, check_clks, - corner1, rf1, slew1, limit1, slack1); -} - -void -CheckSlewLimits::checkSlew1(const Pin *pin, - const Vertex *vertex, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const -{ - if (!vertex->isDisabledConstraint() - && !vertex->isConstant() - && !sta_->clkNetwork()->isIdealClock(pin)) { - ClockSet clks; - if (check_clks) - clks = clockDomains(vertex); - if (corner) - checkSlew2(pin, vertex, corner, min_max, clks, - corner1, rf1, slew1, limit1, slack1); - else { - for (auto corner : *sta_->corners()) { - checkSlew2(pin, vertex, corner, min_max, clks, - corner1, rf1, slew1, limit1, slack1); - } - } - } -} - -void -CheckSlewLimits::checkSlew2(const Pin *pin, - const Vertex *vertex, - const Corner *corner, - const MinMax *min_max, - const ClockSet &clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const -{ - for (const RiseFall *rf : RiseFall::range()) { - float limit; - bool exists; - findLimit(pin, corner, rf, min_max, clks, - limit, exists); - if (exists) { - checkSlew3(vertex, corner, rf, min_max, limit, - corner1, rf1, slew1, slack1, limit1); - } - } -} - -void -CheckSlewLimits::checkSlew3(const Vertex *vertex, - const Corner *corner, - const RiseFall *rf, - const MinMax *min_max, - float limit, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &slack1, - float &limit1) const -{ - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - Slew slew = sta_->graph()->slew(vertex, rf, dcalc_ap->index()); - float slew2 = delayAsFloat(slew); - float slack = (min_max == MinMax::max()) - ? limit - slew2 : slew2 - limit; - if (corner1 == nullptr - || (slack < slack1 - // Break ties for the sake of regression stability. - || (fuzzyEqual(slack, slack1) - && rf->index() < rf1->index()))) { - corner1 = corner; - rf1 = rf; - slew1 = slew; - slack1 = slack; - limit1 = limit; - } -} - -// Return the tightest limit. -void -CheckSlewLimits::findLimit(const Pin *pin, - const Corner *corner, - const RiseFall *rf, - const MinMax *min_max, - const ClockSet &clks, - // Return values. - float &limit, - bool &exists) const -{ - const Network *network = sta_->network(); - Sdc *sdc = sta_->sdc(); - LibertyPort *port = network->libertyPort(pin); - findLimit(port, corner, min_max, - limit, exists); - - float limit1; - bool exists1; - if (!clks.empty()) { - // Look for clock slew limits. - bool is_clk = sta_->clkNetwork()->isIdealClock(pin); - for (Clock *clk : clks) { - PathClkOrData clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; - sdc->slewLimit(clk, rf, clk_data, min_max, - limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } - - if (network->isTopLevelPort(pin)) { - Port *port = network->port(pin); - sdc->slewLimit(port, min_max, limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - InputDrive *drive = sdc->findInputDrive(port); - if (drive) { - for (auto rf : RiseFall::range()) { - const LibertyCell *cell; - const LibertyPort *from_port; - float *from_slews; - const LibertyPort *to_port; - drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); - if (to_port) { - const LibertyPort *corner_port = to_port->cornerPort(corner, min_max); - corner_port->slewLimit(min_max, limit1, exists1); - if (!exists1 - && corner_port->direction()->isAnyOutput() - && min_max == MinMax::max()) - corner_port->libertyLibrary()->defaultMaxSlew(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } - } - } - } -} - -void -CheckSlewLimits::findLimit(const LibertyPort *port, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const -{ - limit = INF; - exists = false; - - const Network *network = sta_->network(); - Sdc *sdc = sta_->sdc(); - float limit1; - bool exists1; - - // Default to top ("design") limit. - Cell *top_cell = network->cell(network->topInstance()); - sdc->slewLimit(top_cell, min_max, - limit1, exists1); - if (exists1) { - limit = limit1; - exists = true; - } - - if (port) { - const LibertyPort *corner_port = port->cornerPort(corner, min_max); - corner_port->slewLimit(min_max, limit1, exists1); - if (!exists1 - // default_max_transition only applies to outputs. - && corner_port->direction()->isAnyOutput() - && min_max == MinMax::max()) - corner_port->libertyLibrary()->defaultMaxSlew(limit1, exists1); - if (exists1 - && (!exists - || min_max->compare(limit, limit1))) { - limit = limit1; - exists = true; - } - } -} - -ClockSet -CheckSlewLimits::clockDomains(const Vertex *vertex) const -{ - ClockSet clks; - VertexPathIterator path_iter(const_cast(vertex), sta_); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - const Clock *clk = path->clock(sta_); - if (clk) - clks.insert(const_cast(clk)); - } - return clks; -} - -//////////////////////////////////////////////////////////////// - -PinSeq -CheckSlewLimits::checkSlewLimits(const Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) -{ - const Network *network = sta_->network(); - PinSeq slew_pins; - float min_slack = MinMax::min()->initValue(); - if (net) { - NetPinIterator *pin_iter = network->pinIterator(net); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - checkSlewLimits(pin, violators, corner, min_max, slew_pins, min_slack); - } - delete pin_iter; - } - else { - LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); - while (inst_iter->hasNext()) { - const Instance *inst = inst_iter->next(); - checkSlewLimits(inst, violators,corner, min_max, slew_pins, min_slack); - } - delete inst_iter; - // Check top level ports. - checkSlewLimits(network->topInstance(), violators, corner, min_max, - slew_pins, min_slack); - } - sort(slew_pins, PinSlewLimitSlackLess(corner, min_max, this, sta_)); - // Keep the min slack pin unless all violators or net pins. - if (!slew_pins.empty() && !violators && net == nullptr) - slew_pins.resize(1); - return slew_pins; -} - -void -CheckSlewLimits::checkSlewLimits(const Instance *inst, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &slew_pins, - float &min_slack) -{ - const Network *network = sta_->network(); - InstancePinIterator *pin_iter = network->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - checkSlewLimits(pin, violators, corner, min_max, slew_pins, min_slack); - } - delete pin_iter; -} - -} // namespace diff --git a/search/CheckSlewLimits.hh b/search/CheckSlewLimits.hh deleted file mode 100644 index ba9071031..000000000 --- a/search/CheckSlewLimits.hh +++ /dev/null @@ -1,129 +0,0 @@ -// 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 "MinMax.hh" -#include "Transition.hh" -#include "NetworkClass.hh" -#include "GraphClass.hh" -#include "Delay.hh" -#include "SdcClass.hh" - -namespace sta { - -class StaState; -class DcalcAnalysisPt; -class Corner; - -class CheckSlewLimits -{ -public: - CheckSlewLimits(const StaState *sta); - // Return pins with the min/max slew limit slack. - // net=null check all nets - // corner=nullptr checks all corners. - PinSeq checkSlewLimits(const Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max); - // corner=nullptr checks all corners. - void checkSlew(const Pin *pin, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - // Corner is nullptr for no slew limit. - const Corner *&corner1, - const RiseFall *&rf, - Slew &slew, - float &limit, - float &slack) const; - void findLimit(const LibertyPort *port, - const Corner *corner, - const MinMax *min_max, - // Return values. - float &limit, - bool &exists) const; - -protected: - void checkSlew1(const Pin *pin, - const Vertex *vertex, - const Corner *corner, - const MinMax *min_max, - bool check_clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const; - void checkSlew2(const Pin *pin, - const Vertex *vertex, - const Corner *corner, - const MinMax *min_max, - const ClockSet &clks, - // Return values. - const Corner *&corner1, - const RiseFall *&rf1, - Slew &slew1, - float &limit1, - float &slack1) const; - void checkSlew3(const Vertex *vertex, - const Corner *corner1, - const RiseFall *rf1, - const MinMax *min_max, - float limit1, - // Return values. - const Corner *&corner, - const RiseFall *&rf, - Slew &slew, - float &slack, - float &limit) const; - void findLimit(const Pin *pin, - const Corner *corner, - const RiseFall *rf, - const MinMax *min_max, - const ClockSet &clks, - // Return values. - float &limit, - bool &limit_exists) const; - void checkSlewLimits(const Instance *inst, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &slew_pins, - float &min_slack); - void checkSlewLimits(const Pin *pin, - bool violators, - const Corner *corner, - const MinMax *min_max, - PinSeq &slew_pins, - float &min_slack); - ClockSet clockDomains(const Vertex *vertex) const; - - const StaState *sta_; -}; - -} // namespace diff --git a/search/CheckSlews.cc b/search/CheckSlews.cc new file mode 100644 index 000000000..faa3a77c1 --- /dev/null +++ b/search/CheckSlews.cc @@ -0,0 +1,420 @@ +// 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 "CheckSlews.hh" + +#include "Fuzzy.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Mode.hh" +#include "InputDrive.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" +#include "StaState.hh" +#include "Scene.hh" +#include "Path.hh" +#include "PortDirection.hh" +#include "Sim.hh" +#include "Search.hh" +#include "ClkNetwork.hh" + +namespace sta { + +CheckSlews::CheckSlews(const StaState *sta) : + heap_(0, SlewCheckSlackLess(sta)), + sta_(sta) +{ +} + +void +CheckSlews::clear() +{ + checks_.clear(); + heap_.clear(); +} + +SlewCheckSeq & +CheckSlews::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + clear(); + if (!violators) + heap_.setMaxSize(max_count); + + if (net) + checkNet(net, violators, scenes, min_max); + else + checkAll(violators, scenes, min_max); + + if (violators) + sort(checks_, SlewCheckSlackLess(sta_)); + else + checks_ = heap_.extract(); + return checks_; +} + +void +CheckSlews::checkNet(const Net *net, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + checkPin(pin, violators, scenes, min_max); + } + delete pin_iter; +} + +void +CheckSlews::checkAll(bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + const Instance *inst = inst_iter->next(); + checkInst(inst, violators, scenes, min_max); + } + delete inst_iter; + // Check top level ports. + checkInst(network->topInstance(), violators, scenes, min_max); +} + +void +CheckSlews::checkInst(const Instance *inst, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + checkPin(pin, violators, scenes, min_max); + } + delete pin_iter; +} + +void +CheckSlews::checkPin(const Pin *pin, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max) +{ + const Scene *scene; + const RiseFall *rf; + Slew slew; + float limit, slack; + check(pin, scenes, min_max, true, slew, limit, slack, rf, scene); + if (scene) { + if (violators) { + if (slack < 0.0) + checks_.emplace_back(pin, rf, slew, limit, slack, scene); + } + else + heap_.insert(SlewCheck(pin, rf, slew, limit, slack, scene)); + } +} + +void +CheckSlews::check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max, + bool check_clks, + // Return values. + Slew &slew, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&scene) const +{ + scene = nullptr; + rf = nullptr; + slew = 0.0; + limit = 0.0; + slack = MinMax::min()->initValue(); + + for (const Scene *scene1 : scenes) { + Vertex *vertex, *bidirect_drvr_vertex; + sta_->graph()->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + check2(vertex, scene1, min_max, check_clks, + scene, rf, slew, limit, slack); + if (bidirect_drvr_vertex) + check2(bidirect_drvr_vertex, scene1, min_max, check_clks, + scene, rf, slew, limit, slack); + } +} + +void +CheckSlews::check2(const Vertex *vertex, + const Scene *scene, + const MinMax *min_max, + bool check_clks, + // Return values. + const Scene *&scene1, + const RiseFall *&rf1, + Slew &slew1, + float &limit1, + float &slack1) const +{ + const Mode *mode = scene->mode(); + const Sdc *sdc = mode->sdc(); + const ClkNetwork *clk_network = mode->clkNetwork(); + const Pin *pin = vertex->pin(); + if (!sdc->isDisabledConstraint(pin) + && !clk_network->isIdealClock(pin)) { + ConstClockSet clks; + if (check_clks) + clks = clockDomains(vertex, scene); + for (const RiseFall *rf : RiseFall::range()) { + float limit; + bool exists; + findLimit(pin, scene, rf, min_max, clks, + limit, exists); + if (exists) { + check3(vertex, scene, rf, min_max, limit, + scene1, rf1, slew1, slack1, limit1); + } + } + } +} + +void +CheckSlews::check3(const Vertex *vertex, + const Scene *scene, + const RiseFall *rf, + const MinMax *min_max, + float limit, + // Return values. + const Scene *&scene1, + const RiseFall *&rf1, + Slew &slew1, + float &slack1, + float &limit1) const +{ + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + Slew slew = sta_->graph()->slew(vertex, rf, ap_index); + float slew2 = delayAsFloat(slew); + float slack = (min_max == MinMax::max()) + ? limit - slew2 : slew2 - limit; + if (scene1 == nullptr + || (slack < slack1 + // Break ties for the sake of regression stability. + || (fuzzyEqual(slack, slack1) + && rf->index() < rf1->index()))) { + scene1 = scene; + rf1 = rf; + slew1 = slew; + slack1 = slack; + limit1 = limit; + } +} + +// Return the tightest limit. +void +CheckSlews::findLimit(const Pin *pin, + const Scene *scene, + const RiseFall *rf, + const MinMax *min_max, + const ConstClockSet &clks, + // Return values. + float &limit, + bool &exists) const +{ + const Network *network = sta_->network(); + const Sdc *sdc = scene->sdc(); + LibertyPort *port = network->libertyPort(pin); + findLimit(port, scene, min_max, + limit, exists); + + float limit1; + bool exists1; + if (!clks.empty()) { + // Look for clock slew limits. + const ClkNetwork *clk_network = scene->mode()->clkNetwork(); + bool is_clk = clk_network->isIdealClock(pin); + for (const Clock *clk : clks) { + PathClkOrData clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; + sdc->slewLimit(clk, rf, clk_data, min_max, + limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } + + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + sdc->slewLimit(port, min_max, limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + InputDrive *drive = sdc->findInputDrive(port); + if (drive) { + for (auto rf : RiseFall::range()) { + const LibertyCell *cell; + const LibertyPort *from_port; + float *from_slews; + const LibertyPort *to_port; + drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); + if (to_port) { + const LibertyPort *scene_port = to_port->scenePort(scene, min_max); + scene_port->slewLimit(min_max, limit1, exists1); + if (!exists1 + && scene_port->direction()->isAnyOutput() + && min_max == MinMax::max()) + scene_port->libertyLibrary()->defaultMaxSlew(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } + } + } + } +} + +void +CheckSlews::findLimit(const LibertyPort *port, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + limit = INF; + exists = false; + + const Network *network = sta_->network(); + const Sdc *sdc = scene->sdc(); + float limit1; + bool exists1; + + // Default to top ("design") limit. + Cell *top_cell = network->cell(network->topInstance()); + sdc->slewLimit(top_cell, min_max, + limit1, exists1); + if (exists1) { + limit = limit1; + exists = true; + } + + if (port) { + const LibertyPort *scene_port = port->scenePort(scene, min_max); + scene_port->slewLimit(min_max, limit1, exists1); + if (!exists1 + // default_max_transition only applies to outputs. + && scene_port->direction()->isAnyOutput() + && min_max == MinMax::max()) + scene_port->libertyLibrary()->defaultMaxSlew(limit1, exists1); + if (exists1 + && (!exists + || min_max->compare(limit, limit1))) { + limit = limit1; + exists = true; + } + } +} + +ConstClockSet +CheckSlews::clockDomains(const Vertex *vertex, + const Scene *scene) const +{ + ConstClockSet clks; + VertexPathIterator path_iter(const_cast(vertex), sta_); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + if (path->scene(sta_) == scene) { + const Clock *clk = path->clock(sta_); + if (clk) + clks.insert(clk); + } + } + return clks; +} + +//////////////////////////////////////////////////////////////// + +SlewCheck::SlewCheck() : + pin_(nullptr), + rf_(nullptr), + slew_(0.0), + limit_(0.0), + slack_(0.0), + scene_(nullptr) +{ +} + +SlewCheck::SlewCheck(const Pin *pin, + const RiseFall *rf, + Slew &slew, + float limit, + float slack, + const Scene *scene) : + pin_(pin), + rf_(rf), + slew_(slew), + limit_(limit), + slack_(slack), + scene_(scene) +{ +} + +//////////////////////////////////////////////////////////////// + +SlewCheckSlackLess::SlewCheckSlackLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +SlewCheckSlackLess::operator()(const SlewCheck &check1, + const SlewCheck &check2) const +{ + float slack1 = check1.slack(); + float slack2 = check2.slack(); + return fuzzyLess(slack1, slack2) + || (fuzzyEqual(slack1, slack2) + // Break ties for the sake of regression stability. + && sta_->network()->pinLess(check1.pin(), check2.pin())); +} + +} // namespace diff --git a/search/CheckSlews.hh b/search/CheckSlews.hh new file mode 100644 index 000000000..60a167dba --- /dev/null +++ b/search/CheckSlews.hh @@ -0,0 +1,168 @@ +// 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 "MinMax.hh" +#include "Transition.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" +#include "Delay.hh" +#include "SdcClass.hh" +#include "StaState.hh" +#include "BoundedHeap.hh" +#include "Network.hh" + +namespace sta { + +class Scene; +class Mode; +class SlewCheckSlackLess; + +class SlewCheck +{ +public: + SlewCheck(); + SlewCheck(const Pin *pin, + const RiseFall *rf, + Slew &slew, + float limit, + float slack, + const Scene *scene); + bool isNull() { return pin_ == nullptr; } + const Pin *pin() const { return pin_; } + Slew slew() const { return slew_; } + const RiseFall *edge() const { return rf_; } + float limit() const { return limit_; } + float slack() const { return slack_; } + const Scene *scene() const { return scene_; } + +private: + const Pin *pin_; + const RiseFall *rf_; + Slew slew_; + float limit_; + float slack_; + const Scene *scene_; +}; + +class SlewCheckSlackLess +{ +public: + SlewCheckSlackLess(const StaState *sta); + bool operator()(const SlewCheck &check1, + const SlewCheck &check2) const; + +private: + const StaState *sta_; + +}; + +using SlewCheckHeap = BoundedHeap; +using SlewCheckSeq = std::vector; + +class CheckSlews +{ +public: + CheckSlews(const StaState *sta); + void clear(); + // net=null check all nets + SlewCheckSeq &check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void check(const Pin *pin, + const SceneSeq &scenes, + const MinMax *min_max, + bool check_clks, + // Return values. + // Scene is nullptr for no slew limit. + Slew &slew, + float &limit, + float &slack, + const RiseFall *&rf, + const Scene *&scene) const; + void findLimit(const LibertyPort *port, + const Scene *scene, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const; + +protected: + void checkNet(const Net *net, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void checkAll(bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void checkInst(const Instance *inst, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void checkPin(const Pin *pin, + bool violators, + const SceneSeq &scenes, + const MinMax *min_max); + void check2(const Vertex *vertex, + const Scene *scene, + const MinMax *min_max, + bool check_clks, + // Return values. + const Scene *&scene1, + const RiseFall *&rf, + Slew &slew1, + float &limit1, + float &slack1) const; + void check3(const Vertex *vertex, + const Scene *scene, + const RiseFall *rf, + const MinMax *min_max, + float limit, + // Return values. + const Scene *&scene1, + const RiseFall *&rf1, + Slew &slew1, + float &slack1, + float &limit1) const; + void findLimit(const Pin *pin, + const Scene *scene, + const RiseFall *rf, + const MinMax *min_max, + const ConstClockSet &clks, + // Return values. + float &limit, + bool &limit_exists) const; + ConstClockSet clockDomains(const Vertex *vertex, + const Scene *scene) const; + + SlewCheckSeq checks_; + SlewCheckHeap heap_; + const StaState *sta_; +}; + +} // namespace + diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc index 76e11698e..9c692fba1 100644 --- a/search/CheckTiming.cc +++ b/search/CheckTiming.cc @@ -24,6 +24,7 @@ #include "CheckTiming.hh" +#include "ContainerHelpers.hh" #include "Error.hh" #include "TimingRole.hh" #include "Network.hh" @@ -33,6 +34,7 @@ #include "PortDelay.hh" #include "ExceptionPath.hh" #include "Sdc.hh" +#include "Mode.hh" #include "SearchPred.hh" #include "Levelize.hh" #include "Bfs.hh" @@ -40,13 +42,18 @@ #include "Genclks.hh" #include "Path.hh" #include "Sim.hh" +#include "ClkNetwork.hh" namespace sta { using std::string; CheckTiming::CheckTiming(StaState *sta) : - StaState(sta) + StaState(sta), + mode_(nullptr), + sdc_(nullptr), + sim_(nullptr), + clk_network_(nullptr) { } @@ -58,9 +65,7 @@ CheckTiming::~CheckTiming() void CheckTiming::deleteErrors() { - CheckErrorSeq::Iterator error_iter(errors_); - while (error_iter.hasNext()) { - CheckError *error = error_iter.next(); + for (CheckError *error : errors_) { deleteContents(error); delete error; } @@ -74,15 +79,21 @@ CheckTiming::clear() } CheckErrorSeq & -CheckTiming::check(bool no_input_delay, - bool no_output_delay, - bool reg_multiple_clks, - bool reg_no_clks, - bool unconstrained_endpoints, - bool loops, - bool generated_clks) +CheckTiming::check(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) { clear(); + mode_ = mode; + sdc_ = mode->sdc(); + sim_ = mode->sim(); + clk_network_ = mode->clkNetwork(); + if (no_input_delay) checkNoInputDelay(); if (no_output_delay) @@ -110,14 +121,14 @@ CheckTiming::checkNoInputDelay() if (!sdc_->isClock(pin)) { PortDirection *dir = network_->direction(pin); if (dir->isAnyInput() - && !sdc_->hasInputDelay(pin) - && !sim_->logicZeroOne(pin)) - no_arrival.insert(pin); + && !sdc_->hasInputDelay(pin) + && !sim_->isConstant(pin)) + no_arrival.insert(pin); } } delete pin_iter; pushPinErrors("Warning: There %is %d input port%s missing set_input_delay.", - no_arrival); + no_arrival); } void @@ -126,7 +137,7 @@ CheckTiming::checkNoOutputDelay() PinSet no_departure(network_); checkNoOutputDelay(no_departure); pushPinErrors("Warning: There %is %d output port%s missing set_output_delay.", - no_departure); + no_departure); } void @@ -138,8 +149,8 @@ CheckTiming::checkNoOutputDelay(PinSet &no_departure) const Pin *pin = pin_iter->next(); PortDirection *dir = network_->direction(pin); if (dir->isAnyOutput() - && !sdc_->hasOutputDelay(pin) - && !sim_->logicZeroOne(pin)) + && !sdc_->hasOutputDelay(pin) + && !sim_->isConstant(pin)) no_departure.insert(pin); } delete pin_iter; @@ -152,31 +163,30 @@ CheckTiming::hasClkedCheck(Vertex *vertex) while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role() == TimingRole::setup() - && search_->isClock(edge->from(graph_))) + && clk_network_->isClock(edge->from(graph_))) return true; } return false; } -// Search incrementally maintains register/latch clock pins, so use it. void CheckTiming::checkRegClks(bool reg_multiple_clks, - bool reg_no_clks) + bool reg_no_clks) { PinSet no_clk_pins(network_); PinSet multiple_clk_pins(network_); - for (Vertex *vertex : *graph_->regClkVertices()) { + for (Vertex *vertex : graph_->regClkVertices()) { const Pin *pin = vertex->pin(); - ClockSet clks = search_->clocks(vertex); - if (reg_no_clks && clks.empty()) + const ClockSet *clks = clk_network_->clocks(pin); + if (reg_no_clks && clks == nullptr) no_clk_pins.insert(pin); - if (reg_multiple_clks && clks.size() > 1) + if (reg_multiple_clks && clks && clks->size() > 1) multiple_clk_pins.insert(pin); } pushPinErrors("Warning: There %is %d unclocked register/latch pin%s.", - no_clk_pins); + no_clk_pins); pushPinErrors("Warning: There %is %d register/latch pin%s with multiple clocks.", - multiple_clk_pins); + multiple_clk_pins); } void @@ -194,23 +204,19 @@ CheckTiming::checkLoops() if (loop_count > 0) { string error_msg; errorMsgSubst("Warning: There %is %d combinational loop%s in the design.", - loop_count, error_msg); + loop_count, error_msg); CheckError *error = new CheckError; error->push_back(stringCopy(error_msg.c_str())); - GraphLoopSeq::Iterator loop_iter2(loops); - while (loop_iter2.hasNext()) { - GraphLoop *loop = loop_iter2.next(); + for (GraphLoop *loop : loops) { if (loop->isCombinational()) { - EdgeSeq::Iterator edge_iter(loop->edges()); - Edge *last_edge = nullptr; - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Pin *pin = edge->from(graph_)->pin(); - const char *pin_name = stringCopy(sdc_network_->pathName(pin)); - error->push_back(pin_name); - last_edge = edge; - } + Edge *last_edge = nullptr; + for (Edge *edge : *loop->edges()) { + Pin *pin = edge->from(graph_)->pin(); + const char *pin_name = stringCopy(sdc_network_->pathName(pin)); + error->push_back(pin_name); + last_edge = edge; + } if (last_edge) { error->push_back(stringCopy("| loop cut point")); const Pin *pin = last_edge->to(graph_)->pin(); @@ -233,7 +239,7 @@ CheckTiming::checkUnconstrainedEndpoints() checkUnconstrainedOutputs(unconstrained_ends); checkUnconstrainedSetups(unconstrained_ends); pushPinErrors("Warning: There %is %d unconstrained endpoint%s.", - unconstrained_ends); + unconstrained_ends); } void @@ -246,10 +252,10 @@ CheckTiming::checkUnconstrainedOutputs(PinSet &unconstrained_ends) PortDirection *dir = network_->direction(pin); Vertex *vertex = graph_->pinLoadVertex(pin); if (dir->isAnyOutput() - && !vertex->isConstant() + && !sim_->isConstant(pin) && !((hasClkedDepature(pin) - && hasClkedArrival(vertex)) - || hasMaxDelay(pin))) + && hasClkedArrival(vertex)) + || hasMaxDelay(pin))) unconstrained_ends.insert(pin); } delete pin_iter; @@ -262,8 +268,8 @@ CheckTiming::hasClkedDepature(Pin *pin) if (output_delays) { for (OutputDelay *output_delay : *output_delays) { if (output_delay->clkEdge() != nullptr - || output_delay->refPin() != nullptr) - return true; + || output_delay->refPin() != nullptr) + return true; } } return false; @@ -273,13 +279,13 @@ CheckTiming::hasClkedDepature(Pin *pin) bool CheckTiming::hasMaxDelay(Pin *pin) { - for (ExceptionPath *exception : sdc_->exceptions()) { + for (const ExceptionPath *exception : sdc_->exceptions()) { ExceptionTo *to = exception->to(); if (exception->isPathDelay() - && exception->minMax() == MinMaxAll::max() - && to - && to->hasPins() - && to->pins()->hasKey(pin)) + && exception->minMax() == MinMaxAll::max() + && to + && to->hasPins() + && to->pins()->contains(pin)) return true; } return false; @@ -291,12 +297,12 @@ CheckTiming::checkUnconstrainedSetups(PinSet &unconstrained_ends) VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (!vertex->isConstant()) { + if (!sim_->isConstant(vertex)) { VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role() == TimingRole::setup() - && (!search_->isClock(edge->from(graph_)) + && (!clk_network_->isClock(edge->from(graph_)) || !hasClkedArrival(edge->to(graph_)))) { unconstrained_ends.insert(vertex->pin()); break; @@ -322,26 +328,24 @@ void CheckTiming::checkGeneratedClocks() { ClockSet gen_clk_errors; - for (auto clk : sdc_->clks()) { + for (auto clk : sdc_->clocks()) { if (clk->isGenerated()) { - search_->genclks()->checkMaster(clk); + mode_->genclks()->checkMaster(clk, sdc_); bool found_clk = false; - VertexSet src_vertices(graph_); + VertexSet src_vertices = makeVertexSet(this); clk->srcPinVertices(src_vertices, network_, graph_); - VertexSet::Iterator vertex_iter(src_vertices); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - if (search_->isClock(vertex)) { - found_clk = true; - break; - } + for (Vertex *vertex : src_vertices) { + if (clk_network_->isClock(vertex)) { + found_clk = true; + break; + } } if (!found_clk) - gen_clk_errors.insert(clk); + gen_clk_errors.insert(clk); } } pushClkErrors("Warning: There %is %d generated clock%s that %is not connected to a clock source.", - gen_clk_errors); + gen_clk_errors); } // Report the "msg" error for each pin in "pins". @@ -354,7 +358,7 @@ CheckTiming::checkGeneratedClocks() // %a - a/"" void CheckTiming::pushPinErrors(const char *msg, - PinSet &pins) + PinSet &pins) { if (!pins.empty()) { CheckError *error = new CheckError; @@ -376,7 +380,7 @@ CheckTiming::pushPinErrors(const char *msg, void CheckTiming::pushClkErrors(const char *msg, - ClockSet &clks) + ClockSet &clks) { if (!clks.empty()) { CheckError *error = new CheckError; @@ -399,40 +403,40 @@ CheckTiming::pushClkErrors(const char *msg, // Copy msg making substitutions for singular/plurals. void CheckTiming::errorMsgSubst(const char *msg, - int obj_count, - string &error_msg) + int obj_count, + 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; + 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; + 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++; + if (obj_count > 1) + error_msg += 's'; + s++; } else if (flag == 'd') { - error_msg += std::to_string(obj_count); - s++; + error_msg += std::to_string(obj_count); + s++; } else - criticalError(245, "unknown print flag"); + criticalError(245, "unknown print flag"); } else error_msg += ch; diff --git a/search/CheckTiming.hh b/search/CheckTiming.hh index 1bbc6dd19..26edc150b 100644 --- a/search/CheckTiming.hh +++ b/search/CheckTiming.hh @@ -24,7 +24,8 @@ #pragma once -#include "Vector.hh" +#include + #include "StringSeq.hh" #include "NetworkClass.hh" #include "GraphClass.hh" @@ -33,21 +34,24 @@ namespace sta { -typedef StringSeq CheckError; -typedef Vector CheckErrorSeq; +class ClkNetwork; + +using CheckError = StringSeq; +using CheckErrorSeq = std::vector; class CheckTiming : public StaState { public: - explicit CheckTiming(StaState *sta); + CheckTiming(StaState *sta); ~CheckTiming(); - CheckErrorSeq &check(bool no_input_delay, - bool no_output_delay, - bool reg_multiple_clks, - bool reg_no_clks, - bool unconstrained_endpoints, - bool loops, - bool generated_clks); + CheckErrorSeq &check(const Mode *sdc, + bool no_input_delay, + bool no_output_delay, + bool reg_multiple_clks, + bool reg_no_clks, + bool unconstrained_endpoints, + bool loops, + bool generated_clks); protected: void clear(); @@ -55,7 +59,7 @@ protected: void checkNoInputDelay(); void checkNoOutputDelay(); void checkRegClks(bool reg_multiple_clks, - bool reg_no_clks); + bool reg_no_clks); void checkUnconstrainedEndpoints(); bool hasClkedArrival(Vertex *vertex); void checkNoOutputDelay(PinSet &ends); @@ -67,14 +71,18 @@ protected: bool hasMaxDelay(Pin *pin); void checkGeneratedClocks(); void pushPinErrors(const char *msg, - PinSet &pins); + PinSet &pins); void pushClkErrors(const char *msg, - ClockSet &clks); + ClockSet &clks); void errorMsgSubst(const char *msg, - int count, - std::string &error_msg); + int count, + std::string &error_msg); CheckErrorSeq errors_; + const Mode *mode_; + const Sdc *sdc_; + const Sim *sim_; + const ClkNetwork *clk_network_; }; } // namespace diff --git a/search/ClkInfo.cc b/search/ClkInfo.cc index a86791016..ac8e1bf99 100644 --- a/search/ClkInfo.cc +++ b/search/ClkInfo.cc @@ -30,25 +30,26 @@ #include "Network.hh" #include "Graph.hh" #include "Sdc.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Search.hh" #include "Tag.hh" -#include "PathAnalysisPt.hh" namespace sta { -ClkInfo::ClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, +ClkInfo::ClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, const Pin *gen_clk_src, - bool is_gen_clk_src_path, - const RiseFall *pulse_clk_sense, - Arrival insertion, - float latency, - ClockUncertainties *uncertainties, - PathAPIndex path_ap_index, - const Path *crpr_clk_path, - const StaState *sta) : + bool is_gen_clk_src_path, + const RiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + const ClockUncertainties *uncertainties, + const MinMax *min_max, + const Path *crpr_clk_path, + const StaState *sta) : + scene_(scene), clk_edge_(clk_edge), clk_src_(clk_src), gen_clk_src_(gen_clk_src), @@ -61,7 +62,7 @@ ClkInfo::ClkInfo(const ClockEdge *clk_edge, crpr_path_refs_filter_(crpr_clk_path ? crpr_clk_path->tag(sta)->isFilter() : false), is_pulse_clk_(pulse_clk_sense != nullptr), pulse_clk_sense_(pulse_clk_sense ? pulse_clk_sense->index() : 0), - path_ap_index_(path_ap_index) + min_max_index_(min_max->index()) { findHash(sta); } @@ -106,7 +107,13 @@ ClkInfo::findHash(const StaState *sta) hashIncr(hash_, is_gen_clk_src_path_); hashIncr(hash_, is_pulse_clk_); hashIncr(hash_, pulse_clk_sense_); - hashIncr(hash_, path_ap_index_); + hashIncr(hash_, min_max_index_); +} + +const MinMax * +ClkInfo::minMax() const +{ + return MinMax::find(min_max_index_); } VertexId @@ -143,15 +150,13 @@ std::string ClkInfo::to_string(const StaState *sta) const { Network *network = sta->network(); - Corners *corners = sta->corners(); std::string result; - PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index_); - result += path_ap->pathMinMax()->to_string(); + result += scene_->name(); result += "/"; - result += std::to_string(path_ap_index_); - + result += minMax()->to_string(); result += " "; + if (clk_edge_) result += clk_edge_->name(); else @@ -235,15 +240,15 @@ ClkInfoEqual::ClkInfoEqual(const StaState *sta) : bool ClkInfoEqual::operator()(const ClkInfo *clk_info1, - const ClkInfo *clk_info2) const + const ClkInfo *clk_info2) const { return ClkInfo::equal(clk_info1, clk_info2, sta_); } bool ClkInfo::equal(const ClkInfo *clk_info1, - const ClkInfo *clk_info2, - const StaState *sta) + const ClkInfo *clk_info2, + const StaState *sta) { return ClkInfo::cmp(clk_info1, clk_info2, sta) == 0; } @@ -257,16 +262,23 @@ ClkInfoLess::ClkInfoLess(const StaState *sta) : bool ClkInfoLess::operator()(const ClkInfo *clk_info1, - const ClkInfo *clk_info2) const + const ClkInfo *clk_info2) const { return ClkInfo::cmp(clk_info1, clk_info2, sta_) < 0; } int ClkInfo::cmp(const ClkInfo *clk_info1, - const ClkInfo *clk_info2, - const StaState *sta) + const ClkInfo *clk_info2, + const StaState *sta) { + size_t scene_index1 = clk_info1->scene()->index(); + size_t scene_index2 = clk_info2->scene()->index(); + if (scene_index1 < scene_index2) + return -1; + if (scene_index1 > scene_index2) + return 1; + const ClockEdge *clk_edge1 = clk_info1->clkEdge(); const ClockEdge *clk_edge2 = clk_info2->clkEdge(); int edge_index1 = clk_edge1 ? clk_edge1->index() : -1; @@ -276,11 +288,11 @@ ClkInfo::cmp(const ClkInfo *clk_info1, if (edge_index1 > edge_index2) return 1; - PathAPIndex path_ap_index1 = clk_info1->pathAPIndex(); - PathAPIndex path_ap_index2 = clk_info2->pathAPIndex(); - if (path_ap_index1 < path_ap_index2) + int mm_index1 = clk_info1->minMaxIndex(); + int mm_index2 = clk_info2->minMaxIndex(); + if (mm_index1 < mm_index2) return -1; - if (path_ap_index1 > path_ap_index2) + if (mm_index1 > mm_index2) return 1; const Network *network = sta->network(); @@ -302,14 +314,11 @@ ClkInfo::cmp(const ClkInfo *clk_info1, if (gen_clk_src_id1 > gen_clk_src_id2) return 1; - bool crpr_on = sta->crprActive(); - if (crpr_on) { - const Path *crpr_path1 = clk_info1->crprClkPathRaw(); - const Path *crpr_path2 = clk_info2->crprClkPathRaw(); - int path_cmp = Path::cmp(crpr_path1, crpr_path2, sta); - if (path_cmp != 0) - return path_cmp; - } + const Path *crpr_path1 = clk_info1->crprClkPathRaw(); + const Path *crpr_path2 = clk_info2->crprClkPathRaw(); + int path_cmp = Path::cmp(crpr_path1, crpr_path2, sta); + if (path_cmp != 0) + return path_cmp; const ClockUncertainties *uncertainties1 = clk_info1->uncertainties(); const ClockUncertainties *uncertainties2 = clk_info2->uncertainties(); diff --git a/search/ClkInfo.hh b/search/ClkInfo.hh index 09d2c0e0c..8b0432bfe 100644 --- a/search/ClkInfo.hh +++ b/search/ClkInfo.hh @@ -36,20 +36,24 @@ class Path; class ClkInfo { public: - ClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, - const Pin *gen_clk_src, - bool is_gen_clk_src_path, - const RiseFall *pulse_clk_sense, - Arrival insertion, - float latency, - ClockUncertainties *uncertainties, - PathAPIndex path_ap_index, - const Path *crpr_clk_path, - const StaState *sta); + ClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + const Pin *gen_clk_src, + bool is_gen_clk_src_path, + const RiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + const ClockUncertainties *uncertainties, + const MinMax *min_max, + const Path *crpr_clk_path, + const StaState *sta); ~ClkInfo(); std::string to_string(const StaState *sta) const; + Scene *scene() const { return scene_; } + const MinMax *minMax() const; + int minMaxIndex() const { return min_max_index_; } const ClockEdge *clkEdge() const { return clk_edge_; } const Clock *clock() const; const Pin *clkSrc() const { return clk_src_; } @@ -61,8 +65,7 @@ public: float latency() const { return latency_; } Arrival &insertion() { return insertion_; } const Arrival &insertion() const { return insertion_; } - ClockUncertainties *uncertainties() const { return uncertainties_; } - PathAPIndex pathAPIndex() const { return path_ap_index_; } + const ClockUncertainties *uncertainties() const { return uncertainties_; } // Clock path used for crpr resolution. // Null for clocks because the path cannot point to itself. Path *crprClkPath(const StaState *sta); @@ -76,20 +79,21 @@ public: const Path *crprClkPathRaw() const; static int cmp(const ClkInfo *clk_info1, - const ClkInfo *clk_info2, - const StaState *sta); + const ClkInfo *clk_info2, + const StaState *sta); static bool equal(const ClkInfo *clk_info1, - const ClkInfo *clk_info2, - const StaState *sta); + const ClkInfo *clk_info2, + const StaState *sta); protected: void findHash(const StaState *sta); private: + Scene *scene_; const ClockEdge *clk_edge_; const Pin *clk_src_; const Pin *gen_clk_src_; Path crpr_clk_path_; - ClockUncertainties *uncertainties_; + const ClockUncertainties *uncertainties_; Arrival insertion_; float latency_; size_t hash_; @@ -100,16 +104,16 @@ private: bool crpr_path_refs_filter_:1; bool is_pulse_clk_:1; unsigned int pulse_clk_sense_:RiseFall::index_bit_count; - unsigned int path_ap_index_:path_ap_index_bit_count; + unsigned int min_max_index_:MinMax::index_bit_count; }; class ClkInfoLess { public: - explicit ClkInfoLess(const StaState *sta); + ClkInfoLess(const StaState *sta); ~ClkInfoLess() {} bool operator()(const ClkInfo *clk_info1, - const ClkInfo *clk_info2) const; + const ClkInfo *clk_info2) const; protected: const StaState *sta_; @@ -126,7 +130,7 @@ class ClkInfoEqual public: ClkInfoEqual(const StaState *sta); bool operator()(const ClkInfo *clk_info1, - const ClkInfo *clk_info2) const; + const ClkInfo *clk_info2) const; protected: const StaState *sta_; diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc index d9b4a68f4..5fc244e82 100644 --- a/search/ClkLatency.cc +++ b/search/ClkLatency.cc @@ -26,6 +26,7 @@ #include +#include "ContainerHelpers.hh" #include "Report.hh" #include "Debug.hh" #include "Units.hh" @@ -36,7 +37,6 @@ #include "Path.hh" #include "StaState.hh" #include "Search.hh" -#include "PathAnalysisPt.hh" #include "ClkInfo.hh" namespace sta { @@ -48,29 +48,32 @@ ClkLatency::ClkLatency(StaState *sta) : ClkDelays ClkLatency::findClkDelays(const Clock *clk, - const Corner *corner, + const Scene *scene, bool include_internal_latency) { ConstClockSeq clks; clks.push_back(clk); - ClkDelayMap clk_delay_map = findClkDelays(clks, corner, + SceneSet scenes; + scenes.insert(scene); + ClkDelayMap clk_delay_map = findClkDelays(clks, scenes, include_internal_latency); return clk_delay_map[clk]; } void ClkLatency::reportClkLatency(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency, int digits) { - ClkDelayMap clk_delay_map = findClkDelays(clks, corner, include_internal_latency); + const SceneSet scenes1 = Scene::sceneSet(scenes); + ClkDelayMap clk_delay_map = findClkDelays(clks, scenes1, include_internal_latency); // Sort the clocks to report in a stable order. ConstClockSeq sorted_clks; for (const Clock *clk : clks) sorted_clks.push_back(clk); - std::sort(sorted_clks.begin(), sorted_clks.end(), ClkNameLess()); + sort(sorted_clks, ClkNameLess()); for (const Clock *clk : sorted_clks) { ClkDelays clk_delays = clk_delay_map[clk]; @@ -143,23 +146,28 @@ ClkLatency::reportClkLatency(const Clock *clk, ClkDelayMap ClkLatency::findClkDelays(ConstClockSeq &clks, - const Corner *corner, + const SceneSet &scenes, bool include_internal_latency) { + ConstClockSet clk_set; + for (const Clock *clk : clks) + clk_set.insert(clk); + ClkDelayMap clk_delay_map; // Make entries for the relevant clocks to filter path clocks. for (const Clock *clk : clks) clk_delay_map[clk]; - for (Vertex *clk_vertex : *graph_->regClkVertices()) { + + for (Vertex *clk_vertex : graph_->regClkVertices()) { VertexPathIterator path_iter(clk_vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const ClockEdge *path_clk_edge = path->clkEdge(this); - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); + const Scene *path_scene = path->scene(this); + const Clock *path_clk = path_clk_edge->clock(); if (path_clk_edge - && (corner == nullptr - || path_ap->corner() == corner)) { - const Clock *path_clk = path_clk_edge->clock(); + && 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()) { ClkDelays &clk_delays = delays_itr->second; @@ -295,10 +303,10 @@ ClkDelays::insertionDelay(Path *clk_path, const RiseFall *clk_rf = clk_edge->transition(); const ClkInfo *clk_info = clk_path->clkInfo(sta); const Pin *src_pin = clk_info->clkSrc(); - const PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(sta); 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, path_ap)); + min_max, mode)); } float diff --git a/search/ClkLatency.hh b/search/ClkLatency.hh index 3a7d0385e..c8fbd49b9 100644 --- a/search/ClkLatency.hh +++ b/search/ClkLatency.hh @@ -35,7 +35,7 @@ namespace sta { -typedef std::map ClkDelayMap; +using ClkDelayMap = std::map; // Find and report clock skews between source/target registers. class ClkLatency : public StaState @@ -44,16 +44,16 @@ public: ClkLatency(StaState *sta); // Report clk latency for clks. void reportClkLatency(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency, int digits); ClkDelays findClkDelays(const Clock *clk, - const Corner *corner, + const Scene *scene, bool include_internal_latency); protected: ClkDelayMap findClkDelays(ConstClockSeq &clks, - const Corner *corner, + const SceneSet &scenes, bool include_internal_latency); void reportClkLatency(const Clock *clk, ClkDelays &clk_delays, diff --git a/search/ClkNetwork.cc b/search/ClkNetwork.cc index 663c350ca..a27ad6100 100644 --- a/search/ClkNetwork.cc +++ b/search/ClkNetwork.cc @@ -29,20 +29,23 @@ #include "Graph.hh" #include "Bfs.hh" #include "Sdc.hh" +#include "Mode.hh" #include "SearchPred.hh" #include "Search.hh" namespace sta { -ClkNetwork::ClkNetwork(StaState *sta) : +ClkNetwork::ClkNetwork(Mode *mode, + StaState *sta) : StaState(sta), + mode_(mode), clk_pins_valid_(false) { } ClkNetwork::~ClkNetwork() { - clk_pins_map_.deleteContentsClear(); + deleteContents(clk_pins_map_); } void @@ -57,7 +60,7 @@ ClkNetwork::clear() { clk_pins_valid_ = false; pin_clks_map_.clear(); - clk_pins_map_.deleteContentsClear(); + deleteContents(clk_pins_map_); pin_ideal_clks_map_.clear(); } @@ -85,7 +88,7 @@ ClkNetwork::disconnectPinBefore(const Pin *pin) void ClkNetwork::connectPinAfter(const Pin *pin) { - if (isClock(pin)) + if (network_->isRegClkPin(pin)) clkPinsInvalid(); } @@ -93,7 +96,8 @@ class ClkSearchPred : public ClkTreeSearchPred { public: ClkSearchPred(const StaState *sta); - virtual bool searchTo(const Vertex *to); + bool searchTo(const Vertex *to, + const Mode *mode) const override; }; ClkSearchPred::ClkSearchPred(const StaState *sta) : @@ -102,10 +106,10 @@ ClkSearchPred::ClkSearchPred(const StaState *sta) : } bool -ClkSearchPred::searchTo(const Vertex *to) +ClkSearchPred::searchTo(const Vertex *to, + const Mode *mode) const { - const Sdc *sdc = sta_->sdc(); - return !sdc->isLeafPinClock(to->pin()); + return !mode->sdc()->isLeafPinClock(to->pin()); } void @@ -120,38 +124,39 @@ ClkNetwork::findClkPins() void ClkNetwork::findClkPins(bool ideal_only, - PinClksMap &pin_clks_map) + PinClksMap &pin_clks_map) { + const Sdc *sdc = mode_->sdc(); ClkSearchPred srch_pred(this); BfsFwdIterator bfs(BfsIndex::other, &srch_pred, this); - for (Clock *clk : sdc_->clks()) { + for (Clock *clk : sdc->clocks()) { if (!ideal_only - || !clk->isPropagated()) { + || !clk->isPropagated()) { PinSet *clk_pins = clk_pins_map_[clk]; if (clk_pins == nullptr) { clk_pins = new PinSet(network_); clk_pins_map_[clk] = clk_pins; } for (const Pin *pin : clk->leafPins()) { - if (!ideal_only - || !sdc_->isPropagatedClock(pin)) { - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - bfs.enqueue(vertex); - if (bidirect_drvr_vertex) - bfs.enqueue(bidirect_drvr_vertex); - } + if (!ideal_only + || !sdc->isPropagatedClock(pin)) { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + bfs.enqueue(vertex); + if (bidirect_drvr_vertex) + bfs.enqueue(bidirect_drvr_vertex); + } } while (bfs.hasNext()) { - Vertex *vertex = bfs.next(); - const Pin *pin = vertex->pin(); - if (!ideal_only - || !sdc_->isPropagatedClock(pin)) { - clk_pins->insert(pin); - ClockSet &pin_clks = pin_clks_map[pin]; + Vertex *vertex = bfs.next(); + const Pin *pin = vertex->pin(); + if (!ideal_only + || !sdc->isPropagatedClock(pin)) { + clk_pins->insert(pin); + ClockSet &pin_clks = pin_clks_map[pin]; pin_clks.insert(clk); - bfs.enqueueAdjacentVertices(vertex); - } + bfs.enqueueAdjacentVertices(vertex); + } } } } @@ -160,8 +165,13 @@ ClkNetwork::findClkPins(bool ideal_only, bool ClkNetwork::isClock(const Pin *pin) const { - return network_->isRegClkPin(pin) - || pin_clks_map_.hasKey(pin); + return pin_clks_map_.contains(pin); +} + +bool +ClkNetwork::isClock(const Vertex *vertex) const +{ + return isClock(vertex->pin()); } bool @@ -183,30 +193,45 @@ ClkNetwork::isClock(const Net *net) const bool ClkNetwork::isIdealClock(const Pin *pin) const { - return pin_ideal_clks_map_.hasKey(pin); + return pin_ideal_clks_map_.contains(pin); +} + +bool +ClkNetwork::isIdealClock(const Vertex *vertex) const +{ + return isIdealClock(vertex->pin()); } bool ClkNetwork::isPropagatedClock(const Pin *pin) const { - return pin_clks_map_.hasKey(pin) - && !pin_ideal_clks_map_.hasKey(pin); + return pin_clks_map_.contains(pin) + && !pin_ideal_clks_map_.contains(pin); } const ClockSet * -ClkNetwork::clocks(const Pin *pin) +ClkNetwork::clocks(const Pin *pin) const { - if (pin_clks_map_.hasKey(pin)) - return &pin_clks_map_[pin]; + auto itr = pin_clks_map_.find(pin); + if (itr != pin_clks_map_.end()) + return &itr->second; else return nullptr; } + +const ClockSet * +ClkNetwork::clocks(const Vertex *vertex) const +{ + return clocks(vertex->pin()); +} + const ClockSet * -ClkNetwork::idealClocks(const Pin *pin) +ClkNetwork::idealClocks(const Pin *pin) const { - if (pin_ideal_clks_map_.hasKey(pin)) - return &pin_ideal_clks_map_[pin]; + auto itr = pin_ideal_clks_map_.find(pin); + if (itr != pin_ideal_clks_map_.end()) + return &itr->second; else return nullptr; } @@ -214,7 +239,7 @@ ClkNetwork::idealClocks(const Pin *pin) const PinSet * ClkNetwork::pins(const Clock *clk) { - if (clk_pins_map_.hasKey(clk)) + if (clk_pins_map_.contains(clk)) return clk_pins_map_[clk]; else return nullptr; @@ -223,14 +248,12 @@ ClkNetwork::pins(const Clock *clk) float ClkNetwork::idealClkSlew(const Pin *pin, const RiseFall *rf, - const MinMax *min_max) + const MinMax *min_max) const { - const ClockSet *clks = clk_network_->idealClocks(pin); + const ClockSet *clks = idealClocks(pin); if (clks && !clks->empty()) { float slew = min_max->initValue(); - ClockSet::ConstIterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + for (Clock *clk : *clks) { float clk_slew = clk->slew(rf, min_max); if (min_max->compare(clk_slew, slew)) slew = clk_slew; diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 33df2af64..9a34fe5ea 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -26,6 +26,8 @@ #include // abs #include +#include +#include #include "Fuzzy.hh" #include "Report.hh" @@ -40,7 +42,6 @@ #include "Bfs.hh" #include "Path.hh" #include "StaState.hh" -#include "PathAnalysisPt.hh" #include "SearchPred.hh" #include "Search.hh" #include "Crpr.hh" @@ -50,131 +51,10 @@ namespace sta { using std::abs; -ClkSkew::ClkSkew() : - src_path_(nullptr), - tgt_path_(nullptr), - include_internal_latency_(false), - skew_(0.0) -{ -} - -ClkSkew::ClkSkew(Path *src_path, - Path *tgt_path, - bool include_internal_latency, - StaState *sta) : - src_path_(src_path), - tgt_path_(tgt_path), - include_internal_latency_(include_internal_latency) -{ - skew_ = srcLatency(sta) - - tgtLatency(sta) - - delayAsFloat(crpr(sta)) - + uncertainty(sta); -} - -ClkSkew::ClkSkew(const ClkSkew &clk_skew) -{ - src_path_ = clk_skew.src_path_; - tgt_path_ = clk_skew.tgt_path_; - include_internal_latency_ = clk_skew.include_internal_latency_; - skew_ = clk_skew.skew_; -} - -void -ClkSkew::operator=(const ClkSkew &clk_skew) -{ - src_path_ = clk_skew.src_path_; - tgt_path_ = clk_skew.tgt_path_; - include_internal_latency_ = clk_skew.include_internal_latency_; - skew_ = clk_skew.skew_; -} - -float -ClkSkew::srcLatency(const StaState *sta) -{ - Arrival src_arrival = src_path_->arrival(); - return delayAsFloat(src_arrival) - src_path_->clkEdge(sta)->time() - + clkTreeDelay(src_path_, sta); -} - -float -ClkSkew::srcInternalClkLatency(const StaState *sta) -{ - return clkTreeDelay(src_path_, sta); -} - -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); -} - -float -ClkSkew::tgtInternalClkLatency(const StaState *sta) -{ - return clkTreeDelay(tgt_path_, sta); -} - -float -ClkSkew::clkTreeDelay(Path *clk_path, - const StaState *sta) -{ - if (include_internal_latency_) { - const Vertex *vertex = clk_path->vertex(sta); - const Pin *pin = vertex->pin(); - 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)); - return port->clkTreeDelay(slew, rf, min_max); - } - else - return 0.0; -} - -Crpr -ClkSkew::crpr(const StaState *sta) -{ - CheckCrpr *check_crpr = sta->search()->checkCrpr(); - return check_crpr->checkCrpr(src_path_, tgt_path_); -} - -float -ClkSkew::uncertainty(const StaState *sta) -{ - const TimingRole *check_role = (src_path_->minMax(sta) == SetupHold::max()) - ? TimingRole::setup() - : TimingRole::hold(); - // Uncertainty decreases slack, but increases skew. - return -PathEnd::checkTgtClkUncertainty(tgt_path_, tgt_path_->clkEdge(sta), - check_role, sta); -} - -bool -ClkSkew::srcTgtPathNameLess(ClkSkew &clk_skew1, - ClkSkew &clk_skew2, - const StaState *sta) -{ - Network *network = sta->sdcNetwork(); - const char *src_path1 = network->pathName(clk_skew1.srcPath()->pin(sta)); - const char *src_path2 = network->pathName(clk_skew2.srcPath()->pin(sta)); - 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)); -} - - -//////////////////////////////////////////////////////////////// - ClkSkews::ClkSkews(StaState *sta) : StaState(sta), - corner_(nullptr), include_internal_latency_(true), - fanout_pred_(sta) + fanout_pred_(this) { } @@ -186,18 +66,18 @@ ClkSkews::clear() void ClkSkews::reportClkSkew(ConstClockSeq &clks, - const Corner *corner, - const SetupHold *setup_hold, + const SceneSeq &scenes, + const SetupHold *setup_hold, bool include_internal_latency, - int digits) + int digits) { - findClkSkew(clks, corner, include_internal_latency); + findClkSkew(clks, scenes, include_internal_latency); // Sort the clocks to report in a stable order. ConstClockSeq sorted_clks; for (const Clock *clk : clks) sorted_clks.push_back(clk); - std::sort(sorted_clks.begin(), sorted_clks.end(), ClkNameLess()); + sort(sorted_clks, ClkNameLess()); for (const Clock *clk : sorted_clks) { report_->reportLine("Clock %s", clk->name()); @@ -212,7 +92,7 @@ ClkSkews::reportClkSkew(ConstClockSeq &clks, void ClkSkews::reportClkSkew(ClkSkew &clk_skew, - int digits) + int digits) { Unit *time_unit = units_->timeUnit(); Path *src_path = clk_skew.srcPath(); @@ -255,14 +135,16 @@ ClkSkews::reportClkSkew(ClkSkew &clk_skew, } float -ClkSkews::findWorstClkSkew(const Corner *corner, +ClkSkews::findWorstClkSkew(const SceneSeq &scenes, const SetupHold *setup_hold, bool include_internal_latency) { ConstClockSeq clks; - for (const Clock *clk : *sdc_->clocks()) - clks.push_back(clk); - findClkSkew(clks, corner, include_internal_latency); + for (const Scene *scene : scenes_) { + for (const Clock *clk : scene->sdc()->clocks()) + clks.push_back(clk); + } + findClkSkew(clks, scenes, include_internal_latency); float worst_skew = 0.0; for (const auto& [clk, clk_skews] : skews_) { float skew = clk_skews[setup_hold->index()].skew(); @@ -274,10 +156,10 @@ ClkSkews::findWorstClkSkew(const Corner *corner, void ClkSkews::findClkSkew(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency) { - if (corner == corner_ + if (scenes == scenes_ && include_internal_latency == include_internal_latency_ && clks == clks_ && !skews_.empty()) @@ -285,16 +167,19 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, skews_.clear(); clks_ = clks; - corner_ = corner; + scenes_ = scenes; include_internal_latency_ = include_internal_latency; clk_set_.clear(); for (const Clock *clk : clks) clk_set_.insert(clk); + scenes_set_ = Scene::sceneSet(scenes); + // This sets modes_ for fanout_pred_ also. + modes_ = Scene::modes(scenes_); if (thread_count_ > 1) { std::vector partial_skews(thread_count_); - for (Vertex *src_vertex : *graph_->regClkVertices()) { + for (Vertex *src_vertex : graph_->regClkVertices()) { if (hasClkPaths(src_vertex)) { dispatch_queue_->dispatch([this, src_vertex, &partial_skews](int i) { findClkSkewFrom(src_vertex, partial_skews[i]); @@ -317,7 +202,8 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, // Copy array elements for (int setup_hold_idx : SetupHold::rangeIndex()) itr->second[setup_hold_idx] = partial_skew[setup_hold_idx]; - } else { + } + else { // Update existing entry for (int setup_hold_idx : SetupHold::rangeIndex()) { ClkSkew &final_skew = itr->second[setup_hold_idx]; @@ -335,7 +221,7 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, } } else { - for (Vertex *src_vertex : *graph_->regClkVertices()) { + for (Vertex *src_vertex : graph_->regClkVertices()) { if (hasClkPaths(src_vertex)) findClkSkewFrom(src_vertex, skews_); } @@ -349,7 +235,7 @@ ClkSkews::hasClkPaths(Vertex *vertex) while (path_iter.hasNext()) { Path *path = path_iter.next(); const Clock *path_clk = path->clock(this); - if (clk_set_.find(path_clk) != clk_set_.end()) + if (clk_set_.contains(path_clk)) return true; } return false; @@ -375,9 +261,9 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex, void ClkSkews::findClkSkewFrom(Vertex *src_vertex, - Vertex *q_vertex, - const RiseFallBoth *src_rf, - ClkSkewMap &skews) + Vertex *q_vertex, + const RiseFallBoth *src_rf, + ClkSkewMap &skews) { VertexSet endpoints = findFanout(q_vertex); for (Vertex *end : endpoints) { @@ -400,50 +286,48 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex, void ClkSkews::findClkSkew(Vertex *src_vertex, - const RiseFallBoth *src_rf, - Vertex *tgt_vertex, - const RiseFallBoth *tgt_rf, + const RiseFallBoth *src_rf, + Vertex *tgt_vertex, + const RiseFallBoth *tgt_rf, ClkSkewMap &skews) { Unit *time_unit = units_->timeUnit(); VertexPathIterator src_iter(src_vertex, this); while (src_iter.hasNext()) { Path *src_path = src_iter.next(); + Scene *src_scene = src_path->scene(this); const Clock *src_clk = src_path->clock(this); if (src_path->isClock(this) && src_rf->matches(src_path->transition(this)) - && clk_set_.find(src_clk) != clk_set_.end()) { - Corner *src_corner = src_path->pathAnalysisPt(this)->corner(); + && clk_set_.contains(src_clk) + && scenes_set_.contains(src_scene)) { const MinMax *tgt_min_max = src_path->minMax(this)->opposite(); - if (corner_ == nullptr - || src_corner == corner_) { - VertexPathIterator tgt_iter(tgt_vertex, this); - 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) - && tgt_rf->matches(tgt_path->transition(this)) - && tgt_path->minMax(this) == tgt_min_max - && tgt_path->pathAnalysisPt(this)->corner() == src_corner) { - ClkSkew probe(src_path, tgt_path, include_internal_latency_, this); - 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", - network_->pathName(src_path->pin(this)), - src_path->transition(this)->to_string().c_str(), - time_unit->asString(probe.srcLatency(this)), - network_->pathName(tgt_path->pin(this)), - tgt_path->transition(this)->to_string().c_str(), - time_unit->asString(probe.tgtLatency(this)), - delayAsString(probe.crpr(this), this), - time_unit->asString(probe.skew())); - if (clk_skew.srcPath() == nullptr - || abs(probe.skew()) > abs(clk_skew.skew())) - clk_skew = probe; - } - } + VertexPathIterator tgt_iter(tgt_vertex, this); + 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) + && tgt_rf->matches(tgt_path->transition(this)) + && tgt_path->minMax(this) == tgt_min_max + && tgt_path->scene(this) == src_scene) { + ClkSkew probe(src_path, tgt_path, include_internal_latency_, this); + 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", + network_->pathName(src_path->pin(this)), + src_path->transition(this)->to_string().c_str(), + time_unit->asString(probe.srcLatency(this)), + network_->pathName(tgt_path->pin(this)), + tgt_path->transition(this)->to_string().c_str(), + time_unit->asString(probe.tgtLatency(this)), + delayAsString(probe.crpr(this), this), + time_unit->asString(probe.skew())); + if (clk_skew.srcPath() == nullptr + || abs(probe.skew()) > abs(clk_skew.skew())) + clk_skew = probe; + } } } } @@ -452,15 +336,15 @@ ClkSkews::findClkSkew(Vertex *src_vertex, VertexSet ClkSkews::findFanout(Vertex *from) { - VertexSet endpoints(graph_); - UnorderedSet visited; + VertexSet endpoints = makeVertexSet(this); + std::unordered_set visited; findFanout1(from, visited, endpoints); return endpoints; } void ClkSkews::findFanout1(Vertex *from, - UnorderedSet &visited, + std::unordered_set &visited, VertexSet &endpoints) { visited.insert(from); @@ -482,16 +366,136 @@ ClkSkews::findFanout1(Vertex *from, //////////////////////////////////////////////////////////////// +ClkSkew::ClkSkew() : + src_path_(nullptr), + tgt_path_(nullptr), + include_internal_latency_(false), + skew_(0.0) +{ +} + +ClkSkew::ClkSkew(Path *src_path, + Path *tgt_path, + bool include_internal_latency, + StaState *sta) : + src_path_(src_path), + tgt_path_(tgt_path), + include_internal_latency_(include_internal_latency) +{ + skew_ = srcLatency(sta) + - tgtLatency(sta) + - delayAsFloat(crpr(sta)) + + uncertainty(sta); +} + +ClkSkew::ClkSkew(const ClkSkew &clk_skew) +{ + src_path_ = clk_skew.src_path_; + tgt_path_ = clk_skew.tgt_path_; + include_internal_latency_ = clk_skew.include_internal_latency_; + skew_ = clk_skew.skew_; +} + +void +ClkSkew::operator=(const ClkSkew &clk_skew) +{ + src_path_ = clk_skew.src_path_; + tgt_path_ = clk_skew.tgt_path_; + include_internal_latency_ = clk_skew.include_internal_latency_; + skew_ = clk_skew.skew_; +} + +float +ClkSkew::srcLatency(const StaState *sta) +{ + Arrival src_arrival = src_path_->arrival(); + return delayAsFloat(src_arrival) - src_path_->clkEdge(sta)->time() + + clkTreeDelay(src_path_, sta); +} + +float +ClkSkew::srcInternalClkLatency(const StaState *sta) +{ + return clkTreeDelay(src_path_, sta); +} + +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); +} + +float +ClkSkew::tgtInternalClkLatency(const StaState *sta) +{ + return clkTreeDelay(tgt_path_, sta); +} + +float +ClkSkew::clkTreeDelay(Path *clk_path, + const StaState *sta) +{ + if (include_internal_latency_) { + const Vertex *vertex = clk_path->vertex(sta); + const Pin *pin = vertex->pin(); + 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)); + return port->clkTreeDelay(slew, rf, min_max); + } + else + return 0.0; +} + +Crpr +ClkSkew::crpr(const StaState *sta) +{ + CheckCrpr *check_crpr = sta->search()->checkCrpr(); + return check_crpr->checkCrpr(src_path_, tgt_path_); +} + +float +ClkSkew::uncertainty(const StaState *sta) +{ + const TimingRole *check_role = (src_path_->minMax(sta) == SetupHold::max()) + ? TimingRole::setup() + : TimingRole::hold(); + // Uncertainty decreases slack, but increases skew. + return -PathEnd::checkTgtClkUncertainty(tgt_path_, tgt_path_->clkEdge(sta), + check_role, sta); +} + +bool +ClkSkew::srcTgtPathNameLess(ClkSkew &clk_skew1, + ClkSkew &clk_skew2, + const StaState *sta) +{ + Network *network = sta->sdcNetwork(); + const char *src_path1 = network->pathName(clk_skew1.srcPath()->pin(sta)); + const char *src_path2 = network->pathName(clk_skew2.srcPath()->pin(sta)); + 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)); +} + +//////////////////////////////////////////////////////////////// + FanOutSrchPred::FanOutSrchPred(const StaState *sta) : SearchPred1(sta) { } bool -FanOutSrchPred::searchThru(Edge *edge) +FanOutSrchPred::searchThru(Edge *edge, + const Mode *mode) const { const TimingRole *role = edge->role(); - return SearchPred1::searchThru(edge) + return SearchPred1::searchThru(edge, mode) && (role == TimingRole::wire() || role == TimingRole::combinational() || role == TimingRole::tristateEnable() diff --git a/search/ClkSkew.hh b/search/ClkSkew.hh index f59d03dcd..cedefa8ac 100644 --- a/search/ClkSkew.hh +++ b/search/ClkSkew.hh @@ -26,7 +26,8 @@ #include -#include "UnorderedSet.hh" +#include + #include "SdcClass.hh" #include "StaState.hh" #include "Transition.hh" @@ -72,13 +73,15 @@ private: float skew_; }; -typedef std::map ClkSkewMap; +using ClkSkewMap = std::map; class FanOutSrchPred : public SearchPred1 { public: FanOutSrchPred(const StaState *sta); - virtual bool searchThru(Edge *edge); + bool searchThru(Edge *edge, + const Mode *mode) const override; + using SearchPred1::searchThru; }; // Find and report clock skews between source/target registers. @@ -89,41 +92,42 @@ public: void clear(); // Report clk skews for clks. void reportClkSkew(ConstClockSeq &clks, - const Corner *corner, - const SetupHold *setup_hold, + const SceneSeq &scenes, + const SetupHold *setup_hold, bool include_internal_latency, - int digits); + int digits); // Find worst clock skew between src/target registers. - float findWorstClkSkew(const Corner *corner, + float findWorstClkSkew(const SceneSeq &scenes, const SetupHold *setup_hold, bool include_internal_latency); protected: void findClkSkew(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency); bool hasClkPaths(Vertex *vertex); void findClkSkewFrom(Vertex *src_vertex, - ClkSkewMap &skews); + ClkSkewMap &skews); void findClkSkewFrom(Vertex *src_vertex, - Vertex *q_vertex, - const RiseFallBoth *src_rf, - ClkSkewMap &skews); + Vertex *q_vertex, + const RiseFallBoth *src_rf, + ClkSkewMap &skews); void findClkSkew(Vertex *src_vertex, - const RiseFallBoth *src_rf, - Vertex *tgt_vertex, - const RiseFallBoth *tgt_rf, - ClkSkewMap &skews); + const RiseFallBoth *src_rf, + Vertex *tgt_vertex, + const RiseFallBoth *tgt_rf, + ClkSkewMap &skews); VertexSet findFanout(Vertex *from); void findFanout1(Vertex *from, - UnorderedSet &visited, + std::unordered_set &visited, VertexSet &endpoints); void reportClkSkew(ClkSkew &clk_skew, int digits); + // Node StaState scenes_ and modes_ are reused there. ConstClockSeq clks_; ConstClockSet clk_set_; - const Corner *corner_; + SceneSet scenes_set_; bool include_internal_latency_; FanOutSrchPred fanout_pred_; ClkSkewMap skews_; diff --git a/search/Corner.cc b/search/Corner.cc index 19a7e5df2..e69de29bb 100644 --- a/search/Corner.cc +++ b/search/Corner.cc @@ -1,461 +0,0 @@ -// 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 "Corner.hh" - -#include "Sdc.hh" -#include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" -#include "PathAnalysisPt.hh" - -namespace sta { - -Corners::Corners(StaState *sta) : - StaState(sta) -{ -} - -Corners::~Corners() -{ - clear(); -} - -void -Corners::clear() -{ - corners_.deleteContentsClear(); - corner_map_.clear(); - dcalc_analysis_pts_.deleteContentsClear(); - path_analysis_pts_.deleteContentsClear(); - parasitic_analysis_pts_.deleteContentsClear(); -} - -int -Corners::count() const -{ - return corners_.size(); -} - -bool -Corners::multiCorner() const -{ - return corners_.size() > 1; -} - -Corner * -Corners::findCorner(const char *corner_name) -{ - return corner_map_.findKey(corner_name); -} - -Corner * -Corners::findCorner(int corner_index) -{ - return corners_[corner_index]; -} - -void -Corners::analysisTypeChanged() -{ - makeAnalysisPts(); -} - -void -Corners::operatingConditionsChanged() -{ - for (DcalcAnalysisPt *dcalc_ap : dcalc_analysis_pts_) { - const MinMax *min_max = dcalc_ap->constraintMinMax(); - const OperatingConditions *op_cond = - sdc_->operatingConditions(min_max); - dcalc_ap->setOperatingConditions(op_cond); - } -} - -void -Corners::makeCorners(StringSet *corner_names) -{ - clear(); - int index = 0; - for (const char *name : *corner_names) { - Corner *corner = new Corner(name, index); - corners_.push_back(corner); - // Use the copied name in the map. - corner_map_[corner->name()] = corner; - index++; - } - makeAnalysisPts(); -} - -void -Corners::copy(Corners *corners) -{ - clear(); - int index = 0; - for (Corner *orig : corners->corners_) { - Corner *corner = new Corner(orig->name(), index); - corners_.push_back(corner); - // Use the copied name in the map. - corner_map_[corner->name()] = corner; - index++; - } - makeAnalysisPts(); - - for (ParasiticAnalysisPt *orig_ap : corners->parasitic_analysis_pts_) { - ParasiticAnalysisPt *ap = new ParasiticAnalysisPt(orig_ap->name(), - orig_ap->index(), - orig_ap->indexMax()); - parasitic_analysis_pts_.push_back(ap); - } - - for (size_t i = 0; i < corners->corners_.size(); i++) { - Corner *orig = corners->corners_[i]; - Corner *corner = corners_[i]; - corner->parasitic_analysis_pts_ = orig->parasitic_analysis_pts_; - } -} - -void -Corners::makeParasiticAnalysisPts(bool per_corner) -{ - parasitic_analysis_pts_.deleteContentsClear(); - if (per_corner) { - // per corner, per min/max - parasitic_analysis_pts_.resize(corners_.size() * MinMax::index_count); - for (Corner *corner : corners_) { - corner->setParasiticAnalysisPtcount(MinMax::index_count); - for (const MinMax *min_max : MinMax::range()) { - int mm_index = min_max->index(); - int ap_index = corner->index() * MinMax::index_count + mm_index; - int ap_index_max = corner->index() * MinMax::index_count - + MinMax::max()->index(); - std::string ap_name = corner->name(); - ap_name += "_"; - ap_name += min_max->to_string(); - ParasiticAnalysisPt *ap = new ParasiticAnalysisPt(ap_name.c_str(), - ap_index, ap_index_max); - parasitic_analysis_pts_[ap_index] = ap; - corner->setParasiticAP(ap, mm_index); - } - } - } - else { - // shared corner, per min/max - parasitic_analysis_pts_.resize(MinMax::index_count); - int ap_index_max = MinMax::max()->index(); - for (const MinMax *min_max : MinMax::range()) { - int mm_index = min_max->index(); - int ap_index = mm_index; - ParasiticAnalysisPt *ap = new ParasiticAnalysisPt(min_max->to_string().c_str(), - ap_index, - ap_index_max); - parasitic_analysis_pts_[ap_index] = ap; - for (Corner *corner : corners_) { - corner->setParasiticAnalysisPtcount(MinMax::index_count); - corner->setParasiticAP(ap, mm_index); - } - } - } -} - -void -Corners::makeAnalysisPts() -{ - dcalc_analysis_pts_.deleteContentsClear(); - path_analysis_pts_.deleteContentsClear(); - - for (Corner *corner : corners_) { - makeDcalcAnalysisPts(corner); - makePathAnalysisPts(corner); - } -} - -void -Corners::makeDcalcAnalysisPts(Corner *corner) -{ - DcalcAnalysisPt *min_ap, *max_ap; - switch (sdc_->analysisType()) { - case AnalysisType::single: - corner->setDcalcAnalysisPtcount(1); - max_ap = makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::min()); - max_ap->setCheckClkSlewIndex(max_ap->index()); - break; - case AnalysisType::bc_wc: - corner->setDcalcAnalysisPtcount(2); - min_ap = makeDcalcAnalysisPt(corner, MinMax::min(), MinMax::min()); - max_ap = makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::max()); - min_ap->setCheckClkSlewIndex(min_ap->index()); - max_ap->setCheckClkSlewIndex(max_ap->index()); - break; - case AnalysisType::ocv: - corner->setDcalcAnalysisPtcount(2); - min_ap = makeDcalcAnalysisPt(corner, MinMax::min(), MinMax::max()); - max_ap = makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::min()); - min_ap->setCheckClkSlewIndex(max_ap->index()); - max_ap->setCheckClkSlewIndex(min_ap->index()); - break; - } -} - -DcalcAnalysisPt * -Corners::makeDcalcAnalysisPt(Corner *corner, - const MinMax *min_max, - const MinMax *check_clk_slew_min_max) -{ - OperatingConditions *op_cond = sdc_->operatingConditions(min_max); - DcalcAnalysisPt *dcalc_ap = new DcalcAnalysisPt(corner, - dcalc_analysis_pts_.size(), - op_cond, min_max, - check_clk_slew_min_max); - dcalc_analysis_pts_.push_back(dcalc_ap); - corner->addDcalcAP(dcalc_ap); - return dcalc_ap; -} - -// The clock insertion delay (source latency) required for setup and -// hold checks is: -// -// hold check -// report_timing -delay_type min -// path insertion pll_delay -// src clk min early max -// tgt clk max late min -// -// setup check -// report_timing -delay_type max -// path insertion pll_delay -// src clk max late min -// tgt clk min early max -// -// For analysis type single or bc_wc only one path is required, but as -// shown above both early and late insertion delays are required. -// To find propagated generated clock insertion delays both early and -// late clock network paths are required. Thus, analysis type single -// makes min and max analysis points. -// Only one of them is enabled to "report paths". -void -Corners::makePathAnalysisPts(Corner *corner) -{ - DcalcAnalysisPt *dcalc_ap_min = corner->findDcalcAnalysisPt(MinMax::min()); - DcalcAnalysisPt *dcalc_ap_max = corner->findDcalcAnalysisPt(MinMax::max()); - switch (sdc_->analysisType()) { - case AnalysisType::single: - case AnalysisType::bc_wc: - makePathAnalysisPts(corner, false, dcalc_ap_min, dcalc_ap_max); - break; - case AnalysisType::ocv: - makePathAnalysisPts(corner, true, dcalc_ap_min, dcalc_ap_max); - break; - } -} - - -void -Corners::makePathAnalysisPts(Corner *corner, - bool swap_clk_min_max, - DcalcAnalysisPt *dcalc_ap_min, - DcalcAnalysisPt *dcalc_ap_max) -{ - PathAnalysisPt *min_ap = new PathAnalysisPt(corner, - path_analysis_pts_.size(), - MinMax::min(), dcalc_ap_min); - path_analysis_pts_.push_back(min_ap); - corner->addPathAP(min_ap); - - PathAnalysisPt *max_ap = new PathAnalysisPt(corner, - path_analysis_pts_.size(), - MinMax::max(), dcalc_ap_max); - path_analysis_pts_.push_back(max_ap); - corner->addPathAP(max_ap); - - if (swap_clk_min_max) { - min_ap->setTgtClkAnalysisPt(max_ap); - max_ap->setTgtClkAnalysisPt(min_ap); - } - else { - min_ap->setTgtClkAnalysisPt(min_ap); - max_ap->setTgtClkAnalysisPt(max_ap); - } - - min_ap->setInsertionAnalysisPt(MinMax::min(), min_ap); - min_ap->setInsertionAnalysisPt(MinMax::max(), max_ap); - max_ap->setInsertionAnalysisPt(MinMax::min(), min_ap); - max_ap->setInsertionAnalysisPt(MinMax::max(), max_ap); -} - -int -Corners::parasiticAnalysisPtCount() const -{ - return parasitic_analysis_pts_.size(); -} - -ParasiticAnalysisPtSeq & -Corners::parasiticAnalysisPts() -{ - return parasitic_analysis_pts_; -} - -DcalcAPIndex -Corners::dcalcAnalysisPtCount() const -{ - return dcalc_analysis_pts_.size(); -} - -DcalcAnalysisPtSeq & -Corners::dcalcAnalysisPts() -{ - return dcalc_analysis_pts_; -} - -const DcalcAnalysisPtSeq & -Corners::dcalcAnalysisPts() const -{ - return dcalc_analysis_pts_; -} - -PathAPIndex -Corners::pathAnalysisPtCount() const -{ - return path_analysis_pts_.size(); -} - -PathAnalysisPtSeq & -Corners::pathAnalysisPts() -{ - return path_analysis_pts_; -} - -const PathAnalysisPtSeq & -Corners::pathAnalysisPts() const -{ - return path_analysis_pts_; -} - -PathAnalysisPt * -Corners::findPathAnalysisPt(PathAPIndex path_index) const -{ - return path_analysis_pts_[path_index]; -} - -//////////////////////////////////////////////////////////////// - -Corner::Corner(const char *name, - int index) : - name_(name), - index_(index), - path_analysis_pts_(MinMax::index_count) -{ -} - -ParasiticAnalysisPt * -Corner::findParasiticAnalysisPt(const MinMax *min_max) const -{ - int ap_count = parasitic_analysis_pts_.size(); - if (ap_count == 0) - return nullptr; - else if (ap_count == 1) - return parasitic_analysis_pts_[0]; - else if (ap_count == 2) - return parasitic_analysis_pts_[min_max->index()]; - else { - criticalError(246, "unknown parasitic analysis point count"); - return nullptr; - } -} - -void -Corner::setParasiticAnalysisPtcount(int ap_count) -{ - parasitic_analysis_pts_.resize(ap_count); -} - -void -Corner::setParasiticAP(ParasiticAnalysisPt *ap, - int mm_index) -{ - parasitic_analysis_pts_[mm_index] = ap; -} - -void -Corner::setDcalcAnalysisPtcount(DcalcAPIndex ap_count) -{ - dcalc_analysis_pts_.resize(ap_count); -} - -void -Corner::addDcalcAP(DcalcAnalysisPt *dcalc_ap) -{ - if (dcalc_analysis_pts_.size() == 1) - dcalc_analysis_pts_[0] = dcalc_ap; - else - dcalc_analysis_pts_[dcalc_ap->constraintMinMax()->index()] = dcalc_ap; -} - -DcalcAnalysisPt * -Corner::findDcalcAnalysisPt(const MinMax *min_max) const -{ - int ap_count = dcalc_analysis_pts_.size(); - if (ap_count == 0) - return nullptr; - else if (ap_count == 1) - return dcalc_analysis_pts_[0]; - else if (ap_count == 2) - return dcalc_analysis_pts_[min_max->index()]; - else { - criticalError(247, "unknown analysis point count"); - return nullptr; - } -} - -PathAnalysisPt * -Corner::findPathAnalysisPt(const MinMax *min_max) const -{ - return path_analysis_pts_[min_max->index()]; -} - -void -Corner::addPathAP(PathAnalysisPt *path_ap) -{ - path_analysis_pts_[path_ap->pathMinMax()->index()] = path_ap; -} - -void -Corner::addLiberty(LibertyLibrary *lib, - const MinMax *min_max) -{ - liberty_[min_max->index()].push_back(lib); -} - -const LibertySeq & -Corner::libertyLibraries(const MinMax *min_max) const -{ - return liberty_[min_max->index()]; -} - -int -Corner::libertyIndex(const MinMax *min_max) const -{ - return index_ * MinMax::index_count + min_max->index(); -} - -} // namespace diff --git a/search/Crpr.cc b/search/Crpr.cc index 5420f7202..c22265413 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -28,12 +28,10 @@ #include #include "Debug.hh" -#include "Vector.hh" #include "Network.hh" #include "Graph.hh" #include "Sdc.hh" #include "Path.hh" -#include "PathAnalysisPt.hh" #include "ClkInfo.hh" #include "Tag.hh" #include "TagGroup.hh" @@ -42,6 +40,7 @@ #include "Search.hh" #include "Genclks.hh" #include "Variables.hh" +#include "Mode.hh" namespace sta { @@ -74,11 +73,12 @@ CheckCrpr::maxCrpr(const ClkInfo *clk_info) Arrival CheckCrpr::otherMinMaxArrival(const Path *path) { - PathAnalysisPt *other_ap = path->pathAnalysisPt(this)->tgtClkAnalysisPt(); + const MinMax *tgt_min_max = path->tgtClkMinMax(this); Tag *tag = path->tag(this); VertexPathIterator other_iter(path->vertex(this), - path->transition(this), - other_ap, this); + path->scene(this), tgt_min_max, + path->transition(this), + this); while (other_iter.hasNext()) { Path *other = other_iter.next(); if (Tag::matchCrpr(other->tag(this), tag)) @@ -91,7 +91,7 @@ CheckCrpr::otherMinMaxArrival(const Path *path) Crpr CheckCrpr::checkCrpr(const Path *src_path, - const Path *tgt_clk_path) + const Path *tgt_clk_path) { Crpr crpr; Pin *crpr_pin; @@ -101,15 +101,15 @@ CheckCrpr::checkCrpr(const Path *src_path, void CheckCrpr::checkCrpr(const Path *src_path, - const Path *tgt_clk_path, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const Path *tgt_clk_path, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; - if (crprActive() - && src_path && tgt_clk_path) { + if (src_path && tgt_clk_path + && crprActive(src_path->mode(this))) { bool same_pin = (variables_->crprMode() == CrprMode::same_pin); checkCrpr1(src_path, tgt_clk_path, same_pin, crpr, crpr_pin); } @@ -117,11 +117,11 @@ CheckCrpr::checkCrpr(const Path *src_path, void CheckCrpr::checkCrpr1(const Path *src_path, - const Path *tgt_clk_path, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const Path *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; @@ -146,12 +146,13 @@ CheckCrpr::checkCrpr1(const Path *src_path, // is from the opposite min/max of the data. && src_clk_min_max != tgt_clk_path->minMax(this) && (src_clk_path - || src_clk->isGenerated())) { + || src_clk->isGenerated())) { // Src path from input port clk path can only be from generated clk path. if (src_clk_path == nullptr) { src_clk_path = portClkPath(src_clk_info->clkEdge(), src_clk_info->clkSrc(), - src_path->pathAnalysisPt(this)); + src_path->scene(this), + src_path->minMax(this)); } findCrpr(src_clk_path, tgt_clk_path, same_pin, crpr, crpr_pin); } @@ -160,16 +161,17 @@ CheckCrpr::checkCrpr1(const Path *src_path, // Find the clk path for an input/output port. Path * CheckCrpr::portClkPath(const ClockEdge *clk_edge, - const Pin *clk_src_pin, - const PathAnalysisPt *path_ap) + const Pin *clk_src_pin, + const Scene *scene, + const MinMax *min_max) { Vertex *clk_vertex = graph_->pinDrvrVertex(clk_src_pin); - VertexPathIterator path_iter(clk_vertex, clk_edge->transition(), - path_ap, this); + VertexPathIterator path_iter(clk_vertex, scene, min_max, + clk_edge->transition(), this); while (path_iter.hasNext()) { Path *path = path_iter.next(); if (path->clkEdge(this) == clk_edge - && path->isClock(this)) { + && path->isClock(this)) { return path; } } @@ -178,11 +180,11 @@ CheckCrpr::portClkPath(const ClockEdge *clk_edge, void CheckCrpr::findCrpr(const Path *src_clk_path, - const Path *tgt_clk_path, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const Path *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; @@ -202,12 +204,12 @@ CheckCrpr::findCrpr(const Path *src_clk_path, const Path *src_path = src_gclk_paths[i]; const Path *tgt_path = tgt_gclk_paths[j]; if (src_path->clkInfo(this)->clkSrc() - == tgt_path->clkInfo(this)->clkSrc()) { - src_clk_path1 = src_gclk_paths[i]; - tgt_clk_path1 = tgt_gclk_paths[j]; + == tgt_path->clkInfo(this)->clkSrc()) { + src_clk_path1 = src_gclk_paths[i]; + tgt_clk_path1 = tgt_gclk_paths[j]; } else - break; + break; } } const Path *src_clk_path2 = src_clk_path1; @@ -222,14 +224,14 @@ CheckCrpr::findCrpr(const Path *src_clk_path, if (level_diff >= 0) { src_clk_path2 = src_clk_path2->prevPath(); if (src_clk_path2 == nullptr - || src_clk_path2->isNull()) + || src_clk_path2->isNull()) break; src_level = src_clk_path2->vertex(this)->level(); } if (level_diff <= 0) { tgt_clk_path2 = tgt_clk_path2->prevPath(); if (tgt_clk_path2 == nullptr - || tgt_clk_path2->isNull()) + || tgt_clk_path2->isNull()) break; tgt_level = tgt_clk_path2->vertex(this)->level(); } @@ -237,7 +239,7 @@ CheckCrpr::findCrpr(const Path *src_clk_path, if (src_clk_path2 && !src_clk_path2->isNull() && tgt_clk_path2 && !tgt_clk_path2->isNull() && (src_clk_path2->transition(this) == tgt_clk_path2->transition(this) - || same_pin)) { + || same_pin)) { debugPrint(debug_, "crpr", 2, "crpr pin %s", network_->pathName(src_clk_path2->pin(this))); crpr = findCrpr1(src_clk_path2, tgt_clk_path2); @@ -252,11 +254,12 @@ CheckCrpr::genClkSrcPaths(const Path *path) const ClkInfo *clk_info = path->clkInfo(this); const ClockEdge *clk_edge = clk_info->clkEdge(); const Pin *clk_src = clk_info->clkSrc(); - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); + const Mode *mode = path->mode(this); + const MinMax *min_max = path->minMax(this); gclk_paths.push_back(path); - Genclks *genclks = search_->genclks(); + Genclks *genclks = mode->genclks(); while (clk_edge->clock()->isGenerated()) { - const Path *genclk_path = genclks->srcPath(clk_edge, clk_src, path_ap); + const Path *genclk_path = genclks->srcPath(clk_edge, clk_src, min_max); if (genclk_path == nullptr) break; clk_info = genclk_path->clkInfo(this); @@ -269,7 +272,7 @@ CheckCrpr::genClkSrcPaths(const Path *path) Crpr CheckCrpr::findCrpr1(const Path *src_clk_path, - const Path *tgt_clk_path) + const Path *tgt_clk_path) { if (variables_->pocvEnabled()) { // Remove variation on the common path. @@ -282,7 +285,7 @@ CheckCrpr::findCrpr1(const Path *src_clk_path, float src_clk_time = src_clk_path->clkEdge(this)->time(); float tgt_clk_time = tgt_clk_path->clkEdge(this)->time(); float crpr_mean = abs(delayAsFloat(src_arrival) - src_clk_time - - (delayAsFloat(tgt_arrival) - tgt_clk_time)); + - (delayAsFloat(tgt_arrival) - tgt_clk_time)); // Remove the sigma from both source and target path arrivals. float crpr_sigma2 = delaySigma2(src_arrival, src_el) + delaySigma2(tgt_arrival, tgt_el); @@ -310,13 +313,13 @@ CheckCrpr::crprArrivalDiff(const Path *path) { Arrival other_arrival = otherMinMaxArrival(path); float crpr_diff = abs(delayAsFloat(path->arrival()) - - delayAsFloat(other_arrival)); + - delayAsFloat(other_arrival)); return crpr_diff; } Crpr CheckCrpr::outputDelayCrpr(const Path *src_clk_path, - const ClockEdge *tgt_clk_edge) + const ClockEdge *tgt_clk_edge) { Crpr crpr; Pin *crpr_pin; @@ -326,30 +329,32 @@ CheckCrpr::outputDelayCrpr(const Path *src_clk_path, void CheckCrpr::outputDelayCrpr(const Path *src_path, - const ClockEdge *tgt_clk_edge, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const ClockEdge *tgt_clk_edge, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; - if (crprActive()) { - const PathAnalysisPt *path_ap = src_path->pathAnalysisPt(this); - const PathAnalysisPt *tgt_path_ap = path_ap->tgtClkAnalysisPt(); + const Scene *scene = src_path->scene(this); + const Mode *mode = scene->mode(); + if (crprActive(mode)) { + const MinMax *tgt_min_max = src_path->tgtClkMinMax(this); bool same_pin = (variables_->crprMode() == CrprMode::same_pin); - outputDelayCrpr1(src_path,tgt_clk_edge,tgt_path_ap, same_pin, - crpr, crpr_pin); + outputDelayCrpr1(src_path, tgt_clk_edge, scene, tgt_min_max, + same_pin, crpr, crpr_pin); } } void CheckCrpr::outputDelayCrpr1(const Path *src_path, - const ClockEdge *tgt_clk_edge, - const PathAnalysisPt *tgt_path_ap, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin) + const ClockEdge *tgt_clk_edge, + const Scene *scene, + const MinMax *min_max, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) { crpr = 0.0; crpr_pin = nullptr; @@ -363,7 +368,7 @@ CheckCrpr::outputDelayCrpr1(const Path *src_path, && crprPossible(src_clk, tgt_clk)) { Path *tgt_genclk_path = portClkPath(tgt_clk_edge, tgt_clk_edge->clock()->defaultPin(), - tgt_path_ap); + scene, min_max); const Path *src_clk_path = src_path->clkInfo(this)->crprClkPath(this); if (src_clk_path) findCrpr(src_clk_path, tgt_genclk_path, same_pin, crpr, crpr_pin); @@ -372,17 +377,17 @@ CheckCrpr::outputDelayCrpr1(const Path *src_path, bool CheckCrpr::crprPossible(const Clock *clk1, - const Clock *clk2) + const Clock *clk2) { return clk1 && clk2 && !clk1->isVirtual() && !clk2->isVirtual() // Generated clocks can have crpr in the source path. && (clk1 == clk2 - || clk1->isGenerated() - || clk2->isGenerated() - // Different non-generated clocks with the same source pins (using -add). - || PinSet::intersects(&clk1->pins(), &clk2->pins(), network_)); + || clk1->isGenerated() + || clk2->isGenerated() + // Different non-generated clocks with the same source pins (using -add). + || intersects(&clk1->pins(), &clk2->pins(), network_)); } } // namespace diff --git a/search/Crpr.hh b/search/Crpr.hh index e26353aa5..379608c1e 100644 --- a/search/Crpr.hh +++ b/search/Crpr.hh @@ -36,58 +36,60 @@ class CrprPaths; class CheckCrpr : public StaState { public: - explicit CheckCrpr(StaState *sta); + CheckCrpr(StaState *sta); // Find the maximum possible crpr (clock min/max delta delay) for path. Arrival maxCrpr(const ClkInfo *clk_info); // Timing check CRPR. Crpr checkCrpr(const Path *src_clk_path, - const Path *tgt_clk_path); + const Path *tgt_clk_path); void checkCrpr(const Path *src_path, - const Path *tgt_clk_path, - // Return values. - Crpr &crpr, - Pin *&crpr_pin); + const Path *tgt_clk_path, + // Return values. + Crpr &crpr, + Pin *&crpr_pin); // Output delay CRPR. Crpr outputDelayCrpr(const Path *src_clk_path, - const ClockEdge *tgt_clk_edge); + const ClockEdge *tgt_clk_edge); void outputDelayCrpr(const Path *src_clk_path, - const ClockEdge *tgt_clk_edge, - // Return values. - Crpr &crpr, - Pin *&crpr_pin); + const ClockEdge *tgt_clk_edge, + // Return values. + Crpr &crpr, + Pin *&crpr_pin); private: void clkPathPrev(const Path *path, Path &prev); Arrival otherMinMaxArrival(const Path *path); void checkCrpr1(const Path *src_path, - const Path *tgt_clk_path, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin); + const Path *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin); void outputDelayCrpr1(const Path *src_path, - const ClockEdge *tgt_clk_edge, - const PathAnalysisPt *tgt_path_ap, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&crpr_pin); + const ClockEdge *tgt_clk_edge, + const Scene *scene, + const MinMax *min_max, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin); bool crprPossible(const Clock *clk1, - const Clock *clk2); + const Clock *clk2); ConstPathSeq genClkSrcPaths(const Path *path); void findCrpr(const Path *src_clk_path, - const Path *tgt_clk_path, - bool same_pin, - // Return values. - Crpr &crpr, - Pin *&common_pin); + const Path *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&common_pin); Path *portClkPath(const ClockEdge *clk_edge, const Pin *clk_src_pin, - const PathAnalysisPt *path_ap); + const Scene *scene, + const MinMax *min_max); Crpr findCrpr1(const Path *src_clk_path, - const Path *tgt_clk_path); + const Path *tgt_clk_path); float crprArrivalDiff(const Path *path); }; diff --git a/search/FindRegister.cc b/search/FindRegister.cc index 66f9b5539..f45f23917 100644 --- a/search/FindRegister.cc +++ b/search/FindRegister.cc @@ -32,6 +32,7 @@ #include "Network.hh" #include "Graph.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Clock.hh" #include "SearchPred.hh" #include "Search.hh" @@ -40,151 +41,147 @@ namespace sta { static TimingSense pathSenseThru(TimingSense from_sense, - TimingSense thru_sense); + TimingSense thru_sense); static bool hasMinPulseWidthCheck(LibertyPort *port); // Predicate used for searching from clocks to find registers. -class FindRegClkPred : public SearchPred1 +class FindRegClkPred : public ClkTreeSearchPred { public: FindRegClkPred(Clock *clk, - const StaState *sta); - virtual bool searchThru(Edge *edge); - virtual bool searchFrom(const Vertex *from_vertex); + const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; private: Clock *clk_; }; FindRegClkPred::FindRegClkPred(Clock *clk, - const StaState *sta) : - SearchPred1(sta), + const StaState *sta) : + ClkTreeSearchPred(sta), clk_(clk) { } bool -FindRegClkPred::searchFrom(const Vertex *from_vertex) +FindRegClkPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { - const Sdc *sdc = sta_->sdc(); const Pin *from_pin = from_vertex->pin(); - return !sdc->clkStopPropagation(from_pin, clk_) - && SearchPred1::searchFrom(from_vertex); + return !mode->sdc()->clkStopPropagation(from_pin, clk_) + && ClkTreeSearchPred::searchFrom(from_vertex, mode); } -bool -FindRegClkPred::searchThru(Edge *edge) -{ - const TimingRole *role = edge->role(); - return (role->isWire() - || role == TimingRole::combinational()) - && SearchPred1::searchThru(edge); -} +//////////////////////////////////////////////////////////////// // Helper for "all_registers". // Visit all register instances. class FindRegVisitor : public StaState { public: - FindRegVisitor(StaState *sta); + FindRegVisitor(const StaState *sta); virtual ~FindRegVisitor() {} void visitRegs(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches); + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode); private: void visitRegs(const Pin *clk_pin, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches); + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches); virtual void visitReg(Instance *inst) = 0; virtual void visitSequential(Instance *inst, - Sequential *seq) = 0; + Sequential *seq) = 0; void visitFanoutRegs(Vertex *from_vertex, - TimingSense from_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - SearchPred &clk_pred, - VertexSet &visited_vertices); + TimingSense from_sense, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + SearchPred &clk_pred, + VertexSet &visited_vertices, + const Mode *mode); void findSequential(const Pin *clk_pin, - Instance *inst, - LibertyCell *cell, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - bool &has_seqs, - bool &matches); + Instance *inst, + LibertyCell *cell, + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + // Return values. + bool &has_seqs, + bool &matches); bool findInferedSequential(LibertyCell *cell, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches); + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches); bool hasTimingCheck(LibertyCell *cell, - LibertyPort *clk, - LibertyPort *d); + LibertyPort *clk, + LibertyPort *d); }; -FindRegVisitor::FindRegVisitor(StaState *sta) : +FindRegVisitor::FindRegVisitor(const StaState *sta) : StaState(sta) { } void FindRegVisitor::visitRegs(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode) { if (clks && !clks->empty()) { // Use DFS search to find all registers downstream of the clocks. - ClockSet::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + for (Clock *clk : *clks) { FindRegClkPred clk_pred(clk, this); - VertexSet visited_vertices(graph_); + VertexSet visited_vertices = makeVertexSet(this); for (const Pin *pin : clk->leafPins()) { - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - visitFanoutRegs(vertex, TimingSense::positive_unate, - clk_rf, edge_triggered, - latches, clk_pred, - visited_vertices); - // Clocks defined on bidirect pins blow it out both ends. - if (bidirect_drvr_vertex) - visitFanoutRegs(bidirect_drvr_vertex, - TimingSense::positive_unate, - clk_rf, edge_triggered, - latches, clk_pred, - visited_vertices); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + visitFanoutRegs(vertex, TimingSense::positive_unate, + clk_rf, edge_triggered, + latches, clk_pred, + visited_vertices, mode); + // Clocks defined on bidirect pins blow it out both ends. + if (bidirect_drvr_vertex) + visitFanoutRegs(bidirect_drvr_vertex, + TimingSense::positive_unate, + clk_rf, edge_triggered, + latches, clk_pred, + visited_vertices, mode); } } } else { - for (Vertex *vertex : *graph_->regClkVertices()) { + for (Vertex *vertex : graph_->regClkVertices()) { visitRegs(vertex->pin(), TimingSense::positive_unate, - RiseFallBoth::riseFall(), - edge_triggered, latches); + RiseFallBoth::riseFall(), + edge_triggered, latches); } } } void FindRegVisitor::visitFanoutRegs(Vertex *from_vertex, - TimingSense from_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - SearchPred &clk_pred, - VertexSet &visited_vertices) -{ - if (!visited_vertices.hasKey(from_vertex) - && clk_pred.searchFrom(from_vertex)) { + TimingSense from_sense, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + SearchPred &clk_pred, + VertexSet &visited_vertices, + const Mode *mode) +{ + if (!visited_vertices.contains(from_vertex) + && clk_pred.searchFrom(from_vertex, mode)) { visited_vertices.insert(from_vertex); VertexOutEdgeIterator edge_iter(from_vertex, graph_); while (edge_iter.hasNext()) { @@ -193,22 +190,22 @@ FindRegVisitor::visitFanoutRegs(Vertex *from_vertex, const Pin *to_pin = to_vertex->pin(); TimingSense to_sense = pathSenseThru(from_sense, edge->sense()); if (to_vertex->isRegClk()) - visitRegs(to_pin, to_sense, clk_rf, edge_triggered, latches); + visitRegs(to_pin, to_sense, clk_rf, edge_triggered, latches); // Even register clock pins can have combinational fanout arcs. - if (clk_pred.searchThru(edge) - && clk_pred.searchTo(to_vertex)) - visitFanoutRegs(to_vertex, to_sense, clk_rf, edge_triggered, latches, - clk_pred, visited_vertices); + if (clk_pred.searchThru(edge, mode) + && clk_pred.searchTo(to_vertex, mode)) + visitFanoutRegs(to_vertex, to_sense, clk_rf, edge_triggered, latches, + clk_pred, visited_vertices, mode); } } } void FindRegVisitor::visitRegs(const Pin *clk_pin, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches) { Instance *inst = network_->instance(clk_pin); LibertyCell *cell = network_->libertyCell(inst); @@ -216,11 +213,11 @@ FindRegVisitor::visitRegs(const Pin *clk_pin, || clk_rf != RiseFallBoth::riseFall()) { bool matches, has_seqs; findSequential(clk_pin, inst, cell, clk_sense, clk_rf, - edge_triggered, latches, - has_seqs, matches); + edge_triggered, latches, + has_seqs, matches); if (!has_seqs) matches = findInferedSequential(cell, clk_sense, clk_rf, - edge_triggered, latches); + edge_triggered, latches); if (matches) visitReg(inst); } @@ -232,39 +229,40 @@ FindRegVisitor::visitRegs(const Pin *clk_pin, void FindRegVisitor::findSequential(const Pin *clk_pin, - Instance *inst, - LibertyCell *cell, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - bool &has_seqs, - bool &matches) + Instance *inst, + LibertyCell *cell, + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + // Return values. + bool &has_seqs, + bool &matches) { has_seqs = false; matches = false; for (Sequential *seq : cell->sequentials()) { has_seqs = true; if ((seq->isRegister() && edge_triggered) - || (seq->isLatch() && latches)) { + || (seq->isLatch() && latches)) { if (clk_rf == RiseFallBoth::riseFall()) { - visitSequential(inst, seq); - matches = true; - break; + visitSequential(inst, seq); + matches = true; + break; } else { - FuncExpr *clk_func = seq->clock(); - LibertyPort *port = network_->libertyPort(clk_pin); - TimingSense port_sense = clk_func->portTimingSense(port); - TimingSense path_sense = pathSenseThru(clk_sense, port_sense); - if ((path_sense == TimingSense::positive_unate - && clk_rf == RiseFallBoth::rise()) - || (path_sense == TimingSense::negative_unate - && clk_rf == RiseFallBoth::fall())) { - visitSequential(inst, seq); - matches = true; - break; - } + FuncExpr *clk_func = seq->clock(); + LibertyPort *port = network_->libertyPort(clk_pin); + TimingSense port_sense = clk_func->portTimingSense(port); + TimingSense path_sense = pathSenseThru(clk_sense, port_sense); + if ((path_sense == TimingSense::positive_unate + && clk_rf == RiseFallBoth::rise()) + || (path_sense == TimingSense::negative_unate + && clk_rf == RiseFallBoth::fall())) { + visitSequential(inst, seq); + matches = true; + break; + } } } } @@ -272,10 +270,10 @@ FindRegVisitor::findSequential(const Pin *clk_pin, bool FindRegVisitor::findInferedSequential(LibertyCell *cell, - TimingSense clk_sense, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + TimingSense clk_sense, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches) { bool matches = false; const RiseFall *clk_rf1 = clk_rf->asRiseFall(); @@ -283,16 +281,16 @@ FindRegVisitor::findInferedSequential(LibertyCell *cell, TimingArc *arc = *arc_set->arcs().begin(); const RiseFall *arc_clk_rf = arc->fromEdge()->asRiseFall(); bool tr_matches = (clk_rf == RiseFallBoth::riseFall() - || (arc_clk_rf == clk_rf1 - && clk_sense == TimingSense::positive_unate) - || (arc_clk_rf == clk_rf1->opposite() - && clk_sense == TimingSense::negative_unate)); + || (arc_clk_rf == clk_rf1 + && clk_sense == TimingSense::positive_unate) + || (arc_clk_rf == clk_rf1->opposite() + && clk_sense == TimingSense::negative_unate)); const TimingRole *role = arc_set->role(); if (tr_matches - && ((role == TimingRole::regClkToQ() - && edge_triggered) - || (role == TimingRole::latchEnToQ() - && latches))) { + && ((role == TimingRole::regClkToQ() + && edge_triggered) + || (role == TimingRole::latchEnToQ() + && latches))) { matches = true; break; } @@ -302,8 +300,8 @@ FindRegVisitor::findInferedSequential(LibertyCell *cell, bool FindRegVisitor::hasTimingCheck(LibertyCell *cell, - LibertyPort *clk, - LibertyPort *d) + LibertyPort *clk, + LibertyPort *d) { for (TimingArcSet *arc_set : cell->timingArcSets(clk, d)) { const TimingRole *role = arc_set->role(); @@ -316,21 +314,22 @@ FindRegVisitor::hasTimingCheck(LibertyCell *cell, class FindRegInstances : public FindRegVisitor { public: - explicit FindRegInstances(StaState *sta); + FindRegInstances(const StaState *sta); InstanceSet findRegs(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, - bool latches); + bool latches, + const Mode *mode); private: virtual void visitReg(Instance *inst); virtual void visitSequential(Instance *inst, - Sequential *seq); + Sequential *seq); InstanceSet regs_; }; -FindRegInstances::FindRegInstances(StaState *sta) : +FindRegInstances::FindRegInstances(const StaState *sta) : FindRegVisitor(sta), regs_(network_) { @@ -338,17 +337,18 @@ FindRegInstances::FindRegInstances(StaState *sta) : InstanceSet FindRegInstances::findRegs(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode) { - visitRegs(clks, clk_rf, edge_triggered, latches); + visitRegs(clks, clk_rf, edge_triggered, latches, mode); return regs_; } void FindRegInstances::visitSequential(Instance *, - Sequential *) + Sequential *) { } @@ -360,13 +360,14 @@ FindRegInstances::visitReg(Instance *inst) InstanceSet findRegInstances(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegInstances find_regs(sta); - return find_regs.findRegs(clks, clk_rf, edge_triggered, latches); + return find_regs.findRegs(clks, clk_rf, edge_triggered, latches, mode); } //////////////////////////////////////////////////////////////// @@ -374,20 +375,21 @@ findRegInstances(ClockSet *clks, class FindRegPins : public FindRegVisitor { public: - FindRegPins(StaState *sta); + FindRegPins(const StaState *sta); PinSet findPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, - bool latches); + bool latches, + const Mode *mode); protected: virtual void visitReg(Instance *inst); virtual void visitSequential(Instance *inst, - Sequential *seq); + Sequential *seq); virtual bool matchPin(Pin *pin); void visitExpr(FuncExpr *expr, - Instance *inst, - Sequential *seq); + Instance *inst, + Sequential *seq); // Sequential expressions to find instance pins. virtual FuncExpr *seqExpr1(Sequential *seq) = 0; virtual FuncExpr *seqExpr2(Sequential *seq) = 0; @@ -395,7 +397,7 @@ class FindRegPins : public FindRegVisitor PinSet pins_; }; -FindRegPins::FindRegPins(StaState *sta) : +FindRegPins::FindRegPins(const StaState *sta) : FindRegVisitor(sta), pins_(network_) { @@ -403,17 +405,18 @@ FindRegPins::FindRegPins(StaState *sta) : PinSet FindRegPins::findPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches) + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode) { - visitRegs(clks, clk_rf, edge_triggered, latches); + visitRegs(clks, clk_rf, edge_triggered, latches, mode); return pins_; } void FindRegPins::visitSequential(Instance *inst, - Sequential *seq) + Sequential *seq) { visitExpr(seqExpr1(seq), inst, seq); visitExpr(seqExpr2(seq), inst, seq); @@ -421,16 +424,15 @@ FindRegPins::visitSequential(Instance *inst, void FindRegPins::visitExpr(FuncExpr *expr, - Instance *inst, - Sequential *) + Instance *inst, + Sequential *) { if (expr) { - FuncExprPortIterator port_iter(expr); - while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); + LibertyPortSet ports = expr->ports(); + for (LibertyPort *port : ports) { Pin *pin = network_->findPin(inst, port); if (pin) - pins_.insert(pin); + pins_.insert(pin); } } } @@ -456,7 +458,7 @@ FindRegPins::matchPin(Pin *) class FindRegDataPins : public FindRegPins { public: - explicit FindRegDataPins(StaState *sta); + FindRegDataPins(const StaState *sta); private: virtual bool matchPin(Pin *pin); @@ -464,7 +466,7 @@ class FindRegDataPins : public FindRegPins virtual FuncExpr *seqExpr2(Sequential *seq); }; -FindRegDataPins::FindRegDataPins(StaState *sta) : +FindRegDataPins::FindRegDataPins(const StaState *sta) : FindRegPins(sta) { } @@ -508,13 +510,14 @@ hasMinPulseWidthCheck(LibertyPort *port) PinSet findRegDataPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegDataPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches); + return find_regs.findPins(clks, clk_rf, edge_triggered, latches, mode); } //////////////////////////////////////////////////////////////// @@ -522,7 +525,7 @@ findRegDataPins(ClockSet *clks, class FindRegClkPins : public FindRegPins { public: - explicit FindRegClkPins(StaState *sta); + FindRegClkPins(const StaState *sta); private: virtual bool matchPin(Pin *pin); @@ -530,7 +533,7 @@ class FindRegClkPins : public FindRegPins virtual FuncExpr *seqExpr2(Sequential *seq); }; -FindRegClkPins::FindRegClkPins(StaState *sta) : +FindRegClkPins::FindRegClkPins(const StaState *sta) : FindRegPins(sta) { } @@ -565,13 +568,14 @@ FindRegClkPins::seqExpr2(Sequential *) PinSet findRegClkPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegClkPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches); + return find_regs.findPins(clks, clk_rf, edge_triggered, latches, mode); } //////////////////////////////////////////////////////////////// @@ -579,7 +583,7 @@ findRegClkPins(ClockSet *clks, class FindRegAsyncPins : public FindRegPins { public: - explicit FindRegAsyncPins(StaState *sta); + FindRegAsyncPins(const StaState *sta); private: virtual bool matchPin(Pin *pin); @@ -587,7 +591,7 @@ class FindRegAsyncPins : public FindRegPins virtual FuncExpr *seqExpr2(Sequential *seq) { return seq->preset(); } }; -FindRegAsyncPins::FindRegAsyncPins(StaState *sta) : +FindRegAsyncPins::FindRegAsyncPins(const StaState *sta) : FindRegPins(sta) { } @@ -607,13 +611,14 @@ FindRegAsyncPins::matchPin(Pin *pin) PinSet findRegAsyncPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegAsyncPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches); + return find_regs.findPins(clks, clk_rf, edge_triggered, latches, mode); } //////////////////////////////////////////////////////////////// @@ -621,20 +626,20 @@ findRegAsyncPins(ClockSet *clks, class FindRegOutputPins : public FindRegPins { public: - explicit FindRegOutputPins(StaState *sta); + FindRegOutputPins(const StaState *sta); private: virtual bool matchPin(Pin *pin); virtual void visitSequential(Instance *inst, - Sequential *seq); + Sequential *seq); void visitOutput(LibertyPort *port, - Instance *inst); + Instance *inst); // Unused. virtual FuncExpr *seqExpr1(Sequential *seq); virtual FuncExpr *seqExpr2(Sequential *seq); }; -FindRegOutputPins::FindRegOutputPins(StaState *sta) : +FindRegOutputPins::FindRegOutputPins(const StaState *sta) : FindRegPins(sta) { } @@ -647,8 +652,8 @@ FindRegOutputPins::matchPin(Pin *pin) for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { const TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ() - || role == TimingRole::latchDtoQ()) + || role == TimingRole::latchEnToQ() + || role == TimingRole::latchDtoQ()) return true; } return false; @@ -656,7 +661,7 @@ FindRegOutputPins::matchPin(Pin *pin) void FindRegOutputPins::visitSequential(Instance *inst, - Sequential *seq) + Sequential *seq) { visitOutput(seq->output(), inst); visitOutput(seq->outputInv(), inst); @@ -664,7 +669,7 @@ FindRegOutputPins::visitSequential(Instance *inst, void FindRegOutputPins::visitOutput(LibertyPort *port, - Instance *inst) + Instance *inst) { if (port) { // Sequential outputs are internal ports. @@ -676,9 +681,9 @@ FindRegOutputPins::visitOutput(LibertyPort *port, LibertyPort *pin_port = network_->libertyPort(pin); FuncExpr *func = pin_port->function(); if (func - && func->port() - && func->port() == port) - pins_.insert(pin); + && func->port() + && func->port() == port) + pins_.insert(pin); } delete pin_iter; } @@ -698,13 +703,14 @@ FindRegOutputPins::seqExpr2(Sequential *) PinSet findRegOutputPins(ClockSet *clks, - const RiseFallBoth *clk_rf, - bool edge_triggered, - bool latches, - StaState *sta) + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta) { FindRegOutputPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches); + return find_regs.findPins(clks, clk_rf, edge_triggered, latches, mode); } //////////////////////////////////////////////////////////////// @@ -713,8 +719,8 @@ static TimingSense path_sense_thru[timing_sense_count][timing_sense_count]; static void initPathSenseThru1(TimingSense from, - TimingSense thru, - TimingSense to) + TimingSense thru, + TimingSense to) { path_sense_thru[int(from)][int(thru)] = to; } @@ -723,63 +729,63 @@ void initPathSenseThru() { initPathSenseThru1(TimingSense::positive_unate, TimingSense::positive_unate, - TimingSense::positive_unate); + TimingSense::positive_unate); initPathSenseThru1(TimingSense::positive_unate, TimingSense::negative_unate, - TimingSense::negative_unate); + TimingSense::negative_unate); initPathSenseThru1(TimingSense::positive_unate, TimingSense::non_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::positive_unate, TimingSense::none, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::positive_unate, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::negative_unate, TimingSense::positive_unate, - TimingSense::negative_unate); + TimingSense::negative_unate); initPathSenseThru1(TimingSense::negative_unate, TimingSense::negative_unate, - TimingSense::positive_unate); + TimingSense::positive_unate); initPathSenseThru1(TimingSense::negative_unate, TimingSense::non_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::negative_unate, TimingSense::none, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::negative_unate, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::non_unate, TimingSense::positive_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::non_unate, TimingSense::negative_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::non_unate, TimingSense::non_unate, - TimingSense::non_unate); + TimingSense::non_unate); initPathSenseThru1(TimingSense::non_unate, TimingSense::none, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::non_unate, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::none, TimingSense::positive_unate, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::negative_unate, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::non_unate, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::none, - TimingSense::none); + TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::positive_unate, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::negative_unate, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::non_unate, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::none, - TimingSense::unknown); + TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::unknown, - TimingSense::unknown); + TimingSense::unknown); } static TimingSense pathSenseThru(TimingSense from_sense, - TimingSense thru_sense) + TimingSense thru_sense) { return path_sense_thru[int(from_sense)][int(thru_sense)]; } diff --git a/search/FindRegister.hh b/search/FindRegister.hh index f9820f9c7..96cd5062b 100644 --- a/search/FindRegister.hh +++ b/search/FindRegister.hh @@ -32,20 +32,40 @@ namespace sta { InstanceSet -findRegInstances(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegInstances(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta); PinSet -findRegDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegDataPins(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta); PinSet -findRegClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegClkPins(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta); PinSet -findRegAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegAsyncPins(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta); PinSet -findRegOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, bool latches, StaState *sta); +findRegOutputPins(ClockSet *clks, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + const Mode *mode, + const StaState *sta); void initPathSenseThru(); diff --git a/search/GatedClk.cc b/search/GatedClk.cc index 804cee9df..e7fa73fc1 100644 --- a/search/GatedClk.cc +++ b/search/GatedClk.cc @@ -29,8 +29,10 @@ #include "PortDirection.hh" #include "Network.hh" #include "Graph.hh" +#include "Mode.hh" #include "Sdc.hh" #include "Search.hh" +#include "ClkNetwork.hh" namespace sta { @@ -40,88 +42,87 @@ GatedClk::GatedClk(const StaState *sta) : } bool -GatedClk::isGatedClkEnable(Vertex *vertex) const +GatedClk::isGatedClkEnable(Vertex *vertex, + const Mode *mode) const { bool is_gated_clk_enable; const Pin *clk_pin; LogicValue logic_active_value; - isGatedClkEnable(vertex, - is_gated_clk_enable, clk_pin, logic_active_value); + isGatedClkEnable(vertex, mode, + is_gated_clk_enable, clk_pin, logic_active_value); return is_gated_clk_enable; } void GatedClk::isGatedClkEnable(Vertex *enable_vertex, - bool &is_gated_clk_enable, - const Pin *&clk_pin, - LogicValue &logic_active_value) const + const Mode *mode, + // Return values. + bool &is_gated_clk_enable, + const Pin *&clk_pin, + LogicValue &logic_active_value) const { is_gated_clk_enable = false; const Pin *enable_pin = enable_vertex->pin(); const Instance *inst = network_->instance(enable_pin); LibertyPort *enable_port = network_->libertyPort(enable_pin); EvalPred *eval_pred = search_->evalPred(); + ClkNetwork *clk_network = mode->clkNetwork(); if (enable_port + && !clk_network->isClock(enable_vertex) && enable_port->direction()->isInput() - && !sdc_->isDisableClockGatingCheck(enable_pin) - && !sdc_->isDisableClockGatingCheck(inst) - && eval_pred->searchFrom(enable_vertex)) { + && eval_pred->searchFrom(enable_vertex, mode)) { + const Sdc *sdc = mode->sdc(); FuncExpr *func = nullptr; Vertex *gclk_vertex = nullptr; VertexOutEdgeIterator edge_iter(enable_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); gclk_vertex = edge->to(graph_); + const Pin *gclk_pin = gclk_vertex->pin(); if (edge->role() == TimingRole::combinational() - && eval_pred->searchTo(gclk_vertex) - && eval_pred->searchThru(edge)) { - const Pin *gclk_pin = gclk_vertex->pin(); - LibertyPort *gclk_port = network_->libertyPort(gclk_pin); - if (gclk_port) { - func = gclk_port->function(); - if (func) - break; - } - } - } - if (func - && search_->isClock(gclk_vertex) - && !search_->isClock(enable_vertex)) { - FuncExprPortIterator clk_port_iter(func); - while (clk_port_iter.hasNext()) { - LibertyPort *clk_port = clk_port_iter.next(); - if (clk_port != enable_port) { - bool is_clk_gate = false; - isClkGatingFunc(func, enable_port, clk_port, - is_clk_gate, logic_active_value); - if (is_clk_gate) { - clk_pin = network_->findPin(inst, clk_port); - if (clk_pin - && !sdc_->isDisableClockGatingCheck(clk_pin) - && search_->isClock(graph_->pinLoadVertex(clk_pin))) { - is_gated_clk_enable = true; - break; - } - } - } + && eval_pred->searchTo(gclk_vertex, mode) + && eval_pred->searchThru(edge, mode)) { + LibertyPort *gclk_port = network_->libertyPort(gclk_pin); + func = gclk_port->function(); + if (gclk_port + && func + && clk_network->isClock(gclk_vertex)) { + LibertyPortSet clk_ports = func->ports(); + for (LibertyPort *clk_port : clk_ports) { + if (clk_port != enable_port) { + bool is_clk_gate = false; + isClkGatingFunc(func, enable_port, clk_port, + is_clk_gate, logic_active_value); + if (is_clk_gate) { + clk_pin = network_->findPin(inst, clk_port); + if (clk_pin + && !sdc->isDisableClockGatingCheck(clk_pin) + && clk_network->isClock(graph_->pinLoadVertex(clk_pin))) { + is_gated_clk_enable = true; + return; + } + } + } + } + } } } + } } -void +PinSet GatedClk::gatedClkEnables(Vertex *clk_vertex, - // Return value. - PinSet &enable_pins) + const Mode *mode) { + PinSet enable_pins(network_); const Pin *clk_pin = clk_vertex->pin(); const Instance *inst = network_->instance(clk_pin); LibertyPort *clk_port = network_->libertyPort(clk_pin); EvalPred *eval_pred = search_->evalPred(); if (clk_port - && !sdc_->isDisableClockGatingCheck(clk_pin) - && !sdc_->isDisableClockGatingCheck(inst) - && eval_pred->searchFrom(clk_vertex)) { + && eval_pred->searchFrom(clk_vertex, mode)) { + ClkNetwork *clk_network = mode->clkNetwork(); FuncExpr *func = nullptr; Vertex *gclk_vertex = nullptr; VertexOutEdgeIterator edge_iter(clk_vertex, graph_); @@ -129,56 +130,55 @@ GatedClk::gatedClkEnables(Vertex *clk_vertex, Edge *edge = edge_iter.next(); gclk_vertex = edge->to(graph_); if (edge->role() == TimingRole::combinational() - && eval_pred->searchTo(gclk_vertex) - && eval_pred->searchThru(edge)) { - const Pin *gclk_pin = gclk_vertex->pin(); - LibertyPort *gclk_port = network_->libertyPort(gclk_pin); - if (gclk_port) { - func = gclk_port->function(); - if (func) { - if (search_->isClock(gclk_vertex)) { - FuncExprPortIterator enable_port_iter(func); - while (enable_port_iter.hasNext()) { - LibertyPort *enable_port = enable_port_iter.next(); - if (enable_port != clk_port) { - bool is_clk_gate; - LogicValue logic_value; - isClkGatingFunc(func, enable_port, clk_port, - is_clk_gate, logic_value); - if (is_clk_gate) { - Pin *enable_pin = network_->findPin(inst, enable_port); - if (enable_pin - && !sdc_->isDisableClockGatingCheck(enable_pin) - && !search_->isClock(graph_->pinLoadVertex(enable_pin))) { - enable_pins.insert(enable_pin); - } - } - } - } - } - break; - } - } + && eval_pred->searchTo(gclk_vertex, mode) + && eval_pred->searchThru(edge, mode)) { + const Pin *gclk_pin = gclk_vertex->pin(); + LibertyPort *gclk_port = network_->libertyPort(gclk_pin); + if (gclk_port) { + func = gclk_port->function(); + if (func) { + if (clk_network->isClock(gclk_vertex)) { + LibertyPortSet enable_ports = func->ports(); + for (LibertyPort *enable_port : enable_ports) { + if (enable_port != clk_port) { + bool is_clk_gate; + LogicValue logic_value; + isClkGatingFunc(func, enable_port, clk_port, + is_clk_gate, logic_value); + if (is_clk_gate) { + Pin *enable_pin = network_->findPin(inst, enable_port); + if (enable_pin + && !clk_network->isClock(graph_->pinLoadVertex(enable_pin))) { + enable_pins.insert(enable_pin); + } + } + } + } + } + break; + } + } } } } + return enable_pins; } void GatedClk::isClkGatingFunc(FuncExpr *func, - LibertyPort *enable_port, - LibertyPort *clk_port, - bool &is_clk_gate, - LogicValue &logic_value) const + LibertyPort *enable_port, + LibertyPort *clk_port, + bool &is_clk_gate, + LogicValue &logic_value) const { // The function should be in two-level SOP or POS form depending on "cost". // We need to apply literal cofactor if any input port is constant and // do "simple" logic minimization based on SOP and POS. - while (func->op() == FuncExpr::op_not) + while (func->op() == FuncExpr::Op::not_) func = func->left(); - if (func->op() == FuncExpr::op_and) + if (func->op() == FuncExpr::Op::and_) logic_value = LogicValue::one; - else if (func->op() == FuncExpr::op_or) + else if (func->op() == FuncExpr::Op::or_) logic_value = LogicValue::zero; else { is_clk_gate = false; @@ -190,35 +190,32 @@ GatedClk::isClkGatingFunc(FuncExpr *func, functionClkOperands(func, func->right(), funcs); bool need_gating_check = false; - FuncExprSet::Iterator expr_iter(funcs); - while (expr_iter.hasNext()) { - FuncExpr *expr = expr_iter.next(); - if (expr->op() == FuncExpr::op_not) { - if (expr->left()->op() == FuncExpr::op_port - && expr->left()->port() == clk_port) { - need_gating_check = true; - logic_value = (logic_value == LogicValue::one) ? LogicValue::zero : LogicValue::one; + for (FuncExpr *expr : funcs) { + if (expr->op() == FuncExpr::Op::not_) { + if (expr->left()->op() == FuncExpr::Op::port + && expr->left()->port() == clk_port) { + need_gating_check = true; + logic_value = (logic_value == LogicValue::one) + ? LogicValue::zero + : LogicValue::one; } } else { - if (expr->op() == FuncExpr::op_port - && expr->port() == clk_port) { - need_gating_check = true; + if (expr->op() == FuncExpr::Op::port + && expr->port() == clk_port) { + need_gating_check = true; } } } if (need_gating_check) { - FuncExprSet::Iterator expr_iter2(funcs); - while (expr_iter2.hasNext()) { - FuncExpr *expr = expr_iter2.next(); - FuncExprPortIterator en_port_iter(expr); - while (en_port_iter.hasNext()) { - LibertyPort *port = en_port_iter.next(); - if (port == enable_port) { - is_clk_gate = true; - return; - } + for (FuncExpr *expr : funcs) { + LibertyPortSet ports = expr->ports(); + for (LibertyPort *port : ports) { + if (port == enable_port) { + is_clk_gate = true; + return; + } } } } @@ -227,8 +224,8 @@ GatedClk::isClkGatingFunc(FuncExpr *func, void GatedClk::functionClkOperands(FuncExpr *root_expr, - FuncExpr *expr, - FuncExprSet &funcs) const + FuncExpr *expr, + FuncExprSet &funcs) const { if (expr->op() != root_expr->op()) funcs.insert(expr); @@ -240,7 +237,7 @@ GatedClk::functionClkOperands(FuncExpr *root_expr, const RiseFall * GatedClk::gatedClkActiveTrans(LogicValue active_value, - const MinMax *min_max) const + const MinMax *min_max) const { const RiseFall *leading_rf; switch (active_value) { diff --git a/search/GatedClk.hh b/search/GatedClk.hh index 8f63307d5..6cec6bc48 100644 --- a/search/GatedClk.hh +++ b/search/GatedClk.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "SdcClass.hh" #include "GraphClass.hh" #include "SearchClass.hh" @@ -31,33 +33,35 @@ namespace sta { -typedef Set FuncExprSet; +using FuncExprSet = std::set; class GatedClk : public StaState { public: GatedClk(const StaState *sta); - bool isGatedClkEnable(Vertex *vertex) const; + bool isGatedClkEnable(Vertex *vertexm, + const Mode *mode) const; void isGatedClkEnable(Vertex *enable_vertex, - bool &is_gated_clk_enable, - const Pin *&clk_pin, - LogicValue &logic_active_value) const; - void gatedClkEnables(Vertex *clk_vertex, - // Return value. - PinSet &enable_pins); + const Mode *mode, + // Return values. + bool &is_gated_clk_enable, + const Pin *&clk_pin, + LogicValue &logic_active_value) const; + PinSet gatedClkEnables(Vertex *clk_vertex, + const Mode *mode); const RiseFall *gatedClkActiveTrans(LogicValue active_value, const MinMax *min_max) const; protected: void isClkGatingFunc(FuncExpr *func, - LibertyPort *enable_port, - LibertyPort *clk_port, - bool &is_clk_gate, - LogicValue &logic_value) const; + LibertyPort *enable_port, + LibertyPort *clk_port, + bool &is_clk_gate, + LogicValue &logic_value) const; void functionClkOperands(FuncExpr *root_expr, - FuncExpr *curr_expr, - FuncExprSet &funcs) const; + FuncExpr *curr_expr, + FuncExprSet &funcs) const; }; } // namespace diff --git a/search/Genclks.cc b/search/Genclks.cc index e002c4c78..66b8cd143 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -24,6 +24,7 @@ #include "Genclks.hh" +#include "ContainerHelpers.hh" #include "Stats.hh" #include "Debug.hh" #include "Report.hh" @@ -31,14 +32,14 @@ #include "PortDirection.hh" #include "Graph.hh" #include "Sdc.hh" +#include "Mode.hh" #include "ExceptionPath.hh" #include "Clock.hh" #include "StaState.hh" #include "SearchPred.hh" #include "Bfs.hh" #include "TagGroup.hh" -#include "Corner.hh" -#include "PathAnalysisPt.hh" +#include "Scene.hh" #include "Levelize.hh" #include "Path.hh" #include "Search.hh" @@ -52,35 +53,33 @@ class GenclkInfo { public: GenclkInfo(Clock *gclk, - Level gclk_level, - VertexSet *fanins, - FilterPath *src_filter); + Level gclk_level, + FilterPath *src_filter, + const StaState *sta); ~GenclkInfo(); - EdgeSet *fdbkEdges() const { return fdbk_edges_; } - VertexSet *fanins() const { return fanins_; } + EdgeSet &fdbkEdges() { return fdbk_edges_; } + VertexSet &fanins() { return fanins_; } Level gclkLevel() const { return gclk_level_; } FilterPath *srcFilter() const { return src_filter_; } - void setLatchFdbkEdges(EdgeSet *fdbk_edges); bool foundLatchFdbkEdges() const { return found_latch_fdbk_edges_; } void setFoundLatchFdbkEdges(bool found); protected: Clock *gclk_; Level gclk_level_; - VertexSet *fanins_; - EdgeSet *fdbk_edges_; + VertexSet fanins_; + EdgeSet fdbk_edges_; bool found_latch_fdbk_edges_; FilterPath *src_filter_; }; GenclkInfo::GenclkInfo(Clock *gclk, - Level gclk_level, - VertexSet *fanins, - FilterPath *src_filter) : + Level gclk_level, + FilterPath *src_filter, + const StaState *sta) : gclk_(gclk), gclk_level_(gclk_level), - fanins_(fanins), - fdbk_edges_(nullptr), + fanins_(makeVertexSet(sta->graph())), found_latch_fdbk_edges_(false), src_filter_(src_filter) { @@ -88,17 +87,9 @@ GenclkInfo::GenclkInfo(Clock *gclk, GenclkInfo::~GenclkInfo() { - delete fanins_; - delete fdbk_edges_; delete src_filter_; } -void -GenclkInfo::setLatchFdbkEdges(EdgeSet *fdbk_edges) -{ - fdbk_edges_ = fdbk_edges; -} - void GenclkInfo::setFoundLatchFdbkEdges(bool found) { @@ -107,8 +98,10 @@ GenclkInfo::setFoundLatchFdbkEdges(bool found) //////////////////////////////////////////////////////////////// -Genclks::Genclks(StaState *sta) : +Genclks::Genclks(const Mode *mode, + StaState *sta) : StaState(sta), + mode_(mode), found_insertion_delays_(false), vertex_src_paths_map_(graph_) { @@ -116,7 +109,7 @@ Genclks::Genclks(StaState *sta) : Genclks::~Genclks() { - genclk_info_map_.deleteContentsClear(); + deleteContents(genclk_info_map_); clearSrcPaths(); } @@ -124,8 +117,7 @@ void Genclks::clear() { found_insertion_delays_ = false; - genclk_info_map_.deleteContentsClear(); - vertex_src_paths_map_.clear(); + deleteContents(genclk_info_map_); clearSrcPaths(); } @@ -134,7 +126,7 @@ Genclks::fanins(const Clock *clk) { GenclkInfo *genclk_info = genclkInfo(clk); if (genclk_info) - return genclk_info->fanins(); + return &genclk_info->fanins(); else return nullptr; } @@ -167,9 +159,9 @@ Genclks::clkPinMaxLevel(const Clock *clk) const class ClockPinMaxLevelLess { public: - explicit ClockPinMaxLevelLess(const Genclks *genclks); + ClockPinMaxLevelLess(const Genclks *genclks); bool operator()(Clock *clk1, - Clock *clk2) const; + Clock *clk2) const; protected: const Genclks *genclks_; @@ -182,7 +174,7 @@ ClockPinMaxLevelLess::ClockPinMaxLevelLess(const Genclks *genclks) : bool ClockPinMaxLevelLess::operator()(Clock *clk1, - Clock *clk2) const + Clock *clk2) const { return genclks_->clkPinMaxLevel(clk1) < genclks_->clkPinMaxLevel(clk2); } @@ -198,106 +190,79 @@ Genclks::ensureInsertionDelays() Stats stats(debug_, report_); debugPrint(debug_, "genclk", 1, "find generated clk insertion delays"); + clearSrcPaths(); + Sdc *sdc = mode_->sdc(); ClockSeq gclks; - for (auto clk : sdc_->clks()) { + for (Clock *clk : sdc->clocks()) { if (clk->isGenerated()) { - checkMaster(clk); - gclks.push_back(clk); + checkMaster(clk, sdc); + gclks.push_back(clk); } } - clearSrcPaths(); - // 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()) { - findInsertionDelays(gclk); - recordSrcPaths(gclk); + findInsertionDelays(gclk); + recordSrcPaths(gclk); } } - stats.report("Find generated clk insertion delays"); found_insertion_delays_ = true; } } -// Similar to ClkTreeSearchPred but ignore constants. -class GenClkMasterSearchPred : public SearchPred +//////////////////////////////////////////////////////////////// + +class GenClkMasterSearchPred : public ClkTreeSearchPred { public: - explicit GenClkMasterSearchPred(const StaState *sta); - virtual bool searchFrom(const Vertex *from_vertex); - virtual bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); - -protected: - const StaState *sta_; + GenClkMasterSearchPred(const StaState *sta); + bool searchThruAllow(const TimingRole *role) const override; }; GenClkMasterSearchPred::GenClkMasterSearchPred(const StaState *sta) : - SearchPred(), - sta_(sta) + ClkTreeSearchPred(sta) { } bool -GenClkMasterSearchPred::searchFrom(const Vertex *from_vertex) +GenClkMasterSearchPred::searchThruAllow(const TimingRole *role) const { - return !from_vertex->isDisabledConstraint(); + return (role->isWire() + || role == TimingRole::combinational() + || role->regClkToQ()); } -bool -GenClkMasterSearchPred::searchThru(Edge *edge) -{ - const Variables *variables = sta_->variables(); - const TimingRole *role = edge->role(); - // Propagate clocks through constants. - return !(edge->role()->isTimingCheck() - || edge->isDisabledLoop() - || edge->isDisabledConstraint() - // Constants disable edge cond expression. - || edge->isDisabledCond() - || sta_->isDisabledCondDefault(edge) - // Register/latch preset/clr edges are disabled by default. - || (!variables->presetClrArcsEnabled() - && role == TimingRole::regSetClr()) - || (edge->isBidirectInstPath() - && !variables->bidirectInstPathsEnabled()) - || (edge->isBidirectNetPath() - && !variables->bidirectNetPathsEnabled())); -} - -bool -GenClkMasterSearchPred::searchTo(const Vertex *) -{ - return true; -} +//////////////////////////////////////////////////////////////// void -Genclks::checkMaster(Clock *gclk) +Genclks::checkMaster(Clock *gclk, + const Sdc *sdc) { - ensureMaster(gclk); + ensureMaster(gclk, sdc); if (gclk->masterClk() == nullptr) report_->warn(1060, "no master clock found for generated clock %s.", - gclk->name()); + gclk->name()); } void -Genclks::ensureMaster(Clock *gclk) +Genclks::ensureMaster(Clock *gclk, + const Sdc *sdc) { Clock *master_clk = gclk->masterClk(); if (master_clk == nullptr) { int master_clk_count = 0; bool found_master = false; Pin *src_pin = gclk->srcPin(); - ClockSet *master_clks = sdc_->findClocks(src_pin); - ClockSet::Iterator master_iter(master_clks); - if (master_iter.hasNext()) { - while (master_iter.hasNext()) { - master_clk = master_iter.next(); + ClockSet *master_clks = sdc->findClocks(src_pin); + if (master_clks) { + ClockSet::iterator master_iter = master_clks->begin(); + while (master_iter != master_clks->end()) { + master_clk = *master_iter++; // Master source pin can actually be a clock source pin. if (master_clk != gclk) { gclk->setInferedMasterClk(master_clk); @@ -317,12 +282,12 @@ Genclks::ensureMaster(Clock *gclk) while (iter.hasNext()) { Vertex *vertex = iter.next(); Pin *pin = vertex->pin(); - if (sdc_->isLeafPinClock(pin)) { - ClockSet *master_clks = sdc_->findLeafPinClocks(pin); + if (sdc->isLeafPinClock(pin)) { + ClockSet *master_clks = sdc->findLeafPinClocks(pin); if (master_clks) { - ClockSet::Iterator master_iter(master_clks); - if (master_iter.hasNext()) { - master_clk = master_iter.next(); + ClockSet::iterator master_iter = master_clks->begin(); + if (master_iter != master_clks->end()) { + master_clk = *master_iter++; // Master source pin can actually be a clock source pin. if (master_clk != gclk) { gclk->setInferedMasterClk(master_clk); @@ -335,7 +300,7 @@ Genclks::ensureMaster(Clock *gclk) } } } - iter.enqueueAdjacentVertices(vertex); + iter.enqueueAdjacentVertices(vertex, mode_); } } if (master_clk_count > 1) @@ -348,9 +313,9 @@ Genclks::ensureMaster(Clock *gclk) void Genclks::seedSrcPins(Clock *clk, - BfsBkwdIterator &iter) + BfsBkwdIterator &iter) { - VertexSet src_vertices(graph_); + VertexSet src_vertices = makeVertexSet(this); clk->srcPinVertices(src_vertices, network_, graph_); for (Vertex *vertex : src_vertices) iter.enqueue(vertex); @@ -359,55 +324,36 @@ Genclks::seedSrcPins(Clock *clk, //////////////////////////////////////////////////////////////// // Similar to ClkTreeSearchPred but -// search thru constants // respect generated clock combinational attribute -// search thru disabled loop arcs class GenClkFaninSrchPred : public GenClkMasterSearchPred { public: - explicit GenClkFaninSrchPred(Clock *gclk, - const StaState *sta); - virtual bool searchFrom(const Vertex *from_vertex); - virtual bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); + GenClkFaninSrchPred(Clock *gclk, + const StaState *sta); + bool searchThruAllow(const TimingRole *role) const override; private: bool combinational_; }; GenClkFaninSrchPred::GenClkFaninSrchPred(Clock *gclk, - const StaState *sta) : + const StaState *sta) : GenClkMasterSearchPred(sta), combinational_(gclk->combinational()) { } bool -GenClkFaninSrchPred::searchFrom(const Vertex *from_vertex) +GenClkFaninSrchPred::searchThruAllow(const TimingRole *role) const { - return !from_vertex->isDisabledConstraint(); -} - -bool -GenClkFaninSrchPred::searchThru(Edge *edge) -{ - const TimingRole *role = edge->role(); - return GenClkMasterSearchPred::searchThru(edge) - && (role == TimingRole::combinational() - || role == TimingRole::wire() - || !combinational_); -} - -bool -GenClkFaninSrchPred::searchTo(const Vertex *) -{ - return true; + return (role == TimingRole::combinational() + || role == TimingRole::wire() + || !combinational_); } void Genclks::findFanin(Clock *gclk, - // Return value. - VertexSet *fanins) + VertexSet &fanins) { // Search backward from generated clock source pin to a clock pin. GenClkFaninSrchPred srch_pred(gclk, this); @@ -415,102 +361,98 @@ Genclks::findFanin(Clock *gclk, seedClkVertices(gclk, iter, fanins); while (iter.hasNext()) { Vertex *vertex = iter.next(); - if (!fanins->hasKey(vertex)) { - fanins->insert(vertex); + if (!fanins.contains(vertex)) { + fanins.insert(vertex); debugPrint(debug_, "genclk", 2, "gen clk %s fanin %s", gclk->name(), vertex->to_string(this).c_str()); - iter.enqueueAdjacentVertices(vertex); + iter.enqueueAdjacentVertices(vertex, mode_); } } } void Genclks::seedClkVertices(Clock *clk, - BfsBkwdIterator &iter, - VertexSet *fanins) + BfsBkwdIterator &iter, + VertexSet &fanins) { for (const Pin *pin : clk->leafPins()) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - fanins->insert(vertex); - iter.enqueueAdjacentVertices(vertex); + fanins.insert(vertex); + iter.enqueueAdjacentVertices(vertex, mode_); if (bidirect_drvr_vertex) { - fanins->insert(bidirect_drvr_vertex); - iter.enqueueAdjacentVertices(bidirect_drvr_vertex); + fanins.insert(bidirect_drvr_vertex); + iter.enqueueAdjacentVertices(bidirect_drvr_vertex, mode_); } } } //////////////////////////////////////////////////////////////// -class GenClkInsertionSearchPred : public SearchPred0, public DynLoopSrchPred +class GenClkInsertionSearchPred : public SearchPred0 { public: GenClkInsertionSearchPred(Clock *gclk, - TagGroupBldr *tag_bldr, - GenclkInfo *genclk_info, - const StaState *sta); - virtual bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); + GenclkInfo *genclk_info, + const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; private: - bool isNonGeneratedClkPin(const Pin *pin) const; + bool isNonGeneratedClkPin(const Pin *pin, + const Sdc *sdc) const; Clock *gclk_; GenclkInfo *genclk_info_; }; GenClkInsertionSearchPred::GenClkInsertionSearchPred(Clock *gclk, - TagGroupBldr *tag_bldr, - GenclkInfo *genclk_info, - const StaState *sta) : + GenclkInfo *genclk_info, + const StaState *sta) : SearchPred0(sta), - DynLoopSrchPred(tag_bldr), gclk_(gclk), genclk_info_(genclk_info) { } bool -GenClkInsertionSearchPred::searchThru(Edge *edge) +GenClkInsertionSearchPred::searchThru(Edge *edge, + const Mode *mode) const { - const Graph *graph = sta_->graph(); - const Sdc *sdc = sta_->sdc(); - Search *search = sta_->search(); const TimingRole *role = edge->role(); - EdgeSet *fdbk_edges = genclk_info_->fdbkEdges(); - return SearchPred0::searchThru(edge) + EdgeSet &fdbk_edges = genclk_info_->fdbkEdges(); + return SearchPred0::searchThru(edge, mode) && !role->isTimingCheck() && (sta_->variables()->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())) - && !(fdbk_edges && fdbk_edges->hasKey(edge)) - && loopEnabled(edge, sdc, graph, search); + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())) + && !fdbk_edges.contains(edge); } bool -GenClkInsertionSearchPred::searchTo(const Vertex *to_vertex) +GenClkInsertionSearchPred::searchTo(const Vertex *to_vertex, + const Mode *mode) const { Pin *to_pin = to_vertex->pin(); - return SearchPred0::searchTo(to_vertex) + return SearchPred0::searchTo(to_vertex, mode) // Propagate through other generated clock roots but not regular // clock roots. - && !(!gclk_->leafPins().hasKey(to_pin) - && isNonGeneratedClkPin(to_pin)) - && genclk_info_->fanins()->hasKey(const_cast(to_vertex)); + && !(!gclk_->leafPins().contains(to_pin) + && isNonGeneratedClkPin(to_pin, mode->sdc())) + && genclk_info_->fanins().contains(const_cast(to_vertex)); } bool -GenClkInsertionSearchPred::isNonGeneratedClkPin(const Pin *pin) const +GenClkInsertionSearchPred::isNonGeneratedClkPin(const Pin *pin, + const Sdc *sdc) const { - const Sdc *sdc = sta_->sdc(); ClockSet *clks = sdc->findLeafPinClocks(pin); if (clks) { - ClockSet::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - const Clock *clk = clk_iter.next(); + for (const Clock *clk : *clks) { if (!clk->isGenerated()) - return true; + return true; } } return false; @@ -525,7 +467,7 @@ Genclks::findInsertionDelays(Clock *gclk) gclk->name()); GenclkInfo *genclk_info = makeGenclkInfo(gclk); FilterPath *src_filter = genclk_info->srcFilter(); - GenClkInsertionSearchPred srch_pred(gclk, nullptr, genclk_info, this); + GenClkInsertionSearchPred srch_pred(gclk, genclk_info, this); BfsFwdIterator insert_iter(BfsIndex::other, &srch_pred, this); seedSrcPins(gclk, src_filter, insert_iter); // Propagate arrivals to generated clk root pin level. @@ -533,46 +475,41 @@ Genclks::findInsertionDelays(Clock *gclk) // Unregister the filter so that it is not triggered by other searches. // The exception itself has to stick around because the source path // tags reference it. - sdc_->unrecordException(src_filter); + mode_->sdc()->unrecordException(src_filter); } GenclkInfo * Genclks::makeGenclkInfo(Clock *gclk) { - FilterPath *src_filter = makeSrcFilter(gclk); + FilterPath *src_filter = makeSrcFilter(gclk, mode_->sdc()); Level gclk_level = clkPinMaxLevel(gclk); - VertexSet *fanins = new VertexSet(graph_); - findFanin(gclk, fanins); - GenclkInfo *genclk_info = new GenclkInfo(gclk, gclk_level, fanins, - src_filter); - genclk_info_map_.insert(gclk, genclk_info); + GenclkInfo *genclk_info = new GenclkInfo(gclk, gclk_level, src_filter, this); + findFanin(gclk, genclk_info->fanins()); + genclk_info_map_[gclk] = genclk_info; return genclk_info; } GenclkInfo * Genclks::genclkInfo(const Clock *gclk) const { - return genclk_info_map_.findKey(const_cast(gclk)); + return findKey(genclk_info_map_, const_cast(gclk)); } FilterPath * Genclks::srcFilter(Clock *gclk) { - GenclkInfo *genclk_info = genclk_info_map_.findKey(gclk); + GenclkInfo *genclk_info = findKey(genclk_info_map_, gclk); if (genclk_info) return genclk_info->srcFilter(); else return nullptr; } -EdgeSet * +EdgeSet & Genclks::latchFdbkEdges(const Clock *clk) { GenclkInfo *genclk_info = genclkInfo(clk); - if (genclk_info) - return genclk_info->fdbkEdges(); - else - return nullptr; + return genclk_info->fdbkEdges(); } void @@ -595,79 +532,77 @@ Genclks::findLatchFdbkEdges(const Clock *clk) // D to Q edge is encountered in the BFS arrival search. void Genclks::findLatchFdbkEdges(const Clock *gclk, - GenclkInfo *genclk_info) + GenclkInfo *genclk_info) { Level gclk_level = genclk_info->gclkLevel(); - EdgeSet *fdbk_edges = nullptr; + EdgeSet &fdbk_edges = genclk_info->fdbkEdges(); for (const Pin *pin : gclk->masterClk()->leafPins()) { Vertex *vertex = graph_->pinDrvrVertex(pin); - VertexSet path_vertices(graph_); - VertexSet visited_vertices(graph_); + VertexSet path_vertices = makeVertexSet(this); + VertexSet visited_vertices = makeVertexSet(this); SearchPred1 srch_pred(this); findLatchFdbkEdges(vertex, gclk_level, srch_pred, path_vertices, - visited_vertices, fdbk_edges); + visited_vertices, fdbk_edges); } - genclk_info->setLatchFdbkEdges(fdbk_edges); genclk_info->setFoundLatchFdbkEdges(true); } void Genclks::findLatchFdbkEdges(Vertex *from_vertex, - Level gclk_level, - SearchPred &srch_pred, - VertexSet &path_vertices, - VertexSet &visited_vertices, - EdgeSet *&fdbk_edges) + Level gclk_level, + SearchPred &srch_pred, + VertexSet &path_vertices, + VertexSet &visited_vertices, + EdgeSet &fdbk_edges) { - if (!visited_vertices.hasKey(from_vertex)) { + if (!visited_vertices.contains(from_vertex)) { visited_vertices.insert(from_vertex); path_vertices.insert(from_vertex); VertexOutEdgeIterator edge_iter(from_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); - if (path_vertices.hasKey(to_vertex)) { - debugPrint(debug_, "genclk", 2, " found feedback edge %s", + if (path_vertices.contains(to_vertex)) { + debugPrint(debug_, "genclk", 2, " found feedback edge %s", edge->to_string(this).c_str()); - if (fdbk_edges == nullptr) - fdbk_edges = new EdgeSet; - fdbk_edges->insert(edge); + fdbk_edges.insert(edge); } - else if (srch_pred.searchThru(edge) - && srch_pred.searchTo(to_vertex) - && to_vertex->level() <= gclk_level) - findLatchFdbkEdges(to_vertex, gclk_level, srch_pred, - path_vertices, visited_vertices, fdbk_edges); + 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); } path_vertices.erase(from_vertex); } } FilterPath * -Genclks::makeSrcFilter(Clock *gclk) +Genclks::makeSrcFilter(Clock *gclk, + Sdc *sdc) { 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); ClockSet *to_clks = new ClockSet; to_clks->insert(gclk); - ExceptionTo *to = sdc_->makeExceptionTo(nullptr, to_clks, nullptr, rf, rf); + ExceptionTo *to = sdc->makeExceptionTo(nullptr, to_clks, nullptr, rf, rf); - return sdc_->makeFilterPath(from, thrus, to); + return sdc->makeFilterPath(from, thrus, to); } void Genclks::seedSrcPins(Clock *gclk, - FilterPath *src_filter, - BfsFwdIterator &insert_iter) + FilterPath *src_filter, + BfsFwdIterator &insert_iter) { Clock *master_clk = gclk->masterClk(); for (const Pin *master_pin : master_clk->leafPins()) { @@ -678,31 +613,33 @@ Genclks::seedSrcPins(Clock *gclk, TagGroupBldr tag_bldr(true, this); tag_bldr.init(vertex); copyGenClkSrcPaths(vertex, &tag_bldr); - for (auto path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); - const EarlyLate *early_late = min_max; - for (const RiseFall *rf : RiseFall::range()) { - Arrival insert = search_->clockInsertion(master_clk, master_pin, rf, - min_max, early_late, path_ap); - Tag *tag = makeTag(gclk, master_clk, master_pin, rf, - src_filter, insert, path_ap); - tag_bldr.setArrival(tag, insert); + for (Scene *scene : mode_->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + const EarlyLate *early_late = min_max; + 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_bldr.setArrival(tag, insert); + } } + search_->setVertexArrivals(vertex, &tag_bldr); + insert_iter.enqueueAdjacentVertices(vertex, mode_); } - search_->setVertexArrivals(vertex, &tag_bldr); - insert_iter.enqueueAdjacentVertices(vertex); } } } Tag * Genclks::makeTag(const Clock *gclk, - const Clock *master_clk, - const Pin *master_pin, - const RiseFall *master_rf, - FilterPath *src_filter, + const Clock *master_clk, + const Pin *master_pin, + const RiseFall *master_rf, + FilterPath *src_filter, Arrival insert, - const PathAnalysisPt *path_ap) + Scene *scene, + const MinMax *min_max) { ExceptionState *state = src_filter->firstState(); // If the src pin is one of the master pins the filter is active @@ -711,101 +648,115 @@ Genclks::makeTag(const Clock *gclk, state = state->nextState(); ExceptionStateSet *states = new ExceptionStateSet(); states->insert(state); - const ClkInfo *clk_info = search_->findClkInfo(master_clk->edge(master_rf), - master_pin, true, nullptr, true, - nullptr, insert, 0.0, nullptr, - path_ap, nullptr); - return search_->findTag(master_rf, path_ap, 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 { public: GenClkArrivalSearchPred(Clock *gclk, - const StaState *sta); - bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); + const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; private: bool combinational_; }; GenClkArrivalSearchPred::GenClkArrivalSearchPred(Clock *gclk, - const StaState *sta) : + const StaState *sta) : EvalPred(sta), combinational_(gclk->combinational()) { } bool -GenClkArrivalSearchPred::searchThru(Edge *edge) +GenClkArrivalSearchPred::searchThru(Edge *edge, + const Mode *mode) const { const TimingRole *role = edge->role(); - return EvalPred::searchThru(edge) + return EvalPred::searchThru(edge, mode) && (role == TimingRole::combinational() - || role->isWire() - || !combinational_) + || role->isWire() + || !combinational_) && (sta_->variables()->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())); + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())); } // Override EvalPred::searchTo to search to generated clock pin. bool -GenClkArrivalSearchPred::searchTo(const Vertex *to_vertex) +GenClkArrivalSearchPred::searchTo(const Vertex *to_vertex, + const Mode *mode) const { - return SearchPred0::searchTo(to_vertex); + return SearchPred0::searchTo(to_vertex, mode); } class GenclkSrcArrivalVisitor : public ArrivalVisitor { public: GenclkSrcArrivalVisitor(Clock *gclk, - BfsFwdIterator *insert_iter, - GenclkInfo *genclk_info, - const StaState *sta); + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + const Mode *mode); virtual VertexVisitor *copy() const; virtual void visit(Vertex *vertex); protected: GenclkSrcArrivalVisitor(Clock *gclk, - BfsFwdIterator *insert_iter, - GenclkInfo *genclk_info, - bool always_to_endpoints, - SearchPred *pred, - const StaState *sta); + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + bool always_to_endpoints, + SearchPred *pred, + const Mode *mode); Clock *gclk_; BfsFwdIterator *insert_iter_; GenclkInfo *genclk_info_; GenClkInsertionSearchPred srch_pred_; + const Mode *mode_; + const Sdc *sdc_; + Genclks *genclks_; }; GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, - BfsFwdIterator *insert_iter, - GenclkInfo *genclk_info, - const StaState *sta): - ArrivalVisitor(sta), + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + const Mode *mode): + ArrivalVisitor(mode), gclk_(gclk), insert_iter_(insert_iter), genclk_info_(genclk_info), - srch_pred_(gclk_, tag_bldr_, genclk_info, sta) + srch_pred_(gclk_, genclk_info, mode), + mode_(mode), + sdc_(mode->sdc()), + genclks_(mode->genclks()) { } // Copy constructor. GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, - BfsFwdIterator *insert_iter, - GenclkInfo *genclk_info, - bool always_to_endpoints, - SearchPred *pred, - const StaState *sta) : - ArrivalVisitor(always_to_endpoints, pred, sta), + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + bool always_to_endpoints, + SearchPred *pred, + const Mode *mode) : + ArrivalVisitor(always_to_endpoints, pred, mode), gclk_(gclk), insert_iter_(insert_iter), genclk_info_(genclk_info), - srch_pred_(gclk, tag_bldr_, genclk_info, sta) + srch_pred_(gclk, genclk_info, mode), + mode_(mode), + sdc_(mode->sdc()), + genclks_(mode->genclks()) { } @@ -813,33 +764,32 @@ VertexVisitor * GenclkSrcArrivalVisitor::copy() const { return new GenclkSrcArrivalVisitor(gclk_, insert_iter_, genclk_info_, - always_to_endpoints_, pred_, this); + always_to_endpoints_, pred_, mode_); } void GenclkSrcArrivalVisitor::visit(Vertex *vertex) { - Genclks *genclks = search_->genclks(); debugPrint(debug_, "genclk", 2, "find gen clk insert arrival %s", vertex->to_string(this).c_str()); tag_bldr_->init(vertex); has_fanin_one_ = graph_->hasFaninOne(vertex); - genclks->copyGenClkSrcPaths(vertex, tag_bldr_); + genclks_->copyGenClkSrcPaths(vertex, tag_bldr_); visitFaninPaths(vertex); // Propagate beyond the clock tree to reach generated clk roots. - insert_iter_->enqueueAdjacentVertices(vertex, &srch_pred_); + insert_iter_->enqueueAdjacentVertices(vertex, &srch_pred_, mode_); search_->setVertexArrivals(vertex, tag_bldr_); } void Genclks::findSrcArrivals(Clock *gclk, - BfsFwdIterator &insert_iter, - GenclkInfo *genclk_info) + BfsFwdIterator &insert_iter, + GenclkInfo *genclk_info) { GenClkArrivalSearchPred eval_pred(gclk, this); GenclkSrcArrivalVisitor arrival_visitor(gclk, &insert_iter, - genclk_info, this); - arrival_visitor.init(true, &eval_pred); + 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. // Parallel visit is slightly slower (at last check). @@ -849,7 +799,7 @@ Genclks::findSrcArrivals(Clock *gclk, // Copy generated clock source paths to tag_bldr. void Genclks::copyGenClkSrcPaths(Vertex *vertex, - TagGroupBldr *tag_bldr) + TagGroupBldr *tag_bldr) { auto itr = vertex_src_paths_map_.find(vertex); if (itr != vertex_src_paths_map_.end()) { @@ -863,8 +813,8 @@ Genclks::copyGenClkSrcPaths(Vertex *vertex, } debugPrint(debug_, "genclk", 3, "vertex %s insert genclk %s src path %s %ss", src_path.vertex(this)->to_string(this).c_str(), - src_path.tag(this)->genClkSrcPathClk(this)->name(), - src_path.tag(this)->pathAnalysisPt(this)->pathMinMax()->to_string().c_str(), + 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()); tag_bldr->insertPath(src_path); } @@ -876,25 +826,25 @@ Genclks::copyGenClkSrcPaths(Vertex *vertex, void Genclks::clearSrcPaths() { - for (auto const & [clk_pin, src_paths] : genclk_src_paths_) { - for (const Path &src_path : src_paths) - delete src_path.prevPath(); + for (auto [vertex, paths] : vertex_src_paths_map_) { + for (const Path *path : paths) + delete path; } + vertex_src_paths_map_.clear(); genclk_src_paths_.clear(); } size_t Genclks::srcPathIndex(const RiseFall *clk_rf, - const PathAnalysisPt *path_ap) const + const MinMax *min_max) const { - return path_ap->index() * RiseFall::index_count + clk_rf->index(); + return min_max->index() * RiseFall::index_count + clk_rf->index(); } void Genclks::recordSrcPaths(Clock *gclk) { - int path_count = RiseFall::index_count - * corners_->pathAnalysisPtCount(); + size_t path_count = RiseFall::index_count * MinMax::index_count; bool divide_by_1 = gclk->isDivideByOneCombinational(); bool invert = gclk->invert(); @@ -904,70 +854,64 @@ Genclks::recordSrcPaths(Clock *gclk) std::vector &src_paths = genclk_src_paths_[ClockPinPair(gclk, gclk_pin)]; src_paths.resize(path_count); Vertex *gclk_vertex = srcPath(gclk_pin); - bool found_src_paths = false; VertexPathIterator path_iter(gclk_vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const ClockEdge *src_clk_edge = path->clkEdge(this); 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); - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - size_t path_index = srcPathIndex(rf, path_ap); - Path &src_path = src_paths[path_index]; - if ((!divide_by_1 + && 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)) - && (src_path.isNull() - || delayGreater(path->arrival(), - src_path.arrival(), - early_late, - this))) { - debugPrint(debug_, "genclk", 2, " %s insertion %s %s %s", + && (!has_edges + || src_clk_rf == gclk->masterClkEdgeTr(rf)) + && (src_path.isNull() + || 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->to_string().c_str(), delayAsString(path->arrival(), this)); - // If this path is replacing another one delete the previous one. - delete src_path.prevPath(); src_path = *path; - Path *prev_copy = &src_path; - Path *p = path->prevPath(); - while (p) { - Path *copy = new Path(p); - copy->setIsEnum(true); - prev_copy->setPrevPath(copy); - prev_copy = copy; - p = p->prevPath(); - } - found_src_paths = true; - } + } } } - if (found_src_paths) { - // Record vertex->genclk src paths. - for (const Path &path : src_paths) { - if (!path.isNull()) { - const Path *p = &path; - while (p && !p->isNull()) { - Vertex *vertex = p->vertex(this); - vertex_src_paths_map_[vertex].push_back(p); - p = p->prevPath(); - } + // Record vertex->genclk src paths. + bool found_src_paths = false; + for (size_t path_index = 0; path_index < path_count; path_index++) { + Path &src_path = src_paths[path_index]; + if (!src_path.isNull()) { + Path *prev_copy = &src_path; + const Path *p = src_path.prevPath(); + while (p) { + Path *copy = new Path(p); + copy->setIsEnum(true); + prev_copy->setPrevPath(copy); + prev_copy = copy; + + Vertex *vertex = p->vertex(this); + vertex_src_paths_map_[vertex].push_back(copy); + p = p->prevPath(); } + found_src_paths = true; } } // Don't warn if the master clock is ideal. - else if (gclk->masterClk() - && gclk->masterClk()->isPropagated()) + 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()); + gclk->name(), + network_->pathName(gclk_pin), + gclk->masterClk()->name()); } deleteGenclkSrcPaths(gclk); } @@ -976,7 +920,7 @@ void Genclks:: deleteGenclkSrcPaths(Clock *gclk) { GenclkInfo *genclk_info = genclkInfo(gclk); - GenClkInsertionSearchPred srch_pred(gclk, nullptr, genclk_info, this); + GenClkInsertionSearchPred srch_pred(gclk, genclk_info, mode_); BfsFwdIterator insert_iter(BfsIndex::other, &srch_pred, this); FilterPath *src_filter = genclk_info->srcFilter(); seedSrcPins(gclk, src_filter, insert_iter); @@ -984,27 +928,25 @@ Genclks:: deleteGenclkSrcPaths(Clock *gclk) while (insert_iter.hasNext()) { Vertex *vertex = insert_iter.next(); search_->deletePaths(vertex); - insert_iter.enqueueAdjacentVertices(vertex, &srch_pred); + insert_iter.enqueueAdjacentVertices(vertex, &srch_pred, mode_); } } bool Genclks::matchesSrcFilter(Path *path, - const Clock *gclk) const + const Clock *gclk) const { Tag *tag = path->tag(this); const ExceptionStateSet *states = tag->states(); if (tag->isGenClkSrcPath() && states) { - ExceptionStateSet::ConstIterator state_iter(states); - while (state_iter.hasNext()) { - ExceptionState *state = state_iter.next(); + for (ExceptionState *state : *states) { ExceptionPath *except = state->exception(); if (except->isFilter() - && state->nextThru() == nullptr - && except->to() - && except->to()->matches(gclk)) - return true; + && state->nextThru() == nullptr + && except->to() + && except->to()->matches(gclk)) + return true; } } return false; @@ -1015,32 +957,31 @@ Genclks::srcPath(const Path *clk_path) const { const Pin *src_pin = clk_path->pin(this); const ClockEdge *clk_edge = clk_path->clkEdge(this); - const PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(this); const EarlyLate *early_late = clk_path->minMax(this); - PathAnalysisPt *insert_ap = path_ap->insertionAnalysisPt(early_late); - return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), - insert_ap); + return srcPath(clk_edge->clock(), src_pin, + clk_edge->transition(), early_late); } const Path * Genclks::srcPath(const ClockEdge *clk_edge, - const Pin *src_pin, - const PathAnalysisPt *path_ap) const + const Pin *src_pin, + const MinMax *min_max) const { - return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), path_ap); + return srcPath(clk_edge->clock(), src_pin, + clk_edge->transition(), min_max); } const Path * Genclks::srcPath(const Clock *gclk, - const Pin *src_pin, - const RiseFall *rf, - const PathAnalysisPt *path_ap) const + const Pin *src_pin, + const RiseFall *rf, + const MinMax *min_max) const { auto itr = genclk_src_paths_.find(ClockPinPair(gclk, src_pin)); if (itr != genclk_src_paths_.end()) { const std::vector &src_paths = itr->second; if (!src_paths.empty()) { - size_t path_index = srcPathIndex(rf, path_ap); + size_t path_index = srcPathIndex(rf, min_max); const Path *src_path = &src_paths[path_index]; if (!src_path->isNull()) return src_path; @@ -1051,13 +992,11 @@ Genclks::srcPath(const Clock *gclk, Arrival Genclks::insertionDelay(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const + const Pin *pin, + const RiseFall *rf, + const EarlyLate *early_late) const { - PathAnalysisPt *insert_ap = path_ap->insertionAnalysisPt(early_late); - const Path *src_path = srcPath(clk, pin, rf, insert_ap); + const Path *src_path = srcPath(clk, pin, rf, early_late); if (src_path) return src_path->arrival(); else @@ -1068,7 +1007,7 @@ Genclks::insertionDelay(const Clock *clk, bool ClockPinPairLess::operator()(const ClockPinPair &pair1, - const ClockPinPair &pair2) const + const ClockPinPair &pair2) const { const Clock *clk1 = pair1.first; @@ -1078,8 +1017,8 @@ ClockPinPairLess::operator()(const ClockPinPair &pair1, const Pin *pin1 = pair1.second; const Pin *pin2 = pair2.second; return (clk_index1 < clk_index2 - || (clk_index1 == clk_index2 - && pin1 < pin2)); + || (clk_index1 == clk_index2 + && pin1 < pin2)); } class ClockPinPairHash @@ -1107,12 +1046,12 @@ class ClockPinPairEqual { public: bool operator()(const ClockPinPair &pair1, - const ClockPinPair &pair2) const; + const ClockPinPair &pair2) const; }; bool ClockPinPairEqual::operator()(const ClockPinPair &pair1, - const ClockPinPair &pair2) const + const ClockPinPair &pair2) const { return pair1.first == pair2.first diff --git a/search/Genclks.hh b/search/Genclks.hh index 0f69f5f06..b3da5bbda 100644 --- a/search/Genclks.hh +++ b/search/Genclks.hh @@ -24,7 +24,8 @@ #pragma once -#include "Map.hh" +#include + #include "Transition.hh" #include "NetworkClass.hh" #include "Graph.hh" @@ -40,51 +41,53 @@ class BfsBkwdIterator; class SearchPred; class TagGroupBldr; -typedef std::pair ClockPinPair; +using ClockPinPair = std::pair; class ClockPinPairLess { public: bool operator()(const ClockPinPair &pair1, - const ClockPinPair &pair2) const; + const ClockPinPair &pair2) const; }; -typedef Map GenclkInfoMap; -typedef Map, ClockPinPairLess> GenclkSrcPathMap; -typedef std::map, VertexIdLess> VertexGenclkSrcPathsMap; +using GenclkInfoMap = std::map; +using GenclkSrcPathMap = std::map, ClockPinPairLess>; +using VertexGenclkSrcPathsMap = std::map, VertexIdLess>; class Genclks : public StaState { public: - Genclks(StaState *sta); - ~Genclks(); + Genclks(const Mode *mode, + StaState *sta); + virtual ~Genclks(); void clear(); void ensureInsertionDelays(); VertexSet *fanins(const Clock *clk); void findLatchFdbkEdges(const Clock *clk); - EdgeSet *latchFdbkEdges(const Clock *clk); - void checkMaster(Clock *gclk); - void ensureMaster(Clock *gclk); + EdgeSet &latchFdbkEdges(const Clock *clk); + void checkMaster(Clock *gclk, + const Sdc *sdc); + void ensureMaster(Clock *gclk, + const Sdc *sdc); // Generated clock insertion delay. Arrival insertionDelay(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const; + const Pin *pin, + const RiseFall *rf, + const EarlyLate *early_late) const; // Generated clock source path for a clock path root. const Path *srcPath(const Path *clk_path) const; // Generated clock source path. const Path *srcPath(const ClockEdge *clk_edge, const Pin *src_pin, - const PathAnalysisPt *path_ap) const; + const MinMax *min_max) const; const Path *srcPath(const Clock *clk, const Pin *src_pin, const RiseFall *rf, - const PathAnalysisPt *path_ap) const; + const MinMax *min_max) const; Vertex *srcPath(const Pin *pin) const; Level clkPinMaxLevel(const Clock *clk) const; void copyGenClkSrcPaths(Vertex *vertex, - TagGroupBldr *tag_bldr); + TagGroupBldr *tag_bldr); private: void findInsertionDelays(); @@ -93,45 +96,47 @@ private: void recordSrcPaths(Clock *gclk); void findInsertionDelays(Clock *gclk); void seedClkVertices(Clock *clk, - BfsBkwdIterator &iter, - VertexSet *fanins); + BfsBkwdIterator &iter, + VertexSet &dfanins); size_t srcPathIndex(const RiseFall *clk_rf, - const PathAnalysisPt *path_ap) const; + const MinMax *min_max) const; bool matchesSrcFilter(Path *path, - const Clock *gclk) const; + const Clock *gclk) const; void seedSrcPins(Clock *gclk, - FilterPath *src_filter, - BfsFwdIterator &insert_iter); + FilterPath *src_filter, + BfsFwdIterator &insert_iter); void findSrcArrivals(Clock *gclk, - BfsFwdIterator &insert_iter, - GenclkInfo *genclk_info); - virtual FilterPath *makeSrcFilter(Clock *gclk); + BfsFwdIterator &insert_iter, + GenclkInfo *genclk_info); + FilterPath *makeSrcFilter(Clock *gclk, + Sdc *sdc); void deleteGenClkInfo(); virtual Tag *makeTag(const Clock *gclk, - const Clock *master_clk, - const Pin *master_pin, - const RiseFall *rf, - FilterPath *src_filter, + const Clock *master_clk, + const Pin *master_pin, + const RiseFall *rf, + FilterPath *src_filter, Arrival insert, - const PathAnalysisPt *path_ap); + Scene *scene, + const MinMax *min_max); void seedSrcPins(Clock *clk, - BfsBkwdIterator &iter); + BfsBkwdIterator &iter); void findInsertionDelay(Clock *gclk); GenclkInfo *makeGenclkInfo(Clock *gclk); FilterPath *srcFilter(Clock *gclk); void findFanin(Clock *gclk, - // Return value. - VertexSet *fanins); + VertexSet &fanins); void findLatchFdbkEdges(const Clock *clk, - GenclkInfo *genclk_info); + GenclkInfo *genclk_info); void findLatchFdbkEdges(Vertex *vertex, - Level gclk_level, - SearchPred &srch_pred, - VertexSet &path_vertices, - VertexSet &visited_vertices, - EdgeSet *&fdbk_edges); + Level gclk_level, + SearchPred &srch_pred, + VertexSet &path_vertices, + VertexSet &visited_vertices, + EdgeSet &fdbk_edges); void deleteGenclkSrcPaths(Clock *gclk); + const Mode *mode_; bool found_insertion_delays_; GenclkSrcPathMap genclk_src_paths_; GenclkInfoMap genclk_info_map_; diff --git a/search/Latches.cc b/search/Latches.cc index 73ffa95cf..3c113c51a 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -32,11 +32,11 @@ #include "Graph.hh" #include "ExceptionPath.hh" #include "Sdc.hh" +#include "Mode.hh" #include "ClkInfo.hh" #include "Tag.hh" #include "Sim.hh" #include "PathEnd.hh" -#include "PathAnalysisPt.hh" #include "Search.hh" #include "Crpr.hh" @@ -49,18 +49,19 @@ Latches::Latches(StaState *sta) : void Latches::latchRequired(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const MultiCyclePath *mcp, - const PathDelay *path_delay, - Arrival src_clk_latency, - const ArcDelay &margin, - // Return values. - Required &required, - Arrival &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const + const Path *enable_path, + const Path *disable_path, + const MultiCyclePath *mcp, + const PathDelay *path_delay, + Arrival src_clk_latency, + const ArcDelay &margin, + // Return values. + Required &required, + Arrival &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const { + Sdc *sdc = data_path->sdc(this); const Arrival data_arrival = data_path->arrival(); float max_delay = 0.0; bool ignore_clk_latency = false; @@ -82,18 +83,18 @@ Latches::latchRequired(const Path *data_path, Crpr open_crpr, crpr_diff; bool borrow_limit_exists; latchBorrowInfo(data_path, enable_path, disable_path, margin, - ignore_clk_latency, - nom_pulse_width, open_latency, latency_diff, - open_uncertainty, open_crpr, crpr_diff, max_borrow, - borrow_limit_exists); + ignore_clk_latency, + nom_pulse_width, open_latency, latency_diff, + open_uncertainty, open_crpr, crpr_diff, max_borrow, + borrow_limit_exists); const ClockEdge *data_clk_edge = data_path->clkEdge(this); const ClockEdge *enable_clk_edge = enable_path->clkEdge(this); const TimingRole *check_role = enable_path->clkInfo(this)->isPulseClk() ? TimingRole::setup() : TimingRole::latchSetup(); - CycleAccting *acct = sdc_->cycleAccting(data_clk_edge, - enable_clk_edge); + CycleAccting *acct = sdc->cycleAccting(data_clk_edge, + enable_clk_edge); // checkTgtClkTime float tgt_clk_time = path_delay ? 0.0 : acct->requiredTime(check_role); // checkTgtClkArrival broken down into components. @@ -102,7 +103,7 @@ Latches::latchRequired(const Path *data_path, + open_latency + open_uncertainty + PathEnd::checkSetupMcpAdjustment(data_clk_edge, enable_clk_edge, mcp, - 1, sdc_) + 1, sdc) + open_crpr; debugPrint(debug_, "latch", 1, "data %s enable %s", delayAsString(data_arrival, this), @@ -118,10 +119,10 @@ Latches::latchRequired(const Path *data_path, // Data arrives while latch is transparent. borrow = data_arrival - enable_arrival; if (delayLessEqual(borrow, max_borrow, this)) - required = data_arrival; + required = data_arrival; else { - borrow = max_borrow; - required = enable_arrival + max_borrow; + borrow = max_borrow; + required = enable_arrival + max_borrow; } time_given_to_startpoint = borrow + open_uncertainty + open_crpr; @@ -129,7 +130,7 @@ Latches::latchRequired(const Path *data_path, // 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); + - acct->targetTimeOffset(check_role); adjusted_data_arrival = required + data_shift_to_enable_clk; } } @@ -147,29 +148,30 @@ Latches::latchRequired(const Path *data_path, time_given_to_startpoint = 0.0; } debugPrint(debug_, "latch", 2, "req %s borrow %s time_given %s adj_arrival %s", - delayAsString(required, this), - delayAsString(borrow, this), - delayAsString(time_given_to_startpoint, this), - delayAsString(adjusted_data_arrival, this)); + delayAsString(required, this), + delayAsString(borrow, this), + delayAsString(time_given_to_startpoint, this), + delayAsString(adjusted_data_arrival, this)); } void Latches::latchBorrowInfo(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const ArcDelay &margin, - bool ignore_clk_latency, - // Return values. - float &nom_pulse_width, - Delay &open_latency, - Delay &latency_diff, - float &open_uncertainty, - Crpr &open_crpr, - Crpr &crpr_diff, - Delay &max_borrow, - bool &borrow_limit_exists) const + const Path *enable_path, + const Path *disable_path, + const ArcDelay &margin, + bool ignore_clk_latency, + // Return values. + float &nom_pulse_width, + Delay &open_latency, + Delay &latency_diff, + float &open_uncertainty, + Crpr &open_crpr, + Crpr &crpr_diff, + Delay &max_borrow, + bool &borrow_limit_exists) const { if (data_path && enable_path && disable_path) { + Sdc *sdc = data_path->sdc(this); const ClockEdge *data_clk_edge = data_path->clkEdge(this); const ClockEdge *enable_clk_edge = enable_path->clkEdge(this); const ClockEdge *disable_clk_edge = disable_path->clkEdge(this); @@ -177,7 +179,7 @@ Latches::latchBorrowInfo(const Path *data_path, nom_pulse_width = is_pulse_clk ? 0.0F : enable_clk_edge->pulseWidth(); open_uncertainty = PathEnd::checkClkUncertainty(data_clk_edge, enable_clk_edge, enable_path, - TimingRole::latchSetup(), this); + TimingRole::latchSetup(), sdc); if (ignore_clk_latency) { open_latency = 0.0; latency_diff = 0.0; @@ -198,9 +200,9 @@ Latches::latchBorrowInfo(const Path *data_path, latency_diff = open_latency - close_latency; } float borrow_limit; - sdc_->latchBorrowLimit(data_path->pin(this), disable_path->pin(this), - enable_clk_edge->clock(), - borrow_limit, borrow_limit_exists); + sdc->latchBorrowLimit(data_path->pin(this), disable_path->pin(this), + enable_clk_edge->clock(), + borrow_limit, borrow_limit_exists); if (borrow_limit_exists) max_borrow = borrow_limit; else @@ -216,63 +218,64 @@ Latches::latchBorrowInfo(const Path *data_path, crpr_diff = 0.0; } debugPrint(debug_, "latch", 2, "nom_width %s open_lat %s lat_diff %s open_uncert %s", - delayAsString(nom_pulse_width, this), - delayAsString(open_latency, this), - delayAsString(latency_diff, this), - delayAsString(open_uncertainty, this)); + 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", - delayAsString(open_crpr, this), - delayAsString(crpr_diff, this), - delayAsString(open_uncertainty, this), - borrow_limit_exists ? delayAsString(max_borrow, this) : "none"); + delayAsString(open_crpr, this), + delayAsString(crpr_diff, this), + delayAsString(open_uncertainty, this), + borrow_limit_exists ? delayAsString(max_borrow, this) : "none"); } void Latches::latchRequired(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const PathAnalysisPt *path_ap, - // Return values. - Required &required, - Arrival &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const + const Path *enable_path, + const Path *disable_path, + // Return values. + Required &required, + Arrival &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const { + Sdc *sdc = data_path->sdc(this); Vertex *data_vertex = data_path->vertex(this); const RiseFall *data_rf = data_path->transition(this); - ArcDelay setup = latchSetupMargin(data_vertex,data_rf,disable_path,path_ap); + ArcDelay setup = latchSetupMargin(data_vertex,data_rf,disable_path); ExceptionPath *excpt = search_->exceptionTo(ExceptionPathType::any, - data_path, data_vertex->pin(), - data_rf, - enable_path->clkEdge(this), - path_ap->pathMinMax(), false, - false); + data_path, data_vertex->pin(), + data_rf, + enable_path->clkEdge(this), + data_path->minMax(this), + false, false, sdc); MultiCyclePath *mcp = dynamic_cast(excpt); PathDelay *path_delay = dynamic_cast(excpt); Arrival src_clk_latency = 0.0; if (path_delay && path_delay->ignoreClkLatency()) src_clk_latency = search_->pathClkPathArrival(data_path); latchRequired(data_path, enable_path, disable_path, mcp, - path_delay, src_clk_latency, setup, - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); + path_delay, src_clk_latency, setup, + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); } // Find the latch enable open/close path from the close/open path. Path * -Latches::latchEnableOtherPath(const Path *path, - const PathAnalysisPt *tgt_clk_path_ap) const +Latches::latchEnableOtherPath(const Path *path) const { Vertex *vertex = path->vertex(this); const ClockEdge *clk_edge = path->clkEdge(this); const ClockEdge *other_clk_edge = path->clkInfo(this)->isPulseClk() ? clk_edge:clk_edge->opposite(); const RiseFall *other_rf = path->transition(this)->opposite(); - VertexPathIterator path_iter(vertex, other_rf, tgt_clk_path_ap, this); + VertexPathIterator path_iter(vertex, path->scene(this), + path->minMax(this), + other_rf, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); if (path->isClock(this) - && path->clkEdge(this) == other_clk_edge) { + && path->clkEdge(this) == other_clk_edge) { return path; } } @@ -281,25 +284,26 @@ Latches::latchEnableOtherPath(const Path *path, Path * Latches::latchEnablePath(const Path *q_path, - const Edge *d_q_edge) const + const Edge *d_q_edge) const { const ClockEdge *en_clk_edge = q_path->clkEdge(this); - PathAnalysisPt *path_ap = q_path->pathAnalysisPt(this); - const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); + const Scene *scene = q_path->scene(this); + const Mode *mode = scene->mode(); + const MinMax *tgt_min_max = q_path->tgtClkMinMax(this); const Instance *latch = network_->instance(q_path->pin(this)); Vertex *en_vertex; const RiseFall *en_rf; LatchEnableState state; - latchDtoQEnable(d_q_edge, latch, en_vertex, en_rf, state); + latchDtoQEnable(d_q_edge, latch, mode, en_vertex, en_rf, state); if (state == LatchEnableState::enabled) { - VertexPathIterator path_iter(en_vertex, en_rf, tgt_clk_path_ap, this); + VertexPathIterator path_iter(en_vertex, scene, tgt_min_max, en_rf, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const ClockEdge *clk_edge = path->clkEdge(this); if (path->isClock(this) - && clk_edge == en_clk_edge) { - return path; + && clk_edge == en_clk_edge) { + return path; } } } @@ -312,20 +316,24 @@ Latches::latchEnablePath(const Path *q_path, // the enable open edge. void Latches::latchOutArrival(const Path *data_path, - const TimingArc *d_q_arc, - const Edge *d_q_edge, - const PathAnalysisPt *path_ap, - // Return values. - Tag *&q_tag, - ArcDelay &arc_delay, - Arrival &q_arrival) + const TimingArc *d_q_arc, + const Edge *d_q_edge, + // Return values. + Tag *&q_tag, + ArcDelay &arc_delay, + Arrival &q_arrival) { + Scene *scene = data_path->scene(this); + Sdc *sdc = scene->sdc(); + const Mode *mode = scene->mode(); Vertex *data_vertex = d_q_edge->from(graph_); const Instance *inst = network_->instance(data_vertex->pin()); + const MinMax *min_max = data_path->minMax(this); + DcalcAPIndex dcalc_ap = data_path->dcalcAnalysisPtIndex(this); Vertex *enable_vertex; const RiseFall *enable_rf; LatchEnableState state; - latchDtoQEnable(d_q_edge, inst, enable_vertex, enable_rf, state); + latchDtoQEnable(d_q_edge, inst, mode, enable_vertex, enable_rf, state); // Latch enable may be missing if library is malformed. switch (state) { case LatchEnableState::closed: @@ -335,70 +343,71 @@ Latches::latchOutArrival(const Path *data_path, ExceptionPath *excpt = exceptionTo(data_path, nullptr); if (!(excpt && excpt->isFalse())) { arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, - false, path_ap); + false, min_max, dcalc_ap, sdc); q_arrival = data_path->arrival() + arc_delay; q_tag = data_path->tag(this); } } break; case LatchEnableState::enabled: { - const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); - VertexPathIterator enable_iter(enable_vertex, enable_rf, - tgt_clk_path_ap, this); + const MinMax *tgt_min_max = data_path->tgtClkMinMax(this); + VertexPathIterator enable_iter(enable_vertex, scene, tgt_min_max, + enable_rf, this); while (enable_iter.hasNext()) { Path *enable_path = enable_iter.next(); const ClkInfo *en_clk_info = enable_path->clkInfo(this); const ClockEdge *en_clk_edge = en_clk_info->clkEdge(); if (enable_path->isClock(this)) { - ExceptionPath *excpt = exceptionTo(data_path, en_clk_edge); - // D->Q is disabled when if there is a path delay -to D or EN clk. - if (!(excpt && (excpt->isFalse() - || excpt->isPathDelay()))) { - Path *disable_path = latchEnableOtherPath(enable_path, tgt_clk_path_ap); - Delay borrow, time_given_to_startpoint; - Arrival adjusted_data_arrival; - Required required; - latchRequired(data_path, enable_path, disable_path, path_ap, - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); - if (delayGreater(borrow, 0.0, this)) { - // Latch is transparent when data arrives. - arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, - false, path_ap); - q_arrival = adjusted_data_arrival + arc_delay; - // 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() ? enable_path : nullptr; - const ClkInfo *q_clk_info = - search_->findClkInfo(en_clk_edge, - en_clk_info->clkSrc(), - en_clk_info->isPropagated(), - en_clk_info->genClkSrc(), - en_clk_info->isGenClkSrcPath(), - en_clk_info->pulseClkSense(), - en_clk_info->insertion(), - en_clk_info->latency(), - en_clk_info->uncertainties(), - path_ap, - crpr_clk_path); - const RiseFall *q_rf = d_q_arc->toEdge()->asRiseFall(); - ExceptionStateSet *states = nullptr; - // Latch data pin is a valid exception -from pin. - if (sdc_->exceptionFromStates(data_path->pin(this), - data_path->transition(this), - nullptr, nullptr, // clk below - MinMax::max(), states) - // -from enable non-filter exceptions apply. - && sdc_->exceptionFromStates(enable_vertex->pin(), - enable_rf, - en_clk_edge->clock(), - en_clk_edge->transition(), - MinMax::max(), false, states)) - q_tag = search_->findTag(q_rf, path_ap, q_clk_info, false, - nullptr, false, states, true, nullptr); - } - return; - } + ExceptionPath *excpt = exceptionTo(data_path, en_clk_edge); + // D->Q is disabled when if there is a path delay -to D or EN clk. + if (!(excpt && (excpt->isFalse() + || excpt->isPathDelay()))) { + Path *disable_path = latchEnableOtherPath(enable_path); + Delay borrow, time_given_to_startpoint; + Arrival adjusted_data_arrival; + Required required; + latchRequired(data_path, enable_path, disable_path, + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); + if (delayGreater(borrow, 0.0, this)) { + // Latch is transparent when data arrives. + arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, + false, min_max, dcalc_ap, sdc); + q_arrival = adjusted_data_arrival + arc_delay; + // 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; + const ClkInfo *q_clk_info = + search_->findClkInfo(en_clk_info->scene(), + en_clk_edge, + en_clk_info->clkSrc(), + en_clk_info->isPropagated(), + en_clk_info->genClkSrc(), + en_clk_info->isGenClkSrcPath(), + en_clk_info->pulseClkSense(), + en_clk_info->insertion(), + en_clk_info->latency(), + en_clk_info->uncertainties(), + min_max, crpr_clk_path); + const RiseFall *q_rf = d_q_arc->toEdge()->asRiseFall(); + ExceptionStateSet *states = nullptr; + // Latch data pin is a valid exception -from pin. + if (sdc->exceptionFromStates(data_path->pin(this), + data_path->transition(this), + nullptr, nullptr, // clk below + MinMax::max(), states) + // -from enable non-filter exceptions apply. + && sdc->exceptionFromStates(enable_vertex->pin(), + enable_rf, + en_clk_edge->clock(), + en_clk_edge->transition(), + MinMax::max(), false, states)) + q_tag = search_->findTag(enable_path->tag(this)->scene(), + q_rf, MinMax::max(), q_clk_info, false, + nullptr, false, states, true, nullptr); + } + return; + } } } // No enable path found. @@ -409,26 +418,30 @@ Latches::latchOutArrival(const Path *data_path, ExceptionPath * Latches::exceptionTo(const Path *data_path, - const ClockEdge *en_clk_edge) + const ClockEdge *en_clk_edge) { + Sdc *sdc = data_path->sdc(this); // Look for exceptions -to data or -to enable clk. return search_->exceptionTo(ExceptionPathType::any, - data_path, - data_path->pin(this), - data_path->transition(this), - en_clk_edge, - data_path->minMax(this), - false, false); + data_path, + data_path->pin(this), + data_path->transition(this), + en_clk_edge, + data_path->minMax(this), + false, false, sdc); } ArcDelay Latches::latchSetupMargin(Vertex *data_vertex, - const RiseFall *data_rf, - const Path *disable_path, - const PathAnalysisPt *path_ap) const + const RiseFall *data_rf, + const Path *disable_path) const { if (disable_path) { + const Mode *mode = disable_path->mode(this); + const Sdc *sdc = mode->sdc(); Vertex *enable_vertex = disable_path->vertex(this); + const MinMax *min_max = disable_path->minMax(this); + DcalcAPIndex dcalc_ap = disable_path->dcalcAnalysisPtIndex(this); const RiseFall *disable_rf = disable_path->transition(this); VertexInEdgeIterator edge_iter(data_vertex, graph_); while (edge_iter.hasNext()) { @@ -436,16 +449,16 @@ Latches::latchSetupMargin(Vertex *data_vertex, const TimingRole *role = edge->role(); Vertex *from_vertex = edge->from(graph_); if (role == TimingRole::setup() - && from_vertex == enable_vertex - && !edge->isDisabledCond() - && !sdc_->isDisabledCondDefault(edge)) { - TimingArcSet *arc_set = edge->timingArcSet(); + && from_vertex == enable_vertex + && !mode->sim()->isDisabledCond(edge) + && !sdc->isDisabledCondDefault(edge)) { + TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *check_arc : arc_set->arcs()) { - if (check_arc->toEdge()->asRiseFall() == data_rf - && check_arc->fromEdge()->asRiseFall() == disable_rf) - return search_->deratedDelay(from_vertex, check_arc, edge, - false, path_ap); - } + if (check_arc->toEdge()->asRiseFall() == data_rf + && check_arc->fromEdge()->asRiseFall() == disable_rf) + return search_->deratedDelay(from_vertex, check_arc, edge, + false, min_max, dcalc_ap, sdc); + } } } } @@ -454,23 +467,21 @@ Latches::latchSetupMargin(Vertex *data_vertex, void Latches::latchTimeGivenToStartpoint(const Path *d_path, - const Path *q_path, - const Edge *d_q_edge, - // Return values. - Arrival &time_given, - Path *&enable_path) const + const Path *q_path, + const Edge *d_q_edge, + // Return values. + Arrival &time_given, + Path *&enable_path) const { enable_path = latchEnablePath(q_path, d_q_edge); if (enable_path && enable_path->isClock(this)) { - const PathAnalysisPt *path_ap = q_path->pathAnalysisPt(this); - const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); - Path *disable_path = latchEnableOtherPath(enable_path, tgt_clk_path_ap); + Path *disable_path = latchEnableOtherPath(enable_path); Delay borrow; Required required; Arrival adjusted_data_arrival; - latchRequired(d_path, enable_path, disable_path, path_ap, - required, borrow, adjusted_data_arrival, time_given); + latchRequired(d_path, enable_path, disable_path, + required, borrow, adjusted_data_arrival, time_given); } else { time_given = 0.0; @@ -480,11 +491,12 @@ Latches::latchTimeGivenToStartpoint(const Path *d_path, void Latches::latchDtoQEnable(const Edge *d_q_edge, - const Instance *inst, - // Return values. - Vertex *&enable_vertex, - const RiseFall *&enable_rf, - LatchEnableState &state) const + const Instance *inst, + const Mode *mode, + // Return values. + Vertex *&enable_vertex, + const RiseFall *&enable_rf, + LatchEnableState &state) const { enable_vertex = nullptr; state = LatchEnableState::open; @@ -495,38 +507,41 @@ Latches::latchDtoQEnable(const Edge *d_q_edge, const FuncExpr *enable_func; cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf); if (enable_port) { + const Sdc *sdc = mode->sdc(); + Sim *sim = mode->sim(); Pin *enable_pin = network_->findPin(inst, enable_port); if (enable_pin) { - enable_vertex = graph_->pinLoadVertex(enable_pin); - if (enable_vertex->isDisabledConstraint()) - state = LatchEnableState::open; - else { - // See if constant values in the latch enable expression force - // it to be continuously open or closed. - LogicValue enable_value = enable_func - ? sim_->evalExpr(enable_func, inst) - : sim_->logicValue(enable_pin); - switch (enable_value) { - case LogicValue::zero: - case LogicValue::fall: - state = LatchEnableState::closed; - break; - case LogicValue::one: - case LogicValue::rise: - state = LatchEnableState::open; - break; - case LogicValue::unknown: - state = LatchEnableState::enabled; - break; - } - } + enable_vertex = graph_->pinLoadVertex(enable_pin); + if (sdc->isDisabledConstraint(enable_pin)) + state = LatchEnableState::open; + else { + // See if constant values in the latch enable expression force + // it to be continuously open or closed. + LogicValue enable_value = enable_func + ? sim->evalExpr(enable_func, inst) + : sim->simValue(enable_pin); + switch (enable_value) { + case LogicValue::zero: + case LogicValue::fall: + state = LatchEnableState::closed; + break; + case LogicValue::one: + case LogicValue::rise: + state = LatchEnableState::open; + break; + case LogicValue::unknown: + state = LatchEnableState::enabled; + break; + } + } } } } } LatchEnableState -Latches::latchDtoQState(const Edge *edge) const +Latches::latchDtoQState(const Edge *edge, + const Mode *mode) const { const Vertex *from_vertex = edge->from(graph_); const Pin *from_pin = from_vertex->pin(); @@ -534,17 +549,18 @@ Latches::latchDtoQState(const Edge *edge) const Vertex *enable_vertex; const RiseFall *enable_rf; LatchEnableState state; - latchDtoQEnable(edge, inst, enable_vertex, enable_rf, state); + latchDtoQEnable(edge, inst, mode, enable_vertex, enable_rf, state); return state; } // Latch D->Q arc looks combinational when the enable pin is disabled // or constant. bool -Latches::isLatchDtoQ(const Edge *edge) const +Latches::isLatchDtoQ(const Edge *edge, + const Mode *mode) const { return edge->role() == TimingRole::latchDtoQ() - && latchDtoQState(edge) == LatchEnableState::enabled; + && latchDtoQState(edge, mode) == LatchEnableState::enabled; } } // namespace diff --git a/search/Latches.hh b/search/Latches.hh index 689e16850..864904e5d 100644 --- a/search/Latches.hh +++ b/search/Latches.hh @@ -39,74 +39,73 @@ class Latches : public StaState public: Latches(StaState *sta); void latchTimeGivenToStartpoint(const Path *d_path, - const Path *q_path, - const Edge *d_q_edge, - // Return values. - Arrival &time_given, - Path *&enable_path) const; + const Path *q_path, + const Edge *d_q_edge, + // Return values. + Arrival &time_given, + Path *&enable_path) const; void latchRequired(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const MultiCyclePath *mcp, - const PathDelay *path_delay, - Arrival src_clk_latency, - const ArcDelay &margin, - // Return values. - Required &required, - Delay &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const; - void latchRequired(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const PathAnalysisPt *path_ap, - // Return values. - Required &required, - Delay &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const; + const Path *enable_path, + const Path *disable_path, + const MultiCyclePath *mcp, + const PathDelay *path_delay, + Arrival src_clk_latency, + const ArcDelay &margin, + // Return values. + Required &required, + Delay &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const; void latchBorrowInfo(const Path *data_path, - const Path *enable_path, - const Path *disable_path, - const ArcDelay &margin, - bool ignore_clk_latency, - // Return values. - float &nom_pulse_width, - Delay &open_latency, - Delay &latency_diff, - float &open_uncertainty, - Crpr &open_crpr, - Crpr &crpr_diff, - Delay &max_borrow, - bool &borrow_limit_exists) const; - bool isLatchDtoQ(const Edge *edge) const; + const Path *enable_path, + const Path *disable_path, + const ArcDelay &margin, + bool ignore_clk_latency, + // Return values. + float &nom_pulse_width, + Delay &open_latency, + Delay &latency_diff, + float &open_uncertainty, + Crpr &open_crpr, + Crpr &crpr_diff, + Delay &max_borrow, + bool &borrow_limit_exists) const; + bool isLatchDtoQ(const Edge *edge, + const Mode *mode) const; // Find the latch EN->Q edge for a D->Q edge. void latchDtoQEnable(const Edge *d_q_edge, - const Instance *inst, - // Return values. - Vertex *&enable_vertex, - const RiseFall *&enable_rf, - LatchEnableState &state) const; - LatchEnableState latchDtoQState(const Edge *d_q_edge) const; - Path *latchEnableOtherPath(const Path *path, - const PathAnalysisPt *tgt_clk_path_ap) const; + const Instance *inst, + const Mode *mode, + // Return values. + Vertex *&enable_vertex, + const RiseFall *&enable_rf, + LatchEnableState &state) const; + LatchEnableState latchDtoQState(const Edge *d_q_edge, + const Mode *mode) const; + Path *latchEnableOtherPath(const Path *path) const; Path *latchEnablePath(const Path *q_path, const Edge *d_q_edge) const; void latchOutArrival(const Path *data_path, - const TimingArc *d_q_arc, - const Edge *d_q_edge, - const PathAnalysisPt *path_ap, - Tag *&q_tag, - ArcDelay &arc_delay, - Arrival &q_arrival); + const TimingArc *d_q_arc, + const Edge *d_q_edge, + Tag *&q_tag, + ArcDelay &arc_delay, + Arrival &q_arrival); protected: + void latchRequired(const Path *data_path, + const Path *enable_path, + const Path *disable_path, + // Return values. + Required &required, + Delay &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const; ArcDelay latchSetupMargin(Vertex *data_vertex, - const RiseFall *data_rf, - const Path *disable_path, - const PathAnalysisPt *path_ap) const; + const RiseFall *data_rf, + const Path *disable_path) const; ExceptionPath *exceptionTo(const Path *data_path, - const ClockEdge *en_clk_edge); + const ClockEdge *en_clk_edge); }; } // namespace diff --git a/search/Levelize.cc b/search/Levelize.cc index 69273479b..02ef86e5d 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -27,6 +27,7 @@ #include #include +#include "ContainerHelpers.hh" #include "Report.hh" #include "Debug.hh" #include "Stats.hh" @@ -34,9 +35,9 @@ #include "PortDirection.hh" #include "Network.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Graph.hh" #include "GraphCmp.hh" -#include "SearchPred.hh" #include "Variables.hh" #include "GraphDelayCalc.hh" @@ -46,13 +47,12 @@ using std::max; Levelize::Levelize(StaState *sta) : StaState(sta), - search_pred_(sta), levelized_(false), levels_valid_(false), max_level_(0), level_space_(10), - roots_(graph_), - relevelize_from_(graph_), + roots_(makeVertexSet(sta)), + relevelize_from_(makeVertexSet(sta)), observer_(nullptr) { } @@ -60,7 +60,8 @@ Levelize::Levelize(StaState *sta) : Levelize::~Levelize() { delete observer_; - loops_.deleteContents(); + for (auto loop : loops_) + delete loop; } void @@ -84,7 +85,9 @@ Levelize::clear() roots_.clear(); relevelize_from_.clear(); clearLoopEdges(); - loops_.deleteContentsClear(); + for (auto loop : loops_) + delete loop; + loops_.clear(); loop_edges_.clear(); max_level_ = 0; } @@ -92,11 +95,8 @@ Levelize::clear() void Levelize::clearLoopEdges() { - EdgeSet::Iterator edge_iter(disabled_loop_edges_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + for (Edge *edge : disabled_loop_edges_) edge->setIsDisabledLoop(false); - } disabled_loop_edges_.clear(); } @@ -173,7 +173,7 @@ Levelize::findRoots() size_t fanout_roots = 0; for (Vertex *root : roots_) { if (hasFanout(root)) - fanout_roots++; + fanout_roots++; } debugPrint(debug_, "levelize", 1, "Found %zu roots %zu with fanout", roots_.size(), @@ -186,43 +186,47 @@ Levelize::findRoots() bool Levelize::isRoot(Vertex *vertex) { - if (search_pred_.searchTo(vertex)) { - VertexInEdgeIterator edge_iter1(vertex, graph_); - while (edge_iter1.hasNext()) { - Edge *edge = edge_iter1.next(); - Vertex *from_vertex = edge->from(graph_); - if (search_pred_.searchFrom(from_vertex) - && search_pred_.searchThru(edge)) - return false; - } - // Levelize bidirect driver as if it was a fanout of the bidirect load. - return !(graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) - && vertex->isBidirectDriver()); + VertexInEdgeIterator edge_iter1(vertex, graph_); + while (edge_iter1.hasNext()) { + Edge *edge = edge_iter1.next(); + if (searchThru(edge)) + return false; } - else - return false; + // Levelize bidirect driver as if it was a fanout of the bidirect load. + return !(graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) + && vertex->isBidirectDriver()); +} + +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()); } bool Levelize::hasFanout(Vertex *vertex) { bool has_fanout = false; - if (search_pred_.searchFrom(vertex)) { - VertexOutEdgeIterator edge_iter2(vertex, graph_); - while (edge_iter2.hasNext()) { - Edge *edge = edge_iter2.next(); - Vertex *to_vertex = edge->from(graph_); - if (search_pred_.searchTo(to_vertex) - && search_pred_.searchThru(edge)) { - has_fanout = true; - break; - } - } - // Levelize bidirect driver as if it was a fanout of the bidirect load. - if (graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) - && !vertex->isBidirectDriver()) + VertexOutEdgeIterator edge_iter2(vertex, graph_); + while (edge_iter2.hasNext()) { + Edge *edge = edge_iter2.next(); + if (searchThru(edge)) { has_fanout = true; + break; + } } + // Levelize bidirect driver as if it was a fanout of the bidirect load. + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) + && !vertex->isBidirectDriver()) + has_fanout = true; return has_fanout; } @@ -268,11 +272,10 @@ Levelize::findBackEdges(EdgeSeq &path, EdgeSet back_edges; while (!stack.empty()) { VertexEdgeIterPair vertex_iter = stack.top(); - Vertex *vertex = vertex_iter.first; - VertexOutEdgeIterator *edge_iter = vertex_iter.second; + const auto& [vertex, edge_iter] = vertex_iter; if (edge_iter->hasNext()) { Edge *edge = edge_iter->next(); - if (search_pred_.searchThru(edge)) { + if (searchThru(edge)) { Vertex *to_vertex = edge->to(graph_); if (!to_vertex->visited()) { to_vertex->setVisited(true); @@ -310,10 +313,10 @@ Levelize::findCycleBackEdges() if (unvisited.size() < 100) sort(unvisited, VertexNameLess(network_)); size_t back_edge_count = 0; - VertexSet visited(graph_); + VertexSet visited = makeVertexSet(this); for (Vertex *vertex : unvisited) { - if (visited.find(vertex) == visited.end()) { - VertexSet path_vertices(graph_); + if (!visited.contains(vertex)) { + VertexSet path_vertices = makeVertexSet(this); EdgeSeq path; FindBackEdgesStack stack; visited.insert(vertex); @@ -336,8 +339,7 @@ Levelize::findUnvisitedVertices() VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (!vertex->visited() - && search_pred_.searchFrom(vertex)) + if (!vertex->visited()) unvisited.push_back(vertex); } return unvisited; @@ -354,25 +356,21 @@ Levelize::findTopologicalOrder() VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (search_pred_.searchFrom(vertex)) { - VertexOutEdgeIterator edge_iter(vertex, graph_); - 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)) - in_degree[to_vertex] += 1; - if (edge->role() == TimingRole::latchDtoQ()) - latch_d_to_q_edges_.insert(edge); - } - // 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);; - if (search_pred_.searchTo(to_vertex)) - in_degree[to_vertex] += 1; - } + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (searchThru(edge)) + in_degree[to_vertex] += 1; + if (edge->role() == TimingRole::latchDtoQ()) + latch_d_to_q_edges_.insert(edge); + } + // 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);; + in_degree[to_vertex] += 1; } } @@ -385,19 +383,16 @@ Levelize::findTopologicalOrder() Vertex *vertex = queue.front(); queue.pop_front(); topo_order.push_back(vertex); - if (search_pred_.searchFrom(vertex)) { - VertexOutEdgeIterator edge_iter(vertex, graph_); - 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)) { - const auto &to_degree_itr = in_degree.find(to_vertex); - int &to_in_degree = to_degree_itr->second; - to_in_degree -= 1; - if (to_in_degree == 0) - queue.push_back(to_vertex); - } + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (searchThru(edge)) { + const auto &to_degree_itr = in_degree.find(to_vertex); + int &to_in_degree = to_degree_itr->second; + to_in_degree -= 1; + if (to_in_degree == 0) + queue.push_back(to_vertex); } } // Levelize bidirect driver as if it was a fanout of the bidirect load. @@ -405,13 +400,11 @@ Levelize::findTopologicalOrder() if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) && !vertex->isBidirectDriver()) { Vertex *to_vertex = graph_->pinDrvrVertex(pin); - if (search_pred_.searchTo(to_vertex)) { - const auto °ree_itr = in_degree.find(to_vertex); - int &in_degree = degree_itr->second; - in_degree -= 1; - if (in_degree == 0) - queue.push_back(to_vertex); - } + const auto °ree_itr = in_degree.find(to_vertex); + int &in_degree = degree_itr->second; + in_degree -= 1; + if (in_degree == 0) + queue.push_back(to_vertex); } } @@ -435,7 +428,7 @@ Levelize::findTopologicalOrder() void Levelize::recordLoop(Edge *edge, - EdgeSeq &path) + EdgeSeq &path) { debugPrint(debug_, "levelize", 2, "Loop edge %s (%s)", edge->to_string(this).c_str(), @@ -443,8 +436,10 @@ Levelize::recordLoop(Edge *edge, EdgeSeq *loop_edges = loopEdges(path, edge); GraphLoop *loop = new GraphLoop(loop_edges); loops_.push_back(loop); - if (variables_->dynamicLoopBreaking()) - sdc_->makeLoopExceptions(loop); + if (variables_->dynamicLoopBreaking()) { + for (Mode *mode : modes_) + mode->sdc()->makeLoopExceptions(loop); + } // Record disabled loop edges so they can be cleared without // traversing the entire graph to find them. @@ -454,16 +449,14 @@ Levelize::recordLoop(Edge *edge, EdgeSeq * Levelize::loopEdges(EdgeSeq &path, - Edge *closing_edge) + Edge *closing_edge) { debugPrint(debug_, "loop", 2, "Loop"); EdgeSeq *loop_edges = new EdgeSeq; // Skip the "head" of the path up to where closing_edge closes the loop. Pin *loop_pin = closing_edge->to(graph_)->pin(); bool copy = false; - EdgeSeq::Iterator edge_iter(path); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + for (Edge *edge : path) { Pin *from_pin = edge->from(graph_)->pin(); if (from_pin == loop_pin) copy = true; @@ -485,9 +478,7 @@ void Levelize::reportPath(EdgeSeq &path) const { bool first_edge = true; - EdgeSeq::Iterator edge_iter(path); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + 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()); @@ -503,14 +494,12 @@ Levelize::assignLevels(VertexSeq &topo_sorted) for (Vertex *root : roots_) setLevel(root, 0); for (Vertex *vertex : topo_sorted) { - if (vertex->level() != -1 - && search_pred_.searchFrom(vertex)) { + if (vertex->level() != -1) { VertexOutEdgeIterator edge_iter(vertex, graph_); 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)) + if (searchThru(edge)) setLevel(to_vertex, max(to_vertex->level(), vertex->level() + level_space_)); } @@ -519,9 +508,8 @@ Levelize::assignLevels(VertexSeq &topo_sorted) if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) && !vertex->isBidirectDriver()) { Vertex *to_vertex = graph_->pinDrvrVertex(pin); - if (search_pred_.searchTo(to_vertex)) - setLevel(to_vertex, max(to_vertex->level(), - vertex->level() + level_space_)); + setLevel(to_vertex, max(to_vertex->level(), + vertex->level() + level_space_)); } } } @@ -536,9 +524,7 @@ Levelize::assignLevels(VertexSeq &topo_sorted) void Levelize::ensureLatchLevels() { - EdgeSet::Iterator latch_edge_iter(latch_d_to_q_edges_); - while (latch_edge_iter.hasNext()) { - Edge *edge = latch_edge_iter.next(); + for (Edge *edge : latch_d_to_q_edges_) { Vertex *from = edge->from(graph_); Vertex *to = edge->to(graph_); if (from->level() == to->level()) @@ -549,7 +535,7 @@ Levelize::ensureLatchLevels() void Levelize::setLevel(Vertex *vertex, - Level level) + Level level) { debugPrint(debug_, "levelize", 2, "set level %s %d", vertex->to_string(this).c_str(), @@ -570,23 +556,6 @@ Levelize::invalid() } } -void -Levelize::invalidFrom(Vertex *vertex) -{ - if (levelized_) { - debugPrint(debug_, "levelize", 1, "level invalid from %s", - vertex->to_string(this).c_str()); - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - relevelize_from_.insert(from_vertex); - } - relevelize_from_.insert(vertex); - levels_valid_ = false; - } -} - void Levelize::deleteVertexBefore(Vertex *vertex) { @@ -611,7 +580,7 @@ void Levelize::deleteEdgeBefore(Edge *edge) { if (levelized_ - && loop_edges_.hasKey(edge)) { + && loop_edges_.contains(edge)) { debugPrint(debug_, "levelize", 2, "delete loop edge %s", edge->to_string(this).c_str()); disabled_loop_edges_.erase(edge); @@ -636,13 +605,11 @@ Levelize::relevelize() for (Vertex *vertex : relevelize_from_) { debugPrint(debug_, "levelize", 1, "relevelize from %s", vertex->to_string(this).c_str()); - if (search_pred_.searchFrom(vertex)) { - if (isRoot(vertex)) - roots_.insert(vertex); - VertexSet path_vertices(graph_); - EdgeSeq path; - visit(vertex, nullptr, vertex->level(), 1, path_vertices, path); - } + if (isRoot(vertex)) + roots_.insert(vertex); + VertexSet path_vertices = makeVertexSet(this); + EdgeSeq path; + visit(vertex, nullptr, vertex->level(), 1, path_vertices, path); } ensureLatchLevels(); levels_valid_ = true; @@ -651,11 +618,11 @@ Levelize::relevelize() void Levelize::visit(Vertex *vertex, - Edge *from, + Edge *from, Level level, - Level level_space, + Level level_space, VertexSet &path_vertices, - EdgeSeq &path) + EdgeSeq &path) { Pin *from_pin = vertex->pin(); setLevelIncr(vertex, level); @@ -663,32 +630,28 @@ Levelize::visit(Vertex *vertex, if (from) path.push_back(from); - if (search_pred_.searchFrom(vertex)) { - VertexOutEdgeIterator edge_iter(vertex, graph_); - 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)) { - if (path_vertices.find(to_vertex) != path_vertices.end()) - // 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); - } - if (edge->role() == TimingRole::latchDtoQ()) - latch_d_to_q_edges_.insert(edge); - } - // Levelize bidirect driver as if it was a fanout of the bidirect load. - if (graph_delay_calc_->bidirectDrvrSlewFromLoad(from_pin) - && !vertex->isBidirectDriver()) { - Vertex *to_vertex = graph_->pinDrvrVertex(from_pin); - if (search_pred_.searchTo(to_vertex) - && (to_vertex->level() <= level)) - visit(to_vertex, nullptr, level+level_space, level_space, - path_vertices, path); + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (searchThru(edge)) { + if (path_vertices.contains(to_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); } + if (edge->role() == TimingRole::latchDtoQ()) + latch_d_to_q_edges_.insert(edge); + } + // Levelize bidirect driver as if it was a fanout of the bidirect load. + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(from_pin) + && !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); } path_vertices.erase(vertex); if (from) @@ -698,7 +661,7 @@ Levelize::visit(Vertex *vertex, bool Levelize::isDisabledLoop(Edge *edge) const { - return disabled_loop_edges_.hasKey(edge); + return disabled_loop_edges_.contains(edge); } void @@ -724,24 +687,21 @@ Levelize::checkLevels() VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - if (search_pred_.searchTo(vertex)) { - Level level = vertex->level(); - VertexInEdgeIterator edge_iter1(vertex, graph_); - while (edge_iter1.hasNext()) { - Edge *edge = edge_iter1.next(); - Vertex *from_vertex = edge->from(graph_); - Level from_level = from_vertex->level(); - if (search_pred_.searchFrom(from_vertex) - && search_pred_.searchThru(edge) - && 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); - } + Level level = vertex->level(); + VertexInEdgeIterator edge_iter1(vertex, graph_); + while (edge_iter1.hasNext()) { + Edge *edge = edge_iter1.next(); + Vertex *from_vertex = edge->from(graph_); + Level from_level = from_vertex->level(); + if (searchThru(edge) + && 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); } } } @@ -761,14 +721,12 @@ GraphLoop::~GraphLoop() bool GraphLoop::isCombinational() const { - EdgeSeq::Iterator edge_iter(edges_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + for (Edge *edge : *edges_) { const TimingRole *role = edge->role(); if (!(role == TimingRole::wire() - || role == TimingRole::combinational() - || role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())) + || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())) return false; } return true; @@ -780,9 +738,7 @@ GraphLoop::report(const StaState *sta) const Graph *graph = sta->graph(); Report *report = sta->report(); bool first_edge = true; - EdgeSeq::Iterator loop_edge_iter(edges_); - while (loop_edge_iter.hasNext()) { - Edge *edge = loop_edge_iter.next(); + 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()); diff --git a/search/Levelize.hh b/search/Levelize.hh index fb9c1af91..aab99a00d 100644 --- a/search/Levelize.hh +++ b/search/Levelize.hh @@ -36,9 +36,11 @@ namespace sta { class SearchPred; class LevelizeObserver; +class GraphLoop; -typedef std::pair VertexEdgeIterPair; -typedef std::stack FindBackEdgesStack; +using VertexEdgeIterPair = std::pair; +using FindBackEdgesStack = std::stack; +using GraphLoopSeq = std::vector; class Levelize : public StaState { @@ -52,7 +54,6 @@ public: void ensureLevelized(); void invalid(); // Levels downstream from vertex are invalid. - void invalidFrom(Vertex *vertex); void relevelizeFrom(Vertex *vertex); void deleteVertexBefore(Vertex *vertex); void deleteEdgeBefore(Edge *edge); @@ -61,6 +62,7 @@ public: VertexSet &roots() { return roots_; } bool isRoot(Vertex *vertex); bool hasFanout(Vertex *vertex); + bool searchThru(Edge *edge); // Reset to virgin state. void clear(); // Edge is disabled to break combinational loops. @@ -96,14 +98,13 @@ protected: VertexSet &path_vertices, EdgeSeq &path); void setLevel(Vertex *vertex, - Level level); + Level level); void setLevelIncr(Vertex *vertex, Level level); void clearLoopEdges(); void deleteLoops(); void reportPath(EdgeSeq &path) const; - SearchPredNonLatch2 search_pred_; bool levelized_; bool levels_valid_; Level max_level_; @@ -123,7 +124,7 @@ protected: class GraphLoop { public: - explicit GraphLoop(EdgeSeq *edges); + GraphLoop(EdgeSeq *edges); ~GraphLoop(); EdgeSeq *edges() { return edges_; } bool isCombinational() const; diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index e20cf4075..40e27ae27 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -37,8 +37,7 @@ #include "liberty/LibertyBuilder.hh" #include "Network.hh" #include "PortDirection.hh" -#include "Corner.hh" -#include "DcalcAnalysisPt.hh" +#include "Scene.hh" #include "GraphDelayCalc.hh" #include "Sdc.hh" #include "StaState.hh" @@ -61,30 +60,32 @@ LibertyLibrary * makeTimingModel(const char *lib_name, const char *cell_name, const char *filename, - const Corner *corner, + const Scene *scene, Sta *sta) { - MakeTimingModel maker(lib_name, cell_name, filename, corner, sta); + MakeTimingModel maker(lib_name, cell_name, filename, scene, sta); return maker.makeTimingModel(); } MakeTimingModel::MakeTimingModel(const char *lib_name, const char *cell_name, const char *filename, - const Corner *corner, + const Scene *scene, Sta *sta) : StaState(sta), lib_name_(lib_name), cell_name_(cell_name), filename_(filename), - corner_(corner), + scene_(scene), cell_(nullptr), min_max_(MinMax::max()), lib_builder_(new LibertyBuilder), tbl_template_index_(1), + sdc_(scene->sdc()), sdc_backup_(nullptr), sta_(sta) { + scenes_.insert(scene_); } MakeTimingModel::~MakeTimingModel() @@ -118,7 +119,7 @@ MakeTimingModel::makeTimingModel() void MakeTimingModel::saveSdc() { - sdc_backup_ = new Sdc(this); + sdc_backup_ = new Sdc(sdc_->mode(), this); swapSdcWithBackup(); sta_->delaysInvalid(); } @@ -186,7 +187,6 @@ MakeTimingModel::findArea() void MakeTimingModel::makePorts() { - const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_); Instance *top_inst = network_->topInstance(); Cell *top_cell = network_->cell(top_inst); CellPortIterator *port_iter = network_->portIterator(top_cell); @@ -207,7 +207,7 @@ MakeTimingModel::makePorts() Port *bit_port = member_iter->next(); Pin *pin = network_->findPin(top_inst, bit_port); LibertyPort *lib_bit_port = modelPort(pin); - float load_cap = graph_delay_calc_->loadCap(pin, dcalc_ap); + float load_cap = graph_delay_calc_->loadCap(pin, scene_, min_max_); lib_bit_port->setCapacitance(load_cap); } delete member_iter; @@ -216,7 +216,7 @@ MakeTimingModel::makePorts() LibertyPort *lib_port = lib_builder_->makePort(cell_, port_name); lib_port->setDirection(network_->direction(port)); Pin *pin = network_->findPin(top_inst, port); - float load_cap = graph_delay_calc_->loadCap(pin, dcalc_ap); + float load_cap = graph_delay_calc_->loadCap(pin, scene_, min_max_); lib_port->setCapacitance(load_cap); } } @@ -275,9 +275,10 @@ void MakeEndTimingArcs::visit(PathEnd *path_end) { Path *src_path = path_end->path(); + 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 == sta_->sdc()->defaultArrivalClock() + if (src_clk == sdc->defaultArrivalClock() && tgt_clk_edge) { Network *network = sta_->network(); Debug *debug = sta_->debug(); @@ -334,7 +335,7 @@ MakeTimingModel::findTimingFromInput(Port *input_port) { Instance *top_inst = network_->topInstance(); Pin *input_pin = network_->findPin(top_inst, input_port); - if (!sta_->isClockSrc(input_pin)) { + if (!sdc_->isClock(input_pin)) { MakeEndTimingArcs end_visitor(sta_); OutputPinDelays output_delays; for (const RiseFall *input_rf : RiseFall::range()) { @@ -342,26 +343,26 @@ MakeTimingModel::findTimingFromInput(Port *input_port) sta_->setInputDelay(input_pin, input_rf1, sdc_->defaultArrivalClock(), sdc_->defaultArrivalClockEdge()->transition(), - nullptr, false, false, MinMaxAll::all(), true, 0.0); + 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); + input_rf1, sdc_); search_->findFilteredArrivals(from, nullptr, nullptr, false, false); end_visitor.setInputRf(input_rf); VertexSeq endpoints = search_->filteredEndpoints(); VisitPathEnds visit_ends(sta_); for (Vertex *end : endpoints) - visit_ends.visitPathEnds(end, corner_, MinMaxAll::all(), true, &end_visitor); + visit_ends.visitPathEnds(end, scenes_, MinMaxAll::all(), true, &end_visitor); findOutputDelays(input_rf, output_delays); search_->deleteFilteredArrivals(); sta_->removeInputDelay(input_pin, input_rf1, sdc_->defaultArrivalClock(), sdc_->defaultArrivalClockEdge()->transition(), - MinMaxAll::all()); + MinMaxAll::all(), sdc_); } makeSetupHoldTimingArcs(input_pin, end_visitor.margins()); makeInputOutputTimingArcs(input_pin, output_delays); @@ -547,7 +548,7 @@ MakeTimingModel::findClkTreeDelays() ClockSet *clks = sdc_->findClocks(pin); if (clks->size() == 1) { for (const Clock *clk : *clks) { - ClkDelays delays = sta_->findClkDelays(clk, true); + 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); @@ -656,9 +657,9 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, Delay delay, const RiseFall *rf) { - const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_); - const Pvt *pvt = dcalc_ap->operatingConditions(); + const Pvt *pvt = sdc_->operatingConditions(min_max_); PinSet *drvrs = network_->drivers(network_->net(network_->term(output_pin))); + DcalcAPIndex ap_index = scene_->dcalcAnalysisPtIndex(min_max_); const Pin *drvr_pin = *drvrs->begin(); const LibertyPort *drvr_port = network_->libertyPort(drvr_pin); if (drvr_port) { @@ -674,11 +675,14 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, Vertex *gate_in_vertex = graph_->pinLoadVertex(gate_in_pin); Slew in_slew = graph_->slew(gate_in_vertex, drvr_arc->fromEdge()->asRiseFall(), - dcalc_ap->index()); + ap_index); float in_slew1 = delayAsFloat(in_slew); - GateTableModel *drvr_gate_model = drvr_arc->gateTableModel(dcalc_ap); + GateTableModel *drvr_gate_model = drvr_arc->gateTableModel(scene_, + min_max_); if (drvr_gate_model) { - float output_load_cap = graph_delay_calc_->loadCap(output_pin, dcalc_ap); + 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, @@ -731,7 +735,7 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, } } Vertex *output_vertex = graph_->pinLoadVertex(output_pin); - Slew slew = graph_->slew(output_vertex, rf, dcalc_ap->index()); + Slew slew = graph_->slew(output_vertex, rf, ap_index); return makeGateModelScalar(delay, slew, rf); } @@ -739,7 +743,7 @@ TableTemplate * MakeTimingModel::ensureTableTemplate(const TableTemplate *drvr_template, TableAxisPtr load_axis) { - TableTemplate *model_template = template_map_.findKey(drvr_template); + TableTemplate *model_template = findKey(template_map_, drvr_template); if (model_template == nullptr) { string template_name = "template_"; template_name += std::to_string(tbl_template_index_++); diff --git a/search/MakeTimingModel.hh b/search/MakeTimingModel.hh index 0f9c8ea90..bf929526b 100644 --- a/search/MakeTimingModel.hh +++ b/search/MakeTimingModel.hh @@ -27,14 +27,14 @@ namespace sta { class LibertyLibrary; -class Corner; +class Scene; class Sta; LibertyLibrary * makeTimingModel(const char *lib_name, const char *cell_name, const char *filename, - const Corner *corner, + const Scene *scene, Sta *sta); } // namespace diff --git a/search/MakeTimingModelPvt.hh b/search/MakeTimingModelPvt.hh index babd247ac..8184c46ef 100644 --- a/search/MakeTimingModelPvt.hh +++ b/search/MakeTimingModelPvt.hh @@ -48,8 +48,8 @@ public: bool rf_path_exists[RiseFall::index_count][RiseFall::index_count]; }; -typedef std::map ClockEdgeDelays; -typedef std::map OutputPinDelays; +using ClockEdgeDelays = std::map; +using OutputPinDelays = std::map; class MakeTimingModel : public StaState { @@ -57,7 +57,7 @@ public: MakeTimingModel(const char *lib_name, const char *cell_name, const char *filename, - const Corner *corner, + const Scene *scene, Sta *sta); ~MakeTimingModel(); LibertyLibrary *makeTimingModel(); @@ -105,14 +105,16 @@ private: const char *lib_name_; const char *cell_name_; const char *filename_; - const Corner *corner_; + const Scene *scene_; + SceneSet scenes_; LibertyLibrary *library_; LibertyCell *cell_; const MinMax *min_max_; LibertyBuilder *lib_builder_; // Output driver table model template to model template. - Map template_map_; + std::map template_map_; int tbl_template_index_; + Sdc *sdc_; Sdc *sdc_backup_; Sta *sta_; }; diff --git a/search/Mode.cc b/search/Mode.cc new file mode 100644 index 000000000..80e5b5fd3 --- /dev/null +++ b/search/Mode.cc @@ -0,0 +1,146 @@ +// 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 "Mode.hh" + +#include "Sdc.hh" +#include "Sim.hh" +#include "ClkNetwork.hh" +#include "Genclks.hh" +#include "PathGroup.hh" + +namespace sta { + +Mode::Mode(const std::string &name, + size_t mode_index, + StaState *sta) : + StaState(sta), + name_(name), + mode_index_(mode_index), + sdc_(new Sdc(this, sta)), + sim_(new Sim(sta)), + clk_network_(new ClkNetwork(this, sta)), + genclks_(new Genclks(this, sta)), + path_groups_(nullptr) +{ +} + +Mode::~Mode() +{ + delete sdc_; + delete sim_; + delete clk_network_; + delete genclks_; + delete path_groups_; +} + +void +Mode::copyState(const StaState *sta) +{ + StaState::copyState(sta); + sdc_->copyState(sta); + sim_->copyState(sta); + clk_network_->copyState(sta); + genclks_->copyState(sta); +} + +void +Mode::clear() +{ + scenes_.clear(); + sim_->clear(); + clk_network_->clear(); + genclks_->clear(); +} + +void +Mode::addScene(Scene *scene) +{ + scenes_.push_back(scene); +} + +void +Mode::removeScene(Scene *scene) +{ + // std iterators just plain suck + scenes_.erase(std::remove(scenes_.begin(), scenes_.end(), scene), scenes_.end()); +} + +const SceneSet +Mode::sceneSet() const +{ + SceneSet scenes; + for (Scene *scene : scenes_) + scenes.insert(scene); + return scenes; +} + +//////////////////////////////////////////////////////////////// + +PathGroups * +Mode::makePathGroups(int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StdStringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold, + bool unconstrained_paths) +{ + path_groups_ = new PathGroups(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, + this); + return path_groups_; +} + +void +Mode::deletePathGroups() +{ + delete path_groups_; + path_groups_ = nullptr; +} + +PathGroupSeq +Mode::pathGroups(const PathEnd *path_end) const +{ + if (path_groups_) + return path_groups_->pathGroups(path_end); + else + return PathGroupSeq(); +} + +} // namespace diff --git a/search/Path.cc b/search/Path.cc index bf8fc2de2..40d96bafe 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -29,11 +29,10 @@ #include "Network.hh" #include "Graph.hh" #include "Clock.hh" -#include "DcalcAnalysisPt.hh" -#include "Corner.hh" -#include "PathAnalysisPt.hh" #include "Tag.hh" #include "TagGroup.hh" +#include "Sdc.hh" +#include "Mode.hh" #include "Search.hh" namespace sta { @@ -123,12 +122,6 @@ Path::Path(Vertex *vertex, } } -Path:: ~Path() -{ - if (is_enum_ && prev_path_ && prev_path_->is_enum_) - delete prev_path_; -} - void Path::init(Vertex *vertex, Arrival arrival, @@ -205,15 +198,13 @@ Path::to_string(const StaState *sta) const { if (isNull()) return "null path"; - else { - const PathAnalysisPt *path_ap = pathAnalysisPt(sta); - return stringPrintTmp("%s %s %s/%d %d", + else + return stringPrintTmp("%s %s %s/%s %d", vertex(sta)->to_string(sta).c_str(), transition(sta)->to_string().c_str(), - path_ap->pathMinMax()->to_string().c_str(), - path_ap->index(), + scene(sta)->name().c_str(), + minMax(sta)->to_string().c_str(), tagIndex(sta)); - } } bool @@ -259,6 +250,24 @@ Path::tag(const StaState *sta) const return search->tag(tag_index_); } +Scene * +Path::scene(const StaState *sta) const +{ + return tag(sta)->scene(); +} + +Mode * +Path::mode(const StaState *sta) const +{ + return tag(sta)->scene()->mode(); +} + +Sdc * +Path::sdc(const StaState *sta) const +{ + return tag(sta)->scene()->sdc(); +} + void Path::setTag(Tag *tag) { @@ -306,26 +315,26 @@ Path::isClock(const StaState *sta) const const MinMax * Path::minMax(const StaState *sta) const { - return tag(sta)->minMax(sta); + return tag(sta)->minMax(); } -PathAPIndex -Path::pathAnalysisPtIndex(const StaState *sta) const +DcalcAPIndex +Path::dcalcAnalysisPtIndex(const StaState *sta) const { - return pathAnalysisPt(sta)->index(); + return scene(sta)->dcalcAnalysisPtIndex(minMax(sta)); } -DcalcAnalysisPt * -Path::dcalcAnalysisPt(const StaState *sta) const +PathAPIndex +Path::pathAnalysisPtIndex(const StaState *sta) const { - return pathAnalysisPt(sta)->dcalcAnalysisPt(); + return scene(sta)->pathIndex(minMax(sta)); } Slew Path::slew(const StaState *sta) const { - return sta->graph()->slew(vertex(sta), transition(sta), - dcalcAnalysisPt(sta)->index()); + DcalcAPIndex slew_index = scene(sta)->dcalcAnalysisPtIndex(minMax(sta)); + return sta->graph()->slew(vertex(sta), transition(sta), slew_index); } const RiseFall * @@ -340,12 +349,6 @@ Path::rfIndex(const StaState *sta) const return transition(sta)->index(); } -PathAnalysisPt * -Path::pathAnalysisPt(const StaState *sta) const -{ - return tag(sta)->pathAnalysisPt(sta); -} - void Path::setArrival(Arrival arrival) { @@ -470,6 +473,23 @@ Path::setIsEnum(bool is_enum) //////////////////////////////////////////////////////////////// +const MinMax * +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; + } +} +//////////////////////////////////////////////////////////////// + Path * Path::vertexPath(const Path *path, const StaState *sta) @@ -512,8 +532,8 @@ Path::vertexPath(const Vertex *vertex, int Path::cmpPinTrClk(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { if (path1 && path2) { const Pin *pin1 = path1->pin(sta); @@ -523,11 +543,11 @@ Path::cmpPinTrClk(const Path *path1, int tr_index1 = path1->rfIndex(sta); int tr_index2 = path2->rfIndex(sta); if (tr_index1 == tr_index2) - return cmpClk(path1, path2, sta); + return cmpClk(path1, path2, sta); else if (tr_index1 < tr_index2) - return -1; + return -1; else - return 1; + return 1; } else if (network->pathNameLess(pin1, pin2)) return -1; @@ -544,8 +564,8 @@ Path::cmpPinTrClk(const Path *path1, int Path::cmpClk(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { const ClockEdge *clk_edge1 = path1->clkEdge(sta); const ClockEdge *clk_edge2 = path2->clkEdge(sta); @@ -560,7 +580,7 @@ Path::cmpClk(const Path *path1, return 1; } else if (clk_edge1 == nullptr - && clk_edge2 == nullptr) + && clk_edge2 == nullptr) return 0; else if (clk_edge2) return -1; @@ -570,15 +590,15 @@ Path::cmpClk(const Path *path1, bool Path::equal(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + 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)); + && path2 + && path1->vertexId(sta) == path2->vertexId(sta) + // Tag equal implies transition and path ap equal. + && path1->tagIndex(sta) == path2->tagIndex(sta)); } //////////////////////////////////////////////////////////////// @@ -590,23 +610,23 @@ PathLess::PathLess(const StaState *sta) : bool PathLess::operator()(const Path *path1, - const Path *path2) const + const Path *path2) const { return Path::less(path1, path2, sta_); } bool Path::less(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { return cmp(path1, path2, sta) < 0; } int Path::cmp(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { if (path1 == path2) return 0; @@ -625,19 +645,19 @@ Path::cmp(const Path *path1, TagIndex tag_index1 = path1->tagIndex(sta); TagIndex tag_index2 = path2->tagIndex(sta); if (tag_index1 == tag_index2) - return 0; + return 0; else if (tag_index1 < tag_index2) - return -1; + return -1; else - return 1; + return 1; } } } int Path::cmpNoCrpr(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { VertexId vertex_id1 = path1->vertexId(sta); VertexId vertex_id2 = path2->vertexId(sta); @@ -651,8 +671,8 @@ Path::cmpNoCrpr(const Path *path1, int Path::cmpAll(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { const Path *p1 = path1; const Path *p2 = path2; @@ -682,8 +702,8 @@ Path::cmpAll(const Path *path1, bool Path::lessAll(const Path *path1, - const Path *path2, - const StaState *sta) + const Path *path2, + const StaState *sta) { return cmpAll(path1, path2, sta) < 0; } @@ -691,12 +711,12 @@ Path::lessAll(const Path *path1, //////////////////////////////////////////////////////////////// VertexPathIterator::VertexPathIterator(Vertex *vertex, - const StaState *sta) : + const StaState *sta) : search_(sta->search()), - filtered_(false), - rf_(nullptr), - path_ap_(nullptr), + scene_(nullptr), min_max_(nullptr), + rf_(nullptr), + filtered_(false), paths_(vertex->paths()), path_count_(0), path_index_(0), @@ -709,38 +729,16 @@ VertexPathIterator::VertexPathIterator(Vertex *vertex, } } -// Iterate over vertex paths with the same transition and -// analysis pt but different but different tags. VertexPathIterator::VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap, - const StaState *sta) : + const Scene *scene, + const MinMax *min_max, + const RiseFall *rf, + const StaState *sta) : search_(sta->search()), - filtered_(true), + scene_(scene), + min_max_(min_max), rf_(rf), - path_ap_(path_ap), - min_max_(nullptr), - paths_(vertex->paths()), - path_count_(0), - path_index_(0), - next_(nullptr) -{ - TagGroup *tag_group = search_->tagGroup(vertex); - if (tag_group) { - path_count_ = tag_group->pathCount(); - findNext(); - } -} - -VertexPathIterator::VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max, - const StaState *sta) : - search_(sta->search()), filtered_(true), - rf_(rf), - path_ap_(nullptr), - min_max_(min_max), paths_(vertex->paths()), path_count_(0), path_index_(0), @@ -754,15 +752,14 @@ VertexPathIterator::VertexPathIterator(Vertex *vertex, } VertexPathIterator::VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap, - const MinMax *min_max, - const StaState *sta) : + const RiseFall *rf, + const MinMax *min_max, + const StaState *sta) : search_(sta->search()), - filtered_(true), - rf_(rf), - path_ap_(path_ap), + scene_(nullptr), min_max_(min_max), + rf_(rf), + filtered_(true), paths_(vertex->paths()), path_count_(0), path_index_(0), @@ -782,12 +779,12 @@ VertexPathIterator::findNext() Path *path = &paths_[path_index_++]; if (filtered_) { const Tag *tag = path->tag(search_); - if ((rf_ == nullptr + if ((scene_ == nullptr + || path->scene(search_) == scene_) + && (rf_ == nullptr || tag->rfIndex() == rf_->index()) - && (path_ap_ == nullptr - || tag->pathAPIndex() == path_ap_->index()) && (min_max_ == nullptr - || tag->pathAnalysisPt(search_)->pathMinMax() == min_max_)) { + || path->minMax(search_) == min_max_)) { next_ = path; return; } diff --git a/search/PathAnalysisPt.cc b/search/PathAnalysisPt.cc index 476a8649d..e69de29bb 100644 --- a/search/PathAnalysisPt.cc +++ b/search/PathAnalysisPt.cc @@ -1,73 +0,0 @@ -// 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 "PathAnalysisPt.hh" - -#include "StringUtil.hh" -#include "Corner.hh" -#include "Search.hh" - -namespace sta { - -PathAnalysisPt::PathAnalysisPt(Corner *corner, - PathAPIndex index, - const MinMax *path_min_max, - DcalcAnalysisPt *dcalc_ap) : - corner_(corner), - index_(index), - path_min_max_(path_min_max), - tgt_clk_ap_(nullptr), - dcalc_ap_(dcalc_ap) -{ -} - -std::string -PathAnalysisPt::to_string() const -{ - std::string name = corner_->name(); - name += '/'; - name += path_min_max_->to_string(); - return name; -} - -void -PathAnalysisPt::setTgtClkAnalysisPt(PathAnalysisPt *path_ap) -{ - tgt_clk_ap_ = path_ap; -} - -PathAnalysisPt * -PathAnalysisPt::insertionAnalysisPt(const EarlyLate *early_late) const -{ - return insertion_aps_[early_late->index()]; -} - -void -PathAnalysisPt::setInsertionAnalysisPt(const EarlyLate *early_late, - PathAnalysisPt *ap) -{ - insertion_aps_[early_late->index()] = ap; -} - -} // namespace diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 12b539d4a..2a7af0246 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -34,10 +34,10 @@ #include "PortDelay.hh" #include "DataCheck.hh" #include "Sdc.hh" +#include "Mode.hh" #include "ExceptionPath.hh" #include "ClkInfo.hh" #include "Tag.hh" -#include "PathAnalysisPt.hh" #include "Search.hh" #include "ReportPath.hh" #include "Sim.hh" @@ -75,13 +75,13 @@ PathEnd::vertex(const StaState *sta) const const MinMax * PathEnd::minMax(const StaState *sta) const { - return path_->pathAnalysisPt(sta)->pathMinMax(); + return path_->minMax(sta); } const EarlyLate * PathEnd::pathEarlyLate(const StaState *sta) const { - return path_->pathAnalysisPt(sta)->pathMinMax(); + return path_->minMax(sta); } const EarlyLate * @@ -96,18 +96,6 @@ PathEnd::transition(const StaState *sta) const return path_->transition(sta); } -PathAPIndex -PathEnd::pathIndex(const StaState *sta) const -{ - return path_->pathAnalysisPtIndex(sta); -} - -PathAnalysisPt * -PathEnd::pathAnalysisPt(const StaState *sta) const -{ - return path_->pathAnalysisPt(sta); -} - const ClockEdge * PathEnd::sourceClkEdge(const StaState *sta) const { @@ -290,7 +278,7 @@ PathEnd::multiCyclePath() const int PathEnd::exceptPathCmp(const PathEnd *path_end, - const StaState *) const + const StaState *) const { Type type1 = type(); Type type2 = path_end->type(); @@ -306,46 +294,46 @@ PathEnd::exceptPathCmp(const PathEnd *path_end, Delay PathEnd::checkTgtClkDelay(const Path *tgt_clk_path, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta) + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta) { Delay insertion, latency; checkTgtClkDelay(tgt_clk_path, tgt_clk_edge, check_role, sta, - insertion, latency); + insertion, latency); return Delay(insertion + latency); } void PathEnd::checkTgtClkDelay(const Path *tgt_clk_path, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - // Return values. - Delay &insertion, - Delay &latency) + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta, + // Return values. + Delay &insertion, + Delay &latency) { if (tgt_clk_path) { Search *search = sta->search(); // If propagated clk, adjust required time for target clk network delay. const MinMax *min_max = tgt_clk_path->minMax(sta); const EarlyLate *early_late = check_role->tgtClkEarlyLate(); - const PathAnalysisPt *tgt_path_ap = tgt_clk_path->pathAnalysisPt(sta); const ClkInfo *clk_info = tgt_clk_path->clkInfo(sta); const Pin *tgt_src_pin = clk_info->clkSrc(); const Clock *tgt_clk = tgt_clk_edge->clock(); const RiseFall *tgt_clk_rf = tgt_clk_edge->transition(); + const Mode *mode = tgt_clk_path->mode(sta); insertion = search->clockInsertion(tgt_clk, tgt_src_pin, tgt_clk_rf, - min_max, early_late, tgt_path_ap); + min_max, early_late, mode); if (clk_info->isPropagated() - // Data check target clock is always propagated. - || check_role->isDataCheck()) { + // Data check target clock is always propagated. + || check_role->isDataCheck()) { // Propagated clock. Propagated arrival is seeded with // early_late==path_min_max insertion delay. Arrival clk_arrival = tgt_clk_path->arrival(); Delay path_insertion = search->clockInsertion(tgt_clk, tgt_src_pin, - tgt_clk_rf, min_max, - min_max, tgt_path_ap); + tgt_clk_rf, min_max, + min_max, mode); latency = delayRemove(clk_arrival - tgt_clk_edge->time(), path_insertion); } else @@ -360,20 +348,20 @@ PathEnd::checkTgtClkDelay(const Path *tgt_clk_path, float PathEnd::checkClkUncertainty(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const Path *tgt_clk_path, - const TimingRole *check_role, - const StaState *sta) + const ClockEdge *tgt_clk_edge, + const Path *tgt_clk_path, + const TimingRole *check_role, + const Sdc *sdc) { float inter_clk; bool inter_exists; - checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role, sta, - inter_clk, inter_exists); + checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role, sdc, + inter_clk, inter_exists); if (inter_exists) return inter_clk; else - return checkTgtClkUncertainty(tgt_clk_path, tgt_clk_edge, check_role, sta); + return checkTgtClkUncertainty(tgt_clk_path, tgt_clk_edge, check_role, sdc); } float @@ -383,7 +371,7 @@ PathEnd::checkTgtClkUncertainty(const Path *tgt_clk_path, const StaState *sta) { const MinMax *min_max = check_role->pathMinMax(); - ClockUncertainties *uncertainties = nullptr; + const ClockUncertainties *uncertainties = nullptr; if (tgt_clk_path && tgt_clk_path->isClock(sta)) uncertainties = tgt_clk_path->clkInfo(sta)->uncertainties(); else if (tgt_clk_edge) @@ -403,13 +391,12 @@ PathEnd::checkTgtClkUncertainty(const Path *tgt_clk_path, void PathEnd::checkInterClkUncertainty(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - float &uncertainty, - bool &exists) + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const Sdc *sdc, + float &uncertainty, + bool &exists) { - Sdc *sdc = sta->sdc(); if (src_clk_edge && src_clk_edge != sdc->defaultArrivalClockEdge() && tgt_clk_edge) { @@ -420,7 +407,7 @@ PathEnd::checkInterClkUncertainty(const ClockEdge *src_clk_edge, check_role->pathMinMax(), uncertainty, exists); if (exists - && check_role->genericRole() == TimingRole::setup()) + && check_role->genericRole() == TimingRole::setup()) uncertainty = -uncertainty; } else @@ -512,7 +499,7 @@ PathEndUnconstrained::typeName() const //////////////////////////////////////////////////////////////// PathEndClkConstrained::PathEndClkConstrained(Path *path, - Path *clk_path) : + Path *clk_path) : PathEnd(path), clk_path_(clk_path), crpr_(0.0), @@ -521,9 +508,9 @@ PathEndClkConstrained::PathEndClkConstrained(Path *path, } PathEndClkConstrained::PathEndClkConstrained(Path *path, - Path *clk_path, - Crpr crpr, - bool crpr_valid) : + Path *clk_path, + Crpr crpr, + bool crpr_valid) : PathEnd(path), clk_path_(clk_path), crpr_(crpr), @@ -542,18 +529,18 @@ float PathEndClkConstrained::sourceClkOffset(const StaState *sta) const { return sourceClkOffset(sourceClkEdge(sta), - targetClkEdge(sta), - checkRole(sta), - sta); + targetClkEdge(sta), + checkRole(sta), + sta); } float PathEndClkConstrained::sourceClkOffset(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta) const + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta) const { - Sdc *sdc = sta->sdc(); + Sdc *sdc = path_->sdc(sta); CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge); return acct->sourceTimeOffset(check_role); } @@ -590,7 +577,7 @@ PathEndClkConstrained::targetClkOffset(const StaState *sta) const const ClockEdge *src_clk_edge = sourceClkEdge(sta); const ClockEdge *tgt_clk_edge = targetClkEdge(sta); const TimingRole *check_role = checkRole(sta); - Sdc *sdc = sta->sdc(); + Sdc *sdc = path_->sdc(sta); CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge); return acct->targetTimeOffset(check_role); } @@ -620,7 +607,7 @@ PathEndClkConstrained::targetClkTime(const StaState *sta) const const ClockEdge *src_clk_edge = sourceClkEdge(sta); const ClockEdge *tgt_clk_edge = targetClkEdge(sta); const TimingRole *check_role = checkRole(sta); - Sdc *sdc = sta->sdc(); + Sdc *sdc = path_->sdc(sta); CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge); return acct->requiredTime(check_role); } @@ -635,12 +622,13 @@ PathEndClkConstrained::targetClkArrival(const StaState *sta) const 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), sta) + targetClkEdge(sta), + targetClkPath(), + checkRole(sta), sdc) + targetClkMcpAdjustment(sta); } @@ -655,8 +643,8 @@ PathEndClkConstrained::targetClkInsertionDelay(const StaState *sta) const { Arrival insertion, latency; checkTgtClkDelay(targetClkPath(), targetClkEdge(sta), - checkRole(sta), sta, - insertion, latency); + checkRole(sta), sta, + insertion, latency); return insertion; } @@ -667,10 +655,11 @@ PathEndClkConstrained::targetNonInterClkUncertainty(const StaState *sta) const const ClockEdge *tgt_clk_edge = targetClkEdge(sta); const TimingRole *check_role = checkRole(sta); + Sdc *sdc = path_->sdc(sta); float inter_clk; bool inter_exists; checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role, - sta, inter_clk, inter_exists); + sdc, inter_clk, inter_exists); if (inter_exists) // This returns non inter-clock uncertainty. return 0.0; @@ -681,11 +670,12 @@ PathEndClkConstrained::targetNonInterClkUncertainty(const StaState *sta) const float PathEndClkConstrained::interClkUncertainty(const StaState *sta) const { + Sdc *sdc = path_->sdc(sta); float uncertainty; bool exists; checkInterClkUncertainty(sourceClkEdge(sta), targetClkEdge(sta), - checkRole(sta), sta, - uncertainty, exists); + checkRole(sta), sdc, + uncertainty, exists); if (exists) return uncertainty; else @@ -695,8 +685,9 @@ PathEndClkConstrained::interClkUncertainty(const StaState *sta) const float PathEndClkConstrained::targetClkUncertainty(const StaState *sta) const { + Sdc *sdc = path_->sdc(sta); return checkClkUncertainty(sourceClkEdge(sta), targetClkEdge(sta), - targetClkPath(), checkRole(sta), sta); + targetClkPath(), checkRole(sta), sdc); } Crpr @@ -741,7 +732,7 @@ PathEndClkConstrained::slack(const StaState *sta) const int PathEndClkConstrained::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEnd::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -757,18 +748,18 @@ PathEndClkConstrained::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp) : + Path *clk_path, + MultiCyclePath *mcp) : PathEndClkConstrained(path, clk_path), mcp_(mcp) { } PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : + Path *clk_path, + MultiCyclePath *mcp, + Crpr crpr, + bool crpr_valid) : PathEndClkConstrained(path, clk_path, crpr, crpr_valid), mcp_(mcp) { @@ -782,56 +773,56 @@ PathEndClkConstrainedMcp::targetClkMcpAdjustment(const StaState *sta) const float PathEndClkConstrainedMcp::checkMcpAdjustment(const Path *path, - const ClockEdge *tgt_clk_edge, - const StaState *sta) const + const ClockEdge *tgt_clk_edge, + const StaState *sta) const { if (mcp_) { const TimingRole *check_role = checkRole(sta); const MinMax *min_max = check_role->pathMinMax(); const ClockEdge *src_clk_edge = path->clkEdge(sta); - Sdc *sdc = sta->sdc(); + Sdc *sdc = path_->sdc(sta); if (min_max == MinMax::max()) return PathEnd::checkSetupMcpAdjustment(src_clk_edge, tgt_clk_edge, - mcp_, setupDefaultCycles(), sdc); + mcp_, setupDefaultCycles(), sdc); else { // Hold check. // Default arrival clock is a proxy for the target clock. if (src_clk_edge == nullptr) - src_clk_edge = tgt_clk_edge; + src_clk_edge = tgt_clk_edge; else if (src_clk_edge->clock() == sdc->defaultArrivalClock()) - src_clk_edge = tgt_clk_edge->clock()->edge(src_clk_edge->transition()); + src_clk_edge = tgt_clk_edge->clock()->edge(src_clk_edge->transition()); const MultiCyclePath *setup_mcp; const MultiCyclePath *hold_mcp; // Hold checks also need the setup mcp for cycle accounting. findHoldMcps(tgt_clk_edge, setup_mcp, hold_mcp, sta); if (setup_mcp && hold_mcp) { - int setup_mult = setup_mcp->pathMultiplier(MinMax::max()); - int hold_mult = hold_mcp->pathMultiplier(MinMax::min()); - const ClockEdge *setup_clk_edge = - setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; - float setup_period = setup_clk_edge->clock()->period(); - const ClockEdge *hold_clk_edge = - hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; - float hold_period = hold_clk_edge->clock()->period(); - return (setup_mult - 1) * setup_period - hold_mult * hold_period; + int setup_mult = setup_mcp->pathMultiplier(MinMax::max()); + int hold_mult = hold_mcp->pathMultiplier(MinMax::min()); + const ClockEdge *setup_clk_edge = + setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + float setup_period = setup_clk_edge->clock()->period(); + const ClockEdge *hold_clk_edge = + hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + float hold_period = hold_clk_edge->clock()->period(); + return (setup_mult - 1) * setup_period - hold_mult * hold_period; } else if (hold_mcp) { - int mult = hold_mcp->pathMultiplier(min_max); - const ClockEdge *clk_edge = - hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; - float period = clk_edge->clock()->period(); - return -mult * period; + int mult = hold_mcp->pathMultiplier(min_max); + const ClockEdge *clk_edge = + hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + float period = clk_edge->clock()->period(); + return -mult * period; } else if (setup_mcp) { - int mult = setup_mcp->pathMultiplier(min_max); - const ClockEdge *clk_edge = - setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; - float period = clk_edge->clock()->period(); - return (mult - 1) * period; + int mult = setup_mcp->pathMultiplier(min_max); + const ClockEdge *clk_edge = + setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + float period = clk_edge->clock()->period(); + return (mult - 1) * period; } else - return 0.0; + return 0.0; } } else @@ -840,10 +831,10 @@ PathEndClkConstrainedMcp::checkMcpAdjustment(const Path *path, float PathEnd::checkSetupMcpAdjustment(const ClockEdge *src_clk_edge, - const ClockEdge *tgt_clk_edge, - const MultiCyclePath *mcp, - int default_cycles, - Sdc *sdc) + const ClockEdge *tgt_clk_edge, + const MultiCyclePath *mcp, + int default_cycles, + Sdc *sdc) { if (mcp) { // Default arrival clock is a proxy for the target clock. @@ -854,7 +845,7 @@ PathEnd::checkSetupMcpAdjustment(const ClockEdge *src_clk_edge, if (mcp->minMax()->matches(MinMax::max())) { int mult = mcp->pathMultiplier(MinMax::max()); const ClockEdge *clk_edge = - mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; float period = clk_edge->clock()->period(); return (mult - default_cycles) * period; } @@ -878,9 +869,9 @@ PathEndClkConstrained::slackNoCrpr(const StaState *sta) const void PathEndClkConstrainedMcp::findHoldMcps(const ClockEdge *tgt_clk_edge, - const MultiCyclePath *&setup_mcp, - const MultiCyclePath *&hold_mcp, - const StaState *sta) const + const MultiCyclePath *&setup_mcp, + const MultiCyclePath *&hold_mcp, + const StaState *sta) const { Pin *pin = path_->pin(sta); @@ -892,25 +883,27 @@ PathEndClkConstrainedMcp::findHoldMcps(const ClockEdge *tgt_clk_edge, hold_mcp = mcp_; setup_mcp = dynamic_cast(search->exceptionTo(ExceptionPathType::multi_cycle, - path_, pin, rf, - tgt_clk_edge, - MinMax::max(), true, - false)); + path_, pin, rf, + tgt_clk_edge, + MinMax::max(), true, + false, + path_->sdc(sta))); } else { setup_mcp = mcp_; hold_mcp = dynamic_cast(search->exceptionTo(ExceptionPathType::multi_cycle, - path_, pin, rf, - tgt_clk_edge, - MinMax::min(), true, - false)); + path_, pin, rf, + tgt_clk_edge, + MinMax::min(), true, + false, + path_->sdc(sta))); } } int PathEndClkConstrainedMcp::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrained::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -931,11 +924,11 @@ PathEndClkConstrainedMcp::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndCheck::PathEndCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - MultiCyclePath *mcp, - const StaState *) : + TimingArc *check_arc, + Edge *check_edge, + Path *clk_path, + MultiCyclePath *mcp, + const StaState *) : PathEndClkConstrainedMcp(path, clk_path, mcp), check_arc_(check_arc), check_edge_(check_edge) @@ -943,12 +936,12 @@ PathEndCheck::PathEndCheck(Path *path, } PathEndCheck::PathEndCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : + 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) @@ -959,7 +952,7 @@ PathEnd * PathEndCheck::copy() const { return new PathEndCheck(path_, check_arc_, check_edge_, - clk_path_, mcp_, crpr_, crpr_valid_); + clk_path_, mcp_, crpr_, crpr_valid_); } PathEnd::Type @@ -996,13 +989,15 @@ ArcDelay PathEndCheck::margin(const StaState *sta) const { return sta->search()->deratedDelay(clk_path_->vertex(sta), - check_arc_, check_edge_, false, - pathAnalysisPt(sta)); + check_arc_, check_edge_, false, + path_->minMax(sta), + path_->dcalcAnalysisPtIndex(sta), + path_->sdc(sta)); } int PathEndCheck::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -1087,21 +1082,19 @@ PathEndCheck::macroClkTreeDelay(const StaState *sta) const //////////////////////////////////////////////////////////////// PathEndLatchCheck::PathEndLatchCheck(Path *path, - TimingArc *check_arc, - Edge *check_edge, - Path *disable_path, - MultiCyclePath *mcp, - PathDelay *path_delay, - const StaState *sta) : + TimingArc *check_arc, + Edge *check_edge, + Path *disable_path, + MultiCyclePath *mcp, + PathDelay *path_delay, + const StaState *sta) : PathEndCheck(path, check_arc, check_edge, nullptr, mcp, sta), disable_path_(disable_path), path_delay_(path_delay), src_clk_arrival_(0.0) { Latches *latches = sta->latches(); - Path *enable_path = - latches->latchEnableOtherPath(disable_path, - disable_path->pathAnalysisPt(sta)); + Path *enable_path = latches->latchEnableOtherPath(disable_path); clk_path_ = enable_path; Search *search = sta->search(); // Same as PathEndPathDelay::findRequired. @@ -1110,15 +1103,15 @@ PathEndLatchCheck::PathEndLatchCheck(Path *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) : + 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), @@ -1130,8 +1123,8 @@ 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_); + clk_path_, disable_path_, mcp_, path_delay_, + src_clk_arrival_, crpr_, crpr_valid_); } PathEnd::Type @@ -1177,9 +1170,9 @@ PathEndLatchCheck::sourceClkOffset(const StaState *sta) const return pathDelaySrcClkOffset(path_, path_delay_, src_clk_arrival_, sta); else return PathEndClkConstrained::sourceClkOffset(sourceClkEdge(sta), - disable_path_->clkEdge(sta), - TimingRole::setup(), - sta); + disable_path_->clkEdge(sta), + TimingRole::setup(), + sta); } const TimingRole * @@ -1219,9 +1212,9 @@ PathEndLatchCheck::requiredTime(const StaState *sta) const Arrival borrow, adjusted_data_arrival, time_given_to_startpoint; Latches *latches = sta->latches(); latches->latchRequired(path_, targetClkPath(), latchDisable(), - mcp_, path_delay_, src_clk_arrival_, margin(sta), - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); + mcp_, path_delay_, src_clk_arrival_, margin(sta), + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); return required; } @@ -1232,47 +1225,47 @@ PathEndLatchCheck::borrow(const StaState *sta) const Required required; Arrival borrow, adjusted_data_arrival, time_given_to_startpoint; latches->latchRequired(path_, targetClkPath(), latchDisable(), - mcp_, path_delay_, src_clk_arrival_, margin(sta), - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); + mcp_, path_delay_, src_clk_arrival_, margin(sta), + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); return borrow; } void PathEndLatchCheck::latchRequired(const StaState *sta, - // Return values. - Required &required, - Delay &borrow, - Arrival &adjusted_data_arrival, - Delay &time_given_to_startpoint) const + // Return values. + Required &required, + Delay &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) const { Latches *latches = sta->latches(); latches->latchRequired(path_, targetClkPath(), latchDisable(), - mcp_, path_delay_, src_clk_arrival_, margin(sta), - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); + mcp_, path_delay_, src_clk_arrival_, margin(sta), + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); } void PathEndLatchCheck::latchBorrowInfo(const StaState *sta, - // Return values. - float &nom_pulse_width, - Delay &open_latency, - Delay &latency_diff, - float &open_uncertainty, - Crpr &open_crpr, - Crpr &crpr_diff, - Delay &max_borrow, - bool &borrow_limit_exists) const + // Return values. + float &nom_pulse_width, + Delay &open_latency, + Delay &latency_diff, + float &open_uncertainty, + Crpr &open_crpr, + Crpr &crpr_diff, + Delay &max_borrow, + bool &borrow_limit_exists) const { Latches *latches = sta->latches(); latches->latchBorrowInfo(path_, targetClkPath(), latchDisable(), - margin(sta), - path_delay_ && ignoreClkLatency(sta), - nom_pulse_width, open_latency, - latency_diff, open_uncertainty, - open_crpr, crpr_diff, max_borrow, - borrow_limit_exists); + margin(sta), + path_delay_ && ignoreClkLatency(sta), + nom_pulse_width, open_latency, + latency_diff, open_uncertainty, + open_crpr, crpr_diff, max_borrow, + borrow_limit_exists); } Arrival @@ -1296,7 +1289,7 @@ PathEndLatchCheck::targetClkWidth(const StaState *sta) const int PathEndLatchCheck::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -1325,10 +1318,10 @@ PathEndLatchCheck::ignoreClkLatency(const StaState *sta) const /////////////////////////////////////////////////////////////// PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - const StaState *) : + Path *path, + Path *clk_path, + MultiCyclePath *mcp, + const StaState *) : // No target clk_path_ for output delays. PathEndClkConstrainedMcp(path, clk_path, mcp), output_delay_(output_delay) @@ -1336,11 +1329,11 @@ PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay, } PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay, - Path *path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : + Path *path, + Path *clk_path, + MultiCyclePath *mcp, + Crpr crpr, + bool crpr_valid) : PathEndClkConstrainedMcp(path, clk_path, mcp, crpr, crpr_valid), output_delay_(output_delay) { @@ -1350,7 +1343,7 @@ PathEnd * PathEndOutputDelay::copy() const { return new PathEndOutputDelay(output_delay_, path_, clk_path_, - mcp_, crpr_, crpr_valid_); + mcp_, crpr_, crpr_valid_); } PathEnd::Type @@ -1385,8 +1378,8 @@ PathEndOutputDelay::margin(const StaState *sta) const float PathEnd::outputDelayMargin(OutputDelay *output_delay, - const Path *path, - const StaState *sta) + const Path *path, + const StaState *sta) { const RiseFall *rf = path->transition(sta); const MinMax *min_max = path->minMax(sta); @@ -1452,42 +1445,41 @@ PathEndOutputDelay::targetClkDelay(const StaState *sta) const Arrival PathEndOutputDelay::tgtClkDelay(const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta) const + const TimingRole *check_role, + const StaState *sta) const { Arrival insertion, latency; tgtClkDelay(tgt_clk_edge, check_role, sta, - insertion, latency); + insertion, latency); return insertion + latency; } void PathEndOutputDelay::tgtClkDelay(const ClockEdge *tgt_clk_edge, - const TimingRole *check_role, - const StaState *sta, - // Return values. - Arrival &insertion, - Arrival &latency) const + const TimingRole *check_role, + const StaState *sta, + // Return values. + Arrival &insertion, + Arrival &latency) const { // Early late: setup early, hold late. const EarlyLate *early_late = check_role->tgtClkEarlyLate(); // Latency min_max depends on bc_wc or ocv. - const PathAnalysisPt *path_ap = path_->pathAnalysisPt(sta); - const MinMax *latency_min_max = path_ap->tgtClkAnalysisPt()->pathMinMax(); + const MinMax *latency_min_max = path_->tgtClkMinMax(sta); Clock *tgt_clk = tgt_clk_edge->clock(); const RiseFall *tgt_clk_rf = tgt_clk_edge->transition(); + const Mode *mode = path_->mode(sta); if (!output_delay_->sourceLatencyIncluded()) insertion = sta->search()->clockInsertion(tgt_clk, - tgt_clk->defaultPin(), - tgt_clk_rf, - latency_min_max, - early_late, path_ap); + tgt_clk->defaultPin(), + tgt_clk_rf, + latency_min_max, + early_late, mode); else insertion = 0.0; - const Sdc *sdc = sta->sdc(); if (!tgt_clk->isPropagated() && !output_delay_->networkLatencyIncluded()) - latency = sdc->clockLatency(tgt_clk, tgt_clk_rf, latency_min_max); + latency = mode->sdc()->clockLatency(tgt_clk, tgt_clk_rf, latency_min_max); else latency = 0.0; } @@ -1500,14 +1492,14 @@ PathEndOutputDelay::targetClkInsertionDelay(const StaState *sta) const else { Arrival insertion, latency; tgtClkDelay(targetClkEdge(sta), checkRole(sta), sta, - insertion, latency); + insertion, latency); return insertion; } } int PathEndOutputDelay::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -1528,11 +1520,11 @@ PathEndOutputDelay::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndGatedClock::PathEndGatedClock(Path *gating_ref, - Path *clk_path, - const TimingRole *check_role, - MultiCyclePath *mcp, - ArcDelay margin, - const StaState *) : + Path *clk_path, + const TimingRole *check_role, + MultiCyclePath *mcp, + ArcDelay margin, + const StaState *) : PathEndClkConstrainedMcp(gating_ref, clk_path, mcp), check_role_(check_role), margin_(margin) @@ -1540,12 +1532,12 @@ 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) : + 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) @@ -1556,7 +1548,7 @@ PathEnd * PathEndGatedClock::copy() const { return new PathEndGatedClock(path_, clk_path_, check_role_, - mcp_, margin_, crpr_, crpr_valid_); + mcp_, margin_, crpr_, crpr_valid_); } PathEnd::Type @@ -1591,7 +1583,7 @@ PathEndGatedClock::reportShort(const ReportPath *report) const int PathEndGatedClock::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -1612,10 +1604,10 @@ PathEndGatedClock::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndDataCheck::PathEndDataCheck(DataCheck *check, - Path *data_path, - Path *data_clk_path, - MultiCyclePath *mcp, - const StaState *sta) : + Path *data_path, + Path *data_clk_path, + MultiCyclePath *mcp, + const StaState *sta) : PathEndClkConstrainedMcp(data_path, nullptr, mcp), data_clk_path_(data_clk_path), check_(check) @@ -1639,15 +1631,15 @@ PathEndDataCheck::clkPath(Path *path, if (prev_arc) { const TimingRole *prev_role = prev_arc->role(); if (prev_role == TimingRole::regClkToQ() - || prev_role == TimingRole::latchEnToQ()) { + || prev_role == TimingRole::latchEnToQ()) { prev_path = p->prevPath(); - return prev_path; + return prev_path; } else if (prev_role == TimingRole::latchDtoQ()) { - const Latches *latches = sta->latches(); - Edge *prev_edge = p->prevEdge(sta); - Path *enable_path = latches->latchEnablePath(p, prev_edge); - return enable_path; + const Latches *latches = sta->latches(); + Edge *prev_edge = p->prevEdge(sta); + Path *enable_path = latches->latchEnablePath(p, prev_edge); + return enable_path; } } p = prev_path; @@ -1656,12 +1648,12 @@ PathEndDataCheck::clkPath(Path *path, } PathEndDataCheck::PathEndDataCheck(DataCheck *check, - Path *data_path, - Path *data_clk_path, - Path *clk_path, - MultiCyclePath *mcp, - Crpr crpr, - bool crpr_valid) : + 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) @@ -1672,7 +1664,7 @@ PathEnd * PathEndDataCheck::copy() const { return new PathEndDataCheck(check_, path_, data_clk_path_, - clk_path_, mcp_, crpr_, crpr_valid_); + clk_path_, mcp_, crpr_, crpr_valid_); } PathEnd::Type @@ -1718,9 +1710,9 @@ PathEndDataCheck::margin(const StaState *sta) const float margin; bool margin_exists; check_->margin(data_clk_path_->transition(sta), - path_->transition(sta), - path_->minMax(sta), - margin, margin_exists); + path_->transition(sta), + path_->minMax(sta), + margin, margin_exists); return margin; } @@ -1747,7 +1739,7 @@ PathEndDataCheck::reportShort(const ReportPath *report) const int PathEndDataCheck::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -1768,8 +1760,8 @@ PathEndDataCheck::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, - Path *path, - const StaState *sta): + Path *path, + const StaState *sta): PathEndClkConstrained(path, nullptr), path_delay_(path_delay), check_arc_(nullptr), @@ -1780,9 +1772,9 @@ PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, } PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, - Path *path, - OutputDelay *output_delay, - const StaState *sta): + Path *path, + OutputDelay *output_delay, + const StaState *sta): PathEndClkConstrained(path, nullptr), path_delay_(path_delay), check_arc_(nullptr), @@ -1793,11 +1785,11 @@ PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, } PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, - Path *path, - Path *clk_path, - TimingArc *check_arc, - Edge *check_edge, - const StaState *sta) : + Path *path, + Path *clk_path, + TimingArc *check_arc, + Edge *check_edge, + const StaState *sta) : PathEndClkConstrained(path, clk_path), path_delay_(path_delay), check_arc_(check_arc), @@ -1808,14 +1800,14 @@ PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, } 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) : + 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), @@ -1829,8 +1821,8 @@ PathEnd * PathEndPathDelay::copy() const { return new PathEndPathDelay(path_delay_, path_, clk_path_, - check_arc_, check_edge_, output_delay_, - src_clk_arrival_, crpr_, crpr_valid_); + check_arc_, check_edge_, output_delay_, + src_clk_arrival_, crpr_, crpr_valid_); } PathEnd::Type @@ -1891,8 +1883,10 @@ PathEndPathDelay::margin(const StaState *sta) const { if (check_arc_) { return sta->search()->deratedDelay(check_edge_->from(sta->graph()), - check_arc_, check_edge_, false, - pathAnalysisPt(sta)); + check_arc_, check_edge_, false, + path_->minMax(sta), + path_->dcalcAnalysisPtIndex(sta), + path_->sdc(sta)); } else if (output_delay_) return outputDelayMargin(output_delay_, path_, sta); @@ -1915,9 +1909,9 @@ PathEnd::clkSkew(const StaState *) // Helper shared by PathEndLatchCheck. float PathEnd::pathDelaySrcClkOffset(const Path *path, - PathDelay *path_delay, - Arrival src_clk_arrival, - const StaState *sta) + PathDelay *path_delay, + Arrival src_clk_arrival, + const StaState *sta) { float offset = 0.0; const ClockEdge *clk_edge = path->clkEdge(sta); @@ -2009,7 +2003,7 @@ PathEndPathDelay::ignoreClkLatency(const StaState *sta) const int PathEndPathDelay::exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const + const StaState *sta) const { int cmp = PathEndClkConstrained::exceptPathCmp(path_end, sta); if (cmp == 0) { @@ -2019,11 +2013,11 @@ PathEndPathDelay::exceptPathCmp(const PathEnd *path_end, if (path_delay_ == path_delay2) { const TimingArc *check_arc2 = path_end2->check_arc_; if (check_arc_ == check_arc2) - return 0; + return 0; else if (check_arc_ < check_arc2) - return -1; + return -1; else - return 1; + return 1; } else if (path_delay_ < path_delay2) return -1; @@ -2043,23 +2037,23 @@ PathEndLess::PathEndLess(const StaState *sta) : bool PathEndLess::operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const + const PathEnd *path_end2) const { return PathEnd::less(path_end1, path_end2, sta_); } bool PathEnd::less(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + const StaState *sta) { return cmp(path_end1, path_end2, sta) < 0; } int PathEnd::cmp(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + const StaState *sta) { int cmp = path_end1->isUnconstrained() ? -cmpArrival(path_end1, path_end2, sta) @@ -2073,9 +2067,9 @@ PathEnd::cmp(const PathEnd *path_end1, const Path *clk_path2 = path_end2->targetClkPath(); cmp = Path::cmpPinTrClk(clk_path1, clk_path2, sta); if (cmp == 0) { - cmp = Path::cmpAll(path1, path2, sta); - if (cmp == 0) - cmp = Path::cmpAll(clk_path1, clk_path2, sta); + cmp = Path::cmpAll(path1, path2, sta); + if (cmp == 0) + cmp = Path::cmpAll(clk_path1, clk_path2, sta); } } } @@ -2084,8 +2078,8 @@ PathEnd::cmp(const PathEnd *path_end1, int PathEnd::cmpSlack(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + const StaState *sta) { Slack slack1 = path_end1->slack(sta); Slack slack2 = path_end2->slack(sta); @@ -2114,8 +2108,8 @@ PathEnd::cmpSlack(const PathEnd *path_end1, int PathEnd::cmpArrival(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + const StaState *sta) { Arrival arrival1 = path_end1->dataArrivalTime(sta); Arrival arrival2 = path_end2->dataArrivalTime(sta); @@ -2130,8 +2124,8 @@ PathEnd::cmpArrival(const PathEnd *path_end1, int PathEnd::cmpNoCrpr(const PathEnd *path_end1, - const PathEnd *path_end2, - const StaState *sta) + const PathEnd *path_end2, + const StaState *sta) { int cmp = path_end1->exceptPathCmp(path_end2, sta); if (cmp == 0) { @@ -2152,7 +2146,7 @@ PathEndSlackLess::PathEndSlackLess(const StaState *sta) : bool PathEndSlackLess::operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const + const PathEnd *path_end2) const { int cmp = path_end1->isUnconstrained() ? -PathEnd::cmpArrival(path_end1, path_end2, sta_) @@ -2169,7 +2163,7 @@ PathEndNoCrprLess::PathEndNoCrprLess(const StaState *sta) : bool PathEndNoCrprLess::operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const + const PathEnd *path_end2) const { int cmp = path_end1->exceptPathCmp(path_end2, sta_); if (cmp == 0) { diff --git a/search/PathEnum.cc b/search/PathEnum.cc index c3a1189aa..7411b7d26 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -31,8 +31,8 @@ #include "TimingArc.hh" #include "Network.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Graph.hh" -#include "PathAnalysisPt.hh" #include "Tag.hh" #include "Search.hh" #include "Latches.hh" @@ -53,7 +53,7 @@ class Diversion { public: Diversion(PathEnd *path_end, - Path *after_div); + Path *after_div); PathEnd *pathEnd() const { return path_end_; } Path *divPath() const { return after_div_; } @@ -63,7 +63,7 @@ class Diversion }; Diversion::Diversion(PathEnd *path_end, - Path *after_div) : + Path *after_div) : path_end_(path_end), after_div_(after_div) { @@ -87,7 +87,7 @@ DiversionGreater::DiversionGreater(const StaState *sta) : // the same delay is kept in the queue. bool DiversionGreater::operator()(Diversion *div1, - Diversion *div2) const + Diversion *div2) const { PathEnd *path_end1 = div1->pathEnd(); PathEnd *path_end2 = div2->pathEnd(); @@ -98,9 +98,8 @@ static void deleteDiversionPathEnd(Diversion *div) { PathEnd *div_end = div->pathEnd(); - Path *div_path = div_end->path(); - if (div_path->isEnum()) - delete div_path; + // This does NOT delete the div_end->path() even if it is enum. + // Search::deletePathss takes care of that. delete div_end; delete div; } @@ -108,11 +107,11 @@ deleteDiversionPathEnd(Diversion *div) //////////////////////////////////////////////////////////////// PathEnum::PathEnum(size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - bool cmp_slack, - const StaState *sta) : + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + bool cmp_slack, + const StaState *sta) : StaState(sta), cmp_slack_(cmp_slack), group_path_count_(group_path_count), @@ -183,10 +182,10 @@ PathEnum::findNext() Diversion *div = div_queue_.top(); div_queue_.pop(); PathEnd *path_end = div->pathEnd(); + Path *path = path_end->path(); Vertex *vertex = path_end->vertex(this); path_counts_[vertex]++; if (debug_->check("path_enum", 2)) { - Path *path = path_end->path(); report_->reportLine("path_enum: next path %zu %s delay %s slack %s", path_counts_[vertex], path->to_string(this).c_str(), @@ -201,7 +200,6 @@ PathEnum::findNext() makeDiversions(path_end, div->divPath()); // Caller owns the path end now, so don't delete it. next_ = path_end; - //search_->saveEnumPath(path_end->path()); delete div; break; } @@ -209,7 +207,7 @@ PathEnum::findNext() // 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", + "endpoint_path_count reached for %s", vertex->to_string(this).c_str()); deleteDiversionPathEnd(div); } @@ -237,44 +235,43 @@ PathEnum::reportDiversionPath(Diversion *div) //////////////////////////////////////////////////////////////// -typedef std::set> VisitedFanins; -typedef std::pair VertexEdge; +using VisitedFanins = std::set>; +using VertexEdge = std::pair; class PathEnumFaninVisitor : public PathVisitor { public: PathEnumFaninVisitor(PathEnd *path_end, - Path *before_div, - bool unique_pins, - bool unique_edges, - PathEnum *path_enum); + Path *before_div, + bool unique_pins, + bool unique_edges, + PathEnum *path_enum); virtual VertexVisitor *copy() const override; void visitFaninPathsThru(Path *before_div, - Vertex *prev_vertex, - TimingArc *prev_arc); + Vertex *prev_vertex, + TimingArc *prev_arc); virtual bool visitFromToPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, + 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, - const PathAnalysisPt *path_ap) override; + 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, Edge *div_edge, - TimingArc *div_arc, - // Return values. - PathEnd *&div_end, - Path *&after_div_copy); + TimingArc *div_arc, + // Return values. + PathEnd *&div_end, + Path *&after_div_copy); bool visitEdge(const Pin *from_pin, Vertex *from_vertex, Edge *edge, @@ -284,7 +281,7 @@ class PathEnumFaninVisitor : public PathVisitor void insertUniqueEdgeDiv(Diversion *div); void reportDiversion(const Edge *edge, const TimingArc *div_arc, - Path *after_div); + Path *after_div); PathEnd *path_end_; Path *before_div_; @@ -295,7 +292,8 @@ class PathEnumFaninVisitor : public PathVisitor Slack path_end_slack_; Tag *before_div_tag_; int before_div_rf_index_; - PathAPIndex before_div_ap_index_; + Scene *scene_; + const MinMax *min_max_; Arrival before_div_arrival_; TimingArc *prev_arc_; Vertex *prev_vertex_; @@ -305,10 +303,10 @@ class PathEnumFaninVisitor : public PathVisitor }; PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end, - Path *before_div, - bool unique_pins, - bool unique_edges, - PathEnum *path_enum) : + Path *before_div, + bool unique_pins, + bool unique_edges, + PathEnum *path_enum) : PathVisitor(path_enum), path_end_(path_end), before_div_(before_div), @@ -319,9 +317,10 @@ PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end, path_end_slack_(path_end->slack(this)), before_div_tag_(before_div_->tag(this)), before_div_rf_index_(before_div_tag_->rfIndex()), - before_div_ap_index_(before_div_tag_->pathAPIndex()), + scene_(before_div_->scene(this)), + min_max_(before_div_->minMax(this)), before_div_arrival_(before_div_->arrival()), - crpr_active_(crprActive()) + crpr_active_(crprActive(path_end->path()->mode(this))) { } @@ -329,19 +328,20 @@ VertexVisitor * PathEnumFaninVisitor::copy() const { return new PathEnumFaninVisitor(path_end_, before_div_, unique_pins_, - unique_edges_, path_enum_); + unique_edges_, path_enum_); } void PathEnumFaninVisitor::visitFaninPathsThru(Path *before_div, - Vertex *prev_vertex, - TimingArc *prev_arc) + Vertex *prev_vertex, + TimingArc *prev_arc) { before_div_ = before_div; before_div_tag_ = before_div_->tag(this); before_div_arrival_ = before_div_->arrival(); before_div_rf_index_ = before_div_tag_->rfIndex(); - before_div_ap_index_ = before_div_tag_->pathAPIndex(); + scene_ = before_div->scene(this); + min_max_ = before_div->minMax(this); prev_arc_ = prev_arc; prev_vertex_ = prev_vertex; @@ -367,27 +367,28 @@ PathEnumFaninVisitor::visitEdge(const Pin *from_pin, TagGroup *from_tag_group = search_->tagGroup(from_vertex); if (from_tag_group) { TimingArcSet *arc_set = edge->timingArcSet(); - VertexPathIterator from_iter(from_vertex, search_); + VertexPathIterator from_iter(from_vertex, scene_, min_max_, nullptr, search_); while (from_iter.hasNext()) { Path *from_path = from_iter.next(); - // Filter paths by path ap. - PathAnalysisPt *path_ap = from_path->pathAnalysisPt(this); - if (path_ap->index() == before_div_ap_index_) { - const MinMax *min_max = path_ap->pathMinMax(); + const Mode *mode = from_path->mode(this); + const Sdc *sdc = mode->sdc(); + 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)) { const RiseFall *from_rf = from_path->transition(this); TimingArc *arc1, *arc2; arc_set->arcsFrom(from_rf, arc1, arc2); // Filter arcs by to edge. - if (arc1 && arc1->toEdge()->asRiseFall()->index() == before_div_rf_index_) { + 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, path_ap)) + 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, path_ap)) + edge, arc2, to_pin, to_vertex, min_max_, mode)) return false; } } @@ -398,20 +399,19 @@ PathEnumFaninVisitor::visitEdge(const Pin *from_pin, bool PathEnumFaninVisitor::visitFromToPath(const Pin *, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *, - Path *from_path, + Vertex *from_vertex, + const RiseFall *from_rf, + Tag *, + Path *from_path, const Arrival &, - Edge *edge, - TimingArc *arc, - ArcDelay, - Vertex *to_vertex, - const RiseFall *to_rf, - Tag *to_tag, - Arrival & /* to_arrival */, - const MinMax * /* min_max */, - const PathAnalysisPt *path_ap) + Edge *edge, + TimingArc *arc, + ArcDelay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival & /* to_arrival */, + const MinMax *) { // These paths fanin to before_div_ so we know to_vertex matches. if ((!unique_pins_ || from_vertex != prev_vertex_) @@ -420,13 +420,16 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, && Tag::matchNoCrpr(to_tag, before_div_tag_) // Ignore paths that only differ by crpr from same vertex/edge. && (!crpr_active_ - || visited_fanins_.find({from_vertex, arc}) == visited_fanins_.end())) { + || !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->to_string().c_str(), delayAsString(search_->deratedDelay(from_vertex, arc, edge, - false,path_ap), this)); + 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. @@ -442,8 +445,8 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, } else debugPrint(debug_, "path_enum", 3, " pruned %s %s", - edge->to_string(this).c_str(), - arc->to_string().c_str()); + edge->to_string(this).c_str(), + arc->to_string().c_str()); return true; } @@ -468,14 +471,14 @@ PathEnumFaninVisitor::insertUniqueEdgeDiv(Diversion *div) void PathEnumFaninVisitor::makeDivertedPathEnd(Path *after_div, Edge *div_edge, - TimingArc *div_arc, - // Return values. - PathEnd *&div_end, - Path *&after_div_copy) + TimingArc *div_arc, + // Return values. + PathEnd *&div_end, + 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); + div_edge, div_arc, div_path, after_div_copy); div_end = path_end_->copy(); div_end->setPath(div_path); } @@ -483,17 +486,16 @@ PathEnumFaninVisitor::makeDivertedPathEnd(Path *after_div, void PathEnumFaninVisitor::reportDiversion(const Edge *div_edge, const TimingArc *div_arc, - Path *after_div) -{ + Path *after_div) +{ if (debug_->check("path_enum", 3)) { Path *path = path_end_->path(); - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); 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, path_ap); + after_div, div_edge, + div_arc); Path *div_prev = before_div_->prevPath(); report_->reportLine("path_enum: diversion %s %s %s -> %s", path->to_string(this).c_str(), @@ -530,8 +532,8 @@ PathEnum::pruneDiversionQueue() Diversion *div = div_queue_.top(); Vertex *vertex = div->pathEnd()->vertex(this); if (end_count < group_path_count_ - && ((unique_pins_ && path_counts[vertex] == 0) - || (!unique_pins_ && path_counts[vertex] < endpoint_path_count_))) { + && ((unique_pins_ && path_counts[vertex] == 0) + || (!unique_pins_ && path_counts[vertex] < endpoint_path_count_))) { divs.push_back(div); path_counts[vertex]++; end_count++; @@ -548,10 +550,9 @@ PathEnum::pruneDiversionQueue() Arrival PathEnum::divSlack(Path *before_div, - Path *after_div, + Path *after_div, const Edge *div_edge, - const TimingArc *div_arc, - const PathAnalysisPt *path_ap) + const TimingArc *div_arc) { Arrival before_div_arrival = before_div->arrival(); if (div_edge) { @@ -559,14 +560,17 @@ PathEnum::divSlack(Path *before_div, Arrival div_arrival; ArcDelay div_delay; Tag *q_tag; - latches_->latchOutArrival(after_div, div_arc, div_edge, path_ap, - q_tag, div_delay, div_arrival); + latches_->latchOutArrival(after_div, div_arc, div_edge, + q_tag, div_delay, div_arrival); return div_arrival - before_div_arrival; } else { + DcalcAPIndex dcalc_ap = before_div->dcalcAnalysisPtIndex(this); ArcDelay div_delay = search_->deratedDelay(div_edge->from(graph_), - div_arc, div_edge, - false, path_ap); + div_arc, div_edge, + 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; } @@ -581,13 +585,13 @@ PathEnum::divSlack(Path *before_div, // starting at "before" to the beginning of the path. void PathEnum::makeDiversions(PathEnd *path_end, - Path *before) + Path *before) { 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); + unique_edges_, this); while (prev_path) { // Fanin visitor does all the work. // While visiting the fanins the fanin_visitor finds the @@ -607,13 +611,13 @@ PathEnum::makeDiversions(PathEnd *path_end, void PathEnum::makeDivertedPath(Path *path, - Path *before_div, - Path *after_div, + Path *before_div, + Path *after_div, Edge *div_edge, - TimingArc *div_arc, - // Returned values. - Path *&div_path, - Path *&after_div_copy) + TimingArc *div_arc, + // Returned values. + Path *&div_path, + Path *&after_div_copy) { div_path = nullptr; after_div_copy = nullptr; @@ -633,6 +637,7 @@ PathEnum::makeDivertedPath(Path *path, p->prevEdge(this), p->prevArc(this), true, this); + search_->saveEnumPath(copy); if (prev_copy) prev_copy->setPrevPath(copy); copies.push_back(copy); @@ -665,7 +670,7 @@ PathEnum::makeDivertedPath(Path *path, void PathEnum::updatePathHeadDelays(PathSeq &paths, - Path *after_div) + Path *after_div) { Tag *prev_tag = after_div->tag(this); const ClkInfo *prev_clk_info = prev_tag->clkInfo(); @@ -678,46 +683,49 @@ PathEnum::updatePathHeadDelays(PathSeq &paths, Edge *edge = path->prevEdge(this); if (edge) { Arrival arrival; - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); const MinMax *min_max = path->minMax(this); if (i == path_idx_max - && edge->role()->isLatchDtoQ() - && min_max == MinMax::max()) { - ArcDelay arc_delay; - Tag *q_tag; - latches_->latchOutArrival(after_div, arc, edge, path_ap, - q_tag, arc_delay, arrival); - path->setArrival(arrival); - path->setTag(q_tag); - prev_clk_info = q_tag->clkInfo(); + && edge->role()->isLatchDtoQ() + && min_max == MinMax::max()) { + ArcDelay arc_delay; + Tag *q_tag; + latches_->latchOutArrival(after_div, arc, edge, + q_tag, arc_delay, arrival); + path->setArrival(arrival); + path->setTag(q_tag); + prev_clk_info = q_tag->clkInfo(); } else { - ArcDelay arc_delay = search_->deratedDelay(edge->from(graph_), - arc, edge, false, path_ap); - arrival = prev_arrival + arc_delay; - path->setArrival(arrival); - const Tag *tag = path->tag(this); - const ClkInfo *clk_info = tag->clkInfo(); - if (crprActive() - && clk_info != prev_clk_info - // D->Q paths use the EN->Q clk info so no need to update. - && 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->transition(this), - path_ap, - 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(), - delayAsString(path->arrival(), this), - delayAsString(arrival, this)); + ArcDelay arc_delay = search_->deratedDelay(edge->from(graph_), + arc, edge, false, + path->minMax(this), + path->dcalcAnalysisPtIndex(this), + path->sdc(this)); + arrival = prev_arrival + arc_delay; + path->setArrival(arrival); + const Tag *tag = path->tag(this); + const ClkInfo *clk_info = tag->clkInfo(); + if (crprActive(path->mode(this)) + && clk_info != prev_clk_info + // D->Q paths use the EN->Q clk info so no need to update. + && 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); + 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(), + delayAsString(path->arrival(), this), + delayAsString(arrival, this)); } prev_arrival = arrival; } diff --git a/search/PathEnum.hh b/search/PathEnum.hh index 1f13f1e9f..f2f68cd81 100644 --- a/search/PathEnum.hh +++ b/search/PathEnum.hh @@ -25,9 +25,10 @@ #pragma once #include +#include +#include "ContainerHelpers.hh" #include "Iterator.hh" -#include "Vector.hh" #include "StaState.hh" #include "SearchClass.hh" #include "Path.hh" @@ -38,9 +39,9 @@ class Diversion; class PathEnumFaninVisitor; class DiversionGreater; -typedef Vector DiversionSeq; -typedef std::priority_queue DiversionQueue; +using DiversionSeq = std::vector; +using DiversionQueue = std::priority_queue; class DiversionGreater { @@ -48,7 +49,7 @@ public: DiversionGreater(); DiversionGreater(const StaState *sta); bool operator()(Diversion *div1, - Diversion *div2) const; + Diversion *div2) const; private: const StaState *sta_; @@ -59,11 +60,11 @@ class PathEnum : public Iterator, StaState { public: PathEnum(size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - bool cmp_slack, - const StaState *sta); + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + bool cmp_slack, + const StaState *sta); // Insert path ends that are enumerated in slack/arrival order. void insert(PathEnd *path_end); virtual ~PathEnum(); @@ -72,23 +73,22 @@ public: private: void makeDiversions(PathEnd *path_end, - Path *before); + Path *before); void insert(Diversion *div); void makeDivertedPath(Path *path, - Path *before_div, - Path *after_div, + Path *before_div, + Path *after_div, Edge *div_edge, - TimingArc *div_arc, - // Returned values. - Path *&div_path, - Path *&after_div_copy); + TimingArc *div_arc, + // Returned values. + Path *&div_path, + Path *&after_div_copy); void updatePathHeadDelays(PathSeq &path, - Path *after_div); + Path *after_div); Arrival divSlack(Path *path, - Path *after_div, + Path *after_div, const Edge *div_edge, - const TimingArc *div_arc, - const PathAnalysisPt *path_ap); + const TimingArc *div_arc); void reportDiversionPath(Diversion *div); void pruneDiversionQueue(); void findNext(); diff --git a/search/PathExpanded.cc b/search/PathExpanded.cc index 170c280a7..6f77a595c 100644 --- a/search/PathExpanded.cc +++ b/search/PathExpanded.cc @@ -32,6 +32,7 @@ #include "Path.hh" #include "Latches.hh" #include "Genclks.hh" +#include "Mode.hh" namespace sta { @@ -41,16 +42,16 @@ PathExpanded::PathExpanded(const StaState *sta) : } PathExpanded::PathExpanded(const Path *path, - // Expand generated clk source paths. - bool expand_genclks, - const StaState *sta) : + // Expand generated clk source paths. + bool expand_genclks, + const StaState *sta) : sta_(sta) { expand(path, expand_genclks); } PathExpanded::PathExpanded(const Path *path, - const StaState *sta) : + const StaState *sta) : sta_(sta) { expand(path, false); @@ -58,9 +59,10 @@ PathExpanded::PathExpanded(const Path *path, void PathExpanded::expand(const Path *path, - bool expand_genclks) + bool expand_genclks) { const Latches *latches = sta_->latches(); + const Mode *mode = path->mode(sta_); // Push the paths from the end into an array of Paths. const Path *p = path; const Path *last_path = nullptr; @@ -71,25 +73,25 @@ PathExpanded::expand(const Path *path, if (!found_start) { if (prev_path) { const TimingArc *prev_arc = p->prevArc(sta_); - const TimingRole *prev_role = prev_arc->role(); - if (prev_role == TimingRole::regClkToQ() - || prev_role == TimingRole::latchEnToQ()) { - start_index_ = i; - found_start = true; - } - else if (prev_role == TimingRole::latchDtoQ()) { - const Edge *prev_edge = p->prevEdge(sta_); - if (prev_edge && latches->isLatchDtoQ(prev_edge)) { - start_index_ = i; - found_start = true; - - paths_.push_back(p); - // Push latch D path. - paths_.push_back(prev_path); - // This breaks latch loop paths. - break; - } - } + const TimingRole *prev_role = prev_arc->role(); + if (prev_role == TimingRole::regClkToQ() + || prev_role == TimingRole::latchEnToQ()) { + start_index_ = i; + found_start = true; + } + else if (prev_role->isLatchDtoQ()) { + const Edge *prev_edge = p->prevEdge(sta_); + if (prev_edge && latches->isLatchDtoQ(prev_edge, mode)) { + start_index_ = i; + found_start = true; + + paths_.push_back(p); + // Push latch D path. + paths_.push_back(prev_path); + // This breaks latch loop paths. + break; + } + } } } paths_.push_back(p); @@ -110,20 +112,21 @@ PathExpanded::expandGenclk(const Path *clk_path) if (clk_path) { const Clock *src_clk = clk_path->clock(sta_); if (src_clk && src_clk->isGenerated()) { - const Path *src_path = sta_->search()->genclks()->srcPath(clk_path); + const Mode *mode = clk_path->mode(sta_); + const Path *src_path = mode->genclks()->srcPath(clk_path); if (src_path) { - // The head of the genclk src path is already in paths_, - // so skip past it. - Path *prev_path = src_path->prevPath(); - Path *p = prev_path; - Path *last_path = nullptr; - while (p) { - prev_path = p->prevPath(); - paths_.push_back(p); - last_path = p; - p = prev_path; - } - expandGenclk(last_path); + // The head of the genclk src path is already in paths_, + // so skip past it. + Path *prev_path = src_path->prevPath(); + Path *p = prev_path; + Path *last_path = nullptr; + while (p) { + prev_path = p->prevPath(); + paths_.push_back(p); + last_path = p; + p = prev_path; + } + expandGenclk(last_path); } } } @@ -188,14 +191,15 @@ PathExpanded::clkPath() const const TimingArc *prev_arc = startPrevArc(); if (start && prev_arc) { const TimingRole *role = prev_arc->role(); - if (role == TimingRole::latchDtoQ()) { + if (role->isLatchDtoQ()) { Edge *prev_edge = start->prevEdge(sta_); - if (prev_edge && latches->isLatchDtoQ(prev_edge)) { - return latches->latchEnablePath(start, prev_edge); + const Mode *mode = start->mode(sta_); + if (prev_edge && latches->isLatchDtoQ(prev_edge, mode)) { + return latches->latchEnablePath(start, prev_edge); } } else if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) { + || role == TimingRole::latchEnToQ()) { const Path *start_prev = startPrevPath(); if (start_prev) return start_prev; @@ -208,9 +212,9 @@ PathExpanded::clkPath() const void PathExpanded::latchPaths(// Return values. - const Path *&d_path, - const Path *&q_path, - Edge *&d_q_edge) const + const Path *&d_path, + const Path *&q_path, + Edge *&d_q_edge) const { d_path = nullptr; q_path = nullptr; @@ -221,9 +225,10 @@ PathExpanded::latchPaths(// Return values. && prev_arc && prev_arc->role() == TimingRole::latchDtoQ()) { Edge *prev_edge = start->prevEdge(sta_); + const Mode *mode = start->mode(sta_); // This breaks latch loop paths. if (prev_edge - && sta_->latches()->isLatchDtoQ(prev_edge)) { + && sta_->latches()->isLatchDtoQ(prev_edge, mode)) { d_path = startPrevPath(); q_path = start; d_q_edge = prev_edge; diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 3ce7443a7..6ac127f03 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -26,7 +26,11 @@ #include #include +#include +#include +#include "ContainerHelpers.hh" +#include "StringUtil.hh" #include "Stats.hh" #include "Debug.hh" #include "Mutex.hh" @@ -35,11 +39,11 @@ #include "DispatchQueue.hh" #include "ExceptionPath.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Graph.hh" #include "PathEnd.hh" -#include "PathAnalysisPt.hh" #include "Tag.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Search.hh" #include "VisitPathEnds.hh" #include "PathEnum.hh" @@ -50,43 +54,43 @@ size_t PathGroup::group_path_count_max = std::numeric_limits::max(); PathGroup * PathGroup::makePathGroupSlack(const char *name, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - const StaState *sta) + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + const StaState *sta) { return new PathGroup(name, group_path_count, endpoint_path_count, - unique_pins, unique_edges, slack_min, slack_max, - true, MinMax::min(), sta); + unique_pins, unique_edges, slack_min, slack_max, + true, MinMax::min(), sta); } PathGroup * PathGroup::makePathGroupArrival(const char *name, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - const MinMax *min_max, - const StaState *sta) + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + const MinMax *min_max, + const StaState *sta) { return new PathGroup(name, group_path_count, endpoint_path_count, - unique_pins, unique_edges, 0.0, 0.0, - false, min_max, sta); + unique_pins, unique_edges, 0.0, 0.0, + false, min_max, sta); } PathGroup::PathGroup(const char *name, - size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - bool cmp_slack, - const MinMax *min_max, - const StaState *sta) : + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + bool cmp_slack, + const MinMax *min_max, + const StaState *sta) : name_(name), group_path_count_(group_path_count), endpoint_path_count_(endpoint_path_count), @@ -103,7 +107,7 @@ PathGroup::PathGroup(const char *name, PathGroup::~PathGroup() { - path_ends_.deleteContents(); + deleteContents(path_ends_); } bool @@ -119,13 +123,13 @@ PathGroup::saveable(PathEnd *path_end) // without crpr first because it is expensive to find. Slack slack = path_end->slackNoCrpr(sta_); if (!delayIsInitValue(slack, min_max_) - && delayLessEqual(slack, threshold, sta_) - && delayLessEqual(slack, slack_max_, sta_)) { + && delayLessEqual(slack, threshold, sta_) + && delayLessEqual(slack, slack_max_, sta_)) { // Now check with crpr. slack = path_end->slack(sta_); return delayLessEqual(slack, threshold, sta_) - && delayLessEqual(slack, slack_max_, sta_) - && delayGreaterEqual(slack, slack_min_, sta_); + && delayLessEqual(slack, slack_max_, sta_) + && delayGreaterEqual(slack, slack_min_, sta_); } } else { @@ -148,11 +152,11 @@ PathGroup::enumMinSlackUnderMin(PathEnd *path_end) && endpoint_path_count_ > 1 && slack_min_ > -INF) { const Path *path = path_end->path(); - PathAnalysisPt *other_ap = path->pathAnalysisPt(sta_)->tgtClkAnalysisPt(); const Tag *tag = path->tag(sta_); VertexPathIterator other_iter(path->vertex(sta_), - path->transition(sta_), - other_ap, sta_); + path->scene(sta_), + path->tgtClkMinMax(sta_), + path->transition(sta_), sta_); while (other_iter.hasNext()) { Path *other = other_iter.next(); if (Tag::matchCrpr(other->tag(sta_), tag)) { @@ -192,7 +196,7 @@ PathGroup::prune() // Squish up to endpoint_path_count path ends per vertex // up to the front of path_ends_. if (end_count < group_path_count_ - && path_counts[vertex] < endpoint_path_count_) { + && path_counts[vertex] < endpoint_path_count_) { path_ends_[end_count++] = path_end; path_counts[vertex]++; } @@ -218,13 +222,6 @@ PathGroup::pushEnds(PathEndSeq &path_ends) path_ends.push_back(path_end); } -PathGroupIterator * -PathGroup::iterator() -{ - ensureSortedMaxPaths(); - return new PathGroupIterator(path_ends_); -} - void PathGroup::ensureSortedMaxPaths() { @@ -256,21 +253,22 @@ const char *PathGroups::async_group_name_ = "asynchronous"; const char *PathGroups::unconstrained_group_name_ = "unconstrained"; PathGroups::PathGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold, - bool unconstrained, - const StaState *sta) : - StaState(sta), + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StdStringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold, + bool unconstrained, + const Mode *mode) : + StaState(mode), + mode_(mode), group_path_count_(group_path_count), endpoint_path_count_(endpoint_path_count), unique_pins_(unique_pins), @@ -278,56 +276,61 @@ PathGroups::PathGroups(int group_path_count, slack_min_(slack_min), slack_max_(slack_max) { + StdStringSet groups; + for (std::string &group_name : group_names) + groups.insert(group_name); + makeGroups(group_path_count, endpoint_path_count, unique_pins, unique_edges, - slack_min, slack_max, group_names, - setup, recovery, clk_gating_setup, unconstrained, - MinMax::max()); + slack_min, slack_max, groups, + setup, recovery, clk_gating_setup, unconstrained, + MinMax::max()); makeGroups(group_path_count, endpoint_path_count, unique_pins, unique_edges, - slack_min, slack_max, group_names, - hold, removal, clk_gating_hold, unconstrained, - MinMax::min()); + slack_min, slack_max, groups, + hold, removal, clk_gating_hold, unconstrained, + MinMax::min()); } void PathGroups::makeGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup_hold, - bool async, - bool gated_clk, - bool unconstrained, - const MinMax *min_max) + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + StdStringSet &group_names, + bool setup_hold, + bool async, + bool gated_clk, + bool unconstrained, + const MinMax *min_max) { int mm_index = min_max->index(); if (setup_hold) { - for (const auto [name, group] : sdc_->groupPaths()) { + const Sdc *sdc = mode_->sdc(); + for (const auto& [name, group] : sdc->groupPaths()) { if (reportGroup(name, group_names)) { - PathGroup *group = PathGroup::makePathGroupSlack(name, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); - named_map_[mm_index][name] = group; + PathGroup *group = PathGroup::makePathGroupSlack(name, + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); + named_map_[mm_index][name] = group; } } - for (auto clk : sdc_->clks()) { + for (Clock *clk : sdc->clocks()) { const char *clk_name = clk->name(); if (reportGroup(clk_name, group_names)) { - PathGroup *group = PathGroup::makePathGroupSlack(clk_name, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); - clk_map_[mm_index][clk] = group; + PathGroup *group = PathGroup::makePathGroupSlack(clk_name, + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); + clk_map_[mm_index][clk] = group; } } } @@ -335,36 +338,36 @@ PathGroups::makeGroups(int group_path_count, if (setup_hold && reportGroup(path_delay_group_name_, group_names)) path_delay_[mm_index] = PathGroup::makePathGroupSlack(path_delay_group_name_, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); else path_delay_[mm_index] = nullptr; if (gated_clk && reportGroup(gated_clk_group_name_, group_names)) gated_clk_[mm_index] = PathGroup::makePathGroupSlack(gated_clk_group_name_, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); else gated_clk_[mm_index] = nullptr; if (async && reportGroup(async_group_name_, group_names)) async_[mm_index] = PathGroup::makePathGroupSlack(async_group_name_, - group_path_count, - endpoint_path_count, - unique_pins, - unique_edges, - slack_min, slack_max, - this); + group_path_count, + endpoint_path_count, + unique_pins, + unique_edges, + slack_min, slack_max, + this); else async_[mm_index] = nullptr; @@ -372,8 +375,8 @@ PathGroups::makeGroups(int group_path_count, && reportGroup(unconstrained_group_name_, group_names)) unconstrained_[mm_index] = PathGroup::makePathGroupArrival(unconstrained_group_name_, - group_path_count, endpoint_path_count, - unique_pins, unique_edges, min_max, this); + group_path_count, endpoint_path_count, + unique_pins, unique_edges, min_max, this); else unconstrained_[mm_index] = nullptr; } @@ -381,8 +384,8 @@ PathGroups::makeGroups(int group_path_count, PathGroups::~PathGroups() { for (auto mm_index : MinMax::rangeIndex()) { - named_map_[mm_index].deleteContents(); - clk_map_[mm_index].deleteContents(); + deleteContents(named_map_[mm_index]); + deleteContents(clk_map_[mm_index]); delete path_delay_[mm_index]; delete gated_clk_[mm_index]; delete async_[mm_index]; @@ -392,25 +395,32 @@ PathGroups::~PathGroups() PathGroup * PathGroups::findPathGroup(const char *name, - const MinMax *min_max) const + const MinMax *min_max) const { - return named_map_[min_max->index()].findKey(name); + auto itr = named_map_[min_max->index()].find(name); + if (itr != named_map_[min_max->index()].end()) + return itr->second; + else + return nullptr; } PathGroup * PathGroups::findPathGroup(const Clock *clock, - const MinMax *min_max) const + const MinMax *min_max) const { - return clk_map_[min_max->index()].findKey(clock); + auto itr = clk_map_[min_max->index()].find(clock); + if (itr != clk_map_[min_max->index()].end()) + return itr->second; + else + return nullptr; } bool PathGroups::reportGroup(const char *group_name, - PathGroupNameSet *group_names) const + StdStringSet &group_names) const { - return group_names == nullptr - || group_names->empty() - || group_names->hasKey(group_name); + return group_names.empty() + || group_names.contains(group_name); } PathGroupSeq @@ -427,12 +437,12 @@ PathGroups::pathGroups(const PathEnd *path_end) const else if (!group_paths.empty()) { for (ExceptionPath *group_path : group_paths) { if (group_path->isDefault()) - path_groups.push_back(path_delay_[mm_index]); + path_groups.push_back(path_delay_[mm_index]); else { - const char *group_name = group_path->name(); - PathGroup *group = findPathGroup(group_name, min_max); - if (group) - path_groups.push_back(group); + const char *group_name = group_path->name(); + PathGroup *group = findPathGroup(group_name, min_max); + if (group) + path_groups.push_back(group); } } } @@ -440,7 +450,7 @@ PathGroups::pathGroups(const PathEnd *path_end) const const TimingRole *check_role = path_end->checkRole(this); const Clock *tgt_clk = path_end->targetClk(this); if (check_role == TimingRole::removal() - || check_role == TimingRole::recovery()) + || check_role == TimingRole::recovery()) path_group = async_[mm_index]; else path_group = findPathGroup(tgt_clk, min_max); @@ -456,7 +466,7 @@ PathGroups::pathGroups(const PathEnd *path_end) const PathDelay *path_delay = path_end->pathDelay(); const Clock *tgt_clk = path_end->targetClk(this); if (tgt_clk - && !path_delay->ignoreClkLatency()) + && !path_delay->ignoreClkLatency()) path_group = findPathGroup(tgt_clk, min_max); else path_group = path_delay_[mm_index]; @@ -469,7 +479,7 @@ PathGroups::pathGroups(const PathEnd *path_end) const // Mirrors PathGroups::pathGroup. StdStringSeq PathGroups::pathGroupNames(const PathEnd *path_end, - const StaState *sta) + const StaState *sta) { StdStringSeq group_names; const char *group_name = nullptr; @@ -481,22 +491,22 @@ PathGroups::pathGroupNames(const PathEnd *path_end, // GroupPaths have precedence. for (ExceptionPath *group_path : group_paths) { if (group_path->isDefault()) - group_names.push_back(path_delay_group_name_); + group_names.push_back(path_delay_group_name_); else - group_names.push_back(group_path->name()); + group_names.push_back(group_path->name()); } } else if (path_end->isCheck() || path_end->isLatchCheck()) { const TimingRole *check_role = path_end->checkRole(sta); const Clock *tgt_clk = path_end->targetClk(sta); if (check_role == TimingRole::removal() - || check_role == TimingRole::recovery()) + || check_role == TimingRole::recovery()) group_name = async_group_name_; else group_name = tgt_clk->name(); } else if (path_end->isOutputDelay() - || path_end->isDataCheck()) { + || path_end->isDataCheck()) { const Clock *tgt_clk = path_end->targetClk(sta); if (tgt_clk) group_name = tgt_clk->name(); @@ -509,7 +519,7 @@ PathGroups::pathGroupNames(const PathEnd *path_end, PathDelay *path_delay = path_end->pathDelay(); const Clock *tgt_clk = path_end->targetClk(sta); if (tgt_clk - && !path_delay->ignoreClkLatency()) + && !path_delay->ignoreClkLatency()) group_name = tgt_clk->name(); else group_name = path_delay_group_name_; @@ -519,16 +529,30 @@ PathGroups::pathGroupNames(const PathEnd *path_end, return group_names; } +GroupPath * +PathGroups::groupPathTo(const PathEnd *path_end, + const StaState *sta) +{ + const Path *path = path_end->path(); + const Pin *pin = path->pin(sta); + ExceptionPath *exception = + sta->search()->exceptionTo(ExceptionPathType::group_path, path, + pin, path->transition(sta), + path_end->targetClkEdge(sta), + path->minMax(sta), false, false, + path->sdc(sta)); + return dynamic_cast(exception); +} + void -PathGroups::pushGroupPathEnds(PathEndSeq &path_ends) +PathGroups::pushEnds(PathEndSeq &path_ends) { - for (auto min_max : MinMax::range()) { + for (const MinMax *min_max : MinMax::range()) { int mm_index = min_max->index(); - for (auto name_group : sdc_->groupPaths()) { - const char *name = name_group.first; - PathGroup *path_group = findPathGroup(name, min_max); + for (std::string &group_name : pathGroupNames()) { + PathGroup *path_group = findPathGroup(group_name.c_str(), min_max); if (path_group) - path_group->pushEnds(path_ends); + path_group->pushEnds(path_ends); } if (async_[mm_index]) @@ -540,61 +564,72 @@ PathGroups::pushGroupPathEnds(PathEndSeq &path_ends) if (path_delay_[mm_index]) path_delay_[mm_index]->pushEnds(path_ends); - ClockSeq clks; - sdc_->sortedClocks(clks); - ClockSeq::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + const Sdc *sdc = mode_->sdc(); + ClockSeq clks = sdc->sortedClocks(); + for (Clock *clk : clks) { PathGroup *path_group = findPathGroup(clk, min_max); if (path_group) - path_group->pushEnds(path_ends); + path_group->pushEnds(path_ends); } } } +StdStringSeq +PathGroups::pathGroupNames() +{ + std::set group_names1; + const Sdc *sdc = mode_->sdc(); + for (const auto& [name, group] : sdc->groupPaths()) + group_names1.insert(name); + StdStringSeq group_names2; + for (const std::string &name : group_names1) + group_names2.push_back(name); + sort(group_names2); + return group_names2; +} + + void PathGroups::pushUnconstrainedPathEnds(PathEndSeq &path_ends, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Set groups; - for (auto path_ap : corners_->pathAnalysisPts()) { - const MinMax *path_min_max = path_ap->pathMinMax(); - if (min_max->matches(path_min_max)) { - int mm_index = path_min_max->index(); - PathGroup *group = unconstrained_[mm_index]; - if (group - // For multiple corner path APs use the same group. - // Only report it once. - && !groups.findKey(group)) { - group->pushEnds(path_ends); - groups.insert(group); - } + std::set groups; + for (const MinMax *mm : min_max->range()) { + int mm_index = mm->index(); + PathGroup *group = unconstrained_[mm_index]; + if (group + // For multiple scene path APs use the same group. + // Only report it once. + && !groups.contains(group)) { + group->pushEnds(path_ends); + groups.insert(group); } } } //////////////////////////////////////////////////////////////// -typedef Map PathGroupEndMap; -typedef Map PathGroupEndsMap; -typedef Set PathEndNoCrprSet; +using PathGroupEndMap = std::map; +using PathGroupEndsMap = std::map; +using PathEndNoCrprSet = std::set; static bool exceptionToEmpty(ExceptionTo *to); -PathEndSeq +void PathGroups::makePathEnds(ExceptionTo *to, - bool unconstrained_paths, - const Corner *corner, - const MinMaxAll *min_max, - bool sort_by_slack) + const SceneSeq &scenes, + const MinMaxAll *min_max, + bool sort_by_slack, + bool unconstrained_paths, + // Return value. + PathEndSeq &path_ends) { Stats stats(debug_, report_); makeGroupPathEnds(to, group_path_count_, endpoint_path_count_, - unique_pins_, unique_edges_, corner, min_max); + unique_pins_, unique_edges_, scenes, min_max); - PathEndSeq path_ends; - pushGroupPathEnds(path_ends); + pushEnds(path_ends); if (sort_by_slack) { sort(path_ends, PathEndLess(this)); } @@ -605,7 +640,6 @@ PathGroups::makePathEnds(ExceptionTo *to, pushUnconstrainedPathEnds(path_ends, min_max); stats.report("Make path ends"); - return path_ends; } //////////////////////////////////////////////////////////////// @@ -623,7 +657,7 @@ class MakePathEnds1 : public PathEndVisitor private: void visitPathEnd(PathEnd *path_end, - PathGroup *group); + PathGroup *group); PathGroups *path_groups_; PathGroupEndMap ends_; @@ -651,15 +685,15 @@ MakePathEnds1::visit(PathEnd *path_end) void MakePathEnds1::visitPathEnd(PathEnd *path_end, - PathGroup *group) + PathGroup *group) { if (group->saveable(path_end)) { // Only keep the path end with the smallest slack/latest arrival. - PathEnd *worst_end = ends_.findKey(group); + PathEnd *worst_end = findKey(ends_, group); if (worst_end) { if (cmp_(path_end, worst_end)) { - ends_[group] = path_end->copy(); - delete worst_end; + ends_[group] = path_end->copy(); + delete worst_end; } } else @@ -671,11 +705,7 @@ MakePathEnds1::visitPathEnd(PathEnd *path_end, void MakePathEnds1::vertexEnd(Vertex *) { - PathGroupEndMap::Iterator group_iter(ends_); - while (group_iter.hasNext()) { - PathGroup *group; - PathEnd *end; - group_iter.next(group, end); + for (auto [group, end] : ends_) { // visitPathEnd already confirmed slack is saveable. if (end) { group->insert(end); @@ -703,7 +733,7 @@ class MakePathEndsAll : public PathEndVisitor private: void visitPathEnd(PathEnd *path_end, - PathGroup *group); + PathGroup *group); int endpoint_path_count_; PathGroups *path_groups_; @@ -714,7 +744,7 @@ class MakePathEndsAll : public PathEndVisitor }; MakePathEndsAll::MakePathEndsAll(int endpoint_path_count, - PathGroups *path_groups) : + PathGroups *path_groups) : endpoint_path_count_(endpoint_path_count), path_groups_(path_groups), sta_(path_groups), @@ -732,13 +762,7 @@ MakePathEndsAll::copy() const MakePathEndsAll::~MakePathEndsAll() { - PathGroupEndsMap::Iterator group_iter(ends_); - while (group_iter.hasNext()) { - PathGroup *group; - PathEndSeq *ends; - group_iter.next(group, ends); - delete ends; - } + deleteContents(ends_); } void @@ -750,9 +774,9 @@ MakePathEndsAll::visit(PathEnd *path_end) void MakePathEndsAll::visitPathEnd(PathEnd *path_end, - PathGroup *group) + PathGroup *group) { - PathEndSeq *ends = ends_.findKey(group); + PathEndSeq *ends = findKey(ends_, group); if (ends == nullptr) { ends = new PathEndSeq; ends_[group] = ends; @@ -764,50 +788,41 @@ void MakePathEndsAll::vertexEnd(Vertex *) { Debug *debug = sta_->debug(); - PathGroupEndsMap::Iterator group_iter(ends_); - while (group_iter.hasNext()) { - PathGroup *group; - PathEndSeq *ends; - group_iter.next(group, ends); + for (auto [group, ends] : ends_) { if (ends) { sort(ends, slack_cmp_); PathEndNoCrprSet unique_ends(path_no_crpr_cmp_); - PathEndSeq::Iterator end_iter(ends); + auto end_iter = ends->begin(); int n = 0; - while (end_iter.hasNext() - && n < endpoint_path_count_) { - PathEnd *path_end = end_iter.next(); - // Only save the worst path end for each crpr tag. - // PathEnum will peel the others. - if (!unique_ends.hasKey(path_end)) { - debugPrint(debug, "path_group", 2, "insert %s %s %s %d", + while (end_iter != ends->end() + && n < endpoint_path_count_) { + PathEnd *path_end = *end_iter++; + // 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(), path_end->typeName(), path_end->transition(sta_)->to_string().c_str(), path_end->path()->tag(sta_)->index()); - // Give the group a copy of the path end because - // it may delete it during pruning. - if (group->saveable(path_end) + // Give the group a copy of the path end because + // it may delete it during pruning. + if (group->saveable(path_end) || group->enumMinSlackUnderMin(path_end)) { - group->insert(path_end->copy()); - unique_ends.insert(path_end); - n++; - } - } - else - debugPrint(debug, "path_group", 3, "prune %s %s %s %d", + group->insert(path_end->copy()); + unique_ends.insert(path_end); + n++; + } + } + else + debugPrint(debug, "path_group", 3, "prune %s %s %s %d", path_end->vertex(sta_)->to_string(sta_).c_str(), path_end->typeName(), path_end->transition(sta_)->to_string().c_str(), path_end->path()->tag(sta_)->index()); } // Clear ends for next vertex. - PathEndSeq::Iterator end_iter2(ends); - while (end_iter2.hasNext()) { - PathEnd *path_end = end_iter2.next(); - delete path_end; - } - ends->clear(); + deleteContents(*ends); } } } @@ -816,78 +831,76 @@ MakePathEndsAll::vertexEnd(Vertex *) void PathGroups::makeGroupPathEnds(ExceptionTo *to, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - const Corner *corner, - const MinMaxAll *min_max) + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + const SceneSeq &scenes, + const MinMaxAll *min_max) { if (endpoint_path_count == 1) { MakePathEnds1 make_path_ends(this); - makeGroupPathEnds(to, corner, min_max, &make_path_ends); + makeGroupPathEnds(to, scenes, min_max, &make_path_ends); } else { MakePathEndsAll make_path_ends(endpoint_path_count, this); - makeGroupPathEnds(to, corner, min_max, &make_path_ends); + makeGroupPathEnds(to, scenes, min_max, &make_path_ends); - for (auto path_min_max : MinMax::range()) { + for (const MinMax *path_min_max : MinMax::range()) { int mm_index = path_min_max->index(); - for (auto name_group : sdc_->groupPaths()) { - const char *name = name_group.first; - PathGroup *group = findPathGroup(name, path_min_max); + for (const Mode *mode : Scene::modes(scenes)) { + const Sdc *sdc = mode->sdc(); + for (const auto& [name, groups] : sdc->groupPaths()) { + PathGroup *group = findPathGroup(name, path_min_max); + if (group) + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, true); + } + } + const Sdc *sdc = mode_->sdc(); + for (const Clock *clk : sdc->clocks()) { + PathGroup *group = findPathGroup(clk, path_min_max); if (group) enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); + unique_pins, unique_edges, true); } - - for (auto clk : sdc_->clks()) { - PathGroup *group = findPathGroup(clk, path_min_max); - if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); - } - PathGroup *group = unconstrained_[mm_index]; if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, false); + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, false); group = path_delay_[mm_index]; if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, true); group = gated_clk_[mm_index]; if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, true); group = async_[mm_index]; if (group) - enumPathEnds(group, group_path_count, endpoint_path_count, - unique_pins, unique_edges, true); + enumPathEnds(group, group_path_count, endpoint_path_count, + unique_pins, unique_edges, true); } } } void PathGroups::enumPathEnds(PathGroup *group, - int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - bool cmp_slack) + int group_path_count, + int endpoint_path_count, + bool unique_pins, + bool unique_edges, + bool cmp_slack) { // Insert the worst max_path path ends in the group into a path // enumerator. PathEnum path_enum(group_path_count, endpoint_path_count, - unique_pins, unique_edges, cmp_slack, this); - PathGroupIterator *end_iter = group->iterator(); - while (end_iter->hasNext()) { - PathEnd *end = end_iter->next(); + unique_pins, unique_edges, cmp_slack, this); + for (PathEnd *end : group->pathEnds()) { if (group->saveable(end) || group->enumMinSlackUnderMin(end)) path_enum.insert(end); } - delete end_iter; group->clear(); // Parallel path enumeratation to find the endpoint_path_count/max path ends. @@ -902,32 +915,28 @@ PathGroups::enumPathEnds(PathGroup *group, void PathGroups::makeGroupPathEnds(ExceptionTo *to, - const Corner *corner, - const MinMaxAll *min_max, - PathEndVisitor *visitor) + const SceneSeq &scenes, + const MinMaxAll *min_max, + PathEndVisitor *visitor) { - Network *network = this->network(); - Graph *graph = this->graph(); - Search *search = this->search(); if (exceptionToEmpty(to)) - makeGroupPathEnds(search->endpoints(), corner, min_max, visitor); + makeGroupPathEnds(search_->endpoints(), scenes, min_max, visitor); else { // Only visit -to filter pins. - VertexSet endpoints(graph_); - PinSet pins = to->allPins(network); - PinSet::Iterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + ModeSeq modes = Scene::modes(scenes); + VertexSet endpoints = makeVertexSet(this); + PinSet pins = to->allPins(network_); + for (const Pin *pin : pins) { Vertex *vertex, *bidirect_drvr_vertex; - graph->pinVertices(pin, vertex, bidirect_drvr_vertex); + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex - && search->isEndpoint(vertex)) - endpoints.insert(vertex); + && search_->isEndpoint(vertex, modes)) + endpoints.insert(vertex); if (bidirect_drvr_vertex - && search->isEndpoint(bidirect_drvr_vertex)) - endpoints.insert(bidirect_drvr_vertex); + && search_->isEndpoint(bidirect_drvr_vertex, modes)) + endpoints.insert(bidirect_drvr_vertex); } - makeGroupPathEnds(&endpoints, corner, min_max, visitor); + makeGroupPathEnds(endpoints, scenes, min_max, visitor); } } @@ -936,7 +945,7 @@ exceptionToEmpty(ExceptionTo *to) { return to == nullptr || (to->pins() == nullptr - && to->instances() == nullptr); + && to->instances() == nullptr); } //////////////////////////////////////////////////////////////// @@ -945,9 +954,9 @@ class MakeEndpointPathEnds : public VertexVisitor { public: MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, - const Corner *corner, - const MinMaxAll *min_max, - const StaState *sta); + const SceneSet &scenes, + const MinMaxAll *min_max, + const StaState *sta); MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends); ~MakeEndpointPathEnds(); virtual VertexVisitor *copy() const; @@ -956,18 +965,18 @@ class MakeEndpointPathEnds : public VertexVisitor private: VisitPathEnds visit_path_ends_; PathEndVisitor *path_end_visitor_; - const Corner *corner_; + const SceneSet scenes_; const MinMaxAll *min_max_; const StaState *sta_; }; MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, - const Corner *corner, - const MinMaxAll *min_max, - const StaState *sta) : + const SceneSet &scenes, + const MinMaxAll *min_max, + const StaState *sta) : visit_path_ends_(sta), path_end_visitor_(path_end_visitor->copy()), - corner_(corner), + scenes_(scenes), min_max_(min_max), sta_(sta) { @@ -976,7 +985,7 @@ MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, MakeEndpointPathEnds::MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends) : visit_path_ends_(make_path_ends.sta_), path_end_visitor_(make_path_ends.path_end_visitor_->copy()), - corner_(make_path_ends.corner_), + scenes_(make_path_ends.scenes_), min_max_(make_path_ends.min_max_), sta_(make_path_ends.sta_) { @@ -990,33 +999,36 @@ MakeEndpointPathEnds::~MakeEndpointPathEnds() VertexVisitor * MakeEndpointPathEnds::copy() const { - return new MakeEndpointPathEnds(path_end_visitor_, corner_, min_max_, sta_); + return new MakeEndpointPathEnds(path_end_visitor_, scenes_, min_max_, sta_); } void MakeEndpointPathEnds::visit(Vertex *vertex) { - visit_path_ends_.visitPathEnds(vertex, corner_, min_max_, true, path_end_visitor_); + visit_path_ends_.visitPathEnds(vertex, scenes_, min_max_, + true, path_end_visitor_); } //////////////////////////////////////////////////////////////// void -PathGroups::makeGroupPathEnds(VertexSet *endpoints, - const Corner *corner, - const MinMaxAll *min_max, - PathEndVisitor *visitor) +PathGroups::makeGroupPathEnds(VertexSet &endpoints, + const SceneSeq &scenes, + const MinMaxAll *min_max, + PathEndVisitor *visitor) { if (thread_count_ == 1) { - MakeEndpointPathEnds end_visitor(visitor, corner, min_max, this); - for (auto endpoint : *endpoints) + MakeEndpointPathEnds end_visitor(visitor, Scene::sceneSet(scenes), + min_max, this); + for (Vertex *endpoint : endpoints) end_visitor.visit(endpoint); } else { - Vector visitors(thread_count_, - MakeEndpointPathEnds(visitor, corner, - min_max, this)); - for (const auto endpoint : *endpoints) { + std::vector + visitors(thread_count_, + MakeEndpointPathEnds(visitor, Scene::sceneSet(scenes), + min_max, this)); + for (const auto endpoint : endpoints) { dispatch_queue_->dispatch( [endpoint, &visitors](int i) { visitors[i].visit(endpoint); } ); } diff --git a/search/Property.cc b/search/Property.cc index ad63155c4..07eddc0ac 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -34,7 +34,7 @@ #include "Network.hh" #include "Graph.hh" #include "Clock.hh" -#include "Corner.hh" +#include "Scene.hh" #include "PathEnd.hh" #include "PathExpanded.hh" #include "Path.hh" @@ -50,9 +50,9 @@ class PropertyUnknown : public Exception { public: PropertyUnknown(const char *type, - const char *property); + const char *property); PropertyUnknown(const char *type, - const string property); + const string property); virtual ~PropertyUnknown() {} virtual const char *what() const noexcept; @@ -62,7 +62,7 @@ class PropertyUnknown : public Exception }; PropertyUnknown::PropertyUnknown(const char *type, - const char *property) : + const char *property) : Exception(), type_(type), property_(property) @@ -70,7 +70,7 @@ PropertyUnknown::PropertyUnknown(const char *type, } PropertyUnknown::PropertyUnknown(const char *type, - const string property) : + const string property) : Exception(), type_(type), property_(property) @@ -81,7 +81,7 @@ const char * PropertyUnknown::what() const noexcept { return stringPrint("%s objects do not have a %s property.", - type_, property_.c_str()); + type_, property_.c_str()); } //////////////////////////////////////////////////////////////// @@ -111,25 +111,25 @@ const char * PropertyTypeWrong::what() const noexcept { return stringPrint("property accessor %s is only valid for %s properties.", - accessor_, type_); + accessor_, type_); } //////////////////////////////////////////////////////////////// PropertyValue::PropertyValue() : - type_(type_none), + type_(Type::none), unit_(nullptr) { } PropertyValue::PropertyValue(const char *value) : - type_(type_string), + type_(Type::string), string_(stringCopy(value)), unit_(nullptr) { } PropertyValue::PropertyValue(std::string &value) : - type_(type_string), + type_(Type::string), string_(stringCopy(value.c_str())), unit_(nullptr) { @@ -137,148 +137,139 @@ PropertyValue::PropertyValue(std::string &value) : PropertyValue::PropertyValue(float value, const Unit *unit) : - type_(type_float), + type_(Type::float_), float_(value), unit_(unit) { } PropertyValue::PropertyValue(bool value) : - type_(type_bool), + type_(Type::bool_), bool_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const LibertyLibrary *value) : - type_(type_liberty_library), + type_(Type::liberty_library), liberty_library_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const LibertyCell *value) : - type_(type_liberty_cell), + type_(Type::liberty_cell), liberty_cell_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const LibertyPort *value) : - type_(type_liberty_port), + type_(Type::liberty_port), liberty_port_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Library *value) : - type_(type_library), + type_(Type::library), library_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Cell *value) : - type_(type_cell), + type_(Type::cell), cell_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Port *value) : - type_(type_port), + type_(Type::port), port_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Instance *value) : - type_(type_instance), + type_(Type::instance), inst_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Pin *value) : - type_(type_pin), + type_(Type::pin), pin_(value), unit_(nullptr) { } PropertyValue::PropertyValue(PinSeq *value) : - type_(type_pins), + type_(Type::pins), pins_(value), unit_(nullptr) { } PropertyValue::PropertyValue(PinSet *value) : - type_(type_pins), + type_(Type::pins), pins_(new PinSeq), unit_(nullptr) { - PinSet::Iterator pin_iter(value); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - pins_->push_back( pin); - } + for (const Pin *pin : *value) + pins_->push_back(pin); } PropertyValue::PropertyValue(const PinSet &value) : - type_(type_pins), + type_(Type::pins), pins_(new PinSeq), unit_(nullptr) { - PinSet::ConstIterator pin_iter(value); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - pins_->push_back( pin); - } + for (const Pin *pin : value) + pins_->push_back(pin); } PropertyValue::PropertyValue(const Net *value) : - type_(type_net), + type_(Type::net), net_(value), unit_(nullptr) { } PropertyValue::PropertyValue(const Clock *value) : - type_(type_clk), + type_(Type::clk), clk_(value), unit_(nullptr) { } PropertyValue::PropertyValue(ClockSeq *value) : - type_(type_clks), + type_(Type::clks), clks_(new ClockSeq(*value)), unit_(nullptr) { } PropertyValue::PropertyValue(ClockSet *value) : - type_(type_clks), + type_(Type::clks), clks_(new ClockSeq), unit_(nullptr) { - ClockSet::Iterator clk_iter(value); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + for (Clock *clk : *value) clks_->push_back(clk); - } } PropertyValue::PropertyValue(ConstPathSeq *value) : - type_(type_paths), + type_(Type::paths), paths_(new ConstPathSeq(*value)), unit_(nullptr) { } PropertyValue::PropertyValue(PwrActivity *value) : - type_(type_pwr_activity), + type_(Type::pwr_activity), pwr_activity_(*value), unit_(nullptr) { @@ -289,125 +280,125 @@ PropertyValue::PropertyValue(const PropertyValue &value) : unit_(value.unit_) { switch (type_) { - case Type::type_none: + case Type::none: break; - case Type::type_string: + case Type::string: string_ = stringCopy(value.string_); break; - case Type::type_float: + case Type::float_: float_ = value.float_; break; - case Type::type_bool: + case Type::bool_: bool_ = value.bool_; break; - case Type::type_liberty_library: + case Type::liberty_library: liberty_library_ = value.liberty_library_; break; - case Type::type_liberty_cell: + case Type::liberty_cell: liberty_cell_ = value.liberty_cell_; break; - case Type::type_liberty_port: + case Type::liberty_port: liberty_port_ = value.liberty_port_; break; - case Type::type_library: + case Type::library: library_ = value.library_; break; - case Type::type_cell: + case Type::cell: cell_ = value.cell_; break; - case Type::type_port: + case Type::port: port_ = value.port_; break; - case Type::type_instance: + case Type::instance: inst_ = value.inst_; break; - case Type::type_pin: + case Type::pin: pin_ = value.pin_; break; - case Type::type_pins: + case Type::pins: pins_ = value.pins_ ? new PinSeq(*value.pins_) : nullptr; break; - case Type::type_net: + case Type::net: net_ = value.net_; break; - case Type::type_clk: + case Type::clk: clk_ = value.clk_; break; - case Type::type_clks: + case Type::clks: clks_ = value.clks_ ? new ClockSeq(*value.clks_) : nullptr; break; - case Type::type_paths: + case Type::paths: paths_ = value.paths_ ? new ConstPathSeq(*value.paths_) : nullptr; break; - case Type::type_pwr_activity: + case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; break; } } -PropertyValue::PropertyValue(PropertyValue &&value) : +PropertyValue::PropertyValue(PropertyValue &&value) noexcept : type_(value.type_), unit_(value.unit_) { switch (type_) { - case Type::type_none: + case Type::none: break; - case Type::type_string: + case Type::string: string_ = value.string_; value.string_ = nullptr; break; - case Type::type_float: + case Type::float_: float_ = value.float_; break; - case Type::type_bool: + case Type::bool_: bool_ = value.bool_; break; - case Type::type_library: + case Type::library: library_ = value.library_; break; - case Type::type_cell: + case Type::cell: cell_ = value.cell_; break; - case Type::type_port: + case Type::port: port_ = value.port_; break; - case Type::type_liberty_library: + case Type::liberty_library: liberty_library_ = value.liberty_library_; break; - case Type::type_liberty_cell: + case Type::liberty_cell: liberty_cell_ = value.liberty_cell_; break; - case Type::type_liberty_port: + case Type::liberty_port: liberty_port_ = value.liberty_port_; break; - case Type::type_instance: + case Type::instance: inst_ = value.inst_; break; - case Type::type_pin: + case Type::pin: pin_ = value.pin_; break; - case Type::type_pins: + case Type::pins: pins_ = value.pins_; value.pins_ = nullptr; break; - case Type::type_net: + case Type::net: net_ = value.net_; break; - case Type::type_clk: + case Type::clk: clk_ = value.clk_; break; - case Type::type_clks: + case Type::clks: clks_ = value.clks_; // Steal the value. value.clks_ = nullptr; break; - case Type::type_paths: + case Type::paths: paths_ = value.paths_; // Steal the value. value.clks_ = nullptr; break; - case Type::type_pwr_activity: + case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; break; } @@ -416,16 +407,16 @@ PropertyValue::PropertyValue(PropertyValue &&value) : PropertyValue::~PropertyValue() { switch (type_) { - case Type::type_string: + case Type::string: stringDelete(string_); break; - case Type::type_clks: + case Type::clks: delete clks_; break; - case Type::type_pins: + case Type::pins: delete pins_; break; - case Type::type_paths: + case Type::paths: delete paths_; break; default: @@ -440,57 +431,57 @@ PropertyValue::operator=(const PropertyValue &value) unit_ = value.unit_; switch (type_) { - case Type::type_none: + case Type::none: break; - case Type::type_string: + case Type::string: string_ = stringCopy(value.string_); break; - case Type::type_float: + case Type::float_: float_ = value.float_; break; - case Type::type_bool: + case Type::bool_: bool_ = value.bool_; break; - case Type::type_library: + case Type::library: library_ = value.library_; break; - case Type::type_cell: + case Type::cell: cell_ = value.cell_; break; - case Type::type_port: + case Type::port: port_ = value.port_; break; - case Type::type_liberty_library: + case Type::liberty_library: liberty_library_ = value.liberty_library_; break; - case Type::type_liberty_cell: + case Type::liberty_cell: liberty_cell_ = value.liberty_cell_; break; - case Type::type_liberty_port: + case Type::liberty_port: liberty_port_ = value.liberty_port_; break; - case Type::type_instance: + case Type::instance: inst_ = value.inst_; break; - case Type::type_pin: + case Type::pin: pin_ = value.pin_; break; - case Type::type_pins: + case Type::pins: pins_ = value.pins_ ? new PinSeq(*value.pins_) : nullptr; break; - case Type::type_net: + case Type::net: net_ = value.net_; break; - case Type::type_clk: + case Type::clk: clk_ = value.clk_; break; - case Type::type_clks: + case Type::clks: clks_ = value.clks_ ? new ClockSeq(*value.clks_) : nullptr; break; - case Type::type_paths: + case Type::paths: paths_ = value.paths_ ? new ConstPathSeq(*value.paths_) : nullptr; break; - case Type::type_pwr_activity: + case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; break; } @@ -498,67 +489,67 @@ PropertyValue::operator=(const PropertyValue &value) } PropertyValue & -PropertyValue::operator=(PropertyValue &&value) +PropertyValue::operator=(PropertyValue &&value) noexcept { type_ = value.type_; unit_ = value.unit_; switch (type_) { - case Type::type_none: + case Type::none: break; - case Type::type_string: + case Type::string: string_ = value.string_; value.string_ = nullptr; break; - case Type::type_float: + case Type::float_: float_ = value.float_; break; - case Type::type_bool: + case Type::bool_: bool_ = value.bool_; break; - case Type::type_library: + case Type::library: library_ = value.library_; break; - case Type::type_cell: + case Type::cell: cell_ = value.cell_; break; - case Type::type_port: + case Type::port: port_ = value.port_; break; - case Type::type_liberty_library: + case Type::liberty_library: liberty_library_ = value.liberty_library_; break; - case Type::type_liberty_cell: + case Type::liberty_cell: liberty_cell_ = value.liberty_cell_; break; - case Type::type_liberty_port: + case Type::liberty_port: liberty_port_ = value.liberty_port_; break; - case Type::type_instance: + case Type::instance: inst_ = value.inst_; break; - case Type::type_pin: + case Type::pin: pin_ = value.pin_; break; - case Type::type_pins: + case Type::pins: pins_ = value.pins_; value.pins_ = nullptr; break; - case Type::type_net: + case Type::net: net_ = value.net_; break; - case Type::type_clk: + case Type::clk: clk_ = value.clk_; break; - case Type::type_clks: + case Type::clks: clks_ = value.clks_; value.clks_ = nullptr; break; - case Type::type_paths: + case Type::paths: paths_ = value.paths_; value.clks_ = nullptr; break; - case Type::type_pwr_activity: + case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; break; } @@ -569,41 +560,41 @@ string PropertyValue::to_string(const Network *network) const { switch (type_) { - case Type::type_string: + case Type::string: return string_; - case Type::type_float: + case Type::float_: return unit_->asString(float_, 6); - case Type::type_bool: + case Type::bool_: // true/false would be better but these are TCL true/false values. if (bool_) return "1"; else return "0"; - case Type::type_liberty_library: + case Type::liberty_library: return liberty_library_->name(); - case Type::type_liberty_cell: + case Type::liberty_cell: return liberty_cell_->name(); - case Type::type_liberty_port: + case Type::liberty_port: return liberty_port_->name(); - case Type::type_library: + case Type::library: return network->name(library_); - case Type::type_cell: + case Type::cell: return network->name(cell_); - case Type::type_port: + case Type::port: return network->name(port_); - case Type::type_instance: + case Type::instance: return network->pathName(inst_); - case Type::type_pin: + case Type::pin: return network->pathName(pin_); - case Type::type_net: + case Type::net: return network->pathName(net_); - case Type::type_clk: + case Type::clk: return clk_->name(); - case Type::type_none: - case Type::type_pins: - case Type::type_clks: - case Type::type_paths: - case Type::type_pwr_activity: + case Type::none: + case Type::pins: + case Type::clks: + case Type::paths: + case Type::pwr_activity: return ""; } return ""; @@ -612,7 +603,7 @@ PropertyValue::to_string(const Network *network) const const char * PropertyValue::stringValue() const { - if (type_ != Type::type_string) + if (type_ != Type::string) throw PropertyTypeWrong("stringValue", "string"); return string_; } @@ -620,7 +611,7 @@ PropertyValue::stringValue() const float PropertyValue::floatValue() const { - if (type_ != Type::type_float) + if (type_ != Type::float_) throw PropertyTypeWrong("floatValue", "float"); return float_; } @@ -628,7 +619,7 @@ PropertyValue::floatValue() const bool PropertyValue::boolValue() const { - if (type_ != Type::type_bool) + if (type_ != Type::bool_) throw PropertyTypeWrong("boolValue", "boolt"); return bool_; } @@ -642,7 +633,7 @@ Properties::Properties(Sta *sta) : PropertyValue Properties::getProperty(const Library *lib, - const std::string property) + const std::string &property) { Network *network = sta_->cmdNetwork(); if (property == "name" @@ -651,7 +642,7 @@ Properties::getProperty(const Library *lib, else { PropertyValue value = registry_library_.getProperty(lib, property, "library", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("library", property); @@ -662,7 +653,7 @@ Properties::getProperty(const Library *lib, PropertyValue Properties::getProperty(const LibertyLibrary *lib, - const std::string property) + const std::string &property) { if (property == "name" || property == "full_name") @@ -673,7 +664,7 @@ Properties::getProperty(const LibertyLibrary *lib, PropertyValue value = registry_liberty_library_.getProperty(lib, property, "liberty_library", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("liberty library", property); @@ -684,7 +675,7 @@ Properties::getProperty(const LibertyLibrary *lib, PropertyValue Properties::getProperty(const Cell *cell, - const std::string property) + const std::string &property) { Network *network = sta_->cmdNetwork(); if (property == "name" @@ -704,7 +695,7 @@ Properties::getProperty(const Cell *cell, else { PropertyValue value = registry_cell_.getProperty(cell, property, "cell", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("cell", property); @@ -715,7 +706,7 @@ Properties::getProperty(const Cell *cell, PropertyValue Properties::getProperty(const LibertyCell *cell, - const std::string property) + const std::string &property) { if (property == "name" || property == "base_name") @@ -745,7 +736,7 @@ Properties::getProperty(const LibertyCell *cell, else { PropertyValue value = registry_liberty_cell_.getProperty(cell, property, "liberty_cell", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("liberty cell", property); @@ -756,14 +747,14 @@ Properties::getProperty(const LibertyCell *cell, PropertyValue Properties::getProperty(const Port *port, - const std::string property) + const std::string &property) { Network *network = sta_->cmdNetwork(); if (property == "name" - || property == "full_name") + || property == "full_name") return PropertyValue(network->name(port)); else if (property == "direction" - || property == "port_direction") + || property == "port_direction") return PropertyValue(network->direction(port)->name()); else if (property == "liberty_port") return PropertyValue(network->libertyPort(port)); @@ -771,40 +762,41 @@ Properties::getProperty(const Port *port, else if (property == "activity") { const Instance *top_inst = network->topInstance(); const Pin *pin = network->findPin(top_inst, port); - PwrActivity activity = sta_->activity(pin); + const Scene *scene = sta_->cmdScene(); + PwrActivity activity = sta_->activity(pin, scene); return PropertyValue(&activity); } else if (property == "slack_max") - return portSlack(port, MinMax::max()); + return portSlack(port, RiseFallBoth::riseFall(), MinMax::max()); else if (property == "slack_max_fall") - return portSlack(port, RiseFall::fall(), MinMax::max()); + return portSlack(port, RiseFallBoth::fall(), MinMax::max()); else if (property == "slack_max_rise") - return portSlack(port, RiseFall::rise(), MinMax::max()); + return portSlack(port, RiseFallBoth::rise(), MinMax::max()); else if (property == "slack_min") - return portSlack(port, MinMax::min()); + return portSlack(port, RiseFallBoth::riseFall(), MinMax::min()); else if (property == "slack_min_fall") - return portSlack(port, RiseFall::fall(), MinMax::min()); + return portSlack(port, RiseFallBoth::fall(), MinMax::min()); else if (property == "slack_min_rise") - return portSlack(port, RiseFall::rise(), MinMax::min()); + return portSlack(port, RiseFallBoth::rise(), MinMax::min()); else if (property == "slew_max") - return portSlew(port, MinMax::max()); + return portSlew(port, RiseFallBoth::fall(), MinMax::max()); else if (property == "slew_max_fall") - return portSlew(port, RiseFall::fall(), MinMax::max()); + return portSlew(port, RiseFallBoth::fall(), MinMax::max()); else if (property == "slew_max_rise") - return portSlew(port, RiseFall::rise(), MinMax::max()); + return portSlew(port, RiseFallBoth::rise(), MinMax::max()); else if (property == "slew_min") - return portSlew(port, MinMax::min()); + return portSlew(port, RiseFallBoth::fall(), MinMax::min()); else if (property == "slew_min_rise") - return portSlew(port, RiseFall::rise(), MinMax::min()); + return portSlew(port, RiseFallBoth::rise(), MinMax::min()); else if (property == "slew_min_fall") - return portSlew(port, RiseFall::fall(), MinMax::min()); + return portSlew(port, RiseFallBoth::fall(), MinMax::min()); else { PropertyValue value = registry_port_.getProperty(port, property, "port", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("port", property); @@ -813,17 +805,7 @@ Properties::getProperty(const Port *port, PropertyValue Properties::portSlew(const Port *port, - const MinMax *min_max) -{ - Network *network = sta_->ensureLibLinked(); - Instance *top_inst = network->topInstance(); - Pin *pin = network->findPin(top_inst, port); - return pinSlew(pin, min_max); -} - -PropertyValue -Properties::portSlew(const Port *port, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max) { Network *network = sta_->ensureLibLinked(); @@ -834,17 +816,7 @@ Properties::portSlew(const Port *port, PropertyValue Properties::portSlack(const Port *port, - const MinMax *min_max) -{ - Network *network = sta_->ensureLibLinked(); - Instance *top_inst = network->topInstance(); - Pin *pin = network->findPin(top_inst, port); - return pinSlack(pin, min_max); -} - -PropertyValue -Properties::portSlack(const Port *port, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max) { Network *network = sta_->ensureLibLinked(); @@ -857,7 +829,7 @@ Properties::portSlack(const Port *port, PropertyValue Properties::getProperty(const LibertyPort *port, - const std::string property) + const std::string &property) { if (property == "name") return PropertyValue(port->name()); @@ -866,7 +838,7 @@ Properties::getProperty(const LibertyPort *port, else if (property == "lib_cell") return PropertyValue(port->libertyCell()); else if (property == "direction" - || property == "port_direction") + || property == "port_direction") return PropertyValue(port->direction()->name()); else if (property == "capacitance") { float cap = port->capacitance(RiseFall::rise(), MinMax::max()); @@ -925,7 +897,7 @@ Properties::getProperty(const LibertyPort *port, else { PropertyValue value = registry_liberty_port_.getProperty(port, property, "liberty_port", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("liberty port", property); @@ -936,7 +908,7 @@ Properties::getProperty(const LibertyPort *port, PropertyValue Properties::getProperty(const Instance *inst, - const string property) + const std::string &property) { Network *network = sta_->ensureLinked(); LibertyCell *liberty_cell = network->libertyCell(inst); @@ -965,7 +937,7 @@ Properties::getProperty(const Instance *inst, else { PropertyValue value = registry_instance_.getProperty(inst, property, "instance", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("instance", property); @@ -976,7 +948,7 @@ Properties::getProperty(const Instance *inst, PropertyValue Properties::getProperty(const Pin *pin, - const std::string property) + const std::string &property) { Network *network = sta_->ensureLinked(); if (property == "name" @@ -985,7 +957,7 @@ Properties::getProperty(const Pin *pin, else if (property == "full_name") return PropertyValue(network->pathName(pin)); else if (property == "direction" - || property == "pin_direction") + || property == "pin_direction") return PropertyValue(network->direction(pin)->name()); else if (property == "is_hierarchical") return PropertyValue(network->isHierarchical(pin)); @@ -1000,56 +972,59 @@ Properties::getProperty(const Pin *pin, return PropertyValue(port && port->isRegClk()); } else if (property == "clocks") { - ClockSet clks = sta_->clocks(pin); + const Mode *mode = sta_->cmdScene()->mode(); + ClockSet clks = sta_->clocks(pin, mode); return PropertyValue(&clks); } else if (property == "clock_domains") { - ClockSet clks = sta_->clockDomains(pin); + const Mode *mode = sta_->cmdScene()->mode(); + ClockSet clks = sta_->clockDomains(pin, mode); return PropertyValue(&clks); } else if (property == "activity") { - PwrActivity activity = sta_->activity(pin); + const Scene *scene = sta_->cmdScene(); + PwrActivity activity = sta_->activity(pin, scene); return PropertyValue(&activity); } else if (property == "arrival_max_rise") - return pinArrival(pin, RiseFall::rise(), MinMax::max()); + return pinArrival(pin, RiseFallBoth::rise(), MinMax::max()); else if (property == "arrival_max_fall") - return pinArrival(pin, RiseFall::fall(), MinMax::max()); + return pinArrival(pin, RiseFallBoth::fall(), MinMax::max()); else if (property == "arrival_min_rise") - return pinArrival(pin, RiseFall::rise(), MinMax::min()); + return pinArrival(pin, RiseFallBoth::rise(), MinMax::min()); else if (property == "arrival_min_fall") - return pinArrival(pin, RiseFall::fall(), MinMax::min()); + return pinArrival(pin, RiseFallBoth::fall(), MinMax::min()); else if (property == "slack_max") - return pinSlack(pin, MinMax::max()); + return pinSlack(pin, RiseFallBoth::riseFall(), MinMax::max()); else if (property == "slack_max_fall") - return pinSlack(pin, RiseFall::fall(), MinMax::max()); + return pinSlack(pin, RiseFallBoth::fall(), MinMax::max()); else if (property == "slack_max_rise") - return pinSlack(pin, RiseFall::rise(), MinMax::max()); + return pinSlack(pin, RiseFallBoth::rise(), MinMax::max()); else if (property == "slack_min") - return pinSlack(pin, MinMax::min()); + return pinSlack(pin, RiseFallBoth::riseFall(), MinMax::min()); else if (property == "slack_min_fall") - return pinSlack(pin, RiseFall::fall(), MinMax::min()); + return pinSlack(pin, RiseFallBoth::fall(), MinMax::min()); else if (property == "slack_min_rise") - return pinSlack(pin, RiseFall::rise(), MinMax::min()); + return pinSlack(pin, RiseFallBoth::rise(), MinMax::min()); else if (property == "slew_max") - return pinSlew(pin, MinMax::max()); + return pinSlew(pin, RiseFallBoth::riseFall(), MinMax::max()); else if (property == "slew_max_fall") - return pinSlew(pin, RiseFall::fall(), MinMax::max()); + return pinSlew(pin, RiseFallBoth::fall(), MinMax::max()); else if (property == "slew_max_rise") - return pinSlew(pin, RiseFall::rise(), MinMax::max()); + return pinSlew(pin, RiseFallBoth::rise(), MinMax::max()); else if (property == "slew_min") - return pinSlew(pin, MinMax::min()); + return pinSlew(pin, RiseFallBoth::riseFall(), MinMax::min()); else if (property == "slew_min_rise") - return pinSlew(pin, RiseFall::rise(), MinMax::min()); + return pinSlew(pin, RiseFallBoth::rise(), MinMax::min()); else if (property == "slew_min_fall") - return pinSlew(pin, RiseFall::fall(), MinMax::min()); + return pinSlew(pin, RiseFallBoth::fall(), MinMax::min()); else { PropertyValue value = registry_pin_.getProperty(pin, property, "pin", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("pin", property); @@ -1058,32 +1033,25 @@ Properties::getProperty(const Pin *pin, PropertyValue Properties::pinArrival(const Pin *pin, - const RiseFall *rf, + const RiseFallBoth *rf, const MinMax *min_max) { - Arrival arrival = sta_->pinArrival(pin, rf, min_max);; + Arrival arrival = sta_->arrival(pin, rf, min_max);; return PropertyValue(delayPropertyValue(arrival)); } PropertyValue Properties::pinSlack(const Pin *pin, + const RiseFallBoth *rf, const MinMax *min_max) { - Slack slack = sta_->pinSlack(pin, min_max); - return PropertyValue(delayPropertyValue(slack)); -} - -PropertyValue -Properties::pinSlack(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) -{ - Slack slack = sta_->pinSlack(pin, rf, min_max); + Slack slack = sta_->slack(pin, rf, sta_->scenes(), min_max); return PropertyValue(delayPropertyValue(slack)); } PropertyValue Properties::pinSlew(const Pin *pin, + const RiseFallBoth *rf, const MinMax *min_max) { Graph *graph = sta_->ensureGraph(); @@ -1091,34 +1059,13 @@ Properties::pinSlew(const Pin *pin, graph->pinVertices(pin, vertex, bidirect_drvr_vertex); Slew slew = min_max->initValue(); if (vertex) { - Slew vertex_slew = sta_->vertexSlew(vertex, min_max); - if (delayGreater(vertex_slew, slew, min_max, sta_)) - slew = vertex_slew; - } - if (bidirect_drvr_vertex) { - Slew vertex_slew = sta_->vertexSlew(bidirect_drvr_vertex, min_max); - if (delayGreater(vertex_slew, slew, min_max, sta_)) - slew = vertex_slew; - } - return delayPropertyValue(slew); -} - -PropertyValue -Properties::pinSlew(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) -{ - Graph *graph = sta_->ensureGraph(); - Vertex *vertex, *bidirect_drvr_vertex; - graph->pinVertices(pin, vertex, bidirect_drvr_vertex); - Slew slew = min_max->initValue(); - if (vertex) { - Slew vertex_slew = sta_->vertexSlew(vertex, rf, min_max); + Slew vertex_slew = sta_->slew(vertex, rf, sta_->scenes(), min_max); if (delayGreater(vertex_slew, slew, min_max, sta_)) slew = vertex_slew; } if (bidirect_drvr_vertex) { - Slew vertex_slew = sta_->vertexSlew(bidirect_drvr_vertex, rf, min_max); + Slew vertex_slew = sta_->slew(bidirect_drvr_vertex, rf, + sta_->scenes(), min_max); if (delayGreater(vertex_slew, slew, min_max, sta_)) slew = vertex_slew; } @@ -1129,7 +1076,7 @@ Properties::pinSlew(const Pin *pin, PropertyValue Properties::getProperty(const Net *net, - const std::string property) + const std::string &property) { Network *network = sta_->ensureLinked(); if (property == "name") @@ -1138,7 +1085,7 @@ Properties::getProperty(const Net *net, return PropertyValue(network->pathName(net)); else { PropertyValue value = registry_net_.getProperty(net, property, "net", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("net", property); @@ -1149,7 +1096,7 @@ Properties::getProperty(const Net *net, PropertyValue Properties::getProperty(Edge *edge, - const std::string property) + const std::string &property) { if (property == "full_name") { string full_name = edge->to_string(sta_); @@ -1184,12 +1131,12 @@ Properties::edgeDelay(Edge *edge, for (TimingArc *arc : arc_set->arcs()) { const RiseFall *to_rf = arc->toEdge()->asRiseFall(); if (to_rf == rf) { - for (const Corner *corner : *sta_->corners()) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - ArcDelay arc_delay = sta_->arcDelay(edge, arc, dcalc_ap); - if (!delay_exists - || delayGreater(arc_delay, delay, min_max, sta_)) { - delay = arc_delay; + for (const Scene *scene : sta_->scenes()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + ArcDelay arc_delay = sta_->arcDelay(edge, arc, ap_index); + if (!delay_exists + || delayGreater(arc_delay, delay, min_max, sta_)) { + delay = arc_delay; delay_exists = true; } } @@ -1202,7 +1149,7 @@ Properties::edgeDelay(Edge *edge, PropertyValue Properties::getProperty(TimingArcSet *arc_set, - const std::string property) + const std::string &property) { if (property == "name" || property == "full_name") { @@ -1225,7 +1172,7 @@ Properties::getProperty(TimingArcSet *arc_set, PropertyValue Properties::getProperty(const Clock *clk, - const std::string property) + const std::string &property) { if (property == "name" || property == "full_name") @@ -1243,7 +1190,7 @@ Properties::getProperty(const Clock *clk, else { PropertyValue value = registry_clock_.getProperty(clk, property, "clock", sta_); - if (value.type() != PropertyValue::Type::type_none) + if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("clock", property); @@ -1254,7 +1201,7 @@ Properties::getProperty(const Clock *clk, PropertyValue Properties::getProperty(PathEnd *end, - const std::string property) + const std::string &property) { if (property == "startpoint") { PathExpanded expanded(end->path(), sta_); @@ -1285,7 +1232,7 @@ Properties::getProperty(PathEnd *end, PropertyValue Properties::getProperty(Path *path, - const std::string property) + const std::string &property) { if (property == "pin") return PropertyValue(path->pin(sta_)); @@ -1320,70 +1267,70 @@ Properties::capacitancePropertyValue(float cap) //////////////////////////////////////////////////////////////// void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_library_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_liberty_library_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_cell_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_liberty_cell_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_port_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_liberty_port_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_instance_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_pin_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_net_.defineProperty(property, handler); } void -Properties::defineProperty(std::string &property, +Properties::defineProperty(std::string_view property, PropertyRegistry::PropertyHandler handler) { registry_clock_.defineProperty(property, handler); @@ -1399,7 +1346,7 @@ PropertyRegistry::getProperty(TYPE object, Sta *sta) { - auto itr = registry_.find({property}); + auto itr = registry_.find(property); if (itr != registry_.end()) return itr->second(object, sta); else @@ -1408,10 +1355,10 @@ PropertyRegistry::getProperty(TYPE object, template void -PropertyRegistry::defineProperty(const std::string &property, +PropertyRegistry::defineProperty(std::string_view property, PropertyHandler handler) { - registry_[property] = handler; + registry_[std::string(property)] = handler; } } // namespace diff --git a/search/Property.i b/search/Property.i index bb3853b28..45f02d085 100644 --- a/search/Property.i +++ b/search/Property.i @@ -37,7 +37,7 @@ using namespace sta; PropertyValue library_property(const Library *lib, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(lib, property); @@ -45,7 +45,7 @@ library_property(const Library *lib, PropertyValue liberty_library_property(const LibertyLibrary *lib, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(lib, property); @@ -53,7 +53,7 @@ liberty_library_property(const LibertyLibrary *lib, PropertyValue cell_property(const Cell *cell, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(cell, property); @@ -61,7 +61,7 @@ cell_property(const Cell *cell, PropertyValue liberty_cell_property(const LibertyCell *cell, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(cell, property); @@ -69,7 +69,7 @@ liberty_cell_property(const LibertyCell *cell, PropertyValue port_property(const Port *port, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(port, property); @@ -77,7 +77,7 @@ port_property(const Port *port, PropertyValue liberty_port_property(const LibertyPort *port, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(port, property); @@ -85,7 +85,7 @@ liberty_port_property(const LibertyPort *port, PropertyValue instance_property(const Instance *inst, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(inst, property); @@ -93,7 +93,7 @@ instance_property(const Instance *inst, PropertyValue pin_property(const Pin *pin, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(pin, property); @@ -101,7 +101,7 @@ pin_property(const Pin *pin, PropertyValue net_property(const Net *net, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(net, property); @@ -109,7 +109,7 @@ net_property(const Net *net, PropertyValue edge_property(Edge *edge, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(edge, property); @@ -117,7 +117,7 @@ edge_property(Edge *edge, PropertyValue clock_property(Clock *clk, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(clk, property); @@ -125,7 +125,7 @@ clock_property(Clock *clk, PropertyValue path_end_property(PathEnd *end, - const char *property) + const char *property) { Properties &properties = Sta::sta()->properties(); return properties.getProperty(end, property); diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 6b991557d..9122a8057 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -26,6 +26,7 @@ #include "ReportPath.hh" +#include "ContainerHelpers.hh" #include "Report.hh" #include "Error.hh" #include "StringUtil.hh" @@ -43,12 +44,10 @@ #include "InputDrive.hh" #include "Sdc.hh" #include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" #include "ArcDelayCalc.hh" #include "GraphDelayCalc.hh" #include "ClkInfo.hh" #include "Tag.hh" -#include "PathAnalysisPt.hh" #include "PathGroup.hh" #include "CheckMinPulseWidths.hh" #include "CheckMinPeriods.hh" @@ -57,7 +56,8 @@ #include "Search.hh" #include "PathExpanded.hh" #include "Latches.hh" -#include "Corner.hh" +#include "Scene.hh" +#include "Mode.hh" #include "Genclks.hh" #include "Variables.hh" @@ -80,11 +80,11 @@ hierPinsThruEdge(const Edge *edge, const Graph *graph); ReportField::ReportField(const char *name, - const char *title, - int width, - bool left_justify, - Unit *unit, - bool enabled) : + const char *title, + int width, + bool left_justify, + Unit *unit, + bool enabled) : name_(name), title_(stringCopy(title)), left_justify_(left_justify), @@ -103,8 +103,8 @@ ReportField::~ReportField() void ReportField::setProperties(const char *title, - int width, - bool left_justify) + int width, + bool left_justify) { if (title_) stringDelete(title_); @@ -172,31 +172,31 @@ ReportPath::makeFields() { field_fanout_ = makeField("fanout", "Fanout", 6, false, nullptr, true); field_capacitance_ = makeField("capacitance", "Cap", 6, false, - units_->capacitanceUnit(), true); + units_->capacitanceUnit(), true); field_slew_ = makeField("slew", "Slew", 6, false, units_->timeUnit(), - true); + true); field_incr_ = makeField("incr", "Delay", 6, false, units_->timeUnit(), - true); + true); field_total_ = makeField("total", "Time", 6, false, units_->timeUnit(), - true); + true); field_edge_ = makeField("edge", "", 1, false, nullptr, true); field_case_ = makeField("case", "case", 11, false, nullptr, false); field_description_ = makeField("description", "Description", 36, - true, nullptr, true); + true, nullptr, true); field_src_attr_ = makeField("src_attr", "Src Attr", 40, - true, nullptr, true); + true, nullptr, true); } ReportField * ReportPath::makeField(const char *name, - const char *title, - int width, - bool left_justify, - Unit *unit, - bool enabled) + const char *title, + int width, + bool left_justify, + Unit *unit, + bool enabled) { ReportField *field = new ReportField(name, title, width, left_justify, - unit, enabled); + unit, enabled); fields_.push_back(field); return field; } @@ -215,11 +215,8 @@ void ReportPath::setReportFieldOrder(StringSeq *field_names) { // Disable all fields. - ReportFieldSeq::Iterator field_iter1(fields_); - while (field_iter1.hasNext()) { - ReportField *field = field_iter1.next(); + for (ReportField *field : fields_) field->setEnabled(false); - } ReportFieldSeq next_fields; for (const char *field_name : *field_names) { @@ -243,11 +240,11 @@ ReportPath::setReportFieldOrder(StringSeq *field_names) void ReportPath::setReportFields(bool report_input_pin, bool report_hier_pins, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout, - bool report_src_attr) + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout, + bool report_src_attr) { report_input_pin_ = report_input_pin; report_hier_pins_ = report_hier_pins; @@ -300,7 +297,7 @@ ReportPath::reportPathEnd(const PathEnd *end) const void ReportPath::reportPathEnd(const PathEnd *end, - const PathEnd *prev_end, + const PathEnd *prev_end, bool last) const { switch (format_) { @@ -337,11 +334,10 @@ ReportPath::reportPathEnds(const PathEndSeq *ends) const { reportPathEndHeader(); if (ends && !ends->empty()) { - PathEnd *prev_end = nullptr; - PathEndSeq::ConstIterator end_iter(ends); - while (end_iter.hasNext()) { - PathEnd *end = end_iter.next(); - reportPathEnd(end, prev_end, !end_iter.hasNext()); + const PathEnd *prev_end = nullptr; + for (size_t i = 0; i < ends->size(); i++) { + const PathEnd *end = (*ends)[i]; + reportPathEnd(end, prev_end, i == ends->size() - 1); prev_end = end; } } @@ -397,7 +393,7 @@ ReportPath::reportPathEndFooter() const void ReportPath::reportEndpointHeader(const PathEnd *end, - const PathEnd *prev_end) const + const PathEnd *prev_end) const { PathGroup *prev_group = nullptr; if (prev_end) @@ -428,7 +424,7 @@ ReportPath::reportShort(const PathEndUnconstrained *end) const void ReportPath::reportShort(const PathEndUnconstrained *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportUnclockedEndpoint(end, "internal pin"); @@ -444,7 +440,7 @@ ReportPath::reportFull(const PathEndUnconstrained *end) const reportPath(end, expanded); reportLine("data arrival time", end->dataArrivalTimeOffset(this), - end->pathEarlyLate(this)); + end->pathEarlyLate(this)); reportDashLine(); report_->reportLine("(Path is unconstrained)"); } @@ -460,7 +456,7 @@ ReportPath::reportShort(const PathEndCheck *end) const void ReportPath::reportShort(const PathEndCheck *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -497,17 +493,17 @@ ReportPath::reportEndpoint(const PathEndCheck *end) const 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()); + check_role->to_string().c_str(), + rise_fall, + clk_name.c_str()); reportEndpoint(inst_name, reason); } else if (check_generic_role == TimingRole::setup() - || check_generic_role == TimingRole::hold()) { + || 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()); + rise_fall, clk_name.c_str()); reportEndpoint(inst_name, reason); } else { @@ -529,7 +525,7 @@ ReportPath::reportShort(const PathEndLatchCheck *end) const void ReportPath::reportShort(const PathEndLatchCheck *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -559,7 +555,7 @@ ReportPath::reportFull(const PathEndLatchCheck *end) const Required req_time; Arrival borrow, adjusted_data_arrival, time_given_to_startpoint; end->latchRequired(this, req_time, borrow, adjusted_data_arrival, - time_given_to_startpoint); + time_given_to_startpoint); // Adjust required to requiredTimeOffset. req_time += end->sourceClkOffset(this); if (path_delay) { @@ -567,11 +563,11 @@ ReportPath::reportFull(const PathEndLatchCheck *end) const reportLine("max_delay", delay, delay, early_late); if (!ignore_clk_latency) { if (reportClkPath() - && isPropagated(end->targetClkPath())) - reportTgtClk(end, delay); + && isPropagated(end->targetClkPath())) + reportTgtClk(end, delay); else { - Delay delay1(delay); - reportCommonClkPessimism(end, delay1); + Delay delay1(delay); + reportCommonClkPessimism(end, delay1); } } } @@ -613,8 +609,8 @@ ReportPath::latchDesc(const PathEndLatchCheck *end) const void ReportPath::reportBorrowing(const PathEndLatchCheck *end, - Arrival &borrow, - Arrival &time_given_to_startpoint) const + Arrival &borrow, + Arrival &time_given_to_startpoint) const { Delay open_latency, latency_diff, max_borrow; float nom_pulse_width, open_uncertainty; @@ -622,8 +618,8 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, bool borrow_limit_exists; const EarlyLate *early_late = EarlyLate::late(); end->latchBorrowInfo(this, nom_pulse_width, open_latency, latency_diff, - open_uncertainty, open_crpr, crpr_diff, - max_borrow, borrow_limit_exists); + open_uncertainty, open_crpr, crpr_diff, + max_borrow, borrow_limit_exists); report_->reportLine("Time Borrowing Information"); reportDashLineTotal(); if (borrow_limit_exists) @@ -636,7 +632,7 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, 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)) - reportLineTotalMinus("clock latency difference", latency_diff, early_late); + reportLineTotalMinus("clock latency difference", latency_diff, early_late); } else { auto width_msg = stdstrPrint("%s pulse width", tgt_clk_name.c_str()); @@ -651,7 +647,7 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, } if (delayGreater(borrow, delay_zero, this) && (!fuzzyZero(open_uncertainty) - || !delayZero(open_crpr))) { + || !delayZero(open_crpr))) { reportDashLineTotal(); reportLineTotal("actual time borrow", borrow, early_late); if (!fuzzyZero(open_uncertainty)) @@ -677,7 +673,7 @@ ReportPath::reportShort(const PathEndPathDelay *end) const void ReportPath::reportShort(const PathEndPathDelay *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); if (end->targetClk(this)) @@ -735,16 +731,16 @@ ReportPath::reportFull(const PathEndPathDelay *end) const if (tgt_clk) { const Path *tgt_clk_path = end->targetClkPath(); if (reportClkPath() - && isPropagated(tgt_clk_path, tgt_clk)) - reportTgtClk(end, delay, 0.0, true); + && isPropagated(tgt_clk_path, tgt_clk)) + 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)) - reportLine(clkNetworkDelayIdealProp(isPropagated(tgt_clk_path)), - tgt_clk_delay, tgt_clk_arrival, early_late); - reportClkUncertainty(end, tgt_clk_arrival); - reportCommonClkPessimism(end, tgt_clk_arrival); + Arrival tgt_clk_delay = end->targetClkDelay(this); + Arrival tgt_clk_arrival = delay + tgt_clk_delay; + if (!delayZero(tgt_clk_delay)) + reportLine(clkNetworkDelayIdealProp(isPropagated(tgt_clk_path)), + tgt_clk_delay, tgt_clk_arrival, early_late); + reportClkUncertainty(end, tgt_clk_arrival); + reportCommonClkPessimism(end, tgt_clk_arrival); } } } @@ -763,7 +759,7 @@ ReportPath::isPropagated(const Path *clk_path) const bool ReportPath::isPropagated(const Path *clk_path, - const Clock *clk) const + const Clock *clk) const { if (clk_path) return clk_path->clkInfo(search_)->isPropagated(); @@ -791,7 +787,7 @@ ReportPath::reportShort(const PathEndOutputDelay *end) const void ReportPath::reportShort(const PathEndOutputDelay *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -856,7 +852,7 @@ ReportPath::reportShort(const PathEndGatedClock *end) const void ReportPath::reportShort(const PathEndGatedClock *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -886,8 +882,8 @@ ReportPath::reportEndpoint(const PathEndGatedClock *end) const const char *rise_fall = asRisingFalling(clk_rf); // 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()); + rise_fall, + clk_name.c_str()); reportEndpoint(inst_name, reason); } @@ -902,7 +898,7 @@ ReportPath::reportShort(const PathEndDataCheck *end) const void ReportPath::reportShort(const PathEndDataCheck *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportStartpoint(end, expanded); reportEndpoint(end); @@ -935,7 +931,7 @@ ReportPath::reportFull(const PathEndDataCheck *end) const float offset = prev - delayAsFloat(clk_delay) - tgt_clk_edge->time(); // Delay to startpoint is already included. reportPath6(data_clk_path, clk_expanded, clk_expanded.startIndex(), - true, false, prev, offset); + true, false, prev, offset); } reportRequired(end, checkRoleReason(end)); reportSlack(end); @@ -949,8 +945,8 @@ ReportPath::reportEndpoint(const PathEndDataCheck *end) const 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); + tgt_clk_rf, + tgt_clk_name); reportEndpoint(inst_name, reason); } @@ -1031,7 +1027,7 @@ ReportPath::reportSummaryLine(const PathEnd *end) const string ReportPath::pathStartpoint(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { const Path *start = expanded.startPath(); Pin *pin = start->pin(graph_); @@ -1185,7 +1181,8 @@ ReportPath::reportJson(const PathExpanded &expanded, const Net *net = network_->net(pin); const Instance *inst = network_->instance(pin); const RiseFall *rf = path->transition(this); - DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(this)->dcalcAnalysisPt(); + const Scene *scene = path->scene(this); + const MinMax *min_max = path->minMax(this); bool is_driver = network_->isDriver(pin); stringAppend(result, "%*s {\n", indent, ""); @@ -1201,7 +1198,7 @@ ReportPath::reportJson(const PathExpanded &expanded, sdc_network_->name(cell)); stringAppend(result, "%*s \"verilog_src\": \"%s\",\n", indent, "", - sdc_network_->getAttribute(inst, "src").c_str()); + sdc_network_->getAttribute(inst, "src").c_str()); } stringAppend(result, "%*s \"pin\": \"%s\",\n", @@ -1241,7 +1238,7 @@ ReportPath::reportJson(const PathExpanded &expanded, if (is_driver) stringAppend(result, "%*s \"capacitance\": %.3e,\n", indent, "", - graph_delay_calc_->loadCap(pin, rf, dcalc_ap)); + graph_delay_calc_->loadCap(pin, rf, scene, min_max)); stringAppend(result, "%*s \"slew\": %.3e\n", indent, "", delayAsFloat(path->slew(this))); @@ -1284,8 +1281,8 @@ ReportPath::reportSlackOnly(const PathEnd *end) const //////////////////////////////////////////////////////////////// void -ReportPath::reportMpwCheck(const MinPulseWidthCheck *check, - bool verbose) const +ReportPath::reportMpwCheck(const MinPulseWidthCheck &check, + bool verbose) const { if (verbose) { reportVerbose(check); @@ -1299,20 +1296,20 @@ ReportPath::reportMpwCheck(const MinPulseWidthCheck *check, } void -ReportPath::reportMpwChecks(const MinPulseWidthCheckSeq *checks, - bool verbose) const +ReportPath::reportMpwChecks(const MinPulseWidthCheckSeq &checks, + bool verbose) const { - if (!checks->empty()) { + if (!checks.empty()) { if (verbose) { - for (const MinPulseWidthCheck *check : *checks) { + for (const MinPulseWidthCheck &check : checks) { reportVerbose(check); reportBlankLine(); } } else { reportMpwHeaderShort(); - for (const MinPulseWidthCheck *check : *checks) - reportShort(check); + for (const MinPulseWidthCheck &check : checks) + reportShort(check); } reportBlankLine(); } @@ -1343,24 +1340,24 @@ ReportPath::reportMpwHeaderShort() const } void -ReportPath::reportShort(const MinPulseWidthCheck *check) const +ReportPath::reportShort(const MinPulseWidthCheck &check) const { string line; - const char *pin_name = cmd_network_->pathName(check->pin(this)); + 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); reportDescription(what.c_str(), line); - reportSpaceFieldTime(check->minWidth(this), line); - reportSpaceFieldDelay(check->width(this), EarlyLate::late(), line); - reportSpaceSlack(check->slack(this), line); + reportSpaceFieldTime(check.minWidth(this), line); + reportSpaceFieldDelay(check.width(this), EarlyLate::late(), line); + reportSpaceSlack(check.slack(this), line); report_->reportLineString(line); } void -ReportPath::reportVerbose(const MinPulseWidthCheck *check) const +ReportPath::reportVerbose(const MinPulseWidthCheck &check) const { string line; - const char *pin_name = cmd_network_->pathName(check->pin(this)); + const char *pin_name = cmd_network_->pathName(check.pin(this)); line += "Pin: "; line += pin_name; report_->reportLineString(line); @@ -1370,7 +1367,7 @@ ReportPath::reportVerbose(const MinPulseWidthCheck *check) const reportPathHeader(); const EarlyLate *open_el = EarlyLate::late(); - const ClockEdge *open_clk_edge = check->openClkEdge(this); + 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()); @@ -1378,48 +1375,48 @@ ReportPath::reportVerbose(const MinPulseWidthCheck *check) const auto open_clk_msg = stdstrPrint("clock %s (%s edge)", open_clk_name, open_rise_fall); reportLine(open_clk_msg.c_str(), open_clk_time, open_clk_time, open_el); - Arrival open_arrival = check->openArrival(this); - bool is_prop = isPropagated(check->openPath()); + Arrival open_arrival = check.openArrival(this); + bool is_prop = isPropagated(check.openPath()); const char *clk_ideal_prop = clkNetworkDelayIdealProp(is_prop); - reportLine(clk_ideal_prop, check->openDelay(this), open_arrival, open_el); + reportLine(clk_ideal_prop, check.openDelay(this), open_arrival, open_el); reportLine(pin_name, delay_zero, open_arrival, open_el); reportLine("open edge arrival time", open_arrival, open_el); reportBlankLine(); const EarlyLate *close_el = EarlyLate::late(); - const ClockEdge *close_clk_edge = check->closeClkEdge(this); + 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_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); reportLine(close_clk_msg.c_str(), close_clk_time, close_clk_time, close_el); - Arrival close_arrival = check->closeArrival(this) + close_offset; - reportLine(clk_ideal_prop, check->closeDelay(this), close_arrival, close_el); + Arrival close_arrival = check.closeArrival(this) + close_offset; + 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); + Crpr pessimism = check.checkCrpr(this); close_arrival += pessimism; reportLine("clock reconvergence pessimism", pessimism, close_arrival, close_el); } reportLine("close edge arrival time", close_arrival, close_el); reportDashLine(); - float min_width = check->minWidth(this); + float min_width = check.minWidth(this); const char *hi_low = mpwCheckHiLow(check); auto rpw_msg = stdstrPrint("required pulse width (%s)", hi_low); reportLine(rpw_msg.c_str(), min_width, EarlyLate::early()); - reportLine("actual pulse width", check->width(this), EarlyLate::early()); + reportLine("actual pulse width", check.width(this), EarlyLate::early()); reportDashLine(); - reportSlack(check->slack(this)); + reportSlack(check.slack(this)); } const char * -ReportPath::mpwCheckHiLow(const MinPulseWidthCheck *check) const +ReportPath::mpwCheckHiLow(const MinPulseWidthCheck &check) const { - if (check->openTransition(this) == RiseFall::rise()) + if (check.openTransition(this) == RiseFall::rise()) return "high"; else return "low"; @@ -1428,8 +1425,8 @@ ReportPath::mpwCheckHiLow(const MinPulseWidthCheck *check) const //////////////////////////////////////////////////////////////// void -ReportPath::reportCheck(const MinPeriodCheck *check, - bool verbose) const +ReportPath::reportCheck(const MinPeriodCheck &check, + bool verbose) const { if (verbose) { reportVerbose(check); @@ -1443,20 +1440,20 @@ ReportPath::reportCheck(const MinPeriodCheck *check, } void -ReportPath::reportChecks(const MinPeriodCheckSeq *checks, - bool verbose) const +ReportPath::reportChecks(const MinPeriodCheckSeq &checks, + bool verbose) const { - if (!checks->empty()) { + if (!checks.empty()) { if (verbose) { - for (const MinPeriodCheck *check : *checks) { - reportVerbose(check); + for (const MinPeriodCheck &check : checks) { + reportVerbose(check); reportBlankLine(); } } else { reportPeriodHeaderShort(); - for (const MinPeriodCheck *check : *checks) - reportShort(check); + for (const MinPeriodCheck &check : checks) + reportShort(check); } reportBlankLine(); } @@ -1489,63 +1486,48 @@ ReportPath::reportPeriodHeaderShort() const } void -ReportPath::reportShort(const MinPeriodCheck *check) const +ReportPath::reportShort(const MinPeriodCheck &check) const { string line; - const char *pin_name = cmd_network_->pathName(check->pin()); + const char *pin_name = cmd_network_->pathName(check.pin()); reportDescription(pin_name, line); - reportSpaceFieldDelay(check->period(), EarlyLate::early(), line); - reportSpaceFieldDelay(check->minPeriod(this), EarlyLate::early(), line); - reportSpaceSlack(check->slack(this), line); + reportSpaceFieldDelay(check.period(), EarlyLate::early(), line); + reportSpaceFieldDelay(check.minPeriod(this), EarlyLate::early(), line); + reportSpaceSlack(check.slack(this), line); report_->reportLineString(line); } void -ReportPath::reportVerbose(const MinPeriodCheck *check) const +ReportPath::reportVerbose(const MinPeriodCheck &check) const { string line; - const char *pin_name = cmd_network_->pathName(check->pin()); + const char *pin_name = cmd_network_->pathName(check.pin()); line += "Pin: "; line += pin_name; report_->reportLineString(line); - reportLine("period", check->period(), EarlyLate::early()); - reportLine("min period", -check->minPeriod(this), EarlyLate::early()); + reportLine("period", check.period(), EarlyLate::early()); + reportLine("min period", -check.minPeriod(this), EarlyLate::early()); reportDashLine(); - reportSlack(check->slack(this)); + reportSlack(check.slack(this)); } //////////////////////////////////////////////////////////////// void -ReportPath::reportCheck(const MaxSkewCheck *check, - bool verbose) const +ReportPath::reportChecks(const MaxSkewCheckSeq &checks, + bool verbose) const { - if (verbose) { - reportVerbose(check); - reportBlankLine(); - } - else { - reportMaxSkewHeaderShort(); - reportShort(check); - } - reportBlankLine(); -} - -void -ReportPath::reportChecks(const MaxSkewCheckSeq *checks, - bool verbose) const -{ - if (!checks->empty()) { + if (!checks.empty()) { if (verbose) { - for (const MaxSkewCheck *check : *checks) - reportVerbose(check); + for (const MaxSkewCheck &check : checks) + reportVerbose(check); } else { reportMaxSkewHeaderShort(); - for (const MaxSkewCheck *check : *checks) - reportShort(check); + for (const MaxSkewCheck &check : checks) + reportShort(check); } reportBlankLine(); } @@ -1578,34 +1560,34 @@ ReportPath::reportMaxSkewHeaderShort() const } void -ReportPath::reportShort(const MaxSkewCheck *check) const +ReportPath::reportShort(const MaxSkewCheck &check) const { string line; - Pin *clk_pin = check->clkPin(this); + Pin *clk_pin = check.clkPin(this); const char *clk_pin_name = network_->pathName(clk_pin); - TimingArc *check_arc = check->checkArc(); + 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()); + clk_pin_name, + check_arc->fromEdge()->to_string().c_str(), + check_arc->toEdge()->to_string().c_str()); reportDescription(what.c_str(), line); const EarlyLate *early_late = EarlyLate::early(); - reportSpaceFieldDelay(check->maxSkew(this), early_late, line); - reportSpaceFieldDelay(check->skew(), early_late, line); - reportSpaceSlack(check->slack(this), line); + reportSpaceFieldDelay(check.maxSkew(this), early_late, line); + reportSpaceFieldDelay(check.skew(), early_late, line); + reportSpaceSlack(check.slack(this), line); report_->reportLineString(line); } void -ReportPath::reportVerbose(const MaxSkewCheck *check) const +ReportPath::reportVerbose(const MaxSkewCheck &check) const { string line; - const char *clk_pin_name = cmd_network_->pathName(check->clkPin(this)); + const char *clk_pin_name = cmd_network_->pathName(check.clkPin(this)); line += "Constrained Pin: "; line += clk_pin_name; report_->reportLineString(line); - const char *ref_pin_name = cmd_network_->pathName(check->refPin(this)); + const char *ref_pin_name = cmd_network_->pathName(check.refPin(this)); line = "Reference Pin: "; line += ref_pin_name; report_->reportLineString(line); @@ -1615,20 +1597,20 @@ ReportPath::reportVerbose(const MaxSkewCheck *check) const reportBlankLine(); reportPathHeader(); - reportSkewClkPath("reference pin arrival time", check->refPath()); - reportSkewClkPath("constrained pin arrival time", check->clkPath()); + reportSkewClkPath("reference pin arrival time", check.refPath()); + reportSkewClkPath("constrained pin arrival time", check.clkPath()); reportDashLine(); - reportLine("allowable skew", check->maxSkew(this), EarlyLate::early()); - reportLine("actual skew", check->skew(), EarlyLate::late()); + reportLine("allowable skew", check.maxSkew(this), EarlyLate::early()); + reportLine("actual skew", check.skew(), EarlyLate::late()); reportDashLine(); - reportSlack(check->slack(this)); + reportSlack(check.slack(this)); } // Based on reportTgtClk. void ReportPath::reportSkewClkPath(const char *arrival_msg, - const Path *clk_path) const + const Path *clk_path) const { const ClockEdge *clk_edge = clk_path->clkEdge(this); const Clock *clk = clk_edge->clock(); @@ -1639,21 +1621,22 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, float clk_time = clk_edge->time(); const Arrival &clk_arrival = search_->clkPathArrival(clk_path); Arrival clk_delay = clk_arrival - clk_time; - PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(this); - const MinMax *min_max = path_ap->pathMinMax(); + 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); bool is_prop = isPropagated(clk_path); if (is_prop && reportClkPath()) { + const Mode *mode = clk_path->mode(this); + const Sdc *sdc = mode->sdc(); const EarlyLate *early_late = TimingRole::skew()->tgtClkEarlyLate(); - if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late)) - reportGenClkSrcAndPath(clk_path, clk, clk_rf, early_late, path_ap, - 0.0, 0.0, false); + if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late, sdc)) + reportGenClkSrcAndPath(clk_path, clk, clk_rf, early_late, 0.0, 0.0, + false, mode); else { Arrival insertion, latency; PathEnd::checkTgtClkDelay(clk_path, clk_edge, TimingRole::skew(), this, - insertion, latency); + insertion, latency); reportClkSrcLatency(insertion, clk_time, early_late); PathExpanded clk_expanded(clk_path, this); reportPath1(clk_path, clk_expanded, false, 0.0); @@ -1662,7 +1645,7 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, else { reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_arrival, early_late); reportLine(descriptionField(clk_vertex).c_str(), clk_arrival, - early_late, clk_end_rf); + early_late, clk_end_rf); } reportLine(arrival_msg, search_->clkPathArrival(clk_path), early_late); reportBlankLine(); @@ -1688,10 +1671,10 @@ ReportPath::reportLimitShortHeader(const ReportField *field) const void ReportPath::reportLimitShort(const ReportField *field, - Pin *pin, - float value, - float limit, - float slack) const + const Pin *pin, + float value, + float limit, + float slack) const { string line; const char *pin_name = cmd_network_->pathName(pin); @@ -1710,12 +1693,12 @@ ReportPath::reportLimitShort(const ReportField *field, void ReportPath::reportLimitVerbose(const ReportField *field, - Pin *pin, - const RiseFall *rf, - float value, - float limit, - float slack, - const Corner *corner, + const Pin *pin, + const RiseFall *rf, + float value, + float limit, + float slack, + const Scene *scene, const MinMax *min_max) const { string line; @@ -1726,10 +1709,10 @@ ReportPath::reportLimitVerbose(const ReportField *field, line += rf->shortName(); else line += ' '; - // Don't report corner if the default corner is the only corner. - if (corner && corners_->count() > 1) { + // Don't report scene if the default scene is the only scene. + if (scene && multiScene()) { line += " (corner "; - line += corner->name(); + line += scene->name(); line += ")"; } report_->reportLineString(line); @@ -1763,9 +1746,10 @@ ReportPath::reportLimitVerbose(const ReportField *field, void ReportPath::reportStartpoint(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { const Path *path = end->path(); + const Sdc *sdc = path->sdc(this); const Path *start = expanded.startPath(); const TimingArc *prev_arc = expanded.startPrevArc(); const Edge *prev_edge = start->prevEdge(this); @@ -1780,7 +1764,7 @@ ReportPath::reportStartpoint(const PathEnd *end, } else if (network_->isTopLevelPort(pin)) { if (clk - && clk != sdc_->defaultArrivalClock()) { + && 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); @@ -1796,7 +1780,7 @@ ReportPath::reportStartpoint(const PathEnd *end, const RiseFall *clk_rf = clk_edge->transition(); const Path *clk_path = expanded.clkPath(); bool clk_inverted = clk_path - && clk_rf != clk_path->transition(this); + && clk_rf != clk_path->transition(this); 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()); @@ -1810,13 +1794,13 @@ ReportPath::reportStartpoint(const PathEnd *end, else if (network_->isLeaf(pin)) { 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); - reportStartpoint(pin_name, reason); + if (clk != sdc->defaultArrivalClock()) { + const char *clk_name = clk->name(); + auto reason = stdstrPrint("internal path startpoint clocked by %s", clk_name); + reportStartpoint(pin_name, reason); } else - reportStartpoint(pin_name, "internal path startpoint"); + reportStartpoint(pin_name, "internal path startpoint"); } else reportStartpoint(pin_name, "internal pin"); @@ -1836,23 +1820,23 @@ ReportPath::pathFromClkPin(const PathExpanded &expanded) const bool ReportPath::pathFromClkPin(const Path *path, - const Pin *start_pin) const + const Pin *start_pin) const { const Clock *clk = path->clock(search_); return clk - && clk->leafPins().hasKey(const_cast(start_pin)); + && clk->leafPins().contains(const_cast(start_pin)); } void ReportPath::reportStartpoint(const char *start, - const string reason) const + const string reason) const { reportStartEndPoint(start, reason, "Startpoint"); } void ReportPath::reportUnclockedEndpoint(const PathEnd *end, - const char *default_reason) const + const char *default_reason) const { Vertex *vertex = end->vertex(this); Pin *pin = vertex->pin(); @@ -1865,25 +1849,25 @@ ReportPath::reportUnclockedEndpoint(const PathEnd *end, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role()->genericRole() == TimingRole::setup()) { - Vertex *clk_vertex = edge->from(graph_); - VertexOutEdgeIterator clk_edge_iter(clk_vertex, graph_); - while (clk_edge_iter.hasNext()) { - Edge *clk_edge = clk_edge_iter.next(); - if (clk_edge->role() == TimingRole::regClkToQ()) { - Instance *inst = network_->instance(pin); - const char *inst_name = cmd_network_->pathName(inst); - const char *reason = regDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); - reportEndpoint(inst_name, reason); - return; - } - if (clk_edge->role() == TimingRole::latchEnToQ()) { - Instance *inst = network_->instance(pin); - const char *inst_name = cmd_network_->pathName(inst); - const char *reason = latchDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); - reportEndpoint(inst_name, reason); - return; - } - } + Vertex *clk_vertex = edge->from(graph_); + VertexOutEdgeIterator clk_edge_iter(clk_vertex, graph_); + while (clk_edge_iter.hasNext()) { + Edge *clk_edge = clk_edge_iter.next(); + if (clk_edge->role() == TimingRole::regClkToQ()) { + Instance *inst = network_->instance(pin); + const char *inst_name = cmd_network_->pathName(inst); + const char *reason = regDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); + reportEndpoint(inst_name, reason); + return; + } + if (clk_edge->role() == TimingRole::latchEnToQ()) { + Instance *inst = network_->instance(pin); + const char *inst_name = cmd_network_->pathName(inst); + const char *reason = latchDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); + reportEndpoint(inst_name, reason); + return; + } + } } } reportEndpoint(cmd_network_->pathName(pin), default_reason); @@ -1894,15 +1878,15 @@ ReportPath::reportUnclockedEndpoint(const PathEnd *end, void ReportPath::reportEndpoint(const char *end, - const string reason) const + const string reason) const { reportStartEndPoint(end, reason, "Endpoint"); } void ReportPath::reportStartEndPoint(const char *pt, - string reason, - const char *key) const + string reason, + const char *key) const { string line; // Account for punctuation in the line. @@ -1947,9 +1931,15 @@ ReportPath::reportGroup(const PathEnd *end) const line += end->minMax(this)->to_string(); report_->reportLineString(line); - if (corners_->multiCorner()) { + if (modes_.size() > 1) { + line = "Mode: "; + line += end->path()->mode(this)->name(); + report_->reportLineString(line); + } + + if (multiScene()) { line = "Corner: "; - line += end->pathAnalysisPt(this)->corner()->name(); + line += end->path()->scene(this)->name(); report_->reportLineString(line); } } @@ -1974,7 +1964,7 @@ ReportPath::tgtClkName(const PathEnd *end) const string ReportPath::clkName(const Clock *clk, - bool inverted) const + bool inverted) const { string name = clk->name(); if (inverted) @@ -1997,37 +1987,37 @@ ReportPath::clkRegLatchDesc(const PathEnd *end) const TimingArcSet *arc_set = edge->timingArcSet(); const TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) { + || role == TimingRole::latchEnToQ()) { const RiseFall *arc_rf = arc_set->isRisingFallingEdge(); clk_set = arc_set; if (arc_rf == check_clk_rf) - clk_rf_set = arc_set; + clk_rf_set = arc_set; } } if (clk_rf_set) return checkRegLatchDesc(clk_rf_set->role(), - clk_rf_set->isRisingFallingEdge()); + clk_rf_set->isRisingFallingEdge()); else if (clk_set) return checkRegLatchDesc(clk_set->role(), - clk_set->isRisingFallingEdge()); + clk_set->isRisingFallingEdge()); else return checkRegLatchDesc(TimingRole::regClkToQ(), check_clk_rf); } void ReportPath::reportSrcPathArrival(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportBlankLine(); reportSrcPath(end, expanded); reportLine("data arrival time", end->dataArrivalTimeOffset(this), - end->pathEarlyLate(this)); + end->pathEarlyLate(this)); reportBlankLine(); } void ReportPath::reportSrcPath(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportPathHeader(); float src_clk_offset = end->sourceClkOffset(this); @@ -2035,30 +2025,32 @@ ReportPath::reportSrcPath(const PathEnd *end, Arrival src_clk_latency = end->sourceClkLatency(this); const Path *path = end->path(); reportSrcClkAndPath(path, expanded, src_clk_offset, src_clk_insertion, - src_clk_latency, end->isPathDelay()); + src_clk_latency, end->isPathDelay()); } void ReportPath::reportSrcClkAndPath(const Path *path, - const PathExpanded &expanded, - float time_offset, - Arrival clk_insertion, - Arrival clk_latency, - bool is_path_delay) const + const PathExpanded &expanded, + float time_offset, + Arrival clk_insertion, + Arrival clk_latency, + bool is_path_delay) const { const ClockEdge *clk_edge = path->clkEdge(this); + const Mode *mode = path->mode(this); + const Sdc *sdc = mode->sdc(); const MinMax *min_max = path->minMax(this); if (clk_edge) { Clock *clk = clk_edge->clock(); const RiseFall *clk_rf = clk_edge->transition(); float clk_time = clk_edge->time() + time_offset; - if (clk == sdc_->defaultArrivalClock()) { + if (clk == sdc->defaultArrivalClock()) { if (!is_path_delay) { - float clk_end_time = clk_time + time_offset; - const EarlyLate *early_late = min_max; - reportLine("clock (input port clock) (rise edge)", - clk_end_time, clk_end_time, early_late); - reportLine(clkNetworkDelayIdealProp(false), 0.0, clk_end_time, early_late); + float clk_end_time = clk_time + time_offset; + const EarlyLate *early_late = min_max; + reportLine("clock (input port clock) (rise edge)", + clk_end_time, clk_end_time, early_late); + reportLine(clkNetworkDelayIdealProp(false), 0.0, clk_end_time, early_late); } reportPath1(path, expanded, false, time_offset); } @@ -2069,91 +2061,89 @@ 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_rf = clk_path->transition(this); + clk_end_time = search_->clkPathArrival(clk_path) + time_offset; + clk_delay = clk_end_time - clk_time; + 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; - - const Path *first_path = expanded.startPath(); - const InputDelay *input_delay = pathInputDelay(first_path); - if (input_delay) { - path_from_input = true; - const Pin *ref_pin = input_delay->refPin(); - if (ref_pin && clk->isPropagated()) { - Path ref_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; - input_has_ref_path = true; - } - } - } + // 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; + + const Path *first_path = expanded.startPath(); + const InputDelay *input_delay = pathInputDelay(first_path); + if (input_delay) { + path_from_input = true; + const Pin *ref_pin = input_delay->refPin(); + if (ref_pin && clk->isPropagated()) { + Path ref_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; + input_has_ref_path = true; + } + } + } } string clk_name = clkName(clk, clk_rf != clk_end_rf); bool clk_used_as_data = pathFromClkPin(expanded); bool is_prop = isPropagated(path); const EarlyLate *early_late = min_max; - if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late) - && !(path_from_input && !input_has_ref_path)) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, - min_max); - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - reportGenClkSrcAndPath(path, clk, clk_rf, early_late, path_ap, - time_offset, time_offset, clk_used_as_data); + if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late, sdc) + && !(path_from_input && !input_has_ref_path)) { + reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); + reportGenClkSrcAndPath(path, clk, clk_rf, early_late, time_offset, + time_offset, clk_used_as_data, mode); } else if (clk_used_as_data - && pathFromGenPropClk(path, path->minMax(this))) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); - const ClkInfo *clk_info = path->tag(search_)->clkInfo(); - if (clk_info->isPropagated()) - reportClkSrcLatency(clk_insertion, clk_time, early_late); - reportPath1(path, expanded, true, time_offset); + && pathFromGenPropClk(path, path->minMax(this))) { + reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); + const ClkInfo *clk_info = path->tag(search_)->clkInfo(); + if (clk_info->isPropagated()) + reportClkSrcLatency(clk_insertion, clk_time, early_late); + reportPath1(path, expanded, true, time_offset); } else if (is_prop - && reportClkPath() - && !(path_from_input && !input_has_ref_path)) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); - reportClkSrcLatency(clk_insertion, clk_time, early_late); - reportPath1(path, expanded, false, time_offset); + && reportClkPath() + && !(path_from_input && !input_has_ref_path)) { + reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); + reportClkSrcLatency(clk_insertion, clk_time, early_late); + reportPath1(path, expanded, false, time_offset); } else if (clk_used_as_data) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); - if (delayGreater(clk_insertion, 0.0, this)) - reportClkSrcLatency(clk_insertion, clk_time, early_late); - if (reportClkPath()) - 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; - reportLine("clock network delay", clk_delay, - end_arrival, early_late); - Vertex *end_vertex = path->vertex(this); - reportLine(descriptionField(end_vertex).c_str(), - end_arrival, early_late, clk_end_rf); - } + reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); + if (delayGreater(clk_insertion, 0.0, this)) + reportClkSrcLatency(clk_insertion, clk_time, early_late); + if (reportClkPath()) + 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; + reportLine("clock network delay", clk_delay, + end_arrival, early_late); + Vertex *end_vertex = path->vertex(this); + reportLine(descriptionField(end_vertex).c_str(), + end_arrival, early_late, clk_end_rf); + } } else { - if (is_path_delay) { - if (delayGreater(clk_delay, 0.0, this)) - reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, - clk_end_time, early_late); - } - else { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); - Arrival clk_arrival = clk_end_time; - reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, - clk_arrival, early_late); - } - reportPath1(path, expanded, false, time_offset); + if (is_path_delay) { + if (delayGreater(clk_delay, 0.0, this)) + reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, + clk_end_time, early_late); + } + else { + reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); + Arrival clk_arrival = clk_end_time; + reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, + clk_arrival, early_late); + } + reportPath1(path, expanded, false, time_offset); } } } @@ -2169,7 +2159,7 @@ ReportPath::reportTgtClk(const PathEnd *end) const void ReportPath::reportTgtClk(const PathEnd *end, - float prev_time) const + float prev_time) const { const Clock *clk = end->targetClk(this); const Path *clk_path = end->targetClkPath(); @@ -2178,8 +2168,8 @@ ReportPath::reportTgtClk(const PathEnd *end, void ReportPath::reportTgtClk(const PathEnd *end, - float prev_time, - bool is_prop) const + float prev_time, + bool is_prop) const { float src_offset = end->sourceClkOffset(this); reportTgtClk(end, prev_time, src_offset, is_prop); @@ -2187,9 +2177,9 @@ ReportPath::reportTgtClk(const PathEnd *end, void ReportPath::reportTgtClk(const PathEnd *end, - float prev_time, - float src_offset, - bool is_prop) const + float prev_time, + float src_offset, + bool is_prop) const { const ClockEdge *clk_edge = end->targetClkEdge(this); Clock *clk = clk_edge->clock(); @@ -2202,8 +2192,7 @@ ReportPath::reportTgtClk(const PathEnd *end, + src_offset; Arrival clk_delay = end->targetClkDelay(this); Arrival clk_arrival = clk_time + clk_delay; - PathAnalysisPt *path_ap = end->pathAnalysisPt(this)->tgtClkAnalysisPt(); - const MinMax *min_max = path_ap->pathMinMax(); + 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); const TimingRole *check_role = end->checkRole(this); @@ -2212,27 +2201,28 @@ ReportPath::reportTgtClk(const PathEnd *end, + end->targetClkOffset(this) + end->targetClkMcpAdjustment(this); const EarlyLate *early_late = check_role->tgtClkEarlyLate(); - if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late)) { + const Mode *mode = end->path()->mode(this); + const Sdc *sdc = mode->sdc(); + if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late, sdc)) { float insertion_offset = - clk_path ? tgtClkInsertionOffet(clk_path, early_late, path_ap) : 0.0; - reportGenClkSrcAndPath(clk_path, clk, clk_rf, early_late, path_ap, - time_offset, time_offset + insertion_offset, false); + clk_path ? tgtClkInsertionOffet(clk_path, early_late) : 0.0; + reportGenClkSrcAndPath(clk_path, clk, clk_rf, early_late, time_offset, + time_offset + insertion_offset, false, mode); } else { Arrival insertion = end->targetClkInsertionDelay(this); if (clk_path) { - reportClkSrcLatency(insertion, clk_time, early_late); - PathExpanded clk_expanded(clk_path, this); - float insertion_offset = tgtClkInsertionOffet(clk_path, early_late, - path_ap); - reportPath6(clk_path, clk_expanded, 0, is_prop, reportClkPath(), - delay_zero, time_offset + insertion_offset); + reportClkSrcLatency(insertion, clk_time, early_late); + PathExpanded clk_expanded(clk_path, this); + float insertion_offset = tgtClkInsertionOffet(clk_path, early_late); + reportPath6(clk_path, clk_expanded, 0, is_prop, reportClkPath(), + delay_zero, time_offset + insertion_offset); } else { - // Output departure. - Arrival clk_arrival = clk_time + clk_delay; - reportLine(clkNetworkDelayIdealProp(clk->isPropagated()), - clk_delay, clk_arrival, min_max); + // Output departure. + Arrival clk_arrival = clk_time + clk_delay; + reportLine(clkNetworkDelayIdealProp(clk->isPropagated()), + clk_delay, clk_arrival, min_max); } } reportClkUncertainty(end, clk_arrival); @@ -2246,49 +2236,48 @@ 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), - min_max, clk_end_rf); + prev_time + + end->targetClkArrival(this) + + end->sourceClkOffset(this), + min_max, clk_end_rf); } } } float ReportPath::tgtClkInsertionOffet(const Path *clk_path, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const + const EarlyLate *early_late) const { const ClkInfo *clk_info = clk_path->clkInfo(this); const Pin *src_pin = clk_info->clkSrc(); const ClockEdge *clk_edge = clk_info->clkEdge(); const Clock *clk = clk_edge->clock(); const RiseFall *clk_rf = clk_edge->transition(); - const MinMax *min_max = path_ap->pathMinMax(); + const MinMax *min_max = clk_path->minMax(this); + const Mode *mode = clk_path->mode(this); Arrival path_insertion = search_->clockInsertion(clk, src_pin, clk_rf, - min_max, min_max, - path_ap); + min_max, min_max, mode); Arrival tgt_insertion = search_->clockInsertion(clk, src_pin, clk_rf, - min_max, early_late, - path_ap); + min_max, early_late, mode); return delayAsFloat(tgt_insertion - path_insertion); } bool ReportPath::pathFromGenPropClk(const Path *clk_path, - const EarlyLate *early_late) const + const EarlyLate *early_late) const { const ClkInfo *clk_info = clk_path->tag(search_)->clkInfo(); const ClockEdge *clk_edge = clk_info->clkEdge(); if (clk_edge) { + const Sdc *sdc = clk_path->sdc(this); const Clock *clk = clk_edge->clock(); float insertion; bool exists; - sdc_->clockInsertion(clk, clk_info->clkSrc(), - clk_edge->transition(), - clk_path->minMax(this), - early_late, - insertion, exists); + sdc->clockInsertion(clk, clk_info->clkSrc(), + clk_edge->transition(), + clk_path->minMax(this), + early_late, + insertion, exists); return !exists && clk->isGeneratedWithPropagatedMaster(); } @@ -2298,36 +2287,37 @@ ReportPath::pathFromGenPropClk(const Path *clk_path, bool ReportPath::isGenPropClk(const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - const EarlyLate *early_late) const + const RiseFall *clk_rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Sdc *sdc) const { float insertion; bool exists; - sdc_->clockInsertion(clk, clk->srcPin(), clk_rf, - min_max, early_late, - insertion, exists); + sdc->clockInsertion(clk, clk->srcPin(), clk_rf, + min_max, early_late, + insertion, exists); return !exists && clk->isGeneratedWithPropagatedMaster(); } void ReportPath::reportClkLine(const Clock *clk, - const char *clk_name, - const RiseFall *clk_rf, - Arrival clk_time, - const MinMax *min_max) const + const char *clk_name, + const RiseFall *clk_rf, + Arrival clk_time, + const MinMax *min_max) const { reportClkLine(clk, clk_name, clk_rf, 0.0, clk_time, min_max); } void ReportPath::reportClkLine(const Clock *clk, - const char *clk_name, - const RiseFall *clk_rf, - Arrival prev_time, - Arrival clk_time, - const MinMax *min_max) const + const char *clk_name, + const RiseFall *clk_rf, + Arrival prev_time, + Arrival clk_time, + const MinMax *min_max) const { const char *rise_fall = asRiseFall(clk_rf); auto clk_msg = stdstrPrint("clock %s (%s edge)", clk_name, rise_fall); @@ -2342,60 +2332,60 @@ ReportPath::reportClkLine(const Clock *clk, bool ReportPath::reportGenClkSrcPath(const Path *clk_path, - const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - const EarlyLate *early_late) const + const Clock *clk, + const RiseFall *clk_rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Sdc *sdc) const { bool from_gen_prop_clk = clk_path ? pathFromGenPropClk(clk_path, early_late) - : isGenPropClk(clk, clk_rf, min_max, early_late); + : isGenPropClk(clk, clk_rf, min_max, early_late, sdc); return from_gen_prop_clk && format_ == ReportPathFormat::full_clock_expanded; } void ReportPath::reportGenClkSrcAndPath(const Path *path, - const Clock *clk, - const RiseFall *clk_rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap, - float time_offset, - float path_time_offset, - bool clk_used_as_data) const + const Clock *clk, + const RiseFall *clk_rf, + const EarlyLate *early_late, + float time_offset, + float path_time_offset, + bool clk_used_as_data, + const Mode *mode) const { const Pin *clk_pin = path ? path->clkInfo(search_)->clkSrc() : clk->defaultPin(); float gclk_time = clk->edge(clk_rf)->time() + time_offset; bool skip_first_path = reportGenClkSrcPath1(clk, clk_pin, clk_rf, - early_late, path_ap, gclk_time, - time_offset, clk_used_as_data); + early_late, gclk_time, + time_offset, clk_used_as_data, + mode); if (path) { PathExpanded expanded(path, this); reportPath2(path, expanded, skip_first_path, clk_used_as_data, - path_time_offset); + path_time_offset); } } bool ReportPath::reportGenClkSrcPath1(const Clock *clk, - const Pin *clk_pin, - const RiseFall *clk_rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap, - float gclk_time, - float time_offset, - bool clk_used_as_data) const -{ - PathAnalysisPt *insert_ap = path_ap->insertionAnalysisPt(early_late); - const MinMax *min_max = path_ap->pathMinMax(); - const Path *src_path = search_->genclks()->srcPath(clk, clk_pin, - clk_rf, insert_ap); + const Pin *clk_pin, + const RiseFall *clk_rf, + const EarlyLate *early_late, + float gclk_time, + float time_offset, + bool clk_used_as_data, + const Mode *mode) const +{ + const Path *src_path = mode->genclks()->srcPath(clk, clk_pin, clk_rf, early_late); if (src_path) { const ClkInfo *src_clk_info = src_path->clkInfo(this); const ClockEdge *src_clk_edge = src_clk_info->clkEdge(); const Clock *src_clk = src_clk_info->clock(); + const MinMax *min_max = src_path->minMax(this); if (src_clk) { bool skip_first_path = false; const RiseFall *src_clk_rf = src_clk_edge->transition(); @@ -2403,15 +2393,17 @@ ReportPath::reportGenClkSrcPath1(const Clock *clk, if (src_clk->isGeneratedWithPropagatedMaster() && src_clk_info->isPropagated()) { skip_first_path = reportGenClkSrcPath1(src_clk, src_clk_pin, - src_clk_rf, early_late, path_ap, + src_clk_rf, early_late, gclk_time, time_offset, - clk_used_as_data); + clk_used_as_data, + mode); } else { + const Mode *mode = src_path->mode(this); const Arrival insertion = search_->clockInsertion(src_clk, src_clk_pin, src_clk_rf, - path_ap->pathMinMax(), - early_late, path_ap); + min_max, + early_late, mode); reportClkSrcLatency(insertion, gclk_time, early_late); } PathExpanded src_expanded(src_path, this); @@ -2426,35 +2418,35 @@ ReportPath::reportGenClkSrcPath1(const Clock *clk, if (clk->isPropagated()) reportClkSrcLatency(0.0, gclk_time, early_late); else if (!clk_used_as_data) - reportLine("clock network delay (ideal)", 0.0, gclk_time, min_max); + reportLine("clock network delay (ideal)", 0.0, gclk_time, MinMax::max()); } return src_path != nullptr; } void ReportPath::reportClkSrcLatency(Arrival insertion, - float clk_time, - const EarlyLate *early_late) const + float clk_time, + const EarlyLate *early_late) const { reportLine("clock source latency", insertion, clk_time + insertion, early_late); } void ReportPath::reportPathLine(const Path *path, - Arrival incr, - Arrival time, - const char *line_case) const + Arrival incr, + Arrival time, + const char *line_case) const { Vertex *vertex = path->vertex(this); Pin *pin = vertex->pin(); const string what = descriptionField(vertex); const RiseFall *rf = path->transition(this); bool is_driver = network_->isDriver(pin); - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - const EarlyLate *early_late = path_ap->pathMinMax(); - DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt(); - DcalcAPIndex ap_index = dcalc_ap->index(); - Slew slew = graph_->slew(vertex, rf, ap_index); + const EarlyLate *early_late = path->minMax(this); + const Scene *scene = path->scene(this); + const MinMax *min_max = path->minMax(this); + DcalcAPIndex slew_index = path->dcalcAnalysisPtIndex(this); + Slew slew = graph_->slew(vertex, rf, slew_index); float cap = field_blank_; Instance *inst = network_->instance(pin); string src_attr = ""; @@ -2462,15 +2454,15 @@ ReportPath::reportPathLine(const Path *path, src_attr = network_->getAttribute(inst, "src"); // Don't show capacitance field for input pins. if (is_driver && field_capacitance_->enabled()) - cap = graph_delay_calc_->loadCap(pin, rf, dcalc_ap); + 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, - line_case); + incr, time, false, early_late, rf, src_attr, + line_case); } void ReportPath::reportRequired(const PathEnd *end, - string margin_msg) const + string margin_msg) const { Required req_time = end->requiredTimeOffset(this); const EarlyLate *early_late = end->clkEarlyLate(this); @@ -2493,7 +2485,7 @@ ReportPath::reportSlack(const PathEnd *end) const { const EarlyLate *early_late = end->pathEarlyLate(this); reportLine("data required time", end->requiredTimeOffset(this), - early_late->opposite()); + early_late->opposite()); reportLineNegative("data arrival time", end->dataArrivalTimeOffset(this), early_late); reportDashLine(); reportSlack(end->slack(this)); @@ -2511,7 +2503,7 @@ ReportPath::reportSlack(Slack slack) const void ReportPath::reportSpaceSlack(const PathEnd *end, - string &result) const + string &result) const { Slack slack = end->slack(this); reportSpaceSlack(slack, result); @@ -2519,7 +2511,7 @@ ReportPath::reportSpaceSlack(const PathEnd *end, void ReportPath::reportSpaceSlack(Slack slack, - string &result) const + string &result) const { const EarlyLate *early_late = EarlyLate::early(); reportSpaceFieldDelay(slack, early_late, result); @@ -2530,19 +2522,19 @@ ReportPath::reportSpaceSlack(Slack slack, void ReportPath::reportCommonClkPessimism(const PathEnd *end, - Arrival &clk_arrival) const + Arrival &clk_arrival) const { if (variables_->crprEnabled()) { Crpr pessimism = end->checkCrpr(this); clk_arrival += pessimism; reportLine("clock reconvergence pessimism", pessimism, clk_arrival, - end->clkEarlyLate(this)); + end->clkEarlyLate(this)); } } void ReportPath::reportClkUncertainty(const PathEnd *end, - Arrival &clk_arrival) const + Arrival &clk_arrival) const { const EarlyLate *early_late = end->clkEarlyLate(this); float uncertainty = end->targetNonInterClkUncertainty(this); @@ -2560,7 +2552,7 @@ ReportPath::reportClkUncertainty(const PathEnd *end, void ReportPath::reportPath(const PathEnd *end, - const PathExpanded &expanded) const + const PathExpanded &expanded) const { reportPathHeader(); // Source clk offset for path delays removes clock phase time. @@ -2602,9 +2594,9 @@ ReportPath::reportPathFull(const Path *path) const // Main entry point for reporting a path. void ReportPath::reportPath1(const Path *path, - const PathExpanded &expanded, - bool clk_used_as_data, - float time_offset) const + const PathExpanded &expanded, + bool clk_used_as_data, + float time_offset) const { reportPath2(path, expanded, false, clk_used_as_data, time_offset); } @@ -2612,38 +2604,38 @@ ReportPath::reportPath1(const Path *path, // Alternate entry point with skip_first_path arg. void ReportPath::reportPath2(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool clk_used_as_data, - float time_offset) const + const PathExpanded &expanded, + bool skip_first_path, + bool clk_used_as_data, + float time_offset) const { bool clk_is_propagated = path->clkInfo(search_)->isPropagated(); bool report_clk_path = (reportClkPath() && clk_is_propagated) || clk_used_as_data; bool propagated_clk = clk_is_propagated || clk_used_as_data; reportPath4(path, expanded, skip_first_path, propagated_clk, - report_clk_path, time_offset); + report_clk_path, time_offset); } // Alternate entry point with report_clk_path arg. void ReportPath::reportPath3(const Path *path, - const PathExpanded &expanded, - bool report_clk_path, - float time_offset) const + const PathExpanded &expanded, + bool report_clk_path, + float time_offset) const { bool propagated_clk = path->clkInfo(search_)->isPropagated(); reportPath4(path, expanded, false, propagated_clk, - report_clk_path, time_offset); + report_clk_path, time_offset); } void ReportPath::reportPath4(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool propagated_clk, - bool report_clk_path, - float time_offset) const + const PathExpanded &expanded, + bool skip_first_path, + bool propagated_clk, + bool report_clk_path, + float time_offset) const { const Path *d_path, *q_path; Edge *d_q_edge; @@ -2657,35 +2649,35 @@ ReportPath::reportPath4(const Path *path, const EarlyLate *early_late = latch_enable_path->minMax(this); latch_enable_time = search_->clkPathArrival(latch_enable_path); if (report_clk_path) { - PathExpanded enable_expanded(latch_enable_path, this); - // Report the path to the latch enable. - reportPath5(latch_enable_path, enable_expanded, skip_first_path, - propagated_clk, report_clk_path, time_offset); + PathExpanded enable_expanded(latch_enable_path, this); + // Report the path to the latch enable. + 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 incr = latch_time_given; if (delayGreaterEqual(incr, 0.0, this)) - reportLine("time given to startpoint", incr, time, early_late); + reportLine("time given to startpoint", incr, time, early_late); else - reportLine("time borrowed from startpoint", incr, time, early_late); + reportLine("time borrowed from startpoint", incr, time, early_late); // 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); + latch_enable_time + latch_time_given, time_offset); } } else reportPath5(path, expanded, skip_first_path, propagated_clk, - report_clk_path, time_offset); + report_clk_path, time_offset); } void ReportPath::reportPath5(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool propagated_clk, - bool report_clk_path, - float time_offset) const + const PathExpanded &expanded, + bool skip_first_path, + bool propagated_clk, + bool report_clk_path, + float time_offset) const { size_t path_first_index = 0; Arrival prev_time = 0.0; @@ -2695,23 +2687,24 @@ ReportPath::reportPath5(const Path *path, prev_time = start->arrival() + time_offset; } reportPath6(path, expanded, path_first_index, propagated_clk, - report_clk_path, prev_time, time_offset); + report_clk_path, prev_time, time_offset); } // This does the real workk of reporting an expanded path. void ReportPath::reportPath6(const Path *path, - const PathExpanded &expanded, - size_t path_first_index, - bool propagated_clk, - bool report_clk_path, - Arrival prev_time, - float time_offset) const + const PathExpanded &expanded, + size_t path_first_index, + bool propagated_clk, + bool report_clk_path, + Arrival prev_time, + float time_offset) const { size_t path_last_index = expanded.size() - 1; + const Scene *scene = path->scene(this); const MinMax *min_max = path->minMax(this); - DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(this)->dcalcAnalysisPt(); - DcalcAPIndex ap_index = dcalc_ap->index(); + const Sdc *sdc = path->sdc(this); + DcalcAPIndex slew_index = path->dcalcAnalysisPtIndex(this); const Path *clk_path = expanded.clkPath(); Vertex *clk_start = clk_path ? clk_path->vertex(this) : nullptr; for (size_t i = path_first_index; i <= path_last_index; i++) { @@ -2731,86 +2724,86 @@ ReportPath::reportPath6(const Path *path, // Always show the search start point (register clk pin). // Skip reporting the clk tree unless it is requested. if (is_clk_start - || report_clk_path - || !is_clk) { + || report_clk_path + || !is_clk) { const RiseFall *rf = path1->transition(this); - Slew slew = graph_->slew(vertex, rf, ap_index); + Slew slew = graph_->slew(vertex, rf, slew_index); if (prev_arc == nullptr) { - // First path. - reportInputExternalDelay(path1, time_offset); - size_t next_index = i + 1; - const Path *next_path = expanded.path(next_index); - if (network_->isTopLevelPort(pin) - && next_path - && !nextArcAnnotated(next_path, next_index, expanded, ap_index) - && hasExtInputDriver(pin, rf, min_max)) { - // Pin is an input port with drive_cell/drive_resistance. - // 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; - incr = delayIncr(next_time, time, min_max); - time = next_time; - line_case = "input_drive"; - } - else if (is_clk) { - 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; - incr = 0.0; - line_case = "clk_first"; - } - else { - incr = 0.0; - line_case = "first"; - } + // First path. + reportInputExternalDelay(path1, time_offset); + size_t next_index = i + 1; + const Path *next_path = expanded.path(next_index); + if (network_->isTopLevelPort(pin) + && next_path + && !nextArcAnnotated(next_path, next_index, expanded, slew_index) + && hasExtInputDriver(pin, rf, min_max, sdc)) { + // Pin is an input port with drive_cell/drive_resistance. + // 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; + incr = delayIncr(next_time, time, min_max); + time = next_time; + line_case = "input_drive"; + } + else if (is_clk) { + 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; + incr = 0.0; + line_case = "clk_first"; + } + else { + incr = 0.0; + line_case = "first"; + } } else if (is_clk_start - && is_clk - && !report_clk_path) { - // Clock start point and clock path are not reported. - incr = 0.0; - if (!propagated_clk) { - // Ideal clock. - const ClockEdge *src_clk_edge = path->clkEdge(this); - time = search_->clkPathArrival(path1) + time_offset; - if (src_clk_edge) { - Clock *src_clk = src_clk_edge->clock(); - const RiseFall *src_clk_rf = src_clk_edge->transition(); - slew = src_clk->slew(src_clk_rf, min_max); - } - } - line_case = "clk_start"; + && is_clk + && !report_clk_path) { + // Clock start point and clock path are not reported. + incr = 0.0; + if (!propagated_clk) { + // Ideal clock. + const ClockEdge *src_clk_edge = path->clkEdge(this); + time = search_->clkPathArrival(path1) + time_offset; + if (src_clk_edge) { + Clock *src_clk = src_clk_edge->clock(); + const RiseFall *src_clk_rf = src_clk_edge->transition(); + slew = src_clk->slew(src_clk_rf, min_max); + } + } + line_case = "clk_start"; } else if (is_clk - && report_clk_path - && !propagated_clk) { - // Zero the clock network delays for ideal clocks. - incr = 0.0; - time = prev_time; - const ClockEdge *src_clk_edge = path->clkEdge(this); - const Clock *src_clk = src_clk_edge->clock(); - const RiseFall *src_clk_rf = src_clk_edge->transition(); - slew = src_clk->slew(src_clk_rf, min_max); - line_case = "clk_ideal"; + && report_clk_path + && !propagated_clk) { + // Zero the clock network delays for ideal clocks. + incr = 0.0; + time = prev_time; + const ClockEdge *src_clk_edge = path->clkEdge(this); + const Clock *src_clk = src_clk_edge->clock(); + const RiseFall *src_clk_rf = src_clk_edge->transition(); + slew = src_clk->slew(src_clk_rf, min_max); + line_case = "clk_ideal"; } else if (is_clk && !is_clk_start) { - incr = delayIncr(time, prev_time, min_max); - line_case = "clk_prop"; + incr = delayIncr(time, prev_time, min_max); + line_case = "clk_prop"; } else { - incr = delayIncr(time, prev_time, min_max); - line_case = "normal"; + incr = delayIncr(time, prev_time, min_max); + line_case = "normal"; } if (vertex->isDriver(network_)) { float cap = field_blank_; float fanout = field_blank_; if (field_capacitance_->enabled()) - cap = graph_delay_calc_->loadCap(pin, rf, dcalc_ap); + cap = graph_delay_calc_->loadCap(pin, rf, scene, min_max); if (field_fanout_->enabled()) - fanout = drvrFanout(vertex, dcalc_ap->corner(), min_max); + fanout = drvrFanout(vertex, scene, min_max); const string what = descriptionField(vertex); reportLine(what.c_str(), cap, slew, fanout, incr, time, false, min_max, rf, src_attr, @@ -2861,8 +2854,8 @@ ReportPath::reportHierPinsThru(const Path *path) const Delay ReportPath::delayIncr(Delay time, - Delay prev, - const MinMax *min_max) const + Delay prev, + const MinMax *min_max) const { if (report_sigmas_) return delayRemove(time, prev); @@ -2872,9 +2865,9 @@ ReportPath::delayIncr(Delay time, bool ReportPath::nextArcAnnotated(const Path *next_path, - size_t next_index, - const PathExpanded &expanded, - DcalcAPIndex ap_index) const + size_t next_index, + const PathExpanded &expanded, + DcalcAPIndex ap_index) const { const TimingArc *arc = expanded.path(next_index)->prevArc(this); Edge *edge = next_path->prevEdge(this); @@ -2934,9 +2927,10 @@ ReportPath::descriptionNet(const Pin *pin) const float ReportPath::drvrFanout(Vertex *drvr, - const Corner *corner, - const MinMax *min_max) const + const Scene *scene, + const MinMax *min_max) const { + const Sdc *sdc = scene->sdc(); float fanout = 0.0; VertexOutEdgeIterator iter(drvr, graph_); while (iter.hasNext()) { @@ -2946,7 +2940,7 @@ ReportPath::drvrFanout(Vertex *drvr, if (network_->isTopLevelPort(pin)) { // Output port counts as a fanout. Port *port = network_->port(pin); - fanout += sdc_->portExtFanout(port, corner, min_max) + 1; + fanout += sdc->portExtFanout(port, min_max) + 1; } else fanout++; @@ -2957,19 +2951,20 @@ ReportPath::drvrFanout(Vertex *drvr, bool ReportPath::hasExtInputDriver(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) const + const RiseFall *rf, + const MinMax *min_max, + const Sdc *sdc) const { Port *port = network_->port(pin); - InputDrive *drive = sdc_->findInputDrive(port); + InputDrive *drive = sdc->findInputDrive(port); return (drive - && (drive->hasDriveResistance(rf, min_max) - || drive->hasDriveCell(rf, min_max))); + && (drive->hasDriveResistance(rf, min_max) + || drive->hasDriveCell(rf, min_max))); } void ReportPath::reportInputExternalDelay(const Path *first_path, - float time_offset) const + float time_offset) const { const Pin *first_pin = first_path->pin(graph_); if (!pathFromClkPin(first_path, first_pin)) { @@ -2980,17 +2975,17 @@ ReportPath::reportInputExternalDelay(const Path *first_path, if (input_delay) { const Pin *ref_pin = input_delay->refPin(); if (ref_pin) { - Path ref_path; - pathInputDelayRefPath(first_path, input_delay, ref_path); - if (!ref_path.isNull() && reportClkPath()) { - PathExpanded ref_expanded(&ref_path, this); - reportPath3(&ref_path, ref_expanded, true, 0.0); - } + Path ref_path; + pathInputDelayRefPath(first_path, input_delay, ref_path); + if (!ref_path.isNull() && reportClkPath()) { + PathExpanded ref_expanded(&ref_path, this); + reportPath3(&ref_path, ref_expanded, true, 0.0); + } } float input_arrival = - input_delay->delays()->value(rf, first_path->minMax(this)); + input_delay->delays()->value(rf, first_path->minMax(this)); reportLine("input external delay", input_arrival, time, - early_late, rf); + early_late, rf); } else if (network_->isTopLevelPort(first_pin)) reportLine("input external delay", 0.0, time, early_late, rf); @@ -3006,17 +3001,17 @@ ReportPath::pathInputDelay(const Path *first_path) const void ReportPath::pathInputDelayRefPath(const Path *path, - const InputDelay *input_delay, - // Return value. - Path &ref_path) const + const InputDelay *input_delay, + // Return value. + Path &ref_path) const { const Pin *ref_pin = input_delay->refPin(); const RiseFall *ref_rf = input_delay->refTransition(); Vertex *ref_vertex = graph_->pinDrvrVertex(ref_pin); if (ref_vertex) { - const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); const ClockEdge *clk_edge = path->clkEdge(this); - VertexPathIterator path_iter(ref_vertex, ref_rf, path_ap, this); + VertexPathIterator path_iter(ref_vertex, path->scene(this), + path->minMax(this), ref_rf,this); while (path_iter.hasNext()) { Path *path = path_iter.next(); if (path->isClock(this) @@ -3038,7 +3033,7 @@ ReportPath::reportPathHeader() const for (const ReportField *field : fields_) { if (field->enabled()) { if (!first_field) - line += ' '; + line += ' '; reportField(field->title(), field, line); first_field = false; } @@ -3051,87 +3046,87 @@ ReportPath::reportPathHeader() const // Report total. void ReportPath::reportLine(const char *what, - Delay total, - const EarlyLate *early_late) const + Delay total, + const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, - field_blank_, total, false, early_late, nullptr, - "", nullptr); + field_blank_, total, false, early_late, nullptr, + "", nullptr); } // Report negative total. void ReportPath::reportLineNegative(const char *what, - Delay total, - const EarlyLate *early_late) const + Delay total, + const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, - field_blank_, total, true, early_late, nullptr, - "", nullptr); + field_blank_, total, true, early_late, nullptr, + "", nullptr); } // Report total, and transition suffix. void ReportPath::reportLine(const char *what, - Delay total, - const EarlyLate *early_late, - const RiseFall *rf) const + Delay total, + const EarlyLate *early_late, + const RiseFall *rf) const { reportLine(what, field_blank_, field_blank_, field_blank_, - field_blank_, total, false, early_late, rf, "", - nullptr); + field_blank_, total, false, early_late, rf, "", + nullptr); } // Report increment, and total. void ReportPath::reportLine(const char *what, - Delay incr, - Delay total, - const EarlyLate *early_late) const + Delay incr, + Delay total, + const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, - incr, total, false, early_late, nullptr, "", - nullptr); + incr, total, false, early_late, nullptr, "", + nullptr); } // Report increment, total, and transition suffix. void ReportPath::reportLine(const char *what, - Delay incr, - Delay total, - const EarlyLate *early_late, - const RiseFall *rf) const + Delay incr, + Delay total, + const EarlyLate *early_late, + const RiseFall *rf) const { reportLine(what, field_blank_, field_blank_, field_blank_, - incr, total, false, early_late, rf, "", - nullptr); + incr, total, false, early_late, rf, "", + nullptr); } // Report slew, increment, and total. void ReportPath::reportLine(const char *what, - Slew slew, - Delay incr, - Delay total, - const EarlyLate *early_late) const + Slew slew, + Delay incr, + Delay total, + const EarlyLate *early_late) const { reportLine(what, field_blank_, slew, field_blank_, - incr, total, false, early_late, nullptr, - "", nullptr); + incr, total, false, early_late, nullptr, + "", nullptr); } void ReportPath::reportLine(const char *what, - float cap, - Slew slew, - float fanout, - Delay incr, - Delay total, - bool total_with_minus, - const EarlyLate *early_late, - const RiseFall *rf, - string src_attr, - const char *line_case) const + float cap, + Slew slew, + float fanout, + Delay incr, + Delay total, + bool total_with_minus, + const EarlyLate *early_late, + const RiseFall *rf, + string src_attr, + const char *line_case) const { string line; size_t field_index = 0; @@ -3141,44 +3136,44 @@ ReportPath::reportLine(const char *what, if (field->enabled()) { if (!first_field) - line += ' '; + line += ' '; if (field == field_description_) - reportDescription(what, first_field, last_field, line); + reportDescription(what, first_field, last_field, line); else if (field == field_fanout_) { - if (fanout == field_blank_) - reportFieldBlank(field, line); - else - line += stdstrPrint("%*d", + if (fanout == field_blank_) + reportFieldBlank(field, line); + else + line += stdstrPrint("%*d", field_fanout_->width(), static_cast(fanout)); } else if (field == field_capacitance_) - reportField(cap, field, line); + reportField(cap, field, line); else if (field == field_slew_) - reportFieldDelay(slew, early_late, field, line); + reportFieldDelay(slew, early_late, field, line); else if (field == field_incr_) - reportFieldDelay(incr, early_late, field, line); + reportFieldDelay(incr, early_late, field, line); else if (field == field_total_) { - if (total_with_minus) - reportFieldDelayMinus(total, early_late, field, line); - else - reportFieldDelay(total, early_late, field, line); + if (total_with_minus) + reportFieldDelayMinus(total, early_late, field, line); + else + reportFieldDelay(total, early_late, field, line); } else if (field == field_edge_) { - if (rf) - reportField(rf->shortName(), field, line); - else - reportFieldBlank(field, line); + if (rf) + reportField(rf->shortName(), field, line); + else + reportFieldBlank(field, line); } else if (field == field_src_attr_) { - if (src_attr != "") - reportField(src_attr.c_str(), field, line); - else - reportFieldBlank(field, line); + if (src_attr != "") + reportField(src_attr.c_str(), field, line); + else + reportFieldBlank(field, line); } else if (field == field_case_ && line_case) - line += line_case; + line += line_case; first_field = false; } @@ -3195,8 +3190,8 @@ ReportPath::reportLine(const char *what, // Only the total field. void ReportPath::reportLineTotal(const char *what, - Delay incr, - const EarlyLate *early_late) const + Delay incr, + const EarlyLate *early_late) const { reportLineTotal1(what, incr, false, early_late); } @@ -3204,17 +3199,17 @@ ReportPath::reportLineTotal(const char *what, // Only the total field and always with leading minus sign. void ReportPath::reportLineTotalMinus(const char *what, - Delay decr, - const EarlyLate *early_late) const + Delay decr, + const EarlyLate *early_late) const { reportLineTotal1(what, decr, true, early_late); } void ReportPath::reportLineTotal1(const char *what, - Delay incr, - bool incr_with_minus, - const EarlyLate *early_late) const + Delay incr, + bool incr_with_minus, + const EarlyLate *early_late) const { string line; reportDescription(what, line); @@ -3236,16 +3231,16 @@ ReportPath::reportDashLineTotal() const void ReportPath::reportDescription(const char *what, - string &line) const + string &line) const { reportDescription(what, false, false, line); } void ReportPath::reportDescription(const char *what, - bool first_field, - bool last_field, - string &line) const + bool first_field, + bool last_field, + string &line) const { line += what; int length = strlen(what); @@ -3264,8 +3259,8 @@ ReportPath::reportDescription(const char *what, void ReportPath::reportFieldTime(float value, - ReportField *field, - string &line) const + ReportField *field, + string &line) const { if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); @@ -3280,7 +3275,7 @@ ReportPath::reportFieldTime(float value, void ReportPath::reportSpaceFieldTime(float value, - string &line) const + string &line) const { line += ' '; reportFieldTime(value, field_total_, line); @@ -3288,8 +3283,8 @@ ReportPath::reportSpaceFieldTime(float value, void ReportPath::reportSpaceFieldDelay(Delay value, - const EarlyLate *early_late, - string &line) const + const EarlyLate *early_late, + string &line) const { line += ' '; reportTotalDelay(value, early_late, line); @@ -3297,8 +3292,8 @@ ReportPath::reportSpaceFieldDelay(Delay value, void ReportPath::reportTotalDelay(Delay value, - const EarlyLate *early_late, - string &line) const + const EarlyLate *early_late, + string &line) const { const char *str = delayAsString(value, early_late, this, digits_); if (stringEq(str, minus_zero_)) @@ -3310,9 +3305,9 @@ ReportPath::reportTotalDelay(Delay value, // Total time always with leading minus sign. void ReportPath::reportFieldDelayMinus(Delay value, - const EarlyLate *early_late, - const ReportField *field, - string &line) const + const EarlyLate *early_late, + const ReportField *field, + string &line) const { if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); @@ -3330,9 +3325,9 @@ ReportPath::reportFieldDelayMinus(Delay value, void ReportPath::reportFieldDelay(Delay value, - const EarlyLate *early_late, - const ReportField *field, - string &line) const + const EarlyLate *early_late, + const ReportField *field, + string &line) const { if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); @@ -3349,8 +3344,8 @@ ReportPath::reportFieldDelay(Delay value, void ReportPath::reportField(float value, - const ReportField *field, - string &line) const + const ReportField *field, + string &line) const { if (value == field_blank_) reportFieldBlank(field, line); @@ -3371,8 +3366,8 @@ ReportPath::reportField(float value, void ReportPath::reportField(const char *value, - const ReportField *field, - string &line) const + const ReportField *field, + string &line) const { if (field->leftJustify()) line += value; @@ -3384,7 +3379,7 @@ ReportPath::reportField(const char *value, void ReportPath::reportFieldBlank(const ReportField *field, - string &line) const + string &line) const { line += field->blank(); } @@ -3396,7 +3391,7 @@ ReportPath::reportDashLine() const for (const ReportField *field : fields_) { if (field->enabled()) { for (int i = 0; i < field->width(); i++) - line += '-'; + line += '-'; } } line += "------"; @@ -3448,7 +3443,7 @@ ReportPath::asRiseFall(const RiseFall *rf) const // Find the startpoint type from the first path edge. const char * ReportPath::edgeRegLatchDesc(const Edge *first_edge, - const TimingArc *first_arc) const + const TimingArc *first_arc) const { const TimingRole *role = first_arc->role(); if (role == TimingRole::latchDtoQ()) { @@ -3459,7 +3454,7 @@ ReportPath::edgeRegLatchDesc(const Edge *first_edge, const FuncExpr *enable_func; const RiseFall *enable_rf; cell->latchEnable(first_edge->timingArcSet(), - enable_port, enable_func, enable_rf); + enable_port, enable_func, enable_rf); return latchDesc(enable_rf); } } @@ -3473,12 +3468,12 @@ ReportPath::edgeRegLatchDesc(const Edge *first_edge, const char * ReportPath::checkRegLatchDesc(const TimingRole *role, - const RiseFall *clk_rf) const + const RiseFall *clk_rf) const { if (role == TimingRole::regClkToQ()) return regDesc(clk_rf); else if (role == TimingRole::latchEnToQ() - || role == TimingRole::latchDtoQ()) + || role == TimingRole::latchDtoQ()) return latchDesc(clk_rf); else // Default when we don't know better. diff --git a/search/ReportPath.hh b/search/ReportPath.hh index b9f4364b2..39c8b1a55 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -25,19 +25,22 @@ #pragma once #include +#include #include "StringSeq.hh" #include "SearchClass.hh" #include "PathEnd.hh" +#include "CheckMinPulseWidths.hh" +#include "CheckMinPeriods.hh" +#include "CheckMaxSkews.hh" namespace sta { -class Corner; -class DcalcAnalysisPt; +class Scene; class PathExpanded; class ReportField; -typedef Vector ReportFieldSeq; +using ReportFieldSeq = std::vector; class ReportPath : public StaState { @@ -49,11 +52,11 @@ public: void setReportFieldOrder(StringSeq *field_names); void setReportFields(bool report_input_pin, bool report_hier_pins, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout, - bool report_src_attr); + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout, + bool report_src_attr); int digits() const { return digits_; } void setDigits(int digits); void setNoSplit(bool no_split); @@ -70,7 +73,7 @@ public: // Previous path end is used to detect path group changes // so headers are reported by group. void reportPathEnd(const PathEnd *end, - const PathEnd *prev_end, + const PathEnd *prev_end, bool last) const; void reportPathEnds(const PathEndSeq *ends) const; void reportPath(const Path *path) const; @@ -116,44 +119,42 @@ public: void reportSlackOnlyHeader() const; void reportSlackOnly(const PathEnd *end) const; - void reportMpwCheck(const MinPulseWidthCheck *check, - bool verbose) const; - void reportMpwChecks(const MinPulseWidthCheckSeq *checks, - bool verbose) const; + void reportMpwCheck(const MinPulseWidthCheck &check, + bool verbose) const; + void reportMpwChecks(const MinPulseWidthCheckSeq &checks, + bool verbose) const; void reportMpwHeaderShort() const; - void reportShort(const MinPulseWidthCheck *check) const; - void reportVerbose(const MinPulseWidthCheck *check) const; + void reportShort(const MinPulseWidthCheck &check) const; + void reportVerbose(const MinPulseWidthCheck &check) const; - void reportCheck(const MinPeriodCheck *check, - bool verbose) const; - void reportChecks(const MinPeriodCheckSeq *checks, - bool verbose) const; + void reportCheck(const MinPeriodCheck &check, + bool verbose) const; + void reportChecks(const MinPeriodCheckSeq &checks, + bool verbose) const; void reportPeriodHeaderShort() const; - void reportShort(const MinPeriodCheck *check) const; - void reportVerbose(const MinPeriodCheck *check) const; + void reportShort(const MinPeriodCheck &check) const; + void reportVerbose(const MinPeriodCheck &check) const; - void reportCheck(const MaxSkewCheck *check, - bool verbose) const; - void reportChecks(const MaxSkewCheckSeq *checks, - bool verbose) const; + void reportChecks(const MaxSkewCheckSeq &checks, + bool verbose) const; void reportMaxSkewHeaderShort() const; - void reportShort(const MaxSkewCheck *check) const; - void reportVerbose(const MaxSkewCheck *check) const; + void reportShort(const MaxSkewCheck &check) const; + void reportVerbose(const MaxSkewCheck &check) const; void reportLimitShortHeader(const ReportField *field) const; void reportLimitShort(const ReportField *field, - Pin *pin, - float value, - float limit, - float slack) const; + const Pin *pin, + float value, + float limit, + float slack) const; void reportLimitVerbose(const ReportField *field, - Pin *pin, - const RiseFall *rf, - float value, - float limit, - float slack, - const Corner *corner, - const MinMax *min_max) const; + const Pin *pin, + const RiseFall *rf, + float value, + float limit, + float slack, + const Scene *scene, + const MinMax *min_max) const; ReportField *fieldSlew() const { return field_slew_; } ReportField *fieldFanout() const { return field_fanout_; } ReportField *fieldCapacitance() const { return field_capacitance_; } @@ -162,27 +163,27 @@ public: protected: void makeFields(); ReportField *makeField(const char *name, - const char *title, - int width, - bool left_justify, - Unit *unit, - bool enabled); + const char *title, + int width, + bool left_justify, + Unit *unit, + bool enabled); void reportEndpointHeader(const PathEnd *end, - const PathEnd *prev_end) const; + const PathEnd *prev_end) const; void reportShort(const PathEndUnconstrained *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndCheck *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndLatchCheck *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndPathDelay *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndOutputDelay *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndGatedClock *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportShort(const PathEndDataCheck *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportEndpoint(const PathEndOutputDelay *end) const; void reportEndpointOutputDelay(const PathEndClkConstrained *end) const; void reportEndpoint(const PathEndPathDelay *end) const; @@ -191,8 +192,8 @@ protected: std::string pathStartpoint(const PathEnd *end, const PathExpanded &expanded) const; void reportBorrowing(const PathEndLatchCheck *end, - Arrival &borrow, - Arrival &time_given_to_startpoint) const; + Arrival &borrow, + Arrival &time_given_to_startpoint) const; void reportEndpoint(const PathEndDataCheck *end) const; const char *clkNetworkDelayIdealProp(bool is_ideal) const; @@ -200,90 +201,92 @@ protected: std::string checkRoleString(const PathEnd *end) const; virtual void reportGroup(const PathEnd *end) const; void reportStartpoint(const PathEnd *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportUnclockedEndpoint(const PathEnd *end, - const char *default_reason) const; + const char *default_reason) const; void reportEndpoint(const PathEndCheck *end) const; void reportEndpoint(const PathEndLatchCheck *end) const; const char *latchDesc(const PathEndLatchCheck *end) const; void reportStartpoint(const char *start, - const std::string reason) const; + const std::string reason) const; void reportEndpoint(const char *end, - const std::string reason) const; + const std::string reason) const; void reportStartEndPoint(const char *pt, - const std::string reason, - const char *key) const; + const std::string reason, + const char *key) const; std::string tgtClkName(const PathEnd *end) const; const char *clkRegLatchDesc(const PathEnd *end) const; void reportSrcPath(const PathEnd *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportTgtClk(const PathEnd *end) const; void reportTgtClk(const PathEnd *end, - float prev_time) const; + float prev_time) const; void reportTgtClk(const PathEnd *end, - float prev_time, - bool is_prop) const; + float prev_time, + bool is_prop) const; void reportTgtClk(const PathEnd *end, float prev_time, float src_offset, bool is_prop) const; bool pathFromGenPropClk(const Path *clk_path, - const EarlyLate *early_late) const; + const EarlyLate *early_late) const; bool isGenPropClk(const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - const EarlyLate *early_late) const; + const RiseFall *clk_rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Sdc *sdc) const; void reportSrcClkAndPath(const Path *path, - const PathExpanded &expanded, - float time_offset, - Arrival clk_insertion, - Arrival clk_latency, - bool is_path_delay) const; + const PathExpanded &expanded, + float time_offset, + Arrival clk_insertion, + Arrival clk_latency, + bool is_path_delay) const; bool reportGenClkSrcPath(const Path *clk_path, const Clock *clk, - const RiseFall *clk_rf, - const MinMax *min_max, - const EarlyLate *early_late) const; + const RiseFall *clk_rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Sdc *sdc) const; void reportGenClkSrcAndPath(const Path *path, - const Clock *clk, - const RiseFall *clk_rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap, - float time_offset, - float path_time_offset, - bool clk_used_as_data) const; + const Clock *clk, + const RiseFall *clk_rf, + const EarlyLate *early_late, + float time_offset, + float path_time_offset, + bool clk_used_as_data, + const Mode *mode) const; bool reportGenClkSrcPath1(const Clock *clk, - const Pin *clk_pin, - const RiseFall *clk_rf, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap, - float gclk_time, - float time_offset, - bool clk_used_as_data) const; + const Pin *clk_pin, + const RiseFall *clk_rf, + const EarlyLate *early_late, + float gclk_time, + float time_offset, + bool clk_used_as_data, + const Mode *mode) const; void reportClkSrcLatency(Arrival insertion, - float clk_time, - const EarlyLate *early_late) const; + float clk_time, + const EarlyLate *early_late) const; void reportPathLine(const Path *path, - Delay incr, - Arrival time, - const char *line_case) const; + Delay incr, + Arrival time, + const char *line_case) const; void reportCommonClkPessimism(const PathEnd *end, - Arrival &clk_arrival) const ; + Arrival &clk_arrival) const ; void reportClkUncertainty(const PathEnd *end, - Arrival &clk_arrival) const ; + Arrival &clk_arrival) const ; void reportClkLine(const Clock *clk, - const char *clk_name, - const RiseFall *clk_rf, - Arrival clk_time, - const MinMax *min_max) const ; + const char *clk_name, + const RiseFall *clk_rf, + Arrival clk_time, + const MinMax *min_max) const ; void reportClkLine(const Clock *clk, - const char *clk_name, - const RiseFall *clk_rf, - Arrival prev_time, - Arrival clk_time, - const MinMax *min_max) const ; + const char *clk_name, + const RiseFall *clk_rf, + Arrival prev_time, + Arrival clk_time, + const MinMax *min_max) const ; void reportRequired(const PathEnd *end, - std::string margin_msg) const ; + std::string margin_msg) const ; void reportSlack(const PathEnd *end) const ; void reportSlack(Slack slack) const ; void reportSpaceSlack(const PathEnd *end, @@ -291,125 +294,125 @@ protected: void reportSpaceSlack(Slack slack, std::string &line) const ; void reportSrcPathArrival(const PathEnd *end, - const PathExpanded &expanded) const ; + const PathExpanded &expanded) const ; void reportPath(const PathEnd *end, - const PathExpanded &expanded) const; + const PathExpanded &expanded) const; void reportPathFull(const Path *path) const; void reportPathHeader() const; void reportPath1(const Path *path, - const PathExpanded &expanded, - bool clk_used_as_data, - float time_offset) const; + const PathExpanded &expanded, + bool clk_used_as_data, + float time_offset) const; void reportPath2(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool clk_used_as_data, - float time_offset) const; + const PathExpanded &expanded, + bool skip_first_path, + bool clk_used_as_data, + float time_offset) const; void reportPath3(const Path *path, - const PathExpanded &expanded, - bool report_clk_path, - float time_offset) const; + const PathExpanded &expanded, + bool report_clk_path, + float time_offset) const; void reportPath4(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool propagated_clk, - bool report_clk_path, - float time_offset) const; + const PathExpanded &expanded, + bool skip_first_path, + bool propagated_clk, + bool report_clk_path, + float time_offset) const; void reportPath5(const Path *path, - const PathExpanded &expanded, - bool skip_first_path, - bool propagated_clk, - bool report_clk_path, - float time_offset) const; + const PathExpanded &expanded, + bool skip_first_path, + bool propagated_clk, + bool report_clk_path, + float time_offset) const; void reportPath6(const Path *path, - const PathExpanded &expanded, - size_t path_first_index, - bool propagated_clk, - bool report_clk_path, - Arrival prev_time, - float time_offset) const; + const PathExpanded &expanded, + size_t path_first_index, + bool propagated_clk, + bool report_clk_path, + Arrival prev_time, + float time_offset) const; void reportHierPinsThru(const Path *path) const; void reportInputExternalDelay(const Path *path, - float time_offset) const; + float time_offset) const; void reportLine(const char *what, - Delay total, - const EarlyLate *early_late) const; + Delay total, + const EarlyLate *early_late) const; void reportLineNegative(const char *what, - Delay total, - const EarlyLate *early_late) const; + Delay total, + const EarlyLate *early_late) const; void reportLine(const char *what, - Delay total, - const EarlyLate *early_late, - const RiseFall *rf) const; + Delay total, + const EarlyLate *early_late, + const RiseFall *rf) const; void reportLine(const char *what, - Delay incr, - Delay total, - const EarlyLate *early_late) const; + Delay incr, + Delay total, + const EarlyLate *early_late) const; void reportLine(const char *what, - Delay incr, - Delay total, - const EarlyLate *early_late, - const RiseFall *rf) const; + Delay incr, + Delay total, + const EarlyLate *early_late, + const RiseFall *rf) const; void reportLine(const char *what, - Slew slew, - Delay incr, - Delay total, - const EarlyLate *early_late) const; + Slew slew, + Delay incr, + Delay total, + const EarlyLate *early_late) const; void reportLine(const char *what, - float cap, - Slew slew, - float fanout, - Delay incr, - Delay total, - bool total_with_minus, - const EarlyLate *early_late, - const RiseFall *rf, - std::string src_attr, - const char *line_case) const; + float cap, + Slew slew, + float fanout, + Delay incr, + 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 EarlyLate *early_late) const; + Delay incr, + const EarlyLate *early_late) const; void reportLineTotalMinus(const char *what, - Delay decr, - const EarlyLate *early_late) const; + Delay decr, + const EarlyLate *early_late) const; void reportLineTotal1(const char *what, - Delay incr, - bool incr_with_minus, - const EarlyLate *early_late) const; + Delay incr, + bool incr_with_minus, + const EarlyLate *early_late) const; void reportDashLineTotal() const; void reportDescription(const char *what, std::string &result) const; void reportDescription(const char *what, - bool first_field, - bool last_field, + bool first_field, + bool last_field, std::string &result) const; void reportFieldTime(float value, - ReportField *field, - std::string &result) const; + ReportField *field, + std::string &result) const; void reportSpaceFieldTime(float value, - std::string &result) const; + std::string &result) const; void reportSpaceFieldDelay(Delay value, - const EarlyLate *early_late, - std::string &result) const; + const EarlyLate *early_late, + std::string &result) const; void reportFieldDelayMinus(Delay value, - const EarlyLate *early_late, - const ReportField *field, - std::string &result) const; + const EarlyLate *early_late, + const ReportField *field, + std::string &result) const; void reportTotalDelay(Delay value, - const EarlyLate *early_late, - std::string &result) const; + const EarlyLate *early_late, + std::string &result) const; void reportFieldDelay(Delay value, - const EarlyLate *early_late, - const ReportField *field, - std::string &result) const; + const EarlyLate *early_late, + const ReportField *field, + std::string &result) const; void reportField(float value, - const ReportField *field, - std::string &result) const; + const ReportField *field, + std::string &result) const; void reportField(const char *value, - const ReportField *field, - std::string &result) const; + const ReportField *field, + std::string &result) const; void reportFieldBlank(const ReportField *field, - std::string &result) const; + std::string &result) const; void reportDashLine() const; void reportDashLine(int line_width) const; void reportBlankLine() const; @@ -420,51 +423,51 @@ protected: std::string clkName(const Clock *clk, bool inverted) const; bool hasExtInputDriver(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) const; + const RiseFall *rf, + const MinMax *min_max, + const Sdc *sdc) const; float drvrFanout(Vertex *drvr, - const Corner *corner, - const MinMax *min_max) const; - const char *mpwCheckHiLow(const MinPulseWidthCheck *check) const; + const Scene *scene, + const MinMax *min_max) const; + const char *mpwCheckHiLow(const MinPulseWidthCheck &check) const; void reportSkewClkPath(const char *arrival_msg, - const Path *clk_path) const; + const Path *clk_path) const; const char *edgeRegLatchDesc(const Edge *edge, - const TimingArc *arc) const; + const TimingArc *arc) const; const char *checkRegLatchDesc(const TimingRole *role, - const RiseFall *clk_rf) const; + const RiseFall *clk_rf) const; const char *regDesc(const RiseFall *clk_rf) const; const char *latchDesc(const RiseFall *clk_rf) const; void pathClkPath(const Path *path, - const Path &clk_path) const; + const Path &clk_path) const; bool isPropagated(const Path *clk_path) const; bool isPropagated(const Path *clk_path, - const Clock *clk) const; + const Clock *clk) const; bool pathFromClkPin(const PathExpanded &expanded) const; bool pathFromClkPin(const Path *path, - const Pin *start_pin) const; + const Pin *start_pin) const; void latchPaths(const Path *path, // Return values. Path &d_path, - Path &q_path, - Edge *&d_q_edge) const; + Path &q_path, + Edge *&d_q_edge) const; bool nextArcAnnotated(const Path *next_path, - size_t next_index, - const PathExpanded &expanded, - DcalcAPIndex ap_index) const; + size_t next_index, + const PathExpanded &expanded, + DcalcAPIndex ap_index) const; float tgtClkInsertionOffet(const Path *clk_path, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const; + const EarlyLate *early_late) const; // InputDelay used to seed path root. InputDelay *pathInputDelay(const Path *path) const; void pathInputDelayRefPath(const Path *path, - const InputDelay *input_delay, - // Return value. - Path &ref_path) const; + const InputDelay *input_delay, + // Return value. + Path &ref_path) const; const char *asRisingFalling(const RiseFall *rf) const; const char *asRiseFall(const RiseFall *rf) const; Delay delayIncr(Delay time, - Delay prev, - const MinMax *min_max) const; + Delay prev, + const MinMax *min_max) const; // Path options. ReportPathFormat format_; @@ -499,15 +502,15 @@ class ReportField { public: ReportField(const char *name, - const char *title, - int width, - bool left_justify, - Unit *unit, - bool enabled); + const char *title, + int width, + bool left_justify, + Unit *unit, + bool enabled); ~ReportField(); void setProperties(const char *title, - int width, - bool left_justify); + int width, + bool left_justify); const char *name() const { return name_; } const char *title() const { return title_; } int width() const { return width_; } diff --git a/search/Scene.cc b/search/Scene.cc new file mode 100644 index 000000000..154eab343 --- /dev/null +++ b/search/Scene.cc @@ -0,0 +1,187 @@ +// 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 "Scene.hh" + +#include "ContainerHelpers.hh" +#include "Parasitics.hh" +#include "Sdc.hh" +#include "Mode.hh" + +namespace sta { + +Scene::Scene(const std::string &name, + size_t index, + Mode *mode, + Parasitics *parasitics_min, + Parasitics *parasitics_max) : + name_(name), + index_(index), + mode_(mode), + parasitics_{parasitics_min, parasitics_max} +{ +} + +Scene::Scene(const std::string &name, + size_t index, + Mode *mode, + Parasitics *parasitics) : + name_(name), + index_(index), + mode_(mode) +{ + for (size_t mm_index : MinMax::rangeIndex()) + parasitics_[mm_index] = parasitics; +} + +size_t +Scene::pathIndex(const MinMax *min_max) const +{ + return index_ * MinMax::index_count + min_max->index(); +} + +void +Scene::setMode(Mode *mode) +{ + mode_ = mode; +} + +Sdc * +Scene::sdc() +{ + return mode_->sdc(); +} + +Sdc * +Scene::sdc() const +{ + return mode_->sdc(); +} + +Parasitics * +Scene::parasitics(const MinMax *min_max) const +{ + return parasitics_[min_max->index()]; +} + +void +Scene::setParasitics(Parasitics *parasitics, + const MinMaxAll *min_max) +{ + for (size_t mm : min_max->rangeIndex()) + parasitics_[mm] = parasitics; +} + +DcalcAPIndex +Scene::dcalcAnalysisPtIndex(const MinMax *min_max) const +{ + return index_ * MinMax::index_count + min_max->index(); +} + +const MinMax * +Scene::checkClkSlewMinMax(const MinMax *min_max) const +{ + switch (mode_->sdc()->analysisType()) { + case AnalysisType::single: + return MinMax::min(); + case AnalysisType::bc_wc: + return min_max; + case AnalysisType::ocv: + return min_max->opposite(); + default: + // suppress gcc warning + return min_max; + } +} + +DcalcAPIndex +Scene::checkClkSlewIndex(const MinMax *min_max) const +{ + return dcalcAnalysisPtIndex(checkClkSlewMinMax(min_max)); +} + +void +Scene::addLiberty(LibertyLibrary *lib, + const MinMax *min_max) +{ + liberty_[min_max->index()].push_back(lib); +} + +const LibertySeq & +Scene::libertyLibraries(const MinMax *min_max) const +{ + return liberty_[min_max->index()]; +} + +int +Scene::libertyIndex(const MinMax *min_max) const +{ + return index_ * MinMax::index_count + min_max->index(); +} + +//////////////////////////////////////////////////////////////// + +SceneSet +Scene::sceneSet(const SceneSeq &scenes) +{ + SceneSet scenes_set; + for (Scene *scene : scenes) + scenes_set.insert(scene); + return scenes_set; +} + +ModeSeq +Scene::modes(const SceneSeq &scenes) +{ + ModeSet mode_set; + for (const Scene *scene : scenes) + mode_set.insert(scene->mode()); + + ModeSeq modes; + for (Mode *mode : mode_set) + modes.push_back(mode); + return modes; +} + +ModeSet +Scene::modeSet(const SceneSeq &scenes) +{ + ModeSet modes; + for (const Scene *scene : scenes) + modes.insert(scene->mode()); + return modes; +} + +ModeSeq +Scene::modesSorted(const SceneSeq &scenes) +{ + ModeSeq modes = Scene::modes(scenes); + sort(modes, [] (const Mode *mode1, + const Mode *mode2) { + return mode1->name() < mode2->name(); + }); + return modes; +} + +} // namespace diff --git a/search/Search.cc b/search/Search.cc index 8a5f69a30..be52ad101 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -27,8 +27,9 @@ #include #include // abs +#include "ContainerHelpers.hh" #include "Mutex.hh" -#include "Report.hh" +#include "Report.hh" #include "Debug.hh" #include "Stats.hh" #include "Fuzzy.hh" @@ -48,11 +49,10 @@ #include "ExceptionPath.hh" #include "DataCheck.hh" #include "Sdc.hh" -#include "DcalcAnalysisPt.hh" +#include "Mode.hh" #include "SearchPred.hh" #include "Levelize.hh" #include "Bfs.hh" -#include "Corner.hh" #include "Sim.hh" #include "Path.hh" #include "ClkInfo.hh" @@ -60,7 +60,6 @@ #include "TagGroup.hh" #include "PathEnd.hh" #include "PathGroup.hh" -#include "PathAnalysisPt.hh" #include "VisitPathEnds.hh" #include "GatedClk.hh" #include "WorstSlack.hh" @@ -90,171 +89,215 @@ EvalPred::setSearchThruLatches(bool thru_latches) } bool -EvalPred::searchThru(Edge *edge) +EvalPred::searchThru(Edge *edge, + const Mode *mode) const { const TimingRole *role = edge->role(); - return SearchPred0::searchThru(edge) + return SearchPred0::searchThru(edge, mode) && (sta_->variables()->dynamicLoopBreaking() - || !edge->isDisabledLoop()) + || !edge->isDisabledLoop()) && !role->isTimingCheck() && (search_thru_latches_ - || role != TimingRole::latchDtoQ() - || sta_->latches()->latchDtoQState(edge) == LatchEnableState::open); + || role->isLatchDtoQ() + || sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open); } bool -EvalPred::searchTo(const Vertex *to_vertex) +EvalPred::searchTo(const Vertex *to_vertex, + const Mode *mode) const { - const Sdc *sdc = sta_->sdc(); - const Pin *pin = to_vertex->pin(); - return SearchPred0::searchTo(to_vertex) - && !(sdc->isLeafPinClock(pin) - && !sdc->isPathDelayInternalTo(pin)); + 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); } //////////////////////////////////////////////////////////////// -DynLoopSrchPred::DynLoopSrchPred(TagGroupBldr *tag_bldr) : - tag_bldr_(tag_bldr) +// EvalPred unless +// latch D->Q edge +class SearchThru : public EvalPred { -} +public: + SearchThru(const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; +}; -bool -DynLoopSrchPred::loopEnabled(Edge *edge, - bool dynamic_loop_breaking_enabled, - const Graph *graph, - Search *search) +SearchThru::SearchThru(const StaState *sta) : + EvalPred(sta) { - return !edge->isDisabledLoop() - || (dynamic_loop_breaking_enabled - && hasPendingLoopPaths(edge, graph, search)); } bool -DynLoopSrchPred::hasPendingLoopPaths(Edge *edge, - const Graph *graph, - Search *search) +SearchThru::searchThru(Edge *edge, + const Mode *mode) const { - if (tag_bldr_ - && tag_bldr_->hasLoopTag()) { - Corners *corners = search->corners(); - Vertex *from_vertex = edge->from(graph); - TagGroup *prev_tag_group = search->tagGroup(from_vertex); - for (auto const [from_tag, path_index] : tag_bldr_->pathIndexMap()) { - if (from_tag->isLoop()) { - // Loop false path exceptions apply to rise/fall edges so to_rf - // does not matter. - PathAPIndex path_ap_index = from_tag->pathAPIndex(); - PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index); - Tag *to_tag = search->thruTag(from_tag, edge, RiseFall::rise(), - path_ap->pathMinMax(), path_ap, nullptr); - if (to_tag - && (prev_tag_group == nullptr - || !prev_tag_group->hasTag(from_tag))) - return true; - } - } - } - return false; + return EvalPred::searchThru(edge, mode) + && !edge->role()->isLatchDtoQ(); } -// EvalPred unless +//////////////////////////////////////////////////////////////// + +// SearchAdj is mode independent. Search unless +// disabled to break combinational loop // latch D->Q edge -class SearchThru : public EvalPred, public DynLoopSrchPred +// timing check edge +// dynamic loop breaking pending tags +class SearchAdj : public SearchPred { public: - SearchThru(TagGroupBldr *tag_bldr, - const StaState *sta); - virtual bool searchThru(Edge *edge); + SearchAdj(TagGroupBldr *tag_bldr, + const StaState *sta); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; + +protected: + bool loopEnabled(Edge *edge) const; + bool hasPendingLoopPaths(Edge *edge) const; + + TagGroupBldr *tag_bldr_; + const StaState *sta_; + }; -SearchThru::SearchThru(TagGroupBldr *tag_bldr, - const StaState *sta) : - EvalPred(sta), - DynLoopSrchPred(tag_bldr) +SearchAdj::SearchAdj(TagGroupBldr *tag_bldr, + const StaState *sta) : + SearchPred(sta), + tag_bldr_(tag_bldr), + sta_(sta) { } bool -SearchThru::searchThru(Edge *edge) +SearchAdj::searchFrom(const Vertex * /* from_vertex */, + const Mode *) const { - const Graph *graph = sta_->graph(); - Search *search = sta_->search(); - return EvalPred::searchThru(edge) - // Only search thru latch D->Q if it is always open. - // Enqueue thru latches is handled explicitly by search. - && (edge->role() != TimingRole::latchDtoQ() - || sta_->latches()->latchDtoQState(edge) == LatchEnableState::open) - && loopEnabled(edge, sta_->variables()->dynamicLoopBreaking(), - graph, search); + return true; } -ClkArrivalSearchPred::ClkArrivalSearchPred(const StaState *sta) : - EvalPred(sta) +bool +SearchAdj::searchThru(Edge *edge, + const Mode *) const { + 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))); } bool -ClkArrivalSearchPred::searchThru(Edge *edge) +SearchAdj::loopEnabled(Edge *edge) const { - const TimingRole *role = edge->role(); - return (role->isWire() - || role == TimingRole::combinational()) - && EvalPred::searchThru(edge); + return !edge->isDisabledLoop() + || (sta_->variables()->dynamicLoopBreaking() + && hasPendingLoopPaths(edge)); } -//////////////////////////////////////////////////////////////// +bool +SearchAdj::hasPendingLoopPaths(Edge *edge) const +{ + if (tag_bldr_ + && tag_bldr_->hasLoopTag()) { + const Graph *graph = sta_->graph(); + Search *search = sta_->search(); + Vertex *from_vertex = edge->from(graph); + TagGroup *prev_tag_group = search->tagGroup(from_vertex); + for (auto const [from_tag, path_index] : tag_bldr_->pathIndexMap()) { + if (from_tag->isLoop()) { + // Loop false path exceptions apply to rise/fall edges so to_rf + // 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))) + return true; + } + } + } + return false; +} -Search::Search(StaState *sta) : - StaState(sta) +bool +SearchAdj::searchTo(const Vertex * /* to_vertex */, + const Mode *) const { - init(sta); + return true; } -void -Search::init(StaState *sta) +//////////////////////////////////////////////////////////////// + +Search::Search(StaState *sta) : + StaState(sta), + unconstrained_paths_(false), + crpr_path_pruning_enabled_(true), + crpr_approx_missing_requireds_(true), + + search_thru_(new SearchThru(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_visitor_(new ArrivalVisitor(this)), + + requireds_exist_(false), + requireds_seeded_(false), + invalid_requireds_(makeVertexSet(this)), + required_iter_(new BfsBkwdIterator(BfsIndex::required, search_adj_, this)), + + tns_exists_(false), + invalid_tns_(makeVertexSet(this)), + worst_slacks_(nullptr), + 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))), + tag_next_(0), + + tag_group_capacity_(tag_capacity_), + tag_groups_(new TagGroup*[tag_group_capacity_]), + tag_group_set_(new TagGroupSet(tag_group_capacity_)), + tag_group_next_(0), + + pending_latch_outputs_(makeVertexSet(this)), + pending_clk_endpoints_(makeVertexSet(this)), + endpoints_(makeVertexSet(this)), + endpoints_initialized_(false), + invalid_endpoints_(makeVertexSet(this)), + + have_filter_(false), + filter_from_(nullptr), + filter_thrus_(nullptr), + filter_to_(nullptr), + filtered_arrivals_(makeVertexSet(this)), + + found_downstream_clk_pins_(false), + postpone_latch_outputs_(false), + + visit_path_ends_(new VisitPathEnds(this)), + gated_clk_(new GatedClk(this)), + check_crpr_(new CheckCrpr(this)) { initVars(); - - search_adj_ = new SearchThru(nullptr, sta); - eval_pred_ = new EvalPred(sta); - check_crpr_ = new CheckCrpr(sta); - genclks_ = new Genclks(sta); - arrival_visitor_ = new ArrivalVisitor(sta); - clk_arrivals_valid_ = false; - arrivals_exist_ = false; - arrivals_at_endpoints_exist_ = false; - arrivals_seeded_ = false; - requireds_exist_ = false; - requireds_seeded_ = false; - invalid_arrivals_ = new VertexSet(graph_); - invalid_requireds_ = new VertexSet(graph_); - invalid_tns_ = new VertexSet(graph_); - tns_exists_ = false; - worst_slacks_ = nullptr; - arrival_iter_ = new BfsFwdIterator(BfsIndex::arrival, nullptr, sta); - required_iter_ = new BfsBkwdIterator(BfsIndex::required, search_adj_, sta); - tag_capacity_ = 128; - tag_set_ = new TagSet(tag_capacity_, TagHash(sta), TagEqual(sta)); - clk_info_set_ = new ClkInfoSet(ClkInfoLess(sta)); - tag_next_ = 0; - tags_ = new Tag*[tag_capacity_]; - tag_group_capacity_ = tag_capacity_; - tag_groups_ = new TagGroup*[tag_group_capacity_]; - tag_group_next_ = 0; - tag_group_set_ = new TagGroupSet(tag_group_capacity_); - pending_latch_outputs_ = new VertexSet(graph_); - visit_path_ends_ = new VisitPathEnds(this); - gated_clk_ = new GatedClk(this); - path_groups_ = nullptr; - endpoints_ = nullptr; - invalid_endpoints_ = nullptr; - filter_ = nullptr; - filter_from_ = nullptr; - filter_to_ = nullptr; - filtered_arrivals_ = new VertexSet(graph_); - found_downstream_clk_pins_ = false; - postpone_latch_outputs_ = false; } // Init "options". @@ -276,23 +319,16 @@ Search::~Search() delete [] tags_; delete [] tag_groups_; delete tag_group_set_; + delete search_thru_; delete search_adj_; delete eval_pred_; delete arrival_visitor_; delete arrival_iter_; delete required_iter_; - delete endpoints_; - delete invalid_arrivals_; - delete invalid_requireds_; - delete invalid_tns_; - delete invalid_endpoints_; - delete pending_latch_outputs_; delete visit_path_ends_; delete gated_clk_; delete worst_slacks_; delete check_crpr_; - delete genclks_; - delete filtered_arrivals_; deleteFilter(); } @@ -301,28 +337,33 @@ Search::clear() { initVars(); - clk_arrivals_valid_ = false; - arrivals_at_endpoints_exist_ = false; arrivals_seeded_ = false; requireds_exist_ = false; requireds_seeded_ = false; tns_exists_ = false; clearWorstSlack(); - invalid_arrivals_->clear(); + invalid_arrivals_.clear(); arrival_iter_->clear(); - invalid_requireds_->clear(); - invalid_tns_->clear(); + invalid_requireds_.clear(); + invalid_tns_.clear(); required_iter_->clear(); endpointsInvalid(); deletePathGroups(); deletePaths(); deleteTags(); clearPendingLatchOutputs(); + pending_clk_endpoints_.clear(); deleteFilter(); - genclks_->clear(); found_downstream_clk_pins_ = false; } +void +Search::deletePathGroups() +{ + for (Mode *mode : modes_) + mode->deletePathGroups(); +} + bool Search::crprPathPruningEnabled() const { @@ -359,26 +400,29 @@ Search::deleteTags() tag_group_free_indices_.clear(); tag_next_ = 0; - tag_set_->deleteContentsClear(); - tag_free_indices_.clear(); + deleteContents(tag_set_); - clk_info_set_->deleteContentsClear(); + deleteContents(clk_info_set_); deleteTagsPrev(); } void Search::deleteFilter() { - if (filter_) { - sdc_->deleteException(filter_); - filter_ = nullptr; - filter_from_ = nullptr; + if (have_filter_) { + for (const Mode *mode : modes_) + mode->sdc()->deleteFilter(); + have_filter_ = false; } - else { - // Filter owns filter_from_ if it exists. - delete filter_from_; - filter_from_ = nullptr; + delete filter_from_; + filter_from_ = nullptr; + + if (filter_thrus_) { + deleteContents(*filter_thrus_); + delete filter_thrus_; + filter_thrus_ = nullptr; } + delete filter_to_; filter_to_ = nullptr; } @@ -391,10 +435,12 @@ Search::copyState(const StaState *sta) arrival_iter_->copyState(sta); required_iter_->copyState(sta); arrival_visitor_->copyState(sta); + eval_pred_->copyState(sta); + search_thru_->copyState(sta); + search_adj_->copyState(sta); visit_path_ends_->copyState(sta); gated_clk_->copyState(sta); check_crpr_->copyState(sta); - genclks_->copyState(sta); } //////////////////////////////////////////////////////////////// @@ -410,11 +456,9 @@ Search::deletePaths() deletePaths(vertex); } - for (Path *path : enum_paths_) - delete path; - enum_paths_.clear(); + deleteContents(enum_paths_); - filtered_arrivals_->clear(); + filtered_arrivals_.clear(); arrivals_exist_ = false; } } @@ -439,10 +483,10 @@ void Search::deletePaths(Vertex *vertex) { debugPrint(debug_, "search", 4, "delete paths %s", - vertex->name(network_)); + vertex->to_string(this).c_str()); TagGroup *tag_group = tagGroup(vertex); if (tag_group) { - graph_->deletePaths(vertex); + vertex->deletePaths(); tag_group->decrRefCount(); } } @@ -454,42 +498,54 @@ Search::deletePaths(Vertex *vertex) // PathEnds are owned by Search PathGroups and deleted on next call. PathEndSeq Search::findPathEnds(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool unconstrained, - const Corner *corner, - const MinMaxAll *min_max, - size_t group_path_count, - size_t endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - bool sort_by_slack, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + const SceneSeq &scenes, + const MinMaxAll *min_max, + size_t group_path_count, + size_t endpoint_path_count, + bool unique_pins, + bool unique_edges, + float slack_min, + float slack_max, + bool sort_by_slack, + StdStringSeq &group_names, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold) { findFilteredArrivals(from, thrus, to, unconstrained, true); if (!variables_->recoveryRemovalChecksEnabled()) recovery = removal = false; if (!variables_->gatedClkChecksEnabled()) clk_gating_setup = clk_gating_hold = false; - 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); ensureDownstreamClkPins(); - PathEndSeq path_ends = path_groups_->makePathEnds(to, unconstrained_paths_, - corner, min_max, - sort_by_slack); - sdc_->reportClkToClkMaxCycleWarnings(); + 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_); + SceneSeq mode_scenes; + for (Scene *scene : scenes) { + if (scene->mode() == mode) + mode_scenes.push_back(scene); + } + path_groups->makePathEnds(to, mode_scenes, min_max, sort_by_slack, + unconstrained_paths_, path_ends); + } + for (const Mode *mode : modes) + mode->sdc()->reportClkToClkMaxCycleWarnings(); return path_ends; } @@ -503,45 +559,25 @@ Search::findFilteredArrivals(ExceptionFrom *from, unconstrained_paths_ = unconstrained; checkFromThrusTo(from, thrus, to); filter_from_ = from; + filter_thrus_ = thrus; filter_to_ = to; if ((from && (from->pins() - || from->instances())) + || from->instances())) || thrus) { - filter_ = sdc_->makeFilterPath(from, thrus, nullptr); + for (const Mode *mode : modes_) { + Sdc *sdc = mode->sdc(); + sdc->makeFilter(from ? from->clone(network_) : nullptr, + thrus ? exceptionThrusClone(thrus, network_) : nullptr); + } + have_filter_ = true; findFilteredArrivals(thru_latches); } else // These cases do not require filtered arrivals. // -from clocks // -to - findAllArrivals(thru_latches); -} - -void -Search::makePathGroups(int group_path_count, - int endpoint_path_count, - bool unique_pins, - bool unique_edges, - float slack_min, - float slack_max, - PathGroupNameSet *group_names, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold) -{ - path_groups_ = new PathGroups(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_, - this); + findAllArrivals(thru_latches, false); } // From/thrus/to are used to make a filter exception. If the last @@ -551,16 +587,13 @@ Search::makePathGroups(int group_path_count, void Search::deleteFilteredArrivals() { - if (filter_) { - ExceptionFrom *from = filter_->from(); - ExceptionThruSeq *thrus = filter_->thrus(); - if ((from - && (from->pins() - || from->instances())) - || thrus) { - for (Vertex *vertex : *filtered_arrivals_) { - if (isClock(vertex)) - clk_arrivals_valid_ = false; + if (have_filter_) { + ExceptionThruSeq *thrus = filter_thrus_; + if ((filter_from_ + && (filter_from_->pins() + || filter_from_->instances())) + || thrus) { + for (Vertex *vertex : filtered_arrivals_) { deletePathsIncr(vertex); arrivalInvalid(vertex); requiredInvalid(vertex); @@ -573,21 +606,22 @@ Search::deleteFilteredArrivals() TagGroup *tag_group = tagGroup(vertex); if (tag_group && tag_group->hasFilterTag()) - filtered_arrivals_->erase(vertex); + filtered_arrivals_.erase(vertex); } - if (!filtered_arrivals_->empty()) { + if (!filtered_arrivals_.empty()) { report_->reportLine("Filtered verticies mismatch"); - for (Vertex *vertex : *filtered_arrivals_) + for (Vertex *vertex : filtered_arrivals_) report_->reportLine(" %s", vertex->to_string(this).c_str()); } } - filtered_arrivals_->clear(); + filtered_arrivals_.clear(); deleteFilterTagGroups(); deleteFilterTags(); deleteFilterClkInfos(); } - deleteFilter(); } + // Delete filter_from/thru/to even if there is no filter_. + deleteFilter(); } void @@ -596,7 +630,7 @@ Search::deleteFilterTagGroups() for (TagGroupIndex i = 0; i < tag_group_next_; i++) { TagGroup *group = tag_groups_[i]; if (group - && group->hasFilterTag()) + && group->hasFilterTag()) deleteTagGroup(group); } } @@ -616,12 +650,11 @@ Search::deleteFilterTags() for (TagIndex i = 0; i < tag_next_; i++) { Tag *tag = tags_[i]; if (tag - && (tag->isFilter() - || tag->clkInfo()->crprPathRefsFilter())) { + && (tag->isFilter() + || tag->clkInfo()->crprPathRefsFilter())) { tags_[i] = nullptr; tag_set_->erase(tag); delete tag; - tag_free_indices_.push_back(i); } } } @@ -643,15 +676,16 @@ Search::deleteFilterClkInfos() void Search::findFilteredArrivals(bool thru_latches) { - filtered_arrivals_->clear(); + filtered_arrivals_.clear(); findArrivalsSeed(); seedFilterStarts(); Level max_level = levelize_->maxLevel(); // Search always_to_endpoint to search from exisiting arrivals at // fanin startpoints to reach -thru/-to endpoints. - arrival_visitor_->init(true); + arrival_visitor_->init(true, false, eval_pred_); // Iterate until data arrivals at all latches stop changing. postpone_latch_outputs_ = true; + enqueuePendingClkFanouts(); for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()) ; pass++) { if (thru_latches) enqueuePendingLatchOutputs(); @@ -691,7 +725,7 @@ VertexSeq Search::filteredEndpoints() { VertexSeq ends; - for (Vertex *vertex : *filtered_arrivals_) { + for (Vertex *vertex : filtered_arrivals_) { if (isEndpoint(vertex)) ends.push_back(vertex); } @@ -702,18 +736,18 @@ class SeedFaninsThruHierPin : public HierPinThruVisitor { public: SeedFaninsThruHierPin(Graph *graph, - Search *search); + Search *search); protected: virtual void visit(const Pin *drvr, - const Pin *load); + const Pin *load); Graph *graph_; Search *search_; }; SeedFaninsThruHierPin::SeedFaninsThruHierPin(Graph *graph, - Search *search) : + Search *search) : HierPinThruVisitor(), graph_(graph), search_(search) @@ -722,19 +756,23 @@ SeedFaninsThruHierPin::SeedFaninsThruHierPin(Graph *graph, void SeedFaninsThruHierPin::visit(const Pin *drvr, - const Pin *) + const Pin *) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(drvr, vertex, bidirect_drvr_vertex); - search_->seedArrival(vertex); + search_->arrivalIterator()->enqueue(vertex); if (bidirect_drvr_vertex) - search_->seedArrival(bidirect_drvr_vertex); + search_->arrivalIterator()->enqueue(bidirect_drvr_vertex); } void Search::seedFilterStarts() { - ExceptionPt *first_pt = filter_->firstPt(); + ExceptionPt *first_pt = nullptr; + if (filter_from_) + first_pt = filter_from_; + else if (filter_thrus_) + first_pt = (*filter_thrus_)[0]; if (first_pt) { PinSet first_pins = first_pt->allPins(network_); for (const Pin *pin : first_pins) { @@ -746,9 +784,9 @@ Search::seedFilterStarts() Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) - seedArrival(vertex); + arrival_iter_->enqueue(vertex); if (bidirect_drvr_vertex) - seedArrival(bidirect_drvr_vertex); + arrival_iter_->enqueue(bidirect_drvr_vertex); } } } @@ -762,18 +800,17 @@ Search::deleteVertexBefore(Vertex *vertex) if (arrivals_exist_) { deletePathsIncr(vertex); arrival_iter_->deleteVertexBefore(vertex); - invalid_arrivals_->erase(vertex); - filtered_arrivals_->erase(vertex); + invalid_arrivals_.erase(vertex); + filtered_arrivals_.erase(vertex); } if (requireds_exist_) { required_iter_->deleteVertexBefore(vertex); - invalid_requireds_->erase(vertex); - invalid_tns_->erase(vertex); + invalid_requireds_.erase(vertex); + invalid_tns_.erase(vertex); } - if (endpoints_) - endpoints_->erase(vertex); - if (invalid_endpoints_) - invalid_endpoints_->erase(vertex); + if (endpoints_initialized_) + endpoints_.erase(vertex); + invalid_endpoints_.erase(vertex); } void @@ -794,7 +831,7 @@ bool Search::arrivalsValid() { return arrivals_exist_ - && invalid_arrivals_->empty(); + && invalid_arrivals_.empty(); } void @@ -808,21 +845,20 @@ Search::arrivalsInvalid() deletePathGroups(); deletePaths(); deleteTags(); - genclks_->clear(); + for (const Mode *mode : modes_) + mode->genclks()->clear(); deleteFilter(); - arrivals_at_endpoints_exist_ = false; arrivals_seeded_ = false; requireds_exist_ = false; requireds_seeded_ = false; - clk_arrivals_valid_ = false; arrival_iter_->clear(); required_iter_->clear(); // No need to keep track of incremental updates any more. - invalid_arrivals_->clear(); - invalid_requireds_->clear(); + invalid_arrivals_.clear(); + invalid_requireds_.clear(); tns_exists_ = false; clearWorstSlack(); - invalid_tns_->clear(); + invalid_tns_.clear(); } } @@ -832,10 +868,10 @@ Search::requiredsInvalid() debugPrint(debug_, "search", 1, "requireds invalid"); requireds_exist_ = false; requireds_seeded_ = false; - invalid_requireds_->clear(); + invalid_requireds_.clear(); tns_exists_ = false; clearWorstSlack(); - invalid_tns_->clear(); + invalid_tns_.clear(); } void @@ -847,7 +883,7 @@ Search::arrivalInvalid(Vertex *vertex) if (!arrival_iter_->inQueue(vertex)) { // Lock for StaDelayCalcObserver called by delay calc threads. LockGuard lock(invalid_arrivals_lock_); - invalid_arrivals_->insert(vertex); + invalid_arrivals_.insert(vertex); } tnsInvalid(vertex); } @@ -926,7 +962,7 @@ Search::requiredInvalid(Vertex *vertex) if (!required_iter_->inQueue(vertex)) { // Lock for StaDelayCalcObserver called by delay calc threads. LockGuard lock(invalid_arrivals_lock_); - invalid_requireds_->insert(vertex); + invalid_requireds_.insert(vertex); } tnsInvalid(vertex); } @@ -937,20 +973,7 @@ Search::requiredInvalid(Vertex *vertex) void Search::findClkArrivals() { - if (!clk_arrivals_valid_) { - genclks_->ensureInsertionDelays(); - Stats stats(debug_, report_); - debugPrint(debug_, "search", 1, "find clk arrivals"); - arrival_iter_->clear(); - seedClkVertexArrivals(); - ClkArrivalSearchPred search_clk(this); - arrival_visitor_->init(false, &search_clk); - arrival_iter_->visitParallel(levelize_->maxLevel(), arrival_visitor_); - deleteTagsPrev(); - arrivals_exist_ = true; - stats.report("Find clk arrivals"); - } - clk_arrivals_valid_ = true; + findAllArrivals(false, true); } void @@ -961,112 +984,46 @@ Search::seedClkVertexArrivals() for (const Pin *pin : clk_pins) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - seedClkVertexArrivals(pin, vertex); + arrival_iter_->enqueue(vertex); if (bidirect_drvr_vertex) - seedClkVertexArrivals(pin, bidirect_drvr_vertex); + arrival_iter_->enqueue(bidirect_drvr_vertex); } } -void -Search::seedClkVertexArrivals(const Pin *pin, - Vertex *vertex) -{ - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - seedClkArrivals(pin, vertex, &tag_bldr); - setVertexArrivals(vertex, &tag_bldr); -} - Arrival Search::clockInsertion(const Clock *clk, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const EarlyLate *early_late, - const PathAnalysisPt *path_ap) const + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max, + const EarlyLate *early_late, + const Mode *mode) const { float insert; bool exists; - sdc_->clockInsertion(clk, pin, rf, min_max, early_late, insert, exists); + mode->sdc()->clockInsertion(clk, pin, rf, min_max, early_late, insert, exists); if (exists) return insert; else if (clk->isGeneratedWithPropagatedMaster()) - return genclks_->insertionDelay(clk, pin, rf, early_late, path_ap); + return mode->genclks()->insertionDelay(clk, pin, rf, early_late); else return 0.0; } //////////////////////////////////////////////////////////////// -void -Search::visitStartpoints(VertexVisitor *visitor) -{ - Instance *top_inst = network_->topInstance(); - InstancePinIterator *pin_iter = network_->pinIterator(top_inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyInput()) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - visitor->visit(vertex); - } - } - delete pin_iter; - - for (const auto [pin, input_delays] : sdc_->inputDelayPinMap()) { - // Already hit these. - if (!network_->isTopLevelPort(pin)) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex) - visitor->visit(vertex); - } - } - - for (const Clock *clk : sdc_->clks()) { - for (const Pin *pin : clk->leafPins()) { - // Already hit these. - if (!network_->isTopLevelPort(pin)) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - visitor->visit(vertex); - } - } - } - - // Register clk pins. - for (Vertex *vertex : *graph_->regClkVertices()) - visitor->visit(vertex); - - const PinSet &startpoints = sdc_->pathDelayInternalFrom(); - for (const Pin *pin : startpoints) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - visitor->visit(vertex); - } -} - -void -Search::visitEndpoints(VertexVisitor *visitor) -{ - for (Vertex *end : *endpoints()) { - Pin *pin = end->pin(); - // Filter register clock pins (fails on set_max_delay -from clk_src). - if (!network_->isRegClkPin(pin) - || sdc_->isPathDelayInternalTo(pin)) - visitor->visit(end); - } -} - -//////////////////////////////////////////////////////////////// - void Search::findAllArrivals() { - findAllArrivals(true); + findAllArrivals(true, false); } void -Search::findAllArrivals(bool thru_latches) +Search::findAllArrivals(bool thru_latches, + bool clks_only) { - arrival_visitor_->init(false); + if (!clks_only) + enqueuePendingClkFanouts(); + 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++) { @@ -1081,19 +1038,19 @@ Search::findAllArrivals(bool thru_latches) bool Search::havePendingLatchOutputs() { - return !pending_latch_outputs_->empty(); + return !pending_latch_outputs_.empty(); } void Search::clearPendingLatchOutputs() { - pending_latch_outputs_->clear(); + pending_latch_outputs_.clear(); } void Search::enqueuePendingLatchOutputs() { - for (Vertex *latch_vertex : *pending_latch_outputs_) { + for (Vertex *latch_vertex : pending_latch_outputs_) { debugPrint(debug_, "search", 2, "enqueue latch output %s", latch_vertex->to_string(this).c_str()); arrival_iter_->enqueue(latch_vertex); @@ -1101,6 +1058,24 @@ Search::enqueuePendingLatchOutputs() clearPendingLatchOutputs(); } +void +Search::enqueuePendingClkFanouts() +{ + for (Vertex *vertex : pending_clk_endpoints_) { + debugPrint(debug_, "search", 2, "enqueue clk fanout %s", + vertex->to_string(this).c_str()); + arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); + } + pending_clk_endpoints_.clear(); +} + +void +Search::postponeClkFanouts(Vertex *vertex) +{ + LockGuard lock(pending_clk_endpoints_lock_); + pending_clk_endpoints_.insert(vertex); +} + void Search::findArrivals() { @@ -1110,7 +1085,7 @@ Search::findArrivals() void Search::findArrivals(Level level) { - arrival_visitor_->init(false); + arrival_visitor_->init(false, false, eval_pred_); findArrivals1(level); } @@ -1125,11 +1100,6 @@ Search::findArrivals1(Level level) if (arrival_count > 0) deleteUnusedTagGroups(); stats.report("Find arrivals"); - if (arrival_iter_->empty() - && invalid_arrivals_->empty()) { - clk_arrivals_valid_ = true; - arrivals_at_endpoints_exist_ = true; - } arrivals_exist_ = true; debugPrint(debug_, "search", 1, "found %d arrivals", arrival_count); } @@ -1138,7 +1108,8 @@ void Search::findArrivalsSeed() { if (!arrivals_seeded_) { - genclks_->ensureInsertionDelays(); + for (const Mode *mode : modes_) + mode->genclks()->ensureInsertionDelays(); arrival_iter_->clear(); required_iter_->clear(); seedArrivals(); @@ -1157,17 +1128,17 @@ ArrivalVisitor::ArrivalVisitor(const StaState *sta) : PathVisitor(nullptr, false, sta) { init0(); - init(true); + init(true, false, nullptr); } // Copy constructor. ArrivalVisitor::ArrivalVisitor(bool always_to_endpoints, - SearchPred *pred, - const StaState *sta) : + SearchPred *pred, + const StaState *sta) : PathVisitor(pred, true, sta) { init0(); - init(always_to_endpoints, pred); + init(always_to_endpoints, false, pred); } void @@ -1175,22 +1146,18 @@ ArrivalVisitor::init0() { tag_bldr_ = new TagGroupBldr(true, this); tag_bldr_no_crpr_ = new TagGroupBldr(false, this); - adj_pred_ = new SearchThru(tag_bldr_, this); -} - -void -ArrivalVisitor::init(bool always_to_endpoints) -{ - init(always_to_endpoints, search_ ? search_->evalPred() : nullptr); + adj_pred_ = new SearchAdj(tag_bldr_, this); } void ArrivalVisitor::init(bool always_to_endpoints, - SearchPred *pred) + bool clks_only, + SearchPred *pred) { always_to_endpoints_ = always_to_endpoints; + clks_only_ = clks_only; pred_ = pred; - crpr_active_ = crprActive(); + crpr_active_ = variables_->crprEnabled(); } @@ -1200,6 +1167,13 @@ ArrivalVisitor::copy() const return new ArrivalVisitor(always_to_endpoints_, pred_, this); } +void +ArrivalVisitor::copyState(const StaState *sta) +{ + StaState::copyState(sta); + adj_pred_->copyState(sta); +} + ArrivalVisitor::~ArrivalVisitor() { delete tag_bldr_; @@ -1225,41 +1199,18 @@ ArrivalVisitor::visit(Vertex *vertex) && !has_fanin_one_) tag_bldr_no_crpr_->init(vertex); - // Fanin paths are broken by path delays internal pin startpoints. - if (!sdc_->isPathDelayInternalFromBreak(pin)) { - visitFaninPaths(vertex); - if (crpr_active_ - && search_->crprPathPruningEnabled() - // No crpr for ideal clocks. - && tag_bldr_->hasPropagatedClk() - && !has_fanin_one_) - pruneCrprArrivals(); - } + visitFaninPaths(vertex); + if (crpr_active_ + && search_->crprPathPruningEnabled() + // No crpr for ideal clocks. + && tag_bldr_->hasPropagatedClk() + && !has_fanin_one_) + pruneCrprArrivals(); // Insert paths that originate here. - if (!network_->isTopLevelPort(pin) - && sdc_->hasInputDelay(pin)) - // set_input_delay on internal pin. - search_->seedInputSegmentArrival(pin, vertex, tag_bldr_); - if (sdc_->isPathDelayInternalFrom(pin)) - // set_min/max_delay -from internal pin. - search_->makeUnclkedPaths(vertex, false, true, tag_bldr_); - if (sdc_->isLeafPinClock(pin)) - // set_min/max_delay -to internal pin also a clock src. Bizzaroland. - // Re-seed the clock arrivals on top of the propagated paths. - search_->seedClkArrivals(pin, vertex, tag_bldr_); - // Register/latch clock pin that is not connected to a declared clock. - // Seed with unclocked tag, zero arrival and allow search thru reg - // clk->q edges. - // These paths are required to report path delays from unclocked registers - // For example, "set_max_delay -to" from an unclocked source register. - bool is_clk = tag_bldr_->hasClkTag(); - if (vertex->isRegClk() && !is_clk) { - debugPrint(debug_, "search", 2, "arrival seed unclked reg clk %s", - network_->pathName(pin)); - search_->makeUnclkedPaths(vertex, true, false, tag_bldr_); - } + seedArrivals(vertex); + bool is_clk = tag_bldr_->hasClkTag(); bool arrivals_changed = search_->arrivalsChanged(vertex, tag_bldr_); // 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 @@ -1268,17 +1219,69 @@ ArrivalVisitor::visit(Vertex *vertex) && network_->isLatchData(pin)) search_->enqueueLatchDataOutputs(vertex); - if (!search_->arrivalsAtEndpointsExist() - || always_to_endpoints_ - || arrivals_changed) - search_->arrivalIterator()->enqueueAdjacentVertices(vertex, adj_pred_); + if ((always_to_endpoints_ + || arrivals_changed)) { + if (clks_only_ + && vertex->isRegClk()) { + debugPrint(debug_, "search", 3, "postponing clk fanout"); + search_->postponeClkFanouts(vertex); + } + else + search_->arrivalIterator()->enqueueAdjacentVertices(vertex, adj_pred_); + } if (arrivals_changed) { debugPrint(debug_, "search", 4, "arrivals changed"); search_->setVertexArrivals(vertex, tag_bldr_); search_->tnsInvalid(vertex); constrainedRequiredsInvalid(vertex, is_clk); } - enqueueRefPinInputDelays(pin); +} + +void +ArrivalVisitor::seedArrivals(Vertex *vertex) +{ + const Pin *pin = vertex->pin(); + bool is_clk = tag_bldr_->hasClkTag(); + for (const Mode *mode : modes_) { + const Sdc *sdc = mode->sdc(); + mode->genclks()->copyGenClkSrcPaths(vertex, tag_bldr_); + if (sdc->isLeafPinClock(pin)) + search_->seedClkArrivals(pin, mode, tag_bldr_); + 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)) + search_->seedInputSegmentArrival(pin, vertex, mode, tag_bldr_); + 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", + network_->pathName(pin)); + search_->makeUnclkedPaths(vertex, is_reg_clk, false, tag_bldr_, mode); + } + } + // Register/latch clock pin that is not connected to a declared clock. + // Seed with unclocked tag, zero arrival and allow search thru reg + // clk->q edges. + // 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", + network_->pathName(pin)); + search_->makeUnclkedPaths(vertex, true, false, tag_bldr_, mode); + } + enqueueRefPinInputDelays(pin, sdc); + } } // When a clock arrival changes, the required time changes for any @@ -1286,7 +1289,7 @@ ArrivalVisitor::visit(Vertex *vertex) // by the clock pin. void ArrivalVisitor::constrainedRequiredsInvalid(Vertex *vertex, - bool is_clk) + bool is_clk) { Pin *pin = vertex->pin(); if (network_->isLoad(pin) @@ -1294,36 +1297,39 @@ ArrivalVisitor::constrainedRequiredsInvalid(Vertex *vertex, if (is_clk && network_->isCheckClk(pin)) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (edge->role()->isTimingCheck()) { - Vertex *to_vertex = edge->to(graph_); - search_->requiredInvalid(to_vertex); - } + Edge *edge = edge_iter.next(); + if (edge->role()->isTimingCheck()) { + Vertex *to_vertex = edge->to(graph_); + search_->requiredInvalid(to_vertex); + } } } // Data checks (vertex does not need to be a clk). - DataCheckSet *data_checks = sdc_->dataChecksFrom(pin); - if (data_checks) { - for (DataCheck *data_check : *data_checks) { - Pin *to = data_check->to(); - search_->requiredInvalid(to); + for (const Mode *mode : modes_) { + const Sdc *sdc = mode->sdc(); + DataCheckSet *data_checks = sdc->dataChecksFrom(pin); + if (data_checks) { + for (DataCheck *data_check : *data_checks) { + Pin *to = data_check->to(); + search_->requiredInvalid(to); + } + } + + // Gated clocks. + if (is_clk && variables_->gatedClkChecksEnabled()) { + PinSet enable_pins = search_->gatedClk()->gatedClkEnables(vertex, mode); + for (const Pin *enable : enable_pins) + search_->requiredInvalid(enable); } - } - // Gated clocks. - if (is_clk && variables_->gatedClkChecksEnabled()) { - PinSet enable_pins(network_); - search_->gatedClk()->gatedClkEnables(vertex, enable_pins); - for (const Pin *enable : enable_pins) - search_->requiredInvalid(enable); } } } bool Search::arrivalsChanged(Vertex *vertex, - TagGroupBldr *tag_bldr) + TagGroupBldr *tag_bldr) { - Path *paths1 = graph_->paths(vertex); + Path *paths1 = vertex->paths(); if (paths1) { TagGroup *tag_group = tagGroup(vertex); if (tag_group == nullptr @@ -1343,25 +1349,24 @@ Search::arrivalsChanged(Vertex *vertex, return false; } else - return true; + return !tag_bldr->empty(); } bool ArrivalVisitor::visitFromToPath(const Pin * /* from_pin */, - Vertex *from_vertex, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, + 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, - const PathAnalysisPt *) + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex * /* to_vertex */, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max) { debugPrint(debug_, "search", 3, " %s", from_vertex->to_string(this).c_str()); @@ -1388,13 +1393,13 @@ ArrivalVisitor::visitFromToPath(const Pin * /* from_pin */, 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() - && !to_is_clk) { + && !has_fanin_one_ + && to_clk_info->hasCrprClkPin() + && !to_is_clk) { tag_bldr_no_crpr_->tagMatchPath(to_tag, match, path_index); if (match == nullptr - || delayGreater(to_arrival, match->arrival(), min_max, this)) { - tag_bldr_no_crpr_->setMatchPath(match, path_index, to_tag, to_arrival, + || delayGreater(to_arrival, match->arrival(), min_max, this)) { + tag_bldr_no_crpr_->setMatchPath(match, path_index, to_tag, to_arrival, from_path, edge, arc); } } @@ -1413,33 +1418,32 @@ ArrivalVisitor::pruneCrprArrivals() const ClkInfo *clk_info = tag->clkInfo(); bool deleted_tag = false; if (!tag->isClock() - && clk_info->hasCrprClkPin()) { - PathAnalysisPt *path_ap = tag->pathAnalysisPt(this); - const MinMax *min_max = path_ap->pathMinMax(); + && clk_info->hasCrprClkPin()) { + const MinMax *min_max = tag->minMax(); Path *path_no_crpr = tag_bldr_no_crpr_->tagMatchPath(tag); if (path_no_crpr) { Arrival max_arrival = path_no_crpr->arrival(); - 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; - debugPrint(debug_, "search", 4, " cmp %s %s - %s = %s", + 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; + debugPrint(debug_, "search", 4, " cmp %s %s - %s = %s", tag->to_string(this).c_str(), delayAsString(max_arrival, this), delayAsString(max_crpr, this), delayAsString(max_arrival_max_crpr, this)); Arrival arrival = tag_bldr_->arrival(path_index); - // Latch D->Q path uses enable min so crpr clk path min/max - // 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", + // Latch D->Q path uses enable min so crpr clk path min/max + // 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()); path_itr = path_index_map.erase(path_itr); deleted_tag = true; - } + } } } if (!deleted_tag) @@ -1451,46 +1455,33 @@ ArrivalVisitor::pruneCrprArrivals() // reference pin as if there is a timing arc from the reference pin to // the input delay pin. void -ArrivalVisitor::enqueueRefPinInputDelays(const Pin *ref_pin) +ArrivalVisitor::enqueueRefPinInputDelays(const Pin *ref_pin, + const Sdc *sdc) { - InputDelaySet *input_delays = sdc_->refPinInputDelays(ref_pin); + InputDelaySet *input_delays = sdc->refPinInputDelays(ref_pin); if (input_delays) { + BfsFwdIterator *arrival_iter = search_->arrivalIterator(); for (InputDelay *input_delay : *input_delays) { const Pin *pin = input_delay->pin(); Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - seedInputDelayArrival(pin, vertex, input_delay); + arrival_iter->enqueue(vertex); if (bidirect_drvr_vertex) - seedInputDelayArrival(pin, bidirect_drvr_vertex, input_delay); + arrival_iter->enqueue(bidirect_drvr_vertex); } } } -void -ArrivalVisitor::seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay) -{ - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - search_->genclks()->copyGenClkSrcPaths(vertex, &tag_bldr); - search_->seedInputDelayArrival(pin, vertex, input_delay, - !network_->isTopLevelPort(pin), &tag_bldr); - search_->setVertexArrivals(vertex, &tag_bldr); - search_->arrivalIterator()->enqueueAdjacentVertices(vertex, - search_->searchAdj()); -} - void Search::enqueueLatchDataOutputs(Vertex *vertex) { - VertexOutEdgeIterator out_edge_iter(vertex, graph_); - while (out_edge_iter.hasNext()) { - Edge *out_edge = out_edge_iter.next(); - if (latches_->isLatchDtoQ(out_edge)) { - Vertex *out_vertex = out_edge->to(graph_); + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role() == TimingRole::latchDtoQ()) { + Vertex *out_vertex = edge->to(graph_); LockGuard lock(pending_latch_outputs_lock_); - pending_latch_outputs_->insert(out_vertex); + pending_latch_outputs_.insert(out_vertex); } } } @@ -1499,31 +1490,34 @@ void Search::enqueueLatchOutput(Vertex *vertex) { LockGuard lock(pending_latch_outputs_lock_); - pending_latch_outputs_->insert(vertex); + pending_latch_outputs_.insert(vertex); } void Search::seedArrivals() { - VertexSet vertices(graph_); + VertexSet vertices = makeVertexSet(this); findClockVertices(vertices); findRootVertices(vertices); findInputDrvrVertices(vertices); for (Vertex *vertex : vertices) - seedArrival(vertex); + arrival_iter_->enqueue(vertex); } void Search::findClockVertices(VertexSet &vertices) { - for (const Clock *clk : sdc_->clks()) { - for (const Pin *pin : clk->leafPins()) { - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - vertices.insert(vertex); - if (bidirect_drvr_vertex) - vertices.insert(bidirect_drvr_vertex); + for (const Mode *mode : modes_) { + const Sdc *sdc = mode->sdc(); + for (const Clock *clk : sdc->clocks()) { + for (const Pin *pin : clk->leafPins()) { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + vertices.insert(vertex); + if (bidirect_drvr_vertex) + vertices.insert(bidirect_drvr_vertex); + } } } } @@ -1531,166 +1525,124 @@ Search::findClockVertices(VertexSet &vertices) void Search::seedInvalidArrivals() { - for (Vertex *vertex : *invalid_arrivals_) - seedArrival(vertex); - invalid_arrivals_->clear(); -} - -void -Search::seedArrival(Vertex *vertex) -{ - const Pin *pin = vertex->pin(); - if (sdc_->isLeafPinClock(pin)) { - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - seedClkArrivals(pin, vertex, &tag_bldr); - // Clock pin may also have input arrivals from other clocks. - seedInputArrival(pin, vertex, &tag_bldr); - setVertexArrivals(vertex, &tag_bldr); - } - else if (isInputArrivalSrchStart(vertex)) { - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - seedInputArrival(pin, vertex, &tag_bldr); - setVertexArrivals(vertex, &tag_bldr); - if (!tag_bldr.empty()) - // Only search downstream if there were non-false paths from here. - arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); - } - else if (levelize_->isRoot(vertex)) { - bool is_reg_clk = vertex->isRegClk(); - if (is_reg_clk - // Internal roots isolated by disabled pins are seeded with no clock. - || (unconstrained_paths_ - && !network_->isTopLevelPort(pin))) { - debugPrint(debug_, "search", 2, "arrival seed unclked root %s", - network_->pathName(pin)); - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - if (makeUnclkedPaths(vertex, is_reg_clk, false, &tag_bldr)) - // Only search downstream if there are no false paths from here. - arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); - setVertexArrivals(vertex, &tag_bldr); - } - else { - deletePathsIncr(vertex); - if (search_adj_->searchFrom(vertex)) - arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); - } - } - else { - debugPrint(debug_, "search", 4, "arrival enqueue %s %u", - network_->pathName(pin), - vertex->level()); + for (Vertex *vertex : invalid_arrivals_) arrival_iter_->enqueue(vertex); - } + invalid_arrivals_.clear(); } // Find all of the clock leaf pins. void Search::findClkVertexPins(PinSet &clk_pins) { - for (const Clock *clk : sdc_->clks()) { - for (const Pin *pin : clk->leafPins()) { - clk_pins.insert(pin); + for (Scene *scene : scenes_) { + const Sdc *sdc = scene->sdc(); + for (const Clock *clk : sdc->clocks()) { + for (const Pin *pin : clk->leafPins()) { + clk_pins.insert(pin); + } } } } void Search::seedClkArrivals(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr) -{ - for (const Clock *clk : *sdc_->findLeafPinClocks(pin)) { - debugPrint(debug_, "search", 2, "arrival seed clk %s pin %s", - clk->name(), network_->pathName(pin)); - for (PathAnalysisPt *path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); - 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, path_ap, - 0.0, tag_bldr); - else { - Arrival insertion = clockInsertion(clk, pin, rf, min_max, - early_late, path_ap); - seedClkArrival(pin, rf, clk, clk_edge, min_max, path_ap, - insertion, tag_bldr); - } + const Mode *mode, + TagGroupBldr *tag_bldr) +{ + const Sdc *sdc = mode->sdc(); + 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)); + 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); + 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_iter_->enqueueAdjacentVertices(vertex, search_adj_); } } void Search::seedClkArrival(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - Arrival insertion, - TagGroupBldr *tag_bldr) -{ + const RiseFall *rf, + const Clock *clk, + const ClockEdge *clk_edge, + const MinMax *min_max, + Arrival insertion, + Scene *scene, + TagGroupBldr *tag_bldr) +{ + Sdc *sdc = scene->sdc(); bool is_propagated = false; 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)) { - latency = 0.0; - latency_exists = false; - is_propagated = true; + if (sdc->isPropagatedClock(pin)) { + latency = 0.0; + latency_exists = false; + is_propagated = true; } } else - is_propagated = sdc_->isPropagatedClock(pin) - || clk->isPropagated(); + is_propagated = sdc->isPropagatedClock(pin) + || clk->isPropagated(); } - ClockUncertainties *uncertainties = sdc_->clockUncertainties(pin); + const ClockUncertainties *uncertainties = sdc->clockUncertainties(pin); if (uncertainties == nullptr) uncertainties = clk->uncertainties(); // 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(clk_edge, pin, is_propagated, nullptr, false, - pulse_clk_sense, insertion, latency, - uncertainties, path_ap, nullptr); + 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; - sdc_->exceptionFromClkStates(pin,rf,clk,rf,min_max,states); - Tag *tag = findTag(rf, path_ap, clk_info, true, nullptr, false, states, - true, nullptr); + 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); tag_bldr->setArrival(tag, arrival); } void Search::seedClkDataArrival(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - Arrival insertion, - TagGroupBldr *tag_bldr) -{ - Tag *tag = clkDataTag(pin, clk, rf, clk_edge, insertion, min_max, path_ap); + const RiseFall *rf, + const Clock *clk, + const ClockEdge *clk_edge, + const MinMax *min_max, + 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. Arrival arrival(clk_edge->time() + insertion); @@ -1700,21 +1652,22 @@ Search::seedClkDataArrival(const Pin *pin, Tag * Search::clkDataTag(const Pin *pin, - const Clock *clk, - const RiseFall *rf, - const ClockEdge *clk_edge, - Arrival insertion, - const MinMax *min_max, - const PathAnalysisPt *path_ap) -{ + const Clock *clk, + const RiseFall *rf, + const ClockEdge *clk_edge, + Arrival insertion, + const MinMax *min_max, + Scene *scene) +{ + Sdc *sdc = scene->sdc(); ExceptionStateSet *states = nullptr; - if (sdc_->exceptionFromStates(pin, rf, clk, rf, min_max, states)) { + if (sdc->exceptionFromStates(pin, rf, clk, rf, min_max, states)) { bool is_propagated = (clk->isPropagated() - || sdc_->isPropagatedClock(pin)); - const ClkInfo *clk_info = findClkInfo(clk_edge, pin, is_propagated, - insertion, path_ap); - return findTag(rf, path_ap, clk_info, false, nullptr, false, states, - true, nullptr); + || 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; @@ -1724,39 +1677,61 @@ Search::clkDataTag(const Pin *pin, bool Search::makeUnclkedPaths(Vertex *vertex, - bool is_segment_start, - bool require_exception, - TagGroupBldr *tag_bldr) + bool is_segment_start, + bool require_exception, + TagGroupBldr *tag_bldr, + const Mode *mode) { bool search_from = false; const Pin *pin = vertex->pin(); - for (PathAnalysisPt *path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); - for (const RiseFall *rf : RiseFall::range()) { - Tag *tag = fromUnclkedInputTag(pin, rf, min_max, path_ap, - is_segment_start, - require_exception); - if (tag) { - tag_bldr->setArrival(tag, delay_zero); - search_from = true; + for (Scene *scene : mode->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + for (const RiseFall *rf : RiseFall::range()) { + Tag *tag = fromUnclkedInputTag(pin, rf, min_max, is_segment_start, + require_exception, scene); + if (tag) { + tag_bldr->setArrival(tag, delay_zero); + search_from = true; + } } } } return search_from; } -// Find graph roots and input ports that do NOT have arrivals. void Search::findRootVertices(VertexSet &vertices) { - for (Vertex *vertex : levelize_->roots()) { - const Pin *pin = vertex->pin(); - if (!sdc_->isLeafPinClock(pin) - && !sdc_->hasInputDelay(pin) - && !vertex->isConstant()) { - vertices.insert(vertex); + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + for (Mode *mode : modes_) { + if (isSrchRoot(vertex, mode)) { + vertices.insert(vertex); + break; + } + } + } +} + +bool +Search::isSrchRoot(Vertex *vertex, + const Mode *mode) const +{ + if (!eval_pred_->searchFrom(vertex, mode)) + return false; + else { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + if (!edge->role()->isTimingCheck() + && (eval_pred_->searchFrom(from_vertex, mode) + && eval_pred_->searchThru(edge, mode))) + return false; } } + return true; } void @@ -1772,13 +1747,6 @@ Search::findInputDrvrVertices(VertexSet &vertices) delete pin_iter; } -bool -Search::isSegmentStart(const Pin *pin) -{ - return sdc_->isInputDelayInternal(pin) - && !sdc_->isLeafPinClock(pin); -} - bool Search::isInputArrivalSrchStart(Vertex *vertex) { @@ -1786,103 +1754,65 @@ 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()))) ; -} - -// Seed input arrivals clocked by clks. -void -Search::seedInputArrivals(ClockSet *clks) -{ - // Input arrivals can be on internal pins, so iterate over the pins - // that have input arrivals rather than the top level input pins. - for (const auto [pin, input_delays] : sdc_->inputDelayPinMap()) { - if (!sdc_->isLeafPinClock(pin)) { - Vertex *vertex = graph_->pinDrvrVertex(pin); - seedInputArrival(pin, vertex, clks); - } - } + && (dir->isInput() + || (dir->isBidirect() && vertex->isBidirectDriver()))) ; } void Search::seedInputArrival(const Pin *pin, - Vertex *vertex, - ClockSet *wrt_clks) -{ - bool has_arrival = false; - // There can be multiple arrivals for a pin with wrt different clocks. - TagGroupBldr tag_bldr(true, this); - tag_bldr.init(vertex); - genclks_->copyGenClkSrcPaths(vertex, &tag_bldr); - InputDelaySet *input_delays = sdc_->inputDelaysLeafPin(pin); - if (input_delays) { - for (InputDelay *input_delay : *input_delays) { - Clock *input_clk = input_delay->clock(); - ClockSet *pin_clks = sdc_->findLeafPinClocks(pin); - if (input_clk && wrt_clks->hasKey(input_clk) - // Input arrivals wrt a clock source pin is the insertion - // delay (source latency), but arrivals wrt other clocks - // propagate. - && (pin_clks == nullptr - || !pin_clks->hasKey(input_clk))) { - seedInputDelayArrival(pin, vertex, input_delay, false, &tag_bldr); - has_arrival = true; - } - } - if (has_arrival) - setVertexArrivals(vertex, &tag_bldr); - } -} - -void -Search::seedInputArrival(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr) + Vertex *vertex, + const Mode *mode, + TagGroupBldr *tag_bldr) { - if (sdc_->hasInputDelay(pin)) - seedInputArrival1(pin, vertex, false, tag_bldr); - else if (!sdc_->isLeafPinClock(pin)) + const Sdc *sdc = mode->sdc(); + if (sdc->hasInputDelay(pin)) + seedInputArrival1(pin, vertex, false, mode, tag_bldr); + else if (!sdc->isLeafPinClock(pin)) // Seed inputs without set_input_delays. - seedInputDelayArrival(pin, vertex, nullptr, false, tag_bldr); + seedInputDelayArrival(pin, vertex, nullptr, false, mode, tag_bldr); } void Search::seedInputSegmentArrival(const Pin *pin, - Vertex *vertex, - TagGroupBldr *tag_bldr) + Vertex *vertex, + const Mode *mode, + TagGroupBldr *tag_bldr) { - seedInputArrival1(pin, vertex, true, tag_bldr); + seedInputArrival1(pin, vertex, true, mode, tag_bldr); } void Search::seedInputArrival1(const Pin *pin, - Vertex *vertex, - bool is_segment_start, - TagGroupBldr *tag_bldr) + Vertex *vertex, + bool is_segment_start, + const Mode *mode, + TagGroupBldr *tag_bldr) { // There can be multiple arrivals for a pin with wrt different clocks. - InputDelaySet *input_delays = sdc_->inputDelaysLeafPin(pin); + const Sdc *sdc = mode->sdc(); + InputDelaySet *input_delays = sdc->inputDelaysLeafPin(pin); if (input_delays) { for (InputDelay *input_delay : *input_delays) { Clock *input_clk = input_delay->clock(); - ClockSet *pin_clks = sdc_->findLeafPinClocks(pin); + ClockSet *pin_clks = sdc->findLeafPinClocks(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->hasKey(input_clk)) - seedInputDelayArrival(pin, vertex, input_delay, is_segment_start, - tag_bldr); + || !pin_clks->contains(input_clk)) + seedInputDelayArrival(pin, vertex, input_delay, is_segment_start, + mode, tag_bldr); } } } void Search::seedInputDelayArrival(const Pin *pin, - Vertex *vertex, - InputDelay *input_delay, - bool is_segment_start, - TagGroupBldr *tag_bldr) + Vertex *vertex, + InputDelay *input_delay, + bool is_segment_start, + const Mode *mode, + TagGroupBldr *tag_bldr) { debugPrint(debug_, "search", 2, input_delay @@ -1891,46 +1821,50 @@ Search::seedInputDelayArrival(const Pin *pin, 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()) - clk_edge = sdc_->defaultArrivalClockEdge(); + && variables_->useDefaultArrivalClock()) + clk_edge = sdc->defaultArrivalClockEdge(); ref_pin = input_delay->refPin(); } else if (variables_->useDefaultArrivalClock()) - clk_edge = sdc_->defaultArrivalClockEdge(); + clk_edge = sdc->defaultArrivalClockEdge(); if (ref_pin) { Vertex *ref_vertex = graph_->pinLoadVertex(ref_pin); - for (PathAnalysisPt *path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); - const RiseFall *ref_rf = input_delay->refTransition(); - const Clock *clk = input_delay->clock(); - VertexPathIterator ref_path_iter(ref_vertex, ref_rf, path_ap, this); - while (ref_path_iter.hasNext()) { - Path *ref_path = ref_path_iter.next(); - if (ref_path->isClock(this) - && (clk == nullptr - || ref_path->clock(this) == clk)) { - float ref_arrival, ref_insertion, ref_latency; - inputDelayRefPinArrival(ref_path, ref_path->clkEdge(this), min_max, - 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, path_ap, tag_bldr); - } + for (Scene *scene : mode->scenes()) { + for (const MinMax *min_max : MinMax::range()) { + const RiseFall *ref_rf = input_delay->refTransition(); + const Clock *clk = input_delay->clock(); + VertexPathIterator ref_path_iter(ref_vertex, scene, min_max, ref_rf, this); + while (ref_path_iter.hasNext()) { + Path *ref_path = ref_path_iter.next(); + if (ref_path->isClock(this) + && (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); + seedInputDelayArrival(pin, input_delay, ref_path->clkEdge(this), + ref_arrival, ref_insertion, ref_latency, + is_segment_start, min_max, scene, tag_bldr); + } + } } } } else { - for (PathAnalysisPt *path_ap : corners_->pathAnalysisPts()) { - const MinMax *min_max = path_ap->pathMinMax(); + for (const MinMax *min_max : MinMax::range()) { float clk_arrival, clk_insertion, clk_latency; - inputDelayClkArrival(input_delay, clk_edge, min_max, path_ap, - clk_arrival, clk_insertion, clk_latency); - seedInputDelayArrival(pin, input_delay, clk_edge, - clk_arrival, clk_insertion, clk_latency, - is_segment_start, min_max, path_ap, tag_bldr); + 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); + } } } } @@ -1939,12 +1873,13 @@ Search::seedInputDelayArrival(const Pin *pin, // from the clock source to the reference pin. void Search::inputDelayRefPinArrival(Path *ref_path, - const ClockEdge *clk_edge, - const MinMax *min_max, - // Return values. - float &ref_arrival, - float &ref_insertion, - float &ref_latency) + const ClockEdge *clk_edge, + const MinMax *min_max, + const Sdc *sdc, + // Return values. + float &ref_arrival, + float &ref_insertion, + float &ref_latency) { Clock *clk = clk_edge->clock(); if (clk->isPropagated()) { @@ -1958,7 +1893,7 @@ Search::inputDelayRefPinArrival(Path *ref_path, const EarlyLate *early_late = min_max; // Input delays from ideal clk reference pins include clock // insertion delay but not latency. - ref_insertion = sdc_->clockInsertion(clk, clk_rf, min_max, early_late); + ref_insertion = sdc->clockInsertion(clk, clk_rf, min_max, early_late); ref_arrival = clk_edge->time() + ref_insertion; ref_latency = 0.0; } @@ -1966,15 +1901,15 @@ Search::inputDelayRefPinArrival(Path *ref_path, void Search::seedInputDelayArrival(const Pin *pin, - InputDelay *input_delay, - const ClockEdge *clk_edge, - float clk_arrival, - float clk_insertion, - float clk_latency, - bool is_segment_start, - const MinMax *min_max, - PathAnalysisPt *path_ap, - TagGroupBldr *tag_bldr) + InputDelay *input_delay, + const ClockEdge *clk_edge, + float clk_arrival, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + Scene *scene, + TagGroupBldr *tag_bldr) { for (const RiseFall *rf : RiseFall::range()) { if (input_delay) { @@ -1982,45 +1917,46 @@ 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, path_ap, 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, path_ap, tag_bldr); + clk_insertion, clk_latency, is_segment_start, + min_max, scene, tag_bldr); } } void Search::seedInputDelayArrival(const Pin *pin, - const RiseFall *rf, - float arrival, - InputDelay *input_delay, - const ClockEdge *clk_edge, - float clk_insertion, - float clk_latency, - bool is_segment_start, - const MinMax *min_max, - PathAnalysisPt *path_ap, - TagGroupBldr *tag_bldr) + const RiseFall *rf, + float arrival, + InputDelay *input_delay, + const ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + Scene *scene, + TagGroupBldr *tag_bldr) { Tag *tag = inputDelayTag(pin, rf, clk_edge, clk_insertion, clk_latency, - input_delay, is_segment_start, min_max, path_ap); + input_delay, is_segment_start, min_max, scene); if (tag) tag_bldr->setArrival(tag, arrival); } void Search::inputDelayClkArrival(InputDelay *input_delay, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - // Return values. - float &clk_arrival, float &clk_insertion, - float &clk_latency) + const ClockEdge *clk_edge, + const MinMax *min_max, + const Mode *mode, + // Return values. + float &clk_arrival, + float &clk_insertion, + float &clk_latency) { clk_arrival = 0.0; clk_insertion = 0.0; @@ -2032,13 +1968,13 @@ Search::inputDelayClkArrival(InputDelay *input_delay, if (!input_delay->sourceLatencyIncluded()) { const EarlyLate *early_late = min_max; clk_insertion = delayAsFloat(clockInsertion(clk, clk->defaultPin(), - clk_rf, min_max, early_late, - path_ap)); + clk_rf, min_max, + early_late, mode)); clk_arrival += clk_insertion; } if (!clk->isPropagated() - && !input_delay->networkLatencyIncluded()) { - clk_latency = sdc_->clockLatency(clk, clk_rf, min_max); + && !input_delay->networkLatencyIncluded()) { + clk_latency = mode->sdc()->clockLatency(clk, clk_rf, min_max); clk_arrival += clk_latency; } } @@ -2046,14 +1982,14 @@ Search::inputDelayClkArrival(InputDelay *input_delay, Tag * Search::inputDelayTag(const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - float clk_insertion, - float clk_latency, - InputDelay *input_delay, - bool is_segment_start, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + const RiseFall *rf, + const ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + InputDelay *input_delay, + bool is_segment_start, + const MinMax *min_max, + Scene *scene) { Clock *clk = nullptr; const Pin *clk_pin = nullptr; @@ -2068,22 +2004,24 @@ Search::inputDelayTag(const Pin *pin, clk_uncertainties = clk->uncertainties(); } + 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(clk_edge, clk_pin, is_propagated, nullptr, - false, nullptr, clk_insertion, clk_latency, - clk_uncertainties, path_ap, nullptr); - tag = findTag(rf, path_ap, 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, min_max, path_ap, nullptr); + pin, rf, false, false, is_segment_start, clk_info, + input_delay, nullptr); } return tag; } @@ -2099,14 +2037,14 @@ PathVisitor::PathVisitor(const StaState *sta) : } PathVisitor::PathVisitor(SearchPred *pred, - bool make_tag_cache, - const StaState *sta) : + bool make_tag_cache, + const StaState *sta) : StaState(sta), pred_(pred), tag_cache_(make_tag_cache - ? new TagSet(128, TagSet::hasher(sta), TagSet::key_equal(sta)) - : nullptr) + ? new TagSet(128, TagSet::hasher(sta), TagSet::key_equal(sta)) + : nullptr) { } @@ -2118,19 +2056,14 @@ PathVisitor::~PathVisitor() void PathVisitor::visitFaninPaths(Vertex *to_vertex) { - if (pred_->searchTo(to_vertex)) { - VertexInEdgeIterator edge_iter(to_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - const Pin *from_pin = from_vertex->pin(); - if (pred_->searchFrom(from_vertex) - && pred_->searchThru(edge)) { - const Pin *to_pin = to_vertex->pin(); - if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) - break; - } - } + VertexInEdgeIterator edge_iter(to_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + const Pin *from_pin = from_vertex->pin(); + const Pin *to_pin = to_vertex->pin(); + if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) + break; } } @@ -2138,49 +2071,49 @@ void PathVisitor::visitFanoutPaths(Vertex *from_vertex) { const Pin *from_pin = from_vertex->pin(); - if (pred_->searchFrom(from_vertex)) { - VertexOutEdgeIterator edge_iter(from_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *to_vertex = edge->to(graph_); - const Pin *to_pin = to_vertex->pin(); - if (pred_->searchTo(to_vertex) - && pred_->searchThru(edge)) { - debugPrint(debug_, "search", 3, " %s", - to_vertex->to_string(this).c_str()); - if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) - break; - } - } + VertexOutEdgeIterator edge_iter(from_vertex, graph_); + while (edge_iter.hasNext()) { + 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()); + if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) + break; } } bool PathVisitor::visitEdge(const Pin *from_pin, - Vertex *from_vertex, - Edge *edge, - const Pin *to_pin, - Vertex *to_vertex) + Vertex *from_vertex, + Edge *edge, + const Pin *to_pin, + Vertex *to_vertex) { TagGroup *from_tag_group = search_->tagGroup(from_vertex); if (from_tag_group) { TimingArcSet *arc_set = edge->timingArcSet(); VertexPathIterator from_iter(from_vertex, search_); + const Mode *prev_mode = nullptr; while (from_iter.hasNext()) { Path *from_path = from_iter.next(); - PathAnalysisPt *path_ap = from_path->pathAnalysisPt(this); - const MinMax *min_max = path_ap->pathMinMax(); - 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, path_ap)) - return false; - if (!visitArc(from_pin, from_vertex, from_rf, from_path, - edge, arc2, to_pin, to_vertex, - min_max, path_ap)) - return false; + const Mode *mode = from_path->mode(this); + if (mode == prev_mode + || (pred_->searchFrom(from_vertex, mode) + && pred_->searchThru(edge, mode) + && pred_->searchTo(to_vertex, 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)) + return false; + if (!visitArc(from_pin, from_vertex, from_rf, from_path, + edge, arc2, to_pin, to_vertex, min_max, mode)) + return false; + } } } return true; @@ -2188,88 +2121,93 @@ PathVisitor::visitEdge(const Pin *from_pin, bool PathVisitor::visitArc(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Path *from_path, - Edge *edge, - TimingArc *arc, - const Pin *to_pin, - Vertex *to_vertex, - const MinMax *min_max, - PathAnalysisPt *path_ap) + Vertex *from_vertex, + const RiseFall *from_rf, + Path *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const MinMax *min_max, + const Mode *mode) { if (arc) { const RiseFall *to_rf = arc->toEdge()->asRiseFall(); - if (searchThru(from_vertex, from_rf, edge, to_vertex, to_rf)) + 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, path_ap); + edge, arc, to_pin, to_vertex, to_rf, min_max); } return true; } bool PathVisitor::visitFromPath(const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - Path *from_path, - Edge *edge, - TimingArc *arc, - const Pin *to_pin, - Vertex *to_vertex, - const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + Vertex *from_vertex, + const RiseFall *from_rf, + Path *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const RiseFall *to_rf, + const MinMax *min_max) { const TimingRole *role = edge->role(); Tag *from_tag = from_path->tag(this); + Scene *scene = from_tag->scene(); + const Mode *mode = scene->mode(); + const Sdc *sdc = scene->sdc(); const ClkInfo *from_clk_info = from_tag->clkInfo(); Tag *to_tag = nullptr; const ClockEdge *clk_edge = from_clk_info->clkEdge(); const Clock *clk = from_clk_info->clock(); Arrival from_arrival = from_path->arrival(); ArcDelay arc_delay = 0.0; + 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) - && (variables_->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable()))) { - const Clock *gclk = from_tag->genClkSrcPathClk(this); + if (!sdc->clkStopPropagation(clk,from_pin,from_rf,to_pin,to_rf) + && (variables_->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable()))) { + const Clock *gclk = from_tag->genClkSrcPathClk(); if (gclk) { - Genclks *genclks = search_->genclks(); - VertexSet *fanins = genclks->fanins(gclk); - // Note: encountering a latch d->q edge means find the - // 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() - || !gclk->combinational()) - && fanins->hasKey(to_vertex) - && !(fdbk_edges && fdbk_edges->hasKey(edge))) { + 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 + // 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() + || !gclk->combinational()) + && fanins->contains(to_vertex) + && !fdbk_edges.contains(edge)) { arc_delay = search_->deratedDelay(from_vertex, arc, edge, - true, path_ap); - const PathAnalysisPt *path_ap_opp = - path_ap->corner()->findPathAnalysisPt(min_max->opposite()); + 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, path_ap_opp); + true, min_max, + dcalc_ap, + 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, true, + to_tag = search_->thruClkTag(from_path, from_vertex, from_tag, true, edge, to_rf, arc_delay_min_max_eq, - min_max, path_ap); + min_max, scene); + if (to_tag) to_arrival = from_arrival + arc_delay; - } + } } } } else if (role->genericRole() == TimingRole::regClkToQ()) { if (clk == nullptr - || !sdc_->clkStopPropagation(from_pin, clk)) { - arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, path_ap); + || !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. @@ -2285,30 +2223,30 @@ 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()) - // Do not propagate paths from input ports with default - // input arrival clk thru CLK->Q edges. - || (clk != sdc_->defaultArrivalClock() - // Only propagate paths from clocks that have not - // passed thru reg/latch D->Q edges. - && from_tag->isClock())) { - const RiseFall *clk_rf = clk_edge ? clk_edge->transition() : nullptr; - const ClkInfo *to_clk_info = from_clk_info; - if (from_clk_info->crprClkPath(this) == nullptr + && from_tag->isSegmentStart()) + // Do not propagate paths from input ports with default + // input arrival clk thru CLK->Q edges. + || (clk != sdc->defaultArrivalClock() + // Only propagate paths from clocks that have not + // passed thru reg/latch D->Q edges. + && from_tag->isClock())) { + const RiseFall *clk_rf = clk_edge ? clk_edge->transition() : nullptr; + 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, path_ap); - to_tag = search_->fromRegClkTag(from_pin, from_rf, clk, clk_rf, + 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, - path_ap); - if (to_tag) - to_tag = search_->thruTag(to_tag, edge, to_rf, min_max, path_ap, tag_cache_); + 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, - clk_edge, min_max, path_ap); - to_arrival = from_arrival + arc_delay; + clk_edge, min_max); + to_arrival = from_arrival + arc_delay; } else - to_tag = nullptr; + to_tag = nullptr; } } else if (edge->role() == TimingRole::latchDtoQ()) { @@ -2336,62 +2274,67 @@ PathVisitor::visitFromPath(const Pin *from_pin, } } if (!postponed) { - arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, path_ap); - latches_->latchOutArrival(from_path, arc, edge, path_ap, - 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, min_max, path_ap, tag_cache_); + to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_); } } } else if (from_tag->isClock()) { - ClockSet *clks = sdc_->findLeafPinClocks(from_pin); + ClockSet *clks = sdc->findLeafPinClocks(from_pin); // Disable edges from hierarchical clock source pins that do // not go thru the hierarchical pin and edges from clock source pins // that traverse a hierarchical source pin of a different clock. // Clock arrivals used as data also need to be disabled. if (!(role == TimingRole::wire() - && sdc_->clkDisabledByHpinThru(clk, from_pin, to_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->hasKey(const_cast(from_tag->clock())))) { + && !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())); + !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, path_ap); - const PathAnalysisPt *path_ap_opp = - path_ap->corner()->findPathAnalysisPt(min_max->opposite()); + 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, path_ap_opp); + 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, to_propagates_clk, edge, to_rf, arc_delay_min_max_eq, - min_max, path_ap); + min_max, scene); to_arrival = from_arrival + arc_delay; } } else { - if (!(sdc_->isPathDelayInternalFromBreak(to_pin) - || sdc_->isPathDelayInternalToBreak(from_pin))) { - to_tag = search_->thruTag(from_tag, edge, to_rf, min_max, path_ap, tag_cache_); - arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, path_ap); - if (!delayInf(arc_delay)) + if (!(sdc->isPathDelayInternalFromBreak(to_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; + to_tag = search_->thruTag(from_tag, edge, to_rf, tag_cache_); + } } } 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, path_ap); + edge, arc, arc_delay, + to_vertex, to_rf, to_tag, to_arrival, + min_max); else return true; } @@ -2401,18 +2344,17 @@ Search::clkPathArrival(const Path *clk_path) const { const ClkInfo *clk_info = clk_path->clkInfo(this); const ClockEdge *clk_edge = clk_info->clkEdge(); - const PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(this); - const MinMax *min_max = path_ap->pathMinMax(); - return clkPathArrival(clk_path, clk_info, clk_edge, min_max, path_ap); + const MinMax *min_max = clk_path->minMax(this); + return clkPathArrival(clk_path, clk_info, clk_edge, min_max); } Arrival Search::clkPathArrival(const Path *clk_path, - const ClkInfo *clk_info, - const ClockEdge *clk_edge, - const MinMax *min_max, - const PathAnalysisPt *path_ap) const + const ClkInfo *clk_info, + const ClockEdge *clk_edge, + const MinMax *min_max) const { + const Scene *scene = clk_path->scene(this); if (clk_path->vertex(this)->isRegClk() && clk_path->isClock(this) && clk_edge @@ -2420,10 +2362,9 @@ Search::clkPathArrival(const Path *clk_path, // 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, path_ap) + + clockInsertion(clk_edge->clock(), clk_info->clkSrc(), + clk_edge->transition(), min_max, + early_late, scene->mode()) + clk_info->latency(); } else @@ -2460,12 +2401,12 @@ Search::pathClkPathArrival1(const Path *path) const if (prev_edge) { const TimingRole *prev_role = prev_edge->role(); if (prev_role == TimingRole::regClkToQ() - || prev_role == TimingRole::latchEnToQ()) { - return p->prevPath(); + || prev_role == TimingRole::latchEnToQ()) { + return p->prevPath(); } else if (prev_role == TimingRole::latchDtoQ()) { - Path *enable_path = latches_->latchEnablePath(p, prev_edge); - return enable_path; + Path *enable_path = latches_->latchEnablePath(p, prev_edge); + return enable_path; } } p = prev_path; @@ -2479,17 +2420,19 @@ Search::pathClkPathArrival1(const Path *path) const // Return nullptr if a false path starts at pin/clk_edge. Tag * Search::fromUnclkedInputTag(const Pin *pin, - const RiseFall *rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap, - bool is_segment_start, - bool require_exception) + const RiseFall *rf, + const MinMax *min_max, + bool is_segment_start, + bool require_exception, + Scene *scene) { + Sdc *sdc = scene->sdc(); ExceptionStateSet *states = nullptr; - if (sdc_->exceptionFromStates(pin, rf, nullptr, nullptr, min_max, states) + if (sdc->exceptionFromStates(pin, rf, nullptr, nullptr, min_max, states) && (!require_exception || states)) { - const ClkInfo *clk_info = findClkInfo(nullptr, nullptr, false, 0.0, path_ap); - return findTag(rf, path_ap, clk_info, false, 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; @@ -2497,22 +2440,23 @@ Search::fromUnclkedInputTag(const Pin *pin, Tag * Search::fromRegClkTag(const Pin *from_pin, - const RiseFall *from_rf, - const Clock *clk, - const RiseFall *clk_rf, - const ClkInfo *clk_info, - const Pin *to_pin, - const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap) -{ + const RiseFall *from_rf, + const Clock *clk, + const RiseFall *clk_rf, + const ClkInfo *clk_info, + const Pin *to_pin, + const RiseFall *to_rf, + const MinMax *min_max, + Scene *scene) +{ + 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(to_rf, path_ap, clk_info, false, nullptr, false, states, - true, nullptr); + sdc->filterRegQStates(to_pin, to_rf, min_max, states); + return findTag(scene, to_rf, min_max, clk_info, false, nullptr, + false, states, true, nullptr); } else return nullptr; @@ -2521,20 +2465,22 @@ Search::fromRegClkTag(const Pin *from_pin, // Insert from_path as ClkInfo crpr_clk_path. const ClkInfo * Search::clkInfoWithCrprClkPath(const ClkInfo *from_clk_info, - Path *from_path, - const PathAnalysisPt *path_ap) -{ - if (crprActive()) - return findClkInfo(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(), - path_ap, from_path); + Path *from_path) +{ + 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(), + 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); else return from_clk_info; } @@ -2545,8 +2491,6 @@ Tag * Search::thruTag(Tag *from_tag, Edge *edge, const RiseFall *to_rf, - const MinMax *min_max, - const PathAnalysisPt *path_ap, TagSet *tag_cache) { const Pin *from_pin = edge->from(graph_)->pin(); @@ -2558,8 +2502,7 @@ Search::thruTag(Tag *from_tag, 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, min_max, path_ap, - tag_cache); + from_clk_info, nullptr, tag_cache); return to_tag; } @@ -2567,13 +2510,13 @@ Search::thruTag(Tag *from_tag, Tag * Search::thruClkTag(Path *from_path, Vertex *from_vertex, - Tag *from_tag, - bool to_propagates_clk, - Edge *edge, - const RiseFall *to_rf, + Tag *from_tag, + bool to_propagates_clk, + Edge *edge, + const RiseFall *to_rf, bool arc_delay_min_max_eq, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + const MinMax *min_max, + Scene *scene) { const Pin *from_pin = edge->from(graph_)->pin(); Vertex *to_vertex = edge->to(graph_); @@ -2584,32 +2527,35 @@ Search::thruClkTag(Path *from_path, 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())); + && 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, path_ap); + 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, min_max, path_ap, nullptr); + to_pin, to_rf, to_is_clk, to_is_reg_clk, false, + to_clk_info, nullptr, nullptr); return to_tag; } const ClkInfo * Search::thruClkInfo(Path *from_path, - Vertex *from_vertex, - const ClkInfo *from_clk_info, + Vertex *from_vertex, + const ClkInfo *from_clk_info, bool from_is_clk, - Edge *edge, - Vertex *to_vertex, - const Pin *to_pin, + Edge *edge, + Vertex *to_vertex, + const Pin *to_pin, bool to_is_clk, bool arc_delay_min_max_eq, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + const MinMax *min_max, + Scene *scene) { + const ClkInfo *to_clk_info = from_clk_info; + const Sdc *sdc = scene->sdc(); + const Mode *mode = scene->mode(); bool changed = false; const ClockEdge *from_clk_edge = from_clk_info->clkEdge(); const RiseFall *clk_rf = from_clk_edge->transition(); @@ -2617,7 +2563,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)) { + && sdc->isPropagatedClock(to_pin)) { to_clk_prop = true; changed = true; } @@ -2627,15 +2573,15 @@ Search::thruClkInfo(Path *from_path, // the clkinfo. const Pin *gen_clk_src = nullptr; if (from_clk_info->isGenClkSrcPath() - && crprActive() - && sdc_->isClock(to_pin)) { + && crprActive(mode) + && sdc->isClock(to_pin)) { // Don't care that it could be a regular clock root. gen_clk_src = to_pin; changed = true; } Path *to_crpr_clk_path = nullptr; - if (crprActive() + 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 @@ -2659,7 +2605,7 @@ Search::thruClkInfo(Path *from_path, changed = true; } else if (from_pulse_sense && - edge->timingArcSet()->sense() == TimingSense::negative_unate) { + edge->timingArcSet()->sense() == TimingSense::negative_unate) { to_pulse_sense = from_pulse_sense->opposite(); changed = true; } @@ -2669,8 +2615,8 @@ 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. @@ -2680,8 +2626,8 @@ 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; @@ -2689,24 +2635,27 @@ Search::thruClkInfo(Path *from_path, } } - ClockUncertainties *to_uncertainties = from_clk_info->uncertainties(); - ClockUncertainties *uncertainties = sdc_->clockUncertainties(to_pin); + const ClockUncertainties *to_uncertainties = from_clk_info->uncertainties(); + const ClockUncertainties *uncertainties = sdc->clockUncertainties(to_pin); if (uncertainties) { to_uncertainties = uncertainties; changed = true; } - if (changed) { - const ClkInfo *to_clk_info = findClkInfo(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, path_ap, - to_crpr_clk_path); - return to_clk_info; - } - return from_clk_info; + 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); + return to_clk_info; +} + +static size_t +tagsTableRfIndex(size_t tag_index, + const RiseFall *rf) +{ + return (tag_index / RiseFall::index_count) * RiseFall::index_count + rf->index(); } // Find the tag for a path going from from_tag thru edge to to_pin. @@ -2723,12 +2672,13 @@ Search::mutateTag(Tag *from_tag, bool to_is_segment_start, const ClkInfo *to_clk_info, InputDelay *to_input_delay, - const MinMax *min_max, - const PathAnalysisPt *path_ap, TagSet *tag_cache) { ExceptionStateSet *new_states = nullptr; ExceptionStateSet *from_states = from_tag->states(); + Scene *scene = from_tag->scene(); + Sdc *sdc = scene->sdc(); + const MinMax *min_max = from_tag->minMax(); if (from_states) { // Check for state changes in from_tag (but postpone copying state set). bool state_change = false; @@ -2738,7 +2688,7 @@ Search::mutateTag(Tag *from_tag, 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; + state_change = true; break; } if (state_change) @@ -2753,32 +2703,32 @@ Search::mutateTag(Tag *from_tag, // to_pin/edge completes a loop path. || (exception->isLoop() && state->isComplete())) - return nullptr; + return nullptr; // Kill path delay tags past the -to pin. if ((exception->isPathDelay() - && sdc_->isCompleteTo(state, to_pin, to_rf, min_max)) + && sdc->isCompleteTo(state, to_pin, to_rf, min_max)) // Kill loop tags at register clock pins. || (exception->isLoop() && to_is_reg_clk)) { - state_change = true; + state_change = true; break; } } // Get the set of -thru exceptions starting at to_pin/edge. - sdc_->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); + sdc->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); if (new_states || state_change) { // Second pass to apply state changes and add updated existing // states to new states. if (new_states == nullptr) - new_states = new ExceptionStateSet(); + new_states = new ExceptionStateSet(); 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_)) - // Found a -thru that we've been waiting for. - state = state->nextState(); + ExceptionPath *exception = state->exception(); + // One edge may traverse multiple hierarchical thru pins. + 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 @@ -2794,34 +2744,35 @@ Search::mutateTag(Tag *from_tag, } // Kill path delay tags past the -to pin. - if (!((exception->isPathDelay() - && sdc_->isCompleteTo(state, from_pin, from_rf, min_max)) + 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()))) - new_states->insert(state); + new_states->insert(state); } } } else // Get the set of -thru exceptions starting at to_pin/edge. - sdc_->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); + sdc->exceptionThruStates(from_pin, to_pin, to_rf, min_max, new_states); if (new_states) - return findTag(to_rf, path_ap, to_clk_info, to_is_clk, - from_tag->inputDelay(), to_is_segment_start, new_states, - true, tag_cache); + 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); else { // No state change. if (to_clk_info == from_clk_info - && to_rf == from_rf - && to_is_clk == from_is_clk - && from_tag->isSegmentStart() == to_is_segment_start - && from_tag->inputDelay() == to_input_delay) - return from_tag; + && 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(to_rf, path_ap, 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); } } @@ -2830,7 +2781,7 @@ Search::findTagGroup(TagGroupBldr *tag_bldr) { TagGroup probe(tag_bldr, this); LockGuard lock(tag_group_lock_); - TagGroup *tag_group = tag_group_set_->findKey(&probe); + TagGroup *tag_group = findKey(tag_group_set_, &probe); if (tag_group == nullptr) { TagGroupIndex tag_group_index; if (tag_group_free_indices_.empty()) @@ -2863,13 +2814,13 @@ Search::findTagGroup(TagGroupBldr *tag_bldr) void Search::setVertexArrivals(Vertex *vertex, - TagGroupBldr *tag_bldr) + TagGroupBldr *tag_bldr) { if (tag_bldr->empty()) deletePathsIncr(vertex); else { TagGroup *prev_tag_group = tagGroup(vertex); - Path *prev_paths = graph_->paths(vertex); + Path *prev_paths = vertex->paths(); TagGroup *tag_group = findTagGroup(tag_bldr); if (tag_group == prev_tag_group) { tag_bldr->copyPaths(tag_group, prev_paths); @@ -2877,19 +2828,19 @@ Search::setVertexArrivals(Vertex *vertex, } else { if (prev_tag_group) { - graph_->deletePaths(vertex); - prev_tag_group->decrRefCount(); + vertex->deletePaths(); + prev_tag_group->decrRefCount(); requiredInvalid(vertex); } size_t path_count = tag_group->pathCount(); - Path *paths = graph_->makePaths(vertex, path_count); + Path *paths = vertex->makePaths(path_count); tag_bldr->copyPaths(tag_group, paths); vertex->setTagGroupIndex(tag_group->index()); tag_group->incrRefCount(); } if (tag_group->hasFilterTag()) { LockGuard lock(filtered_arrivals_lock_); - filtered_arrivals_->insert(vertex); + filtered_arrivals_.insert(vertex); } } } @@ -2913,7 +2864,7 @@ class ReportPathLess public: ReportPathLess(const StaState *sta); bool operator()(const Path *path1, - const Path *path2) const; + const Path *path2) const; private: const StaState *sta_; @@ -2934,7 +2885,7 @@ ReportPathLess::operator()(const Path *path1, void Search::reportArrivals(Vertex *vertex, - bool report_tag_index) const + bool report_tag_index) const { report_->reportLine("Vertex %s", vertex->to_string(this).c_str()); TagGroup *tag_group = tagGroup(vertex); @@ -2947,32 +2898,35 @@ Search::reportArrivals(Vertex *vertex, const Path *path = path_iter.next(); paths.push_back(path); } - sort(paths.begin(), paths.end(), ReportPathLess(this)); + sort(paths, ReportPathLess(this)); for (const Path *path : paths) { const Tag *tag = path->tag(this); - const PathAnalysisPt *path_ap = tag->pathAnalysisPt(this); const RiseFall *rf = tag->transition(); const char *req = delayAsString(path->required(), this); + bool report_prev = false; std::string prev_str; - Path *prev_path = path->prevPath(); - if (prev_path) { - prev_str += prev_path->to_string(this); - prev_str += " "; - const Edge *prev_edge = path->prevEdge(this); - TimingArc *arc = path->prevArc(this); - prev_str += prev_edge->from(graph_)->to_string(this); - prev_str += " "; - prev_str += arc->fromEdge()->to_string(); - prev_str += " -> "; - prev_str += prev_edge->to(graph_)->to_string(this); - prev_str += " "; - prev_str += arc->toEdge()->to_string(); + if (report_prev) { + prev_str = "prev "; + Path *prev_path = path->prevPath(); + if (prev_path) { + prev_str += prev_path->to_string(this); + prev_str += " "; + const Edge *prev_edge = path->prevEdge(this); + TimingArc *arc = path->prevArc(this); + prev_str += prev_edge->from(graph_)->to_string(this); + prev_str += " "; + prev_str += arc->fromEdge()->to_string(); + prev_str += " -> "; + prev_str += prev_edge->to(graph_)->to_string(this); + prev_str += " "; + prev_str += arc->toEdge()->to_string(); + } + else + prev_str += "NULL"; } - else - prev_str = "NULL"; - report_->reportLine(" %s %s %s / %s %s prev %s", + report_->reportLine(" %s %s %s / %s %s%s", rf->to_string().c_str(), - path_ap->pathMinMax()->to_string().c_str(), + path->minMax(this)->to_string().c_str(), delayAsString(path->arrival(), this), req, tag->to_string(report_tag_index, false, this).c_str(), @@ -3031,7 +2985,7 @@ Search::reportTagGroups() const void Search::reportPathCountHistogram() const { - Vector vertex_counts(10); + std::vector vertex_counts(10); VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); @@ -3039,7 +2993,7 @@ Search::reportPathCountHistogram() const if (tag_group) { size_t path_count = tag_group->pathCount(); if (path_count >= vertex_counts.size()) - vertex_counts.resize(path_count * 2); + vertex_counts.resize(path_count * 2); vertex_counts[path_count]++; } } @@ -3066,8 +3020,9 @@ Search::tagCount() const } Tag * -Search::findTag(const RiseFall *rf, - const PathAnalysisPt *path_ap, +Search::findTag(Scene *scene, + const RiseFall *rf, + const MinMax *min_max, const ClkInfo *clk_info, bool is_clk, InputDelay *input_delay, @@ -3076,33 +3031,39 @@ Search::findTag(const RiseFall *rf, bool own_states, TagSet *tag_cache) { - Tag probe(0, rf->index(), path_ap->index(), clk_info, is_clk, input_delay, - is_segment_start, states, false, this); + Tag probe(scene, 0, rf, min_max, clk_info, is_clk, + input_delay, is_segment_start, states, false); if (tag_cache) { - Tag *tag = tag_cache->findKey(&probe); + Tag *tag = findKey(tag_cache, &probe); if (tag) return tag; } + LockGuard lock(tag_lock_); - Tag *tag = tag_set_->findKey(&probe); + Tag *tag = findKey(tag_set_, &probe); if (tag == nullptr) { - ExceptionStateSet *new_states = !own_states && states - ? new ExceptionStateSet(*states) : states; - TagIndex tag_index; - if (tag_free_indices_.empty()) - tag_index = tag_next_++; - else { - tag_index = tag_free_indices_.back(); - tag_free_indices_.pop_back(); + // 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; + 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); + own_states = false; + // Make sure tag can be indexed in tags_ before it is visible to + // other threads via tag_set_. + tags_[tagsTableRfIndex(tag_index, rf1)] = tag1; + tag_set_->insert(tag1); + if (tag_cache) + tag_cache->insert(tag1); + if (rf1 == rf) + tag = tag1; + + if (tag_next_ == tag_index_max) + report_->critical(1511, "max tag index exceeded"); } - tag = new Tag(tag_index, rf->index(), path_ap->index(), - clk_info, is_clk, input_delay, is_segment_start, - new_states, true, this); - own_states = false; - // Make sure tag can be indexed in tags_ before it is visible to - // other threads via tag_set_. - tags_[tag_index] = tag; - tag_set_->insert(tag); + // If tags_ needs to grow make the new array and copy the // contents into it before updating tags_ so that other threads // can use Search::tag(TagIndex) without returning gubbish. @@ -3115,15 +3076,9 @@ Search::findTag(const RiseFall *rf, tag_capacity_ = tag_capacity; tag_set_->reserve(tag_capacity); } - if (tag_next_ == tag_index_max) - report_->critical(1511, "max tag index exceeded"); } if (own_states) delete states; - - if (tag_cache) - tag_cache->insert(tag); - return tag; } @@ -3148,7 +3103,7 @@ Search::reportTags() const void Search::reportClkInfos() const { - 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); @@ -3159,42 +3114,46 @@ Search::reportClkInfos() const } const ClkInfo * -Search::findClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, +Search::findClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, const Pin *gen_clk_src, - bool gen_clk_src_path, - const RiseFall *pulse_clk_sense, - Arrival insertion, - float latency, - ClockUncertainties *uncertainties, - const PathAnalysisPt *path_ap, - Path *crpr_clk_path) -{ - ClkInfo probe(clk_edge, clk_src, is_propagated, gen_clk_src, gen_clk_src_path, - pulse_clk_sense, insertion, latency, uncertainties, - path_ap->index(), crpr_clk_path, this); + bool gen_clk_src_path, + const RiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + const ClockUncertainties *uncertainties, + const MinMax *min_max, + 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); LockGuard lock(clk_info_lock_); - const ClkInfo *clk_info = clk_info_set_->findKey(&probe); + const ClkInfo *clk_info = findKey(clk_info_set_, &probe); if (clk_info == nullptr) { - clk_info = new ClkInfo(clk_edge, clk_src, - is_propagated, gen_clk_src, gen_clk_src_path, - pulse_clk_sense, insertion, latency, uncertainties, - path_ap->index(), 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; } const ClkInfo * -Search::findClkInfo(const ClockEdge *clk_edge, - const Pin *clk_src, - bool is_propagated, - Arrival insertion, - const PathAnalysisPt *path_ap) +Search::findClkInfo(Scene *scene, + const ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + Arrival insertion, + const MinMax *min_max) { - return findClkInfo(clk_edge, clk_src, is_propagated, nullptr, false, nullptr, - insertion, 0.0, nullptr, path_ap, nullptr); + return findClkInfo(scene, clk_edge, clk_src, is_propagated, + nullptr, false, nullptr, + insertion, 0.0, nullptr, min_max, nullptr); } int @@ -3205,24 +3164,25 @@ Search::clkInfoCount() const ArcDelay Search::deratedDelay(const Vertex *from_vertex, - const TimingArc *arc, - const Edge *edge, - bool is_clk, - const PathAnalysisPt *path_ap) -{ - const DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt(); - DcalcAPIndex ap_index = dcalc_ap->index(); - float derate = timingDerate(from_vertex, arc, edge, is_clk, path_ap); - ArcDelay delay = graph_->arcDelay(edge, arc, ap_index); + const TimingArc *arc, + const Edge *edge, + bool is_clk, + const MinMax *min_max, + DcalcAPIndex dcalc_ap, + 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; } float Search::timingDerate(const Vertex *from_vertex, - const TimingArc *arc, - const Edge *edge, - bool is_clk, - const PathAnalysisPt *path_ap) + const TimingArc *arc, + const Edge *edge, + bool is_clk, + const Sdc *sdc, + const MinMax *min_max) { PathClkOrData derate_clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; @@ -3230,8 +3190,7 @@ Search::timingDerate(const Vertex *from_vertex, const Pin *pin = from_vertex->pin(); if (role->isWire()) { const RiseFall *rf = arc->toEdge()->asRiseFall(); - return sdc_->timingDerateNet(pin, derate_clk_data, rf, - path_ap->pathMinMax()); + return sdc->timingDerateNet(pin, derate_clk_data, rf, min_max); } else { TimingDerateCellType derate_type; @@ -3244,21 +3203,24 @@ Search::timingDerate(const Vertex *from_vertex, derate_type = TimingDerateCellType::cell_delay; rf = arc->fromEdge()->asRiseFall(); } - return sdc_->timingDerateInstance(pin, derate_type, derate_clk_data, rf, - path_ap->pathMinMax()); + return sdc->timingDerateInstance(pin, derate_type, derate_clk_data, + rf, min_max); } } ClockSet -Search::clockDomains(const Vertex *vertex) const +Search::clockDomains(const Vertex *vertex, + const Mode *mode) const + { ClockSet clks; - clockDomains(vertex, clks); + clockDomains(vertex, mode, clks); return clks; } void Search::clockDomains(const Vertex *vertex, + const Mode *mode, // Return value. ClockSet &clks) const { @@ -3266,78 +3228,63 @@ Search::clockDomains(const Vertex *vertex, while (path_iter.hasNext()) { Path *path = path_iter.next(); const Clock *clk = path->clock(this); - if (clk) + if (clk && path->mode(this) == mode) clks.insert(const_cast(clk)); } } ClockSet -Search::clockDomains(const Pin *pin) const +Search::clockDomains(const Pin *pin, + const Mode *mode) const { ClockSet clks; Vertex *vertex; Vertex *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) - clockDomains(vertex, clks); + clockDomains(vertex, mode, clks); if (bidirect_drvr_vertex) - clockDomains(bidirect_drvr_vertex, clks); + clockDomains(bidirect_drvr_vertex, mode, clks); return clks; } ClockSet -Search::clocks(const Vertex *vertex) const -{ - ClockSet clks; - clocks(vertex, clks); - return clks; -} - -void -Search::clocks(const Vertex *vertex, - // Return value. - ClockSet &clks) const -{ - VertexPathIterator path_iter(const_cast(vertex), this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - if (path->isClock(this)) - clks.insert(const_cast(path->clock(this))); - } -} - -ClockSet -Search::clocks(const Pin *pin) const +Search::clocks(const Pin *pin, + const Mode *mode) const { ClockSet clks; Vertex *vertex; Vertex *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) - clocks(vertex, clks); + clocks(vertex, mode, clks); if (bidirect_drvr_vertex) - clocks(bidirect_drvr_vertex, clks); + clocks(bidirect_drvr_vertex, mode, clks); return clks; } -bool -Search::isClock(const Vertex *vertex) const +ClockSet +Search::clocks(const Vertex *vertex, + const Mode *mode) const { - TagGroup *tag_group = tagGroup(vertex); - if (tag_group) - return tag_group->hasClkTag(); - else - return false; + ClockSet clks; + clocks(vertex, mode, clks); + return clks; } -bool -Search::isGenClkSrc(const Vertex *vertex) const +void +Search::clocks(const Vertex *vertex, + const Mode *mode, + // Return value. + ClockSet &clks) const { - TagGroup *tag_group = tagGroup(vertex); - if (tag_group) - return tag_group->hasGenClkSrcTag(); - else - return false; + VertexPathIterator path_iter(const_cast(vertex), this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + if (path->isClock(this) + && path->mode(this) == mode) + clks.insert(const_cast(path->clock(this))); + } } //////////////////////////////////////////////////////////////// @@ -3368,44 +3315,43 @@ void Search::seedRequireds() { ensureDownstreamClkPins(); - for (Vertex *vertex : *endpoints()) + for (Vertex *vertex : endpoints()) seedRequired(vertex); requireds_seeded_ = true; requireds_exist_ = true; } -VertexSet * +VertexSet & Search::endpoints() { - if (endpoints_ == nullptr) { - endpoints_ = new VertexSet(graph_); - invalid_endpoints_ = new VertexSet(graph_); + if (!endpoints_initialized_) { VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); if (isEndpoint(vertex)) { - debugPrint(debug_, "endpoint", 2, "insert %s", + debugPrint(debug_, "endpoint", 2, "insert %s", vertex->to_string(this).c_str()); - endpoints_->insert(vertex); + endpoints_.insert(vertex); } } + endpoints_initialized_ = true; } - if (invalid_endpoints_) { - for (Vertex *vertex : *invalid_endpoints_) { + if (!invalid_endpoints_.empty()) { + for (Vertex *vertex : invalid_endpoints_) { if (isEndpoint(vertex)) { - debugPrint(debug_, "endpoint", 2, "insert %s", + debugPrint(debug_, "endpoint", 2, "insert %s", vertex->to_string(this).c_str()); - endpoints_->insert(vertex); + endpoints_.insert(vertex); } else { - if (debug_->check("endpoint", 2) - && endpoints_->hasKey(vertex)) - report_->reportLine("endpoint: remove %s", + if (debug_->check("endpoint", 2) + && endpoints_.contains(vertex)) + report_->reportLine("endpoint: remove %s", vertex->to_string(this).c_str()); - endpoints_->erase(vertex); + endpoints_.erase(vertex); } } - invalid_endpoints_->clear(); + invalid_endpoints_.clear(); } return endpoints_; } @@ -3413,44 +3359,67 @@ Search::endpoints() void Search::endpointInvalid(Vertex *vertex) { - if (invalid_endpoints_) { - debugPrint(debug_, "endpoint", 2, "invalid %s", - vertex->to_string(this).c_str()); - invalid_endpoints_->insert(vertex); - } + debugPrint(debug_, "endpoint", 2, "invalid %s", + vertex->to_string(this).c_str()); + invalid_endpoints_.insert(vertex); } bool Search::isEndpoint(Vertex *vertex) const { - return isEndpoint(vertex, search_adj_); + for (const Mode *mode : modes_) { + if (isEndpoint(vertex, search_thru_, mode)) + return true; + } + return false; } bool Search::isEndpoint(Vertex *vertex, - SearchPred *pred) const + const ModeSeq &modes) const { - Pin *pin = vertex->pin(); - return hasFanin(vertex, pred, graph_) + for (const Mode *mode : modes) { + if (isEndpoint(vertex, search_thru_, mode)) + return true; + } + return false; +} + +bool +Search::isEndpoint(Vertex *vertex, + const Mode *mode) const +{ + return isEndpoint(vertex, search_thru_, mode); +} + +bool +Search::isEndpoint(Vertex *vertex, + SearchPred *pred, + const Mode *mode) const +{ + const Pin *pin = vertex->pin(); + const Sdc *sdc = mode->sdc(); + return hasFanin(vertex, pred, graph_, mode) && ((vertex->hasChecks() - && hasEnabledChecks(vertex)) - || (variables_->gatedClkChecksEnabled() - && gated_clk_->isGatedClkEnable(vertex)) - || vertex->isConstrained() - || sdc_->isPathDelayInternalTo(pin) - || !hasFanout(vertex, pred, graph_) - // Unconstrained paths at register clk pins. - || (unconstrained_paths_ - && vertex->isRegClk())); + && 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 -Search::hasEnabledChecks(Vertex *vertex) const +Search::hasEnabledChecks(Vertex *vertex, + const Mode *mode) const { VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - if (visit_path_ends_->checkEdgeEnabled(edge)) + if (visit_path_ends_->checkEdgeEnabled(edge, mode)) return true; } return false; @@ -3459,18 +3428,17 @@ Search::hasEnabledChecks(Vertex *vertex) const void Search::endpointsInvalid() { - delete endpoints_; - delete invalid_endpoints_; - endpoints_ = nullptr; - invalid_endpoints_ = nullptr; + endpoints_.clear(); + endpoints_initialized_ = false; + invalid_endpoints_.clear(); } void Search::seedInvalidRequireds() { - for (Vertex *vertex : *invalid_requireds_) + for (Vertex *vertex : invalid_requireds_) required_iter_->enqueue(vertex); - invalid_requireds_->clear(); + invalid_requireds_.clear(); } //////////////////////////////////////////////////////////////// @@ -3480,7 +3448,7 @@ class FindEndRequiredVisitor : public PathEndVisitor { public: FindEndRequiredVisitor(RequiredCmp *required_cmp, - const StaState *sta); + const StaState *sta); FindEndRequiredVisitor(const StaState *sta); virtual ~FindEndRequiredVisitor(); virtual PathEndVisitor *copy() const; @@ -3493,7 +3461,7 @@ class FindEndRequiredVisitor : public PathEndVisitor }; FindEndRequiredVisitor::FindEndRequiredVisitor(RequiredCmp *required_cmp, - const StaState *sta) : + const StaState *sta) : sta_(sta), required_cmp_(required_cmp), own_required_cmp_(false) @@ -3567,7 +3535,7 @@ RequiredCmp::RequiredCmp() : void RequiredCmp::requiredsInit(Vertex *vertex, - const StaState *sta) + const StaState *sta) { Search *search = sta->search(); TagGroup *tag_group = search->tagGroup(vertex); @@ -3575,8 +3543,7 @@ RequiredCmp::requiredsInit(Vertex *vertex, size_t path_count = tag_group->pathCount(); requireds_.resize(path_count); for (auto const [tag, path_index] : *tag_group->pathIndexMap()) { - PathAnalysisPt *path_ap = tag->pathAnalysisPt(sta); - const MinMax *min_max = path_ap->pathMinMax(); + const MinMax *min_max = tag->minMax(); requireds_[path_index] = delayInitValue(min_max->opposite()); } } @@ -3587,9 +3554,9 @@ RequiredCmp::requiredsInit(Vertex *vertex, void RequiredCmp::requiredSet(size_t path_index, - Required &required, - const MinMax *min_max, - const StaState *sta) + Required &required, + const MinMax *min_max, + const StaState *sta) { if (delayGreater(required, requireds_[path_index], min_max, sta)) { requireds_[path_index] = required; @@ -3599,7 +3566,7 @@ RequiredCmp::requiredSet(size_t path_index, bool RequiredCmp::requiredsSave(Vertex *vertex, - const StaState *sta) + const StaState *sta) { bool requireds_changed = false; Debug *debug = sta->debug(); @@ -3637,7 +3604,7 @@ RequiredVisitor::RequiredVisitor(const StaState *sta) : } RequiredVisitor::RequiredVisitor(bool make_tag_cache, - const StaState *sta) : + const StaState *sta) : PathVisitor(sta->search()->evalPred(), make_tag_cache, sta), required_cmp_(new RequiredCmp), visit_path_ends_(new VisitPathEnds(sta)) @@ -3678,20 +3645,19 @@ RequiredVisitor::visit(Vertex *vertex) bool RequiredVisitor::visitFromToPath(const Pin *, - Vertex * /* from_vertex */, - const RiseFall *from_rf, - Tag *from_tag, - Path *from_path, + Vertex * /* from_vertex */, + const RiseFall *from_rf, + Tag *from_tag, + Path *from_path, const Arrival &, - Edge *edge, - TimingArc *, - ArcDelay arc_delay, - Vertex *to_vertex, - const RiseFall *to_rf, - Tag *to_tag, - Arrival &, - const MinMax *min_max, - const PathAnalysisPt *path_ap) + Edge *edge, + TimingArc *, + ArcDelay arc_delay, + Vertex *to_vertex, + const RiseFall *to_rf, + Tag *to_tag, + Arrival &, + const MinMax *min_max) { // Don't propagate required times through latch D->Q edges. if (edge->role() != TimingRole::latchDtoQ()) { @@ -3724,30 +3690,31 @@ RequiredVisitor::visitFromToPath(const Pin *, } else { if (search_->crprApproxMissingRequireds()) { - // Arrival on to_vertex that differs by crpr_pin was pruned. - // Find an arrival that matches everything but the crpr_pin - // as an appromate required. - VertexPathIterator to_iter(to_vertex, to_rf, path_ap, this); - while (to_iter.hasNext()) { - Path *to_path = to_iter.next(); - 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; - debugPrint(debug_, "search", 3, " to tag %2u: %s", + // Arrival on to_vertex that differs by crpr_pin was pruned. + // Find an arrival that matches everything but the crpr_pin + // as an appromate required. + VertexPathIterator to_iter(to_vertex, from_path->scene(this), + from_path->minMax(this), to_rf, this); + while (to_iter.hasNext()) { + Path *to_path = to_iter.next(); + 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; + debugPrint(debug_, "search", 3, " to tag %2u: %s", 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, " %s - %s = %s %s %s", delayAsString(to_required, this), delayAsString(arc_delay, this), delayAsString(from_required, this), min_max == MinMax::max() ? "<" : ">", delayAsString(required_cmp_->required(path_index), this)); - required_cmp_->requiredSet(path_index, from_required, req_min, this); - break; - } - } + required_cmp_->requiredSet(path_index, from_required, req_min, this); + break; + } + } } } } @@ -3764,10 +3731,8 @@ Search::ensureDownstreamClkPins() // as having downstream clk pins. ClkTreeSearchPred pred(this); BfsBkwdIterator iter(BfsIndex::other, &pred, this); - for (Vertex *vertex : *graph_->regClkVertices()) { - if (!vertex->isConstant()) - iter.enqueue(vertex); - } + for (Vertex *vertex : graph_->regClkVertices()) + iter.enqueue(vertex); while (iter.hasNext()) { Vertex *vertex = iter.next(); @@ -3782,42 +3747,42 @@ Search::ensureDownstreamClkPins() bool Search::matchesFilter(Path *path, - const ClockEdge *to_clk_edge) + const ClockEdge *to_clk_edge) { - if (filter_ == nullptr + if (!have_filter_ && filter_from_ == nullptr && filter_to_ == nullptr) return true; - else if (filter_) { + else if (have_filter_) { // -from pins|inst // -thru // Path has to be tagged by traversing the filter exception points. ExceptionStateSet *states = path->tag(this)->states(); if (states) { for (auto state : *states) { - if (state->exception() == filter_ - && state->nextThru() == nullptr - && matchesFilterTo(path, to_clk_edge)) - return true; + 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()) { + && 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()->hasKey(const_cast(path_clk)) + 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_) + && filter_to_) // -to return matchesFilterTo(path, to_clk_edge); else { @@ -3826,14 +3791,14 @@ Search::matchesFilter(Path *path, } } -// Similar to Constraints::exceptionMatchesTo. +// Similar to Sdf::exceptionMatchesTo. bool Search::matchesFilterTo(Path *path, - const ClockEdge *to_clk_edge) const + const ClockEdge *to_clk_edge) const { return (filter_to_ == nullptr - || filter_to_->matchesFilter(path->pin(graph_), to_clk_edge, - path->transition(this), network_)); + || filter_to_->matchesFilter(path->pin(graph_), to_clk_edge, + path->transition(this), network_)); } //////////////////////////////////////////////////////////////// @@ -3842,13 +3807,14 @@ Search::matchesFilterTo(Path *path, // including exceptions that start at the end pin or target clock. ExceptionPath * Search::exceptionTo(ExceptionPathType type, - const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max, - bool match_min_max_exactly, - bool require_to_pin) const + const Path *path, + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin, + Sdc *sdc) const { // Find the highest priority exception carried by the path's tag. int hi_priority = -1; @@ -3859,22 +3825,22 @@ Search::exceptionTo(ExceptionPathType type, ExceptionPath *exception = state->exception(); int priority = exception->priority(min_max); 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 - || (priority == hi_priority - && exception->tighterThan(hi_priority_exception)))) { - hi_priority = priority; - hi_priority_exception = exception; + || 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 + || (priority == hi_priority + && exception->tighterThan(hi_priority_exception)))) { + hi_priority = priority; + hi_priority_exception = exception; } } } // 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, - hi_priority_exception, hi_priority); + sdc->exceptionTo(type, pin, rf, clk_edge, min_max, + match_min_max_exactly, + hi_priority_exception, hi_priority); return hi_priority_exception; } @@ -3888,21 +3854,22 @@ Search::groupPathsTo(const PathEnd *path_end) const const Path *path = path_end->path(); const Pin *pin = path->pin(this); const Tag *tag = path->tag(this); + Sdc *sdc = tag->scene()->sdc(); const RiseFall *rf = tag->transition(); const ClockEdge *clk_edge = path_end->targetClkEdge(this); - const MinMax *min_max = tag->minMax(this); + const MinMax *min_max = tag->minMax(); const ExceptionStateSet *states = path->tag(this)->states(); if (states) { for (auto state : *states) { ExceptionPath *exception = state->exception(); if (exception->isGroupPath() - && sdc_->exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, - false, false)) - group_paths.push_back(exception); + && sdc->exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, + false, false)) + group_paths.push_back(exception); } } // Check for group_path -to exceptions originating at the end pin or target clock. - sdc_->groupPathsTo(pin, rf, clk_edge, min_max, group_paths); + sdc->groupPathsTo(pin, rf, clk_edge, min_max, group_paths); return group_paths; } @@ -3913,9 +3880,9 @@ Search::totalNegativeSlack(const MinMax *min_max) { tnsPreamble(); Slack tns = 0.0; - for (Corner *corner : *corners_) { - PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); - Slack tns1 = tns_[path_ap_index]; + for (Scene *scene : scenes_) { + size_t path_index = scene->pathIndex(min_max); + Slack tns1 = tns_[path_index]; if (delayLess(tns1, tns, this)) tns = tns1; } @@ -3923,11 +3890,11 @@ Search::totalNegativeSlack(const MinMax *min_max) } Slack -Search::totalNegativeSlack(const Corner *corner, - const MinMax *min_max) +Search::totalNegativeSlack(const Scene *scene, + const MinMax *min_max) { tnsPreamble(); - PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + PathAPIndex path_ap_index = scene->pathIndex(min_max); return tns_[path_ap_index]; } @@ -3935,9 +3902,9 @@ void Search::tnsPreamble() { wnsTnsPreamble(); - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - tns_.resize(path_ap_count); - tns_slacks_.resize(path_ap_count); + size_t path_count = scenePathCount(); + tns_.resize(path_count); + tns_slacks_.resize(path_count); if (tns_exists_) updateInvalidTns(); else @@ -3952,44 +3919,44 @@ Search::tnsInvalid(Vertex *vertex) debugPrint(debug_, "tns", 2, "tns invalid %s", vertex->to_string(this).c_str()); LockGuard lock(tns_lock_); - invalid_tns_->insert(vertex); + invalid_tns_.insert(vertex); } } void Search::updateInvalidTns() { - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - for (Vertex *vertex : *invalid_tns_) { + size_t path_count = scenePathCount(); + 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()); - SlackSeq slacks(path_ap_count); + SlackSeq slacks(path_count); wnsSlacks(vertex, slacks); if (tns_exists_) - updateTns(vertex, slacks); + updateTns(vertex, slacks); if (worst_slacks_) - worst_slacks_->updateWorstSlacks(vertex, slacks); + worst_slacks_->updateWorstSlacks(vertex, slacks); } } - invalid_tns_->clear(); + invalid_tns_.clear(); } void Search::findTotalNegativeSlacks() { - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - for (PathAPIndex i = 0; i < path_ap_count; i++) { + size_t path_count = scenePathCount(); + for (size_t i = 0; i < path_count; i++) { tns_[i] = 0.0; tns_slacks_[i].clear(); } - for (Vertex *vertex : *endpoints()) { + for (Vertex *vertex : endpoints()) { // No locking required. - SlackSeq slacks(path_ap_count); + SlackSeq slacks(path_count); wnsSlacks(vertex, slacks); - for (PathAPIndex i = 0; i < path_ap_count; i++) + for (size_t i = 0; i < path_count; i++) tnsIncr(vertex, slacks[i], i); } tns_exists_ = true; @@ -3997,10 +3964,10 @@ Search::findTotalNegativeSlacks() void Search::updateTns(Vertex *vertex, - SlackSeq &slacks) + SlackSeq &slacks) { - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - for (PathAPIndex i = 0; i < path_ap_count; i++) { + size_t path_count = scenePathCount(); + for (size_t i = 0; i < path_count; i++) { tnsDecr(vertex, i); tnsIncr(vertex, slacks[i], i); } @@ -4008,15 +3975,15 @@ Search::updateTns(Vertex *vertex, void Search::tnsIncr(Vertex *vertex, - Slack slack, - PathAPIndex path_ap_index) + Slack slack, + PathAPIndex path_ap_index) { if (delayLess(slack, 0.0, this)) { debugPrint(debug_, "tns", 3, "tns+ %s %s", delayAsString(slack, this), vertex->to_string(this).c_str()); tns_[path_ap_index] += slack; - if (tns_slacks_[path_ap_index].hasKey(vertex)) + if (tns_slacks_[path_ap_index].contains(vertex)) report_->critical(1513, "tns incr existing vertex"); tns_slacks_[path_ap_index][vertex] = slack; } @@ -4024,11 +3991,11 @@ Search::tnsIncr(Vertex *vertex, void Search::tnsDecr(Vertex *vertex, - PathAPIndex path_ap_index) + PathAPIndex path_ap_index) { Slack slack; bool found; - tns_slacks_[path_ap_index].findKey(vertex, slack, found); + findKeyValue(tns_slacks_[path_ap_index], vertex, slack, found); if (found && delayLess(slack, 0.0, this)) { debugPrint(debug_, "tns", 3, "tns- %s %s", @@ -4045,8 +4012,8 @@ Search::tnsNotifyBefore(Vertex *vertex) { if (tns_exists_ && isEndpoint(vertex)) { - int ap_count = corners_->pathAnalysisPtCount(); - for (int i = 0; i < ap_count; i++) { + size_t path_count = scenePathCount(); + for (size_t i = 0; i < path_count; i++) { tnsDecr(vertex, i); } } @@ -4056,23 +4023,23 @@ Search::tnsNotifyBefore(Vertex *vertex) void Search::worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { worstSlackPreamble(); worst_slacks_->worstSlack(min_max, worst_slack, worst_vertex); } void -Search::worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) +Search::worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { worstSlackPreamble(); - worst_slacks_->worstSlack(corner, min_max, worst_slack, worst_vertex); + worst_slacks_->worstSlack(scene, min_max, worst_slack, worst_vertex); } void @@ -4091,19 +4058,19 @@ 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()); if (isEndpoint(vertex)) { - seedRequired(vertex); - // If the endpoint has fanout it's required time - // depends on downstream checks, so enqueue it to - // force required propagation to it's level if - // the required time is requested later. - if (hasFanout(vertex, search_adj_, graph_)) - required_iter_->enqueue(vertex); - itr = invalid_requireds_->erase(itr); + seedRequired(vertex); + // If the endpoint has fanout it's required time + // depends on downstream checks, so enqueue it to + // force required propagation to it's level if + // the required time is requested later. + if (vertex->hasFanout()) + required_iter_->enqueue(vertex); + itr = invalid_requireds_.erase(itr); } else itr++; @@ -4129,7 +4096,7 @@ class FindEndSlackVisitor : public PathEndVisitor { public: FindEndSlackVisitor(SlackSeq &slacks, - const StaState *sta); + const StaState *sta); FindEndSlackVisitor(const FindEndSlackVisitor &) = default; virtual PathEndVisitor *copy() const; virtual void visit(PathEnd *path_end); @@ -4140,7 +4107,7 @@ class FindEndSlackVisitor : public PathEndVisitor }; FindEndSlackVisitor::FindEndSlackVisitor(SlackSeq &slacks, - const StaState *sta) : + const StaState *sta) : slacks_(slacks), sta_(sta) { @@ -4166,14 +4133,14 @@ FindEndSlackVisitor::visit(PathEnd *path_end) void Search::wnsSlacks(Vertex *vertex, - // Return values. - SlackSeq &slacks) + // Return values. + SlackSeq &slacks) { Slack slack_init = MinMax::min()->initValue(); - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - for (PathAPIndex i = 0; i < path_ap_count; i++) + size_t path_count = scenePathCount(); + for (size_t i = 0; i < path_count; i++) slacks[i] = slack_init; - if (hasFanout(vertex, search_adj_, graph_)) { + if (vertex->hasFanout()) { // If the vertex has fanout the path slacks include downstream // PathEnd slacks so find the endpoint slack directly. FindEndSlackVisitor end_visitor(slacks, this); @@ -4186,64 +4153,20 @@ Search::wnsSlacks(Vertex *vertex, PathAPIndex path_ap_index = path->pathAnalysisPtIndex(this); const Slack path_slack = path->slack(this); if (!path->tag(this)->isFilter() - && delayLess(path_slack, slacks[path_ap_index], this)) - slacks[path_ap_index] = path_slack; + && delayLess(path_slack, slacks[path_ap_index], this)) + slacks[path_ap_index] = path_slack; } } } Slack Search::wnsSlack(Vertex *vertex, - PathAPIndex path_ap_index) + PathAPIndex path_ap_index) { - PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); - SlackSeq slacks(path_ap_count); + size_t path_count = scenePathCount(); + SlackSeq slacks(path_count); wnsSlacks(vertex, slacks); return slacks[path_ap_index]; } -//////////////////////////////////////////////////////////////// - -void -Search::deletePathGroups() -{ - delete path_groups_; - path_groups_ = nullptr; -} - -PathGroupSeq -Search::pathGroups(const PathEnd *path_end) const -{ - if (path_groups_) - return path_groups_->pathGroups(path_end); - else - return PathGroupSeq(); -} - -bool -Search::havePathGroups() const -{ - return path_groups_ != nullptr; -} - -PathGroup * -Search::findPathGroup(const char *name, - const MinMax *min_max) const -{ - if (path_groups_) - return path_groups_->findPathGroup(name, min_max); - else - return nullptr; -} - -PathGroup * -Search::findPathGroup(const Clock *clk, - const MinMax *min_max) const -{ - if (path_groups_) - return path_groups_->findPathGroup(clk, min_max); - else - return nullptr; -} - } // namespace diff --git a/search/Search.i b/search/Search.i index 3e2e761aa..8978d5301 100644 --- a/search/Search.i +++ b/search/Search.i @@ -24,8 +24,12 @@ %module search +%include "std_string.i" + %{ +#include + #include "Units.hh" #include "PathGroup.hh" #include "Search.hh" @@ -33,6 +37,7 @@ #include "search/ReportPath.hh" #include "PathExpanded.hh" #include "Bfs.hh" +#include "Scene.hh" #include "Sta.hh" using namespace sta; @@ -67,36 +72,10 @@ private: ~PathEnd(); }; -class MinPulseWidthCheck -{ -private: - MinPulseWidthCheck(); - ~MinPulseWidthCheck(); -}; - -class MinPulseWidthCheckSeq -{ -private: - MinPulseWidthCheckSeq(); - ~MinPulseWidthCheckSeq(); -}; - -class MinPulseWidthCheckSeqIterator -{ -private: - MinPulseWidthCheckSeqIterator(); - ~MinPulseWidthCheckSeqIterator(); -}; - -class Corner -{ -private: - Corner(); - ~Corner(); -}; - %inline %{ +using std::string; + int group_path_count_max = PathGroup::group_path_count_max; //////////////////////////////////////////////////////////////// @@ -158,12 +137,6 @@ arrivals_invalid() sta->arrivalsInvalid(); } -PinSet -startpoints() -{ - return Sta::sta()->startpointPins(); -} - PinSet endpoints() { @@ -171,7 +144,7 @@ endpoints() } size_t -endpoint_path_count() +endpoint_count() { return Sta::sta()->endpointPins().size(); } @@ -189,10 +162,10 @@ total_negative_slack_cmd(const MinMax *min_max) } Slack -total_negative_slack_corner_cmd(const Corner *corner, - const MinMax *min_max) +total_negative_slack_scene_cmd(const Scene *scene, + const MinMax *min_max) { - return Sta::sta()->totalNegativeSlack(corner, min_max); + return Sta::sta()->totalNegativeSlack(scene, min_max); } Slack @@ -214,18 +187,18 @@ worst_slack_vertex(const MinMax *min_max) } Slack -worst_slack_corner(const Corner *corner, - const MinMax *min_max) +worst_slack_scene(const Scene *scene, + const MinMax *min_max) { Slack worst_slack; Vertex *worst_vertex; - Sta::sta()->worstSlack(corner, min_max, worst_slack, worst_vertex); + Sta::sta()->worstSlack(scene, min_max, worst_slack, worst_vertex); return worst_slack; } Path * vertex_worst_arrival_path(Vertex *vertex, - const MinMax *min_max) + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); @@ -234,8 +207,8 @@ vertex_worst_arrival_path(Vertex *vertex, Path * vertex_worst_arrival_path_rf(Vertex *vertex, - const RiseFall *rf, - MinMax *min_max) + const RiseFall *rf, + MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); @@ -244,7 +217,7 @@ vertex_worst_arrival_path_rf(Vertex *vertex, Path * vertex_worst_slack_path(Vertex *vertex, - const MinMax *min_max) + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); @@ -253,18 +226,18 @@ vertex_worst_slack_path(Vertex *vertex, Slack endpoint_slack(const Pin *pin, - const char *path_group_name, - const MinMax *min_max) + const char *path_group_name, + const MinMax *min_max) { Sta *sta = Sta::sta(); sta->ensureLibLinked(); - if (sta->isGroupPathName(path_group_name)) { + 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)); } else { sta->report()->error(1577, "%s is not a known path group name.", - path_group_name); + path_group_name); return INF; } } @@ -273,7 +246,7 @@ StdStringSeq path_group_names() { Sta *sta = Sta::sta(); - return sta->pathGroupNames(); + return sta->pathGroupNames(sta->cmdSdc()); } int @@ -290,7 +263,7 @@ report_tag_groups() void report_tag_arrivals_cmd(Vertex *vertex, - bool report_tag_index) + bool report_tag_index) { Sta::sta()->search()->reportArrivals(vertex, report_tag_index); } @@ -351,7 +324,9 @@ report_loops() char pin_sim_logic_value(const Pin *pin) { - return logicValueString(Sta::sta()->simLogicValue(pin)); + Sta *sta = Sta::sta(); + const Mode *sdc = sta->cmdMode(); + return logicValueString(sta->simLogicValue(pin, sdc)); } InstanceSeq @@ -364,38 +339,36 @@ slow_drivers(int count) PathEndSeq find_path_ends(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool unconstrained, - Corner *corner, - const MinMaxAll *delay_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, - PathGroupNameSet *groups, - bool setup, - bool hold, - bool recovery, - bool removal, - bool clk_gating_setup, - bool clk_gating_hold) + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + SceneSeq scenes, + const MinMaxAll *delay_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, + StdStringSeq path_groups, + bool setup, + bool hold, + bool recovery, + bool removal, + bool clk_gating_setup, + bool clk_gating_hold) { Sta *sta = Sta::sta(); PathEndSeq ends = sta->findPathEnds(from, thrus, to, unconstrained, - corner, delay_min_max, + scenes, delay_min_max, group_path_count, endpoint_path_count, - unique_pins, unique_edges, + unique_pins, unique_edges, slack_min, slack_max, - sort_by_slack, - groups->size() ? groups : nullptr, + sort_by_slack, path_groups, setup, hold, recovery, removal, clk_gating_setup, clk_gating_hold); - delete groups; return ends; } @@ -421,7 +394,7 @@ report_path_end(PathEnd *end) void report_path_end2(PathEnd *end, - PathEnd *prev_end, + PathEnd *prev_end, bool last) { Sta::sta()->reportPathEnd(end, prev_end, last); @@ -443,26 +416,26 @@ set_report_path_field_order(StringSeq *field_names) void set_report_path_fields(bool report_input_pin, bool report_hier_pins, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout, - bool report_src_attr) + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout, + bool report_src_attr) { Sta::sta()->setReportPathFields(report_input_pin, report_hier_pins, - report_net, - report_cap, - report_slew, - report_fanout, - report_src_attr); + report_net, + report_cap, + report_slew, + report_fanout, + report_src_attr); } void set_report_path_field_properties(const char *field_name, - const char *title, - int width, - bool left_justify) + const char *title, + int width, + bool left_justify) { Sta *sta = Sta::sta(); ReportField *field = sta->findReportPathField(field_name); @@ -474,7 +447,7 @@ set_report_path_field_properties(const char *field_name, void set_report_path_field_width(const char *field_name, - int width) + int width) { Sta *sta = Sta::sta(); ReportField *field = sta->findReportPathField(field_name); @@ -518,130 +491,95 @@ report_path_ends(PathEndSeq *ends) //////////////////////////////////////////////////////////////// void -report_clk_skew(ConstClockSeq clks, - const Corner *corner, - const SetupHold *setup_hold, - bool include_internal_latency, - int digits) +report_arrival_wrt_clks(const Pin *pin, + const Scene *scene, + int digits) { - Sta::sta()->reportClkSkew(clks, corner, setup_hold, - include_internal_latency, digits); + Sta::sta()->reportArrivalWrtClks(pin, scene, digits); } void -report_clk_latency(ConstClockSeq clks, - const Corner *corner, - bool include_internal_latency, - int digits) +report_required_wrt_clks(const Pin *pin, + const Scene *scene, + int digits) { - Sta::sta()->reportClkLatency(clks, corner, include_internal_latency, digits); + Sta::sta()->reportRequiredWrtClks(pin, scene, digits); } -float -worst_clk_skew_cmd(const SetupHold *setup_hold, - bool include_internal_latency) +void +report_slack_wrt_clks(const Pin *pin, + const Scene *scene, + int digits) { - return Sta::sta()->findWorstClkSkew(setup_hold, include_internal_latency); + Sta::sta()->reportSlackWrtClks(pin, scene, digits); } //////////////////////////////////////////////////////////////// -MinPulseWidthCheckSeq & -min_pulse_width_violations(const Corner *corner) -{ - return Sta::sta()->minPulseWidthViolations(corner); -} - -MinPulseWidthCheckSeq & -min_pulse_width_check_pins(PinSeq *pins, - const Corner *corner) -{ - Sta *sta = Sta::sta(); - MinPulseWidthCheckSeq &checks = sta->minPulseWidthChecks(pins, corner); - delete pins; - return checks; -} - -MinPulseWidthCheckSeq & -min_pulse_width_checks(const Corner *corner) -{ - return Sta::sta()->minPulseWidthChecks(corner); -} - -MinPulseWidthCheck * -min_pulse_width_check_slack(const Corner *corner) -{ - return Sta::sta()->minPulseWidthSlack(corner); -} - void -report_mpw_checks(MinPulseWidthCheckSeq *checks, - bool verbose) +report_clk_skew(ConstClockSeq clks, + const SceneSeq scenes, + const SetupHold *setup_hold, + bool include_internal_latency, + int digits) { - Sta::sta()->reportMpwChecks(checks, verbose); + Sta::sta()->reportClkSkew(clks, scenes, setup_hold, + include_internal_latency, digits); } -void -report_mpw_check(MinPulseWidthCheck *check, - bool verbose) +float +worst_clk_skew_cmd(const SetupHold *setup_hold, + bool include_internal_latency) { - Sta::sta()->reportMpwCheck(check, verbose); + return Sta::sta()->findWorstClkSkew(setup_hold, include_internal_latency); } //////////////////////////////////////////////////////////////// -MinPeriodCheckSeq & -min_period_violations() -{ - return Sta::sta()->minPeriodViolations(); -} - -MinPeriodCheck * -min_period_check_slack() -{ - return Sta::sta()->minPeriodSlack(); -} - void -report_min_period_checks(MinPeriodCheckSeq *checks, - bool verbose) -{ - Sta::sta()->reportChecks(checks, verbose); -} - -void -report_min_period_check(MinPeriodCheck *check, - bool verbose) +report_clk_latency(ConstClockSeq clks, + const SceneSeq scenes, + bool include_internal_latency, + int digits) { - Sta::sta()->reportCheck(check, verbose); + Sta::sta()->reportClkLatency(clks, scenes, include_internal_latency, digits); } //////////////////////////////////////////////////////////////// -MaxSkewCheckSeq & -max_skew_violations() +void +report_min_pulse_width_checks(const Net *net, + size_t max_count, + bool violations, + bool verbose, + const SceneSeq scenes) { - return Sta::sta()->maxSkewViolations(); + return Sta::sta()->reportMinPulseWidthChecks(net, max_count, violations, + verbose, scenes); } -MaxSkewCheck * -max_skew_check_slack() -{ - return Sta::sta()->maxSkewSlack(); -} +//////////////////////////////////////////////////////////////// void -report_max_skew_checks(MaxSkewCheckSeq *checks, - bool verbose) +report_min_period_checks(const Net *net, + size_t max_count, + bool violations, + bool verbose, + const SceneSeq scenes) { - Sta::sta()->reportChecks(checks, verbose); + Sta::sta()->reportMinPeriodChecks(net, max_count, violations, verbose, scenes); } +//////////////////////////////////////////////////////////////// + void -report_max_skew_check(MaxSkewCheck *check, - bool verbose) +report_max_skew_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes) { - Sta::sta()->reportCheck(check, verbose); + Sta::sta()->reportMaxSkewChecks(net, max_count, violators, verbose, scenes); } //////////////////////////////////////////////////////////////// @@ -655,19 +593,22 @@ find_clk_min_period(const Clock *clk, //////////////////////////////////////////////////////////////// -PinSeq -check_slew_limits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) +void +report_slew_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes, + const MinMax *min_max) { - return Sta::sta()->checkSlewLimits(net, violators, corner, min_max); + return Sta::sta()->reportSlewChecks(net, max_count, violators, verbose, + scenes, min_max); } size_t max_slew_violation_count() { - return Sta::sta()->checkSlewLimits(nullptr, true, nullptr, MinMax::max()).size(); + return Sta::sta()->maxSlewViolationCount(); } float @@ -694,103 +635,82 @@ max_slew_check_limit() return sta->units()->timeUnit()->staToUser(limit); } -void -report_slew_limit_short_header() -{ - Sta::sta()->reportSlewLimitShortHeader(); -} - -void -report_slew_limit_short(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - Sta::sta()->reportSlewLimitShort(pin, corner, min_max); -} - -void -report_slew_limit_verbose(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - Sta::sta()->reportSlewLimitVerbose(pin, corner, min_max); -} - //////////////////////////////////////////////////////////////// -PinSeq -check_fanout_limits(Net *net, - bool violators, - const MinMax *min_max) +void +report_fanout_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes, + const MinMax *min_max) { - return Sta::sta()->checkFanoutLimits(net, violators, min_max); + Sta *sta = Sta::sta(); + return sta->reportFanoutChecks(net, max_count, violators, verbose, + scenes, min_max); } size_t max_fanout_violation_count() { - return Sta::sta()->checkFanoutLimits(nullptr, true, MinMax::max()).size(); + Sta *sta = Sta::sta(); + return sta->fanoutViolationCount(MinMax::max(), sta->modes()); } float -max_fanout_check_slack() +max_fanout_min_slack() { Sta *sta = Sta::sta(); const Pin *pin; - float fanout; - float slack; - float limit; - sta->maxFanoutCheck(pin, fanout, slack, limit); + float fanout, limit, slack; + const Mode *mode; + sta->maxFanoutMinSlackPin(sta->modes(), pin, fanout, limit, slack, mode); return slack;; } +// Deprecated 11/16/2025 float -max_fanout_check_limit() -{ - Sta *sta = Sta::sta(); - const Pin *pin; - float fanout; - float slack; - float limit; - sta->maxFanoutCheck(pin, fanout, slack, limit); - return limit;; -} - -void -report_fanout_limit_short_header() +max_fanout_check_slack() { - Sta::sta()->reportFanoutLimitShortHeader(); + return max_fanout_min_slack(); } -void -report_fanout_limit_short(Pin *pin, - const MinMax *min_max) +float +max_fanout_min_slack_limit() { - Sta::sta()->reportFanoutLimitShort(pin, min_max); + Sta *sta = Sta::sta(); + const Pin *pin; + float fanout, limit, slack; + const Mode *mode; + sta->maxFanoutMinSlackPin(sta->modes(), pin, fanout, limit, slack, mode); + return limit;; } -void -report_fanout_limit_verbose(Pin *pin, - const MinMax *min_max) +// Deprecated 11/16/2025 +float +max_fanout_check_limit() { - Sta::sta()->reportFanoutLimitVerbose(pin, min_max); + return max_fanout_min_slack_limit(); } //////////////////////////////////////////////////////////////// -PinSeq -check_capacitance_limits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) +void +report_capacitance_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes, + const MinMax *min_max) { - return Sta::sta()->checkCapacitanceLimits(net, violators, corner, min_max); + Sta::sta()->reportCapacitanceChecks(net, max_count, violators, verbose, + scenes, min_max); } size_t max_capacitance_violation_count() { - return Sta::sta()->checkCapacitanceLimits(nullptr, true,nullptr,MinMax::max()).size(); + return Sta::sta()->maxCapacitanceViolationCount(); } float @@ -817,162 +737,206 @@ max_capacitance_check_limit() return sta->units()->capacitanceUnit()->staToUser(limit); } +//////////////////////////////////////////////////////////////// + void -report_capacitance_limit_short_header() +write_timing_model_cmd(const char *lib_name, + const char *cell_name, + const char *filename, + const Scene *scene) { - Sta::sta()->reportCapacitanceLimitShortHeader(); + Sta::sta()->writeTimingModel(lib_name, cell_name, filename, scene); } +//////////////////////////////////////////////////////////////// + void -report_capacitance_limit_short(Pin *pin, - const Corner *corner, - const MinMax *min_max) +define_scene_cmd(const char *name, + const char *mode_name, + const StdStringSeq liberty_min_files, + const StdStringSeq liberty_max_files, + const char *spef_min_file, + const char *spef_max_file) { - Sta::sta()->reportCapacitanceLimitShort(pin, corner, min_max); + Sta *sta = Sta::sta(); + sta->makeScene(name, mode_name, + liberty_min_files, liberty_max_files, + spef_min_file, spef_max_file); } void -report_capacitance_limit_verbose(Pin *pin, - const Corner *corner, - const MinMax *min_max) +define_scenes_cmd(StringSeq *scene_names) { - Sta::sta()->reportCapacitanceLimitVerbose(pin, corner, min_max); + Sta *sta = Sta::sta(); + sta->makeScenes(scene_names); + delete scene_names; } -//////////////////////////////////////////////////////////////// +Scene * +cmd_scene() +{ + return Sta::sta()->cmdScene(); +} void -write_timing_model_cmd(const char *lib_name, - const char *cell_name, - const char *filename, - const Corner *corner) +set_cmd_scene(Scene *scene) { - Sta::sta()->writeTimingModel(lib_name, cell_name, filename, corner); + Sta::sta()->setCmdScene(scene); } -//////////////////////////////////////////////////////////////// - -void -define_corners_cmd(StringSet *corner_names) +const SceneSeq +scenes() { Sta *sta = Sta::sta(); - sta->makeCorners(corner_names); - delete corner_names; + return sta->scenes(); } -Corner * -cmd_corner() +Scene * +find_scene(const char *scene_name) { - return Sta::sta()->cmdCorner(); + return Sta::sta()->findScene(scene_name); } -void -set_cmd_corner(Corner *corner) +SceneSeq +find_scenes_matching(std::string scene_name) { - Sta::sta()->setCmdCorner(corner); + return Sta::sta()->findScenes(scene_name); } -Corner * -find_corner(const char *corner_name) +SceneSeq +find_mode_scenes_matching(std::string scene_name, + ModeSeq modes) { - return Sta::sta()->findCorner(corner_name); + return Sta::sta()->findScenes(scene_name, modes); } -Corners * -corners() +bool +multi_scene() { - return Sta::sta()->corners(); + return Sta::sta()->multiScene(); } -bool -multi_corner() +ClockSeq +get_scene_clocks(SceneSeq scenes) { - return Sta::sta()->multiCorner(); + ClockSeq clks; + ModeSet modes = Scene::modeSet(scenes); + for (const Mode *mode : modes) { + for (Clock *clk : mode->sdc()->clocks()) + clks.push_back(clk); + } + return clks; +} + +//////////////////////////////////////////////////////////////// + +std::string +cmd_mode_name() +{ + return Sta::sta()->cmdMode()->name(); +} + +void +set_mode_cmd(std::string mode_name) +{ + Sta::sta()->setCmdMode(mode_name); +} + +ModeSeq +find_modes(std::string mode_name) +{ + return Sta::sta()->findModes(mode_name); } //////////////////////////////////////////////////////////////// CheckErrorSeq & check_timing_cmd(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) { - return Sta::sta()->checkTiming(no_input_delay, no_output_delay, - reg_multiple_clks, reg_no_clks, - unconstrained_endpoints, - loops, generated_clks); + Sta *sta = Sta::sta(); + const Mode *sdc = sta->cmdMode(); + return sta->checkTiming(sdc, no_input_delay, no_output_delay, + reg_multiple_clks, reg_no_clks, + unconstrained_endpoints, + loops, generated_clks); } //////////////////////////////////////////////////////////////// PinSet find_fanin_pins(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) { Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); PinSet fanin = sta->findFaninPins(to, flat, startpoints_only, inst_levels, pin_levels, - thru_disabled, thru_constants); + thru_disabled, thru_constants, mode); delete to; return fanin; } InstanceSet find_fanin_insts(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) { Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); InstanceSet fanin = sta->findFaninInstances(to, flat, startpoints_only, inst_levels, pin_levels, - thru_disabled, thru_constants); + thru_disabled, thru_constants, mode); delete to; return fanin; } PinSet find_fanout_pins(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) { Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); PinSet fanout = sta->findFanoutPins(from, flat, endpoints_only, inst_levels, pin_levels, - thru_disabled, thru_constants); + thru_disabled, thru_constants, mode); delete from; return fanout; } InstanceSet find_fanout_insts(PinSeq *from, - bool flat, - bool endpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) { Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); InstanceSet fanout = sta->findFanoutInstances(from, flat, endpoints_only, inst_levels, pin_levels, - thru_disabled, thru_constants); + thru_disabled, thru_constants, mode); delete from; return fanout; } @@ -1096,18 +1060,6 @@ set_bidirect_inst_paths_enabled(bool enabled) Sta::sta()->setBidirectInstPathsEnabled(enabled); } -bool -bidirect_net_paths_enabled() -{ - return Sta::sta()->bidirectNetPathsEnabled(); -} - -void -set_bidirect_net_paths_enabled(bool enabled) -{ - Sta::sta()->setBidirectNetPathsEnabled(enabled); -} - bool recovery_removal_checks_enabled() { @@ -1270,7 +1222,7 @@ edge() return self->transition(Sta::sta()); } -string +std::string tag() { Sta *sta = Sta::sta(); @@ -1310,13 +1262,3 @@ next() void finish() { delete self; } } - -%extend MinPulseWidthCheckSeqIterator { -bool has_next() { return self->hasNext(); } -MinPulseWidthCheck *next() { return self->next(); } -void finish() { delete self; } -} // MinPulseWidthCheckSeqIterator methods - -%extend Corner { -const char *name() { return self->name(); } -} diff --git a/search/Search.tcl b/search/Search.tcl index ba5acba48..a9c1312d0 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -54,7 +54,7 @@ proc check_setup_cmd { cmd cmd_args } { } else { parse_key_args $cmd cmd_args keys {} \ flags {-no_input_delay -no_output_delay -multiple_clock -no_clock \ - -unconstrained_endpoints -loops -generated_clocks} + -unconstrained_endpoints -loops -generated_clocks} set no_input_delay [info exists flags(-no_input_delay)] set no_output_delay [info exists flags(-no_output_delay)] set multiple_clock [info exists flags(-multiple_clock)] @@ -65,15 +65,15 @@ proc check_setup_cmd { cmd cmd_args } { } set verbose [info exists flags(-verbose)] set errors [check_timing_cmd $no_input_delay $no_output_delay \ - $multiple_clock $no_clock \ - $unconstrained_endpoints $loops \ - $generated_clocks] + $multiple_clock $no_clock \ + $unconstrained_endpoints $loops \ + $generated_clocks] foreach error $errors { # First line is the error msg. report_line [lindex $error 0] if { $verbose } { foreach obj [lrange $error 1 end] { - report_line " $obj" + report_line " $obj" } } } @@ -98,7 +98,7 @@ define_cmd_args "find_timing_paths" \ [-to to_list|-rise_to to_list|-fall_to to_list]\ [-path_delay min|min_rise|min_fall|max|max_rise|max_fall|min_max]\ [-unconstrained] - [-corner corner]\ + [-scenes scenes]\ [-group_path_count path_count] \ [-endpoint_path_count path_count]\ [-unique_paths_to_endpoint]\ @@ -119,12 +119,12 @@ proc find_timing_paths_cmd { cmd args_var } { parse_key_args $cmd args \ keys {-from -rise_from -fall_from -to -rise_to -fall_to \ - -path_delay -corner -group_count -endpoint_count \ - -group_path_count -endpoint_path_count \ - -slack_max -slack_min -path_group} \ + -path_delay -corner -scenes -group_count -endpoint_count \ + -group_path_count -endpoint_path_count \ + -slack_max -slack_min -path_group} \ flags {-unconstrained -sort_by_slack \ - -unique_paths_to_endpoint \ - -unique_edges_to_endpoint} 0 + -unique_paths_to_endpoint \ + -unique_edges_to_endpoint} 0 set min_max "max" set end_rf "rise_fall" @@ -168,7 +168,7 @@ proc find_timing_paths_cmd { cmd args_var } { set unconstrained 0 } - set corner [parse_corner_or_all keys] + set scenes [parse_scenes_or_all keys] set endpoint_path_count 1 if { [info exists keys(-endpoint_count)] } { @@ -232,111 +232,20 @@ proc find_timing_paths_cmd { cmd args_var } { } set path_ends [find_path_ends $from $thrus $to $unconstrained \ - $corner $min_max \ - $group_path_count $endpoint_path_count \ - $unique_pins $unique_edges \ - $slack_min $slack_max \ - $sort_by_slack $groups \ - 1 1 1 1 1 1] + $scenes $min_max \ + $group_path_count $endpoint_path_count \ + $unique_pins $unique_edges \ + $slack_min $slack_max \ + $sort_by_slack $groups \ + 1 1 1 1 1 1] return $path_ends } ################################################################ -define_cmd_args "report_arrival" {pin} - -proc report_arrival { pin } { - report_delays_wrt_clks $pin "arrivals_clk_delays" -} - -proc report_delays_wrt_clks { pin_arg what } { - set pin [get_port_pin_error "pin" $pin_arg] - foreach vertex [$pin vertices] { - if { $vertex != "NULL" } { - report_delays_wrt_clk $vertex $what "NULL" "rise" - report_delays_wrt_clk $vertex $what [default_arrival_clock] "rise" - foreach clk [all_clocks] { - report_delays_wrt_clk $vertex $what $clk "rise" - report_delays_wrt_clk $vertex $what $clk "fall" - } - } - } -} - -proc report_delays_wrt_clk { vertex what clk clk_rf } { - global sta_report_default_digits - - set rise [$vertex $what rise $clk $clk_rf $sta_report_default_digits] - set fall [$vertex $what fall $clk $clk_rf $sta_report_default_digits] - # Filter INF/-INF arrivals. - if { !([delays_are_inf $rise] && [delays_are_inf $fall]) } { - set rise_fmt [format_delays $rise] - set fall_fmt [format_delays $fall] - if {$clk != "NULL"} { - set clk_str " ([get_name $clk] [rf_short_name $clk_rf])" - } else { - set clk_str "" - } - report_line "$clk_str r $rise_fmt f $fall_fmt" - } -} - -proc report_wrt_clks { pin_arg what } { - set pin [get_port_pin_error "pin" $pin_arg] - foreach vertex [$pin vertices] { - if { $vertex != "NULL" } { - report_wrt_clk $vertex $what "NULL" "rise" - report_wrt_clk $vertex $what [default_arrival_clock] "rise" - foreach clk [all_clocks] { - report_wrt_clk $vertex $what $clk "rise" - report_wrt_clk $vertex $what $clk "fall" - } - } - } -} - -proc report_wrt_clk { vertex what clk clk_rf } { - global sta_report_default_digits - - set rise [$vertex $what rise $clk $clk_rf] - set fall [$vertex $what fall $clk $clk_rf] - # Filter INF/-INF arrivals. - if { !([times_are_inf $rise] && [times_are_inf $fall]) } { - set rise_fmt [format_times $rise $sta_report_default_digits] - set fall_fmt [format_times $fall $sta_report_default_digits] - if {$clk != "NULL"} { - set clk_str " ([get_name $clk] [rf_short_name $clk_rf])" - } else { - set clk_str "" - } - report_line "$clk_str r $rise_fmt f $fall_fmt" - } -} - -proc times_are_inf { times } { - foreach time $times { - if { $time < 1e+10 && $time > -1e+10 } { - return 0 - } - } - return 1 -} - -proc delays_are_inf { delays } { - foreach delay $delays { - if { !([string match "INF*" $delay] \ - || [string match "-INF*" $delay]) } { - return 0 - } - } - return 1 -} - -################################################################ - define_cmd_args "report_clock_skew" {[-setup|-hold]\ - [-clock clocks]\ - [-corner corner]\ + [-clocks clocks]\ + [-scenes scenes]\ [-include_internal_latency] [-digits digits]} @@ -344,7 +253,7 @@ proc_redirect report_clock_skew { global sta_report_default_digits parse_key_args "report_clock_skew" args \ - keys {-clock -corner -digits} \ + keys {-clocks -corner -scenes -digits} \ flags {-setup -hold -include_internal_latency} check_argc_eq0 "report_clock_skew" $args @@ -358,12 +267,13 @@ proc_redirect report_clock_skew { set setup_hold "setup" } - if [info exists keys(-clock)] { - set clks [get_clocks_warn "-clocks" $keys(-clock)] + set scenes [parse_scenes_or_all keys] + if [info exists keys(-clocks)] { + puts "clks1 = [get_object_names $clks]" } else { - set clks [all_clocks] + set clks [get_scene_clocks $scenes] } - set corner [parse_corner_or_all keys] + set include_internal_latency [info exists flags(-include_internal_latency)] if [info exists keys(-digits)] { set digits $keys(-digits) @@ -372,14 +282,14 @@ proc_redirect report_clock_skew { set digits $sta_report_default_digits } if { $clks != {} } { - report_clk_skew $clks $corner $setup_hold $include_internal_latency $digits + report_clk_skew $clks $scenes $setup_hold $include_internal_latency $digits } } ################################################################ -define_cmd_args "report_clock_latency" {[-clock clocks]\ - [-corner corner]\ +define_cmd_args "report_clock_latency" {[-clocks clocks]\ + [-scenes scene]\ [-include_internal_latency] [-digits digits]} @@ -387,16 +297,16 @@ proc_redirect report_clock_latency { global sta_report_default_digits parse_key_args "report_clock_" args \ - keys {-clock -corner -digits} \ + keys {-clocks -scenes -digits} \ flags {-include_internal_latency} check_argc_eq0 "report_clock_latency" $args - if [info exists keys(-clock)] { - set clks [get_clocks_warn "-clocks" $keys(-clock)] + set scenes [parse_scenes_or_all keys] + if [info exists keys(-clocks)] { + set clks [get_clocks_warn "-clocks" $keys(-clocks)] } else { - set clks [all_clocks] + set clks [get_scene_clocks $scenes] } - set corner [parse_corner_or_all keys] set include_internal_latency [info exists flags(-include_internal_latency)] if [info exists keys(-digits)] { set digits $keys(-digits) @@ -405,7 +315,7 @@ proc_redirect report_clock_latency { set digits $sta_report_default_digits } if { $clks != {} } { - report_clk_latency $clks $corner $include_internal_latency $digits + report_clk_latency $clks $scenes $include_internal_latency $digits } } @@ -417,7 +327,7 @@ define_cmd_args "report_checks" \ [-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]\ - [-corner corner]\ + [-scenes scenes]\ [-group_path_count path_count] \ [-endpoint_path_count path_count]\ [-unique_paths_to_endpoint]\ @@ -441,8 +351,7 @@ proc_redirect report_checks { ################################################################ define_cmd_args "report_check_types" \ - {[-violators] [-verbose]\ - [-corner corner]\ + {[-scenes scenes] [-violators] [-verbose]\ [-format slack_only|end]\ [-max_delay] [-min_delay]\ [-recovery] [-removal]\ @@ -452,13 +361,15 @@ define_cmd_args "report_check_types" \ [-max_capacitance] [-min_capacitance]\ [-min_pulse_width] [-min_period] [-max_skew]\ [-net net]\ + [-max_count max_count]\ [-digits digits] [-no_line_splits]\ [> filename] [>> filename]} proc_redirect report_check_types { variable path_options - parse_key_args "report_check_types" args keys {-net -corner}\ + parse_key_args "report_check_types" args \ + keys {-scenes -corner -net -max_count}\ flags {-violators -verbose -no_line_splits} 0 set violators [info exists flags(-violators)] @@ -477,13 +388,17 @@ proc_redirect report_check_types { set min_max "max" } - set corner [parse_corner_or_all keys] - set net "NULL" if { [info exists keys(-net)] } { set net [get_net_arg "-net" $keys(-net)] } + set max_count 1 + if { [info exists keys(-max_count)] } { + set max_count $keys(-max_count) + check_positive_integer "-max_count" $max_count + } + if { $args == {} } { if { $min_max == "max" || $min_max == "min_max" } { set setup 1 @@ -522,12 +437,12 @@ proc_redirect report_check_types { } else { parse_key_args "report_check_types" args keys {} \ flags {-max_delay -min_delay -recovery -removal \ - -clock_gating_setup -clock_gating_hold \ - -max_slew -min_slew \ - -max_fanout -min_fanout \ - -max_capacitance -min_capacitance \ - -min_pulse_width \ - -min_period -max_skew} 1 + -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} 1 set setup [info exists flags(-max_delay)] set hold [info exists flags(-min_delay)] @@ -545,24 +460,23 @@ proc_redirect report_check_types { set min_period [info exists flags(-min_period)] set max_skew [info exists flags(-max_skew)] if { [operating_condition_analysis_type] == "single" \ - && (($setup && $hold) \ - || ($recovery && $removal) \ - || ($clk_gating_setup && $clk_gating_hold)) } { + && (($setup && $hold) \ + || ($recovery && $removal) \ + || ($clk_gating_setup && $clk_gating_hold)) } { sta_error 520 "analysis type single is not consistent with doing both setup/max and hold/min checks." } } + set scenes [parse_scenes_or_all keys] if { $args != {} } { sta_error 521 "positional arguments not supported." } - set corner [parse_corner_or_all keys] - if { $setup || $hold || $recovery || $removal \ - || $clk_gating_setup || $clk_gating_hold } { + || $clk_gating_setup || $clk_gating_hold } { if { ($setup && $hold) \ - || ($recovery && $removal) \ - || ($clk_gating_setup && $clk_gating_hold) } { + || ($recovery && $removal) \ + || ($clk_gating_setup && $clk_gating_hold) } { set path_min_max "min_max" } elseif { $setup || $recovery || $clk_gating_setup } { set path_min_max "max" @@ -578,126 +492,43 @@ proc_redirect report_check_types { set slack_min [expr -$sta::float_inf] set slack_max $sta::float_inf } + set path_ends [find_path_ends "NULL" {} "NULL" 0 \ - $corner $path_min_max $group_path_count 1 1 0 \ - $slack_min $slack_max \ - 0 {} \ - $setup $hold \ - $recovery $removal \ - $clk_gating_setup $clk_gating_hold] + $scenes $path_min_max $group_path_count 1 1 0 \ + $slack_min $slack_max \ + 0 {} \ + $setup $hold \ + $recovery $removal \ + $clk_gating_setup $clk_gating_hold] report_path_ends $path_ends } if { $max_slew } { - report_slew_limits $net $corner "max" $violators $verbose $nosplit + report_slew_checks $net $max_count $violators $verbose $scenes "max" } if { $min_slew } { - report_slew_limits $net $corner "min" $violators $verbose $nosplit + report_slew_checks $net $max_count $violators $verbose $scenes "min" } if { $max_fanout } { - report_fanout_limits $net "max" $violators $verbose $nosplit + report_fanout_checks $net $max_count $violators $verbose $scenes "max" } if { $min_fanout } { - report_fanout_limits $net "min" $violators $verbose $nosplit + report_fanout_checks $net $max_count $violators $verbose $scenes "min" } if { $max_capacitance } { - report_capacitance_limits $net $corner "max" $violators $verbose $nosplit + report_capacitance_checks $net $max_count $violators $verbose $scenes "max" } if { $min_capacitance } { - report_capacitance_limits $net $corner "min" $violators $verbose $nosplit + report_capacitance_checks $net $max_count $violators $verbose $scenes "min" } if { $min_pulse_width } { - if { $violators } { - set checks [min_pulse_width_violations $corner] - report_mpw_checks $checks $verbose - } else { - set check [min_pulse_width_check_slack $corner] - if { $check != "NULL" } { - report_mpw_check $check $verbose - } - } + report_min_pulse_width_checks $net $max_count $violators $verbose $scenes } if { $min_period } { - if { $violators } { - set checks [min_period_violations] - report_min_period_checks $checks $verbose - } else { - set check [min_period_check_slack] - if { $check != "NULL" } { - report_min_period_check $check $verbose - } - } + report_min_period_checks $net $max_count $violators $verbose $scenes } if { $max_skew } { - if { $violators } { - set checks [max_skew_violations] - report_max_skew_checks $checks $verbose - } else { - set check [max_skew_check_slack] - if { $check != "NULL" } { - report_max_skew_check $check $verbose - } - } - } -} - -proc report_slew_limits { net corner min_max violators verbose nosplit } { - set pins [check_slew_limits $net $violators $corner $min_max] - if { $pins != {} } { - report_line "${min_max} slew" - report_line "" - if { $verbose } { - foreach pin $pins { - report_slew_limit_verbose $pin $corner $min_max - report_line "" - } - } else { - report_slew_limit_short_header - foreach pin $pins { - report_slew_limit_short $pin $corner $min_max - } - report_line "" - } - } -} - -proc report_fanout_limits { net min_max violators verbose nosplit } { - set pins [check_fanout_limits $net $violators $min_max] - if { $pins != {} } { - report_line "${min_max} fanout" - report_line "" - if { $verbose } { - foreach pin $pins { - report_fanout_limit_verbose $pin $min_max - report_line "" - } - } else { - report_fanout_limit_short_header - foreach pin $pins { - report_fanout_limit_short $pin $min_max - } - report_line "" - } - } -} - -proc report_capacitance_limits { net corner min_max violators verbose nosplit } { - set pins [check_capacitance_limits $net $violators $corner $min_max] - if { $pins != {} } { - report_line "${min_max} capacitance" - report_line "" - if { $verbose } { - foreach pin $pins { - report_capacitance_limit_verbose $pin $corner $min_max - report_line "" - } - } else { - report_capacitance_limit_short_header - foreach pin $pins { - report_capacitance_limit_short $pin $corner $min_max - } - report_line "" - } + report_max_skew_checks $net $max_count $violators $verbose $scenes } } @@ -780,33 +611,6 @@ proc_redirect report_worst_slack { ################################################################ -define_cmd_args "report_pulse_width_checks" \ - {[-verbose] [-corner corner] [-digits digits] [-no_line_splits] [pins]\ - [> filename] [>> filename]} - -proc_redirect report_pulse_width_checks { - variable path_options - - parse_key_args "report_pulse_width_checks" args keys {-corner} \ - flags {-verbose} 0 - # Only -digits and -no_line_splits are respected. - parse_report_path_options "report_pulse_width_checks" args "full" 0 - check_argc_eq0or1 "report_pulse_width_checks" $args - set corner [parse_corner_or_all keys] - set verbose [info exists flags(-verbose)] - if { [llength $args] == 1 } { - set pins [get_port_pins_error "pins" [lindex $args 0]] - set checks [min_pulse_width_check_pins $pins $corner] - } else { - set checks [min_pulse_width_checks $corner] - } - if { $checks != {} } { - report_mpw_checks $checks $verbose - } -} - -################################################################ - # Note that -all and -tags are intentionally "hidden". define_cmd_args "report_path" \ {[-min|-max]\ @@ -845,39 +649,39 @@ proc_redirect report_path { } else { foreach vertex [$pin vertices] { if { $vertex != "NULL" } { - if { $report_all } { - set first 1 - set path_iter [$vertex path_iterator $rf $min_max] - while {[$path_iter has_next]} { - set path [$path_iter next] - if { $first } { - report_line "Tag group: [$vertex tag_group_index]" - } else { - report_line "" - } - if { $report_tags } { - report_line "Tag: [$path tag]" - } - report_path_cmd $path - set first 0 - } - $path_iter finish - } else { - set worst_path [vertex_worst_arrival_path_rf $vertex $rf $min_max] - if { $worst_path != "NULL" } { - if { $report_tags } { - report_line "Tag: [$worst_path tag]" - } - report_path_cmd $worst_path - } - } + if { $report_all } { + set first 1 + set path_iter [$vertex path_iterator $rf $min_max] + while {[$path_iter has_next]} { + set path [$path_iter next] + if { $first } { + report_line "Tag group: [$vertex tag_group_index]" + } else { + report_line "" + } + if { $report_tags } { + report_line "Tag: [$path tag]" + } + report_path_cmd $path + set first 0 + } + $path_iter finish + } else { + set worst_path [vertex_worst_arrival_path_rf $vertex $rf $min_max] + if { $worst_path != "NULL" } { + if { $report_tags } { + report_line "Tag: [$worst_path tag]" + } + report_path_cmd $worst_path + } + } } } } } proc parse_report_path_options { cmd args_var default_format - unknown_key_is_error } { + unknown_key_is_error } { variable path_options variable report_path_field_width_extra global sta_report_default_digits @@ -893,7 +697,7 @@ proc parse_report_path_options { cmd args_var default_format if [info exists path_options(-format)] { set format $path_options(-format) set formats {full full_clock full_clock_expanded short \ - end slack_only summary json} + end slack_only summary json} if { [lsearch $formats $format] == -1 } { sta_error 524 "-format $format not recognized." } @@ -963,18 +767,65 @@ proc parse_report_path_options { cmd args_var default_format ################################################################ -define_cmd_args "report_required" {pin} +define_cmd_args "report_arrival" {[-scene scene] [-digits digits] pin} -proc report_required { pin } { - report_delays_wrt_clks $pin "requireds_clk_delays" +proc report_arrival { args } { + global sta_report_default_digits + + parse_key_args "report_arrival" args keys {-scene -digits} flags {} + check_argc_eq1 "report_arrival" $args + + set pin [get_port_pin_error "pin" [lindex $args 0]] + set scene [parse_scene keys] + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + report_arrival_wrt_clks $pin $scene $digits } ################################################################ -define_cmd_args "report_slack" {pin} +define_cmd_args "report_required" {[-scene scene] [-digits digits] pin} -proc report_slack { pin } { - report_delays_wrt_clks $pin "slacks_clk_delays" +proc report_required { args } { + global sta_report_default_digits + + parse_key_args "report_required" args keys {-scene -digits} flags {} + check_argc_eq1 "report_required" $args + + set pin [get_port_pin_error "pin" [lindex $args 0]] + set scene [parse_scene keys] + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + report_required_wrt_clks $pin $scene $digits +} + +################################################################ + +define_cmd_args "report_slack" {[-scene scene] [-digits digits] pin} + +proc report_slack { args } { + global sta_report_default_digits + + parse_key_args "report_slack" args keys {-scene -digits} flags {} + check_argc_eq1 "report_slack" $args + + set pin [get_port_pin_error "pin" [lindex $args 0]] + set scene [parse_scene keys] + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + report_slack_wrt_clks $pin $scene $digits } ################################################################ @@ -990,16 +841,17 @@ proc report_tag_arrivals { pin } { ################################################################ define_hidden_cmd_args "total_negative_slack" \ - {[-corner corner] [-min]|[-max]} + {[-scene scene] [-min]|[-max]} proc total_negative_slack { args } { parse_key_args "total_negative_slack" args \ - keys {-corner} flags {-min -max} + keys {-scene -corner} flags {-min -max} check_argc_eq0 "total_negative_slack" $args set min_max [parse_min_max_flags flags] - if { [info exists keys(-corner)] } { - set corner [parse_corner_required keys] - set tns [total_negative_slack_corner_cmd $corner $min_max] + # compabibility 05/29/2025 + if { [info exists keys(-scene)] || [info exists keys(-corner)]} { + set scene [parse_scene_required keys] + set tns [total_negative_slack_scene_cmd $scene $min_max] } else { set tns [total_negative_slack_cmd $min_max] } @@ -1009,7 +861,7 @@ proc total_negative_slack { args } { ################################################################ define_hidden_cmd_args "worst_negative_slack" \ - {[-corner corner] [-min]|[-max]} + {[-scene scene] [-min]|[-max]} proc worst_negative_slack { args } { set worst_slack [worst_slack1 "worst_negative_slack" $args] @@ -1023,7 +875,7 @@ proc worst_negative_slack { args } { ################################################################ define_hidden_cmd_args "worst_slack" \ - {[-corner corner] [-min]|[-max]} + {[-scene scene] [-min]|[-max]} proc worst_slack { args } { return [worst_slack1 "worst_slack" $args] @@ -1032,12 +884,13 @@ proc worst_slack { args } { # arg parsing common to worst_slack/worst_negative_slack proc worst_slack1 { cmd args1 } { parse_key_args $cmd args1 \ - keys {-corner} flags {-min -max} + keys {-corner -scene} flags {-min -max} check_argc_eq0 $cmd $args1 set min_max [parse_min_max_flags flags] - if { [info exists keys(-corner)] } { - set corner [parse_corner_required keys] - set worst_slack [worst_slack_corner $corner $min_max] + # compabibility 05/29/2025 + if { [info exists keys(-scene)] || [info exists keys(-corner)]} { + set scene [parse_scene_required keys] + set worst_slack [worst_slack_scene $scene $min_max] } else { set worst_slack [worst_slack_cmd $min_max] } @@ -1067,14 +920,14 @@ proc worst_clock_skew { args } { ################################################################ -define_cmd_args "write_timing_model" {[-corner corner] \ +define_cmd_args "write_timing_model" {[-scene scene] \ [-library_name lib_name]\ [-cell_name cell_name]\ filename} proc write_timing_model { args } { parse_key_args "write_timing_model" args \ - keys {-library_name -cell_name -corner} flags {} + keys {-library_name -cell_name -scene} flags {} check_argc_eq1 "write_timing_model" $args set filename [file nativename [lindex $args 0]] @@ -1088,8 +941,8 @@ proc write_timing_model { args } { } else { set lib_name $cell_name } - set corner [parse_corner keys] - write_timing_model_cmd $lib_name $cell_name $filename $corner + set scene [parse_scene keys] + write_timing_model_cmd $lib_name $cell_name $filename $scene } diff --git a/search/SearchPred.cc b/search/SearchPred.cc index 9cb0401ea..da7d52364 100644 --- a/search/SearchPred.cc +++ b/search/SearchPred.cc @@ -30,186 +30,226 @@ #include "Network.hh" #include "Graph.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Levelize.hh" #include "Search.hh" #include "Latches.hh" +#include "Sim.hh" #include "Variables.hh" namespace sta { static bool -searchThruSimEdge(const Vertex *vertex, const RiseFall *rf); +searchThruSimEdge(const Vertex *vertex, + const RiseFall *rf, + const Mode *mode); static bool -searchThruTimingSense(const Edge *edge, const RiseFall *from_rf, - const RiseFall *to_rf); +searchThruTimingSense(const Edge *edge, + const RiseFall *from_rf, + const RiseFall *to_rf, + const Mode *mode); -SearchPred0::SearchPred0(const StaState *sta) : +SearchPred::SearchPred(const StaState *sta) : sta_(sta) { } +void +SearchPred::copyState(const StaState *sta) +{ + sta_ = sta; +} + bool -SearchPred0::searchFrom(const Vertex *from_vertex) +SearchPred::searchFrom(const Vertex *from_vertex) const { - return !(from_vertex->isDisabledConstraint() - || from_vertex->isConstant()); + for (const Mode *mode : sta_->modes()) { + if (searchFrom(from_vertex, mode)) + return true; + } + return false; } bool -SearchPred0::searchThru(Edge *edge) +SearchPred::searchThru(Edge *edge) const { - const TimingRole *role = edge->role(); - const Sdc *sdc = sta_->sdc(); - const Variables *variables = sta_->variables(); - return !(edge->isDisabledConstraint() - // Constants disable edge cond expression. - || edge->isDisabledCond() - || sdc->isDisabledCondDefault(edge) - // Register/latch preset/clr edges are disabled by default. - || (role == TimingRole::regSetClr() - && !variables->presetClrArcsEnabled()) - // Constants on other pins disable this edge (ie, a mux select). - || edge->simTimingSense() == TimingSense::none - || (edge->isBidirectInstPath() - && !variables->bidirectInstPathsEnabled()) - || (edge->isBidirectNetPath() - && !variables->bidirectNetPathsEnabled()) - || (role == TimingRole::latchDtoQ() - && sta_->latches()->latchDtoQState(edge) - == LatchEnableState::closed)); + for (const Mode *mode : sta_->modes()) { + if (searchThru(edge, mode)) + return true; + } + return false; } bool -SearchPred0::searchTo(const Vertex *to_vertex) +SearchPred::searchTo(const Vertex *to_vertex) const { - return !to_vertex->isConstant(); + for (const Mode *mode : sta_->modes()) { + if (searchTo(to_vertex, mode)) + return true; + } + return false; } //////////////////////////////////////////////////////////////// -SearchPred1::SearchPred1(const StaState *sta) : - SearchPred0(sta) +SearchPred0::SearchPred0(const StaState *sta) : + SearchPred(sta) { } bool -SearchPred1::searchThru(Edge *edge) +SearchPred0::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { - return SearchPred0::searchThru(edge) - && !edge->isDisabledLoop(); + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); + const Sim *sim = mode->sim(); + return !(sdc->isDisabledConstraint(from_pin) + || sim->isConstant(from_vertex)); } -//////////////////////////////////////////////////////////////// - -SearchPred2::SearchPred2(const StaState *sta) : - SearchPred1(sta) +bool +SearchPred0::searchThru(Edge *edge, + const Mode *mode) const { + const TimingRole *role = edge->role(); + const Variables *variables = sta_->variables(); + const Sdc *sdc = mode->sdc(); + const Sim *sim = mode->sim(); + return !(sdc->isDisabledConstraint(edge) + // Constants disable edge cond expression. + || sim->isDisabledCond(edge) + || sdc->isDisabledCondDefault(edge) + // Register/latch preset/clr edges are disabled by default. + || (role == TimingRole::regSetClr() + && !variables->presetClrArcsEnabled()) + // Constants on other pins disable this edge (ie, a mux select). + || sim->simTimingSense(edge) == TimingSense::none + || (edge->isBidirectInstPath() + && !variables->bidirectInstPathsEnabled()) + || (role == TimingRole::latchDtoQ() + && sta_->latches()->latchDtoQState(edge, mode) + == LatchEnableState::closed)); } bool -SearchPred2::searchThru(Edge *edge) +SearchPred0::searchTo(const Vertex *to_vertex, + const Mode *mode) const { - return SearchPred1::searchThru(edge) - && !edge->role()->isTimingCheck(); + return !mode->sim()->isConstant(to_vertex); } //////////////////////////////////////////////////////////////// -SearchPredNonLatch2::SearchPredNonLatch2(const StaState *sta) : - SearchPred2(sta) +SearchPred1::SearchPred1(const StaState *sta) : + SearchPred0(sta) { } bool -SearchPredNonLatch2::searchThru(Edge *edge) +SearchPred1::searchThru(Edge *edge, + const Mode *mode) const { - return SearchPred2::searchThru(edge) - && !sta_->latches()->isLatchDtoQ(edge); + return SearchPred0::searchThru(edge, mode) + && !edge->isDisabledLoop(); } //////////////////////////////////////////////////////////////// -SearchPredNonReg2::SearchPredNonReg2(const StaState *sta) : - SearchPred2(sta) +ClkTreeSearchPred::ClkTreeSearchPred(const StaState *sta) : + SearchPred(sta) { } bool -SearchPredNonReg2::searchThru(Edge *edge) +ClkTreeSearchPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { - const TimingRole *role = edge->role(); - return SearchPred2::searchThru(edge) - // Enqueue thru latches is handled explicitly by search. - && !sta_->latches()->isLatchDtoQ(edge) - && role->genericRole() != TimingRole::regClkToQ(); + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); + return !sdc->isDisabledConstraint(from_pin); } -//////////////////////////////////////////////////////////////// - -ClkTreeSearchPred::ClkTreeSearchPred(const StaState *sta) : - SearchPred1(sta) +bool +ClkTreeSearchPred::searchThru(Edge *edge, + const Mode *mode) const { + const TimingRole *role = edge->role(); + const Sdc *sdc = mode->sdc(); + return searchThruAllow(role) + && !((role == TimingRole::tristateEnable() + && !sta_->variables()->clkThruTristateEnabled()) + || role == TimingRole::regSetClr() + || sdc->isDisabledConstraint(edge) + || sdc->isDisabledCondDefault(edge) + || edge->isBidirectInstPath() + || edge->isDisabledLoop()); } bool -ClkTreeSearchPred::searchThru(Edge *edge) +ClkTreeSearchPred::searchThruAllow(const TimingRole *role) const { - // Propagate clocks through constants. - const TimingRole *role = edge->role(); - return (role->isWire() - || role == TimingRole::combinational()) - && (sta_->variables()->clkThruTristateEnabled() - || !(role == TimingRole::tristateEnable() - || role == TimingRole::tristateDisable())) - && SearchPred1::searchThru(edge); + return role->isWire() + || role == TimingRole::combinational(); } bool isClkEnd(Vertex *vertex, - Graph *graph) + const Mode *mode) { + Graph *graph = mode->graph(); ClkTreeSearchPred pred(graph); VertexOutEdgeIterator edge_iter(vertex, graph); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - if (pred.searchThru(edge)) + if (pred.searchThru(edge, mode)) return false; } return true; } +bool +ClkTreeSearchPred::searchTo(const Vertex *, + const Mode *) const +{ + return true; +} + //////////////////////////////////////////////////////////////// bool searchThru(const Edge *edge, - const TimingArc *arc, - const Graph *graph) + const TimingArc *arc, + const Mode *mode) { + const Graph *graph = mode->graph(); const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); const RiseFall *to_rf = arc->toEdge()->asRiseFall(); // Ignore transitions other than rise/fall. return from_rf && to_rf - && searchThru(edge->from(graph), from_rf, edge, edge->to(graph), to_rf); + && searchThru(edge->from(graph), from_rf, edge, edge->to(graph), to_rf, mode); } bool searchThru(Vertex *from_vertex, - const RiseFall *from_rf, - const Edge *edge, - Vertex *to_vertex, - const RiseFall *to_rf) + const RiseFall *from_rf, + const Edge *edge, + Vertex *to_vertex, + const RiseFall *to_rf, + const Mode *mode) { - return searchThruTimingSense(edge, from_rf, to_rf) - && searchThruSimEdge(from_vertex, from_rf) - && searchThruSimEdge(to_vertex, to_rf); + return searchThruTimingSense(edge, from_rf, to_rf, mode) + && searchThruSimEdge(from_vertex, from_rf, mode) + && searchThruSimEdge(to_vertex, to_rf, mode); } // set_case_analysis rising/falling filters rise/fall edges during search. static bool searchThruSimEdge(const Vertex *vertex, - const RiseFall *rf) + const RiseFall *rf, + const Mode *mode) { - LogicValue sim_value = vertex->simValue(); + LogicValue sim_value = mode->sim()->simValue(vertex->pin()); switch (sim_value) { case LogicValue::rise: return rf == RiseFall::rise(); @@ -221,10 +261,12 @@ searchThruSimEdge(const Vertex *vertex, } static bool -searchThruTimingSense(const Edge *edge, const RiseFall *from_rf, - const RiseFall *to_rf) +searchThruTimingSense(const Edge *edge, + const RiseFall *from_rf, + const RiseFall *to_rf, + const Mode *mode) { - switch (edge->simTimingSense()) { + switch (mode->sim()->simTimingSense(edge)) { case TimingSense::unknown: return true; case TimingSense::positive_unate: @@ -244,17 +286,18 @@ searchThruTimingSense(const Edge *edge, const RiseFall *from_rf, bool hasFanin(Vertex *vertex, - SearchPred *pred, - const Graph *graph) + SearchPred *pred, + const Graph *graph, + const Mode *mode) { - if (pred->searchTo(vertex)) { + if (pred->searchTo(vertex, mode)) { VertexInEdgeIterator edge_iter(vertex, graph); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *from_vertex = edge->from(graph); - if (pred->searchFrom(from_vertex) - && pred->searchThru(edge)) - return true; + if (pred->searchFrom(from_vertex, mode) + && pred->searchThru(edge, mode)) + return true; } } return false; @@ -262,17 +305,18 @@ hasFanin(Vertex *vertex, bool hasFanout(Vertex *vertex, - SearchPred *pred, - const Graph *graph) + SearchPred *pred, + const Graph *graph, + const Mode *mode) { - if (pred->searchFrom(vertex)) { + if (pred->searchFrom(vertex, mode)) { VertexOutEdgeIterator edge_iter(vertex, graph); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph); - if (pred->searchTo(to_vertex) - && pred->searchThru(edge)) - return true; + if (pred->searchTo(to_vertex, mode) + && pred->searchThru(edge, mode)) + return true; } } return false; diff --git a/search/Sim.cc b/search/Sim.cc index 959b088bb..70b6384eb 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -27,6 +27,7 @@ // https://davidkebo.com/cudd #include "cudd.h" +#include "ContainerHelpers.hh" #include "Error.hh" #include "Mutex.hh" #include "Debug.hh" @@ -41,6 +42,7 @@ #include "Network.hh" #include "Sdc.hh" #include "Graph.hh" +#include "Mode.hh" namespace sta { @@ -48,10 +50,11 @@ static LogicValue logicNot(LogicValue value); static const Pin * findDrvrPin(const Pin *pin, - Network *network); + Network *network); Sim::Sim(StaState *sta) : StaState(sta), + mode_(nullptr), observer_(nullptr), valid_(false), incremental_(false), @@ -60,7 +63,6 @@ Sim::Sim(StaState *sta) : invalid_insts_(network_), invalid_drvr_pins_(network_), invalid_load_pins_(network_), - instances_with_const_pins_(network_), instances_to_annotate_(network_), bdd_(sta) { @@ -71,10 +73,24 @@ Sim::~Sim() delete observer_; } +void +Sim::copyState(const StaState *sta) +{ + StaState::copyState(sta); + // Notify sub-components. + observer_->copyState(sta); +} + +void +Sim::setMode(Mode *mode) +{ + mode_ = mode; +} + TimingSense Sim::functionSense(const FuncExpr *expr, - const Pin *input_pin, - const Instance *inst) + const Pin *input_pin, + const Instance *inst) { debugPrint(debug_, "sim", 4, "find sense pin %s %s", network_->pathName(input_pin), @@ -88,9 +104,9 @@ Sim::functionSense(const FuncExpr *expr, 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)); + == Cudd_ReadOne(cudd_mgr)); decreasing = (Cudd_Decreasing(cudd_mgr, bdd, input_index) - == Cudd_ReadOne(cudd_mgr)); + == Cudd_ReadOne(cudd_mgr)); Cudd_RecursiveDeref(cudd_mgr, bdd); bdd_.clearVarMap(); } @@ -109,7 +125,7 @@ Sim::functionSense(const FuncExpr *expr, LogicValue Sim::evalExpr(const FuncExpr *expr, - const Instance *inst) + const Instance *inst) { LockGuard lock(bdd_lock_); DdNode *bdd = funcBddSim(expr, inst); @@ -140,7 +156,7 @@ Sim::funcBddSim(const FuncExpr *expr, const LibertyPort *port = network_->libertyPort(pin); DdNode *port_node = bdd_.findNode(port); if (port_node) { - LogicValue value = logicValue(pin); + LogicValue value = simValue(pin); int var_index = Cudd_NodeReadIndex(port_node); switch (value) { case LogicValue::zero: @@ -164,8 +180,8 @@ static LogicValue logicNot(LogicValue value) { static LogicValue logic_not[5] = {LogicValue::one, LogicValue::zero, - LogicValue::unknown, LogicValue::unknown, - LogicValue::unknown}; + LogicValue::unknown, LogicValue::unknown, + LogicValue::unknown}; return logic_not[int(value)]; } @@ -176,11 +192,11 @@ Sim::clear() incremental_ = false; const_func_pins_.clear(); const_func_pins_valid_ = false; - instances_with_const_pins_.clear(); instances_to_annotate_.clear(); invalid_insts_.clear(); invalid_drvr_pins_.clear(); invalid_load_pins_.clear(); + clearSimValues(); } void @@ -190,6 +206,89 @@ Sim::setObserver(SimObserver *observer) observer_ = observer; } +SimObserver::SimObserver(StaState *sta) : + StaState(sta) +{ +} + +LogicValue +Sim::simValue(const Vertex *vertex) const +{ + if (vertex->hasSimValue()) { + LogicValue value; + bool exists; + findKeyValue(sim_value_map_, vertex->pin(), value, exists); + if (exists) + return value; + } + return LogicValue::unknown; +} + +LogicValue +Sim::simValue(const Pin *pin) const +{ + Vertex *vertex = graph_->pinLoadVertex(pin); + if (vertex) + return simValue(vertex); + LogicValue value; + bool exists; + findKeyValue(sim_value_map_, pin, value, exists); + if (exists) + return value; + else + return LogicValue::unknown; +} + +bool +Sim::isConstant(const Vertex *vertex) const +{ + LogicValue value = simValue(vertex); + 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; +} + +TimingSense +Sim::simTimingSense(const Edge *edge) const +{ + if (edge->hasSimSense()) { + TimingSense sense; + bool exists; + findKeyValue(edge_timing_sense_map_, edge, sense, exists); + if (exists) + return sense; + } + return TimingSense::unknown; +} + +void +Sim::setSimTimingSense(Edge *edge, + TimingSense sense) +{ + if (sense == TimingSense::unknown) + edge_timing_sense_map_.erase(edge); + else { + edge_timing_sense_map_[edge] = sense; + edge->setHasSimSense(true); + } +} + +bool +Sim::isDisabledCond(const Edge *edge) const +{ + return edge->hasDisabledCond() + && edge_disabled_cond_set_.contains(edge); +} + +//////////////////////////////////////////////////////////////// + void Sim::ensureConstantsPropagated() { @@ -208,7 +307,7 @@ Sim::ensureConstantsPropagated() } invalid_insts_.clear(); propagateConstants(false); - annotateGraphEdges(); + findDisabledEdges(); valid_ = true; incremental_ = true; @@ -246,7 +345,7 @@ Sim::propagateToInvalidLoads() else { const Pin *drvr_pin = findDrvrPin(load_pin, network_); if (drvr_pin) - propagateDrvrToLoad(drvr_pin, load_pin); + propagateDrvrToLoad(drvr_pin, load_pin); } } invalid_load_pins_.clear(); @@ -256,15 +355,15 @@ void Sim::propagateFromInvalidDrvrsToLoads() { for (const Pin *drvr_pin : invalid_drvr_pins_) { - LogicValue value = const_func_pins_.hasKey(drvr_pin) + LogicValue value = const_func_pins_.contains(drvr_pin) ? pinConstFuncValue(drvr_pin) - : logicValue(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)) - setPinValue(load_pin, value); + && network_->isLoad(load_pin)) + setPinValue(load_pin, value); } delete load_iter; } @@ -273,9 +372,9 @@ Sim::propagateFromInvalidDrvrsToLoads() void Sim::propagateDrvrToLoad(const Pin *drvr_pin, - const Pin *load_pin) + const Pin *load_pin) { - LogicValue value = logicValue(drvr_pin); + LogicValue value = simValue(drvr_pin); setPinValue(load_pin, value); } @@ -295,8 +394,8 @@ Sim::ensureConstantFuncPins() Instance *inst = inst_iter->next(); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - recordConstPinFunc(pin); + const Pin *pin = pin_iter->next(); + recordConstPinFunc(pin); } delete pin_iter; } @@ -312,10 +411,10 @@ Sim::recordConstPinFunc(const Pin *pin) if (port) { FuncExpr *expr = port->function(); 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)) + // Tristate outputs do not force the output to be constant. + && port->tristateEnable() == nullptr + && (expr->op() == FuncExpr::Op::zero + || expr->op() == FuncExpr::Op::one)) const_func_pins_.insert(pin); } } @@ -323,7 +422,6 @@ Sim::recordConstPinFunc(const Pin *pin) void Sim::deleteInstanceBefore(const Instance *inst) { - instances_with_const_pins_.erase(inst); invalid_insts_.erase(inst); } @@ -337,6 +435,7 @@ Sim::makePinAfter(const Pin *pin) void Sim::deletePinBefore(const Pin *pin) { + sim_value_map_.erase(pin); // Incrementally update const_func_pins_. const_func_pins_.erase(pin); invalid_load_pins_.erase(pin); @@ -373,12 +472,8 @@ Sim::disconnectPinBefore(const Pin *pin) void Sim::pinSetFuncAfter(const Pin *pin) { - if (incremental_) { - Instance *inst = network_->instance(pin); - if (instances_with_const_pins_.hasKey(inst)) - invalid_insts_.insert(inst); + if (incremental_) valid_ = false; - } // Incrementally update const_func_pins_. const_func_pins_.erase(pin); recordConstPinFunc(pin); @@ -387,12 +482,13 @@ Sim::pinSetFuncAfter(const Pin *pin) void Sim::seedConstants() { + const Sdc *sdc = mode_->sdc(); // Propagate constants from inputs tied hi/low in the network. enqueueConstantPinInputs(); - // Propagate set_LogicValue::zero, set_LogicValue::one, set_logic_dc constants. - setConstraintConstPins(sdc_->logicValues()); + // Propagate set_logic_zero/one/dc constants. + setConstraintConstPins(sdc->logicValues()); // Propagate set_case_analysis constants. - setConstraintConstPins(sdc_->caseLogicValues()); + setConstraintConstPins(sdc->caseLogicValues()); // Propagate 0/1 constant functions. setConstFuncPins(); } @@ -408,7 +504,7 @@ Sim::propagateConstants(bool thru_sequentials) } void -Sim::setConstraintConstPins(LogicValueMap &value_map) +Sim::setConstraintConstPins(const LogicValueMap &value_map) { for (const auto [pin, value] : value_map) { debugPrint(debug_, "sim", 2, "case pin %s = %c", @@ -419,12 +515,12 @@ Sim::setConstraintConstPins(LogicValueMap &value_map) bool pin_is_output = network_->direction(pin)->isAnyOutput(); PinConnectedPinIterator *pin_iter=network_->connectedPinIterator(pin); while (pin_iter->hasNext()) { - const Pin *pin1 = pin_iter->next(); - 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); + const Pin *pin1 = pin_iter->next(); + 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); } delete pin_iter; } @@ -453,9 +549,9 @@ Sim::pinConstFuncValue(const Pin *pin) LibertyPort *port = network_->libertyPort(pin); if (port) { FuncExpr *expr = port->function(); - if (expr->op() == FuncExpr::op_zero) + if (expr->op() == FuncExpr::Op::zero) return LogicValue::zero; - else if (expr->op() == FuncExpr::op_one) + else if (expr->op() == FuncExpr::Op::one) return LogicValue::one; } return LogicValue::unknown; @@ -481,66 +577,50 @@ void Sim::removePropagatedValue(const Pin *pin) { Instance *inst = network_->instance(pin); - if (instances_with_const_pins_.hasKey(inst)) { - invalid_insts_.insert(inst); - valid_ = false; + invalid_insts_.insert(inst); + valid_ = false; - LogicValue constraint_value; - bool exists; - sdc_->caseLogicValue(pin, constraint_value, exists); + const Sdc *sdc = mode_->sdc(); + LogicValue constraint_value; + bool exists; + sdc->caseLogicValue(pin, constraint_value, exists); + if (!exists) { + sdc->logicValue(pin, constraint_value, exists); if (!exists) { - sdc_->logicValue(pin, constraint_value, exists); - if (!exists) { - debugPrint(debug_, "sim", 2, "pin %s remove prop constant", - network_->pathName(pin)); - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - setSimValue(vertex, LogicValue::unknown); - if (bidirect_drvr_vertex) - setSimValue(bidirect_drvr_vertex, LogicValue::unknown); - } + debugPrint(debug_, "sim", 2, "pin %s remove prop constant", + network_->pathName(pin)); + setSimValue(pin, LogicValue::unknown); } } } void Sim::setPinValue(const Pin *pin, - LogicValue value) + LogicValue value) { + const Sdc *sdc = mode_->sdc(); LogicValue constraint_value; bool exists; - sdc_->caseLogicValue(pin, constraint_value, exists); + sdc->caseLogicValue(pin, constraint_value, exists); if (!exists) - sdc_->logicValue(pin, constraint_value, exists); + sdc->logicValue(pin, constraint_value, exists); 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)); + logicValueString(value), + logicValueString(constraint_value), + sdc_network_->pathName(pin)); } else { debugPrint(debug_, "sim", 3, "pin %s = %c", network_->pathName(pin), logicValueString(value)); - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - // Set vertex constant flags. bool value_changed = false; - if (vertex) { - value_changed |= value != vertex->simValue(); - setSimValue(vertex, value); - } - if (bidirect_drvr_vertex) { - value_changed |= value != bidirect_drvr_vertex->simValue(); - setSimValue(bidirect_drvr_vertex, value); - } + value_changed |= value != simValue(pin); + setSimValue(pin, value); if (value_changed) { Instance *inst = network_->instance(pin); - if (logicValueZeroOne(value)) - instances_with_const_pins_.insert(inst); instances_to_annotate_.insert(inst); if (network_->isLeaf(inst) @@ -577,9 +657,9 @@ Sim::evalInstance(const Instance *inst, PortDirection *dir = port->direction(); if (dir->isAnyOutput()) { LogicValue value = LogicValue::unknown; - FuncExpr *expr = port->function(); + FuncExpr *expr = port->function(); LibertyCell *cell = port->libertyCell(); - if (expr) { + if (expr) { FuncExpr *tri_en_expr = port->tristateEnable(); if (tri_en_expr) { if (evalExpr(tri_en_expr, inst) == LogicValue::one) { @@ -619,7 +699,7 @@ Sim::evalInstance(const Instance *inst, port->name(), logicValueString(value)); } - if (value != logicValue(pin)) + if (value != simValue(pin)) setPinValue(pin, value); } } @@ -637,33 +717,41 @@ Sim::clockGateOutValue(const Instance *inst) if (port->isClockGateClock() || port->isClockGateEnable()) { Pin *gclk_pin = network_->findPin(inst, port); - if (gclk_pin) { - Vertex *gclk_vertex = graph_->pinLoadVertex(gclk_pin); - if (gclk_vertex->simValue() == LogicValue::zero) - return LogicValue::zero; - } + if (gclk_pin + && simValue(gclk_pin) == LogicValue::zero) + return LogicValue::zero; } } return LogicValue::unknown; } void -Sim::setSimValue(Vertex *vertex, - LogicValue value) +Sim::setSimValue(const Pin *pin, + LogicValue value) { - if (value != vertex->simValue()) { - vertex->setSimValue(value); + if (value != simValue(pin)) { + if (value == LogicValue::unknown) + sim_value_map_.erase(pin); + else { + sim_value_map_[pin] = value; + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + vertex->setHasSimValue(true); + if (bidirect_drvr_vertex) + bidirect_drvr_vertex->setHasSimValue(true); + } if (observer_) - observer_->valueChangeAfter(vertex); + observer_->valueChangeAfter(pin); } } TimingSense Sim::functionSense(const Instance *inst, - const Pin *from_pin, - const Pin *to_pin) + const Pin *from_pin, + const Pin *to_pin) { - if (logicZeroOne(from_pin)) + if (isConstant((from_pin))) return TimingSense::none; else { LibertyPort *from_port = network_->libertyPort(from_pin); @@ -671,217 +759,98 @@ Sim::functionSense(const Instance *inst, if (to_port) { const FuncExpr *func = to_port->function(); if (func) { - PortDirection *to_dir = to_port->direction(); - if (to_dir->isAnyTristate()) { - FuncExpr *tri_func = to_port->tristateEnable(); - if (tri_func) { - if (func->hasPort(from_port)) { - // from_pin is an input to the to_pin function. - LogicValue tri_enable = evalExpr(tri_func, inst); - if (tri_enable == LogicValue::zero) - // Tristate is disabled. - return TimingSense::none; - else - return functionSense(func, from_pin, inst); - } - } - else { - // Missing tristate enable function. - if (func->hasPort(from_port)) - // from_pin is an input to the to_pin function. - return functionSense(func, from_pin, inst); - } - } - else { - if (func->hasPort(from_port)) - // from_pin is an input to the to_pin function. - return functionSense(func, from_pin, inst); - } + PortDirection *to_dir = to_port->direction(); + if (to_dir->isAnyTristate()) { + FuncExpr *tri_func = to_port->tristateEnable(); + if (tri_func) { + if (func->hasPort(from_port)) { + // from_pin is an input to the to_pin function. + LogicValue tri_enable = evalExpr(tri_func, inst); + if (tri_enable == LogicValue::zero) + // Tristate is disabled. + return TimingSense::none; + else + return functionSense(func, from_pin, inst); + } + } + else { + // Missing tristate enable function. + if (func->hasPort(from_port)) + // from_pin is an input to the to_pin function. + return functionSense(func, from_pin, inst); + } + } + else { + if (func->hasPort(from_port)) + // from_pin is an input to the to_pin function. + return functionSense(func, from_pin, inst); + } } } return TimingSense::unknown; } } -LogicValue -Sim::logicValue(const Pin *pin) const -{ - Vertex *vertex = graph_->pinLoadVertex(pin); - if (vertex) - return vertex->simValue(); - else { - if (network_->isHierarchical(pin)) { - const Pin *drvr_pin = findDrvrPin(pin, network_); - if (drvr_pin) - return logicValue(drvr_pin); - } - return LogicValue::unknown; - } -} - static const Pin * findDrvrPin(const Pin *pin, - Network *network) + Network *network) { PinSet *drvrs = network->drivers(pin); - if (drvrs) { - PinSet::Iterator drvr_iter(drvrs); - if (drvr_iter.hasNext()) - return drvr_iter.next(); - } - return nullptr; -} - -bool -logicValueZeroOne(LogicValue value) -{ - return value == LogicValue::zero || value == LogicValue::one; -} - -bool -Sim::logicZeroOne(const Pin *pin) const -{ - return logicValueZeroOne(logicValue(pin)); -} - -bool -Sim::logicZeroOne(const Vertex *vertex) const -{ - return logicValueZeroOne(vertex->simValue()); + if (drvrs && !drvrs->empty()) + return *drvrs->begin(); + else + return nullptr; } void Sim::clearSimValues() { - for (const Instance *inst : instances_with_const_pins_) { - // Clear sim values on all pins before evaling functions. - clearInstSimValues(inst); - annotateVertexEdges(inst, false); - } - instances_with_const_pins_.clear(); + for (auto const [pin, value] : sim_value_map_) + observer_->valueChangeAfter(pin); + sim_value_map_.clear(); + edge_timing_sense_map_.clear(); + edge_disabled_cond_set_.clear(); } -void -Sim::clearInstSimValues(const Instance *inst) -{ - debugPrint(debug_, "sim", 4, "clear %s", - network_->pathName(inst)); - InstancePinIterator *pin_iter = network_->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - setSimValue(vertex, LogicValue::unknown); - if (bidirect_drvr_vertex) - setSimValue(bidirect_drvr_vertex, LogicValue::unknown); - } - delete pin_iter; -} - -// Annotate graph edges disabled by constant values. -void -Sim::annotateGraphEdges() -{ - for (const Instance *inst : instances_to_annotate_) - annotateVertexEdges(inst, true); -} - -void -Sim::annotateVertexEdges(const Instance *inst, - bool annotate) -{ - debugPrint(debug_, "sim", 4, "annotate %s %s", - network_->pathName(inst), - annotate ? "true" : "false"); - InstancePinIterator *pin_iter = network_->pinIterator(inst); - while (pin_iter->hasNext()) { - Pin *pin = pin_iter->next(); - Vertex *vertex = graph_->pinDrvrVertex(pin); - if (vertex) - annotateVertexEdges(inst, pin, vertex, annotate); - } - delete pin_iter; -} +//////////////////////////////////////////////////////////////// void -Sim::annotateVertexEdges(const Instance *inst, - const Pin *pin, - Vertex *vertex, - bool annotate) +Sim::setIsDisabledCond(Edge *edge, + bool disabled) { - bool fanin_disables_changed = false; - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (!edge->role()->isWire()) { - Vertex *from_vertex = edge->from(graph_); - Pin *from_pin = from_vertex->pin(); - TimingSense sense = TimingSense::unknown; - bool is_disabled_cond = false; - if (annotate) { - // Set timing sense on edges in instances that have constant pins. - if (logicZeroOne(from_vertex)) - sense = TimingSense::none; - else - sense = functionSense(inst, from_pin, pin); - - if (sense != TimingSense::none) - // Disable conditional timing edges based on constant pins. - is_disabled_cond = isCondDisabled(edge, inst, from_pin, - pin, network_,sim_) - // Disable mode conditional timing - // edges based on constant pins. - || isModeDisabled(edge,inst,network_,sim_); - } - bool disables_changed = false; - if (sense != edge->simTimingSense()) { - edge->setSimTimingSense(sense); - disables_changed = true; - fanin_disables_changed = true; - } - if (is_disabled_cond != edge->isDisabledCond()) { - edge->setIsDisabledCond(is_disabled_cond); - disables_changed = true; - fanin_disables_changed = true; - } - if (observer_ && disables_changed) - observer_->fanoutEdgesChangeAfter(from_vertex); - } + if (!disabled) + edge_disabled_cond_set_.erase(edge); + else { + edge_disabled_cond_set_.insert(edge); + edge->setHasDisabledCond(true); } - if (observer_ && fanin_disables_changed) - observer_->faninEdgesChangeAfter(vertex); } bool -isCondDisabled(Edge *edge, - const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const Network *network, - Sim *sim) +Sim::isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin) { bool is_disabled; FuncExpr *disable_cond; - isCondDisabled(edge, inst, from_pin, to_pin, network, sim, - is_disabled, disable_cond); + isDisabledCond(edge, inst, from_pin, to_pin, is_disabled, disable_cond); return is_disabled; } void -isCondDisabled(Edge *edge, - const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const Network *network, - Sim *sim, - bool &is_disabled, - FuncExpr *&disable_cond) +Sim::isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond) { TimingArcSet *arc_set = edge->timingArcSet(); FuncExpr *cond = arc_set->cond(); if (cond) { - LogicValue cond_value = sim->evalExpr(cond, inst); + LogicValue cond_value = evalExpr(cond, inst); disable_cond = cond; is_disabled = (cond_value == LogicValue::zero); } @@ -889,41 +858,37 @@ isCondDisabled(Edge *edge, // Unconditional "default" arc set is disabled if another // conditional arc from/to the same pins is enabled (condition // evals to logic one). - LibertyCell *cell = network->libertyCell(inst); - LibertyPort *from_port = network->libertyPort(from_pin); - LibertyPort *to_port = network->libertyPort(to_pin); + LibertyCell *cell = network_->libertyCell(inst); + LibertyPort *from_port = network_->libertyPort(from_pin); + LibertyPort *to_port = network_->libertyPort(to_pin); is_disabled = false; for (TimingArcSet *cond_set : cell->timingArcSets(from_port, to_port)) { FuncExpr *cond = cond_set->cond(); - if (cond && sim->evalExpr(cond, inst) == LogicValue::one) { - disable_cond = cond; - is_disabled = true; - break; + if (cond && evalExpr(cond, inst) == LogicValue::one) { + disable_cond = cond; + is_disabled = true; + break; } } } } bool -isModeDisabled(Edge *edge, - const Instance *inst, - const Network *network, - Sim *sim) +Sim::isDisabledMode(Edge *edge, + const Instance *inst) { bool is_disabled; FuncExpr *disable_cond; - isModeDisabled(edge, inst, network, sim, - is_disabled, disable_cond); + isDisabledMode(edge, inst, is_disabled, disable_cond); return is_disabled; } void -isModeDisabled(Edge *edge, - const Instance *inst, - const Network *network, - Sim *sim, - bool &is_disabled, - FuncExpr *&disable_cond) +Sim::isDisabledMode(Edge *edge, + const Instance *inst, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond) { // Default values. is_disabled = false; @@ -932,35 +897,106 @@ isModeDisabled(Edge *edge, const char *mode_name = arc_set->modeName(); const char *mode_value = arc_set->modeValue(); if (mode_name && mode_value) { - LibertyCell *cell = network->libertyCell(inst); + LibertyCell *cell = network_->libertyCell(inst); ModeDef *mode_def = cell->findModeDef(mode_name); if (mode_def) { ModeValueDef *value_def = mode_def->findValueDef(mode_value); if (value_def) { - FuncExpr *cond = value_def->cond(); - if (cond) { - LogicValue cond_value = sim->evalExpr(cond, inst); - if (cond_value == LogicValue::zero) { - // For a mode value to be disabled by having a value of - // logic zero one mode value must logic one. - for (const auto [name, value_def] : *mode_def->values()) { - if (value_def) { - FuncExpr *cond1 = value_def->cond(); - if (cond1) { - LogicValue cond_value1 = sim->evalExpr(cond1, inst); - if (cond_value1 == LogicValue::one) { - disable_cond = cond; - is_disabled = true; - break; - } - } - } - } - } - } + FuncExpr *cond = value_def->cond(); + if (cond) { + LogicValue cond_value = evalExpr(cond, inst); + if (cond_value == LogicValue::zero) { + // For a mode value to be disabled by having a value of + // logic zero one mode value must logic one. + for (const auto [name, value_def] : *mode_def->values()) { + if (value_def) { + FuncExpr *cond1 = value_def->cond(); + if (cond1) { + LogicValue cond_value1 = evalExpr(cond1, inst); + if (cond_value1 == LogicValue::one) { + disable_cond = cond; + is_disabled = true; + break; + } + } + } + } + } + } } } } } +//////////////////////////////////////////////////////////////// + +// Find graph edges disabled by constant values. +void +Sim::findDisabledEdges() +{ + 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)); + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + Vertex *vertex = graph_->pinDrvrVertex(pin); + if (vertex) + findDisabledEdges(inst, pin, vertex); + } + delete pin_iter; +} + +void +Sim::findDisabledEdges(const Instance *inst, + const Pin *pin, + Vertex *vertex) +{ + bool fanin_disables_changed = false; + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (!edge->role()->isWire()) { + Vertex *from_vertex = edge->from(graph_); + Pin *from_pin = from_vertex->pin(); + TimingSense sense = TimingSense::unknown; + bool is_disabled_cond = false; + // Set timing sense on edges in instances that have constant pins. + if (isConstant(from_vertex)) + sense = TimingSense::none; + else + sense = functionSense(inst, from_pin, pin); + + 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); + + bool disables_changed = false; + if (sense != simTimingSense(edge)) { + setSimTimingSense(edge, sense); + disables_changed = true; + fanin_disables_changed = true; + } + if (is_disabled_cond != isDisabledCond(edge)) { + setIsDisabledCond(edge, is_disabled_cond); + disables_changed = true; + fanin_disables_changed = true; + } + if (observer_ && disables_changed) + observer_->fanoutEdgesChangeAfter(from_vertex->pin()); + } + } + if (observer_ && fanin_disables_changed) + observer_->faninEdgesChangeAfter(vertex->pin()); +} + } // namespace diff --git a/search/Sim.hh b/search/Sim.hh index 958e9d82b..841170947 100644 --- a/search/Sim.hh +++ b/search/Sim.hh @@ -26,9 +26,10 @@ #include #include +#include +#include #include "StaConfig.hh" // CUDD -#include "Map.hh" #include "NetworkClass.hh" #include "GraphClass.hh" #include "SdcClass.hh" @@ -39,33 +40,75 @@ namespace sta { class SimObserver; -typedef Map PinValueMap; -typedef std::queue EvalQueue; +using SimValueMap = std::unordered_map; +using EdgeDisabledCondSet = std::unordered_set; +using EdgeTimingSenseMap = std::unordered_map; +using EvalQueue = std::queue; // Propagate constants from constraints and netlist tie high/low // connections thru gates. class Sim : public StaState { public: - explicit Sim(StaState *sta); + Sim(StaState *sta); virtual ~Sim(); + virtual void copyState(const StaState *sta); void clear(); + void setMode(Mode *mode); // Set the observer for simulation value changes. void setObserver(SimObserver *observer); void ensureConstantsPropagated(); void constantsInvalid(); + + // Dertived by Sim from set_case_analysis or set_logic constant. + LogicValue simValue(const Pin *pin) const; + LogicValue simValue(const Vertex *vertex) const; + // Constant zero/one from simulation. + bool isConstant(const Pin *pin) const; + bool isConstant(const Vertex *vertex) const; + // Timing sense for the to_pin function after simplifying the + // function based constants on the instance pins. + TimingSense simTimingSense(const Edge *edge) const; + // Edge is disabled by constants in condition (when) function. + bool isDisabledCond(const Edge *edge) const; + LogicValue evalExpr(const FuncExpr *expr, - const Instance *inst); - LogicValue logicValue(const Pin *pin) const; - bool logicZeroOne(const Pin *pin) const; - bool logicZeroOne(const Vertex *vertex) const; + const Instance *inst); // Timing sense for the function between from_pin and to_pin // after simplifying the function based constants on the pins. - virtual TimingSense functionSense(const Instance *inst, - const Pin *from_pin, - const Pin *to_pin); + TimingSense functionSense(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin); + // Propagate liberty constant functions and pins tied high/low through + // combinational logic and registers (ignores Sdc). + // Used by OpenROAD/Restructure.cpp void findLogicConstants(); + // Edge cond (liberty "when") function is disabled (evals to zero). + bool isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin); + // isDisabledCond but also return the cond expression that causes + // the disable. This can differ from the edge cond expression + // when the default timing edge is disabled by another edge between + // the same pins that is enabled. + void isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond); + // Edge mode function is disabled (evals zero). + bool isDisabledMode(Edge *edge, + const Instance *inst); + void isDisabledMode(Edge *edge, + const Instance *inst, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond); + // Network edits. void deleteInstanceBefore(const Instance *inst); void makePinAfter(const Pin *pin); @@ -73,54 +116,63 @@ public: void connectPinAfter(const Pin *pin); void disconnectPinBefore(const Pin *pin); void pinSetFuncAfter(const Pin *pin); + const SimValueMap &simValues() const { return sim_value_map_; } protected: void ensureConstantFuncPins(); void recordConstPinFunc(const Pin *pin); - virtual void seedConstants(); + void seedConstants(); void seedInvalidConstants(); void propagateConstants(bool thru_sequentials); - void setConstraintConstPins(LogicValueMap &pin_value_map); + void setConstraintConstPins(const LogicValueMap &pin_value_map); void setConstFuncPins(); LogicValue pinConstFuncValue(const Pin *pin); void enqueueConstantPinInputs(); - virtual void setPinValue(const Pin *pin, - LogicValue value); + void setSimValue(const Pin *pin, + LogicValue value); + void setPinValue(const Pin *pin, + LogicValue value); + void setSimTimingSense(Edge *edge, + TimingSense sense); + void setIsDisabledCond(Edge *edge, + bool disabled); void enqueue(const Instance *inst); void evalInstance(const Instance *inst, bool thru_sequentials); LogicValue clockGateOutValue(const Instance *inst); TimingSense functionSense(const FuncExpr *expr, - const Pin *input_pin, - const Instance *inst); + const Pin *input_pin, + const Instance *inst); void functionSense(const FuncExpr *expr, - const Pin *input_pin, - const Instance *inst, - // return values - TimingSense &sense, - LogicValue &value) const; + const Pin *input_pin, + const Instance *inst, + // return values + TimingSense &sense, + LogicValue &value) const; void clearSimValues(); - virtual void clearInstSimValues(const Instance *inst); - void annotateGraphEdges(); - void annotateVertexEdges(const Instance *inst, - const Pin *pin, - Vertex *vertex, - bool annotate); - void annotateVertexEdges(const Instance *inst, - bool annotate); + + void findDisabledEdges(); + void findDisabledEdges(const Instance *inst); + void findDisabledEdges(const Instance *inst, + const Pin *pin, + Vertex *vertex); + void removePropagatedValue(const Pin *pin); void propagateFromInvalidDrvrsToLoads(); void propagateToInvalidLoads(); void propagateDrvrToLoad(const Pin *drvr_pin, - const Pin *load_pin); - void setSimValue(Vertex *vertex, - LogicValue value); + const Pin *load_pin); DdNode *funcBddSim(const FuncExpr *expr, const Instance *inst); + Mode *mode_; SimObserver *observer_; bool valid_; bool incremental_; + // Constants propagated by Sim.cc + SimValueMap sim_value_map_; + EdgeDisabledCondSet edge_disabled_cond_set_; + EdgeTimingSenseMap edge_timing_sense_map_; // Cache of pins that have constant functions (tie high and tie low // cell instances). PinSet const_func_pins_; @@ -132,63 +184,20 @@ protected: // Load pins that waiting for the driver constant to propagate. PinSet invalid_load_pins_; EvalQueue eval_queue_; - // Instances with constant pin values for annotateVertexEdges. - InstanceSet instances_with_const_pins_; InstanceSet instances_to_annotate_; Bdd bdd_; mutable std::mutex bdd_lock_; }; // Abstract base class for Sim value change observer. -class SimObserver +class SimObserver : public StaState { public: - SimObserver() {} + SimObserver(StaState *sta); virtual ~SimObserver() {} - virtual void valueChangeAfter(Vertex *vertex) = 0; - virtual void faninEdgesChangeAfter(Vertex *vertex) = 0; - virtual void fanoutEdgesChangeAfter(Vertex *vertex) = 0; + virtual void valueChangeAfter(const Pin *pin) = 0; + virtual void faninEdgesChangeAfter(const Pin *pin) = 0; + virtual void fanoutEdgesChangeAfter(const Pin *pin) = 0; }; -bool -logicValueZeroOne(LogicValue value); - -// Edge cond (liberty "when") function is disabled (evals to zero). -bool -isCondDisabled(Edge *edge, - const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const Network *network, - Sim *sim); - -// isCondDisabled but also return the cond expression that causes -// the disable. This can differ from the edge cond expression -// when the default timing edge is disabled by another edge between -// the same pins that is enabled. -void -isCondDisabled(Edge *edge, - const Instance *inst, - const Pin *from_pin, - const Pin *to_pin, - const Network *network, - Sim *sim, - bool &is_disabled, - FuncExpr *&disable_cond); - - -// Edge mode function is disabled (evals to zero). -bool -isModeDisabled(Edge *edge, - const Instance *inst, - const Network *network, - Sim *sim); -void -isModeDisabled(Edge *edge, - const Instance *inst, - const Network *network, - Sim *sim, - bool &is_disabled, - FuncExpr *&disable_cond); - } // namespace diff --git a/search/Sta.cc b/search/Sta.cc index eb3896469..b79a74c9a 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -24,13 +24,17 @@ #include "Sta.hh" +#include + #include "Machine.hh" +#include "ContainerHelpers.hh" #include "DispatchQueue.hh" #include "ReportTcl.hh" #include "Debug.hh" #include "Stats.hh" #include "Fuzzy.hh" #include "Units.hh" +#include "PatternMatch.hh" #include "TimingArc.hh" #include "FuncExpr.hh" #include "EquivCells.hh" @@ -44,10 +48,10 @@ #include "Graph.hh" #include "GraphCmp.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Variables.hh" #include "WriteSdc.hh" #include "ExceptionPath.hh" -#include "MakeConcreteParasitics.hh" #include "Parasitics.hh" #include "parasitics/SpefReader.hh" #include "parasitics/ReportParasiticAnnotation.hh" @@ -59,15 +63,13 @@ #include "Sim.hh" #include "ClkInfo.hh" #include "TagGroup.hh" -#include "PathAnalysisPt.hh" -#include "Corner.hh" #include "Search.hh" #include "Latches.hh" #include "PathGroup.hh" #include "CheckTiming.hh" -#include "CheckSlewLimits.hh" -#include "CheckFanoutLimits.hh" -#include "CheckCapacitanceLimits.hh" +#include "CheckSlews.hh" +#include "CheckFanouts.hh" +#include "CheckCapacitances.hh" #include "CheckMinPulseWidths.hh" #include "CheckMinPeriods.hh" #include "CheckMaxSkews.hh" @@ -81,6 +83,7 @@ #include "VisitPathEnds.hh" #include "PathExpanded.hh" #include "MakeTimingModel.hh" +#include "parasitics/ConcreteParasitics.hh" #include "spice/WritePathSpice.hh" namespace sta { @@ -89,14 +92,12 @@ using std::string; using std::min; using std::max; -static const ClockEdge *clk_edge_wildcard = reinterpret_cast(1); - static bool libertyPortCapsEqual(const LibertyPort *port1, const LibertyPort *port2); static bool hasDisabledArcs(Edge *edge, - Graph *graph); + const Mode *mode); static InstanceSet pinInstances(PinSet &pins, const Network *network); @@ -114,7 +115,7 @@ pinInstances(PinSet &pins, class StaDelayCalcObserver : public DelayCalcObserver { public: - explicit StaDelayCalcObserver(Search *search); + StaDelayCalcObserver(Search *search); virtual void delayChangedFrom(Vertex *vertex); virtual void delayChangedTo(Vertex *vertex); virtual void checkDelayChangedTo(Vertex *vertex); @@ -152,26 +153,14 @@ StaDelayCalcObserver::checkDelayChangedTo(Vertex *vertex) class StaSimObserver : public SimObserver { public: - StaSimObserver(GraphDelayCalc *graph_delay_calc, - Levelize *levelize, - Search *search); - virtual void valueChangeAfter(Vertex *vertex); - virtual void faninEdgesChangeAfter(Vertex *vertex); - virtual void fanoutEdgesChangeAfter(Vertex *vertex); - -private: - GraphDelayCalc *graph_delay_calc_; - Levelize *levelize_; - Search *search_; + StaSimObserver(StaState *sta); + void valueChangeAfter(const Pin *pin) override; + void faninEdgesChangeAfter(const Pin *pin) override; + void fanoutEdgesChangeAfter(const Pin *pin) override; }; -StaSimObserver::StaSimObserver(GraphDelayCalc *graph_delay_calc, - Levelize *levelize, - Search *search) : - SimObserver(), - graph_delay_calc_(graph_delay_calc), - levelize_(levelize), - search_(search) +StaSimObserver::StaSimObserver(StaState *sta) : + SimObserver(sta) { } @@ -180,26 +169,28 @@ StaSimObserver::StaSimObserver(GraphDelayCalc *graph_delay_calc, // because the search predicate does not search through constants. // This observer makes sure the delays and arrivals are invalidated. void -StaSimObserver::valueChangeAfter(Vertex *vertex) +StaSimObserver::valueChangeAfter(const Pin *pin) { - graph_delay_calc_->delayInvalid(vertex); + Vertex *vertex = graph_->pinDrvrVertex(pin); + if (vertex) { search_->arrivalInvalid(vertex); search_->requiredInvalid(vertex); search_->endpointInvalid(vertex); - levelize_->invalidFrom(vertex); + } } void -StaSimObserver::faninEdgesChangeAfter(Vertex *vertex) +StaSimObserver::faninEdgesChangeAfter(const Pin *pin) { - graph_delay_calc_->delayInvalid(vertex); + Vertex *vertex = graph_->pinDrvrVertex(pin); search_->arrivalInvalid(vertex); search_->endpointInvalid(vertex); } void -StaSimObserver::fanoutEdgesChangeAfter(Vertex *vertex) +StaSimObserver::fanoutEdgesChangeAfter(const Pin *pin) { + Vertex *vertex = graph_->pinDrvrVertex(pin); search_->requiredInvalid(vertex); search_->endpointInvalid(vertex); } @@ -272,13 +263,13 @@ Sta *Sta::sta_; Sta::Sta() : StaState(), + cmd_scene_(nullptr), current_instance_(nullptr), - cmd_corner_(nullptr), verilog_reader_(nullptr), check_timing_(nullptr), - check_slew_limits_(nullptr), - check_fanout_limits_(nullptr), - check_capacitance_limits_(nullptr), + check_slews_(nullptr), + check_fanouts_(nullptr), + check_capacitances_(nullptr), check_min_pulse_widths_(nullptr), check_min_periods_(nullptr), check_max_skews_(nullptr), @@ -287,9 +278,6 @@ Sta::Sta() : power_(nullptr), update_genclks_(false), equiv_cells_(nullptr), - graph_sdc_annotated_(false), - // Default to same parasitics for all corners. - parasitics_per_corner_(false), properties_(this) { } @@ -302,16 +290,12 @@ Sta::makeComponents() makeDebug(); makeUnits(); makeNetwork(); - makeSdc(); makeLevelize(); - makeParasitics(); - makeCorners(); + makeDefaultScene(); makeArcDelayCalc(); makeGraphDelayCalc(); - makeSim(); makeSearch(); makeLatches(); - makeClkNetwork(); makeSdcNetwork(); makeReportPath(); makePower(); @@ -322,15 +306,14 @@ Sta::makeComponents() updateComponentsState(); makeObservers(); - // This must follow updateComponentsState. - makeParasiticAnalysisPts(); } void Sta::makeObservers() { graph_delay_calc_->setObserver(new StaDelayCalcObserver(search_)); - sim_->setObserver(new StaSimObserver(graph_delay_calc_, levelize_, search_)); + for (Mode *mode : modes_) + mode->sim()->setObserver(new StaSimObserver(this)); levelize_->setObserver(new StaLevelizeObserver(search_, graph_delay_calc_)); } @@ -365,20 +348,18 @@ Sta::updateComponentsState() sdc_network_->copyState(this); if (graph_) graph_->copyState(this); - sdc_->copyState(this); - corners_->copyState(this); + for (Mode *mode : modes_) + mode->copyState(this); levelize_->copyState(this); - parasitics_->copyState(this); arc_delay_calc_->copyState(this); - sim_->copyState(this); search_->copyState(this); latches_->copyState(this); graph_delay_calc_->copyState(this); report_path_->copyState(this); if (check_timing_) check_timing_->copyState(this); - clk_network_->copyState(this); clk_skews_->copyState(this); + if (power_) power_->copyState(this); } @@ -407,24 +388,12 @@ Sta::makeNetwork() network_ = makeConcreteNetwork(); } -void -Sta::makeSdc() -{ - sdc_ = new Sdc(this); -} - void Sta::makeLevelize() { levelize_ = new Levelize(this); } -void -Sta::makeParasitics() -{ - parasitics_ = makeConcreteParasitics(this); -} - void Sta::makeArcDelayCalc() { @@ -437,12 +406,6 @@ Sta::makeGraphDelayCalc() graph_delay_calc_ = new GraphDelayCalc(this); } -void -Sta::makeSim() -{ - sim_ = new Sim(this); -} - void Sta::makeSearch() { @@ -468,21 +431,21 @@ Sta::makeCheckTiming() } void -Sta::makeCheckSlewLimits() +Sta::makeCheckSlews() { - check_slew_limits_ = new CheckSlewLimits(this); + check_slews_ = new CheckSlews(this); } void -Sta::makeCheckFanoutLimits() +Sta::makeCheckFanouts() { - check_fanout_limits_ = new CheckFanoutLimits(this); + check_fanouts_ = new CheckFanouts(this); } void -Sta::makeCheckCapacitanceLimits() +Sta::makeCheckCapacitances() { - check_capacitance_limits_ = new CheckCapacitanceLimits(this); + check_capacitances_ = new CheckCapacitances(this); } void @@ -509,12 +472,6 @@ Sta::makeReportPath() report_path_ = new ReportPath(this); } -void -Sta::makeClkNetwork() -{ - clk_network_ = new ClkNetwork(this); -} - void Sta::makePower() { @@ -546,9 +503,9 @@ Sta::~Sta() // to deleted before the network. delete verilog_reader_; // Delete "top down" to minimize chance of referencing deleted memory. - delete check_slew_limits_; - delete check_fanout_limits_; - delete check_capacitance_limits_; + delete check_slews_; + delete check_fanouts_; + delete check_capacitances_; delete check_min_pulse_widths_; delete check_min_periods_; delete check_max_skews_; @@ -558,39 +515,36 @@ Sta::~Sta() // Sdc references search filter, so delete search first. delete search_; delete latches_; - delete parasitics_; delete arc_delay_calc_; delete graph_delay_calc_; - delete sim_; delete levelize_; - delete sdc_; - delete corners_; delete graph_; delete sdc_network_; delete network_; delete debug_; delete units_; delete report_; - delete clk_network_; delete power_; delete equiv_cells_; delete dispatch_queue_; + deleteContents(parasitics_name_map_); + deleteContents(modes_); + deleteContents(scenes_); } void Sta::clear() { - clkPinsInvalid(); - // Constraints reference search filter, so clear search first. + // Sdc holds search filter, so clear search first. search_->clear(); - sdc_->clear(); - graph_sdc_annotated_ = false; - // corners are NOT cleared because they are used to index liberty files. + for (Mode *mode : modes_) { + mode->sdc()->clear(); + mode->clkNetwork()->clkPinsInvalid(); + } + // scenes are NOT cleared because they are used to index liberty files. levelize_->clear(); - if (parasitics_) - parasitics_->clear(); + deleteParasitics(); graph_delay_calc_->clear(); - sim_->clear(); power_->clear(); if (check_min_pulse_widths_) check_min_pulse_widths_->clear(); @@ -604,16 +558,68 @@ Sta::clear() updateComponentsState(); } +Sdc * +Sta::cmdSdc() const +{ + return cmdMode()->sdc(); +} + +void +Sta::setCmdMode(const string &mode_name) +{ + if (!mode_name.empty()) { + if (!mode_name_map_.contains(mode_name)) { + if (modes_.size() == 1 + && modes_[0]->name() == "default") { + // No need for default mode if one is defined. + delete modes_[0]; + mode_name_map_.clear(); + modes_.clear(); + } + Mode *mode = new Mode(mode_name, mode_name_map_.size(), this); + mode_name_map_[mode_name] = mode; + modes_.push_back(mode); + mode->sim()->setMode(mode); + mode->sim()->setObserver(new StaSimObserver(this)); + + if (scenes_.size() == 1 + && scenes_[0]->name() == "default") + scenes_[0]->setMode(mode); + updateComponentsState(); + } + } +} + +Mode * +Sta::findMode(const std::string &mode_name) const +{ + return findKey(mode_name_map_, mode_name); +} + +ModeSeq +Sta::findModes(const std::string &name) const +{ + ModeSeq matches; + PatternMatch pattern(name.c_str()); + for (Mode *mode : modes_) { + if (pattern.match(mode->name())) + matches.push_back(mode); + } + return matches; +} + void Sta::networkChanged() { // Everything else from clear(). search_->clear(); levelize_->clear(); - if (parasitics_) - parasitics_->clear(); + deleteContents(parasitics_name_map_); graph_delay_calc_->clear(); - sim_->clear(); + for (const Mode *mode : modes_) + mode->sim()->clear(); + for (Scene *scene : scenes_) + scene->setParasitics(nullptr, MinMaxAll::minMax()); if (check_min_pulse_widths_) check_min_pulse_widths_->clear(); if (check_min_periods_) @@ -621,7 +627,6 @@ Sta::networkChanged() clk_skews_->clear(); delete graph_; graph_ = nullptr; - graph_sdc_annotated_ = false; current_instance_ = nullptr; updateComponentsState(); } @@ -685,12 +690,12 @@ Sta::setCurrentInstance(Instance *inst) LibertyLibrary * Sta::readLiberty(const char *filename, - Corner *corner, + Scene *scene, const MinMaxAll *min_max, bool infer_latches) { Stats stats(debug_, report_); - LibertyLibrary *library = readLibertyFile(filename, corner, min_max, + LibertyLibrary *library = readLibertyFile(filename, scene, min_max, infer_latches); if (library // The default library is the first library read. @@ -706,7 +711,7 @@ Sta::readLiberty(const char *filename, LibertyLibrary * Sta::readLibertyFile(const char *filename, - Corner *corner, + Scene *scene, const MinMaxAll *min_max, bool infer_latches) { @@ -716,11 +721,11 @@ Sta::readLibertyFile(const char *filename, // Don't map liberty cells if they are redefined by reading another // library with the same cell names. if (min_max == MinMaxAll::all()) { - readLibertyAfter(liberty, corner, MinMax::min()); - readLibertyAfter(liberty, corner, MinMax::max()); + readLibertyAfter(liberty, scene, MinMax::min()); + readLibertyAfter(liberty, scene, MinMax::max()); } else - readLibertyAfter(liberty, corner, min_max->asMinMax()); + readLibertyAfter(liberty, scene, min_max->asMinMax()); network_->readLibertyAfter(liberty); } return liberty; @@ -735,11 +740,11 @@ Sta::readLibertyFile(const char *filename, void Sta::readLibertyAfter(LibertyLibrary *liberty, - Corner *corner, + Scene *scene, const MinMax *min_max) { - corner->addLiberty(liberty, min_max); - LibertyLibrary::makeCornerMap(liberty, corner->libertyIndex(min_max), + scene->addLiberty(liberty, min_max); + LibertyLibrary::makeSceneMap(liberty, scene->libertyIndex(min_max), network_, report_); } @@ -791,38 +796,40 @@ Sta::setDebugLevel(const char *what, //////////////////////////////////////////////////////////////// void -Sta::setAnalysisType(AnalysisType analysis_type) +Sta::setAnalysisType(AnalysisType analysis_type, + Sdc *sdc) { - if (analysis_type != sdc_->analysisType()) { - sdc_->setAnalysisType(analysis_type); + if (analysis_type != sdc->analysisType()) { + sdc->setAnalysisType(analysis_type); delaysInvalid(); search_->deletePathGroups(); - corners_->analysisTypeChanged(); if (graph_) - graph_->setDelayCount(corners_->dcalcAnalysisPtCount()); + graph_->setDelayCount(dcalcAnalysisPtCount()); } } OperatingConditions * -Sta::operatingConditions(const MinMax *min_max) const +Sta::operatingConditions(const MinMax *min_max, + const Sdc *sdc) const { - return sdc_->operatingConditions(min_max); + return sdc->operatingConditions(min_max); } void Sta::setOperatingConditions(OperatingConditions *op_cond, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->setOperatingConditions(op_cond, min_max); - corners_->operatingConditionsChanged(); + sdc->setOperatingConditions(op_cond, min_max); delaysInvalid(); } const Pvt * Sta::pvt(Instance *inst, - const MinMax *min_max) + const MinMax *min_max, + Sdc *sdc) { - return sdc_->pvt(inst, min_max); + return sdc->pvt(inst, min_max); } void @@ -830,34 +837,38 @@ Sta::setPvt(Instance *inst, const MinMaxAll *min_max, float process, float voltage, - float temperature) + float temperature, + Sdc *sdc) { Pvt pvt(process, voltage, temperature); - setPvt(inst, min_max, pvt); + setPvt(inst, min_max, pvt, sdc); } void Sta::setPvt(const Instance *inst, const MinMaxAll *min_max, - const Pvt &pvt) + const Pvt &pvt, + Sdc *sdc) { - sdc_->setPvt(inst, min_max, pvt); + sdc->setPvt(inst, min_max, pvt); delaysInvalidFrom(inst); } void Sta::setVoltage(const MinMax *min_max, - float voltage) + float voltage, + Sdc *sdc) { - sdc_->setVoltage(min_max, voltage); + sdc->setVoltage(min_max, voltage); } void Sta::setVoltage(const Net *net, const MinMax *min_max, - float voltage) + float voltage, + Sdc *sdc) { - sdc_->setVoltage(net, min_max, voltage); + sdc->setVoltage(net, min_max, voltage); } void @@ -865,9 +876,10 @@ Sta::setTimingDerate(TimingDerateType type, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, - float derate) + float derate, + Sdc *sdc) { - sdc_->setTimingDerate(type, clk_data, rf, early_late, derate); + sdc->setTimingDerate(type, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); @@ -878,9 +890,10 @@ Sta::setTimingDerate(const Net *net, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, - float derate) + float derate, + Sdc *sdc) { - sdc_->setTimingDerate(net, clk_data, rf, early_late, derate); + sdc->setTimingDerate(net, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); @@ -892,9 +905,10 @@ Sta::setTimingDerate(const Instance *inst, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, - float derate) + float derate, + Sdc *sdc) { - sdc_->setTimingDerate(inst, type, clk_data, rf, early_late, derate); + sdc->setTimingDerate(inst, type, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); @@ -906,18 +920,19 @@ Sta::setTimingDerate(const LibertyCell *cell, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, - float derate) + float derate, + Sdc *sdc) { - sdc_->setTimingDerate(cell, type, clk_data, rf, early_late, derate); + sdc->setTimingDerate(cell, type, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); } void -Sta::unsetTimingDerate() +Sta::unsetTimingDerate(Sdc *sdc) { - sdc_->unsetTimingDerate(); + sdc->unsetTimingDerate(); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); @@ -927,9 +942,10 @@ void Sta::setInputSlew(const Port *port, const RiseFallBoth *rf, const MinMaxAll *min_max, - float slew) + float slew, + Sdc *sdc) { - sdc_->setInputSlew(port, rf, min_max, slew); + sdc->setInputSlew(port, rf, min_max, slew); delaysInvalidFrom(port); } @@ -941,9 +957,10 @@ Sta::setDriveCell(const LibertyLibrary *library, float *from_slews, const LibertyPort *to_port, const RiseFallBoth *rf, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->setDriveCell(library, cell, port, from_port, from_slews, to_port, + sdc->setDriveCell(library, cell, port, from_port, from_slews, to_port, rf, min_max); delaysInvalidFrom(port); } @@ -952,87 +969,98 @@ void Sta::setDriveResistance(const Port *port, const RiseFallBoth *rf, const MinMaxAll *min_max, - float res) + float res, + Sdc *sdc) { - sdc_->setDriveResistance(port, rf, min_max, res); + sdc->setDriveResistance(port, rf, min_max, res); delaysInvalidFrom(port); } void Sta::setLatchBorrowLimit(const Pin *pin, - float limit) + float limit, + Sdc *sdc) { - sdc_->setLatchBorrowLimit(pin, limit); + sdc->setLatchBorrowLimit(pin, limit); search_->requiredInvalid(pin); } void Sta::setLatchBorrowLimit(const Instance *inst, - float limit) + float limit, + Sdc *sdc) { - sdc_->setLatchBorrowLimit(inst, limit); + sdc->setLatchBorrowLimit(inst, limit); search_->requiredInvalid(inst); } void Sta::setLatchBorrowLimit(const Clock *clk, - float limit) + float limit, + Sdc *sdc) { - sdc_->setLatchBorrowLimit(clk, limit); + sdc->setLatchBorrowLimit(clk, limit); search_->arrivalsInvalid(); } void Sta::setMinPulseWidth(const RiseFallBoth *rf, - float min_width) + float min_width, + Sdc *sdc) { - sdc_->setMinPulseWidth(rf, min_width); + sdc->setMinPulseWidth(rf, min_width); } void Sta::setMinPulseWidth(const Pin *pin, const RiseFallBoth *rf, - float min_width) + float min_width, + Sdc *sdc) { - sdc_->setMinPulseWidth(pin, rf, min_width); + sdc->setMinPulseWidth(pin, rf, min_width); } void Sta::setMinPulseWidth(const Instance *inst, const RiseFallBoth *rf, - float min_width) + float min_width, + Sdc *sdc) { - sdc_->setMinPulseWidth(inst, rf, min_width); + sdc->setMinPulseWidth(inst, rf, min_width); } void Sta::setMinPulseWidth(const Clock *clk, const RiseFallBoth *rf, - float min_width) + float min_width, + Sdc *sdc) { - sdc_->setMinPulseWidth(clk, rf, min_width); + sdc->setMinPulseWidth(clk, rf, min_width); } void -Sta::setWireloadMode(WireloadMode mode) +Sta::setWireloadMode(WireloadMode mode, + Sdc *sdc) { - sdc_->setWireloadMode(mode); + sdc->setWireloadMode(mode); delaysInvalid(); } void Sta::setWireload(Wireload *wireload, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->setWireload(wireload, min_max); + sdc->setWireload(wireload, min_max); delaysInvalid(); } void Sta::setWireloadSelection(WireloadSelection *selection, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->setWireloadSelection(selection, min_max); + sdc->setWireloadSelection(selection, min_max); delaysInvalid(); } @@ -1041,71 +1069,80 @@ Sta::setSlewLimit(Clock *clk, const RiseFallBoth *rf, const PathClkOrData clk_data, const MinMax *min_max, - float slew) + float slew, + Sdc *sdc) { - sdc_->setSlewLimit(clk, rf, clk_data, min_max, slew); + sdc->setSlewLimit(clk, rf, clk_data, min_max, slew); } void Sta::setSlewLimit(Port *port, const MinMax *min_max, - float slew) + float slew, + Sdc *sdc) { - sdc_->setSlewLimit(port, min_max, slew); + sdc->setSlewLimit(port, min_max, slew); } void Sta::setSlewLimit(Cell *cell, const MinMax *min_max, - float slew) + float slew, + Sdc *sdc) { - sdc_->setSlewLimit(cell, min_max, slew); + sdc->setSlewLimit(cell, min_max, slew); } void Sta::setCapacitanceLimit(Cell *cell, const MinMax *min_max, - float cap) + float cap, + Sdc *sdc) { - sdc_->setCapacitanceLimit(cell, min_max, cap); + sdc->setCapacitanceLimit(cell, min_max, cap); } void Sta::setCapacitanceLimit(Port *port, const MinMax *min_max, - float cap) + float cap, + Sdc *sdc) { - sdc_->setCapacitanceLimit(port, min_max, cap); + sdc->setCapacitanceLimit(port, min_max, cap); } void Sta::setCapacitanceLimit(Pin *pin, const MinMax *min_max, - float cap) + float cap, + Sdc *sdc) { - sdc_->setCapacitanceLimit(pin, min_max, cap); + sdc->setCapacitanceLimit(pin, min_max, cap); } void Sta::setFanoutLimit(Cell *cell, const MinMax *min_max, - float fanout) + float fanout, + Sdc *sdc) { - sdc_->setFanoutLimit(cell, min_max, fanout); + sdc->setFanoutLimit(cell, min_max, fanout); } void Sta::setFanoutLimit(Port *port, const MinMax *min_max, - float fanout) + float fanout, + Sdc *sdc) { - sdc_->setFanoutLimit(port, min_max, fanout); + sdc->setFanoutLimit(port, min_max, fanout); } void -Sta::setMaxArea(float area) +Sta::setMaxArea(float area, + Sdc *sdc) { - sdc_->setMaxArea(area); + sdc->setMaxArea(area); } void @@ -1114,12 +1151,14 @@ Sta::makeClock(const char *name, bool add_to_pins, float period, FloatSeq *waveform, - char *comment) + char *comment, + const Mode *mode) { - sdc_->makeClock(name, pins, add_to_pins, period, waveform, comment); + mode->sdc()->makeClock(name, pins, add_to_pins, period, waveform, comment); update_genclks_ = true; search_->arrivalsInvalid(); power_->activitiesInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void @@ -1135,9 +1174,10 @@ Sta::makeGeneratedClock(const char *name, bool combinational, IntSeq *edges, FloatSeq *edge_shifts, - char *comment) + char *comment, + const Mode *mode) { - sdc_->makeGeneratedClock(name, pins, add_to_pins, + mode->sdc()->makeGeneratedClock(name, pins, add_to_pins, src_pin, master_clk, divide_by, multiply_by, duty_cycle, invert, combinational, @@ -1145,68 +1185,77 @@ Sta::makeGeneratedClock(const char *name, update_genclks_ = true; search_->arrivalsInvalid(); power_->activitiesInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void -Sta::removeClock(Clock *clk) +Sta::removeClock(Clock *clk, + Sdc *sdc) { - sdc_->removeClock(clk); + sdc->removeClock(clk); search_->arrivalsInvalid(); power_->activitiesInvalid(); } bool -Sta::isClockSrc(const Pin *pin) const +Sta::isClockSrc(const Pin *pin, + const Sdc *sdc) const { - return sdc_->isClock(pin); + return sdc->isClock(pin); } void -Sta::setPropagatedClock(Clock *clk) +Sta::setPropagatedClock(Clock *clk, + const Mode *mode) { - sdc_->setPropagatedClock(clk); + mode->sdc()->setPropagatedClock(clk); delaysInvalid(); - clkPinsInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void -Sta::removePropagatedClock(Clock *clk) +Sta::removePropagatedClock(Clock *clk, + const Mode *mode) { - sdc_->removePropagatedClock(clk); + mode->sdc()->removePropagatedClock(clk); delaysInvalid(); - clkPinsInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void -Sta::setPropagatedClock(Pin *pin) +Sta::setPropagatedClock(Pin *pin, + const Mode *mode) { - sdc_->setPropagatedClock(pin); + mode->sdc()->setPropagatedClock(pin); delaysInvalid(); - clkPinsInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void -Sta::removePropagatedClock(Pin *pin) +Sta::removePropagatedClock(Pin *pin, + const Mode *mode) { - sdc_->removePropagatedClock(pin); + mode->sdc()->removePropagatedClock(pin); delaysInvalid(); - clkPinsInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } void Sta::setClockSlew(Clock *clk, const RiseFallBoth *rf, const MinMaxAll *min_max, - float slew) + float slew, + Sdc *sdc) { - sdc_->setClockSlew(clk, rf, min_max, slew); + sdc->setClockSlew(clk, rf, min_max, slew); clockSlewChanged(clk); } void -Sta::removeClockSlew(Clock *clk) +Sta::removeClockSlew(Clock *clk, + Sdc *sdc) { - sdc_->removeClockSlew(clk); + sdc->removeClockSlew(clk); clockSlewChanged(clk); } @@ -1223,36 +1272,19 @@ Sta::setClockLatency(Clock *clk, Pin *pin, const RiseFallBoth *rf, const MinMaxAll *min_max, - float delay) + float delay, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->setClockLatency(clk, pin, rf, min_max, delay); + sdc->setClockLatency(clk, pin, rf, min_max, delay); search_->arrivalsInvalid(); } -void -Sta::sdcChangedGraph() -{ - if (graph_sdc_annotated_) - sdc_->removeGraphAnnotations(); - graph_sdc_annotated_ = false; -} - -void -Sta::ensureGraphSdcAnnotated() -{ - if (!graph_sdc_annotated_) { - sdc_->annotateGraph(); - graph_sdc_annotated_ = true; - } -} - void Sta::removeClockLatency(const Clock *clk, - const Pin *pin) + const Pin *pin, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->removeClockLatency(clk, pin); + sdc->removeClockLatency(clk, pin); search_->arrivalsInvalid(); } @@ -1262,17 +1294,19 @@ Sta::setClockInsertion(const Clock *clk, const RiseFallBoth *rf, const MinMaxAll *min_max, const EarlyLateAll *early_late, - float delay) + float delay, + Sdc *sdc) { - sdc_->setClockInsertion(clk, pin, rf, min_max, early_late, delay); + sdc->setClockInsertion(clk, pin, rf, min_max, early_late, delay); search_->arrivalsInvalid(); } void Sta::removeClockInsertion(const Clock *clk, - const Pin *pin) + const Pin *pin, + Sdc *sdc) { - sdc_->removeClockInsertion(clk, pin); + sdc->removeClockInsertion(clk, pin); search_->arrivalsInvalid(); } @@ -1296,17 +1330,19 @@ Sta::removeClockUncertainty(Clock *clk, void Sta::setClockUncertainty(Pin *pin, const SetupHoldAll *setup_hold, - float uncertainty) + float uncertainty, + Sdc *sdc) { - sdc_->setClockUncertainty(pin, setup_hold, uncertainty); + sdc->setClockUncertainty(pin, setup_hold, uncertainty); search_->arrivalsInvalid(); } void Sta::removeClockUncertainty(Pin *pin, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold, + Sdc *sdc) { - sdc_->removeClockUncertainty(pin, setup_hold); + sdc->removeClockUncertainty(pin, setup_hold); search_->arrivalsInvalid(); } @@ -1316,9 +1352,10 @@ Sta::setClockUncertainty(Clock *from_clk, Clock *to_clk, const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold, - float uncertainty) + float uncertainty, + Sdc *sdc) { - sdc_->setClockUncertainty(from_clk, from_rf, to_clk, to_rf, + sdc->setClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold, uncertainty); search_->arrivalsInvalid(); } @@ -1328,9 +1365,10 @@ Sta::removeClockUncertainty(Clock *from_clk, const RiseFallBoth *from_rf, Clock *to_clk, const RiseFallBoth *to_rf, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold, + Sdc *sdc) { - sdc_->removeClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold); + sdc->removeClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold); search_->arrivalsInvalid(); } @@ -1340,9 +1378,10 @@ Sta::makeClockGroups(const char *name, bool physically_exclusive, bool asynchronous, bool allow_paths, - const char *comment) + const char *comment, + Sdc *sdc) { - ClockGroups *groups = sdc_->makeClockGroups(name, + ClockGroups *groups = sdc->makeClockGroups(name, logically_exclusive, physically_exclusive, asynchronous, @@ -1353,39 +1392,44 @@ Sta::makeClockGroups(const char *name, } void -Sta::removeClockGroupsLogicallyExclusive(const char *name) +Sta::removeClockGroupsLogicallyExclusive(const char *name, + Sdc *sdc) { - sdc_->removeClockGroupsLogicallyExclusive(name); + sdc->removeClockGroupsLogicallyExclusive(name); search_->requiredsInvalid(); } void -Sta::removeClockGroupsPhysicallyExclusive(const char *name) +Sta::removeClockGroupsPhysicallyExclusive(const char *name, + Sdc *sdc) { - sdc_->removeClockGroupsPhysicallyExclusive(name); + sdc->removeClockGroupsPhysicallyExclusive(name); search_->requiredsInvalid(); } void -Sta::removeClockGroupsAsynchronous(const char *name) +Sta::removeClockGroupsAsynchronous(const char *name, + Sdc *sdc) { - sdc_->removeClockGroupsAsynchronous(name); + sdc->removeClockGroupsAsynchronous(name); search_->requiredsInvalid(); } void Sta::makeClockGroup(ClockGroups *clk_groups, - ClockSet *clks) + ClockSet *clks, + Sdc *sdc) { - sdc_->makeClockGroup(clk_groups, clks); + sdc->makeClockGroup(clk_groups, clks); } void Sta::setClockSense(PinSet *pins, ClockSet *clks, - ClockSense sense) + ClockSense sense, + Sdc *sdc) { - sdc_->setClockSense(pins, clks, sense); + sdc->setClockSense(pins, clks, sense); search_->arrivalsInvalid(); } @@ -1394,9 +1438,10 @@ Sta::setClockSense(PinSet *pins, void Sta::setClockGatingCheck(const RiseFallBoth *rf, const SetupHold *setup_hold, - float margin) + float margin, + Sdc *sdc) { - sdc_->setClockGatingCheck(rf, setup_hold, margin); + sdc->setClockGatingCheck(rf, setup_hold, margin); search_->arrivalsInvalid(); } @@ -1404,9 +1449,10 @@ void Sta::setClockGatingCheck(Clock *clk, const RiseFallBoth *rf, const SetupHold *setup_hold, - float margin) + float margin, + Sdc *sdc) { - sdc_->setClockGatingCheck(clk, rf, setup_hold, margin); + sdc->setClockGatingCheck(clk, rf, setup_hold, margin); search_->arrivalsInvalid(); } @@ -1415,9 +1461,10 @@ Sta::setClockGatingCheck(Instance *inst, const RiseFallBoth *rf, const SetupHold *setup_hold, float margin, - LogicValue active_value) + 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(); } @@ -1426,9 +1473,10 @@ Sta::setClockGatingCheck(Pin *pin, const RiseFallBoth *rf, const SetupHold *setup_hold, float margin, - LogicValue active_value) + 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(); } @@ -1439,10 +1487,10 @@ Sta::setDataCheck(Pin *from, const RiseFallBoth *to_rf, Clock *clk, const SetupHoldAll *setup_hold, - float margin) + float margin, + Sdc *sdc) { - sdcChangedGraph(); - 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); } @@ -1452,33 +1500,30 @@ Sta::removeDataCheck(Pin *from, Pin *to, const RiseFallBoth *to_rf, Clock *clk, - const SetupHoldAll *setup_hold) + const SetupHoldAll *setup_hold, + Sdc *sdc) { - sdc_->removeDataCheck(from, from_rf, to, to_rf, clk, setup_hold); + sdc->removeDataCheck(from, from_rf, to, to_rf, clk, setup_hold); search_->requiredInvalid(to); } //////////////////////////////////////////////////////////////// void -Sta::disable(Pin *pin) +Sta::disable(Pin *pin, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->disable(pin); - // Levelization respects disabled edges. - levelize_->invalid(); + sdc->disable(pin); graph_delay_calc_->delayInvalid(pin); search_->arrivalsInvalid(); } void -Sta::removeDisable(Pin *pin) +Sta::removeDisable(Pin *pin, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->removeDisable(pin); + sdc->removeDisable(pin); disableAfter(); - // Levelization respects disabled edges. - levelize_->invalid(); graph_delay_calc_->delayInvalid(pin); search_->arrivalsInvalid(); } @@ -1486,11 +1531,10 @@ Sta::removeDisable(Pin *pin) void Sta::disable(Instance *inst, LibertyPort *from, - LibertyPort *to) + LibertyPort *to, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->disable(inst, from, to); - + sdc->disable(inst, from, to); if (from) { Pin *from_pin = network_->findPin(inst, from); graph_delay_calc_->delayInvalid(from_pin); @@ -1507,19 +1551,16 @@ Sta::disable(Instance *inst, } delete pin_iter; } - // Levelization respects disabled edges. - levelize_->invalid(); search_->arrivalsInvalid(); } void Sta::removeDisable(Instance *inst, LibertyPort *from, - LibertyPort *to) + LibertyPort *to, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->removeDisable(inst, from, to); - + sdc->removeDisable(inst, from, to); if (from) { Pin *from_pin = network_->findPin(inst, from); graph_delay_calc_->delayInvalid(from_pin); @@ -1536,100 +1577,105 @@ Sta::removeDisable(Instance *inst, } delete pin_iter; } - // Levelization respects disabled edges. - levelize_->invalid(); search_->arrivalsInvalid(); } void Sta::disable(LibertyCell *cell, LibertyPort *from, - LibertyPort *to) + LibertyPort *to, + Sdc *sdc) { - sdc_->disable(cell, from, to); + sdc->disable(cell, from, to); disableAfter(); } void Sta::removeDisable(LibertyCell *cell, LibertyPort *from, - LibertyPort *to) + LibertyPort *to, + Sdc *sdc) { - sdc_->removeDisable(cell, from, to); + sdc->removeDisable(cell, from, to); disableAfter(); } void -Sta::disable(LibertyPort *port) +Sta::disable(LibertyPort *port, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->disable(port); + sdc->disable(port); disableAfter(); } void -Sta::removeDisable(LibertyPort *port) +Sta::removeDisable(LibertyPort *port, + Sdc *sdc) { - sdcChangedGraph(); - sdc_->removeDisable(port); + sdc->removeDisable(port); disableAfter(); } void -Sta::disable(Port *port) +Sta::disable(Port *port, + Sdc *sdc) { - sdc_->disable(port); + sdc->disable(port); disableAfter(); } void -Sta::removeDisable(Port *port) +Sta::removeDisable(Port *port, + Sdc *sdc) { - sdc_->removeDisable(port); + sdc->removeDisable(port); disableAfter(); } void -Sta::disable(Edge *edge) +Sta::disable(Edge *edge, + Sdc *sdc) { - sdc_->disable(edge); + sdc->disable(edge); disableAfter(); } void -Sta::removeDisable(Edge *edge) +Sta::removeDisable(Edge *edge, + Sdc *sdc) { - sdc_->removeDisable(edge); + sdc->removeDisable(edge); disableAfter(); } void -Sta::disable(TimingArcSet *arc_set) +Sta::disable(TimingArcSet *arc_set, + Sdc *sdc) { - sdc_->disable(arc_set); + sdc->disable(arc_set); disableAfter(); } void -Sta::removeDisable(TimingArcSet *arc_set) +Sta::removeDisable(TimingArcSet *arc_set, + Sdc *sdc) { - sdc_->removeDisable(arc_set); + sdc->removeDisable(arc_set); disableAfter(); } void Sta::disableAfter() { - // Levelization respects disabled edges. - levelize_->invalid(); delaysInvalid(); } //////////////////////////////////////////////////////////////// EdgeSeq -Sta::disabledEdges() +Sta::disabledEdges(const Mode *mode) { + const Sdc *sdc = mode->sdc(); ensureLevelized(); EdgeSeq disabled_edges; VertexIterator vertex_iter(graph_); @@ -1638,9 +1684,9 @@ Sta::disabledEdges() VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - if (isDisabledConstant(edge) + if (isDisabledConstant(edge, mode) || isDisabledCondDefault(edge) - || isDisabledConstraint(edge) + || isDisabledConstraint(edge, sdc) || edge->isDisabledLoop() || isDisabledPresetClr(edge)) disabled_edges.push_back(edge); @@ -1651,52 +1697,51 @@ Sta::disabledEdges() EdgeSeq -Sta::disabledEdgesSorted() +Sta::disabledEdgesSorted(const Mode *mode) { - EdgeSeq disabled_edges = disabledEdges(); + EdgeSeq disabled_edges = disabledEdges(mode); sortEdges(&disabled_edges, network_, graph_); return disabled_edges; } bool -Sta::isDisabledConstraint(Edge *edge) +Sta::isDisabledConstraint(Edge *edge, + const Sdc *sdc) { Pin *from_pin = edge->from(graph_)->pin(); Pin *to_pin = edge->to(graph_)->pin(); - const Instance *inst = network_->instance(from_pin); - TimingArcSet *arc_set = edge->timingArcSet(); - return sdc_->isDisabled(from_pin) - || sdc_->isDisabled(to_pin) - || sdc_->isDisabled(inst, from_pin, to_pin, edge->role()) - || sdc_->isDisabled(edge) - || sdc_->isDisabled(arc_set); + return sdc->isDisabledConstraint(from_pin) + || sdc->isDisabledConstraint(to_pin) + || sdc->isDisabledConstraint(edge); } bool -Sta::isDisabledConstant(Edge *edge) +Sta::isDisabledConstant(Edge *edge, + const Mode *mode) { - sim_->ensureConstantsPropagated(); + Sim *sim = mode->sim(); + sim->ensureConstantsPropagated(); const TimingRole *role = edge->role(); Vertex *from_vertex = edge->from(graph_); Pin *from_pin = from_vertex->pin(); Vertex *to_vertex = edge->to(graph_); Pin *to_pin = to_vertex->pin(); const Instance *inst = network_->instance(from_pin); - return sim_->logicZeroOne(from_vertex) - || sim_->logicZeroOne(to_vertex) + return sim->isConstant(from_vertex) + || sim->isConstant(to_vertex) || (!role->isWire() - && (isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_) - || isModeDisabled(edge, inst, network_, sim_) - || hasDisabledArcs(edge, graph_))); + && (sim->isDisabledCond(edge, inst, from_pin, to_pin) + || sim->isDisabledMode(edge, inst) + || hasDisabledArcs(edge, mode))); } static bool hasDisabledArcs(Edge *edge, - Graph *graph) + const Mode *mode) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { - if (!searchThru(edge, arc, graph)) + if (!searchThru(edge, arc, mode)) return true; } return false; @@ -1709,39 +1754,40 @@ Sta::isDisabledLoop(Edge *edge) const } PinSet -Sta::disabledConstantPins(Edge *edge) +Sta::disabledConstantPins(Edge *edge, + const Mode *mode) { - sim_->ensureConstantsPropagated(); + Sim *sim = mode->sim(); + sim->ensureConstantsPropagated(); PinSet pins(network_); Vertex *from_vertex = edge->from(graph_); Pin *from_pin = from_vertex->pin(); Vertex *to_vertex = edge->to(graph_); const Pin *to_pin = to_vertex->pin(); - if (sim_->logicZeroOne(from_vertex)) + if (sim->isConstant(from_vertex)) pins.insert(from_pin); if (edge->role()->isWire()) { - if (sim_->logicZeroOne(to_vertex)) + if (sim->isConstant(to_vertex)) pins.insert(to_pin); } else { const Instance *inst = network_->instance(to_pin); bool is_disabled; FuncExpr *disable_cond; - isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_, + sim->isDisabledCond(edge, inst, from_pin, to_pin, is_disabled, disable_cond); if (is_disabled) - exprConstantPins(disable_cond, inst, pins); - isModeDisabled(edge, inst, network_, sim_, - is_disabled, disable_cond); + exprConstantPins(disable_cond, inst, mode, pins); + sim->isDisabledMode(edge, inst, is_disabled, disable_cond); if (is_disabled) - exprConstantPins(disable_cond, inst, pins); - if (hasDisabledArcs(edge, graph_)) { + exprConstantPins(disable_cond, inst, mode, pins); + 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()) - exprConstantPins(func, inst, pins); + && sim->functionSense(inst, from_pin, to_pin) != edge->sense()) + exprConstantPins(func, inst, mode, pins); } } } @@ -1749,25 +1795,27 @@ Sta::disabledConstantPins(Edge *edge) } TimingSense -Sta::simTimingSense(Edge *edge) +Sta::simTimingSense(Edge *edge, + const Mode *mode) { Pin *from_pin = edge->from(graph_)->pin(); Pin *to_pin = edge->to(graph_)->pin(); Instance *inst = network_->instance(from_pin); - return sim_->functionSense(inst, from_pin, to_pin); + return mode->sim()->functionSense(inst, from_pin, to_pin); } void Sta::exprConstantPins(FuncExpr *expr, const Instance *inst, + const Mode *mode, + // Return value. PinSet &pins) { - FuncExprPortIterator port_iter(expr); - while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); + LibertyPortSet ports = expr->ports(); + for (LibertyPort *port : ports) { Pin *pin = network_->findPin(inst, port); if (pin) { - LogicValue value = sim_->logicValue(pin); + LogicValue value = mode->sim()->simValue(pin); if (value != LogicValue::unknown) pins.insert(pin); } @@ -1781,13 +1829,6 @@ Sta::isDisabledBidirectInstPath(Edge *edge) const && edge->isBidirectInstPath(); } -bool -Sta::isDisabledBidirectNetPath(Edge *edge) const -{ - return !variables_->bidirectNetPathsEnabled() - && edge->isBidirectNetPath(); -} - bool Sta::isDisabledPresetClr(Edge *edge) const { @@ -1796,42 +1837,44 @@ Sta::isDisabledPresetClr(Edge *edge) const } void -Sta::disableClockGatingCheck(Instance *inst) +Sta::disableClockGatingCheck(Instance *inst, + Sdc *sdc) { - sdc_->disableClockGatingCheck(inst); + sdc->disableClockGatingCheck(inst); search_->endpointsInvalid(); } void -Sta::disableClockGatingCheck(Pin *pin) +Sta::disableClockGatingCheck(Pin *pin, + Sdc *sdc) { - sdc_->disableClockGatingCheck(pin); + sdc->disableClockGatingCheck(pin); search_->endpointsInvalid(); } void -Sta::removeDisableClockGatingCheck(Instance *inst) +Sta::removeDisableClockGatingCheck(Instance *inst, + Sdc *sdc) { - sdc_->removeDisableClockGatingCheck(inst); + sdc->removeDisableClockGatingCheck(inst); search_->endpointsInvalid(); } void -Sta::removeDisableClockGatingCheck(Pin *pin) +Sta::removeDisableClockGatingCheck(Pin *pin, + Sdc *sdc) { - sdc_->removeDisableClockGatingCheck(pin); + sdc->removeDisableClockGatingCheck(pin); search_->endpointsInvalid(); } void Sta::setLogicValue(Pin *pin, - LogicValue value) + LogicValue value, + Mode *mode) { - sdc_->setLogicValue(pin, value); - // Levelization respects constant disabled edges. - levelize_->invalid(); - power_->activitiesInvalid(); - sim_->constantsInvalid(); + mode->sdc()->setLogicValue(pin, value); + mode->sim()->constantsInvalid(); // Constants disable edges which isolate downstream vertices of the // graph from the delay calculator's BFS search. This means that // simply invaldating the delays downstream from the constant pin @@ -1839,32 +1882,32 @@ Sta::setLogicValue(Pin *pin, // calculator searched thru disabled edges but ignored their // results. delaysInvalid(); + power_->activitiesInvalid(); } void Sta::setCaseAnalysis(Pin *pin, - LogicValue value) + LogicValue value, + Mode *mode) { - sdc_->setCaseAnalysis(pin, value); - power_->activitiesInvalid(); // Levelization respects constant disabled edges. - levelize_->invalid(); - sim_->constantsInvalid(); + mode->sdc()->setCaseAnalysis(pin, value); + mode->sim()->constantsInvalid(); // Constants disable edges which isolate downstream vertices of the // graph from the delay calculator's BFS search. This means that // simply invaldating the delays downstream from the constant pin // fails. This could be handled incrementally by invalidating delays // on the output of gates one level downstream. delaysInvalid(); + power_->activitiesInvalid(); } void -Sta::removeCaseAnalysis(Pin *pin) +Sta::removeCaseAnalysis(Pin *pin, + Mode *mode) { - sdc_->removeCaseAnalysis(pin); - // Levelization respects constant disabled edges. - levelize_->invalid(); - sim_->constantsInvalid(); + mode->sdc()->removeCaseAnalysis(pin); + mode->sim()->constantsInvalid(); // Constants disable edges which isolate downstream vertices of the // graph from the delay calculator's BFS search. This means that // simply invaldating the delays downstream from the constant pin @@ -1883,9 +1926,10 @@ Sta::setInputDelay(const Pin *pin, bool network_latency_included, const MinMaxAll *min_max, bool add, - float delay) + float delay, + Sdc *sdc) { - sdc_->setInputDelay(pin, rf, clk, clk_rf, ref_pin, + sdc->setInputDelay(pin, rf, clk, clk_rf, ref_pin, source_latency_included, network_latency_included, min_max, add, delay); @@ -1897,9 +1941,10 @@ Sta::removeInputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->removeInputDelay(pin, rf, clk, clk_rf, min_max); + sdc->removeInputDelay(pin, rf, clk, clk_rf, min_max); search_->arrivalInvalid(pin); } @@ -1913,12 +1958,12 @@ Sta::setOutputDelay(const Pin *pin, bool network_latency_included, const MinMaxAll *min_max, bool add, - float delay) + float delay, + Sdc *sdc) { - sdc_->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, + sdc->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, source_latency_included,network_latency_included, min_max, add, delay); - sdcChangedGraph(); search_->requiredInvalid(pin); } @@ -1927,10 +1972,10 @@ Sta::removeOutputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->removeOutputDelay(pin, rf, clk, clk_rf, min_max); - sdcChangedGraph(); + sdc->removeOutputDelay(pin, rf, clk, clk_rf, min_max); search_->arrivalInvalid(pin); } @@ -1939,9 +1984,10 @@ Sta::makeFalsePath(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max, - const char *comment) + const char *comment, + Sdc *sdc) { - sdc_->makeFalsePath(from, thrus, to, min_max, comment); + sdc->makeFalsePath(from, thrus, to, min_max, comment); search_->arrivalsInvalid(); } @@ -1952,9 +1998,10 @@ Sta::makeMulticyclePath(ExceptionFrom *from, const MinMaxAll *min_max, bool use_end_clk, int path_multiplier, - const char *comment) + const char *comment, + Sdc *sdc) { - sdc_->makeMulticyclePath(from, thrus, to, min_max, + sdc->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, path_multiplier, comment); search_->arrivalsInvalid(); @@ -1968,9 +2015,10 @@ Sta::makePathDelay(ExceptionFrom *from, bool ignore_clk_latency, bool break_path, float delay, - const char *comment) + const char *comment, + Sdc *sdc) { - sdc_->makePathDelay(from, thrus, to, min_max, + sdc->makePathDelay(from, thrus, to, min_max, ignore_clk_latency, break_path, delay, comment); search_->endpointsInvalid(); @@ -1981,9 +2029,10 @@ void Sta::resetPath(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - sdc_->resetPath(from, thrus, to, min_max); + sdc->resetPath(from, thrus, to, min_max); search_->arrivalsInvalid(); } @@ -1993,23 +2042,26 @@ Sta::makeGroupPath(const char *name, ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, - const char *comment) + const char *comment, + Sdc *sdc) { - sdc_->makeGroupPath(name, is_default, from, thrus, to, comment); + sdc->makeGroupPath(name, is_default, from, thrus, to, comment); search_->arrivalsInvalid(); } bool -Sta::isGroupPathName(const char *group_name) +Sta::isGroupPathName(const char *group_name, + const Sdc *sdc) { - return isPathGroupName(group_name); + return isPathGroupName(group_name, sdc); } bool -Sta::isPathGroupName(const char *group_name) const +Sta::isPathGroupName(const char *group_name, + const Sdc *sdc) const { - return sdc_->findClock(group_name) - || sdc_->isGroupPathName(group_name) + return sdc->findClock(group_name) + || sdc->isGroupPathName(group_name) || stringEq(group_name, PathGroups::asyncPathGroupName()) || stringEq(group_name, PathGroups::pathDelayGroupName()) || stringEq(group_name, PathGroups::gatedClkGroupName()) @@ -2017,13 +2069,13 @@ Sta::isPathGroupName(const char *group_name) const } StdStringSeq -Sta::pathGroupNames() const +Sta::pathGroupNames(const Sdc *sdc) const { StdStringSeq names; - for (const Clock *clk : *sdc_->clocks()) + for (const Clock *clk : sdc->clocks()) names.push_back(clk->name()); - for (auto const &[name, group] : sdc_->groupPaths()) + for (auto const &[name, group] : sdc->groupPaths()) names.push_back(name); names.push_back(PathGroups::asyncPathGroupName()); @@ -2037,22 +2089,23 @@ ExceptionFrom * Sta::makeExceptionFrom(PinSet *from_pins, ClockSet *from_clks, InstanceSet *from_insts, - const RiseFallBoth *from_rf) + const RiseFallBoth *from_rf, + const Sdc *sdc) { - return sdc_->makeExceptionFrom(from_pins, from_clks, from_insts, - from_rf); + return sdc->makeExceptionFrom(from_pins, from_clks, from_insts, from_rf); } void Sta::checkExceptionFromPins(ExceptionFrom *from, const char *file, - int line) const + int line, + const Sdc *sdc) const { if (from) { - PinSet::ConstIterator pin_iter(from->pins()); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - if (!sdc_->isExceptionStartpoint(pin)) { + PinSet *pins = from->pins(); + 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)); @@ -2062,6 +2115,7 @@ Sta::checkExceptionFromPins(ExceptionFrom *from, } } } + } } void @@ -2074,9 +2128,10 @@ ExceptionThru * Sta::makeExceptionThru(PinSet *pins, NetSet *nets, InstanceSet *insts, - const RiseFallBoth *rf) + const RiseFallBoth *rf, + const Sdc *sdc) { - return sdc_->makeExceptionThru(pins, nets, insts, rf); + return sdc->makeExceptionThru(pins, nets, insts, rf); } void @@ -2090,9 +2145,10 @@ Sta::makeExceptionTo(PinSet *to_pins, ClockSet *to_clks, InstanceSet *to_insts, const RiseFallBoth *rf, - const RiseFallBoth *end_rf) + const RiseFallBoth *end_rf, + const Sdc *sdc) { - return sdc_->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf); + return sdc->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf); } void @@ -2104,13 +2160,14 @@ Sta::deleteExceptionTo(ExceptionTo *to) void Sta::checkExceptionToPins(ExceptionTo *to, const char *file, - int line) const + int line, + const Sdc *sdc) const { if (to) { - PinSet::Iterator pin_iter(to->pins()); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); - if (!sdc_->isExceptionEndpoint(pin)) { + PinSet *pins = to->pins(); + 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)); @@ -2120,31 +2177,12 @@ Sta::checkExceptionToPins(ExceptionTo *to, } } } + } } void -Sta::removeConstraints() -{ - levelize_->invalid(); - graph_delay_calc_->clear(); - search_->clear(); - sim_->constantsInvalid(); - if (graph_) - sdc_->removeGraphAnnotations(); - sdc_->clear(); - clk_network_->clear(); -} - -void -Sta::constraintsChanged() -{ - levelize_->invalid(); - delaysInvalid(); - sim_->constantsInvalid(); -} - -void -Sta::writeSdc(const char *filename, +Sta::writeSdc(const Sdc *sdc, + const char *filename, bool leaf, bool native, int digits, @@ -2152,14 +2190,15 @@ Sta::writeSdc(const char *filename, bool no_timestamp) { ensureLibLinked(); - sta::writeSdc(network_->topInstance(), filename, "write_sdc", - leaf, native, digits, gzip, no_timestamp, sdc_); + sta::writeSdc(sdc, network_->topInstance(), filename, "write_sdc", + leaf, native, digits, gzip, no_timestamp); } //////////////////////////////////////////////////////////////// CheckErrorSeq & -Sta::checkTiming(bool no_input_delay, +Sta::checkTiming(const Mode *mode, + bool no_input_delay, bool no_output_delay, bool reg_multiple_clks, bool reg_no_clks, @@ -2167,15 +2206,20 @@ Sta::checkTiming(bool no_input_delay, bool loops, bool generated_clks) { + if (unconstrained_endpoints) { + // Only arrivals to find unconstrained_endpoints. searchPreamble(); - if (unconstrained_endpoints) - // Only need non-clock arrivals for unconstrained_endpoints. search_->findAllArrivals(); - else - search_->findClkArrivals(); + } + else { + ensureGraph(); + ensureLevelized(); + mode->sim()->ensureConstantsPropagated(); + mode->clkNetwork()->ensureClkNetwork(); + } if (check_timing_ == nullptr) makeCheckTiming(); - return check_timing_->check(no_input_delay, no_output_delay, + return check_timing_->check(mode, no_input_delay, no_output_delay, reg_multiple_clks, reg_no_clks, unconstrained_endpoints, loops, generated_clks); @@ -2192,9 +2236,7 @@ Sta::crprEnabled() const void Sta::setCrprEnabled(bool enabled) { - // Pessimism is only relevant for on_chip_variation analysis. - if (sdc_->analysisType() == AnalysisType::ocv - && enabled != variables_->crprEnabled()) + if (enabled != variables_->crprEnabled()) search_->arrivalsInvalid(); variables_->setCrprEnabled(enabled); } @@ -2209,8 +2251,7 @@ void Sta::setCrprMode(CrprMode mode) { // Pessimism is only relevant for on_chip_variation analysis. - if (sdc_->analysisType() == AnalysisType::ocv - && variables_->crprEnabled() + if (variables_->crprEnabled() && variables_->crprMode() != mode) search_->arrivalsInvalid(); variables_->setCrprMode(mode); @@ -2301,21 +2342,6 @@ Sta::setBidirectInstPathsEnabled(bool enabled) } } -bool -Sta::bidirectNetPathsEnabled() const -{ - return variables_->bidirectNetPathsEnabled(); -} - -void -Sta::setBidirectNetPathsEnabled(bool enabled) -{ - if (variables_->bidirectNetPathsEnabled() != enabled) { - delaysInvalid(); - variables_->setBidirectNetPathsEnabled(enabled); - } -} - bool Sta::recoveryRemovalChecksEnabled() const { @@ -2356,11 +2382,12 @@ void Sta::setDynamicLoopBreaking(bool enable) { if (variables_->dynamicLoopBreaking() != enable) { - if (levelize_->levelized()) { + for (Mode *mode : modes_) { + Sdc *sdc = mode->sdc(); if (enable) - sdc_->makeLoopExceptions(); + sdc->makeLoopExceptions(); else - sdc_->deleteLoopExceptions(); + sdc->deleteLoopExceptions(); } search_->arrivalsInvalid(); variables_->setDynamicLoopBreaking(enable); @@ -2411,54 +2438,222 @@ Sta::setClkThruTristateEnabled(bool enable) //////////////////////////////////////////////////////////////// -Corner * -Sta::findCorner(const char *corner_name) +// Init one scene named "default". +void +Sta::makeDefaultScene() { - return corners_->findCorner(corner_name); + const char *name = "default"; + StringSeq scene_names; + scene_names.push_back(name); + Parasitics *parasitics = makeConcreteParasitics(name, ""); + + Mode *mode = new Mode(name, 0, this); + modes_.push_back(mode); + mode_name_map_[name] = mode; + mode->sim()->setMode(mode); + mode->sim()->setObserver(new StaSimObserver(this)); + + deleteScenes(); + makeScene(name, mode, parasitics); + + cmd_scene_ = scenes_[0]; } -bool -Sta::multiCorner() +// define_corners (before read_liberty). +void +Sta::makeScenes(StringSeq *scene_names) +{ + if (scene_names->size() > scene_count_max) + report_->error(1553, "maximum scene count exceeded"); + Parasitics *parasitics = findParasitics("default"); + Mode *mode = modes_[0]; + mode->sdc()->makeSceneBefore(); + mode->clear(); + + deleteScenes(); + for (const char *name : *scene_names) + makeScene(name, mode, parasitics); + + cmd_scene_ = scenes_[0]; + updateComponentsState(); + if (graph_) + graph_->makeSceneAfter(); +} + +void +Sta::makeScene(const std::string &name, + const std::string &mode_name, + const StdStringSeq &liberty_min_files, + const StdStringSeq &liberty_max_files, + const std::string &spef_min_file, + const std::string &spef_max_file) +{ + Mode *mode = findMode(mode_name); + Parasitics *parasitics_default = findParasitics("default"); + Parasitics *parasitics_min = parasitics_default; + Parasitics *parasitics_max = parasitics_default; + if (!spef_min_file.empty() && !spef_max_file.empty()) { + 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()); + } + + mode->sdc()->makeSceneBefore(); + Scene *scene = makeScene(name, mode, parasitics_min, parasitics_max); + updateComponentsState(); + if (graph_) + graph_->makeSceneAfter(); + updateSceneLiberty(scene, liberty_min_files, MinMax::min()); + updateSceneLiberty(scene, liberty_max_files, MinMax::max()); + cmd_scene_ = scene; +} + +Scene * +Sta::makeScene(const std::string &name, + Mode *mode, + Parasitics *parasitics) +{ + Scene *scene = new Scene(name, scenes_.size(), mode, parasitics); + scene_name_map_[name] = scene; + scenes_.push_back(scene); + mode->addScene(scene); + return scene; +} + +void +Sta::deleteScenes() +{ + for (Scene *scene : scenes_) { + scene->mode()->removeScene(scene); + delete scene; + } + scenes_.clear(); + scene_name_map_.clear(); +} + +Scene * +Sta::makeScene(const std::string &name, + Mode *mode, + Parasitics *parasitics_min, + Parasitics *parasitics_max) +{ + if (scenes_.size() == 1 + && findScene("default")) + deleteScenes(); + + Scene *scene = new Scene(name, scenes_.size(), mode, + parasitics_min, parasitics_max); + scene_name_map_[name] = scene; + scenes_.push_back(scene); + mode->addScene(scene); + return scene; +} + +Scene * +Sta::findScene(const std::string &name) const +{ + return findKey(scene_name_map_, name); +} + +SceneSeq +Sta::findScenes(const std::string &name) const { - return corners_->multiCorner(); + SceneSeq matches; + PatternMatch pattern(name.c_str()); + for (Scene *scene : scenes_) { + if (pattern.match(scene->name())) + matches.push_back(scene); + } + return matches; +} + +SceneSeq +Sta::findScenes(const std::string &name, + ModeSeq &modes) const +{ + SceneSeq matches; + PatternMatch pattern(name.c_str()); + for (Mode *mode : modes) { + for (Scene *scene : mode->scenes()) { + if (pattern.match(scene->name())) + matches.push_back(scene); + } + } + return matches; } -// Init one corner named "default". void -Sta::makeCorners() +Sta::updateSceneLiberty(Scene *scene, + const StdStringSeq &liberty_files, + const MinMax *min_max) { - corners_ = new Corners(this); - StringSet corner_names; - corner_names.insert("default"); - corners_->makeCorners(&corner_names); - cmd_corner_ = corners_->findCorner(0); - sdc_->makeCornersAfter(corners_); + for (const std::string &lib_file : liberty_files) { + LibertyLibrary *lib = findLibertyFileBasename(lib_file); + if (lib) + LibertyLibrary::makeSceneMap(lib, scene->libertyIndex(min_max), + network_, report_); + else + report_->warn(1555, "liberty filename %s not found.", lib_file.c_str()); + } +} + +LibertyLibrary * +Sta::findLibertyFileBasename(const std::string &filename) const +{ + LibertyLibraryIterator *lib_iter = network_->libertyLibraryIterator(); + while (lib_iter->hasNext()) { + LibertyLibrary *lib = lib_iter->next(); + auto lib_file = std::filesystem::path(lib->filename()).filename().stem(); + auto stem = lib_file.stem(); + if (stem.string() == filename) { + delete lib_iter; + return lib; + } + } + delete lib_iter; + return nullptr; } void -Sta::makeCorners(StringSet *corner_names) +Sta::updateLibertyScenes() { - if (corner_names->size() > corner_count_max) - report_->error(1553, "maximum corner count exceeded"); - sdc_->makeCornersBefore(); - parasitics_->deleteParasitics(); - corners_->makeCorners(corner_names); - makeParasiticAnalysisPts(); - cmd_corner_ = corners_->findCorner(0); - updateComponentsState(); - sdc_->makeCornersAfter(corners_); + for (Scene *scene : scenes_) { + LibertyLibraryIterator *iter = network_->libertyLibraryIterator(); + while (iter->hasNext()) { + LibertyLibrary *lib = iter->next(); + for (const MinMax *min_max : MinMax::range()) { + LibertyLibrary::makeSceneMap(lib, scene->libertyIndex(min_max), + network_, report_); + } + } + } } -Corner * -Sta::cmdCorner() const +Scene * +Sta::cmdScene() const { - return cmd_corner_; + return cmd_scene_; } void -Sta::setCmdCorner(Corner *corner) +Sta::setCmdScene(Scene *scene) +{ + cmd_scene_ = scene; +} + +SceneSeq +Sta::makeSceneSeq(Scene *scene) const { - cmd_corner_ = corner; + SceneSeq scenes; + if (scene) + scenes.push_back(scene); + else + scenes = scenes_; + return scenes; } //////////////////////////////////////////////////////////////// @@ -2471,7 +2666,7 @@ Sta::findPathEnds(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, bool unconstrained, - const Corner *corner, + const SceneSeq &scenes, const MinMaxAll *min_max, int group_path_count, int endpoint_path_count, @@ -2480,7 +2675,7 @@ Sta::findPathEnds(ExceptionFrom *from, float slack_min, float slack_max, bool sort_by_slack, - PathGroupNameSet *group_names, + StdStringSeq &group_names, bool setup, bool hold, bool recovery, @@ -2491,10 +2686,9 @@ Sta::findPathEnds(ExceptionFrom *from, searchPreamble(); clk_skews_->clear(); return search_->findPathEnds(from, thrus, to, unconstrained, - corner, min_max, group_path_count, + scenes, min_max, group_path_count, endpoint_path_count, - unique_pins, unique_edges, - slack_min, slack_max, + unique_pins, unique_edges, slack_min, slack_max, sort_by_slack, group_names, setup, hold, recovery, removal, @@ -2505,18 +2699,21 @@ Sta::findPathEnds(ExceptionFrom *from, // Overall flow: // make graph -// propagate constants // levelize // delay calculation // update generated clocks +// propagate constants // find arrivals void Sta::searchPreamble() { findDelays(); + for (Mode *mode : modes_) { + mode->sim()->ensureConstantsPropagated(); + mode->sdc()->searchPreamble(); + } updateGeneratedClks(); - sdc_->searchPreamble(); // Delete results from last findPathEnds because they point to filtered arrivals. search_->deletePathGroups(); search_->deleteFilteredArrivals(); @@ -2623,13 +2820,13 @@ Sta::updateTiming(bool full) void Sta::reportClkSkew(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, const SetupHold *setup_hold, bool include_internal_latency, int digits) { clkSkewPreamble(); - clk_skews_->reportClkSkew(clks, corner, setup_hold, + clk_skews_->reportClkSkew(clks, scenes, setup_hold, include_internal_latency, digits); } @@ -2639,7 +2836,7 @@ Sta::findWorstClkSkew(const SetupHold *setup_hold, { clkSkewPreamble(); - return clk_skews_->findWorstClkSkew(nullptr, setup_hold, + return clk_skews_->findWorstClkSkew(scenes_, setup_hold, include_internal_latency); } @@ -2659,22 +2856,23 @@ Sta::makeClkSkews() void Sta::reportClkLatency(ConstClockSeq &clks, - const Corner *corner, + const SceneSeq &scenes, bool include_internal_latency, int digits) { ensureClkArrivals(); ClkLatency clk_latency(this); - clk_latency.reportClkLatency(clks, corner, include_internal_latency, digits); + clk_latency.reportClkLatency(clks, scenes, include_internal_latency, digits); } ClkDelays Sta::findClkDelays(const Clock *clk, + const Scene *scene, bool include_internal_latency) { ensureClkArrivals(); ClkLatency clk_latency(this); - return clk_latency.findClkDelays(clk, nullptr, include_internal_latency); + return clk_latency.findClkDelays(clk, scene, include_internal_latency); } //////////////////////////////////////////////////////////////// @@ -2701,17 +2899,7 @@ Sta::ensureClkArrivals() //////////////////////////////////////////////////////////////// -PinSet -Sta::startpointPins() -{ - ensureGraph(); - PinSet pins(network_); - VertexPinCollector visitor(pins); - search_->visitStartpoints(&visitor); - return pins; -} - -VertexSet * +VertexSet & Sta::endpoints() { ensureGraph(); @@ -2723,7 +2911,7 @@ Sta::endpointPins() { ensureGraph(); PinSet pins(network_); - for (Vertex *vertex : *search_->endpoints()) + for (Vertex *vertex : search_->endpoints()) pins.insert(vertex->pin()); return pins; } @@ -2732,8 +2920,8 @@ int Sta::endpointViolationCount(const MinMax *min_max) { int violations = 0; - for (Vertex *end : *search_->endpoints()) { - if (delayLess(vertexSlack(end, min_max), 0.0, this)) + for (Vertex *end : search_->endpoints()) { + if (delayLess(slack(end, min_max), 0.0, this)) violations++; } return violations; @@ -2751,22 +2939,6 @@ Sta::findRequireds() //////////////////////////////////////////////////////////////// -VertexPathIterator * -Sta::vertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap) -{ - return new VertexPathIterator(vertex, rf, path_ap, this); -} - -VertexPathIterator * -Sta::vertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) -{ - return new VertexPathIterator(vertex, rf, min_max, this); -} - Path * Sta::vertexWorstArrivalPath(Vertex *vertex, const MinMax *min_max) @@ -2851,59 +3023,43 @@ Sta::vertexWorstSlackPath(Vertex *vertex, } Arrival -Sta::pinArrival(const Pin *pin, - const RiseFall *rf, +Sta::arrival(const Pin *pin, + const RiseFallBoth *rf, const MinMax *min_max) { Vertex *vertex, *bidirect_vertex; graph_->pinVertices(pin, vertex, bidirect_vertex); - Arrival arrival; + Arrival worst_arrival = min_max->initValue(); if (vertex) - arrival = vertexArrival(vertex, rf, clk_edge_wildcard, nullptr, min_max); + worst_arrival = arrival(vertex, rf, scenes_, min_max); if (bidirect_vertex) { - Arrival arrival1 = vertexArrival(bidirect_vertex, rf, clk_edge_wildcard, - nullptr, min_max); - if (delayLess(arrival1, arrival, this)) - arrival = arrival1; + Arrival arrival2 = arrival(bidirect_vertex, rf, scenes_, min_max); + if (delayGreater(arrival2, worst_arrival, min_max, this)) + worst_arrival = arrival2; } - return arrival; + return worst_arrival; } Arrival -Sta::vertexArrival(Vertex *vertex, - const MinMax *min_max) -{ - return vertexArrival(vertex, nullptr, clk_edge_wildcard, nullptr, min_max); -} - -Arrival -Sta::vertexArrival(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap) -{ - return vertexArrival(vertex, rf, clk_edge_wildcard, path_ap, nullptr); -} - -Arrival -Sta::vertexArrival(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap, +Sta::arrival(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, const MinMax *min_max) { searchPreamble(); search_->findArrivals(vertex->level()); - if (min_max == nullptr) - min_max = path_ap->pathMinMax(); + const SceneSet scenes_set = Scene::sceneSet(scenes); Arrival arrival = min_max->initValue(); - VertexPathIterator path_iter(vertex, rf, path_ap, this); + VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const Arrival &path_arrival = path->arrival(); const ClkInfo *clk_info = path->clkInfo(search_); - if ((clk_edge == clk_edge_wildcard - || clk_info->clkEdge() == clk_edge) - && !clk_info->isGenClkSrcPath() + 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)) arrival = path_arrival; } @@ -2911,118 +3067,131 @@ Sta::vertexArrival(Vertex *vertex, } Required -Sta::vertexRequired(Vertex *vertex, - const MinMax *min_max) -{ - return vertexRequired(vertex, nullptr, clk_edge_wildcard, nullptr, min_max); -} - -Required -Sta::vertexRequired(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) -{ - return vertexRequired(vertex, rf, clk_edge_wildcard, nullptr, min_max); -} - -Required -Sta::vertexRequired(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap) -{ - return vertexRequired(vertex, rf, clk_edge_wildcard, path_ap, nullptr); -} - -Required -Sta::vertexRequired(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap) -{ - return vertexRequired(vertex, rf, clk_edge, path_ap, nullptr); -} - -Required -Sta::vertexRequired(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap, +Sta::required(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, const MinMax *min_max) { findRequired(vertex); - const MinMax *req_min_max = min_max - ? min_max->opposite() - : path_ap->pathMinMax()->opposite(); + const SceneSet scenes_set = Scene::sceneSet(scenes); + const MinMax *req_min_max = min_max->opposite(); Required required = req_min_max->initValue(); - VertexPathIterator path_iter(vertex, rf, path_ap, min_max, this); + VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { const Path *path = path_iter.next(); const Required path_required = path->required(); - if ((clk_edge == clk_edge_wildcard - || path->clkEdge(search_) == clk_edge) + 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)) required = path_required; } return required; } +//////////////////////////////////////////////////////////////// + Slack -Sta::netSlack(const Net *net, +Sta::slack(const Net *net, const MinMax *min_max) { ensureGraph(); - Slack slack = MinMax::min()->initValue(); + Slack min_slack = MinMax::min()->initValue(); NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (network_->isLoad(pin)) { Vertex *vertex = graph_->pinLoadVertex(pin); - Slack pin_slack = vertexSlack(vertex, min_max); - if (delayLess(pin_slack, slack, this)) - slack = pin_slack; + Slack pin_slack = slack(vertex, min_max); + if (delayLess(pin_slack, min_slack, this)) + min_slack = pin_slack; } } delete pin_iter; - return slack; + return min_slack; } Slack -Sta::pinSlack(const Pin *pin, +Sta::slack(const Pin *pin, + const RiseFallBoth *rf, + const SceneSeq &scenes, const MinMax *min_max) { ensureGraph(); Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - Slack slack = MinMax::min()->initValue(); + Slack min_slack = MinMax::min()->initValue(); if (vertex) - slack = vertexSlack(vertex, min_max); + min_slack = slack(vertex, rf, scenes, min_max); if (bidirect_drvr_vertex) { - Slack slack1 = vertexSlack(bidirect_drvr_vertex, min_max); - if (delayLess(slack1, slack, this)) - slack = slack1; + Slack slack1 = slack(bidirect_drvr_vertex, rf, scenes, min_max); + if (delayLess(slack1, min_slack, this)) + min_slack = slack1; } - return slack; + return min_slack; +} + +Slack +Sta::slack(Vertex *vertex, + const MinMax *min_max) +{ + return slack(vertex, RiseFallBoth::riseFall(), scenes_, min_max); } Slack -Sta::pinSlack(const Pin *pin, +Sta::slack(Vertex *vertex, const RiseFall *rf, const MinMax *min_max) { - ensureGraph(); - Vertex *vertex, *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - Slack slack = MinMax::min()->initValue(); - if (vertex) - slack = vertexSlack(vertex, rf, min_max); - if (bidirect_drvr_vertex) { - Slack slack1 = vertexSlack(bidirect_drvr_vertex, rf, min_max); - if (delayLess(slack1, slack, this)) - slack = slack1; + return slack(vertex, rf->asRiseFallBoth(), scenes_, min_max); +} + +Slack +Sta::slack(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, + const MinMax *min_max) +{ + findRequired(vertex); + const SceneSet scenes_set = Scene::sceneSet(scenes); + const MinMax *min = MinMax::min(); + Slack slack = min->initValue(); + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + 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)) + && delayLess(path_slack, slack, this)) + slack = path_slack; } return slack; } +void +Sta::slacks(Vertex *vertex, + Slack (&slacks)[RiseFall::index_count][MinMax::index_count]) +{ + findRequired(vertex); + for (int rf_index : RiseFall::rangeIndex()) { + for (const MinMax *min_max : MinMax::range()) { + slacks[rf_index][min_max->index()] = MinMax::min()->initValue(); + } + } + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + Slack path_slack = path->slack(this); + int rf_index = path->rfIndex(this); + int mm_index = path->minMax(this)->index(); + if (delayLess(path_slack, slacks[rf_index][mm_index], this)) + slacks[rf_index][mm_index] = path_slack; + } +} + //////////////////////////////////////////////////////////////// class EndpointPathEndVisitor : public PathEndVisitor @@ -3093,99 +3262,136 @@ Sta::endpointSlack(const Pin *pin, //////////////////////////////////////////////////////////////// -Slack -Sta::vertexSlack(Vertex *vertex, - const MinMax *min_max) +void +Sta::reportArrivalWrtClks(const Pin *pin, + const Scene *scene, + int digits) { - findRequired(vertex); - Slack slack = MinMax::min()->initValue(); - VertexPathIterator path_iter(vertex, this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - if (path->minMax(this) == min_max) { - Slack path_slack = path->slack(this); - if (delayLess(path_slack, slack, this)) - slack = path_slack; - } - } - return slack; + reportDelaysWrtClks(pin, scene, digits, + [] (const Path *path) { + return path->arrival(); + }); } -Slack -Sta::vertexSlack(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) +void +Sta::reportRequiredWrtClks(const Pin *pin, + const Scene *scene, + int digits) { - findRequired(vertex); - Slack slack = MinMax::min()->initValue(); - VertexPathIterator path_iter(vertex, rf, min_max, this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - Slack path_slack = path->slack(this); - if (delayLess(path_slack, slack, this)) - slack = path_slack; - } - return slack; + reportDelaysWrtClks(pin, scene, digits, + [] (const Path *path) { + return path->required(); + }); } -Slack -Sta::vertexSlack(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap) +void +Sta::reportSlackWrtClks(const Pin *pin, + const Scene *scene, + int digits) { - findRequired(vertex); - return vertexSlack1(vertex, rf, clk_edge_wildcard, path_ap); + reportDelaysWrtClks(pin, scene, digits, + [this] (const Path *path) { + return path->slack(this); + }); } -Slack -Sta::vertexSlack(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap) +void +Sta::reportDelaysWrtClks(const Pin *pin, + const Scene *scene, + int digits, + PathDelayFunc get_path_delay) { - findRequired(vertex); - return vertexSlack1(vertex, rf, clk_edge, path_ap); + ensureGraph(); + Vertex *vertex, *bidir_vertex; + graph_->pinVertices(pin, vertex, bidir_vertex); + if (vertex) + reportDelaysWrtClks(vertex, scene, digits, get_path_delay); + if (bidir_vertex) + reportDelaysWrtClks(vertex, scene, digits, get_path_delay); } -Slack -Sta::vertexSlack1(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap) +void +Sta::reportDelaysWrtClks(Vertex *vertex, + const Scene *scene, + int digits, + PathDelayFunc get_path_delay) { - const MinMax *min = MinMax::min(); - Slack slack = min->initValue(); - VertexPathIterator path_iter(vertex, rf, path_ap, this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - Slack path_slack = path->slack(this); - if ((clk_edge == clk_edge_wildcard - || path->clkEdge(search_) == clk_edge) - && delayLess(path_slack, slack, this)) - slack = path_slack; + findRequired(vertex); + const Sdc *sdc = scene->sdc(); + reportDelaysWrtClks(vertex, nullptr, scene, digits, get_path_delay); + const ClockEdge *default_clk_edge = sdc->defaultArrivalClock()->edge(RiseFall::rise()); + reportDelaysWrtClks(vertex, default_clk_edge, scene, 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); + } } - return slack; } void -Sta::vertexSlacks(Vertex *vertex, - Slack (&slacks)[RiseFall::index_count][MinMax::index_count]) +Sta::reportDelaysWrtClks(Vertex *vertex, + const ClockEdge *clk_edge, + const Scene *scene, + int digits, + PathDelayFunc get_path_delay) { - findRequired(vertex); - for (int rf_index : RiseFall::rangeIndex()) { - for (const MinMax *min_max : MinMax::range()) { - slacks[rf_index][min_max->index()] = MinMax::min()->initValue(); + 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, digits).c_str(), + formatDelay(RiseFall::rise(), MinMax::max(), + delays, digits).c_str(), + formatDelay(RiseFall::fall(), MinMax::min(), + delays, digits).c_str(), + formatDelay(RiseFall::fall(), MinMax::max(), + delays, digits).c_str()); } - VertexPathIterator path_iter(vertex, this); +} + +RiseFallMinMaxDelay +Sta::findDelaysWrtClks(Vertex *vertex, + const ClockEdge *clk_edge, + const Scene *scene, + PathDelayFunc get_path_delay) +{ + RiseFallMinMaxDelay delays; + VertexPathIterator path_iter(vertex, scene, nullptr, nullptr, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); - Slack path_slack = path->slack(this); - int rf_index = path->rfIndex(this); - int mm_index = path->minMax(this)->index(); - if (delayLess(path_slack, slacks[rf_index][mm_index], this)) - slacks[rf_index][mm_index] = path_slack; + Delay delay = get_path_delay(path); + const RiseFall *rf = path->transition(this); + const MinMax *min_max = path->minMax(this); + const ClockEdge *path_clk_edge = path->clkEdge(this); + if (path_clk_edge == clk_edge + && !delayInf(delay)) + delays.mergeValue(rf, min_max, delay, this); } + return delays; +} + +std::string +Sta::formatDelay(const RiseFall *rf, + const MinMax *min_max, + const RiseFallMinMaxDelay &delays, + int digits) +{ + Delay delay; + bool exists; + delays.value(rf, min_max, delay, exists); + if (exists) + return delayAsString(delay, this, digits); + else + return "---"; } //////////////////////////////////////////////////////////////// @@ -3270,7 +3476,7 @@ Sta::findClkMinPeriod(const Clock *clk, search_->findArrivals(); VisitPathEnds visit_ends(this); MinPeriodEndVisitor min_period_visitor(clk, include_port_paths, this); - for (Vertex *vertex : *search_->endpoints()) { + for (Vertex *vertex : search_->endpoints()) { findRequired(vertex); visit_ends.visitPathEnds(vertex, &min_period_visitor); } @@ -3286,7 +3492,7 @@ Sta::findRequired(Vertex *vertex) search_->findAllArrivals(); if (search_->isEndpoint(vertex) // Need to include downstream required times if there is fanout. - && !hasFanout(vertex, search_->searchAdj(), graph_)) + && !hasFanout(vertex, search_->searchAdj(), graph_, cmdMode())) search_->seedRequired(vertex); else search_->findRequireds(vertex->level()); @@ -3300,11 +3506,11 @@ Sta::totalNegativeSlack(const MinMax *min_max) } Slack -Sta::totalNegativeSlack(const Corner *corner, +Sta::totalNegativeSlack(const Scene *scene, const MinMax *min_max) { searchPreamble(); - return search_->totalNegativeSlack(corner, min_max); + return search_->totalNegativeSlack(scene, min_max); } Slack @@ -3328,14 +3534,14 @@ Sta::worstSlack(const MinMax *min_max, } void -Sta::worstSlack(const Corner *corner, +Sta::worstSlack(const Scene *scene, const MinMax *min_max, // Return values. Slack &worst_slack, Vertex *&worst_vertex) { searchPreamble(); - return search_->worstSlack(corner, min_max, worst_slack, worst_vertex); + return search_->worstSlack(scene, min_max, worst_slack, worst_vertex); } //////////////////////////////////////////////////////////////// @@ -3343,12 +3549,12 @@ Sta::worstSlack(const Corner *corner, string Sta::reportDelayCalc(Edge *edge, TimingArc *arc, - const Corner *corner, + const Scene *scene, const MinMax *min_max, int digits) { findDelays(); - return graph_delay_calc_->reportDelayCalc(edge, arc, corner, min_max, digits); + return graph_delay_calc_->reportDelayCalc(edge, arc, scene, min_max, digits); } void @@ -3385,8 +3591,9 @@ Sta::findDelays(Level level) void Sta::delayCalcPreamble() { - ensureLibLinked(); - ensureClkNetwork(); + ensureLevelized(); + for (Mode *mode : modes_) + mode->clkNetwork()->ensureClkNetwork(); } void @@ -3398,27 +3605,31 @@ Sta::setIncrementalDelayTolerance(float tol) ArcDelay Sta::arcDelay(Edge *edge, TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) + DcalcAPIndex ap_index) { findDelays(edge->to(graph_)); - return graph_->arcDelay(edge, arc, dcalc_ap->index()); + return graph_->arcDelay(edge, arc, ap_index); } bool Sta::arcDelayAnnotated(Edge *edge, TimingArc *arc, - DcalcAnalysisPt *dcalc_ap) + const Scene *scene, + const MinMax *min_max) { - return graph_->arcDelayAnnotated(edge, arc, dcalc_ap->index()); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + return graph_->arcDelayAnnotated(edge, arc, ap_index); } void Sta::setArcDelayAnnotated(Edge *edge, TimingArc *arc, - DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, bool annotated) { - graph_->setArcDelayAnnotated(edge, arc, dcalc_ap->index(), annotated); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + graph_->setArcDelayAnnotated(edge, arc, ap_index, annotated); Vertex *to = edge->to(graph_); search_->arrivalInvalid(to); search_->requiredInvalid(edge->from(graph_)); @@ -3427,49 +3638,17 @@ Sta::setArcDelayAnnotated(Edge *edge, } Slew -Sta::vertexSlew(Vertex *vertex, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) -{ - findDelays(vertex); - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - return graph_->slew(vertex, rf, dcalc_ap->index()); -} - -Slew -Sta::vertexSlew(Vertex *vertex, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) -{ - findDelays(vertex); - return graph_->slew(vertex, rf, dcalc_ap->index()); -} - -Slew -Sta::vertexSlew(Vertex *vertex, - const RiseFall *rf, +Sta::slew(Vertex *vertex, + const RiseFallBoth *rf, + const SceneSeq &scenes, const MinMax *min_max) { findDelays(vertex); Slew mm_slew = min_max->initValue(); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - Slew slew = graph_->slew(vertex, rf, dcalc_ap->index()); - if (delayGreater(slew, mm_slew, min_max, this)) - mm_slew = slew; - } - return mm_slew; -} - -Slew -Sta::vertexSlew(Vertex *vertex, - const MinMax *min_max) -{ - findDelays(vertex); - Slew mm_slew = min_max->initValue(); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - for (const RiseFall *rf : RiseFall::range()) { - Slew slew = graph_->slew(vertex, rf, dcalc_ap->index()); + for (const Scene *scene : scenes) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + for (const RiseFall *rf : rf->range()) { + Slew slew = graph_->slew(vertex, rf, ap_index); if (delayGreater(slew, mm_slew, min_max, this)) mm_slew = slew; } @@ -3518,7 +3697,7 @@ Sta::ensureGraph() void Sta::makeGraph() { - graph_ = new Graph(this, 2, corners_->dcalcAnalysisPtCount()); + graph_ = new Graph(this, 2, dcalcAnalysisPtCount()); graph_->makeGraph(); } @@ -3526,10 +3705,6 @@ void Sta::ensureLevelized() { ensureGraph(); - ensureGraphSdcAnnotated(); - // Need constant propagation before levelization to know edges that - // are disabled by constants. - sim_->ensureConstantsPropagated(); levelize_->ensureLevelized(); } @@ -3538,12 +3713,15 @@ Sta::updateGeneratedClks() { if (update_genclks_) { ensureLevelized(); + 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; - for (Clock *clk : sdc_->clks()) { + for (Clock *clk : sdc->clocks()) { if (clk->isGenerated() && !clk->waveformValid()) { - search_->genclks()->ensureMaster(clk); + genclks->ensureMaster(clk, sdc); Clock *master_clk = clk->masterClk(); if (master_clk && master_clk->waveformValid()) { clk->generate(master_clk); @@ -3553,6 +3731,7 @@ Sta::updateGeneratedClks() } } } + } update_genclks_ = false; } @@ -3570,18 +3749,6 @@ Sta::graphLoops() return levelize_->loops(); } -PathAnalysisPt * -Sta::pathAnalysisPt(Path *path) -{ - return path->pathAnalysisPt(this); -} - -DcalcAnalysisPt * -Sta::pathDcalcAnalysisPt(Path *path) -{ - return pathAnalysisPt(path)->dcalcAnalysisPt(); -} - Vertex * Sta::maxPathCountVertex() const { @@ -3642,14 +3809,13 @@ Sta::clkInfoCount() const void Sta::setArcDelay(Edge *edge, TimingArc *arc, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max, ArcDelay delay) { ensureGraph(); for (const MinMax *mm : min_max->range()) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); - DcalcAPIndex ap_index = dcalc_ap->index(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(mm); graph_->setArcDelay(edge, arc, ap_index, delay); // Don't let delay calculation clobber the value. graph_->setArcDelayAnnotated(edge, arc, ap_index, true); @@ -3664,15 +3830,14 @@ Sta::setArcDelay(Edge *edge, void Sta::setAnnotatedSlew(Vertex *vertex, - const Corner *corner, + const Scene *scene, const MinMaxAll *min_max, const RiseFallBoth *rf, float slew) { ensureGraph(); for (const MinMax *mm : min_max->range()) { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); - DcalcAPIndex ap_index = dcalc_ap->index(); + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(mm); for (const RiseFall *rf1 : rf->range()) { graph_->setSlew(vertex, rf1, ap_index, slew); // Don't let delay calculation clobber the value. @@ -3684,7 +3849,7 @@ Sta::setAnnotatedSlew(Vertex *vertex, void Sta::writeSdf(const char *filename, - const Corner *corner, + const Scene *scene, char divider, bool include_typ, int digits, @@ -3693,7 +3858,7 @@ Sta::writeSdf(const char *filename, bool no_version) { findDelays(); - sta::writeSdf(filename, corner, divider, include_typ, digits, gzip, + sta::writeSdf(filename, scene, divider, include_typ, digits, gzip, no_timestamp, no_version, this); } @@ -3707,50 +3872,55 @@ Sta::removeDelaySlewAnnotations() } LogicValue -Sta::simLogicValue(const Pin *pin) +Sta::simLogicValue(const Pin *pin, + const Mode *mode) { ensureGraph(); - sim_->ensureConstantsPropagated(); - return sim_->logicValue(pin); + Sim *sim = mode->sim(); + sim->ensureConstantsPropagated(); + return sim->simValue(pin); } +//////////////////////////////////////////////////////////////// + +// These constants are mode/sdc independent. + void Sta::findLogicConstants() { ensureGraph(); - sim_->findLogicConstants(); + // Sdc independent constants so any mode should return the same values. + Sim *sim = cmdMode()->sim(); + sim->findLogicConstants(); } void Sta::clearLogicConstants() { - sim_->clear(); + Sim *sim = cmdMode()->sim(); + sim->clear(); } +//////////////////////////////////////////////////////////////// + void Sta::setPortExtPinCap(const Port *port, const RiseFallBoth *rf, - const Corner *corner, const MinMaxAll *min_max, - float cap) + float cap, + Sdc *sdc) { for (const RiseFall *rf1 : rf->range()) { - for (const MinMax *mm : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner : *corners_) - sdc_->setPortExtPinCap(port, rf1, corner, mm, cap); - } - else - sdc_->setPortExtPinCap(port, rf1, corner, mm, cap); - } + for (const MinMax *mm : min_max->range()) + sdc->setPortExtPinCap(port, rf1, mm, cap); } delaysInvalidFromFanin(port); } void Sta::portExtCaps(const Port *port, - const Corner *corner, const MinMax *min_max, + const Sdc *sdc, float &pin_cap, float &wire_cap, int &fanout) @@ -3765,7 +3935,7 @@ Sta::portExtCaps(const Port *port, float pin_cap1, wire_cap1; int fanout1; bool pin_exists1, wire_exists1, fanout_exists1; - sdc_->portExtCap(port, rf, corner, min_max, + sdc->portExtCap(port, rf, min_max, pin_cap1, pin_exists1, wire_cap1, wire_exists1, fanout1, fanout_exists1); @@ -3792,82 +3962,63 @@ Sta::portExtCaps(const Port *port, void Sta::setPortExtWireCap(const Port *port, - bool subtract_pin_cap, const RiseFallBoth *rf, - const Corner *corner, const MinMaxAll *min_max, - float cap) + float cap, + Sdc *sdc) { for (const RiseFall *rf1 : rf->range()) { - for (const MinMax *mm : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner : *corners_) - sdc_->setPortExtWireCap(port, subtract_pin_cap, rf1, corner, mm, cap); - } - else - sdc_->setPortExtWireCap(port, subtract_pin_cap, rf1, corner, mm, cap); - } + for (const MinMax *mm : min_max->range()) + sdc->setPortExtWireCap(port, rf1, mm, cap); } delaysInvalidFromFanin(port); } void -Sta::removeNetLoadCaps() const +Sta::removeNetLoadCaps(Sdc *sdc) const { - sdc_->removeNetLoadCaps(); + sdc->removeNetLoadCaps(); delaysInvalid(); } void Sta::setPortExtFanout(const Port *port, int fanout, - const Corner *corner, - const MinMaxAll *min_max) + const MinMaxAll *min_max, + Sdc *sdc) { - for (const MinMax *mm : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner : *corners_) - sdc_->setPortExtFanout(port, corner, mm, fanout); - } - else - sdc_->setPortExtFanout(port, corner, mm, fanout); - } + for (const MinMax *mm : min_max->range()) + sdc->setPortExtFanout(port, mm, fanout); delaysInvalidFromFanin(port); } void Sta::setNetWireCap(const Net *net, bool subtract_pin_cap, - const Corner *corner, const MinMaxAll *min_max, - float cap) + float cap, + Sdc *sdc) { - for (const MinMax *mm : min_max->range()) { - if (corner == nullptr) { - for (const Corner *corner : *corners_) - sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap); - } - else - sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap); - } + for (const MinMax *mm : min_max->range()) + sdc->setNetWireCap(net, subtract_pin_cap, mm, cap); delaysInvalidFromFanin(net); } void Sta::connectedCap(const Pin *drvr_pin, const RiseFall *rf, - const Corner *corner, + const Scene *scene, const MinMax *min_max, float &pin_cap, float &wire_cap) const { - const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - graph_delay_calc_->loadCap(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap); + graph_delay_calc_->loadCap(drvr_pin, rf, scene, min_max, + pin_cap, wire_cap); } void Sta::connectedCap(const Net *net, - Corner *corner, + Scene *scene, const MinMax *min_max, float &pin_cap, float &wire_cap) const @@ -3876,10 +4027,10 @@ Sta::connectedCap(const Net *net, if (drvr_pin) { pin_cap = min_max->initValue(); wire_cap = min_max->initValue(); - for (const Corner *corner : makeCornerSeq(corner)) { + for (const Scene *scene : makeSceneSeq(scene)) { for (const RiseFall *rf : RiseFall::range()) { float pin_cap1, wire_cap1; - connectedCap(drvr_pin, rf, corner, min_max, pin_cap1, wire_cap1); + connectedCap(drvr_pin, rf, scene, min_max, pin_cap1, wire_cap1); pin_cap = min_max->minMax(pin_cap, pin_cap1); wire_cap = min_max->minMax(wire_cap, wire_cap1); } @@ -3891,28 +4042,18 @@ Sta::connectedCap(const Net *net, } } -CornerSeq -Sta::makeCornerSeq(Corner *corner) const -{ - CornerSeq corners; - if (corner) - corners.push_back(corner); - else - corners = corners_->corners(); - return corners; -} - float Sta::capacitance(const LibertyPort *port, - Corner *corner, + Scene *scene, const MinMax *min_max) { - OperatingConditions *op_cond = operatingConditions(min_max); float cap = min_max->initValue(); - for (const Corner *corner : makeCornerSeq(corner)) { - const LibertyPort *corner_port = port->cornerPort(corner, min_max); + for (const Scene *scene : makeSceneSeq(scene)) { + const Sdc *sdc = scene->sdc(); + 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, corner_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; } @@ -3940,17 +4081,19 @@ Sta::findNetParasiticDrvrPin(const Net *net) const void Sta::setResistance(const Net *net, const MinMaxAll *min_max, - float res) + float res, + Sdc *sdc) { - sdc_->setResistance(net, min_max, res); + sdc->setResistance(net, min_max, res); } //////////////////////////////////////////////////////////////// bool -Sta::readSpef(const char *filename, +Sta::readSpef(const std::string &name, + const std::string &filename, Instance *instance, - const Corner *corner, + Scene *scene, // -scene deprecated 11/20/2025 const MinMaxAll *min_max, bool pin_cap_included, bool keep_coupling_caps, @@ -3958,43 +4101,66 @@ Sta::readSpef(const char *filename, bool reduce) { ensureLibLinked(); - setParasiticAnalysisPts(corner != nullptr); - const MinMax *ap_min_max = (min_max == MinMaxAll::all()) - ? MinMax::max() - : min_max->asMinMax(); - const Corner *ap_corner = corner ? corner : corners_->corners()[0]; - ParasiticAnalysisPt *ap = ap_corner->findParasiticAnalysisPt(ap_min_max); - bool success = readSpefFile(filename, instance, ap, + Parasitics *parasitics = nullptr; + // 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) + spef_name = scene->name(); + if (min_max != MinMaxAll::minMax()) { + spef_name += "_"; + spef_name += min_max->to_string(); + } + parasitics = makeConcreteParasitics(spef_name, filename); + } + else + parasitics = findParasitics(spef_name); + if (scene) + scene->setParasitics(parasitics, min_max); + else { + parasitics = findParasitics(spef_name); + for (Scene *scene : scenes_) + scene->setParasitics(parasitics, min_max); + } + } + else { + parasitics = findParasitics(name); + if (parasitics == nullptr) + parasitics = makeConcreteParasitics(name, filename); + } + + bool success = readSpefFile(filename.c_str(), instance, pin_cap_included, keep_coupling_caps, coupling_cap_factor, reduce, - corner, min_max, this); + scene, min_max, parasitics, this); delaysInvalid(); return success; } -void -Sta::setParasiticAnalysisPts(bool per_corner) +Parasitics * +Sta::findParasitics(const std::string &name) { - if (per_corner != parasitics_per_corner_) { - deleteParasitics(); - parasitics_per_corner_ = per_corner; - makeParasiticAnalysisPts(); - } + return findKey(parasitics_name_map_, name); } void -Sta::makeParasiticAnalysisPts() -{ - corners_->makeParasiticAnalysisPts(parasitics_per_corner_); -} - -void -Sta::reportParasiticAnnotation(bool report_unannotated, - const Corner *corner) +Sta::reportParasiticAnnotation(const string &spef_name, + bool report_unannotated) { ensureLibLinked(); ensureGraph(); - sta::reportParasiticAnnotation(report_unannotated, corner, this); + Parasitics *parasitics = nullptr; + if (!spef_name.empty()) { + parasitics = findParasitics(spef_name); + if (parasitics == nullptr) + report_->error(1560, "spef %s not found.", spef_name.c_str()); + } + else + parasitics = cmd_scene_->parasitics(MinMax::max()); + sta::reportParasiticAnnotation(parasitics, report_unannotated, + cmd_scene_, this); } void @@ -4006,11 +4172,11 @@ Sta::findPiElmore(Pin *drvr_pin, float &c1, bool &exists) const { - Corner *corner = cmd_corner_; - const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max); - Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); + Scene *scene = cmd_scene_; + const Parasitics *parasitics = scene->parasitics(min_max); + Parasitic *pi_elmore = parasitics->findPiElmore(drvr_pin, rf, min_max); if (pi_elmore) { - parasitics_->piModel(pi_elmore, c2, rpi, c1); + parasitics->piModel(pi_elmore, c2, rpi, c1); exists = true; } else @@ -4025,10 +4191,10 @@ Sta::makePiElmore(Pin *drvr_pin, float rpi, float c1) { - const Corner *corner = cmd_corner_; + const Scene *scene = cmd_scene_; for (const MinMax *mm : min_max->range()) { - ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); - parasitics_->makePiElmore(drvr_pin, rf, ap, c2, rpi, c1); + Parasitics *parasitics = scene->parasitics(mm); + parasitics->makePiElmore(drvr_pin, rf, mm, c2, rpi, c1); } delaysInvalidFrom(drvr_pin); } @@ -4041,11 +4207,11 @@ Sta::findElmore(Pin *drvr_pin, float &elmore, bool &exists) const { - Corner *corner = cmd_corner_; - const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max); - Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); + Scene *scene = cmd_scene_; + const Parasitics *parasitics = scene->parasitics(min_max); + Parasitic *pi_elmore = parasitics->findPiElmore(drvr_pin, rf, min_max); if (pi_elmore) - parasitics_->findElmore(pi_elmore, load_pin, elmore, exists); + parasitics->findElmore(pi_elmore, load_pin, elmore, exists); else exists = false; } @@ -4057,12 +4223,12 @@ Sta::setElmore(Pin *drvr_pin, const MinMaxAll *min_max, float elmore) { - const Corner *corner = cmd_corner_; + const Scene *scene = cmd_scene_; for (const MinMax *mm : min_max->range()) { - const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); - Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); + Parasitics *parasitics = scene->parasitics(mm); + Parasitic *pi_elmore = parasitics->findPiElmore(drvr_pin, rf, mm); if (pi_elmore) - parasitics_->setElmore(pi_elmore, load_pin, elmore); + parasitics->setElmore(pi_elmore, load_pin, elmore); } delaysInvalidFrom(drvr_pin); } @@ -4070,17 +4236,39 @@ Sta::setElmore(Pin *drvr_pin, void Sta::deleteParasitics() { - parasitics_->deleteParasitics(); + Parasitics *parasitics_default = findParasitics("default"); + for (auto [name, parasitics] : parasitics_name_map_) { + if (parasitics != parasitics_default) + delete parasitics; + } + parasitics_name_map_.clear(); + + parasitics_name_map_[parasitics_default->name()] = parasitics_default; + + for (Scene *scene : scenes_) + scene->setParasitics(parasitics_default, MinMaxAll::minMax()); + delaysInvalid(); } +Parasitics * +Sta::makeConcreteParasitics(std::string name, + std::string filename) +{ + Parasitics *parasitics = new ConcreteParasitics(name, filename, this); + parasitics_name_map_[name] = parasitics; + return parasitics; +} + Parasitic * Sta::makeParasiticNetwork(const Net *net, bool includes_pin_caps, - const ParasiticAnalysisPt *ap) + const Scene *scene, + const MinMax *min_max) { - Parasitic *parasitic = parasitics_->makeParasiticNetwork(net, includes_pin_caps, ap); - delaysInvalidFromFanin(const_cast(net)); + Parasitics *parasitics = scene->parasitics(min_max); + Parasitic *parasitic = parasitics->makeParasiticNetwork(net, includes_pin_caps); + delaysInvalidFromFanin(net); return parasitic; } @@ -4284,7 +4472,7 @@ Sta::replaceEquivCellBefore(const Instance *inst, if (to_set) edge->setTimingArcSet(to_set); else - report_->critical(1555, "corresponding timing arc set not found in equiv cells"); + report_->critical(1556, "corresponding timing arc set not found in equiv cells"); } } } @@ -4307,8 +4495,10 @@ Sta::replaceEquivCellAfter(const Instance *inst) InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (network_->direction(pin)->isAnyInput()) - parasitics_->loadPinCapacitanceChanged(pin); + if (network_->direction(pin)->isAnyInput()) { + for (auto [name, parasitics] : parasitics_name_map_) + parasitics->loadPinCapacitanceChanged(pin); + } } delete pin_iter; } @@ -4336,10 +4526,13 @@ Sta::replaceCellPinInvalidate(const LibertyPort *from_port, bool Sta::idealClockMode() { - for (Clock *clk : sdc_->clks()) { + for (Mode *mode : modes_) { + Sdc *sdc = mode->sdc(); + for (Clock *clk : sdc->clocks()) { if (clk->isPropagated()) return false; } + } return true; } @@ -4393,9 +4586,12 @@ Sta::replaceCellAfter(const Instance *inst) InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - sim_->pinSetFuncAfter(pin); - if (network_->direction(pin)->isAnyInput()) - parasitics_->loadPinCapacitanceChanged(pin); + for (const Mode *mode : modes_) + mode->sim()->pinSetFuncAfter(pin); + if (network_->direction(pin)->isAnyInput()) { + for (auto [name, parasitics] : parasitics_name_map_) + parasitics->loadPinCapacitanceChanged(pin); + } } delete pin_iter; } @@ -4444,8 +4640,10 @@ Sta::connectPinAfter(const Pin *pin) } } } - sdc_->connectPinAfter(pin); - sim_->connectPinAfter(pin); + for (Mode *mode : modes_) { + mode->sdc()->connectPinAfter(pin); + mode->sim()->connectPinAfter(pin); + } clk_skews_->clear(); } @@ -4459,15 +4657,18 @@ Sta::connectDrvrPinAfter(Vertex *vertex) Vertex *to_vertex = edge->to(graph_); search_->arrivalInvalid(to_vertex); search_->endpointInvalid(to_vertex); - sdc_->clkHpinDisablesChanged(to_vertex->pin()); + for (Mode *mode : modes_) + mode->sdc()->clkHpinDisablesChanged(to_vertex->pin()); } Pin *pin = vertex->pin(); - sdc_->clkHpinDisablesChanged(pin); + for (Mode *mode : modes_) { + mode->sdc()->clkHpinDisablesChanged(pin); + mode->clkNetwork()->connectPinAfter(pin); + } graph_delay_calc_->delayInvalid(vertex); search_->requiredInvalid(vertex); search_->endpointInvalid(vertex); levelize_->relevelizeFrom(vertex); - clk_network_->connectPinAfter(pin); } void @@ -4480,15 +4681,18 @@ Sta::connectLoadPinAfter(Vertex *vertex) Vertex *from_vertex = edge->from(graph_); graph_delay_calc_->delayInvalid(from_vertex); search_->requiredInvalid(from_vertex); - sdc_->clkHpinDisablesChanged(from_vertex->pin()); + for (Mode *mode : modes_) + mode->sdc()->clkHpinDisablesChanged(from_vertex->pin()); levelize_->relevelizeFrom(from_vertex); } Pin *pin = vertex->pin(); - sdc_->clkHpinDisablesChanged(pin); + for (Mode *mode : modes_) { + mode->sdc()->clkHpinDisablesChanged(pin); + mode->clkNetwork()->connectPinAfter(pin); + } graph_delay_calc_->delayInvalid(vertex); search_->arrivalInvalid(vertex); search_->endpointInvalid(vertex); - clk_network_->connectPinAfter(pin); } void @@ -4497,8 +4701,16 @@ Sta::disconnectPinBefore(const Pin *pin) debugPrint(debug_, "network_edit", 1, "disconnect %s from %s", sdc_network_->pathName(pin), sdc_network_->pathName(network_->net(pin))); - parasitics_->disconnectPinBefore(pin, network_); - sim_->disconnectPinBefore(pin); + + for (auto [name, parasitics] : parasitics_name_map_) + parasitics->disconnectPinBefore(pin); + bool is_hierarchical = network_->isHierarchical(pin); + for (Mode *mode : modes_) { + mode->sim()->disconnectPinBefore(pin); + if (!is_hierarchical) + mode->clkNetwork()->disconnectPinBefore(pin); + } + if (graph_) { if (network_->isDriver(pin)) { Vertex *vertex = graph_->pinDrvrVertex(pin); @@ -4510,7 +4722,6 @@ Sta::disconnectPinBefore(const Pin *pin) if (edge->role()->isWire()) deleteEdge(edge); } - clk_network_->disconnectPinBefore(pin); } } if (network_->isLoad(pin)) { @@ -4523,17 +4734,18 @@ Sta::disconnectPinBefore(const Pin *pin) if (edge->role()->isWire()) deleteEdge(edge); } - clk_network_->disconnectPinBefore(pin); } } - if (network_->isHierarchical(pin)) { + 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); - clk_network_->disconnectPinBefore(edge->from(graph_)->pin()); + const Pin *from_pin = edge->from(graph_)->pin(); + for (Mode *mode : modes_) + mode->clkNetwork()->disconnectPinBefore(from_pin); } } } @@ -4552,7 +4764,8 @@ Sta::deleteEdge(Edge *edge) graph_delay_calc_->delayInvalid(to); levelize_->relevelizeFrom(to); levelize_->deleteEdgeBefore(edge); - sdc_->clkHpinDisablesChanged(edge->from(graph_)->pin()); + for (Mode *mode : modes_) + mode->sdc()->clkHpinDisablesChanged(edge->from(graph_)->pin()); graph_->deleteEdge(edge); } @@ -4581,7 +4794,8 @@ Sta::deleteNetBefore(const Net *net) } delete pin_iter; } - sdc_->deleteNetBefore(net); + for (Mode *mode : modes_) + mode->sdc()->deleteNetBefore(net); } void @@ -4607,8 +4821,10 @@ Sta::deleteInstanceBefore(const Instance *inst) void Sta::deleteLeafInstanceBefore(const Instance *inst) { - sim_->deleteInstanceBefore(inst); - sdc_->deleteInstanceBefore(inst); + for (Mode *mode : modes_) { + mode->sim()->deleteInstanceBefore(inst); + mode->sdc()->deleteInstanceBefore(inst); + } power_->deleteInstanceBefore(inst); } @@ -4684,9 +4900,12 @@ Sta::deletePinBefore(const Pin *pin) } } } - sdc_->deletePinBefore(pin); - sim_->deletePinBefore(pin); - clk_network_->deletePinBefore(pin); + + for (const Mode *mode : modes_) { + mode->sdc()->deletePinBefore(pin); + mode->sim()->deletePinBefore(pin); + mode->clkNetwork()->deletePinBefore(pin); + } power_->deletePinBefore(pin); clk_skews_->clear(); } @@ -4796,18 +5015,20 @@ Sta::delaysInvalidFromFanin(Vertex *vertex) //////////////////////////////////////////////////////////////// ClockSet -Sta::clocks(const Pin *pin) +Sta::clocks(const Pin *pin, + const Mode *mode) { ensureClkArrivals(); - return search_->clocks(pin); + return search_->clocks(pin, mode); } ClockSet -Sta::clockDomains(const Pin *pin) +Sta::clockDomains(const Pin *pin, + const Mode *mode) { searchPreamble(); search_->findAllArrivals(); - return search_->clockDomains(pin); + return search_->clockDomains(pin, mode); } //////////////////////////////////////////////////////////////// @@ -4816,59 +5037,68 @@ InstanceSet Sta::findRegisterInstances(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, - bool latches) + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegInstances(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegInstances(clks, clk_rf, edge_triggered, latches, + mode, this); } PinSet Sta::findRegisterDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, - bool latches) + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegDataPins(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegDataPins(clks, clk_rf, edge_triggered, latches, + mode, this); } PinSet Sta::findRegisterClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, - bool latches) + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegClkPins(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegClkPins(clks, clk_rf, edge_triggered, latches, + mode, this); } PinSet Sta::findRegisterAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, - bool latches) + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegAsyncPins(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegAsyncPins(clks, clk_rf, edge_triggered, latches, + mode, this); } PinSet Sta::findRegisterOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, - bool latches) + bool latches, + const Mode *mode) { - findRegisterPreamble(); - return findRegOutputPins(clks, clk_rf, edge_triggered, latches, this); + findRegisterPreamble(mode); + return findRegOutputPins(clks, clk_rf, edge_triggered, latches, + mode, this); } void -Sta::findRegisterPreamble() +Sta::findRegisterPreamble(const Mode *mode) { ensureLibLinked(); ensureGraph(); - ensureGraphSdcAnnotated(); - sim_->ensureConstantsPropagated(); + mode->sim()->ensureConstantsPropagated(); } //////////////////////////////////////////////////////////////// @@ -4879,13 +5109,16 @@ class FanInOutSrchPred : public SearchPred FanInOutSrchPred(bool thru_disabled, bool thru_constants, const StaState *sta); - virtual bool searchFrom(const Vertex *from_vertex); - virtual bool searchThru(Edge *edge); - virtual bool searchTo(const Vertex *to_vertex); + bool searchFrom(const Vertex *from_vertex, + const Mode *mode) const override; + bool searchThru(Edge *edge, + const Mode *mode) const override; + bool searchTo(const Vertex *to_vertex, + const Mode *mode) const override; protected: bool crossesHierarchy(Edge *edge); - virtual bool searchThruRole(Edge *edge); + virtual bool searchThruRole(Edge *edge) const; bool thru_disabled_; bool thru_constants_; @@ -4895,7 +5128,7 @@ class FanInOutSrchPred : public SearchPred FanInOutSrchPred::FanInOutSrchPred(bool thru_disabled, bool thru_constants, const StaState *sta) : - SearchPred(), + SearchPred(sta), thru_disabled_(thru_disabled), thru_constants_(thru_constants), sta_(sta) @@ -4903,28 +5136,34 @@ FanInOutSrchPred::FanInOutSrchPred(bool thru_disabled, } bool -FanInOutSrchPred::searchFrom(const Vertex *from_vertex) +FanInOutSrchPred::searchFrom(const Vertex *from_vertex, + const Mode *mode) const { + const Pin *from_pin = from_vertex->pin(); + const Sdc *sdc = mode->sdc(); return (thru_disabled_ - || !from_vertex->isDisabledConstraint()) + || !sdc->isDisabledConstraint(from_pin)) && (thru_constants_ - || !from_vertex->isConstant()); + || !mode->sim()->isConstant(from_vertex)); } bool -FanInOutSrchPred::searchThru(Edge *edge) +FanInOutSrchPred::searchThru(Edge *edge, + const Mode *mode) const { + const Sdc *sdc = mode->sdc(); + const Sim *sim = mode->sim(); return searchThruRole(edge) && (thru_disabled_ - || !(edge->isDisabledConstraint() - || edge->isDisabledCond() + || !(sdc->isDisabledConstraint(edge) + || sim->isDisabledCond(edge) || sta_->isDisabledCondDefault(edge))) && (thru_constants_ - || edge->simTimingSense() != TimingSense::none); + || sim->simTimingSense(edge) != TimingSense::none); } bool -FanInOutSrchPred::searchThruRole(Edge *edge) +FanInOutSrchPred::searchThruRole(Edge *edge) const { const TimingRole *role = edge->role(); return role == TimingRole::wire() @@ -4946,12 +5185,15 @@ FanInOutSrchPred::crossesHierarchy(Edge *edge) } bool -FanInOutSrchPred::searchTo(const Vertex *to_vertex) +FanInOutSrchPred::searchTo(const Vertex *to_vertex, + const Mode *mode) const { + const Pin *to_pin = to_vertex->pin(); + const Sdc *sdc = mode->sdc(); return (thru_disabled_ - || !to_vertex->isDisabledConstraint()) + || !sdc->isDisabledConstraint(to_pin)) && (thru_constants_ - || !to_vertex->isConstant()); + || !mode->sim()->isConstant(to_vertex)); } class FaninSrchPred : public FanInOutSrchPred @@ -4962,7 +5204,7 @@ class FaninSrchPred : public FanInOutSrchPred const StaState *sta); protected: - virtual bool searchThruRole(Edge *edge); + bool searchThruRole(Edge *edge) const override; }; FaninSrchPred::FaninSrchPred(bool thru_disabled, @@ -4973,7 +5215,7 @@ FaninSrchPred::FaninSrchPred(bool thru_disabled, } bool -FaninSrchPred::searchThruRole(Edge *edge) +FaninSrchPred::searchThruRole(Edge *edge) const { const TimingRole *role = edge->role(); return role == TimingRole::wire() @@ -4991,10 +5233,12 @@ Sta::findFaninPins(PinSeq *to, int inst_levels, int pin_levels, bool thru_disabled, - bool thru_constants) + bool thru_constants, + const Mode *mode) { ensureGraph(); ensureLevelized(); + mode->sim()->ensureConstantsPropagated(); PinSet fanin(network_); FaninSrchPred pred(thru_disabled, thru_constants, this); for (const Pin *pin : *to) { @@ -5003,13 +5247,13 @@ Sta::findFaninPins(PinSeq *to, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); findFaninPins(edge->from(graph_), flat, startpoints_only, - inst_levels, pin_levels, fanin, pred); + inst_levels, pin_levels, fanin, pred, mode); } } else { Vertex *vertex = graph_->pinLoadVertex(pin); findFaninPins(vertex, flat, startpoints_only, - inst_levels, pin_levels, fanin, pred); + inst_levels, pin_levels, fanin, pred, mode); } } return fanin; @@ -5022,18 +5266,17 @@ Sta::findFaninPins(Vertex *vertex, int inst_levels, int pin_levels, PinSet &fanin, - SearchPred &pred) + SearchPred &pred, + const Mode *mode) { - VertexSet visited(graph_); + VertexSet visited = makeVertexSet(this); findFaninPins(vertex, flat, inst_levels, - pin_levels, visited, &pred, 0, 0); - VertexSet::Iterator visited_iter(visited); - while (visited_iter.hasNext()) { - Vertex *visited_vertex = visited_iter.next(); + 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) - || !hasFanin(visited_vertex, &pred, graph_)) + || !hasFanin(visited_vertex, &pred, graph_, mode)) fanin.insert(visited_pin); } } @@ -5046,11 +5289,12 @@ Sta::findFaninPins(Vertex *to, VertexSet &visited, SearchPred *pred, int inst_level, - int pin_level) + int pin_level, + const Mode *mode) { debugPrint(debug_, "fanin", 1, "%s", to->to_string(this).c_str()); - if (!visited.hasKey(to)) { + if (!visited.contains(to)) { visited.insert(to); Pin *to_pin = to->pin(); bool is_reg_clk_pin = network_->isRegClkPin(to_pin); @@ -5059,19 +5303,19 @@ Sta::findFaninPins(Vertex *to, || inst_level < inst_levels) && (pin_levels <= 0 || pin_level < pin_levels) - && pred->searchTo(to)) { + && 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) + if (pred->searchThru(edge, mode) && (flat || !crossesHierarchy(edge)) - && pred->searchFrom(from_vertex)) { + && 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); + pin_level+1, mode); } } } @@ -5085,10 +5329,11 @@ Sta::findFaninInstances(PinSeq *to, int inst_levels, int pin_levels, bool thru_disabled, - bool thru_constants) + bool thru_constants, + const Mode *mode) { PinSet pins = findFaninPins(to, flat, startpoints_only, inst_levels, - pin_levels, thru_disabled, thru_constants); + pin_levels, thru_disabled, thru_constants, mode); return pinInstances(pins, network_); } @@ -5099,27 +5344,27 @@ Sta::findFanoutPins(PinSeq *from, int inst_levels, int pin_levels, bool thru_disabled, - bool thru_constants) + bool thru_constants, + const Mode *mode) { ensureGraph(); ensureLevelized(); + mode->sim()->ensureConstantsPropagated(); PinSet fanout(network_); FanInOutSrchPred pred(thru_disabled, thru_constants, this); - PinSeq::Iterator from_iter(from); - while (from_iter.hasNext()) { - const Pin *pin = from_iter.next(); + for (const Pin *pin : *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); + inst_levels, pin_levels, fanout, pred, mode); } } else { Vertex *vertex = graph_->pinDrvrVertex(pin); findFanoutPins(vertex, flat, endpoints_only, - inst_levels, pin_levels, fanout, pred); + inst_levels, pin_levels, fanout, pred, mode); } } return fanout; @@ -5132,17 +5377,16 @@ Sta::findFanoutPins(Vertex *vertex, int inst_levels, int pin_levels, PinSet &fanout, - SearchPred &pred) + SearchPred &pred, + const Mode *mode) { - VertexSet visited(graph_); + VertexSet visited = makeVertexSet(this); findFanoutPins(vertex, flat, inst_levels, - pin_levels, visited, &pred, 0, 0); - VertexSet::Iterator visited_iter(visited); - while (visited_iter.hasNext()) { - Vertex *visited_vertex = visited_iter.next(); + 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)) + || search_->isEndpoint(visited_vertex, &pred, mode)) fanout.insert(visited_pin); } } @@ -5156,30 +5400,31 @@ Sta::findFanoutPins(Vertex *from, VertexSet &visited, SearchPred *pred, int inst_level, - int pin_level) + int pin_level, + const Mode *mode) { debugPrint(debug_, "fanout", 1, "%s", from->to_string(this).c_str()); - if (!visited.hasKey(from)) { + if (!visited.contains(from)) { visited.insert(from); - if (!search_->isEndpoint(from, pred) + if (!search_->isEndpoint(from, pred, mode) && (inst_levels <= 0 || inst_level < inst_levels) && (pin_levels <= 0 || pin_level < pin_levels) - && pred->searchFrom(from)) { + && 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) + if (pred->searchThru(edge, mode) && (flat || !crossesHierarchy(edge)) - && pred->searchTo(to_vertex)) { + && 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); + pin_level+1, mode); } } } @@ -5193,10 +5438,11 @@ Sta::findFanoutInstances(PinSeq *from, int inst_levels, int pin_levels, bool thru_disabled, - bool thru_constants) + bool thru_constants, + const Mode *mode) { PinSet pins = findFanoutPins(from, flat, endpoints_only, inst_levels, - pin_levels, thru_disabled, thru_constants); + pin_levels, thru_disabled, thru_constants, mode); return pinInstances(pins, network_); } @@ -5245,7 +5491,8 @@ instMaxSlew(const Instance *inst, Pin *pin = pin_iter->next(); if (network->isDriver(pin)) { Vertex *vertex = graph->pinDrvrVertex(pin); - Slew slew = sta->vertexSlew(vertex, MinMax::max()); + Slew slew = sta->slew(vertex, RiseFallBoth::riseFall(), + sta->scenes(), MinMax::max()); if (delayGreater(slew, max_slew, sta)) max_slew = slew; } @@ -5272,79 +5519,77 @@ Sta::slowDrivers(int count) //////////////////////////////////////////////////////////////// void -Sta::checkSlewLimitPreamble() +Sta::reportSlewChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes, + const MinMax *min_max) +{ + checkSlewsPreamble(); + 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(""); + + if (!verbose) + report_path_->reportLimitShortHeader(report_path_->fieldSlew()); + for (SlewCheck &check : checks) { + if (verbose) + report_path_->reportLimitVerbose(report_path_->fieldSlew(), + check.pin(), check.edge(), + delayAsFloat(check.slew()), + check.limit(), check.slack(), + check.scene(), min_max); + else + report_path_->reportLimitShort(report_path_->fieldSlew(), check.pin(), + delayAsFloat(check.slew()), + check.limit(), check.slack()); + } + report_->reportLine(""); + } +} + +void +Sta::checkSlewsPreamble() { - if (sdc_->haveClkSlewLimits()) + ensureLevelized(); + bool have_clk_slew_limits = false; + for (Mode *mode : modes_) { + 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); else findDelays(); - if (check_slew_limits_ == nullptr) - makeCheckSlewLimits(); - ensureClkNetwork(); -} - -PinSeq -Sta::checkSlewLimits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) -{ - checkSlewLimitPreamble(); - return check_slew_limits_->checkSlewLimits(net, violators, corner, min_max); -} - -void -Sta::reportSlewLimitShortHeader() -{ - report_path_->reportLimitShortHeader(report_path_->fieldSlew()); -} - -void -Sta::reportSlewLimitShort(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - const Corner *corner1; - const RiseFall *rf1; - Slew slew1; - float limit1, slack1; - check_slew_limits_->checkSlew(pin, corner, min_max, true, - corner1, rf1, slew1, limit1, slack1); - report_path_->reportLimitShort(report_path_->fieldSlew(), pin, - delayAsFloat(slew1), limit1, slack1); -} - -void -Sta::reportSlewLimitVerbose(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - const Corner *corner1; - const RiseFall *rf1; - Slew slew1; - float limit1, slack1; - check_slew_limits_->checkSlew(pin, corner, min_max, true, - corner1, rf1, slew1, limit1, slack1); - report_path_->reportLimitVerbose(report_path_->fieldSlew(), pin, rf1, - delayAsFloat(slew1), - limit1, slack1, corner1, min_max); + if (check_slews_ == nullptr) + makeCheckSlews(); } void Sta::checkSlew(const Pin *pin, - const Corner *corner, + const SceneSeq &scenes, const MinMax *min_max, bool check_clks, // Return values. - const Corner *&corner1, - const RiseFall *&rf, Slew &slew, float &limit, - float &slack) + float &slack, + const RiseFall *&rf, + const Scene *&scene) +{ + check_slews_->check(pin, scenes, min_max, check_clks, + slew, limit, slack, rf, scene); +} + +size_t +Sta::maxSlewViolationCount() { - check_slew_limits_->checkSlew(pin, corner, min_max, check_clks, - corner1, rf, slew, limit, slack); + checkSlewsPreamble(); + return check_slews_->check(nullptr, 1, true, scenes_, MinMax::max()).size(); } void @@ -5354,188 +5599,210 @@ Sta::maxSlewCheck(// Return values. float &slack, float &limit) { - checkSlewLimitPreamble(); - PinSeq pins = check_slew_limits_->checkSlewLimits(nullptr, false, nullptr, - MinMax::max()); + checkSlewsPreamble(); + SlewCheckSeq &checks = check_slews_->check(nullptr, 1, false, scenes_, MinMax::max()); + if (!checks.empty()) { + SlewCheck &check = checks[0]; + pin = check.pin(); + slew = check.slew(); + slack = check.slack(); + limit = check.limit(); + } + else { pin = nullptr; slew = 0.0; slack = INF; - limit = INF; - if (!pins.empty()) { - pin = pins[0]; - const Corner *corner; - const RiseFall *rf; - check_slew_limits_->checkSlew(pin, nullptr, MinMax::max(), true, - corner, rf, slew, limit, slack); } } void Sta::findSlewLimit(const LibertyPort *port, - const Corner *corner, + const Scene *scene, const MinMax *min_max, // Return values. float &limit, bool &exists) { - if (check_slew_limits_ == nullptr) - makeCheckSlewLimits(); - check_slew_limits_->findLimit(port, corner, min_max, + if (check_slews_ == nullptr) + makeCheckSlews(); + check_slews_->findLimit(port, scene, min_max, limit, exists); } ////////////////////////////////////////////////////////////////' void -Sta::checkFanoutLimitPreamble() -{ - if (check_fanout_limits_ == nullptr) - makeCheckFanoutLimits(); - ensureClkNetwork(); -} - -PinSeq -Sta::checkFanoutLimits(Net *net, +Sta::reportFanoutChecks(const Net *net, + size_t max_count, bool violators, + bool verbose, + const SceneSeq &scenes, const MinMax *min_max) { - checkFanoutLimitPreamble(); - return check_fanout_limits_->checkFanoutLimits(net, violators, min_max); -} + checkFanoutPreamble(); + 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(""); -void -Sta::reportFanoutLimitShortHeader() -{ + if (!verbose) 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); + else + report_path_->reportLimitShort(report_path_->fieldFanout(), + check.pin(), check.fanout(), + check.limit(), check.slack()); + } + report_->reportLine(""); + } } void -Sta::reportFanoutLimitShort(Pin *pin, - const MinMax *min_max) +Sta::checkFanoutPreamble() { - float fanout, limit, slack; - check_fanout_limits_->checkFanout(pin, min_max, - fanout, limit, slack); - report_path_->reportLimitShort(report_path_->fieldFanout(), - pin, fanout, limit, slack); + if (check_fanouts_ == nullptr) + makeCheckFanouts(); + ensureLevelized(); + for (Mode *mode : modes_) { + mode->sim()->ensureConstantsPropagated(); + mode->clkNetwork()->ensureClkNetwork(); + } } -void -Sta::reportFanoutLimitVerbose(Pin *pin, - const MinMax *min_max) +size_t +Sta::fanoutViolationCount(const MinMax *min_max, + const ModeSeq &modes) { - float fanout, limit, slack; - check_fanout_limits_->checkFanout(pin, min_max, - fanout, limit, slack); - report_path_->reportLimitVerbose(report_path_->fieldFanout(), - pin, nullptr, fanout, - limit, slack, nullptr, min_max); + FanoutCheckSeq &checks = check_fanouts_->check(nullptr, 0, true, modes, min_max); + return checks.size(); } void Sta::checkFanout(const Pin *pin, + const Mode *mode, const MinMax *min_max, // Return values. float &fanout, float &limit, float &slack) { - check_fanout_limits_->checkFanout(pin, min_max, - fanout, limit, slack); + FanoutCheck check = check_fanouts_->check(pin, mode, min_max); + pin = check.pin(); + fanout = check.fanout(); + limit = check.limit(); + slack = check.slack(); } void -Sta::maxFanoutCheck(// Return values. +Sta::maxFanoutMinSlackPin(const ModeSeq &modes, + // Return values. const Pin *&pin, float &fanout, + float &limit, float &slack, - float &limit) -{ - checkFanoutLimitPreamble(); - PinSeq pins = check_fanout_limits_->checkFanoutLimits(nullptr, false, MinMax::max()); + const Mode *&mode) +{ + checkFanoutPreamble(); + FanoutCheckSeq &checks = check_fanouts_->check(nullptr, 1, false, + modes, MinMax::max()); + if (!checks.empty()) { + FanoutCheck &check = checks[0]; + pin = check.pin(); + fanout = check.fanout(); + limit = check.limit(); + slack = check.slack(); + mode = check.mode(); + } + else { pin = nullptr; fanout = 0; - slack = INF; limit = INF; - if (!pins.empty()) { - pin = pins[0]; - check_fanout_limits_->checkFanout(pin, MinMax::max(), - fanout, limit, slack); + slack = INF; + mode = nullptr; } } ////////////////////////////////////////////////////////////////' void -Sta::checkCapacitanceLimitPreamble() -{ - if (check_capacitance_limits_ == nullptr) - makeCheckCapacitanceLimits(); - ensureClkNetwork(); -} - -PinSeq -Sta::checkCapacitanceLimits(Net *net, +Sta::reportCapacitanceChecks(const Net *net, + size_t max_count, bool violators, - const Corner *corner, + bool verbose, + const SceneSeq &scenes, const MinMax *min_max) { - checkCapacitanceLimitPreamble(); - return check_capacitance_limits_->checkCapacitanceLimits(net, violators, - corner, min_max); -} + checkCapacitancesPreamble(scenes); + 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(""); -void -Sta::reportCapacitanceLimitShortHeader() -{ + if (!verbose) 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); + else + report_path_->reportLimitShort(report_path_->fieldCapacitance(), + check.pin(), check.capacitance(), + check.limit(), check.slack()); + report_->reportLine(""); + } + } } void -Sta::reportCapacitanceLimitShort(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - const Corner *corner1; - const RiseFall *rf; - float capacitance, limit, slack; - check_capacitance_limits_->checkCapacitance(pin, corner, min_max, - corner1, rf, capacitance, - limit, slack); - report_path_->reportLimitShort(report_path_->fieldCapacitance(), - pin, capacitance, limit, slack); -} - -void -Sta::reportCapacitanceLimitVerbose(Pin *pin, - const Corner *corner, - const MinMax *min_max) +Sta::checkCapacitancesPreamble(const SceneSeq &scenes) { - const Corner *corner1; - const RiseFall *rf1; - float capacitance1, limit1, slack1; - check_capacitance_limits_->checkCapacitance(pin, corner, min_max, - corner1, rf1, capacitance1, - limit1, slack1); - report_path_->reportLimitVerbose(report_path_->fieldCapacitance(), - pin, rf1, capacitance1, - limit1, slack1, corner1, min_max); + ensureLevelized(); + if (check_capacitances_ == nullptr) + makeCheckCapacitances(); + for (Mode *mode : Scene::modes(scenes)) { + mode->sim()->ensureConstantsPropagated(); + mode->clkNetwork()->ensureClkNetwork(); + } } void Sta::checkCapacitance(const Pin *pin, - const Corner *corner, + const SceneSeq &scenes, const MinMax *min_max, // Return values. - const Corner *&corner1, - const RiseFall *&rf, float &capacitance, float &limit, - float &slack) + float &slack, + const RiseFall *&rf, + const Scene *&scene) +{ + CapacitanceCheck check = check_capacitances_->check(pin, scenes, min_max); + pin = check.pin(); + capacitance = check.capacitance(); + limit = check.limit(); + slack = check.slack(); + rf = check.rf(); + scene = check.scene(); +} + +size_t +Sta::maxCapacitanceViolationCount() { - check_capacitance_limits_->checkCapacitance(pin, corner, min_max, - corner1, rf, capacitance, - limit, slack); + checkCapacitancesPreamble(scenes_); + return check_capacitances_->check(nullptr, 1, true, scenes_, + MinMax::max()).size(); } void @@ -5545,131 +5812,70 @@ Sta::maxCapacitanceCheck(// Return values. float &slack, float &limit) { - checkCapacitanceLimitPreamble(); - PinSeq pins = check_capacitance_limits_->checkCapacitanceLimits(nullptr, false, - nullptr, + checkCapacitancesPreamble(scenes_); + CapacitanceCheckSeq &checks = check_capacitances_->check(nullptr, 1, false, + scenes_, MinMax::max()); pin = nullptr; capacitance = 0.0; slack = INF; limit = INF; - if (!pins.empty()) { - pin = pins[0]; - const Corner *corner; - const RiseFall *rf; - check_capacitance_limits_->checkCapacitance(pin, nullptr, MinMax::max(), - corner, rf, capacitance, limit, slack); + if (!checks.empty()) { + const CapacitanceCheck &check = checks[0]; + pin = check.pin(); + capacitance = check.capacitance(); + limit = check.limit(); + slack = check.slack(); } } //////////////////////////////////////////////////////////////// void -Sta::minPulseWidthPreamble() +Sta::reportMinPulseWidthChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes) { ensureClkArrivals(); if (check_min_pulse_widths_ == nullptr) makeCheckMinPulseWidths(); -} - -MinPulseWidthCheckSeq & -Sta::minPulseWidthChecks(PinSeq *pins, - const Corner *corner) -{ - minPulseWidthPreamble(); - return check_min_pulse_widths_->check(pins, corner); -} - -MinPulseWidthCheckSeq & -Sta::minPulseWidthChecks(const Corner *corner) -{ - minPulseWidthPreamble(); - return check_min_pulse_widths_->check(corner); -} - -MinPulseWidthCheckSeq & -Sta::minPulseWidthViolations(const Corner *corner) -{ - minPulseWidthPreamble(); - return check_min_pulse_widths_->violations(corner); -} - -MinPulseWidthCheck * -Sta::minPulseWidthSlack(const Corner *corner) -{ - minPulseWidthPreamble(); - return check_min_pulse_widths_->minSlackCheck(corner); -} - -void -Sta::reportMpwChecks(MinPulseWidthCheckSeq *checks, - bool verbose) -{ + MinPulseWidthCheckSeq &checks = + check_min_pulse_widths_->check(net, max_count, violators, scenes); report_path_->reportMpwChecks(checks, verbose); } -void -Sta::reportMpwCheck(MinPulseWidthCheck *check, - bool verbose) -{ - report_path_->reportMpwCheck(check, verbose); -} - //////////////////////////////////////////////////////////////// -MinPeriodCheckSeq & -Sta::minPeriodViolations() -{ - minPeriodPreamble(); - const Corner *corner = cmdCorner(); - return check_min_periods_->violations(corner); -} - -MinPeriodCheck * -Sta::minPeriodSlack() -{ - minPeriodPreamble(); - const Corner *corner = cmdCorner(); - return check_min_periods_->minSlackCheck(corner); -} - void -Sta::minPeriodPreamble() +Sta::reportMinPeriodChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes) { // Need clk arrivals to know what clks arrive at the clk tree endpoints. ensureClkArrivals(); if (check_min_periods_ == nullptr) makeCheckMinPeriods(); -} - -void -Sta::reportChecks(MinPeriodCheckSeq *checks, - bool verbose) -{ + MinPeriodCheckSeq &checks = check_min_periods_->check(net, max_count, + violators, scenes); report_path_->reportChecks(checks, verbose); } -void -Sta::reportCheck(MinPeriodCheck *check, - bool verbose) -{ - report_path_->reportCheck(check, verbose); -} - //////////////////////////////////////////////////////////////// -MaxSkewCheckSeq & -Sta::maxSkewViolations() -{ - maxSkewPreamble(); - return check_max_skews_->violations(); -} - -MaxSkewCheck * -Sta::maxSkewSlack() +void +Sta::reportMaxSkewChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes) { maxSkewPreamble(); - return check_max_skews_->minSlackCheck(); + MaxSkewCheckSeq &checks = check_max_skews_->check(net, max_count, violators, scenes); + report_path_->reportChecks(checks, verbose); } void @@ -5680,20 +5886,6 @@ Sta::maxSkewPreamble() makeCheckMaxSkews(); } -void -Sta::reportChecks(MaxSkewCheckSeq *checks, - bool verbose) -{ - report_path_->reportChecks(checks, verbose); -} - -void -Sta::reportCheck(MaxSkewCheck *check, - bool verbose) -{ - report_path_->reportCheck(check, verbose); -} - //////////////////////////////////////////////////////////////// void @@ -5719,29 +5911,77 @@ void Sta::writeTimingModel(const char *lib_name, const char *cell_name, const char *filename, - const Corner *corner) + const Scene *scene) { ensureLibLinked(); ensureGraph(); LibertyLibrary *library = makeTimingModel(lib_name, cell_name, filename, - corner, this); + scene, this); writeLiberty(library, filename, this); } //////////////////////////////////////////////////////////////// void -Sta::powerPreamble() +Sta::reportPowerDesign(const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportDesign(scene, digits); +} + +void +Sta::reportPowerInsts(const InstanceSeq &insts, + const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportInsts(insts, scene, digits); +} + +void +Sta::reportPowerHighestInsts(size_t count, + const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportHighestInsts(count, scene, digits); +} + +void +Sta::reportPowerDesignJson(const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportDesignJson(scene, digits); +} + +void +Sta::reportPowerInstsJson(const InstanceSeq &insts, + const Scene *scene, + int digits) +{ + powerPreamble(scene); + power_->reportInstsJson(insts, scene, digits); +} + +void +Sta::powerPreamble(const Scene *scene) { ensureLibLinked(); // Use arrivals to find clocking info. searchPreamble(); search_->findAllArrivals(); - ensureClkNetwork(); + if (scene) + scene->mode()->clkNetwork()->ensureClkNetwork(); + else { + for (Mode *mode : modes_) + mode->clkNetwork()->ensureClkNetwork(); + } } void -Sta::power(const Corner *corner, +Sta::power(const Scene *scene, // Return values. PowerResult &total, PowerResult &sequential, @@ -5750,23 +5990,24 @@ Sta::power(const Corner *corner, PowerResult ¯o, PowerResult &pad) { - powerPreamble(); - power_->power(corner, total, sequential, combinational, clock, macro, pad); + powerPreamble(scene); + power_->power(scene, total, sequential, combinational, clock, macro, pad); } PowerResult Sta::power(const Instance *inst, - const Corner *corner) + const Scene *scene) { - powerPreamble(); - return power_->power(inst, corner); + powerPreamble(scene); + return power_->power(inst, scene); } PwrActivity -Sta::activity(const Pin *pin) +Sta::activity(const Pin *pin, + const Scene *scene) { - powerPreamble(); - return power_->pinActivity(pin); + powerPreamble(scene); + return power_->pinActivity(pin, scene); } //////////////////////////////////////////////////////////////// @@ -5790,46 +6031,51 @@ Sta::writePathSpice(Path *path, //////////////////////////////////////////////////////////////// void -Sta::ensureClkNetwork() +Sta::ensureClkNetwork(const Mode *mode) { ensureLevelized(); - clk_network_->ensureClkNetwork(); + mode->clkNetwork()->ensureClkNetwork(); } bool -Sta::isClock(const Pin *pin) const +Sta::isClock(const Pin *pin, + const Mode *mode) const { - return clk_network_->isClock(pin); + return mode->clkNetwork()->isClock(pin); } bool -Sta::isClock(const Net *net) const +Sta::isClock(const Net *net, + const Mode *mode) const { - return clk_network_->isClock(net); + return mode->clkNetwork()->isClock(net); } bool -Sta::isIdealClock(const Pin *pin) const +Sta::isIdealClock(const Pin *pin, + const Mode *mode) const { - return clk_network_->isIdealClock(pin); + return mode->clkNetwork()->isIdealClock(pin); } bool -Sta::isPropagatedClock(const Pin *pin) const +Sta::isPropagatedClock(const Pin *pin, + const Mode *mode) const { - return clk_network_->isPropagatedClock(pin); + return mode->clkNetwork()->isPropagatedClock(pin); } const PinSet * -Sta::pins(const Clock *clk) +Sta::pins(const Clock *clk, + const Mode *mode) { - return clk_network_->pins(clk); + return mode->clkNetwork()->pins(clk); } void -Sta::clkPinsInvalid() +Sta::clkPinsInvalid(const Mode *mode) { - clk_network_->clkPinsInvalid(); + mode->clkNetwork()->clkPinsInvalid(); } } // namespace diff --git a/search/StaState.cc b/search/StaState.cc index c9eb7438b..fca517929 100644 --- a/search/StaState.cc +++ b/search/StaState.cc @@ -26,6 +26,7 @@ #include +#include "ContainerHelpers.hh" #include "DispatchQueue.hh" #include "Units.hh" #include "Network.hh" @@ -33,6 +34,8 @@ #include "Sdc.hh" #include "Graph.hh" #include "TimingArc.hh" +#include "Mode.hh" +#include "Scene.hh" namespace sta { @@ -41,17 +44,12 @@ StaState::StaState() : debug_(nullptr), units_(nullptr), network_(nullptr), - sdc_(nullptr), - corners_(nullptr), graph_(nullptr), levelize_(nullptr), - parasitics_(nullptr), arc_delay_calc_(nullptr), graph_delay_calc_(nullptr), - sim_(nullptr), search_(nullptr), latches_(nullptr), - clk_network_(nullptr), variables_(nullptr), thread_count_(1), dispatch_queue_(nullptr), @@ -113,17 +111,60 @@ StaState::setDebug(Debug *debug) } bool -StaState::crprActive() const +StaState::crprActive(const Mode *mode) const { - return sdc_->analysisType() == AnalysisType::ocv + return mode->sdc()->analysisType() == AnalysisType::ocv && variables_->crprEnabled(); } bool -StaState::isDisabledCondDefault(Edge *edge) const +StaState::isDisabledCondDefault(const Edge *edge) const { return !variables_->condDefaultArcsEnabled() && edge->timingArcSet()->isCondDefault(); } +//////////////////////////////////////////////////////////////// + +size_t +StaState::scenePathCount() const +{ + return scenes_.size() * MinMax::index_count; +} + +// The clock insertion delay (source latency) required for setup and +// hold checks is: +// +// hold check +// report_timing -delay_type min +// path insertion pll_delay +// src clk min early max +// tgt clk max late min +// +// setup check +// report_timing -delay_type max +// path insertion pll_delay +// src clk max late min +// tgt clk min early max +// +// For analysis type single or bc_wc only one path is required, but as +// shown above both early and late insertion delays are required. +// To find propagated generated clock insertion delays both early and +// late clock network paths are required. Thus, analysis type single +// makes min and max analysis points. +// Only one of them is enabled to "report paths". + +DcalcAPIndex +StaState::dcalcAnalysisPtCount() const +{ + return MinMax::index_count * scenes_.size(); +} + +const SceneSet +StaState::scenesSet() +{ + return Scene::sceneSet(scenes_); +} + + } // namespace diff --git a/search/Tag.cc b/search/Tag.cc index 27badadcc..5e368d809 100644 --- a/search/Tag.cc +++ b/search/Tag.cc @@ -31,23 +31,23 @@ #include "ExceptionPath.hh" #include "Sdc.hh" #include "Graph.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Search.hh" -#include "PathAnalysisPt.hh" #include "ClkInfo.hh" namespace sta { -Tag::Tag(TagIndex index, - int rf_index, - PathAPIndex path_ap_index, - const ClkInfo *clk_info, - bool is_clk, - InputDelay *input_delay, - bool is_segment_start, - ExceptionStateSet *states, - bool own_states, - const StaState *sta) : +Tag::Tag(Scene *scene, + TagIndex index, + const RiseFall *rf, + const MinMax *min_max, + const ClkInfo *clk_info, + bool is_clk, + InputDelay *input_delay, + bool is_segment_start, + ExceptionStateSet *states, + bool own_states) : + scene_(scene), clk_info_(clk_info), input_delay_(input_delay), states_(states), @@ -57,18 +57,18 @@ Tag::Tag(TagIndex index, is_loop_(false), is_segment_start_(is_segment_start), own_states_(own_states), - rf_index_(rf_index), - path_ap_index_(path_ap_index) + rf_index_(rf->index()), + min_max_index_(min_max->index()) { findHash(); if (states_) { - FilterPath *filter = sta->search()->filter(); + FilterPath *filter = scene_->sdc()->filter(); for (ExceptionState *state : *states_) { ExceptionPath *exception = state->exception(); if (exception->isLoop()) - is_loop_ = true; + is_loop_ = true; if (exception == filter) - is_filter_ = true; + is_filter_ = true; } } } @@ -91,24 +91,25 @@ Tag::to_string(bool report_index, const StaState *sta) const { const Network *network = sta->network(); - const Corners *corners = sta->corners(); std::string result; - if (report_index) + if (report_index) { result += std::to_string(index_); + result += " "; + } + + result += scene_->name(); + result += " "; if (report_rf_min_max) { const RiseFall *rf = transition(); - PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index_); + const MinMax *min_max = minMax(); + result += rf->to_string(); result += " "; - result += rf->to_string().c_str(); + result += min_max->to_string(); result += " "; - result += path_ap->pathMinMax()->to_string(); - result += "/"; - result += std::to_string(path_ap_index_); } - result += " "; const ClockEdge *clk_edge = clkEdge(); if (clk_edge) result += clk_edge->name(); @@ -121,11 +122,11 @@ Tag::to_string(bool report_index, if (is_clk_) { result += "clock"; if (clk_info_->isPropagated()) - result += " prop"; + result += " prop"; else - result += " ideal"; + result += " ideal"; if (is_genclk_src) - result += " "; + result += " "; } if (clk_info_->isGenClkSrcPath()) result += "genclk"; @@ -162,13 +163,13 @@ Tag::to_string(bool report_index, result += " "; result += exception->asString(network); if (state->nextThru()) { - result += " (next thru "; - result += state->nextThru()->asString(network); - result += ")"; + result += " (next thru "; + result += state->nextThru()->asString(network); + result += ")"; } else { - if (exception->thrus() != nullptr) - result += " (thrus complete)"; + if (exception->thrus() != nullptr) + result += " (thrus complete)"; } } } @@ -182,16 +183,9 @@ Tag::transition() const } const MinMax * -Tag::minMax(const StaState *sta) const -{ - return pathAnalysisPt(sta)->pathMinMax(); -} - -PathAnalysisPt * -Tag::pathAnalysisPt(const StaState *sta) const +Tag::minMax() const { - const Corners *corners = sta->corners(); - return corners->findPathAnalysisPt(path_ap_index_); + return MinMax::find(min_max_index_); } void @@ -225,24 +219,24 @@ Tag::isGenClkSrcPath() const } const Clock * -Tag::genClkSrcPathClk(const StaState *sta) const +Tag::genClkSrcPathClk() const { if (clk_info_->isGenClkSrcPath() && states_) { - FilterPath *filter = sta->search()->filter(); + FilterPath *filter = scene_->sdc()->filter(); for (ExceptionState *state : *states_) { ExceptionPath *except = state->exception(); if (except->isFilter() - && except != filter) { - ExceptionTo *to = except->to(); - if (to) { - ClockSet *clks = to->clks(); - if (clks && clks->size() == 1) { - ClockSet::Iterator clk_iter(clks); - const Clock *clk = clk_iter.next(); - return clk; - } - } + && except != filter) { + ExceptionTo *to = except->to(); + if (to) { + ClockSet *clks = to->clks(); + if (clks && clks->size() == 1) { + ClockSet::iterator clk_iter = clks->begin(); + const Clock *clk = *clk_iter; + return clk; + } + } } } } @@ -254,8 +248,9 @@ Tag::findHash() { // Common to hash_ and match_hash_. hash_ = hash_init_value; + hashIncr(hash_, scene_->index()); hashIncr(hash_, rf_index_); - hashIncr(hash_, path_ap_index_); + hashIncr(hash_, min_max_index_); hashIncr(hash_, is_clk_); hashIncr(hash_, is_segment_start_); if (states_) { @@ -275,7 +270,7 @@ Tag::findHash() size_t Tag::hash(bool match_crpr_clk_pin, - const StaState *sta) const + const StaState *sta) const { if (match_crpr_clk_pin) return hashSum(hash_, clk_info_->crprClkVertexId(sta)); @@ -302,32 +297,32 @@ TagLess::TagLess(const StaState *sta) : bool TagLess::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return Tag::cmp(tag1, tag2, sta_) < 0; } int Tag::cmp(const Tag *tag1, - const Tag *tag2, - const StaState *sta) + const Tag *tag2, + const StaState *sta) { if (tag1 == tag2) return 0; + size_t scene_index1 = tag1->scene()->index(); + size_t scene_index2 = tag2->scene()->index(); + if (scene_index1 < scene_index2) + return -1; + else if (scene_index1 > scene_index2) + return 1; + const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); int clk_cmp = ClkInfo::cmp(clk_info1, clk_info2, sta); if (clk_cmp != 0) return clk_cmp; - PathAPIndex path_ap_index1 = tag1->pathAPIndex(); - PathAPIndex path_ap_index2 = tag2->pathAPIndex(); - if (path_ap_index1 < path_ap_index2) - return -1; - if (path_ap_index1 > path_ap_index2) - return 1; - int rf_index1 = tag1->rfIndex(); int rf_index2 = tag2->rfIndex(); if (rf_index1 < rf_index2) @@ -335,6 +330,13 @@ Tag::cmp(const Tag *tag1, if (rf_index1 > rf_index2) return 1; + int mm_index1 = tag1->minMaxIndex(); + int mm_index2 = tag2->minMaxIndex(); + if (mm_index1 < mm_index2) + return -1; + if (mm_index1 > mm_index2) + return 1; + bool is_clk1 = tag1->isClock(); bool is_clk2 = tag2->isClock(); if (!is_clk1 && is_clk2) @@ -363,8 +365,8 @@ Tag::cmp(const Tag *tag1, bool Tag::equal(const Tag *tag1, - const Tag *tag2, - const StaState *sta) + const Tag *tag2, + const StaState *sta) { return cmp(tag1, tag2, sta) == 0; } @@ -373,7 +375,7 @@ Tag::equal(const Tag *tag1, bool TagIndexLess::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return tag1->index() < tag2->index(); } @@ -381,7 +383,7 @@ TagIndexLess::operator()(const Tag *tag1, //////////////////////////////////////////////////////////////// TagMatchLess::TagMatchLess(bool match_crpr_clk_pin, - const StaState *sta) : + const StaState *sta) : match_crpr_clk_pin_(match_crpr_clk_pin), sta_(sta) { @@ -389,7 +391,7 @@ TagMatchLess::TagMatchLess(bool match_crpr_clk_pin, bool TagMatchLess::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return Tag::matchCmp(tag1, tag2, match_crpr_clk_pin_, sta_) < 0; } @@ -398,30 +400,37 @@ TagMatchLess::operator()(const Tag *tag1, bool Tag::match(const Tag *tag1, - const Tag *tag2, - const StaState *sta) + const Tag *tag2, + const StaState *sta) { return Tag::matchCmp(tag1, tag2, true, sta) == 0; } bool Tag::match(const Tag *tag1, - const Tag *tag2, - bool match_crpr_clk_pin, - const StaState *sta) + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta) { return Tag::matchCmp(tag1, tag2, match_crpr_clk_pin, sta) == 0; } int Tag::matchCmp(const Tag *tag1, - const Tag *tag2, - bool match_crpr_clk_pin, - const StaState *sta) + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta) { if (tag1 == tag2) return 0; + size_t scene_index1 = tag1->scene()->index(); + size_t scene_index2 = tag2->scene()->index(); + if (scene_index1 < scene_index2) + return -1; + else if (scene_index1 > scene_index2) + return 1; + int rf_index1 = tag1->rfIndex(); int rf_index2 = tag2->rfIndex(); if (rf_index1 < rf_index2) @@ -429,11 +438,11 @@ Tag::matchCmp(const Tag *tag1, if (rf_index1 > rf_index2) return 1; - PathAPIndex path_ap_index1 = tag1->pathAPIndex(); - PathAPIndex path_ap_index2 = tag2->pathAPIndex(); - if (path_ap_index1 < path_ap_index2) + int mm_index1 = tag1->minMaxIndex(); + int mm_index2 = tag2->minMaxIndex(); + if (mm_index1 < mm_index2) return -1; - if (path_ap_index1 > path_ap_index2) + if (mm_index1 > mm_index2) return 1; const ClkInfo *clk_info1 = tag1->clkInfo(); @@ -468,8 +477,7 @@ Tag::matchCmp(const Tag *tag1, if (is_segment_start1 && !is_segment_start2) return 1; - if (match_crpr_clk_pin - && sta->crprActive()) { + if (match_crpr_clk_pin) { VertexId crpr_vertex1 = clk_info1->crprClkVertexId(sta); VertexId crpr_vertex2 = clk_info2->crprClkVertexId(sta); if (crpr_vertex1 < crpr_vertex2) @@ -483,54 +491,59 @@ Tag::matchCmp(const Tag *tag1, bool Tag::matchNoCrpr(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); return tag1 == tag2 - || (clk_info1->clkEdge() == clk_info2->clkEdge() - && tag1->rfIndex() == tag2->rfIndex() - && tag1->pathAPIndex() == tag2->pathAPIndex() - && tag1->isClock() == tag2->isClock() - && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() - && Tag::stateEqual(tag1, tag2)); + || (tag1->scene() == tag2->scene() + && clk_info1->clkEdge() == clk_info2->clkEdge() + && tag1->rfIndex() == tag2->rfIndex() + && tag1->minMaxIndex() == tag2->minMaxIndex() + && tag1->isClock() == tag2->isClock() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && Tag::stateEqual(tag1, tag2)); } bool Tag::matchNoPathAp(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); return tag1 == tag2 - || (clk_info1->clkEdge() == clk_info2->clkEdge() - && tag1->rfIndex() == tag2->rfIndex() - && tag1->isClock() == tag2->isClock() - && tag1->isSegmentStart() == tag2->isSegmentStart() - && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() - && Tag::stateEqual(tag1, tag2)); + || (tag1->scene() == tag2->scene() + && clk_info1->clkEdge() == clk_info2->clkEdge() + && tag1->rfIndex() == tag2->rfIndex() + // min_max not matched + && tag1->isClock() == tag2->isClock() + && tag1->isSegmentStart() == tag2->isSegmentStart() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && Tag::stateEqual(tag1, tag2)); } bool Tag::matchCrpr(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); return tag1 == tag2 - || (clk_info1->clkEdge() == clk_info2->clkEdge() - && tag1->rfIndex() == tag2->rfIndex() - && tag1->isClock() == tag2->isClock() - && tag1->isSegmentStart() == tag2->isSegmentStart() - && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() - && stateEqualCrpr(tag1, tag2)); + || (tag1->scene() == tag2->scene() + && clk_info1->clkEdge() == clk_info2->clkEdge() + && tag1->rfIndex() == tag2->rfIndex() + // min_max not matched + && tag1->isClock() == tag2->isClock() + && tag1->isSegmentStart() == tag2->isSegmentStart() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && stateEqualCrpr(tag1, tag2)); } //////////////////////////////////////////////////////////////// int Tag::stateCmp(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { ExceptionStateSet *states1 = tag1->states(); ExceptionStateSet *states2 = tag2->states(); @@ -553,23 +566,24 @@ Tag::stateCmp(const Tag *tag1, if (state_size1 > state_size2) return 1; - ExceptionStateSet::Iterator state_iter1(states1); - ExceptionStateSet::Iterator state_iter2(states2); - while (state_iter1.hasNext() - && state_iter2.hasNext()) { - ExceptionState *state1 = state_iter1.next(); - ExceptionState *state2 = state_iter2.next(); - if (exceptionStateLess(state1, state2)) - return -1; - if (exceptionStateLess(state2, state1)) - return 1; + auto iter1 = states1->begin(); + auto iter2 = states2->begin(); + while (iter1 != states1->end() + && iter2 != states2->end()) { + ExceptionState *state1 = *iter1; + ExceptionState *state2 = *iter2; + int state_cmp = exceptionStateCmp(state1, state2); + if (state_cmp != 0) + return state_cmp; + ++iter1; + ++iter2; } return 0; } bool Tag::stateEqual(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { return stateCmp(tag1, tag2) == 0; } @@ -577,37 +591,43 @@ Tag::stateEqual(const Tag *tag1, // Match loop exception states only for crpr min/max paths. bool Tag::stateEqualCrpr(const Tag *tag1, - const Tag *tag2) + const Tag *tag2) { ExceptionStateSet *states1 = tag1->states(); ExceptionStateSet *states2 = tag2->states(); - ExceptionStateSet::Iterator state_iter1(states1); - ExceptionStateSet::Iterator state_iter2(states2); - ExceptionState *state1, *state2; - do { - state1 = nullptr; - while (state_iter1.hasNext()) { - state1 = state_iter1.next(); - ExceptionPath *exception1 = state1->exception(); - if (exception1->isLoop()) - break; - else - state1 = nullptr; - } - state2 = nullptr; - while (state_iter2.hasNext()) { - state2 = state_iter2.next(); - ExceptionPath *exception2 = state2->exception(); - if (exception2->isLoop()) - break; - else - state2 = nullptr; - } - if (state1 != state2) - return false; - } while (state1 && state2); - return state1 == nullptr - && state2 == nullptr; + if (states1 && states2) { + auto iter1 = states1->begin(); + auto iter2 = states2->begin(); + ExceptionState *state1, *state2; + do { + state1 = nullptr; + while (iter1 != states1->end()) { + state1 = *iter1; + ++iter1; + ExceptionPath *exception1 = state1->exception(); + if (exception1->isLoop()) + break; + else + state1 = nullptr; + } + state2 = nullptr; + while (iter2 != states2->end()) { + state2 = *iter2; + ++iter2; + ExceptionPath *exception2 = state2->exception(); + if (exception2->isLoop()) + break; + else + state2 = nullptr; + } + if (state1 != state2) + return false; + } while (state1 && state2); + return state1 == nullptr + && state2 == nullptr; + } + else + return true; } //////////////////////////////////////////////////////////////// @@ -620,8 +640,7 @@ TagHash::TagHash(const StaState *sta) : size_t TagHash::operator()(const Tag *tag) const { - bool crpr_on = sta_->crprActive(); - return tag->matchHash(crpr_on, sta_); + return tag->matchHash(true, sta_); } TagEqual::TagEqual(const StaState *sta) : @@ -631,13 +650,13 @@ TagEqual::TagEqual(const StaState *sta) : bool TagEqual::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return Tag::equal(tag1, tag2, sta_); } TagMatchHash::TagMatchHash(bool match_crpr_clk_pin, - const StaState *sta) : + const StaState *sta) : match_crpr_clk_pin_(match_crpr_clk_pin), sta_(sta) { @@ -650,7 +669,7 @@ TagMatchHash::operator()(const Tag *tag) const } TagMatchEqual::TagMatchEqual(bool match_crpr_clk_pin, - const StaState *sta) : + const StaState *sta) : match_crpr_clk_pin_(match_crpr_clk_pin), sta_(sta) { @@ -658,7 +677,7 @@ TagMatchEqual::TagMatchEqual(bool match_crpr_clk_pin, bool TagMatchEqual::operator()(const Tag *tag1, - const Tag *tag2) const + const Tag *tag2) const { return Tag::match(tag1, tag2, match_crpr_clk_pin_, sta_); } diff --git a/search/Tag.hh b/search/Tag.hh index e084bd29f..1b24c1900 100644 --- a/search/Tag.hh +++ b/search/Tag.hh @@ -24,12 +24,11 @@ #pragma once -#include "Vector.hh" -#include "Set.hh" #include "Transition.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "Path.hh" +#include "Scene.hh" namespace sta { @@ -51,21 +50,22 @@ namespace sta { class Tag { public: - Tag(TagIndex index, - int rf_index, - PathAPIndex path_ap_index, + Tag(Scene *scene, + TagIndex index, + const RiseFall *rf, + const MinMax *min_max, const ClkInfo *clk_info, bool is_clk, InputDelay *input_delay, bool is_segment_start, ExceptionStateSet *states, - bool own_states, - const StaState *sta); + bool own_states); ~Tag(); std::string to_string(const StaState *sta) const; std::string to_string(bool report_index, bool report_rf_min_max, const StaState *sta) const; + Scene *scene() const { return scene_; } const ClkInfo *clkInfo() const { return clk_info_; } bool isClock() const { return is_clk_; } const ClockEdge *clkEdge() const; @@ -73,60 +73,60 @@ public: const Pin *clkSrc() const; int rfIndex() const { return rf_index_; } const RiseFall *transition() const; - const MinMax *minMax(const StaState *sta) const; - PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; - PathAPIndex pathAPIndex() const { return path_ap_index_; } + const MinMax *minMax() const; + int minMaxIndex() const { return min_max_index_; } TagIndex index() const { return index_; } ExceptionStateSet *states() const { return states_; } void setStates(ExceptionStateSet *states); bool isGenClkSrcPath() const; - const Clock *genClkSrcPathClk(const StaState *sta) const; + const Clock *genClkSrcPathClk() const; // Input delay at search startpoint (not propagated). InputDelay *inputDelay() const { return input_delay_; } bool isLoop() const { return is_loop_; } bool isFilter() const { return is_filter_; } bool isSegmentStart() const { return is_segment_start_; } size_t hash(bool match_crpr_clk_pin, - const StaState *sta) const; + const StaState *sta) const; size_t matchHash(bool match_crpr_clk_pin, const StaState *sta) const; static int cmp(const Tag *tag1, - const Tag *tag2, - const StaState *sta); + const Tag *tag2, + const StaState *sta); static int matchCmp(const Tag *tag1, - const Tag *tag2, - bool match_clk_clk_pin, - const StaState *sta); + const Tag *tag2, + bool match_clk_clk_pin, + const StaState *sta); static bool match(const Tag *tag1, - const Tag *tag2, - bool match_crpr_clk_pin, - const StaState *sta); + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta); static bool equal(const Tag *tag1, - const Tag *tag2, - const StaState *sta); + const Tag *tag2, + const StaState *sta); static bool matchNoPathAp(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); static bool matchCrpr(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); static bool matchNoCrpr(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); protected: void findHash(); // Match tag clock edge, clock driver and exception states but not clk info. static bool match(const Tag *tag1, - const Tag *tag2, - const StaState *sta); + const Tag *tag2, + const StaState *sta); static bool stateEqual(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); static int stateCmp(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); static bool stateEqualCrpr(const Tag *tag1, - const Tag *tag2); + const Tag *tag2); private: + Scene *scene_; const ClkInfo *clk_info_; InputDelay *input_delay_; ExceptionStateSet *states_; @@ -140,7 +140,7 @@ private: // Indicates that states_ is owned by the tag. bool own_states_:1; unsigned int rf_index_:RiseFall::index_bit_count; - unsigned int path_ap_index_:path_ap_index_bit_count; + unsigned int min_max_index_:MinMax::index_bit_count; }; class TagLess @@ -148,7 +148,7 @@ class TagLess public: TagLess(const StaState *sta); bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; private: const StaState *sta_; @@ -158,7 +158,7 @@ class TagIndexLess { public: bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; }; class TagHash @@ -176,7 +176,7 @@ class TagEqual public: TagEqual(const StaState *sta); bool operator()(const Tag *tag1, - const Tag *tag2) const; + const Tag *tag2) const; private: const StaState *sta_; diff --git a/search/TagGroup.cc b/search/TagGroup.cc index 2496ef11a..93e0251e2 100644 --- a/search/TagGroup.cc +++ b/search/TagGroup.cc @@ -27,22 +27,21 @@ #include "Report.hh" #include "Debug.hh" #include "Graph.hh" -#include "PathAnalysisPt.hh" #include "ClkInfo.hh" #include "Tag.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Search.hh" #include "Path.hh" namespace sta { TagGroup::TagGroup(TagGroupIndex index, - PathIndexMap *path_index_map, - bool has_clk_tag, - bool has_genclk_src_tag, - bool has_filter_tag, - bool has_loop_tag, - const StaState *sta) : + PathIndexMap *path_index_map, + bool has_clk_tag, + bool has_genclk_src_tag, + bool has_filter_tag, + bool has_loop_tag, + const StaState *sta) : path_index_map_(path_index_map), hash_(hash(path_index_map, sta)), ref_count_(0), @@ -56,7 +55,7 @@ TagGroup::TagGroup(TagGroupIndex index, } TagGroup::TagGroup(TagGroupBldr *tag_bldr, - const StaState *sta) : + const StaState *sta) : path_index_map_(&tag_bldr->pathIndexMap()), hash_(hash(path_index_map_, sta)), ref_count_(0), @@ -84,28 +83,30 @@ TagGroup::decrRefCount() size_t TagGroup::hash(PathIndexMap *path_index_map, - const StaState *sta) + const StaState *sta) { - bool crpr_on = sta->crprActive(); size_t hash = 0; for (auto const [tag, path_index] : *path_index_map) - hash += tag->hash(crpr_on, sta); + hash += tag->hash(true, sta); return hash; } bool TagGroup::hasTag(Tag *tag) const { - return path_index_map_->hasKey(tag); + return path_index_map_->contains(tag); } size_t TagGroup::pathIndex(Tag *tag) const { - size_t path_index = 0; + size_t path_index; bool exists; - pathIndex(tag, path_index, exists); - return path_index; + findKeyValue(path_index_map_, tag, path_index, exists); + if (exists) + return path_index; + else + return 0; } void @@ -113,7 +114,7 @@ TagGroup::pathIndex(Tag *tag, size_t &path_index, bool &exists) const { - path_index_map_->findKey(tag, path_index, exists); + findKeyValue(path_index_map_, tag, path_index, exists); } void @@ -145,8 +146,8 @@ pathIndexMapReport(const PathIndexMap *path_index_map, //////////////////////////////////////////////////////////////// TagGroupBldr::TagGroupBldr(bool match_crpr_clk_pin, - const StaState *sta) : - default_path_count_(sta->corners()->count() + const StaState *sta) : + default_path_count_(sta->scenes().size() * RiseFall::index_count * MinMax::index_count), path_index_map_(TagMatchLess(match_crpr_clk_pin, sta)), @@ -203,8 +204,7 @@ TagGroupBldr::tagMatchPath(Tag *tag, // Match is not necessarily equal to original tag because it // must only satisfy tagMatch. bool exists; - Tag *tag_match; - path_index_map_.findKey(tag, tag_match, path_index, exists); + findKeyValue(path_index_map_, tag, path_index, exists); if (exists) match = &paths_[path_index]; else { @@ -221,7 +221,7 @@ TagGroupBldr::arrival(size_t path_index) const void TagGroupBldr::setArrival(Tag *tag, - const Arrival &arrival) + const Arrival &arrival) { // Find matching group tag (not necessarily equal to original tag). Path *match; @@ -245,7 +245,7 @@ TagGroupBldr::setMatchPath(Path *match, if (tag_match != tag) { // Replace tag in arrival map. path_index_map_.erase(tag_match); - path_index_map_.insert(tag, path_index); + path_index_map_[tag] = path_index; } paths_[path_index].init(vertex_, tag, arrival, prev_path, prev_edge, prev_arc, sta_); @@ -263,7 +263,7 @@ TagGroupBldr::insertPath(Tag *tag, { size_t path_index = paths_.size(); - path_index_map_.insert(tag, path_index); + path_index_map_[tag] = path_index; paths_.emplace_back(vertex_, tag, arrival, prev_path, prev_edge, prev_arc, sta_); @@ -289,11 +289,11 @@ TagGroupBldr::insertPath(const Path &path) TagGroup * TagGroupBldr::makeTagGroup(TagGroupIndex index, - const StaState *sta) + const StaState *sta) { return new TagGroup(index, makePathIndexMap(sta), - has_clk_tag_, has_genclk_src_tag_, has_filter_tag_, - has_loop_tag_, sta); + has_clk_tag_, has_genclk_src_tag_, has_filter_tag_, + has_loop_tag_, sta); } @@ -301,10 +301,9 @@ PathIndexMap * TagGroupBldr::makePathIndexMap(const StaState *sta) { PathIndexMap *path_index_map = new PathIndexMap(TagMatchLess(true, sta)); - size_t path_index = 0; for (auto const [tag, path_index1] : path_index_map_) { - path_index_map->insert(tag, path_index); + (*path_index_map)[tag] = path_index; path_index++; } return path_index_map; @@ -339,12 +338,8 @@ pathIndexMapEqual(const PathIndexMap *path_index_map1, { if (path_index_map1->size() == path_index_map2->size()) { for (auto const [tag1, path_index1] : *path_index_map1) { - Tag *tag2; - size_t path_index2; - bool exists2; - path_index_map2->findKey(tag1, tag2, path_index2, exists2); - if (!exists2) - return false; + if (!path_index_map2->contains(tag1)) + return false; } return true; } @@ -354,11 +349,11 @@ pathIndexMapEqual(const PathIndexMap *path_index_map1, bool TagGroupEqual::operator()(const TagGroup *tag_group1, - const TagGroup *tag_group2) const + const TagGroup *tag_group2) const { return tag_group1 == tag_group2 || (tag_group1->hash() == tag_group2->hash() - && pathIndexMapEqual(tag_group1->pathIndexMap(), + && pathIndexMapEqual(tag_group1->pathIndexMap(), tag_group2->pathIndexMap())); } diff --git a/search/TagGroup.hh b/search/TagGroup.hh index 88722d5f7..2a02f2b97 100644 --- a/search/TagGroup.hh +++ b/search/TagGroup.hh @@ -26,9 +26,6 @@ #include -#include "Vector.hh" -#include "Map.hh" -#include "Iterator.hh" #include "MinMax.hh" #include "Transition.hh" #include "GraphClass.hh" @@ -43,15 +40,15 @@ class TagGroup { public: TagGroup(TagGroupIndex index, - PathIndexMap *path_index_map, - bool has_clk_tag, - bool has_genclk_src_tag, - bool has_filter_tag, - bool has_loop_tag, - const StaState *sta); + PathIndexMap *path_index_map, + bool has_clk_tag, + bool has_genclk_src_tag, + bool has_filter_tag, + bool has_loop_tag, + const StaState *sta); // For Search::findTagGroup to probe. TagGroup(TagGroupBldr *tag_bldr, - const StaState *sta); + const StaState *sta); ~TagGroup(); TagGroupIndex index() const { return index_; } size_t hash() const { return hash_; } @@ -75,7 +72,7 @@ public: protected: static size_t hash(PathIndexMap *path_index_map, - const StaState *sta); + const StaState *sta); // tag -> path index PathIndexMap *path_index_map_; @@ -99,7 +96,7 @@ class TagGroupEqual { public: bool operator()(const TagGroup *group1, - const TagGroup *group2) const; + const TagGroup *group2) const; }; // Incremental tag group used to build tag group and associated @@ -108,12 +105,12 @@ class TagGroupBldr { public: TagGroupBldr(bool match_crpr_clk_pin, - const StaState *sta); + const StaState *sta); void init(Vertex *vertex); bool empty(); void reportArrivalEntries() const; TagGroup *makeTagGroup(TagGroupIndex index, - const StaState *sta); + const StaState *sta); size_t pathCount() const { return path_index_map_.size();; } bool hasClkTag() const { return has_clk_tag_; } bool hasGenClkSrcTag() const { return has_genclk_src_tag_; } @@ -128,7 +125,7 @@ public: Arrival arrival(size_t path_index) const; // prev_path == hull void setArrival(Tag *tag, - const Arrival &arrival); + const Arrival &arrival); void setMatchPath(Path *match, size_t path_index, Tag *tag, diff --git a/search/VisitPathEnds.cc b/search/VisitPathEnds.cc index b5a762d54..528590103 100644 --- a/search/VisitPathEnds.cc +++ b/search/VisitPathEnds.cc @@ -31,15 +31,17 @@ #include "ExceptionPath.hh" #include "PortDelay.hh" #include "Sdc.hh" +#include "Mode.hh" #include "Graph.hh" #include "ClkInfo.hh" #include "Tag.hh" #include "Path.hh" -#include "PathAnalysisPt.hh" #include "PathEnd.hh" #include "Search.hh" #include "GatedClk.hh" +#include "Sim.hh" #include "Variables.hh" +#include "Scene.hh" namespace sta { @@ -50,17 +52,17 @@ VisitPathEnds::VisitPathEnds(const StaState *sta) : void VisitPathEnds::visitPathEnds(Vertex *vertex, - PathEndVisitor *visitor) + PathEndVisitor *visitor) { - visitPathEnds(vertex, nullptr, MinMaxAll::all(), false, visitor); + visitPathEnds(vertex, Scene::sceneSet(scenes_), MinMaxAll::all(), false, visitor); } void VisitPathEnds::visitPathEnds(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor) + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor) { // Ignore slack on bidirect driver vertex. The load vertex gets the slack. if (!vertex->isBidirectDriver()) { @@ -69,115 +71,115 @@ VisitPathEnds::visitPathEnds(Vertex *vertex, vertex->to_string(this).c_str()); visitor->vertexBegin(vertex); bool is_constrained = false; - visitClkedPathEnds(pin, vertex, corner, min_max, filtered, visitor, - is_constrained); + visitClkedPathEnds(pin, vertex, scenes, min_max, filtered, visitor, + is_constrained); if (search_->unconstrainedPaths() - && !is_constrained - && !vertex->isDisabledConstraint()) - visitUnconstrainedPathEnds(pin, vertex, corner, min_max, filtered, - visitor); + && !is_constrained) + visitUnconstrainedPathEnds(pin, vertex, scenes, min_max, filtered, + visitor); visitor->vertexEnd(vertex); } } void VisitPathEnds::visitClkedPathEnds(const Pin *pin, - Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Vertex *vertex, + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { - bool is_segment_start = search_->isSegmentStart(pin); VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - const MinMax *path_min_max = path_ap->pathMinMax(); + const MinMax *path_min_max = path->minMax(this); const RiseFall *end_rf = path->transition(this); Tag *tag = path->tag(this); - if ((corner == nullptr - || path_ap->corner() == corner) - && min_max->matches(path_min_max) - // Ignore generated clock source paths. - && !path->clkInfo(this)->isGenClkSrcPath() - && !falsePathTo(path, pin, end_rf, path_min_max) - // Ignore segment startpoint paths. - && (!is_segment_start - || !tag->isSegmentStart())) { + const Mode *mode = path->mode(this); + const Sdc *sdc = mode->sdc(); + Scene *scene = path->scene(this); + if (scenes.contains(scene) + && min_max->matches(path_min_max) + // Ignore generated clock source paths. + && !path->clkInfo(this)->isGenClkSrcPath() + && !falsePathTo(path, pin, end_rf, path_min_max) + // Ignore segment startpoint paths. + && !tag->isSegmentStart()) { // set_output_delay to timing check has precedence. - if (sdc_->hasOutputDelay(pin)) - visitOutputDelayEnd(pin, path, end_rf, path_ap, filtered, visitor, - is_constrained); + if (sdc->hasOutputDelay(pin)) + visitOutputDelayEnd(pin, path, end_rf, filtered, visitor, + is_constrained); else if (vertex->hasChecks()) - visitCheckEnd(pin, vertex, path, end_rf, path_ap, filtered, visitor, - is_constrained); + visitCheckEnd(pin, vertex, path, end_rf, filtered, visitor, + is_constrained); else if (!filtered || search_->matchesFilter(path, nullptr)) { - PathDelay *path_delay = pathDelayTo(path, pin, end_rf, path_min_max); - if (path_delay) { - PathEndPathDelay path_end(path_delay, path, this); - visitor->visit(&path_end); - is_constrained = true; - } + PathDelay *path_delay = pathDelayTo(path, pin, end_rf, path_min_max); + if (path_delay) { + PathEndPathDelay path_end(path_delay, path, this); + visitor->visit(&path_end); + is_constrained = true; + } } if (variables_->gatedClkChecksEnabled()) - visitGatedClkEnd(pin, vertex, path, end_rf, path_ap, filtered, visitor, - is_constrained); - visitDataCheckEnd(pin, path, end_rf, path_ap, filtered, visitor, - is_constrained); + visitGatedClkEnd(pin, vertex, path, end_rf, filtered, visitor, + is_constrained); + visitDataCheckEnd(pin, path, end_rf, filtered, visitor, + is_constrained); } } } void VisitPathEnds::visitCheckEnd(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { const ClockEdge *src_clk_edge = path->clkEdge(this); const Clock *src_clk = path->clock(this); - const MinMax *min_max = path_ap->pathMinMax(); - const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); + const MinMax *min_max = path->minMax(this); + const MinMax *tgt_min_max = path->tgtClkMinMax(this); bool check_clked = false; + const Scene *scene = path->scene(this); + const Mode *mode = scene->mode(); + const Sdc *sdc = scene->sdc(); VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *tgt_clk_vertex = edge->from(graph_); const TimingRole *check_role = edge->role(); - if (checkEdgeEnabled(edge) - && check_role->pathMinMax() == min_max) { + if (checkEdgeEnabled(edge, mode) + && check_role->pathMinMax() == min_max) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *check_arc : arc_set->arcs()) { - const RiseFall *clk_rf = check_arc->fromEdge()->asRiseFall(); - if (check_arc->toEdge()->asRiseFall() == end_rf - && clk_rf) { - VertexPathIterator tgt_clk_path_iter(tgt_clk_vertex, clk_rf, - tgt_clk_path_ap, this); - while (tgt_clk_path_iter.hasNext()) { - Path *tgt_clk_path = tgt_clk_path_iter.next(); - const ClkInfo *tgt_clk_info = tgt_clk_path->clkInfo(this); - const ClockEdge *tgt_clk_edge = tgt_clk_path->clkEdge(this); - const Clock *tgt_clk = tgt_clk_path->clock(this); - const Pin *tgt_pin = tgt_clk_vertex->pin(); - ExceptionPath *exception = exceptionTo(path, pin, end_rf, - tgt_clk_edge, min_max); - // Ignore generated clock source paths. - if (!tgt_clk_info->isGenClkSrcPath() + const RiseFall *clk_rf = check_arc->fromEdge()->asRiseFall(); + if (check_arc->toEdge()->asRiseFall() == end_rf + && clk_rf) { + VertexPathIterator tgt_clk_path_iter(tgt_clk_vertex, scene, + tgt_min_max, clk_rf, this); + while (tgt_clk_path_iter.hasNext()) { + Path *tgt_clk_path = tgt_clk_path_iter.next(); + const ClkInfo *tgt_clk_info = tgt_clk_path->clkInfo(this); + const ClockEdge *tgt_clk_edge = tgt_clk_path->clkEdge(this); + const Clock *tgt_clk = tgt_clk_path->clock(this); + const Pin *tgt_pin = tgt_clk_vertex->pin(); + ExceptionPath *exception = exceptionTo(path, pin, end_rf, + tgt_clk_edge, min_max); + // Ignore generated clock source paths. + if (!tgt_clk_info->isGenClkSrcPath() && tgt_clk_path->isClock(this)) { check_clked = true; if (!filtered || search_->matchesFilter(path, tgt_clk_edge)) { if (src_clk_edge - && tgt_clk != sdc_->defaultArrivalClock() - && sdc_->sameClockGroup(src_clk, tgt_clk) - && !sdc_->clkStopPropagation(tgt_pin, tgt_clk) + && tgt_clk != sdc->defaultArrivalClock() + && sdc->sameClockGroup(src_clk, tgt_clk) + && !sdc->clkStopPropagation(tgt_pin, tgt_clk) // False paths and path delays override // paths. && (exception == nullptr @@ -203,8 +205,7 @@ VisitPathEnds::visitCheckEnd(const Pin *pin, else if (exception && exception->isPathDelay() && (src_clk == nullptr - || sdc_->sameClockGroup(src_clk, - tgt_clk))) { + || sdc->sameClockGroup(src_clk, tgt_clk))) { PathDelay *path_delay = dynamic_cast(exception); if (network_->isLatchData(pin) && check_role == TimingRole::setup()) { @@ -221,113 +222,117 @@ VisitPathEnds::visitCheckEnd(const Pin *pin, } } } - } - } - } + } + } + } } } } if (!check_clked) - visitCheckEndUnclked(pin, vertex, path, end_rf, path_ap, filtered, - visitor, is_constrained); + visitCheckEndUnclked(pin, vertex, path, end_rf, filtered, + visitor, is_constrained); } void VisitPathEnds::visitCheckEndUnclked(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { - const MinMax *min_max = path_ap->pathMinMax(); + const Mode *mode = path->mode(this); + const MinMax *min_max = path->minMax(this); VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); const TimingRole *check_role = edge->role(); - if (checkEdgeEnabled(edge) - && check_role->pathMinMax() == min_max) { + if (checkEdgeEnabled(edge, mode) + && check_role->pathMinMax() == min_max) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *check_arc : arc_set->arcs()) { - const RiseFall *clk_rf = check_arc->fromEdge()->asRiseFall(); - if (check_arc->toEdge()->asRiseFall() == end_rf - && clk_rf - && (!filtered - || search_->matchesFilter(path, nullptr))) { - ExceptionPath *exception = exceptionTo(path, pin, end_rf, - nullptr, min_max); - // False paths and path delays override multicycle paths. - if (exception - && exception->isPathDelay()) { - PathDelay *path_delay = dynamic_cast(exception); - PathEndPathDelay path_end(path_delay, path, nullptr, - check_arc, edge, this); - visitor->visit(&path_end); - is_constrained = true; - } - } + const RiseFall *clk_rf = check_arc->fromEdge()->asRiseFall(); + if (check_arc->toEdge()->asRiseFall() == end_rf + && clk_rf + && (!filtered + || search_->matchesFilter(path, nullptr))) { + ExceptionPath *exception = exceptionTo(path, pin, end_rf, + nullptr, min_max); + // False paths and path delays override multicycle paths. + if (exception + && exception->isPathDelay()) { + PathDelay *path_delay = dynamic_cast(exception); + PathEndPathDelay path_end(path_delay, path, nullptr, + check_arc, edge, this); + visitor->visit(&path_end); + is_constrained = true; + } + } } } } } bool -VisitPathEnds::checkEdgeEnabled(Edge *edge) const +VisitPathEnds::checkEdgeEnabled(const Edge *edge, + const Mode *mode) const { const TimingRole *check_role = edge->role(); + const Sdc *sdc = mode->sdc(); return check_role->isTimingCheck() - && search_->evalPred()->searchFrom(edge->from(graph_)) - && !edge->isDisabledConstraint() - && !edge->isDisabledCond() - && !sdc_->isDisabledCondDefault(edge) + && search_->evalPred()->searchFrom(edge->from(graph_), mode) + && !sdc->isDisabledConstraint(edge) + && !mode->sim()->isDisabledCond(edge) + && !isDisabledCondDefault(edge) && !((check_role == TimingRole::recovery() - || check_role == TimingRole::removal()) - && !variables_->recoveryRemovalChecksEnabled()); + || check_role == TimingRole::removal()) + && !variables_->recoveryRemovalChecksEnabled()); } void VisitPathEnds::visitOutputDelayEnd(const Pin *pin, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { - const MinMax *min_max = path_ap->pathMinMax(); - OutputDelaySet *output_delays = sdc_->outputDelaysLeafPin(pin); + const Scene *scene = path->scene(this); + const Sdc *sdc = scene->sdc(); + const MinMax *min_max = path->minMax(this); + OutputDelaySet *output_delays = sdc->outputDelaysLeafPin(pin); if (output_delays) { for (OutputDelay *output_delay : *output_delays) { float margin; bool exists; output_delay->delays()->value(end_rf, min_max, margin, exists); if (exists) { - const Pin *ref_pin = output_delay->refPin(); - const ClockEdge *tgt_clk_edge = output_delay->clkEdge(); - if (!filtered - || search_->matchesFilter(path, tgt_clk_edge)) { - if (ref_pin) { - Clock *tgt_clk = output_delay->clock(); - Vertex *ref_vertex = graph_->pinLoadVertex(ref_pin); - const RiseFall *ref_rf = output_delay->refTransition(); - VertexPathIterator ref_path_iter(ref_vertex,ref_rf,path_ap,this); - while (ref_path_iter.hasNext()) { - Path *ref_path = ref_path_iter.next(); - if (ref_path->isClock(this) - && (tgt_clk == nullptr - || ref_path->clock(this) == tgt_clk)) - visitOutputDelayEnd1(output_delay, pin, path, end_rf, - ref_path->clkEdge(this), ref_path, min_max, - visitor, is_constrained); - } - } - else - visitOutputDelayEnd1(output_delay, pin, path, end_rf, - tgt_clk_edge, nullptr, min_max, - visitor, is_constrained); - } + const Pin *ref_pin = output_delay->refPin(); + const ClockEdge *tgt_clk_edge = output_delay->clkEdge(); + if (!filtered + || search_->matchesFilter(path, tgt_clk_edge)) { + if (ref_pin) { + Clock *tgt_clk = output_delay->clock(); + Vertex *ref_vertex = graph_->pinLoadVertex(ref_pin); + const RiseFall *ref_rf = output_delay->refTransition(); + VertexPathIterator ref_path_iter(ref_vertex, scene, min_max, + ref_rf, this); + while (ref_path_iter.hasNext()) { + Path *ref_path = ref_path_iter.next(); + if (ref_path->isClock(this) + && (tgt_clk == nullptr + || ref_path->clock(this) == tgt_clk)) + visitOutputDelayEnd1(output_delay, pin, path, end_rf, + ref_path->clkEdge(this), ref_path, min_max, + visitor, is_constrained); + } + } + else + visitOutputDelayEnd1(output_delay, pin, path, end_rf, + tgt_clk_edge, nullptr, min_max, + visitor, is_constrained); + } } } } @@ -335,20 +340,21 @@ VisitPathEnds::visitOutputDelayEnd(const Pin *pin, void VisitPathEnds::visitOutputDelayEnd1(OutputDelay *output_delay, - const Pin *pin, - Path *path, - const RiseFall *end_rf, - const ClockEdge *tgt_clk_edge, - Path *ref_path, - const MinMax *min_max, - PathEndVisitor *visitor, - bool &is_constrained) + const Pin *pin, + Path *path, + const RiseFall *end_rf, + const ClockEdge *tgt_clk_edge, + Path *ref_path, + const MinMax *min_max, + PathEndVisitor *visitor, + bool &is_constrained) { // Target clk is not required for path delay, // but the exception may be -to clk. ExceptionPath *exception = exceptionTo(path, pin, end_rf, tgt_clk_edge, - min_max); + min_max); const ClockEdge *src_clk_edge = path->clkEdge(this); + const Sdc *sdc = path->sdc(this); if (exception && exception->isPathDelay()) { PathDelay *path_delay = dynamic_cast(exception); @@ -358,12 +364,12 @@ VisitPathEnds::visitOutputDelayEnd1(OutputDelay *output_delay, } else if (src_clk_edge && tgt_clk_edge - && sdc_->sameClockGroup(path->clock(this), tgt_clk_edge->clock()) - // False paths and path delays override. - && (exception == nullptr - || exception->isFilter() - || exception->isGroupPath() - || exception->isMultiCycle())) { + && sdc->sameClockGroup(path->clock(this), tgt_clk_edge->clock()) + // False paths and path delays override. + && (exception == nullptr + || exception->isFilter() + || exception->isGroupPath() + || exception->isMultiCycle())) { MultiCyclePath *mcp = dynamic_cast(exception); PathEndOutputDelay path_end(output_delay, path, ref_path, mcp, this); visitor->visit(&path_end); @@ -376,71 +382,77 @@ VisitPathEnds::visitOutputDelayEnd1(OutputDelay *output_delay, // Look for clock gating functions where path is the clock enable. void VisitPathEnds::visitGatedClkEnd(const Pin *pin, - Vertex *vertex, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Vertex *vertex, + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { + const Scene *scene = path->scene(this); + const Sdc *sdc = scene->sdc(); const ClockEdge *src_clk_edge = path->clkEdge(this); - if (src_clk_edge) { + if (src_clk_edge + && !path->isClock(this) + && !sdc->isDisableClockGatingCheck(pin) + && !sdc->isDisableClockGatingCheck(network_->instance(pin))) { + const Mode *mode = scene->mode(); GatedClk *gated_clk = search_->gatedClk(); Clock *src_clk = src_clk_edge->clock(); bool is_gated_clk_enable; const Pin *clk_pin; LogicValue logic_active_value; - gated_clk->isGatedClkEnable(vertex, - is_gated_clk_enable, clk_pin, logic_active_value); + gated_clk->isGatedClkEnable(vertex, mode, + is_gated_clk_enable, clk_pin, logic_active_value); if (is_gated_clk_enable) { - const PathAnalysisPt *clk_path_ap = path_ap->tgtClkAnalysisPt(); - const MinMax *min_max = path_ap->pathMinMax(); + const MinMax *min_max = path->minMax(this); + const MinMax *tgt_min_max = path->tgtClkMinMax(this); Vertex *clk_vertex = graph_->pinLoadVertex(clk_pin); LogicValue active_value = - sdc_->clockGatingActiveValue(clk_pin, pin); + sdc->clockGatingActiveValue(clk_pin, pin); const RiseFall *clk_rf = - // Clock active value specified by set_clock_gating_check - // overrides the library cell function active value. - gated_clk->gatedClkActiveTrans((active_value == LogicValue::unknown) ? - logic_active_value : active_value, - min_max); - VertexPathIterator clk_path_iter(clk_vertex, clk_rf, clk_path_ap, this); + // Clock active value specified by set_clock_gating_check + // overrides the library cell function active value. + gated_clk->gatedClkActiveTrans((active_value == LogicValue::unknown) ? + logic_active_value : active_value, + min_max); + VertexPathIterator clk_path_iter(clk_vertex, scene, tgt_min_max, + clk_rf, this); while (clk_path_iter.hasNext()) { - Path *clk_path = clk_path_iter.next(); - const ClockEdge *clk_edge = clk_path->clkEdge(this); - const Clock *clk = clk_edge ? clk_edge->clock() : nullptr; - if (clk_path->isClock(this) - // Ignore unclocked paths (from path delay constraints). - && clk_edge - && clk_edge != sdc_->defaultArrivalClockEdge() - // Ignore generated clock source paths. - && !path->clkInfo(this)->isGenClkSrcPath() - && !sdc_->clkStopPropagation(pin, clk) - && clk_vertex->hasDownstreamClkPin()) { - const TimingRole *check_role = (min_max == MinMax::max()) - ? TimingRole::gatedClockSetup() - : TimingRole::gatedClockHold(); - float margin = clockGatingMargin(clk, clk_pin, - pin, end_rf, min_max); - ExceptionPath *exception = exceptionTo(path, pin, end_rf, - clk_edge, min_max); - if (sdc_->sameClockGroup(src_clk, clk) - // False paths and path delays override. - && (exception == nullptr - || exception->isFilter() - || exception->isGroupPath() - || exception->isMultiCycle()) - && (!filtered - || search_->matchesFilter(path, clk_edge))) { - MultiCyclePath *mcp = - dynamic_cast(exception); - PathEndGatedClock path_end(path, clk_path, check_role, - mcp, margin, this); - visitor->visit(&path_end); - is_constrained = true; - } - } + Path *clk_path = clk_path_iter.next(); + const ClockEdge *clk_edge = clk_path->clkEdge(this); + const Clock *clk = clk_edge ? clk_edge->clock() : nullptr; + if (clk_path->isClock(this) + // Ignore unclocked paths (from path delay constraints). + && clk_edge + && clk_edge != sdc->defaultArrivalClockEdge() + // Ignore generated clock source paths. + && !path->clkInfo(this)->isGenClkSrcPath() + && !sdc->clkStopPropagation(pin, clk) + && clk_vertex->hasDownstreamClkPin()) { + const TimingRole *check_role = (min_max == MinMax::max()) + ? TimingRole::gatedClockSetup() + : TimingRole::gatedClockHold(); + float margin = clockGatingMargin(clk, clk_pin, pin, + end_rf, min_max, sdc); + ExceptionPath *exception = exceptionTo(path, pin, end_rf, + clk_edge, min_max); + if (sdc->sameClockGroup(src_clk, clk) + // False paths and path delays override. + && (exception == nullptr + || exception->isFilter() + || exception->isGroupPath() + || exception->isMultiCycle()) + && (!filtered + || search_->matchesFilter(path, clk_edge))) { + MultiCyclePath *mcp = + dynamic_cast(exception); + PathEndGatedClock path_end(path, clk_path, check_role, + mcp, margin, this); + visitor->visit(&path_end); + is_constrained = true; + } + } } } } @@ -450,32 +462,33 @@ VisitPathEnds::visitGatedClkEnd(const Pin *pin, // Look for margin from highest precedence level to lowest. float VisitPathEnds::clockGatingMargin(const Clock *clk, - const Pin *clk_pin, - const Pin *enable_pin, - const RiseFall *enable_rf, - const SetupHold *setup_hold) + const Pin *clk_pin, + const Pin *enable_pin, + const RiseFall *enable_rf, + const SetupHold *setup_hold, + const Sdc *sdc) { bool exists; float margin; - sdc_->clockGatingMarginEnablePin(enable_pin, enable_rf, - setup_hold, exists, margin); + sdc->clockGatingMarginEnablePin(enable_pin, enable_rf, + setup_hold, exists, margin); if (exists) return margin; Instance *inst = network_->instance(enable_pin); - sdc_->clockGatingMarginInstance(inst, enable_rf, setup_hold, - exists, margin); + sdc->clockGatingMarginInstance(inst, enable_rf, setup_hold, + exists, margin); if (exists) return margin; - sdc_->clockGatingMarginClkPin(clk_pin, enable_rf, setup_hold, - exists, margin); + sdc->clockGatingMarginClkPin(clk_pin, enable_rf, setup_hold, + exists, margin); if (exists) return margin; - sdc_->clockGatingMarginClk(clk, enable_rf, setup_hold, - exists, margin); + sdc->clockGatingMarginClk(clk, enable_rf, setup_hold, + exists, margin); if (exists) return margin; - sdc_->clockGatingMargin(enable_rf, setup_hold, - exists, margin); + sdc->clockGatingMargin(enable_rf, setup_hold, + exists, margin); if (exists) return margin; else @@ -486,34 +499,31 @@ VisitPathEnds::clockGatingMargin(const Clock *clk, void VisitPathEnds::visitDataCheckEnd(const Pin *pin, - Path *path, - const RiseFall *end_rf, - const PathAnalysisPt *path_ap, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + Path *path, + const RiseFall *end_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { const ClockEdge *src_clk_edge = path->clkEdge(this); if (src_clk_edge) { - DataCheckSet *checks = sdc_->dataChecksTo(pin); + const Sdc *sdc = path->sdc(this); + DataCheckSet *checks = sdc->dataChecksTo(pin); if (checks) { const Clock *src_clk = src_clk_edge->clock(); - const MinMax *min_max = path_ap->pathMinMax(); - const PathAnalysisPt *clk_ap = path_ap->tgtClkAnalysisPt(); - DataCheckSet::Iterator check_iter(checks); - while (check_iter.hasNext()) { - DataCheck *check = check_iter.next(); - const Pin *from_pin = check->from(); - Vertex *from_vertex = graph_->pinLoadVertex(from_pin); - for (auto from_rf : RiseFall::range()) { - float margin; - bool margin_exists; - check->margin(from_rf, end_rf, min_max, margin, margin_exists); - if (margin_exists) - visitDataCheckEnd1(check, pin, path, src_clk, end_rf, - min_max, clk_ap, from_pin, from_vertex, - from_rf, filtered, visitor, is_constrained); - } + const MinMax *min_max = path->minMax(this); + for (DataCheck *check : *checks) { + const Pin *from_pin = check->from(); + Vertex *from_vertex = graph_->pinLoadVertex(from_pin); + for (auto from_rf : RiseFall::range()) { + float margin; + bool margin_exists; + check->margin(from_rf, end_rf, min_max, margin, margin_exists); + if (margin_exists) + visitDataCheckEnd1(check, pin, path, src_clk, end_rf, + min_max, from_pin, from_vertex, + from_rf, filtered, visitor, is_constrained); + } } } } @@ -521,21 +531,24 @@ VisitPathEnds::visitDataCheckEnd(const Pin *pin, bool VisitPathEnds::visitDataCheckEnd1(DataCheck *check, - const Pin *pin, - Path *path, - const Clock *src_clk, - const RiseFall *end_rf, - const MinMax *min_max, - const PathAnalysisPt *clk_ap, - const Pin *from_pin, - Vertex *from_vertex, - const RiseFall *from_rf, - bool filtered, - PathEndVisitor *visitor, - bool &is_constrained) + const Pin *pin, + Path *path, + const Clock *src_clk, + const RiseFall *end_rf, + const MinMax *min_max, + const Pin *from_pin, + Vertex *from_vertex, + const RiseFall *from_rf, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) { bool found_from_path = false; - VertexPathIterator tgt_clk_path_iter(from_vertex,from_rf,clk_ap,this); + const Scene *scene = path->scene(this); + const Sdc *sdc = scene->sdc(); + const MinMax *tgt_min_max = path->tgtClkMinMax(this); + VertexPathIterator tgt_clk_path_iter(from_vertex, scene, tgt_min_max, + from_rf,this); while (tgt_clk_path_iter.hasNext()) { Path *tgt_clk_path = tgt_clk_path_iter.next(); const ClockEdge *tgt_clk_edge = tgt_clk_path->clkEdge(this); @@ -546,19 +559,19 @@ VisitPathEnds::visitDataCheckEnd1(DataCheck *check, const Clock *tgt_clk = tgt_clk_edge->clock(); ExceptionPath *exception = exceptionTo(path, pin, end_rf, tgt_clk_edge, min_max); - if (sdc_->sameClockGroup(src_clk, tgt_clk) - && !sdc_->clkStopPropagation(from_pin, tgt_clk) - // False paths and path delays override. - && (exception == 0 - || exception->isFilter() - || exception->isGroupPath() - || exception->isMultiCycle()) - && (!filtered - || search_->matchesFilter(path, tgt_clk_edge))) { - MultiCyclePath *mcp=dynamic_cast(exception); - PathEndDataCheck path_end(check, path, tgt_clk_path, mcp, this); - visitor->visit(&path_end); - is_constrained = true; + if (sdc->sameClockGroup(src_clk, tgt_clk) + && !sdc->clkStopPropagation(from_pin, tgt_clk) + // False paths and path delays override. + && (exception == 0 + || exception->isFilter() + || exception->isGroupPath() + || exception->isMultiCycle()) + && (!filtered + || search_->matchesFilter(path, tgt_clk_edge))) { + MultiCyclePath *mcp=dynamic_cast(exception); + PathEndDataCheck path_end(check, path, tgt_clk_path, mcp, this); + visitor->visit(&path_end); + is_constrained = true; } } } @@ -569,26 +582,27 @@ VisitPathEnds::visitDataCheckEnd1(DataCheck *check, void VisitPathEnds::visitUnconstrainedPathEnds(const Pin *pin, - Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - bool filtered, - PathEndVisitor *visitor) + Vertex *vertex, + const SceneSet &scenes, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor) { VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); - PathAnalysisPt *path_ap = path->pathAnalysisPt(this); - const MinMax *path_min_max = path_ap->pathMinMax(); - if ((corner == nullptr - || path_ap->corner() == corner) - && min_max->matches(path_min_max) - // Ignore generated clock source paths. - && !path->clkInfo(this)->isGenClkSrcPath() - && (!filtered - || search_->matchesFilter(path, nullptr)) - && !falsePathTo(path, pin, path->transition(this), - path->minMax(this))) { + const MinMax *path_min_max = path->minMax(this); + Scene *scene = path->scene(this); + const Sdc *sdc = scene->sdc(); + if (scenes.contains(scene) + && min_max->matches(path_min_max) + && !sdc->isDisabledConstraint(pin) + // Ignore generated clock source paths. + && !path->clkInfo(this)->isGenClkSrcPath() + && (!filtered + || search_->matchesFilter(path, nullptr)) + && !falsePathTo(path, pin, path->transition(this), + path_min_max)) { PathEndUnconstrained path_end(path); visitor->visit(&path_end); } @@ -599,40 +613,41 @@ VisitPathEnds::visitUnconstrainedPathEnds(const Pin *pin, bool VisitPathEnds::falsePathTo(Path *path, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max) { ExceptionPath *exception = search_->exceptionTo(ExceptionPathType::false_path, path, - pin, rf, nullptr, min_max, - false, false); + pin, rf, nullptr, min_max, + false, false, path->sdc(this)); return exception != nullptr; } PathDelay * VisitPathEnds::pathDelayTo(Path *path, - const Pin *pin, - const RiseFall *rf, - const MinMax *min_max) + const Pin *pin, + const RiseFall *rf, + const MinMax *min_max) { ExceptionPath *exception = search_->exceptionTo(ExceptionPathType::path_delay, - path, pin, rf, nullptr, - min_max, false, - // Register clk pins only - // match with -to pin. - network_->isRegClkPin(pin)); + path, pin, rf, nullptr, + min_max, false, + // Register clk pins only + // match with -to pin. + network_->isRegClkPin(pin), + path->sdc(this)); return dynamic_cast(exception); } ExceptionPath * VisitPathEnds::exceptionTo(const Path *path, - const Pin *pin, - const RiseFall *rf, - const ClockEdge *clk_edge, - const MinMax *min_max) const + const Pin *pin, + const RiseFall *rf, + const ClockEdge *clk_edge, + const MinMax *min_max) const { return search_->exceptionTo(ExceptionPathType::any, path, pin, rf, clk_edge, - min_max, false, false); + min_max, false, false, path->sdc(this)); } } // namespace diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index e61fcccaa..cad69282e 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -24,38 +24,38 @@ #include "WorstSlack.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" #include "Report.hh" #include "Mutex.hh" #include "Graph.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Search.hh" -#include "PathAnalysisPt.hh" namespace sta { using std::min; WorstSlacks::WorstSlacks(StaState *sta) : - worst_slacks_(sta->corners()->pathAnalysisPtCount(), sta), + worst_slacks_(sta->scenePathCount(), sta), sta_(sta) { } void WorstSlacks::worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { worst_slack = MinMax::min()->initValue(); worst_vertex = nullptr; - for (auto corner : *sta_->corners()) { - PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + for (Scene *scene : sta_->scenes()) { + 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_slack1, worst_vertex1); if (delayLess(worst_slack1, worst_slack, sta_)) { worst_slack = worst_slack1; worst_vertex = worst_vertex1; @@ -64,22 +64,22 @@ WorstSlacks::worstSlack(const MinMax *min_max, } void -WorstSlacks::worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) +WorstSlacks::worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { - PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + PathAPIndex path_ap_index = scene->pathIndex(min_max); worst_slacks_[path_ap_index].worstSlack(path_ap_index, - worst_slack, worst_vertex); + worst_slack, worst_vertex); } void WorstSlacks::updateWorstSlacks(Vertex *vertex, - SlackSeq &slacks) + SlackSeq &slacks) { - PathAPIndex path_ap_count = sta_->corners()->pathAnalysisPtCount(); + PathAPIndex path_ap_count = sta_->scenePathCount(); for (PathAPIndex i = 0; i < path_ap_count; i++) worst_slacks_[i].updateWorstSlack(vertex, slacks, i); } @@ -87,11 +87,8 @@ WorstSlacks::updateWorstSlacks(Vertex *vertex, void WorstSlacks::worstSlackNotifyBefore(Vertex *vertex) { - WorstSlackSeq::Iterator worst_iter(worst_slacks_); - while (worst_iter.hasNext()) { - WorstSlack &worst_slack = worst_iter.next(); + for (WorstSlack &worst_slack : worst_slacks_) worst_slack.deleteVertexBefore(vertex); - } } //////////////////////////////////////////////////////////////// @@ -102,7 +99,7 @@ WorstSlack::WorstSlack(StaState *sta) : worst_vertex_(nullptr), worst_slack_(slack_init_), slack_threshold_(slack_init_), - queue_(new VertexSet(graph_)), + queue_(new VertexSet(VertexIdLess(graph_))), min_queue_size_(10), max_queue_size_(20) { @@ -119,7 +116,7 @@ WorstSlack::WorstSlack(const WorstSlack &worst_slack) : worst_vertex_(nullptr), worst_slack_(slack_init_), slack_threshold_(slack_init_), - queue_(new VertexSet(graph_)), + queue_(new VertexSet(VertexIdLess(graph_))), min_queue_size_(10), max_queue_size_(20) { @@ -138,9 +135,9 @@ WorstSlack::deleteVertexBefore(Vertex *vertex) void WorstSlack::worstSlack(PathAPIndex path_ap_index, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex) + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { findWorstSlack(path_ap_index); worst_slack = worst_slack_; @@ -167,16 +164,16 @@ 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_)) { if (delayLess(slack, worst_slack_, this)) - setWorstSlack(vertex, slack); + setWorstSlack(vertex, slack); if (delayLessEqual(slack, slack_threshold_, this)) - queue_->insert(vertex); + queue_->insert(vertex); int queue_size = queue_->size(); if (queue_size >= max_queue_size_) - sortQueue(path_ap_index); + sortQueue(path_ap_index); } } debugPrint(debug_, "wns", 3, "threshold %s", @@ -206,12 +203,10 @@ WorstSlack::sortQueue(PathAPIndex path_ap_index) // Reinsert vertices with slack < threshold. queue_->clear(); - VertexSeq::Iterator queue_iter2(vertices); - while (queue_iter2.hasNext()) { - Vertex *vertex = queue_iter2.next(); + for (Vertex *vertex : vertices) { Slack slack = search_->wnsSlack(vertex, path_ap_index); if (delayGreater(slack, slack_threshold_, this)) - break; + break; queue_->insert(vertex); } max_queue_size_ = queue_->size() * 2; @@ -239,20 +234,20 @@ void WorstSlack::checkQueue(PathAPIndex path_ap_index) { VertexSeq ends; - for(Vertex *end : *search_->endpoints()) { + for(Vertex *end : search_->endpoints()) { if (delayLessEqual(search_->wnsSlack(end, path_ap_index), - slack_threshold_, this)) + slack_threshold_, this)) ends.push_back(end); } WnsSlackLess slack_less(path_ap_index, this); sort(ends, slack_less); - VertexSet end_set(graph_); + VertexSet end_set = makeVertexSet(this); for (Vertex *end : ends) { end_set.insert(end); - if (!queue_->hasKey(end) - && delayLessEqual(search_->wnsSlack(end, path_ap_index), - slack_threshold_, this)) + 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), @@ -260,7 +255,7 @@ WorstSlack::checkQueue(PathAPIndex path_ap_index) } for (Vertex *end : *queue_) { - if (!end_set.hasKey(end)) + 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), @@ -270,8 +265,8 @@ WorstSlack::checkQueue(PathAPIndex path_ap_index) void WorstSlack::updateWorstSlack(Vertex *vertex, - SlackSeq &slacks, - PathAPIndex path_ap_index) + SlackSeq &slacks, + PathAPIndex path_ap_index) { // Do not touch the state unless queue has been initialized if (!queue_->empty()) { @@ -305,7 +300,7 @@ WorstSlack::updateWorstSlack(Vertex *vertex, void WorstSlack::setWorstSlack(Vertex *vertex, - Slack slack) + Slack slack) { debugPrint(debug_, "wns", 3, "%s %s", vertex->to_string(this).c_str(), @@ -317,7 +312,7 @@ WorstSlack::setWorstSlack(Vertex *vertex, //////////////////////////////////////////////////////////////// WnsSlackLess::WnsSlackLess(PathAPIndex path_ap_index, - const StaState *sta) : + const StaState *sta) : path_ap_index_(path_ap_index), search_(sta->search()) { @@ -325,11 +320,11 @@ WnsSlackLess::WnsSlackLess(PathAPIndex path_ap_index, bool WnsSlackLess::operator()(Vertex *vertex1, - Vertex *vertex2) + 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 diff --git a/search/WorstSlack.hh b/search/WorstSlack.hh index c304fd6b7..b23b8a9e0 100644 --- a/search/WorstSlack.hh +++ b/search/WorstSlack.hh @@ -25,9 +25,9 @@ #pragma once #include +#include #include "MinMax.hh" -#include "Vector.hh" #include "GraphClass.hh" #include "SearchClass.hh" #include "StaState.hh" @@ -38,23 +38,23 @@ class StaState; class WorstSlack; class WnsSlackLess; -typedef Vector WorstSlackSeq; +using WorstSlackSeq = std::vector; class WorstSlacks { public: WorstSlacks(StaState *sta); void worstSlack(const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); - void worstSlack(const Corner *corner, - const MinMax *min_max, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void worstSlack(const Scene *scene, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); void updateWorstSlacks(Vertex *vertex, - SlackSeq &slacks); + SlackSeq &slacks); void worstSlackNotifyBefore(Vertex *vertex); protected: @@ -66,9 +66,9 @@ class WnsSlackLess { public: WnsSlackLess(PathAPIndex path_ap_index, - const StaState *sta); + const StaState *sta); bool operator()(Vertex *vertex1, - Vertex *vertex2); + Vertex *vertex2); private: PathAPIndex path_ap_index_; @@ -82,12 +82,12 @@ public: ~WorstSlack(); WorstSlack(const WorstSlack &); void worstSlack(PathAPIndex path_ap_index, - // Return values. - Slack &worst_slack, - Vertex *&worst_vertex); + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); void updateWorstSlack(Vertex *vertex, - SlackSeq &slacks, - PathAPIndex path_ap_index); + SlackSeq &slacks, + PathAPIndex path_ap_index); void deleteVertexBefore(Vertex *vertex); protected: @@ -95,7 +95,7 @@ protected: void initQueue(PathAPIndex path_ap_index); void findWorstInQueue(PathAPIndex path_ap_index); void setWorstSlack(Vertex *vertex, - Slack slack); + Slack slack); void sortQueue(PathAPIndex path_ap_index); void checkQueue(PathAPIndex path_ap_index); diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index ae8bab8f7..694ba7b38 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -41,9 +41,7 @@ #include "Network.hh" #include "Graph.hh" #include "Sdc.hh" -#include "DcalcAnalysisPt.hh" #include "Parasitics.hh" -#include "PathAnalysisPt.hh" #include "Path.hh" #include "PathExpanded.hh" #include "StaState.hh" @@ -65,14 +63,14 @@ class WritePathSpice : public WriteSpice { public: WritePathSpice(Path *path, - const char *spice_filename, + const char *spice_filename, const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + const char *lib_subckt_filename, + const char *model_filename, + const char *power_name, + const char *gnd_name, CircuitSim ckt_sim, - const StaState *sta); + const StaState *sta); void writeSpice(); private: @@ -92,10 +90,10 @@ class WritePathSpice : public WriteSpice float maxTime(); float pathMaxTime(); void writeMeasureDelayStmt(Stage stage, - const Path *from_path, - const Path *to_path); + const Path *from_path, + const Path *to_path); void writeMeasureSlewStmt(Stage stage, - const Path *path); + const Path *path); void writeInputWaveform(); void writeClkWaveform(); @@ -138,8 +136,8 @@ class WritePathSpice : public WriteSpice float findSlew(const Path *path); float findSlew(const Path *path, - const RiseFall *rf, - const TimingArc *next_arc); + const RiseFall *rf, + const TimingArc *next_arc); Path *path_; PathExpanded path_expanded_; // Input clock waveform cycles. @@ -160,14 +158,14 @@ class WritePathSpice : public WriteSpice void writePathSpice(Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, + const char *spice_filename, + const char *subckt_filename, + const char *lib_subckt_filename, + const char *model_filename, const char *power_name, - const char *gnd_name, + const char *gnd_name, CircuitSim ckt_sim, - StaState *sta) + StaState *sta) { WritePathSpice writer(path, spice_filename, subckt_filename, lib_subckt_filename, model_filename, @@ -177,16 +175,16 @@ writePathSpice(Path *path, WritePathSpice::WritePathSpice(Path *path, const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + const char *subckt_filename, + const char *lib_subckt_filename, + const char *model_filename, + const char *power_name, + const char *gnd_name, CircuitSim ckt_sim, - const StaState *sta) : + const StaState *sta) : WriteSpice(spice_filename, subckt_filename, lib_subckt_filename, model_filename, power_name, gnd_name, ckt_sim, - path->dcalcAnalysisPt(sta), sta), + path->scene(sta), path->minMax(sta), sta), path_(path), path_expanded_(sta), clk_cycle_count_(3), @@ -297,16 +295,16 @@ WritePathSpice::writeStageInstances() 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); + 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, + stageGateInputPinName(stage), + stageDrvrPinName(stage), + stageLoadPinName(stage), stage_cname); } } @@ -380,7 +378,7 @@ 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)); + stageDrvrPinName(input_stage)); streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); for (int cycle = 0; cycle < clk_cycle_count_; cycle++) { float time0 = time_offset + cycle * period; @@ -438,8 +436,8 @@ WritePathSpice::writeMeasureStmts() void WritePathSpice::writeMeasureDelayStmt(Stage stage, - const Path *from_path, - const Path *to_path) + const Path *from_path, + const Path *to_path) { writeMeasureDelayStmt(from_path->pin(this), from_path->transition(this), to_path->pin(this), to_path->transition(this), @@ -448,7 +446,7 @@ WritePathSpice::writeMeasureDelayStmt(Stage stage, void WritePathSpice::writeMeasureSlewStmt(Stage stage, - const Path *path) + const Path *path) { const Pin *pin = path->pin(this); const RiseFall *rf = path->transition(this); @@ -484,9 +482,9 @@ WritePathSpice::writeInputStage(Stage stage) const char *load_pin_name = stageLoadPinName(stage); string prefix = stageName(stage); streamPrint(spice_stream_, ".subckt %s %s %s\n", - prefix.c_str(), - drvr_pin_name, - load_pin_name); + prefix.c_str(), + drvr_pin_name, + load_pin_name); writeStageParasitics(stage); streamPrint(spice_stream_, ".ends\n\n"); } @@ -508,16 +506,16 @@ WritePathSpice::writeGateStage(Stage stage) 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); + subckt_name.c_str(), + 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()); + network_->pathName(inst), + input_port->name(), + drvr_port->name()); writeSubcktInst(inst); const Path *drvr_path = stageDrvrPath(stage); @@ -551,13 +549,12 @@ void WritePathSpice::writeStageParasitics(Stage stage) { const Path *drvr_path = stageDrvrPath(stage); - DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(this); - ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + const MinMax *min_max = drvr_path->minMax(this); const Pin *drvr_pin = stageDrvrPin(stage); - const Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + const Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin); if (parasitic == nullptr) { const RiseFall *drvr_rf = drvr_path->transition(this); - parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, parasitic_ap); + parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, min_max); } NetSet coupling_nets; writeDrvrParasitics(drvr_pin, parasitic, coupling_nets); @@ -583,19 +580,19 @@ WritePathSpice::findPathCellNames() if (arc) { LibertyCell *cell = arc->set()->libertyCell(); if (cell) { - debugPrint(debug_, "write_spice", 2, "cell %s", cell->name()); - path_cell_names.insert(cell->name()); + debugPrint(debug_, "write_spice", 2, "cell %s", cell->name()); + path_cell_names.insert(cell->name()); } // Include side receivers. Pin *drvr_pin = stageDrvrPin(stage); auto pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - LibertyPort *port = network_->libertyPort(pin); - if (port) { - LibertyCell *cell = port->libertyCell(); - path_cell_names.insert(cell->name()); - } + const Pin *pin = pin_iter->next(); + LibertyPort *port = network_->libertyPort(pin); + if (port) { + LibertyCell *cell = port->libertyCell(); + path_cell_names.insert(cell->name()); + } } delete pin_iter; } diff --git a/spice/WritePathSpice.hh b/spice/WritePathSpice.hh index bf4ab5061..a6d014d10 100644 --- a/spice/WritePathSpice.hh +++ b/spice/WritePathSpice.hh @@ -36,17 +36,17 @@ class StaState; // Throws FileNotReadable, FileNotWritable, SubcktEndsMissing void writePathSpice(Path *path, - // Spice file written for path. - const char *spice_filename, - // Subckts used by path included in spice file. - const char *subckt_filename, - // File of all cell spice subckt definitions. - const char *lib_subckt_filename, - // Device model file included in spice file. - const char *model_filename, + // Spice file written for path. + const char *spice_filename, + // Subckts used by path included in spice file. + const char *subckt_filename, + // File of all cell spice subckt definitions. + const char *lib_subckt_filename, + // Device model file included in spice file. + const char *model_filename, const char *power_name, - const char *gnd_name, + const char *gnd_name, CircuitSim ckt_sim, - StaState *sta); + StaState *sta); } // namespace diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index f336bc8fb..9ef7b05da 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -27,6 +27,9 @@ #include // swap #include +#include "cudd.h" + +#include "ContainerHelpers.hh" #include "Debug.hh" #include "Units.hh" #include "TableModel.hh" @@ -41,9 +44,9 @@ #include "search/Sim.hh" #include "Clock.hh" #include "Path.hh" -#include "DcalcAnalysisPt.hh" #include "Bdd.hh" -#include "cudd.h" +#include "Sdc.hh" +#include "Mode.hh" namespace sta { @@ -93,7 +96,8 @@ WriteSpice::WriteSpice(const char *spice_filename, const char *power_name, const char *gnd_name, CircuitSim ckt_sim, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const StaState *sta) : StaState(sta), spice_filename_(spice_filename), @@ -103,13 +107,15 @@ WriteSpice::WriteSpice(const char *spice_filename, power_name_(power_name), gnd_name_(gnd_name), ckt_sim_(ckt_sim), - dcalc_ap_(dcalc_ap), + scene_(scene), + min_max_(min_max), default_library_(network_->defaultLibertyLibrary()), short_ckt_resistance_(.0001), cap_index_(1), res_index_(1), volt_index_(1), - bdd_(sta) + bdd_(sta), + parasitics_(scene->parasitics(min_max)) { } @@ -119,7 +125,8 @@ WriteSpice::initPowerGnd() bool exists = false; default_library_->supplyVoltage(power_name_, power_voltage_, exists); if (!exists) { - const OperatingConditions *op_cond = dcalc_ap_->operatingConditions(); + const OperatingConditions *op_cond = + scene_->sdc()->operatingConditions(min_max_); if (op_cond == nullptr) op_cond = network_->defaultLibertyLibrary()->defaultOperatingConditions(); power_voltage_ = op_cond->voltage(); @@ -211,7 +218,7 @@ WriteSpice::writeSubckts(StdStringSet &cell_names) if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { const char *cell_name = tokens[1].c_str(); - if (cell_names.find(cell_name) != cell_names.end()) { + if (cell_names.contains(cell_name)) { subckts_stream << line << "\n"; bool found_ends = false; while (getline(lib_subckts_stream, line)) { @@ -288,7 +295,7 @@ WriteSpice::findCellSubckts(StdStringSet &cell_names) if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { const char *cell_name = tokens[1].c_str(); - if (cell_names.find(cell_name) != cell_names.end()) { + if (cell_names.contains(cell_name)) { // Scan the subckt definition for subckt calls. string stmt; while (getline(lib_subckts_stream, line)) { @@ -370,15 +377,15 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, else if (stringEq(subckt_port_name, gnd_name_)) writeVoltageSource(inst_name, subckt_port_name, gnd_voltage_); else if (port - && excluded_input_pins.find(pin) == excluded_input_pins.end() + && !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. - LogicValue port_value = sim_->logicValue(pin); + LogicValue port_value = scene_->mode()->sim()->simValue(pin); if (port_value == LogicValue::unknown) { bool has_value; LogicValue value; - port_values.findKey(port, value, has_value); + findKeyValue(port_values, port, value, has_value); if (has_value) port_value = value; } @@ -468,7 +475,8 @@ WriteSpice::findSlew(Vertex *vertex, const RiseFall *rf, const TimingArc *next_arc) { - float slew = delayAsFloat(graph_->slew(vertex, rf, dcalc_ap_->index())); + DcalcAPIndex ap_index = scene_->dcalcAnalysisPtIndex(min_max_); + float slew = delayAsFloat(graph_->slew(vertex, rf, ap_index)); if (slew == 0.0 && next_arc) slew = slewAxisMinValue(next_arc); if (slew == 0.0) @@ -480,7 +488,7 @@ WriteSpice::findSlew(Vertex *vertex, float WriteSpice::slewAxisMinValue(const TimingArc *arc) { - GateTableModel *gate_model = arc->gateTableModel(dcalc_ap_); + GateTableModel *gate_model = arc->gateTableModel(scene_, min_max_); if (gate_model) { const TableModel *model = gate_model->delayModel(); const TableAxis *axis1 = model->axis1(); @@ -533,8 +541,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, set reachable_pins; // Sort resistors for consistent regression results. ParasiticResistorSeq resistors = parasitics_->resistors(parasitic); - sort(resistors.begin(), resistors.end(), - [this] (const ParasiticResistor *r1, + sort(resistors, [this] (const ParasiticResistor *r1, const ParasiticResistor *r2) { return parasitics_->id(r1) < parasitics_->id(r2); }); @@ -564,7 +571,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, if (pin != drvr_pin && network_->isLoad(pin) && !network_->isHierarchical(pin) - && reachable_pins.find(pin) == reachable_pins.end()) { + && !reachable_pins.contains(pin)) { streamPrint(spice_stream_, "R%d %s %s %.3e\n", res_index_++, network_->pathName(drvr_pin), @@ -577,8 +584,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, // Grounded node capacitors. // Sort nodes for consistent regression results. ParasiticNodeSeq nodes = parasitics_->nodes(parasitic); - sort(nodes.begin(), nodes.end(), - [this] (const ParasiticNode *node1, + sort(nodes, [this] (const ParasiticNode *node1, const ParasiticNode *node2) { const char *name1 = parasitics_->name(node1); const char *name2 = parasitics_->name(node2); @@ -598,8 +604,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, // Sort coupling capacitors for consistent regression results. ParasiticCapacitorSeq capacitors = parasitics_->capacitors(parasitic); - sort(capacitors.begin(), capacitors.end(), - [this] (const ParasiticCapacitor *c1, + sort(capacitors, [this] (const ParasiticCapacitor *c1, const ParasiticCapacitor *c2) { return parasitics_->id(c1) < parasitics_->id(c2); }); @@ -614,7 +619,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, swap(net1, net2); swap(node1, node2); } - if (net2 && coupling_nets.hasKey(net2)) + 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_++, @@ -965,19 +970,19 @@ WriteSpice::onePort(FuncExpr *expr) FuncExpr *right = expr->right(); LibertyPort *port; switch (expr->op()) { - case FuncExpr::op_port: + case FuncExpr::Op::port: return expr->port(); - case FuncExpr::op_not: + case FuncExpr::Op::not_: return onePort(left); - case FuncExpr::op_or: - case FuncExpr::op_and: - case FuncExpr::op_xor: + 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: + case FuncExpr::Op::one: + case FuncExpr::Op::zero: default: return nullptr; } @@ -1018,7 +1023,7 @@ WriteSpice::writeSubcktInstLoads(const Pin *drvr_pin, && network_->direction(load_pin)->isAnyInput() && !network_->isHierarchical(load_pin) && !network_->isTopLevelPort(load_pin) - && !written_insts.hasKey(load_inst)) { + && !written_insts.contains(load_inst)) { writeSubcktInst(load_inst); writeSubcktInstVoltSrcs(load_inst, port_values, excluded_input_pins); streamPrint(spice_stream_, "\n"); diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index 98995a44d..3e55e2942 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -39,10 +39,10 @@ namespace sta { -typedef std::map ParasiticNodeMap; -typedef Map CellSpicePortNames; -typedef Map LibertyPortLogicValues; -typedef std::vector StdStringSeq; +using ParasiticNodeMap = std::map; +using CellSpicePortNames = std::map; +using LibertyPortLogicValues = std::map; +using StdStringSeq = std::vector; // Utilities for writing a spice deck. class WriteSpice : public StaState @@ -55,7 +55,8 @@ public: const char *power_name, const char *gnd_name, CircuitSim ckt_sim, - const DcalcAnalysisPt *dcalc_ap, + const Scene *scene, + const MinMax *min_max, const StaState *sta); protected: @@ -68,28 +69,23 @@ protected: void writeSubckts(StdStringSet &cell_names); void findCellSubckts(StdStringSet &cell_names); void recordSpicePortNames(const char *cell_name, - StringVector &tokens); + StringVector &tokens); void writeSubcktInst(const Instance *inst); void writeSubcktInstVoltSrcs(const Instance *inst, - LibertyPortLogicValues &port_values, + LibertyPortLogicValues &port_values, const PinSet &excluded_input_pins); float pgPortVoltage(LibertyPort *pg_port); void writeVoltageSource(const char *inst_name, - const char *port_name, - float voltage); + const char *port_name, + float voltage); void writeVoltageSource(LibertyCell *cell, - const char *inst_name, - const char *subckt_port_name, - const char *pg_port_name, - float voltage); + const char *inst_name, + const char *subckt_port_name, + const char *pg_port_name, + float voltage); void writeClkedStepSource(const Pin *pin, - const RiseFall *rf, - const Clock *clk); - void writeDrvrParasitics(const Pin *drvr_pin, - const RiseFall *drvr_rf, - // Nets with parasitics to include coupling caps to. - const NetSet &coupling_nets, - const ParasiticAnalysisPt *parasitic_ap); + const RiseFall *rf, + const Clock *clk); void writeDrvrParasitics(const Pin *drvr_pin, const Parasitic *parasitic, const NetSet &coupling_nets); @@ -103,23 +99,23 @@ protected: void writeVoltageSource(const char *node_name, float voltage); void writeRampVoltSource(const Pin *pin, - const RiseFall *rf, - float time, - float slew); + const RiseFall *rf, + float time, + float slew); void writeWaveformVoltSource(const Pin *pin, DriverWaveform *drvr_waveform, const RiseFall *rf, float delay, float slew); void writeWaveformEdge(const RiseFall *rf, - float time, - float slew); + float time, + float slew); float railToRailSlew(float slew, const RiseFall *rf); void seqPortValues(Sequential *seq, - const RiseFall *rf, - // Return values. - LibertyPortLogicValues &port_values); + const RiseFall *rf, + // Return values. + LibertyPortLogicValues &port_values); LibertyPort *onePort(FuncExpr *expr); void writeMeasureDelayStmt(const Pin *from_pin, const RiseFall *from_rf, @@ -131,8 +127,8 @@ protected: std::string prefix); const char *spiceTrans(const RiseFall *rf); float findSlew(Vertex *vertex, - const RiseFall *rf, - const TimingArc *next_arc); + const RiseFall *rf, + const TimingArc *next_arc); float slewAxisMinValue(const TimingArc *arc); float clkWaveformTimeOffset(const Clock *clk); @@ -140,21 +136,21 @@ protected: const Pin *drvr_pin, const RiseFall *drvr_rf, const Edge *gate_edge, - // Return values. - LibertyPortLogicValues &port_values, - bool &is_clked); + // Return values. + LibertyPortLogicValues &port_values, + bool &is_clked); void regPortValues(const Pin *input_pin, const RiseFall *drvr_rf, const LibertyPort *drvr_port, const FuncExpr *drvr_func, - // Return values. - LibertyPortLogicValues &port_values, + // Return values. + LibertyPortLogicValues &port_values, bool &is_clked); void gatePortValues(const Instance *inst, - const FuncExpr *expr, - const LibertyPort *input_port, - // Return values. - LibertyPortLogicValues &port_values); + const FuncExpr *expr, + const LibertyPort *input_port, + // Return values. + LibertyPortLogicValues &port_values); void writeSubcktInstLoads(const Pin *drvr_pin, const Pin *path_load, const PinSet &excluded_input_pins, @@ -171,7 +167,8 @@ protected: const char *power_name_; const char *gnd_name_; CircuitSim ckt_sim_; - const DcalcAnalysisPt *dcalc_ap_; + const Scene *scene_; + const MinMax *min_max_; std::ofstream spice_stream_; LibertyLibrary *default_library_; @@ -187,11 +184,12 @@ protected: int volt_index_; CellSpicePortNames cell_spice_port_names_; Bdd bdd_; + Parasitics *parasitics_; }; void streamPrint(std::ofstream &stream, - const char *fmt, - ...) __attribute__((format (printf, 2, 3))); + const char *fmt, + ...) __attribute__((format (printf, 2, 3))); } // namespace diff --git a/spice/WriteSpice.i b/spice/WriteSpice.i index 727bf82ba..e7adadbe2 100644 --- a/spice/WriteSpice.i +++ b/spice/WriteSpice.i @@ -34,12 +34,12 @@ void write_path_spice_cmd(Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + const char *spice_filename, + const char *subckt_filename, + const char *lib_subckt_filename, + const char *model_filename, + const char *power_name, + const char *gnd_name, CircuitSim ckt_sim) { Sta *sta = Sta::sta(); diff --git a/spice/WriteSpice.tcl b/spice/WriteSpice.tcl index af15905b4..7bf9674e5 100644 --- a/spice/WriteSpice.tcl +++ b/spice/WriteSpice.tcl @@ -25,17 +25,17 @@ namespace eval sta { define_cmd_args "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\ + -spice_directory spice_directory\ + -lib_subckt_file lib_subckts_file\ + -model_file model_file\ + -power power\ + -ground ground\ [-simulator hspice|ngspice|xyce]} proc write_path_spice { args } { parse_key_args "write_path_spice" args \ keys {-spice_directory -lib_subckt_file -model_file \ - -power -ground -path_args -simulator} \ + -power -ground -path_args -simulator} \ flags {} if { [info exists keys(-spice_directory)] } { @@ -100,7 +100,7 @@ proc write_path_spice { args } { set spice_file [file join $spice_dir "$path_name.sp"] set subckt_file [file join $spice_dir "$path_name.subckt"] write_path_spice_cmd $path $spice_file $subckt_file \ - $lib_subckt_file $model_file $power $ground $ckt_sim + $lib_subckt_file $model_file $power $ground $ckt_sim incr path_index } } @@ -132,13 +132,13 @@ define_cmd_args "write_gate_spice" \ -power power\ -ground ground\ [-simulator hspice|ngspice|xyce]\ - [-corner corner]\ + [-scene scene]\ [-min] [-max]} proc write_gate_spice { args } { parse_key_args "write_gate_spice" args \ keys {-gates -spice_filename -lib_subckt_file -model_file \ - -power -ground -simulator -corner}\ + -power -ground -simulator -scene -corner}\ flags {-measure_stmts -min -max} if { [info exists keys(-gates)] } { @@ -188,7 +188,7 @@ proc write_gate_spice { args } { set ckt_sim [parse_ckt_sim_key keys] - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_flags flags] check_argc_eq0 "write_gate_spice" $args @@ -197,7 +197,7 @@ proc write_gate_spice { args } { set subckt_file [file join $spice_dir "$spice_root.subckt"] write_gate_spice_cmd $gates $spice_file $subckt_file \ $lib_subckt_file $model_file $power $ground $ckt_sim \ - $corner $min_max + $scene $min_max } ################################################################ @@ -207,11 +207,11 @@ define_cmd_args "write_gate_gnuplot" \ { -gates {{instance input_port driver_port edge [delay]}...}\ -plot_pins plot_pins\ -plot_basename plot_basename\ - [-corner corner] [-min] [-max]} + [-scene scene] [-min] [-max]} proc write_gate_gnuplot { args } { parse_key_args "write_gate_gnuplot" args \ - keys {-gates -plot_pins -plot_basename -spice_waveforms -corner} \ + keys {-gates -plot_pins -plot_basename -spice_waveforms -corner -scene} \ flags {-min -max} if { [info exists keys(-gates)] } { @@ -262,11 +262,11 @@ proc write_gate_gnuplot { args } { set sim_wave_filename $keys(-spice_waveforms) } - set corner [parse_corner keys] + set scene [parse_scene keys] set min_max [parse_min_max_flags flags] write_gate_gnuplot_cmd $gates $plot_pins $sim_wave_filename \ - $gnuplot_filename $csv_filename $corner $min_max + $gnuplot_filename $csv_filename $scene $min_max } proc parse_gate_drvr_pin { gate_arg } { diff --git a/spice/Xyce.hh b/spice/Xyce.hh index 848c9987a..c13a918bc 100644 --- a/spice/Xyce.hh +++ b/spice/Xyce.hh @@ -31,8 +31,8 @@ namespace sta { -typedef std::vector StdStringSeq; -typedef std::vector WaveformSeq; +using StdStringSeq = std::vector; +using WaveformSeq = std::vector; void readXyceCsv(const char *csv_filename, diff --git a/tcl/CmdArgs.tcl b/tcl/CmdArgs.tcl index c1a598892..094fc6332 100644 --- a/tcl/CmdArgs.tcl +++ b/tcl/CmdArgs.tcl @@ -48,8 +48,8 @@ namespace eval sta { # net proc get_object_args { objects clks_var libcells_var libports_var \ - cells_var insts_var ports_var pins_var nets_var \ - edges_var timing_arc_sets_var } { + cells_var insts_var ports_var pins_var nets_var \ + edges_var timing_arc_sets_var } { if { $clks_var != {} } { upvar 1 $clks_var clks } @@ -87,97 +87,97 @@ proc get_object_args { objects clks_var libcells_var libports_var \ if { [llength $obj] > 1 } { # List arg. Recursive call without initing objects. get_object_args $obj $clks_var $libcells_var $libports_var $cells_var $insts_var \ - $ports_var $pins_var $nets_var $edges_var $timing_arc_sets_var + $ports_var $pins_var $nets_var $edges_var $timing_arc_sets_var } elseif { [is_object $obj] } { # Explicit object arg. set object_type [object_type $obj] if { $pins_var != {} && $object_type == "Pin" } { - lappend pins $obj + lappend pins $obj } elseif { $insts_var != {} && $object_type == "Instance" } { - lappend insts $obj + lappend insts $obj } elseif { $nets_var != {} && $object_type == "Net" } { - lappend nets $obj + lappend nets $obj } elseif { $ports_var != {} && $object_type == "Port" } { - lappend ports $obj + lappend ports $obj } elseif { $edges_var != {} && $object_type == "Edge" } { - lappend edges $obj + lappend edges $obj } elseif { $clks_var != {} && $object_type == "Clock" } { - lappend clks $obj + lappend clks $obj } elseif { $libcells_var != {} && $object_type == "LibertyCell" } { - lappend libcells $obj + lappend libcells $obj } elseif { $libports_var != {} && $object_type == "LibertyPort" } { - lappend libports $obj + lappend libports $obj } elseif { $cells_var != {} && $object_type == "Cell" } { - lappend cells $obj + lappend cells $obj } elseif { $timing_arc_sets_var != {} \ - && $object_type == "TimingArcSet" } { - lappend timing_arc_sets $obj + && $object_type == "TimingArcSet" } { + lappend timing_arc_sets $obj } else { - sta_error 100 "unsupported object type $object_type." + sta_error 100 "unsupported object type $object_type." } } elseif { $obj != {} } { # Check for implicit arg. # Search for most general object type first. set matches {} if { $clks_var != {} } { - set matches [get_clocks -quiet $obj] + set matches [get_clocks -quiet $obj] } if { $matches != {} } { - set clks [concat $clks $matches] + set clks [concat $clks $matches] } else { - if { $libcells_var != {} } { - set matches [get_lib_cells -quiet $obj] - } - if { $matches != {} } { - set libcells [concat $libcells $matches] - } else { - - if { $libports_var != {} } { - set matches [get_lib_pins -quiet $obj] - } - if { $matches != {} } { - set libports [concat $libports $matches] - } else { - - if { $cells_var != {} } { - set matches [find_cells_matching $obj 0 0] - } - if { $matches != {} } { - set cells [concat $cells $matches] - } else { - - if { $insts_var != {} } { - set matches [get_cells -quiet $obj] - } - if { $matches != {} } { - set insts [concat $insts $matches] - } else { - if { $ports_var != {} } { - set matches [get_ports -quiet $obj] - } - if { $matches != {} } { - set ports [concat $ports $matches] - } else { - if { $pins_var != {} } { - set matches [get_pins -quiet $obj] - } - if { $matches != {} } { - set pins [concat $pins $matches] - } else { - if { $nets_var != {} } { - set matches [get_nets -quiet $obj] - } - if { $matches != {} } { - set nets [concat $nets $matches] - } else { - sta_warn 101 "object '$obj' not found." - } - } - } - } - } - } - } + if { $libcells_var != {} } { + set matches [get_lib_cells -quiet $obj] + } + if { $matches != {} } { + set libcells [concat $libcells $matches] + } else { + + if { $libports_var != {} } { + set matches [get_lib_pins -quiet $obj] + } + if { $matches != {} } { + set libports [concat $libports $matches] + } else { + + if { $cells_var != {} } { + set matches [find_cells_matching $obj 0 0] + } + if { $matches != {} } { + set cells [concat $cells $matches] + } else { + + if { $insts_var != {} } { + set matches [get_cells -quiet $obj] + } + if { $matches != {} } { + set insts [concat $insts $matches] + } else { + if { $ports_var != {} } { + set matches [get_ports -quiet $obj] + } + if { $matches != {} } { + set ports [concat $ports $matches] + } else { + if { $pins_var != {} } { + set matches [get_pins -quiet $obj] + } + if { $matches != {} } { + set pins [concat $pins $matches] + } else { + if { $nets_var != {} } { + set matches [get_nets -quiet $obj] + } + if { $matches != {} } { + set nets [concat $nets $matches] + } else { + sta_warn 101 "object '$obj' not found." + } + } + } + } + } + } + } } } } @@ -194,7 +194,7 @@ proc parse_clk_cell_port_args { objects clks_var cells_var ports_var } { } proc parse_clk_cell_port_pin_args { objects clks_var cells_var ports_var \ - pins_arg } { + pins_arg } { upvar 1 $clks_var clks upvar 1 $cells_var cells upvar 1 $ports_var ports @@ -243,13 +243,13 @@ proc parse_clk_port_pin_arg { objects clks_var pins_var } { } proc parse_libcell_libport_inst_port_pin_edge_timing_arc_set_arg { objects \ - libcells_var \ - libports_var \ - insts_var \ - ports_var \ - pins_var \ - edges_var \ - timing_arc_sets_var } { + libcells_var \ + libports_var \ + insts_var \ + ports_var \ + pins_var \ + edges_var \ + timing_arc_sets_var } { upvar 1 $libcells_var libcells upvar 1 $libports_var libports upvar 1 $insts_var insts @@ -399,87 +399,156 @@ proc get_ports_or_pins { pattern } { ################################################################ -# -corner keyword is optional. -# If -corner keyword is missing: -# one corner: return default -# multiple corners: error -proc parse_corner { keys_var } { +# -scene keyword is optional. +# If -scene keyword is missing: +# one scene: return default +# multiple scenes: error +proc parse_scene { keys_var } { upvar 1 $keys_var keys + set scene_arg "" + # compabibility 05/29/2025 if { [info exists keys(-corner)] } { - set corner_arg $keys(-corner) - if { [is_object $corner_arg] } { - set object_type [object_type $corner_arg] - if { $object_type == "Corner" } { - return $corner_arg + set scene_arg $keys(-corner) + } + if { [info exists keys(-scene)] } { + set scene_arg $keys(-scene) + } + if { $scene_arg != "" } { + if { [is_object $scene_arg] } { + set object_type [object_type $scene_arg] + if { $object_type == "Scene" } { + return $scene_arg } else { - sta_error 144 "corner object type '$object_type' is not a corner." + sta_error 144 "scene object type '$object_type' is not a scene." } } else { - set corner [find_corner $corner_arg] - if { $corner == "NULL" } { - sta_error 102 "$corner_arg is not the name of process corner." + set scene [find_scene $scene_arg] + if { $scene == "NULL" } { + sta_error 102 "$scene_arg is not the name of a scene." } else { - return $corner + return $scene } } - } elseif { [multi_corner] } { - sta_error 103 "-corner keyword required with multi-corner analysis." + } elseif { [multi_scene] } { + sta_error 103 "-scene keyword required with multi-scene analysis." } else { - return [cmd_corner] + return [cmd_scene] } } -# -corner keyword is required. -proc parse_corner_required { keys_var } { +# -scene keyword is required. +proc parse_scene_required { keys_var } { upvar 1 $keys_var keys + set scene_name "" + if { [info exists keys(-scene)] } { + set scene_name $keys(-scene) + } + # compabibility 05/29/2025 if { [info exists keys(-corner)] } { - set corner_name $keys(-corner) - set corner [find_corner $corner_name] - if { $corner == "NULL" } { - sta_error 104 "$corner_name is not the name of process corner." + set scene_name $keys(-corner) + } + if { $scene_name != "" } { + set scene [find_scene $scene_name] + if { $scene == "NULL" } { + sta_error 104 "$scene_name is not the name of a scene." } else { - return $corner + return $scene } } else { - sta_error 105 "missing -corner arg." + sta_error 105 "missing -scene arg." } } -proc parse_corner_or_default { keys_var } { +proc parse_scene_or_default { keys_var } { upvar 1 $keys_var keys + set scene_name "" + if { [info exists keys(-scene)] } { + set scene_name $keys(-scene) + } + # compabibility 05/29/2025 if { [info exists keys(-corner)] } { - set corner_name $keys(-corner) - set corner [find_corner $corner_name] - if { $corner == "NULL" } { - sta_error 106 "$corner_name is not the name of process corner." + set scene_name $keys(-corner) + } + if { $scene_name != "" } { + set scene [find_scene $scene_name] + if { $scene == "NULL" } { + sta_error 106 "$scene_name is not the name of a scene." } else { - return $corner + return $scene } } else { - return [cmd_corner] + return [cmd_scene] } } -# Return NULL for all. -proc parse_corner_or_all { keys_var } { +# If -scene/-corner return scene, else return NULL. +proc parse_scene_or_null { keys_var } { upvar 1 $keys_var keys + set scene_name "" + if { [info exists keys(-scene)] } { + set scene_name $keys(-scene) + } + # compabibility 05/29/2025 if { [info exists keys(-corner)] } { - set corner_name $keys(-corner) - set corner [find_corner $corner_name] - if { $corner == "NULL" } { - sta_error 107 "$corner_name is not the name of process corner." + set scene_name $keys(-corner) + } + if { $scene_name != "" } { + set scene [find_scene $scene_name] + if { $scene == "NULL" } { + sta_error 107 "$scene_name is not the name of a scene." } else { - return $corner + return $scene } } else { return "NULL" } } +# If -scenes/-corner return scenes, else return default scene. +proc parse_scenes_or_default { keys_var } { + upvar 1 $keys_var keys + + if { [info exists keys(-scenes)] } { + return [find_scenes $keys(-scenes)] + } elseif { [info exists keys(-corner)] } { + # compabibility 05/29/2025 + return [find_scenes $keys(-corner)] + } else { + return [cmd_scene] + } +} + +# If -scenes/-corner return scenes, else return all scenes. +proc parse_scenes_or_all { keys_var } { + upvar 1 $keys_var keys + + if { [info exists keys(-scenes)] } { + return [find_scenes $keys(-scenes)] + } elseif { [info exists keys(-corner)] } { + # compabibility 05/29/2025 + return [find_scenes $keys(-corner)] + } else { + return [scenes] + } +} + +proc find_scenes { scene_names } { + set scenes {} + foreach scene_name $scene_names { + set scene [find_scene $scene_name] + if { $scene == "NULL" } { + sta_error 134 "$scene_name is not the name of a scene." + } else { + lappend scenes $scene + } + } + return $scenes +} + ################################################################ proc parse_rise_fall_flags { flags_var } { @@ -617,7 +686,7 @@ proc get_lib_cell_arg { arg_name arg error_proc } { if { $library != "NULL" } { set lib_cell [$library find_liberty_cell $cell_name] if { $lib_cell == "NULL" } { - $error_proc 117 "liberty cell '$arg' not found." + $error_proc 117 "liberty cell '$arg' not found." } } else { $error_proc 118 "library '$lib_name' not found." @@ -643,14 +712,14 @@ proc get_lib_cells_arg { arg_name arglist error_proc } { } elseif { [is_object $arg] } { set object_type [object_type $arg] if { $object_type == "LibertyCell" } { - lappend lib_cells $arg + lappend lib_cells $arg } else { - $error_proc 120 "unsupported object type $object_type." + $error_proc 120 "unsupported object type $object_type." } } elseif { $arg != {} } { set arg_lib_cells [get_lib_cells1 $arg $error_proc] if { $arg_lib_cells != {} } { - set lib_cells [concat $lib_cells $arg_lib_cells] + set lib_cells [concat $lib_cells $arg_lib_cells] } } } diff --git a/tcl/CmdUtil.tcl b/tcl/CmdUtil.tcl index a2380ace1..3eab60187 100644 --- a/tcl/CmdUtil.tcl +++ b/tcl/CmdUtil.tcl @@ -65,15 +65,15 @@ proc show_cmd_args { cmd } { # Break the arglist up into max_col length lines. while {1} { if {[regexp {(^[\n ]*)([a-zA-Z0-9_\\\|\-]+|\[[^\[]+\])(.*)} \ - $arglist ignore space arg rest]} { + $arglist ignore space arg rest]} { set arg_length [string length $arg] if { $col + $arg_length < $max_col } { - set line "$line $arg" - set col [expr $col + $arg_length + 1] + set line "$line $arg" + set col [expr $col + $arg_length + 1] } else { report_line $line - set line "$indent_str $arg" - set col [expr $indent + $arg_length + 1] + set line "$indent_str $arg" + set col [expr $indent + $arg_length + 1] } set arglist $rest } else { @@ -147,7 +147,7 @@ define_cmd_args "set_cmd_units" \ proc set_cmd_units { args } { parse_key_args "set_cmd_units" args \ keys {-capacitance -resistance -time -voltage -current -power \ - -distance -digits -suffix} \ + -distance -digits -suffix} \ flags {} check_argc_eq0 "set_cmd_units" $args @@ -207,25 +207,25 @@ proc delete_objects_from_list_cmd { list objects } { # type. if {$list_is_object && ![is_object $obj]} { if {$list_type == "Clock"} { - set obj [find_clock $obj] + set obj [find_clock $obj] } elseif {$list_type == "Port"} { - set top_instance [top_instance] - set top_cell [$top_instance cell] - set obj [$top_cell find_port $obj] + set top_instance [top_instance] + set top_cell [$top_instance cell] + set obj [$top_cell find_port $obj] } elseif {$list_type == "Pin"} { - set obj [find_pin $obj] + set obj [find_pin $obj] } elseif {$list_type == "Instance"} { - set obj [find_instance $obj] + set obj [find_instance $obj] } elseif {$list_type == "Net"} { - set obj [find_net $obj] + set obj [find_net $obj] } elseif {$list_type == "LibertyLibrary"} { - set obj [find_liberty $obj] + set obj [find_liberty $obj] } elseif {$list_type == "LibertyCell"} { - set obj [find_liberty_cell $obj] + set obj [find_liberty_cell $obj] } elseif {$list_type == "LibertyPort"} { - set obj [get_lib_pins $obj] + set obj [get_lib_pins $obj] } else { - sta_error 164 "unsupported object type $list_type." + sta_error 164 "unsupported object type $list_type." } } set index [lsearch $list $obj] diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index 60c9cc2b7..65d9d9508 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -30,6 +30,122 @@ namespace eval sta { # ################################################################ +define_cmd_args "define_scene" {name -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]} + +proc define_scene { args } { + parse_key_args "define_scene" args \ + keys {-mode -liberty -liberty_min -liberty_max \ + -spef -spef_min -spef_max} \ + flags {} + + check_argc_eq1 "define_scene" $args + set name [lindex $args 0] + + if { [info exists keys(-mode)] } { + set mode_name $keys(-mode) + } else { + set mode_name [sta::cmd_mode_name] + } + + set liberty_min_files {} + set liberty_max_files {} + if { [info exists keys(-liberty)] } { + set liberty_files $keys(-liberty) + set liberty_min_files $liberty_files + set liberty_max_files $liberty_files + } else { + if { [info exists keys(-liberty_min)] && [info exists keys(-liberty_max)] } { + set liberty_min_files $keys(-liberty_min) + set liberty_max_files $keys(-liberty_max) + } else { + sta_error 483 "-liberty or -liberty_min and -liberty_max are required arguments." + } + } + + set spef_min_file "" + set spef_max_file "" + if { [info exists keys(-spef)] } { + set spef_file $keys(-spef) + set spef_min_file $spef_file + set spef_max_file $spef_file + } elseif { [info exists keys(-spef_min)] && [info exists keys(-spef_max)] } { + set spef_min_file $keys(-spef_min) + set spef_max_file $keys(-spef_max) + } elseif { [info exists keys(-spef_min)] ||[info exists keys(-spef_max)] } { + sta_error 484 "-spef_min and -spef_max are required arguments." + } + + define_scene_cmd $name $mode_name \ + $liberty_min_files $liberty_max_files \ + $spef_min_file $spef_max_file +} + +# deprecated 11/22/2025 +define_cmd_args "define_corners" { corner1 [corner2]... } + +proc define_corners { args } { + if { [get_libs -quiet *] != {} } { + sta_error 482 "define_corners must be called before read_liberty." + } + if { [llength $args] == 0 } { + sta_error 577 "define_corners must define at least one corner." + } + define_scenes_cmd $args +} + +################################################################ + +define_cmd_args "set_scene" {scene_name} + +proc set_scene { args } { + check_argc_eq1 "set_scene" $args + set_scene_cmd [lindex $args 0] +} + +################################################################ + +define_cmd_args "get_scenes" {[-modes mode_names] scene_names} + +proc get_scenes { args } { + parse_key_args "get_scenes" args keys {-modes} flags {} + check_argc_eq0or1 "get_scenes" $args + + if { [llength $args] == 0 } { + set scene_name "*" + } else { + set scene_name [lindex $args 0] + } + set mode_names {} + if { [info exists keys(-modes)] } { + set mode_names $keys(-modes) + return [find_mode_scenes_matching $scene_name $mode_names] + } else { + return [find_scenes_matching $scene_name] + } +} + +################################################################ + +define_cmd_args "get_modes" {mode_name} + +proc get_modes { args } { + return [find_modes [lindex $args 0]] +} + +################################################################ + +define_cmd_args "set_mode" {mode_name} + +proc set_mode { args } { + check_argc_eq1 "set_mode" $args + set_mode_cmd [lindex $args 0] +} + +################################################################ + define_cmd_args "get_fanin" \ {-to sink_list [-flat] [-only_cells] [-startpoints_only]\ [-levels level_count] [-pin_levels pin_count]\ @@ -85,10 +201,10 @@ proc get_fanin { args } { } if { $only_insts } { return [find_fanin_insts $pins $flat $startpoints_only \ - $inst_levels $pin_levels $thru_disabled $thru_constants] + $inst_levels $pin_levels $thru_disabled $thru_constants] } else { return [find_fanin_pins $pins $flat $startpoints_only \ - $inst_levels $pin_levels $thru_disabled $thru_constants] + $inst_levels $pin_levels $thru_disabled $thru_constants] } } @@ -143,10 +259,10 @@ proc get_fanout { args } { } if { $only_insts } { return [find_fanout_insts $pins $flat $endpoints_only \ - $inst_levels $pin_levels $thru_disabled $thru_constants] + $inst_levels $pin_levels $thru_disabled $thru_constants] } else { return [find_fanout_pins $pins $flat $endpoints_only \ - $inst_levels $pin_levels $thru_disabled $thru_constants] + $inst_levels $pin_levels $thru_disabled $thru_constants] } } @@ -167,15 +283,15 @@ proc get_timing_edges_cmd { cmd cmd_args } { set arcs {} if { [info exists keys(-of_objects)] } { if { [info exists keys(-from)] \ - || [info exists keys(-from)] } { + || [info exists keys(-from)] } { sta_error 540 "-from/-to arguments not supported with -of_objects." } set arcs [get_timing_arcs_objects $keys(-of_objects)] } elseif { [info exists keys(-from)] \ - && [info exists keys(-to)] } { + && [info exists keys(-to)] } { set arcs [get_timing_arcs_from_to \ - [get_port_pin_error "from" $keys(-from)] \ - [get_port_pin_error "to" $keys(-to)]] + [get_port_pin_error "from" $keys(-from)] \ + [get_port_pin_error "to" $keys(-to)]] } elseif { [info exists keys(-from)] } { set arcs [get_timing_arcs_from $keys(-from)] } elseif { [info exists keys(-to)] } { @@ -214,10 +330,10 @@ proc instance_edges { inst } { foreach vertex [$pin vertices] { set edge_iter [$vertex out_edge_iterator] while {[$edge_iter has_next]} { - set edge [$edge_iter next] - if { [$edge role] != "wire" } { - lappend edges $edge - } + set edge [$edge_iter next] + if { [$edge role] != "wire" } { + lappend edges $edge + } } $edge_iter finish } @@ -234,10 +350,10 @@ proc get_timing_arcs_from_to { from_pin_arg to_pin_arg } { foreach to_vertex [$to_pin vertices] { set edge_iter [$from_vertex out_edge_iterator] while {[$edge_iter has_next]} { - set edge [$edge_iter next] - if { [$edge to] == $to_vertex } { - lappend edges $edge - } + set edge [$edge_iter next] + if { [$edge to] == $to_vertex } { + lappend edges $edge + } } $edge_iter finish } @@ -304,7 +420,7 @@ proc report_clock1 { clk } { } else { set wave "" foreach edge $waveform { - set wave "$wave[format "%10s" [format_time $edge $digits]]" + set wave "$wave[format %10s [format_time $edge $digits]]" } } if {[$clk is_generated]} { diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index f4c0e3841..e5b68051a 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -30,7 +30,6 @@ #include "StringSet.hh" #include "StringSeq.hh" #include "PatternMatch.hh" -#include "Vector.hh" #include "Network.hh" #include "Liberty.hh" #include "FuncExpr.hh" @@ -40,7 +39,7 @@ #include "Graph.hh" #include "NetworkClass.hh" #include "Clock.hh" -#include "Corner.hh" +#include "Scene.hh" #include "Search.hh" #include "Path.hh" #include "search/Tag.hh" @@ -53,15 +52,15 @@ namespace sta { -typedef MinPulseWidthCheckSeq::Iterator MinPulseWidthCheckSeqIterator; typedef MinMaxAll MinMaxAllNull; +typedef std::vector StdStringSeq; #if TCL_MAJOR_VERSION < 9 typedef int Tcl_Size; #endif template -Vector * +std::vector * tclListSeqPtr(Tcl_Obj *const source, swig_type_info *swig_type, Tcl_Interp *interp) @@ -71,7 +70,7 @@ tclListSeqPtr(Tcl_Obj *const source, if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK && argc > 0) { - Vector *seq = new Vector; + std::vector *seq = new std::vector; for (int i = 0; i < argc; i++) { void *obj; // Ignore returned TCL_ERROR because can't get swig_type_info. @@ -276,8 +275,8 @@ using namespace sta; //////////////////////////////////////////////////////////////// // String that is deleted after crossing over to tcland. -%typemap(out) string { - string &str = $1; +%typemap(out) std::string { + std::string &str = $1; // String is volatile because it is deleted. Tcl_SetResult(interp, const_cast(str.c_str()), TCL_VOLATILE); } @@ -302,6 +301,14 @@ using namespace sta; $1 = tclListSetStdString($input, interp); } +%typemap(in) StdStringSeq { + $1 = tclListSeqStdString($input, interp); +} + +%typemap(in) StdStringSeq* { + $1 = tclListSeqStdString($input, interp); +} + %typemap(out) StringSeq* { StringSeq *strs = $1; Tcl_Obj *list = Tcl_NewListObj(0, nullptr); @@ -322,6 +329,14 @@ using namespace sta; Tcl_SetObjResult(interp, list); } +%typemap(in) StdStringSet* { + $1 = tclListSetStdString($input, interp); +} + +%typemap(in) StdStringSeq { + $1 = tclListSeqStdString($input, interp); +} + %typemap(out) StdStringSeq { StdStringSeq &strs = $1; Tcl_Obj *list = Tcl_NewListObj(0, nullptr); @@ -578,6 +593,10 @@ using namespace sta; $1 = tclListSeqPtr($input, SWIGTYPE_p_Instance, interp); } +%typemap(in) InstanceSeq { + $1 = tclListSeq($input, SWIGTYPE_p_Instance, interp); + } + %typemap(out) InstanceSeq { seqTclList($1, SWIGTYPE_p_Instance, interp); } @@ -922,7 +941,7 @@ using namespace sta; || stringEqual(arg, "min")) $1 = const_cast(MinMax::min()); else if (stringEqual(arg, "setup") - || stringEqual(arg, "max")) + || stringEqual(arg, "max")) $1 = const_cast(MinMax::max()); else { tclArgError(interp, 2162, "%s not setup, hold, min or max.", arg); @@ -939,13 +958,14 @@ using namespace sta; || stringEqual(arg, "min")) $1 = const_cast(SetupHoldAll::min()); else if (stringEqual(arg, "setup") - || stringEqual(arg, "max")) + || stringEqual(arg, "max")) $1 = const_cast(SetupHoldAll::max()); else if (stringEqual(arg, "setup_hold") - || stringEqual(arg, "min_max")) + || stringEqual(arg, "min_max")) $1 = const_cast(SetupHoldAll::all()); else { - tclArgError(interp, 2163, "%s not setup, hold, setup_hold, min, max or min_max.", arg); + tclArgError(interp, 2163, "%s not setup, hold, setup_hold, min, max or min_max.", + arg); return TCL_ERROR; } } @@ -1109,16 +1129,12 @@ using namespace sta; %typemap(out) CheckErrorSeq & { Tcl_Obj *error_list = Tcl_NewListObj(0, nullptr); CheckErrorSeq *check_errors = $1; - CheckErrorSeq::Iterator check_iter(check_errors); - while (check_iter.hasNext()) { - CheckError *error = check_iter.next(); + for (CheckError *error : *check_errors) { Tcl_Obj *string_list = Tcl_NewListObj(0, nullptr); - CheckError::Iterator string_iter(error); - while (string_iter.hasNext()) { - const char *str = string_iter.next(); + for (const char *str : *error) { size_t str_len = strlen(str); Tcl_Obj *obj = Tcl_NewStringObj(const_cast(str), - static_cast(str_len)); + static_cast(str_len)); Tcl_ListObjAppendElement(interp, string_list, obj); } Tcl_ListObjAppendElement(interp, error_list, string_list); @@ -1153,11 +1169,6 @@ using namespace sta; seqTclList($1, SWIGTYPE_p_PathEnd, interp); } -%typemap(out) MinPulseWidthCheckSeqIterator* { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); -} - %typemap(out) PathSeq* { Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); Tcl_SetObjResult(interp, obj); @@ -1174,21 +1185,6 @@ using namespace sta; Tcl_SetObjResult(interp, list); } -%typemap(out) MinPulseWidthCheck* { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); -} - -%typemap(out) MinPulseWidthCheckSeq & { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); -} - -%typemap(out) MinPulseWidthCheckSeqIterator & { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); -} - %typemap(out) VertexPathIterator* { Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); Tcl_SetObjResult(interp, obj); @@ -1247,24 +1243,107 @@ using namespace sta; Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); } -%typemap(in) PathGroupNameSet* { +%typemap(in) StringSet* { $1 = tclListSetConstChar($input, interp); } -%typemap(in) StringSet* { - $1 = tclListSetConstChar($input, interp); +%typemap(out) Mode* { + const Mode *mode = $1; + if (mode) + Tcl_SetResult(interp, const_cast($1->name().c_str()), TCL_VOLATILE); + else + Tcl_SetResult(interp, const_cast("NULL"), TCL_STATIC); } -%typemap(out) Corner* { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); +%typemap(in) ModeSeq { + Tcl_Size argc; + Tcl_Obj **argv; + + Sta *sta = Sta::sta(); + std::vector seq; + if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK + && argc > 0) { + for (int i = 0; i < argc; i++) { + int length; + const char *mode_name = Tcl_GetStringFromObj(argv[i], &length); + Mode *mode = sta->findMode(mode_name); + if (mode) + seq.push_back(mode); + else { + tclArgError(interp, 2174, "mode %s not found.", mode_name); + return TCL_ERROR; + } + } + } + $1 = seq; +} + +%typemap(out) ModeSeq { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + ModeSeq &modes = $1; + for (Mode *mode : modes) { + const std::string &mode_name = mode->name(); + Tcl_Obj *obj = Tcl_NewStringObj(mode_name.c_str(), mode_name.size()); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); +} + +%typemap(in) Scene* { + sta::Sta *sta = Sta::sta(); + int length; + char *scene_name = Tcl_GetStringFromObj($input, &length); + // parse_scene_or_all support depreated 11/21/2025 + if (stringEq(scene_name, "NULL")) + $1 = nullptr; + else { + Scene *scene = sta->findScene(scene_name); + if (scene) + $1 = scene; + else { + tclArgError(interp, 2173, "scene %s not found.", scene_name); + return TCL_ERROR; + } + } +} + +%typemap(out) Scene* { + const Scene *scene = $1; + if (scene) + Tcl_SetResult(interp, const_cast($1->name().c_str()), TCL_VOLATILE); + else + Tcl_SetResult(interp, const_cast("NULL"), TCL_STATIC); +} + +%typemap(in) SceneSeq { + Tcl_Size argc; + Tcl_Obj **argv; + + Sta *sta = Sta::sta(); + std::vector seq; + if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK + && argc > 0) { + for (int i = 0; i < argc; i++) { + int length; + const char *scene_name = Tcl_GetStringFromObj(argv[i], &length); + Scene *scene = sta->findScene(scene_name); + if (scene) + seq.push_back(scene); + else { + tclArgError(interp, 2172, "scene %s not found.", scene_name); + return TCL_ERROR; + } + } + } + $1 = seq; } -%typemap(out) Corners* { +%typemap(out) SceneSeq { Tcl_Obj *list = Tcl_NewListObj(0, nullptr); - Corners *corners = $1; - for (Corner *corner : *corners) { - Tcl_Obj *obj = SWIG_NewInstanceObj(corner, SWIGTYPE_p_Corner, false); + SceneSeq &scenes = $1; + for (Scene *scene : scenes) { + const std::string &scene_name = scene->name(); + Tcl_Obj *obj = Tcl_NewStringObj(scene_name.c_str(), scene_name.size()); Tcl_ListObjAppendElement(interp, list, obj); } Tcl_SetObjResult(interp, list); @@ -1279,108 +1358,103 @@ using namespace sta; %typemap(out) PropertyValue { PropertyValue value = $1; switch (value.type()) { - case PropertyValue::Type::type_none: + case PropertyValue::Type::none: Tcl_SetResult(interp, const_cast(""), TCL_STATIC); break; - case PropertyValue::Type::type_string: + case PropertyValue::Type::string: Tcl_SetResult(interp, const_cast(value.stringValue()), TCL_VOLATILE); break; - case PropertyValue::Type::type_float: { + 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); } break; - case PropertyValue::Type::type_bool: { + case PropertyValue::Type::bool_: { const char *bool_string = value.boolValue() ? "1" : "0"; Tcl_SetResult(interp, const_cast(bool_string), TCL_STATIC); } break; - case PropertyValue::Type::type_library: { + case PropertyValue::Type::library: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.library()), - SWIGTYPE_p_Library, false); + SWIGTYPE_p_Library, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_cell: { + case PropertyValue::Type::cell: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.cell()), - SWIGTYPE_p_Cell, false); + SWIGTYPE_p_Cell, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_port: { + case PropertyValue::Type::port: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.port()), - SWIGTYPE_p_Port, false); + SWIGTYPE_p_Port, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_liberty_library: { + case PropertyValue::Type::liberty_library: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.libertyLibrary()), - SWIGTYPE_p_LibertyLibrary, false); + SWIGTYPE_p_LibertyLibrary, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_liberty_cell: { + case PropertyValue::Type::liberty_cell: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.libertyCell()), - SWIGTYPE_p_LibertyCell, false); + SWIGTYPE_p_LibertyCell, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_liberty_port: { + case PropertyValue::Type::liberty_port: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.libertyPort()), - SWIGTYPE_p_LibertyPort, false); + SWIGTYPE_p_LibertyPort, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_instance: { + case PropertyValue::Type::instance: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.instance()), - SWIGTYPE_p_Instance, false); + SWIGTYPE_p_Instance, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_pin: { + case PropertyValue::Type::pin: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.pin()), SWIGTYPE_p_Pin, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_pins: { + case PropertyValue::Type::pins: { Tcl_Obj *list = Tcl_NewListObj(0, nullptr); - PinSeq *pins = value.pins(); - PinSeq::Iterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + for (const Pin *pin : *value.pins()) { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(pin), SWIGTYPE_p_Pin, false); Tcl_ListObjAppendElement(interp, list, obj); } Tcl_SetObjResult(interp, list); } break; - case PropertyValue::Type::type_net: { + case PropertyValue::Type::net: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.net()), - SWIGTYPE_p_Net, false); + SWIGTYPE_p_Net, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_clk: { + case PropertyValue::Type::clk: { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(value.clock()), - SWIGTYPE_p_Clock, false); + SWIGTYPE_p_Clock, false); Tcl_SetObjResult(interp, obj); } break; - case PropertyValue::Type::type_clks: { + case PropertyValue::Type::clks: { Tcl_Obj *list = Tcl_NewListObj(0, nullptr); ClockSeq *clks = value.clocks(); - ClockSeq::Iterator clk_iter(clks); - while (clk_iter.hasNext()) { - Clock *clk = clk_iter.next(); + for (Clock *clk : *clks) { Tcl_Obj *obj = SWIG_NewInstanceObj(clk, SWIGTYPE_p_Clock, false); Tcl_ListObjAppendElement(interp, list, obj); } Tcl_SetObjResult(interp, list); } break; - case PropertyValue::Type::type_paths: { + case PropertyValue::Type::paths: { Tcl_Obj *list = Tcl_NewListObj(0, nullptr); for (const Path *path : *value.paths()) { Tcl_Obj *obj = SWIG_NewInstanceObj(const_cast(path), SWIGTYPE_p_Path, false); @@ -1389,7 +1463,7 @@ using namespace sta; Tcl_SetObjResult(interp, list); } break; - case PropertyValue::Type::type_pwr_activity: { + case PropertyValue::Type::pwr_activity: { PwrActivity activity = value.pwrActivity(); Tcl_Obj *list = Tcl_NewListObj(0, nullptr); Tcl_Obj *obj; diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index f5d734423..c5cc219dd 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -32,7 +32,7 @@ namespace sta { StringSet * tclListSetConstChar(Tcl_Obj *const source, - Tcl_Interp *interp) + Tcl_Interp *interp) { Tcl_Size argc; Tcl_Obj **argv; @@ -52,7 +52,7 @@ tclListSetConstChar(Tcl_Obj *const source, StringSeq * tclListSeqConstChar(Tcl_Obj *const source, - Tcl_Interp *interp) + Tcl_Interp *interp) { Tcl_Size argc; Tcl_Obj **argv; @@ -70,9 +70,47 @@ tclListSeqConstChar(Tcl_Obj *const source, return nullptr; } +StdStringSeq +tclListSeqStdString(Tcl_Obj *const source, + Tcl_Interp *interp) +{ + Tcl_Size argc; + Tcl_Obj **argv; + + StdStringSeq seq; + if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { + for (int i = 0; i < argc; i++) { + int length; + const char *str = Tcl_GetStringFromObj(argv[i], &length); + seq.push_back(str); + } + } + return seq; +} + +StdStringSeq * +tclListSeqStdStringPtr(Tcl_Obj *const source, + Tcl_Interp *interp) +{ + Tcl_Size argc; + Tcl_Obj **argv; + + if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { + StdStringSeq *seq = new StdStringSeq; + for (int i = 0; i < argc; i++) { + int length; + const char *str = Tcl_GetStringFromObj(argv[i], &length); + seq->push_back(str); + } + return seq; + } + else + return nullptr; +} + StdStringSet * tclListSetStdString(Tcl_Obj *const source, - Tcl_Interp *interp) + Tcl_Interp *interp) { Tcl_Size argc; Tcl_Obj **argv; @@ -90,7 +128,6 @@ tclListSetStdString(Tcl_Obj *const source, return nullptr; } - void tclArgError(Tcl_Interp *interp, int id, @@ -107,10 +144,10 @@ tclArgError(Tcl_Interp *interp, void objectListNext(const char *list, - const char *type, - // Return values. - bool &type_match, - const char *&next) + const char *type, + // Return values. + bool &type_match, + const char *&next) { // Default return values (failure). type_match = false; @@ -122,21 +159,21 @@ objectListNext(const char *list, while (*s && isxdigit(*s)) s++; if ((s - list - 1) == sizeof(void*) * 2 - && *s && *s++ == '_' - && *s && *s++ == 'p' - && *s && *s++ == '_') { + && *s && *s++ == '_' + && *s && *s++ == 'p' + && *s && *s++ == '_') { const char *t = type; while (*s && *s != ' ') { - if (*s != *t) - return; - s++; - t++; + if (*s != *t) + return; + s++; + t++; } type_match = true; if (*s) - next = s + 1; + next = s + 1; else - next = nullptr; + next = nullptr; } } } diff --git a/tcl/Util.tcl b/tcl/Util.tcl index ad47cdce2..49028698c 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -37,7 +37,7 @@ namespace eval sta { # $flag_var(flag) -> 1 if the flag is present # Keys and flags are removed from arg_var in the caller. proc parse_key_args { cmd arg_var key_var keys {flag_var ""} {flags {}} \ - {unknown_key_is_error 1} } { + {unknown_key_is_error 1} } { upvar 1 $arg_var args upvar 1 $key_var key_value upvar 1 $flag_var flag_present @@ -47,41 +47,41 @@ proc parse_key_args { cmd arg_var key_var keys {flag_var ""} {flags {}} \ if { [is_keyword_arg $arg] } { set key_index [lsearch -exact $keys $arg] if { $key_index >= 0 } { - set key $arg - if { [llength $args] == 1 } { - sta_error 560 "$cmd $key missing value." - } - set key_value($key) [lindex $args 1] - set args [lrange $args 1 end] + set key $arg + if { [llength $args] == 1 } { + sta_error 560 "$cmd $key missing value." + } + set key_value($key) [lindex $args 1] + set args [lrange $args 1 end] } else { - set flag_index [lsearch -exact $flags $arg] - if { $flag_index >= 0 } { - set flag_present($arg) 1 - } else { - # No exact keyword/flag match found. - # Try finding a keyword/flag that begins with - # the same substring. - set wild_arg "${arg}*" - set key_index [lsearch -glob $keys $wild_arg] - if { $key_index >= 0 } { - set key [lindex $keys $key_index] - if { [llength $args] == 1 } { - sta_error 561 "$cmd $key missing value." - } - set key_value($key) [lindex $args 1] - set args [lrange $args 1 end] - } else { - set flag_index [lsearch -glob $flags $wild_arg] - if { $flag_index >= 0 } { - set flag [lindex $flags $flag_index] - set flag_present($flag) 1 - } elseif { $unknown_key_is_error } { - sta_error 562 "$cmd $arg is not a known keyword or flag." - } else { - lappend args_rtn $arg - } - } - } + set flag_index [lsearch -exact $flags $arg] + if { $flag_index >= 0 } { + set flag_present($arg) 1 + } else { + # No exact keyword/flag match found. + # Try finding a keyword/flag that begins with + # the same substring. + set wild_arg "${arg}*" + set key_index [lsearch -glob $keys $wild_arg] + if { $key_index >= 0 } { + set key [lindex $keys $key_index] + if { [llength $args] == 1 } { + sta_error 561 "$cmd $key missing value." + } + set key_value($key) [lindex $args 1] + set args [lrange $args 1 end] + } else { + set flag_index [lsearch -glob $flags $wild_arg] + if { $flag_index >= 0 } { + set flag [lindex $flags $flag_index] + set flag_present($flag) 1 + } elseif { $unknown_key_is_error } { + sta_error 562 "$cmd $arg is not a known keyword or flag." + } else { + lappend args_rtn $arg + } + } + } } } else { lappend args_rtn $arg @@ -109,8 +109,8 @@ proc check_for_key_args { cmd arg_var } { proc is_keyword_arg { arg } { if { [string length $arg] >= 2 \ - && [string index $arg 0] == "-" \ - && [string is alpha [string index $arg 1]] } { + && [string index $arg 0] == "-" \ + && [string is alpha [string index $arg 1]] } { return 1 } else { return 0 @@ -124,11 +124,11 @@ proc is_keyword_arg { arg } { # The value of the last expression in the body is returned. proc proc_redirect { proc_name body } { set proc_body [concat "proc $proc_name { args } {" \ - "global errorCode errorInfo;" \ - "set redirect \[parse_redirect_args args\];" \ - "set code \[catch {" $body "} ret \];" \ - "if {\$redirect} { redirect_file_end };" \ - "if {\$code == 1} {return -code \$code -errorcode \$errorCode -errorinfo \$errorInfo \$ret} else {return \$ret} }" ] + "global errorCode errorInfo;" \ + "set redirect \[parse_redirect_args args\];" \ + "set code \[catch {" $body "} ret \];" \ + "if {\$redirect} { redirect_file_end };" \ + "if {\$code == 1} {return -code \$code -errorcode \$errorCode -errorinfo \$errorInfo \$ret} else {return \$ret} }" ] eval $proc_body } diff --git a/tcl/Variables.tcl b/tcl/Variables.tcl index a019c2959..336e72865 100644 --- a/tcl/Variables.tcl +++ b/tcl/Variables.tcl @@ -41,7 +41,7 @@ proc trace_report_default_digits { name1 name2 op } { if { $op == "w" } { if { !([string is integer $sta_report_default_digits] \ - && $sta_report_default_digits >= 0) } { + && $sta_report_default_digits >= 0) } { sta_error 590 "sta_report_default_digits must be a positive integer." } } @@ -96,14 +96,6 @@ proc trace_internal_bidirect_instance_paths_enabled { name1 name2 op } { bidirect_inst_paths_enabled set_bidirect_inst_paths_enabled } -trace variable ::sta_bidirect_net_paths_enabled "rw" \ - sta::trace_bidirect_net_paths_enabled - -proc trace_bidirect_net_paths_enabled { name1 name2 op } { - trace_boolean_var $op ::sta_bidirect_net_paths_enabled \ - bidirect_net_paths_enabled set_bidirect_net_paths_enabled -} - trace variable ::sta_clock_through_tristate_enabled "rw" \ sta::trace_clock_through_tristate_enabled diff --git a/test/disconnect_mcp_pin.ok b/test/disconnect_mcp_pin.ok index a0645c2ba..c05e6a3b0 100644 --- a/test/disconnect_mcp_pin.ok +++ b/test/disconnect_mcp_pin.ok @@ -1,7 +1,7 @@ -Warning: disconnect_mcp_pin.tcl line 15, 'u0/A' is not a valid endpoint. -Warning: disconnect_mcp_pin.tcl line 16, 'u1/A' is not a valid endpoint. -Warning: disconnect_mcp_pin.tcl line 17, 'u0/A' is not a valid endpoint. -Warning: disconnect_mcp_pin.tcl line 18, 'u1/A' is not a valid endpoint. +Warning 1551: disconnect_mcp_pin.tcl line 15, 'u0/A' is not a valid endpoint. +Warning 1551: disconnect_mcp_pin.tcl line 16, 'u1/A' is not a valid endpoint. +Warning 1551: disconnect_mcp_pin.tcl line 17, 'u0/A' is not a valid endpoint. +Warning 1551: disconnect_mcp_pin.tcl line 18, 'u1/A' is not a valid endpoint. Startpoint: data_in[1] (input port clocked by clk) Endpoint: u1 (falling edge-triggered data to data check clocked by clk) Path Group: clk diff --git a/test/liberty_arcs_one2one_1.ok b/test/liberty_arcs_one2one_1.ok index 64c906ffd..dc99d32b1 100644 --- a/test/liberty_arcs_one2one_1.ok +++ b/test/liberty_arcs_one2one_1.ok @@ -1,4 +1,4 @@ -Warning: liberty_arcs_one2one_1.lib line 48, timing port A and related port Y are different sizes. +Warning 1216: liberty_arcs_one2one_1.lib line 48, timing port A and related port Y are different sizes. report_edges -from partial_wide_inv_cell/A[0] A[0] -> Y[0] combinational ^ -> v 1.00:1.00 diff --git a/test/liberty_arcs_one2one_2.ok b/test/liberty_arcs_one2one_2.ok index 879b8a747..323145d37 100644 --- a/test/liberty_arcs_one2one_2.ok +++ b/test/liberty_arcs_one2one_2.ok @@ -1,4 +1,4 @@ -Warning: liberty_arcs_one2one_2.lib line 48, timing port A and related port Y are different sizes. +Warning 1216: liberty_arcs_one2one_2.lib line 48, timing port A and related port Y are different sizes. report_edges -to partial_wide_inv_cell/Y[0] A[0] -> Y[0] combinational ^ -> v 1.00:1.00 diff --git a/test/mcmm3.ok b/test/mcmm3.ok new file mode 100644 index 000000000..e220a8819 --- /dev/null +++ b/test/mcmm3.ok @@ -0,0 +1,208 @@ +Startpoint: r2 (rising edge-triggered flip-flop clocked by m1_clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by m1_clk) +Path Group: m1_clk +Path Type: max +Mode: mode1 +Corner: scene1 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m1_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 52.65 52.65 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 49.30 101.95 ^ u1/Y (BUFx2_ASAP7_75t_R) + 61.03 162.97 ^ u2/Y (AND2x2_ASAP7_75t_R) + 15.77 178.74 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 178.74 data arrival time + +1000.00 1000.00 clock m1_clk (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -13.66 986.34 library setup time + 986.34 data required time +--------------------------------------------------------- + 986.34 data required time + -178.74 data arrival time +--------------------------------------------------------- + 807.59 slack (MET) + + +Startpoint: r1 (rising edge-triggered flip-flop clocked by m2_clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by m2_clk) +Path Group: m2_clk +Path Type: max +Mode: mode2 +Corner: scene2 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m2_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R) + 123.56 123.56 ^ r1/Q (DFFHQx4_ASAP7_75t_R) + 112.01 235.57 ^ u2/Y (AND2x2_ASAP7_75t_R) + 22.14 257.71 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 257.71 data arrival time + + 500.00 500.00 clock m2_clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -40.66 459.34 library setup time + 459.34 data required time +--------------------------------------------------------- + 459.34 data required time + -257.71 data arrival time +--------------------------------------------------------- + 201.62 slack (MET) + + +Startpoint: r2 (rising edge-triggered flip-flop clocked by m1_clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by m1_clk) +Path Group: m1_clk +Path Type: max +Mode: mode1 +Corner: scene1 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m1_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 52.65 52.65 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 49.30 101.95 ^ u1/Y (BUFx2_ASAP7_75t_R) + 61.03 162.97 ^ u2/Y (AND2x2_ASAP7_75t_R) + 15.77 178.74 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 178.74 data arrival time + +1000.00 1000.00 clock m1_clk (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -13.66 986.34 library setup time + 986.34 data required time +--------------------------------------------------------- + 986.34 data required time + -178.74 data arrival time +--------------------------------------------------------- + 807.59 slack (MET) + + +Startpoint: in1 (input port clocked by m1_clk) +Endpoint: r1 (rising edge-triggered flip-flop clocked by m1_clk) +Path Group: m1_clk +Path Type: max +Mode: mode1 +Corner: scene1 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m1_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 100.00 100.00 ^ input external delay + 0.00 100.00 ^ in1 (in) + 12.28 112.28 ^ r1/D (DFFHQx4_ASAP7_75t_R) + 112.28 data arrival time + +1000.00 1000.00 clock m1_clk (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R) + -12.80 987.20 library setup time + 987.20 data required time +--------------------------------------------------------- + 987.20 data required time + -112.28 data arrival time +--------------------------------------------------------- + 874.92 slack (MET) + + +Startpoint: in2 (input port clocked by m1_clk) +Endpoint: r2 (rising edge-triggered flip-flop clocked by m1_clk) +Path Group: m1_clk +Path Type: max +Mode: mode1 +Corner: scene1 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m1_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 100.00 100.00 ^ input external delay + 0.00 100.00 ^ in2 (in) + 12.28 112.28 ^ r2/D (DFFHQx4_ASAP7_75t_R) + 112.28 data arrival time + +1000.00 1000.00 clock m1_clk (rise edge) + 0.00 1000.00 clock network delay (ideal) + 0.00 1000.00 clock reconvergence pessimism + 1000.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + -12.80 987.20 library setup time + 987.20 data required time +--------------------------------------------------------- + 987.20 data required time + -112.28 data arrival time +--------------------------------------------------------- + 874.92 slack (MET) + + +Startpoint: r1 (rising edge-triggered flip-flop clocked by m2_clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by m2_clk) +Path Group: m2_clk +Path Type: max +Mode: mode2 +Corner: scene2 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m2_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R) + 123.56 123.56 ^ r1/Q (DFFHQx4_ASAP7_75t_R) + 112.01 235.57 ^ u2/Y (AND2x2_ASAP7_75t_R) + 22.14 257.71 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 257.71 data arrival time + + 500.00 500.00 clock m2_clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -40.66 459.34 library setup time + 459.34 data required time +--------------------------------------------------------- + 459.34 data required time + -257.71 data arrival time +--------------------------------------------------------- + 201.62 slack (MET) + + +Startpoint: r3 (rising edge-triggered flip-flop clocked by m2_clk) +Endpoint: out (output port clocked by m2_clk) +Path Group: m2_clk +Path Type: max +Mode: mode2 +Corner: scene2 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock m2_clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + 123.30 123.30 ^ r3/Q (DFFHQx4_ASAP7_75t_R) + 19.50 142.80 ^ out (out) + 142.80 data arrival time + + 500.00 500.00 clock m2_clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism +-100.00 400.00 output external delay + 400.00 data required time +--------------------------------------------------------- + 400.00 data required time + -142.80 data arrival time +--------------------------------------------------------- + 257.20 slack (MET) + + diff --git a/test/power.ok b/test/power.ok index 19f4adfdd..a79a9ab6e 100644 --- a/test/power.ok +++ b/test/power.ok @@ -1,12 +1,12 @@ -Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. +Warning 198: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. Group Internal Switching Leakage Total Power Power Power Power (Watts) ---------------------------------------------------------------- -Sequential 3.07e-04 4.76e-05 2.96e-10 3.54e-04 40.0% -Combinational 1.59e-04 2.05e-04 6.86e-10 3.64e-04 41.1% -Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 18.9% +Sequential 3.06e-04 4.71e-05 2.96e-10 3.53e-04 40.1% +Combinational 1.57e-04 2.03e-04 6.86e-10 3.60e-04 40.9% +Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 19.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 5.12e-04 3.73e-04 1.00e-09 8.85e-04 100.0% - 57.8% 42.2% 0.0% +Total 5.10e-04 3.70e-04 1.00e-09 8.81e-04 100.0% + 57.9% 42.1% 0.0% diff --git a/test/power_json.tcl b/test/power_json.tcl index 7edf1d3c0..108b81b2f 100644 --- a/test/power_json.tcl +++ b/test/power_json.tcl @@ -1,4 +1,4 @@ -# report_power reg1_asap7 +# report_power json read_liberty asap7_small.lib.gz read_verilog reg1_asap7.v link_design top diff --git a/test/power_vcd.ok b/test/power_vcd.ok index 47a160029..5733375e8 100644 --- a/test/power_vcd.ok +++ b/test/power_vcd.ok @@ -1,4 +1,4 @@ -Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. +Warning 198: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. Annotated 937 pin activities. vcd 937 unannotated 0 diff --git a/test/prima3.ok b/test/prima3.ok index 98c9c3cd6..a4ac1d9c8 100644 --- a/test/prima3.ok +++ b/test/prima3.ok @@ -1,13 +1,3 @@ -Warning: asap7_simple.lib.gz line 71029, timing group from output port. -Warning: asap7_simple.lib.gz line 71505, timing group from output port. -Warning: asap7_simple.lib.gz line 71981, timing group from output port. -Warning: asap7_simple.lib.gz line 72457, timing group from output port. -Warning: asap7_simple.lib.gz line 72933, timing group from output port. -Warning: asap7_simple.lib.gz line 73409, timing group from output port. -Warning: asap7_simple.lib.gz line 73885, timing group from output port. -Warning: asap7_simple.lib.gz line 81795, timing group from output port. -Warning: asap7_simple.lib.gz line 82271, timing group from output port. -Warning: asap7_simple.lib.gz line 82747, timing group from output port. Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) Path Group: clk diff --git a/test/regression.tcl b/test/regression.tcl index a6a86f978..447a85adc 100755 --- a/test/regression.tcl +++ b/test/regression.tcl @@ -84,8 +84,8 @@ proc parse_args {} { } elseif { $arg == "-threads" } { set threads [lindex $argv 1] if { !([string is integer $threads] || $threads == "max") } { - puts "Error: -threads arg $threads is not an integer or max." - exit 0 + puts "Error: -threads arg $threads is not an integer or max." + exit 0 } lappend app_options "-threads" lappend app_options $threads @@ -131,12 +131,12 @@ proc expand_tests { argv } { if { [info exists test_groups($arg)] } { set tests [concat $tests $test_groups($arg)] } elseif { [string first "*" $arg] != -1 \ - || [string first "?" $arg] != -1 } { + || [string first "?" $arg] != -1 } { # Find wildcard matches. foreach test [group_tests "all"] { - if [string match $arg $test] { - lappend tests $test - } + if [string match $arg $test] { + lappend tests $test + } } } elseif { [lsearch [group_tests "all"] $arg] != -1 } { lappend tests $arg @@ -175,52 +175,52 @@ proc run_test { test } { puts " *ERROR* [lrange $test_errors 1 end]" append_failure $test incr errors(error) - + # For some reason seg faults aren't echoed in the log - add them. if { [llength $test_errors] > 1 && [file exists $log_file] } { - set log_ch [open $log_file "a"] - puts $log_ch $test_errors - close $log_ch + set log_ch [open $log_file "a"] + puts $log_ch $test_errors + close $log_ch } # Report partial log diff anyway. if [file exists $ok_file] { - catch [concat exec diff $diff_options $ok_file $log_file \ - >> $diff_file] + catch [concat exec diff $diff_options $ok_file $log_file \ + >> $diff_file] } } else { set error_msg "" if { [lsearch $test_errors "MEMORY"] != -1 } { - append error_msg " *MEMORY*" - append_failure $test - incr errors(memory) + append error_msg " *MEMORY*" + append_failure $test + incr errors(memory) } if { [lsearch $test_errors "LEAK"] != -1 } { - append error_msg " *LEAK*" - append_failure $test - incr errors(leak) + append error_msg " *LEAK*" + append_failure $test + incr errors(leak) } if { $report_stats } { - append error_msg " [test_stats_summary $test]" + append error_msg " [test_stats_summary $test]" } if [file exists $ok_file] { - # Filter dos '/r's from log file. - set tmp_file [file join $result_dir $test.tmp] - exec tr -d "\r" < $log_file > $tmp_file - file rename -force $tmp_file $log_file - if [catch [concat exec diff $diff_options $ok_file $log_file \ - >> $diff_file]] { - puts " *FAIL*$error_msg" - append_failure $test - incr errors(fail) - } else { - puts " pass$error_msg" - } + # Filter dos '/r's from log file. + set tmp_file [file join $result_dir $test.tmp] + exec tr -d "\r" < $log_file > $tmp_file + file rename -force $tmp_file $log_file + if [catch [concat exec diff $diff_options $ok_file $log_file \ + >> $diff_file]] { + puts " *FAIL*$error_msg" + append_failure $test + incr errors(fail) + } else { + puts " pass$error_msg" + } } else { puts " *NO OK FILE*" - append_failure $test - incr errors(no_ok) + append_failure $test + incr errors(no_ok) } } } else { @@ -282,7 +282,6 @@ proc run_test_app { test cmd_file log_file } { proc run_test_plain { test cmd_file log_file } { global app_path app_options result_dir errorCode global report_stats - global test_expect_eror if { ![file exists $app_path] } { return "ERROR $app_path not found." @@ -299,25 +298,22 @@ proc run_test_plain { test cmd_file log_file } { } close $run_stream - if { [catch [concat exec $app_path $app_options $run_file >& $log_file]] \ - && ![info exists test_expect_eror($test)] } { + if { [catch [concat exec $app_path $app_options $run_file >& $log_file]] } { set signal [lindex $errorCode 2] set error [lindex $errorCode 3] # Error strings are not consistent across platforms but signal # names are. if { $signal == "SIGSEGV" } { - # Save corefiles to regression results directory. - set pid [lindex $errorCode 1] - set sys_corefile [test_sys_core_file $test $pid] - if { [file exists $sys_corefile] } { - file copy $sys_corefile [test_core_file $test] - } + # Save corefiles to regression results directory. + set pid [lindex $errorCode 1] + set sys_corefile [test_sys_core_file $test $pid] + if { [file exists $sys_corefile] } { + file copy $sys_corefile [test_core_file $test] + } } - cleanse_logfile $test $log_file return "ERROR $error" } file delete $run_file - cleanse_logfile $test $log_file return "" } } @@ -333,13 +329,12 @@ proc run_test_valgrind { test cmd_file log_file } { close $vg_stream set cmd [concat exec valgrind $valgrind_options \ - $app_path $app_options $vg_cmd_file >& $log_file] + $app_path $app_options $vg_cmd_file >& $log_file] set error_msg "" if { [catch $cmd] } { set error_msg "ERROR [lindex $errorCode 3]" } file delete $vg_cmd_file - cleanse_logfile $test $log_file set error_msg [concat $error_msg [cleanse_valgrind_logfile $test $log_file]] return $error_msg } @@ -386,13 +381,13 @@ proc cleanse_valgrind_logfile { test log_file } { if {[regexp "^==" $line]} { puts $valgrind $line if {[regexp $valgrind_leak_regexp $line]} { - set leaks 1 + set leaks 1 } if {[regexp $valgrind_mem_regexp $line]} { - set mem_errors 1 + set mem_errors 1 } if {[regexp $valgrind_shared_lib_failure_regexp $line]} { - set valgrind_shared_lib_failure 1 + set valgrind_shared_lib_failure 1 } } elseif {[regexp {^--[0-9]+} $line]} { # Valgrind notification line. @@ -459,8 +454,8 @@ proc found_errors {} { global errors return [expr $errors(error) != 0 || $errors(fail) != 0 \ - || $errors(no_cmd) != 0 || $errors(no_ok) != 0 \ - || $errors(memory) != 0 || $errors(leak) != 0] + || $errors(no_cmd) != 0 || $errors(no_ok) != 0 \ + || $errors(memory) != 0 || $errors(leak) != 0] } ################################################################ @@ -474,10 +469,10 @@ proc save_ok_main {} { if [file exists $failure_file] { set fail_ch [open $failure_file "r"] while { ! [eof $fail_ch] } { - set test [gets $fail_ch] - if { $test != "" } { - save_ok $test - } + set test [gets $fail_ch] + if { $test != "" } { + save_ok $test + } } close $fail_ch } diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index acf6906bc..c1ed29eac 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -56,10 +56,6 @@ if { [exec "uname"] == "Darwin" } { append valgrind_options " --dsymutil=yes" } -proc cleanse_logfile { test log_file } { - # Nothing to be done here. -} - ################################################################ # Record a test in the regression suite. @@ -136,6 +132,7 @@ proc record_example_tests { tests } { record_example_tests { delay_calc min_max_delays + mcmm3 multi_corner power power_vcd diff --git a/test/report_json1.ok b/test/report_json1.ok index 51e8ae793..7f70fbf70 100644 --- a/test/report_json1.ok +++ b/test/report_json1.ok @@ -36,7 +36,7 @@ "net": "mid", "arrival": 3.296e-10, "capacitance": 1.949e-15, - "slew": 3.612e-11 + "slew": 4.035e-11 }, { "instance": "_1416_[0]", @@ -45,7 +45,7 @@ "pin": "_1416_[0]/D", "net": "mid", "arrival": 3.296e-10, - "slew": 3.612e-11 + "slew": 4.035e-11 } ], "target_clock": "clk", @@ -72,9 +72,9 @@ ], "data_arrival_time": 3.296e-10, "crpr": 0.000e+00, - "margin": 1.207e-10, - "required_time": 9.879e-09, - "slack": 9.550e-09 + "margin": 1.225e-10, + "required_time": 9.877e-09, + "slack": 9.548e-09 } ] } diff --git a/test/suppress_msg.ok b/test/suppress_msg.ok index 1e892fe89..0fd4d548b 100644 --- a/test/suppress_msg.ok +++ b/test/suppress_msg.ok @@ -1,12 +1,12 @@ -Warning: suppress_msg.tcl line 18, cmd warn 1 +Warning 1: suppress_msg.tcl line 18, cmd warn 1 caught Error: suppress_msg.tcl line 18, cmd error 1 -Warning: cmd warn 2 -caught Error: cmd error 2 +Warning 1: cmd warn 2 +caught Error: 2cmd error 2 after error caught caught after error -Warning: suppress_msg.tcl line 51, cmd warn 7 +Warning 1: suppress_msg.tcl line 51, cmd warn 7 caught Error: suppress_msg.tcl line 51, cmd error 7 -Warning: cmd warn 8 -caught Error: cmd error 8 +Warning 1: cmd warn 8 +caught Error: 2cmd error 8 diff --git a/util/Debug.cc b/util/Debug.cc index 16836d7a7..ba46f8088 100644 --- a/util/Debug.cc +++ b/util/Debug.cc @@ -24,6 +24,7 @@ #include "Debug.hh" +#include "ContainerHelpers.hh" #include "Report.hh" namespace sta { @@ -33,35 +34,18 @@ bool debug_on = false; Debug::Debug(Report *report) : report_(report), debug_on_(false), - debug_map_(nullptr), stats_level_(0) { } -Debug::~Debug() -{ - if (debug_map_) { - DebugMap::Iterator debug_iter(debug_map_); - // Delete the debug map keys. - while (debug_iter.hasNext()) { - const char *what; - int level; - debug_iter.next(what, level); - delete [] what; - } - delete debug_map_; - } -} - bool Debug::check(const char *what, - int level) const + int level) const { - if (debug_on_ - && debug_map_) { + if (debug_on_) { int dbg_level; bool exists; - debug_map_->findKey(what, dbg_level, exists); + findKeyValue(debug_map_, what, dbg_level, exists); if (exists) return dbg_level >= level; } @@ -71,11 +55,10 @@ Debug::check(const char *what, int Debug::level(const char *what) { - if (debug_map_) { - const char *key; + if (debug_on_) { int dbg_level; bool exists; - debug_map_->findKey(what, key, dbg_level, exists); + findKeyValue(debug_map_, what, dbg_level, exists); if (exists) return dbg_level; } @@ -84,29 +67,16 @@ Debug::level(const char *what) void Debug::setLevel(const char *what, - int level) + int level) { if (stringEq(what, "stats")) stats_level_ = level; else if (level == 0) { - if (debug_map_) { - int dbg_level; - bool exists; - const char *key; - debug_map_->findKey(what, key, dbg_level, exists); - if (exists) { - debug_map_->erase(what); - delete [] key; - } - debug_on_ = !debug_map_->empty(); - } + debug_map_.erase(what); + debug_on_ = !debug_map_.empty(); } else { - char *what_cpy = new char[strlen(what) + 1]; - strcpy(what_cpy, what); - if (debug_map_ == nullptr) - debug_map_ = new DebugMap; - (*debug_map_)[what_cpy] = level; + debug_map_[what] = level; debug_on_ = true; } } diff --git a/util/Error.cc b/util/Error.cc index b198c4a42..4f0062fed 100644 --- a/util/Error.cc +++ b/util/Error.cc @@ -37,7 +37,7 @@ Exception::Exception() : } ExceptionMsg::ExceptionMsg(const char *msg, - const bool suppressed) : + const bool suppressed) : Exception(), msg_(msg), suppressed_(suppressed) @@ -51,7 +51,7 @@ ExceptionMsg::what() const noexcept } ExceptionLine::ExceptionLine(const char *filename, - int line) : + int line) : Exception(), filename_(filename), line_(line) diff --git a/util/Fuzzy.cc b/util/Fuzzy.cc index b6c69e6ee..f71583669 100644 --- a/util/Fuzzy.cc +++ b/util/Fuzzy.cc @@ -38,7 +38,7 @@ constexpr static float float_equal_tolerance = 1E-15F; bool fuzzyEqual(float v1, - float v2) + float v2) { if (v1 == v2) return true; @@ -59,28 +59,28 @@ fuzzyZero(float v) bool fuzzyLess(float v1, - float v2) + float v2) { return v1 < v2 && !fuzzyEqual(v1, v2); } bool fuzzyLessEqual(float v1, - float v2) + float v2) { return v1 < v2 || fuzzyEqual(v1, v2); } bool fuzzyGreater(float v1, - float v2) + float v2) { return v1 > v2 && !fuzzyEqual(v1, v2); } bool fuzzyGreaterEqual(float v1, - float v2) + float v2) { return v1 > v2 || fuzzyEqual(v1, v2); } diff --git a/util/MachineLinux.cc b/util/MachineLinux.cc index 231634d6d..261d143f2 100644 --- a/util/MachineLinux.cc +++ b/util/MachineLinux.cc @@ -91,13 +91,13 @@ memoryUsage() while (fgets(line, line_length, status) != nullptr) { char *field = strtok(line, " \t"); if (field && stringEq(field, "VmRSS:")) { - char *size = strtok(nullptr, " \t"); - if (size) { - char *ignore; - // VmRSS is in kilobytes. - memory = strtol(size, &ignore, 10) * 1000; - break; - } + char *size = strtok(nullptr, " \t"); + if (size) { + char *ignore; + // VmRSS is in kilobytes. + memory = strtol(size, &ignore, 10) * 1000; + break; + } } } fclose(status); diff --git a/util/MinMax.cc b/util/MinMax.cc index 074030bda..864b09de9 100644 --- a/util/MinMax.cc +++ b/util/MinMax.cc @@ -35,14 +35,14 @@ const float INF = 1E+30F; static bool compareMin(float value1, - float value2) + float value2) { return value1 < value2; } static bool compareMax(float value1, - float value2) + float value2) { return value1 > value2; } @@ -55,10 +55,10 @@ const std::array MinMax::range_{&min_, &max_}; const std::array MinMax::range_index_{min_.index(), max_.index()}; MinMax::MinMax(const char *name, - int index, - float init_value, + int index, + float init_value, int init_value_int, - bool (*compare)(float value1, float value2)) : + bool (*compare)(float value1, float value2)) : name_(name), index_(index), init_value_(init_value), @@ -92,7 +92,7 @@ MinMax::find(const char *min_max) || stringEq(min_max, "early")) return &min_; else if (stringEq(min_max, "max") - || stringEq(min_max, "late")) + || stringEq(min_max, "late")) return &max_; else return nullptr; @@ -111,14 +111,14 @@ MinMax::find(int index) bool MinMax::compare(float value1, - float value2) const + float value2) const { return compare_(value1, value2); } float MinMax::minMax(float value1, - float value2) const + float value2) const { if (compare_(value1, value2)) return value1; @@ -131,12 +131,12 @@ MinMax::minMax(float value1, const MinMaxAll MinMaxAll::min_("min", 0, {MinMax::min()}, {MinMax::min()->index()}); const MinMaxAll MinMaxAll::max_("max", 1, {MinMax::max()}, {MinMax::max()->index()}); const MinMaxAll MinMaxAll::all_("all", 2, {MinMax::min(), MinMax::max()}, - {MinMax::min()->index(), MinMax::max()->index()}); + {MinMax::min()->index(), MinMax::max()->index()}); MinMaxAll::MinMaxAll(const char *name, - int index, - std::vector range, - std::vector range_index) : + int index, + std::vector range, + std::vector range_index) : name_(name), index_(index), range_(range), @@ -172,11 +172,11 @@ MinMaxAll::find(const char *min_max) || stringEq(min_max, "early")) return &min_; else if (stringEq(min_max, "max") - || stringEq(min_max, "late")) + || stringEq(min_max, "late")) return &max_; else if (stringEq(min_max, "all") - || stringEq(min_max, "min_max") - || stringEq(min_max, "minmax")) + || stringEq(min_max, "min_max") + || stringEq(min_max, "minmax")) return &all_; else return nullptr; diff --git a/util/PatternMatch.cc b/util/PatternMatch.cc index 2dd32df92..8b1e49e89 100644 --- a/util/PatternMatch.cc +++ b/util/PatternMatch.cc @@ -31,9 +31,9 @@ namespace sta { using std::string; PatternMatch::PatternMatch(const char *pattern, - bool is_regexp, - bool nocase, - Tcl_Interp *interp) : + bool is_regexp, + bool nocase, + Tcl_Interp *interp) : pattern_(pattern), is_regexp_(is_regexp), nocase_(nocase), @@ -54,7 +54,7 @@ PatternMatch::PatternMatch(const char *pattern) : } PatternMatch::PatternMatch(const char *pattern, - const PatternMatch *inherit_from) : + const PatternMatch *inherit_from) : pattern_(pattern), is_regexp_(inherit_from->is_regexp_), nocase_(inherit_from->nocase_), @@ -66,7 +66,7 @@ PatternMatch::PatternMatch(const char *pattern, } PatternMatch::PatternMatch(const string &pattern, - const PatternMatch *inherit_from) : + const PatternMatch *inherit_from) : pattern_(pattern.c_str()), is_regexp_(inherit_from->is_regexp_), nocase_(inherit_from->nocase_), @@ -88,7 +88,7 @@ PatternMatch::compileRegexp() anchored_pattern += pattern_; anchored_pattern += '$'; Tcl_Obj *pattern_obj = Tcl_NewStringObj(anchored_pattern.c_str(), - anchored_pattern.size()); + anchored_pattern.size()); Tcl_IncrRefCount(pattern_obj); regexp_ = Tcl_GetRegExpFromObj(interp_, pattern_obj, flags); Tcl_DecrRefCount(pattern_obj); @@ -155,7 +155,7 @@ RegexpCompileError::what() const noexcept bool patternMatch(const char *pattern, - const char *str) + const char *str) { const char *p = pattern; const char *s = str; @@ -171,7 +171,7 @@ patternMatch(const char *pattern, return true; while (*s) { if (patternMatch(p + 1, s)) - return true; + return true; s++; } } @@ -180,8 +180,8 @@ patternMatch(const char *pattern, inline bool equalCase(char s, - char p, - bool nocase) + char p, + bool nocase) { return nocase ? tolower(s) == tolower(p) @@ -190,8 +190,8 @@ bool equalCase(char s, bool patternMatchNoCase(const char *pattern, - const char *str, - bool nocase) + const char *str, + bool nocase) { const char *p = pattern; const char *s = str; @@ -207,7 +207,7 @@ patternMatchNoCase(const char *pattern, return true; while (*s) { if (patternMatchNoCase(p + 1, s, nocase)) - return true; + return true; s++; } } diff --git a/util/Report.cc b/util/Report.cc index 413d03c41..f2d0955d2 100644 --- a/util/Report.cc +++ b/util/Report.cc @@ -185,7 +185,7 @@ Report::warn(int id, if (!isSuppressed(id)) { va_list args; va_start(args, fmt); - printToBuffer("Warning: "); + printToBuffer("Warning %d: ", id); printToBufferAppend(fmt, args); printBufferLine(); va_end(args); @@ -199,7 +199,7 @@ Report::vwarn(int id, { // Skip suppressed messages. if (!isSuppressed(id)) { - printToBuffer("Warning: "); + printToBuffer("Warning %d: ", id); printToBufferAppend(fmt, args); printBufferLine(); } @@ -216,7 +216,7 @@ Report::fileWarn(int id, if (!isSuppressed(id)) { va_list args; va_start(args, fmt); - printToBuffer("Warning: %s line %d, ", filename, line); + printToBuffer("Warning %d: %s line %d, ", id, filename, line); printToBufferAppend(fmt, args); printBufferLine(); va_end(args); @@ -232,7 +232,7 @@ Report::vfileWarn(int id, { // Skip suppressed messages. if (!isSuppressed(id)) { - printToBuffer("Warning: %s line %d, ", filename, line); + printToBuffer("Warning %d: %s line %d, ", id, filename, line); printToBufferAppend(fmt, args); printBufferLine(); } @@ -247,7 +247,8 @@ Report::error(int id, va_list args; va_start(args, fmt); // No prefix msg, no \n. - printToBuffer(fmt, args); + printToBuffer("%d", id); + printToBufferAppend(fmt, args); va_end(args); throw ExceptionMsg(buffer_, isSuppressed(id)); } @@ -258,7 +259,8 @@ Report::verror(int id, va_list args) { // No prefix msg, no \n. - printToBuffer(fmt, args); + printToBuffer("%d", id); + printToBufferAppend(fmt, args); throw ExceptionMsg(buffer_, isSuppressed(id)); } @@ -272,7 +274,7 @@ Report::fileError(int id, va_list args; va_start(args, fmt); // No prefix msg, no \n. - printToBuffer("%s line %d, ", filename, line); + printToBuffer("%d %s line %d, ", id, filename, line); printToBufferAppend(fmt, args); va_end(args); throw ExceptionMsg(buffer_, isSuppressed(id)); @@ -286,7 +288,7 @@ Report::vfileError(int id, va_list args) { // No prefix msg, no \n. - printToBuffer("%s line %d, ", filename, line); + printToBuffer("%d %s line %d, ", id, filename, line); printToBufferAppend(fmt, args); throw ExceptionMsg(buffer_, isSuppressed(id)); } @@ -294,13 +296,13 @@ Report::vfileError(int id, //////////////////////////////////////////////////////////////// void -Report::critical(int /* id */, +Report::critical(int id, const char *fmt, ...) { va_list args; va_start(args, fmt); - printToBuffer("Critical: "); + printToBuffer("Critical %d: ", id); printToBufferAppend(fmt, args); printBufferLine(); va_end(args); @@ -308,7 +310,7 @@ Report::critical(int /* id */, } void -Report::fileCritical(int /* id */, +Report::fileCritical(int id, const char *filename, int line, const char *fmt, @@ -316,7 +318,7 @@ Report::fileCritical(int /* id */, { va_list args; va_start(args, fmt); - printToBuffer("Critical: %s line %d, ", filename, line); + printToBuffer("Critical %d: %s line %d, ", id, filename, line); printToBufferAppend(fmt, args); printBufferLine(); va_end(args); @@ -340,7 +342,7 @@ Report::unsuppressMsgId(int id) bool Report::isSuppressed(int id) { - return suppressed_msg_ids_.find(id) != suppressed_msg_ids_.end(); + return suppressed_msg_ids_.contains(id); } //////////////////////////////////////////////////////////////// diff --git a/util/RiseFallMinMax.cc b/util/RiseFallMinMax.cc index abc96eb31..4b21a4267 100644 --- a/util/RiseFallMinMax.cc +++ b/util/RiseFallMinMax.cc @@ -69,8 +69,8 @@ RiseFallMinMax::setValue(float value) void RiseFallMinMax::setValue(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float value) + const MinMaxAll *min_max, + float value) { for (auto rf_index : rf->rangeIndex()) { for (auto mm_index : min_max->rangeIndex()) { @@ -82,7 +82,7 @@ RiseFallMinMax::setValue(const RiseFallBoth *rf, void RiseFallMinMax::removeValue(const RiseFallBoth *rf, - const MinMax *min_max) + const MinMax *min_max) { int mm_index = min_max->index(); for (auto rf_index : rf->rangeIndex()) @@ -91,7 +91,7 @@ RiseFallMinMax::removeValue(const RiseFallBoth *rf, void RiseFallMinMax::removeValue(const RiseFallBoth *rf, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { for (auto mm : min_max->range()) removeValue(rf, mm); @@ -99,16 +99,16 @@ RiseFallMinMax::removeValue(const RiseFallBoth *rf, void RiseFallMinMax::mergeValue(const RiseFallBoth *rf, - const MinMaxAll *min_max, - float value) + const MinMaxAll *min_max, + float value) { for (auto rf_index : rf->rangeIndex()) { for (auto mm : min_max->range()) { int mm_index = mm->index(); if (!exists_[rf_index][mm_index] - || mm->compare(value, values_[rf_index][mm_index])) { - values_[rf_index][mm_index] = value; - exists_[rf_index][mm_index] = true; + || mm->compare(value, values_[rf_index][mm_index])) { + values_[rf_index][mm_index] = value; + exists_[rf_index][mm_index] = true; } } } @@ -116,8 +116,8 @@ RiseFallMinMax::mergeValue(const RiseFallBoth *rf, void RiseFallMinMax::mergeValue(const RiseFall *rf, - const MinMax *min_max, - float value) + const MinMax *min_max, + float value) { int rf_index = rf->index(); int mm_index = min_max->index(); @@ -130,8 +130,8 @@ RiseFallMinMax::mergeValue(const RiseFall *rf, void RiseFallMinMax::setValue(const RiseFallBoth *rf, - const MinMax *min_max, - float value) + const MinMax *min_max, + float value) { int mm_index = min_max->index(); for (auto rf_index : rf->rangeIndex()) { @@ -142,8 +142,8 @@ RiseFallMinMax::setValue(const RiseFallBoth *rf, void RiseFallMinMax::setValue(const RiseFall *rf, - const MinMax *min_max, - float value) + const MinMax *min_max, + float value) { int rf_index = rf->index(); int mm_index = min_max->index(); @@ -164,8 +164,8 @@ RiseFallMinMax::setValues(RiseFallMinMax *values) void RiseFallMinMax::value(const RiseFall *rf, - const MinMax *min_max, - // Return values. + const MinMax *min_max, + // Return values. float &value, bool &exists) const { @@ -188,7 +188,7 @@ RiseFallMinMax::value(const MinMax *min_max) const float RiseFallMinMax::value(const RiseFall *rf, - const MinMax *min_max) const + const MinMax *min_max) const { return values_[rf->index()][min_max->index()]; } @@ -201,16 +201,16 @@ RiseFallMinMax::hasValue() const void RiseFallMinMax::maxValue(// Return values - float &max_value, - bool &exists) const + float &max_value, + bool &exists) const { max_value = MinMax::max()->initValue(); exists = false; for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { if (exists_[rf_index][mm_index]) { - max_value = std::max(max_value, values_[rf_index][mm_index]); - exists = true; + max_value = std::max(max_value, values_[rf_index][mm_index]); + exists = true; } } } @@ -222,7 +222,7 @@ RiseFallMinMax::empty() const for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { if (exists_[rf_index][mm_index]) - return false; + return false; } } return true; @@ -243,13 +243,13 @@ RiseFallMinMax::mergeWith(RiseFallMinMax *rfmm) bool exists1 = exists_[rf_index][mm_index]; bool exists2 = rfmm->exists_[rf_index][mm_index]; if (exists1 && exists2) { - float rfmm_value = rfmm->values_[rf_index][mm_index]; - if (min_max->compare(rfmm_value, values_[rf_index][mm_index])) - values_[rf_index][mm_index] = rfmm_value; + float rfmm_value = rfmm->values_[rf_index][mm_index]; + if (min_max->compare(rfmm_value, values_[rf_index][mm_index])) + values_[rf_index][mm_index] = rfmm_value; } else if (!exists1 && exists2) { - values_[rf_index][mm_index] = rfmm->values_[rf_index][mm_index]; - exists_[rf_index][mm_index] = true; + values_[rf_index][mm_index] = rfmm->values_[rf_index][mm_index]; + exists_[rf_index][mm_index] = true; } } } @@ -263,10 +263,10 @@ RiseFallMinMax::equal(const RiseFallMinMax *values) const bool exists1 = exists_[rf_index][mm_index]; bool exists2 = values->exists_[rf_index][mm_index]; if (exists1 != exists2) - return false; + return false; if (exists1 && exists2 - && values_[rf_index][mm_index] != values->values_[rf_index][mm_index]) - return false; + && values_[rf_index][mm_index] != values->values_[rf_index][mm_index]) + return false; } } return true; @@ -286,9 +286,9 @@ RiseFallMinMax::isOneValue(float &value) const value = values_[0][0]; for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { for (int mm_index=0; mm_indexindex(); if (exists_[0][mm_index]) { value = values_[0][mm_index]; for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { if (!exists_[rf_index][mm_index] - || values_[rf_index][mm_index] != value) - return false; + || values_[rf_index][mm_index] != value) + return false; } return true; } diff --git a/util/RiseFallMinMaxDelay.cc b/util/RiseFallMinMaxDelay.cc new file mode 100644 index 000000000..c07469971 --- /dev/null +++ b/util/RiseFallMinMaxDelay.cc @@ -0,0 +1,77 @@ +// 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 "RiseFallMinMaxDelay.hh" + +namespace sta { + +RiseFallMinMaxDelay::RiseFallMinMaxDelay() +{ + for (int rf_index = 0; rf_indexindex()][min_max->index()]; + if (exists) + value = values_[rf->index()][min_max->index()]; +} + +void +RiseFallMinMaxDelay::mergeValue(const RiseFall *rf, + const MinMax *min_max, + Delay &value, + const StaState *sta) +{ + int rf_index = rf->index(); + int mm_index = min_max->index(); + if (!exists_[rf_index][mm_index] + || delayGreater(value, values_[rf_index][mm_index], min_max, sta)) { + values_[rf_index][mm_index] = value; + exists_[rf_index][mm_index] = true; + } +} + +} // namespace diff --git a/util/RiseFallValues.cc b/util/RiseFallValues.cc index 6dd4fda4a..fb52568fb 100644 --- a/util/RiseFallValues.cc +++ b/util/RiseFallValues.cc @@ -54,7 +54,7 @@ RiseFallValues::setValue(float value) void RiseFallValues::setValue(const RiseFallBoth *rf, - float value) + float value) { for (auto rf_index : rf->rangeIndex()) { values_[rf_index] = value; @@ -64,7 +64,7 @@ RiseFallValues::setValue(const RiseFallBoth *rf, void RiseFallValues::setValue(const RiseFall *rf, - float value) + float value) { int rf_index = rf->index(); values_[rf_index] = value; @@ -82,7 +82,7 @@ RiseFallValues::setValues(RiseFallValues *values) void RiseFallValues::value(const RiseFall *rf, - float &value, bool &exists) const + float &value, bool &exists) const { int rf_index = rf->index(); exists = exists_[rf_index]; diff --git a/util/StringSeq.cc b/util/StringSeq.cc index 67d51a4b8..f5615d6c9 100644 --- a/util/StringSeq.cc +++ b/util/StringSeq.cc @@ -29,11 +29,8 @@ namespace sta { void deleteContents(StringSeq *strings) { - StringSeq::Iterator iter(strings); - while (iter.hasNext()) { - const char *string = iter.next(); + for (const char *string : *strings) stringDelete(string); - } } } // namespace diff --git a/util/StringSet.cc b/util/StringSet.cc index 22d0d303a..798635472 100644 --- a/util/StringSet.cc +++ b/util/StringSet.cc @@ -29,11 +29,8 @@ namespace sta { void deleteContents(StringSet *strings) { - StringSet::Iterator iter(strings); - while (iter.hasNext()) { - const char *string = iter.next(); + for (const char *string : *strings) stringDelete(string); - } } } // namespace diff --git a/util/StringUtil.cc b/util/StringUtil.cc index e9b0d74ab..162373315 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -41,14 +41,14 @@ using std::string; static void stringPrintTmp(const char *fmt, - va_list args, - // Return values. - char *&str, - size_t &length); + va_list args, + // Return values. + char *&str, + size_t &length); static void getTmpString(// Return values. - char *&str, - size_t &length); + char *&str, + size_t &length); char * stringCopy(const char *str) @@ -77,8 +77,8 @@ isDigits(const char *str) // print for c++ strings. void stringPrint(string &str, - const char *fmt, - ...) + const char *fmt, + ...) { va_list args; va_start(args, fmt); @@ -105,7 +105,7 @@ stringAppend(string &str, string stdstrPrint(const char *fmt, - ...) + ...) { va_list args; va_start(args, fmt); @@ -118,7 +118,7 @@ stdstrPrint(const char *fmt, char * stringPrint(const char *fmt, - ...) + ...) { va_list args; va_start(args, fmt); @@ -129,7 +129,7 @@ stringPrint(const char *fmt, char * stringPrintArgs(const char *fmt, - va_list args) + va_list args) { char *tmp; size_t tmp_length; @@ -141,7 +141,7 @@ stringPrintArgs(const char *fmt, char * stringPrintTmp(const char *fmt, - ...) + ...) { va_list args; va_start(args, fmt); @@ -154,11 +154,11 @@ stringPrintTmp(const char *fmt, static void stringPrintTmp(const char *fmt, - va_list args, - // Return values. - char *&tmp, - // strlen(tmp), not including terminating '\0'. - size_t &tmp_length) + 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); @@ -188,8 +188,8 @@ thread_local static int tmp_string_next = 0; static void getTmpString(// Return values. - char *&str, - size_t &length) + char *&str, + size_t &length) { if (tmp_string_next == tmp_string_count) tmp_string_next = 0; diff --git a/util/TokenParser.cc b/util/TokenParser.cc index dba068df4..e048cecfc 100644 --- a/util/TokenParser.cc +++ b/util/TokenParser.cc @@ -58,20 +58,20 @@ TokenParser::hasNext() token_ = token_end_ + 1; // Skip spaces. while (*token_ != '\0' && isspace(*token_)) - token_++; + token_++; // Skip delimiters. while (*token_ != '\0' && strchr(delimiters_,*token_) != nullptr) token_++; if (*token_ == '\0') - token_ = nullptr; + token_ = nullptr; else { - token_end_ = strpbrk(token_, delimiters_); - if (token_end_) { - // Save the delimiter. - token_delimiter_ = *token_end_; - // Replace the separator with a terminator. - *token_end_ = '\0'; - } + token_end_ = strpbrk(token_, delimiters_); + if (token_end_) { + // Save the delimiter. + token_delimiter_ = *token_end_; + // Replace the separator with a terminator. + *token_end_ = '\0'; + } } } else diff --git a/util/Transition.cc b/util/Transition.cc index 5188f9f22..a77a898b5 100644 --- a/util/Transition.cc +++ b/util/Transition.cc @@ -24,6 +24,8 @@ #include "Transition.hh" +#include "ContainerHelpers.hh" + namespace sta { using std::max; @@ -157,9 +159,9 @@ RiseFallBoth::matches(const Transition *tr) const { return this == &rise_fall_ || (this == &rise_ - && tr == Transition::rise()) + && tr == Transition::rise()) || (this == &fall_ - && tr == Transition::fall()); + && tr == Transition::fall()); } //////////////////////////////////////////////////////////////// @@ -183,9 +185,9 @@ const Transition Transition::tr_ZX_{"ZX", "ZX", nullptr, 11}; const Transition Transition::rise_fall_{"*", "**", nullptr, -1}; Transition::Transition(const char *name, - const char *init_final, - const RiseFall *as_rise_fall, - int sdf_triple_index) : + const char *init_final, + const RiseFall *as_rise_fall, + int sdf_triple_index) : name_(name), init_final_(init_final), as_rise_fall_(as_rise_fall), @@ -205,7 +207,7 @@ Transition::matches(const Transition *tr) const const Transition * Transition::find(const char *tr_str) { - return transition_map_.findKey(tr_str); + return findKey(transition_map_, tr_str); } const RiseFallBoth * diff --git a/util/Util.i b/util/Util.i index 6c7ec10f1..5bc47a535 100644 --- a/util/Util.i +++ b/util/Util.i @@ -226,7 +226,7 @@ log_end() void set_debug(const char *what, - int level) + int level) { Sta::sta()->setDebugLevel(what, level); } @@ -264,7 +264,7 @@ object_type(const char *obj) bool is_object_list(const char *list, - const char *type) + const char *type) { const char *s = list; while (s) { @@ -390,7 +390,7 @@ area_sta_ui(double value) void set_cmd_unit_scale(const char *unit_name, - float scale) + float scale) { Unit *unit = Sta::sta()->units()->find(unit_name); if (unit) @@ -399,7 +399,7 @@ set_cmd_unit_scale(const char *unit_name, void set_cmd_unit_digits(const char *unit_name, - int digits) + int digits) { Unit *unit = Sta::sta()->units()->find(unit_name); if (unit) @@ -408,7 +408,7 @@ set_cmd_unit_digits(const char *unit_name, void set_cmd_unit_suffix(const char *unit_name, - const char *suffix) + const char *suffix) { Unit *unit = Sta::sta()->units()->find(unit_name); if (unit) { @@ -473,7 +473,7 @@ unit_scale(const char *unit_name) // Pass value arg as string to support NaNs. const char * format_time(const char *value, - int digits) + int digits) { float value1 = strtof(value, nullptr); return Sta::sta()->units()->timeUnit()->asString(value1, digits); @@ -481,7 +481,7 @@ format_time(const char *value, const char * format_capacitance(const char *value, - int digits) + int digits) { float value1 = strtof(value, nullptr); return Sta::sta()->units()->capacitanceUnit()->asString(value1, digits); @@ -489,7 +489,7 @@ format_capacitance(const char *value, const char * format_resistance(const char *value, - int digits) + int digits) { float value1 = strtof(value, nullptr); return Sta::sta()->units()->resistanceUnit()->asString(value1, digits); @@ -497,7 +497,7 @@ format_resistance(const char *value, const char * format_voltage(const char *value, - int digits) + int digits) { float value1 = strtof(value, nullptr); return Sta::sta()->units()->voltageUnit()->asString(value1, digits); @@ -505,7 +505,7 @@ format_voltage(const char *value, const char * format_current(const char *value, - int digits) + int digits) { float value1 = strtof(value, nullptr); return Sta::sta()->units()->currentUnit()->asString(value1, digits); @@ -513,7 +513,7 @@ format_current(const char *value, const char * format_power(const char *value, - int digits) + int digits) { float value1 = strtof(value, nullptr); return Sta::sta()->units()->powerUnit()->asString(value1, digits); @@ -521,7 +521,7 @@ format_power(const char *value, const char * format_distance(const char *value, - int digits) + int digits) { float value1 = strtof(value, nullptr); Unit *dist_unit = Sta::sta()->units()->distanceUnit(); @@ -530,7 +530,7 @@ format_distance(const char *value, const char * format_area(const char *value, - int digits) + int digits) { float value1 = strtof(value, nullptr); Unit *dist_unit = Sta::sta()->units()->distanceUnit(); @@ -555,7 +555,7 @@ fall_short_name() bool fuzzy_equal(float value1, - float value2) + float value2) { return fuzzyEqual(value1, value2); } diff --git a/verilog/Verilog.i b/verilog/Verilog.i index 5e1744ef4..c1485fea1 100644 --- a/verilog/Verilog.i +++ b/verilog/Verilog.i @@ -39,8 +39,8 @@ read_verilog_cmd(const char *filename) void write_verilog_cmd(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells) + bool include_pwr_gnd, + CellSeq *remove_cells) { // This does NOT want the SDC (cmd) network because it wants // to see the sta internal names. diff --git a/verilog/Verilog.tcl b/verilog/Verilog.tcl index f4a459f5e..73e753dea 100644 --- a/verilog/Verilog.tcl +++ b/verilog/Verilog.tcl @@ -32,7 +32,7 @@ proc_redirect read_verilog { } define_cmd_args "write_verilog" {[-include_pwr_gnd]\ - [-remove_cells cells] filename} + [-remove_cells cells] filename} proc write_verilog { args } { # -sort deprecated 12/12/2025 diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy index 530e9cf28..8572f71da 100644 --- a/verilog/VerilogParse.yy +++ b/verilog/VerilogParse.yy @@ -223,9 +223,8 @@ stmts: { if ($2) $1->push_back($2); } | stmts stmt_seq // Append stmt_seq to stmts. - { sta::VerilogStmtSeq::Iterator iter($2); - while (iter.hasNext()) - $1->push_back(iter.next()); + { for (sta::VerilogStmt *stmt : *$2) + $1->push_back(stmt); delete $2; } ; diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 26ed54d80..e7ac21d33 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -26,6 +26,7 @@ #include +#include "ContainerHelpers.hh" #include "Zlib.hh" #include "Debug.hh" #include "Report.hh" @@ -43,14 +44,14 @@ namespace sta { using std::string; -typedef unsigned long long VerilogConstant10; +using VerilogConstant10 = unsigned long long; static string verilogBusBitName(const string &bus_name, int index); static int hierarchyLevel(Net *net, - Network *network); + Network *network); VerilogReader * makeVerilogReader(NetworkReader *network) @@ -60,7 +61,7 @@ makeVerilogReader(NetworkReader *network) bool readVerilogFile(const char *filename, - VerilogReader *verilog_reader) + VerilogReader *verilog_reader) { return verilog_reader->read(filename); } @@ -78,9 +79,9 @@ class VerilogError public: VerilogError(int id, const char *filename, - int line, - const char *msg, - bool warn); + int line, + const char *msg, + bool warn); ~VerilogError(); const char *msg() const { return msg_; } const char *filename() const { return filename_; } @@ -100,9 +101,9 @@ class VerilogError VerilogError::VerilogError(int id, const char *filename, - int line, - const char *msg, - bool warn) : + int line, + const char *msg, + bool warn) : id_(id), filename_(filename), line_(line), @@ -121,14 +122,14 @@ class VerilogErrorCmp { public: bool operator()(const VerilogError *error1, - const VerilogError *error2) const + const VerilogError *error2) const { int file_cmp = strcmp(error1->filename_, error2->filename_); if (file_cmp == 0) { if (error1->line_ == error2->line_) - return strcmp(error1->msg_, error2->msg_) < 0; + return strcmp(error1->msg_, error2->msg_) < 0; else - return error1->line_ < error2->line_; + return error1->line_ < error2->line_; } else return file_cmp < 0; @@ -222,15 +223,15 @@ VerilogReader::init(const char *filename) VerilogModule * VerilogReader::module(Cell *cell) { - return module_map_.findKey(cell); + return findKey(module_map_, cell); } void VerilogReader::makeModule(const string *module_vname, - VerilogNetSeq *ports, - VerilogStmtSeq *stmts, + VerilogNetSeq *ports, + VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { const string module_name = moduleVerilogToSta(module_vname); Cell *cell = network_->findCell(library_, module_name.c_str()); @@ -242,7 +243,7 @@ VerilogReader::makeModule(const string *module_vname, } VerilogModule *module = new VerilogModule(module_name.c_str(), ports, stmts, - attr_stmts, filename_, line, this); + attr_stmts, filename_, line, this); cell = network_->makeCell(library_, module_name.c_str(), false, filename_.c_str()); for (VerilogAttrStmt *stmt : *attr_stmts) { @@ -258,10 +259,10 @@ VerilogReader::makeModule(const string *module_vname, void VerilogReader::makeModule(const string *module_name, - VerilogStmtSeq *port_dcls, - VerilogStmtSeq *stmts, + VerilogStmtSeq *port_dcls, + VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { VerilogNetSeq *ports = new VerilogNetSeq; // Pull the port names out of the port declarations. @@ -269,8 +270,8 @@ VerilogReader::makeModule(const string *module_name, if (dcl->isDeclaration()) { VerilogDcl *dcl1 = dynamic_cast(dcl); for (VerilogDclArg *arg : *dcl1->args()) { - VerilogNetNamed *port = new VerilogNetScalar(arg->netName()); - ports->push_back(port); + VerilogNetNamed *port = new VerilogNetScalar(arg->netName()); + ports->push_back(port); } // Add the port declarations to the statements. stmts->push_back(dcl); @@ -282,34 +283,34 @@ VerilogReader::makeModule(const string *module_name, void VerilogReader::makeCellPorts(Cell *cell, - VerilogModule *module, - VerilogNetSeq *ports) + VerilogModule *module, + VerilogNetSeq *ports) { StdStringSet port_names; for (VerilogNet *mod_port : *ports) { const string &port_name = mod_port->name(); - if (port_names.find(port_name) == port_names.end()) { + if (!port_names.contains(port_name)) { port_names.insert(port_name); if (mod_port->isNamed()) { - if (mod_port->isNamedPortRef()) - makeNamedPortRefCellPorts(cell, module, mod_port, port_names); - else - makeCellPort(cell, module, mod_port->name()); + if (mod_port->isNamedPortRef()) + makeNamedPortRefCellPorts(cell, module, mod_port, port_names); + else + makeCellPort(cell, module, mod_port->name()); } } else warn(165, module->filename(), module->line(), - "module %s repeated port name %s.", - module->name().c_str(), - port_name.c_str()); + "module %s repeated port name %s.", + module->name().c_str(), + port_name.c_str()); } checkModuleDcls(module, port_names); } Port * VerilogReader::makeCellPort(Cell *cell, - VerilogModule *module, - const string &port_name) + VerilogModule *module, + const string &port_name) { VerilogDcl *dcl = module->declaration(port_name.c_str()); if (dcl) { @@ -317,25 +318,25 @@ VerilogReader::makeCellPort(Cell *cell, VerilogDclBus *dcl_bus = dynamic_cast(dcl); Port *port = dcl->isBus() ? network_->makeBusPort(cell, port_name.c_str(), dcl_bus->fromIndex(), - dcl_bus->toIndex()) + 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(), - port_name.c_str()); + "module %s missing declaration for port %s.", + module->name().c_str(), + port_name.c_str()); return network_->makePort(cell, port_name.c_str()); } } void VerilogReader::makeNamedPortRefCellPorts(Cell *cell, - VerilogModule *module, - VerilogNet *mod_port, - StdStringSet &port_names) + VerilogModule *module, + VerilogNet *mod_port, + StdStringSet &port_names) { PortSeq *member_ports = new PortSeq; VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module,this); @@ -353,40 +354,40 @@ VerilogReader::makeNamedPortRefCellPorts(Cell *cell, // Make sure each declaration appears in the module port list. void VerilogReader::checkModuleDcls(VerilogModule *module, - std::set &port_names) + std::set &port_names) { for (auto const & [port_name, dcl] : *module->declarationMap()) { PortDirection *dir = dcl->direction(); if (dir->isInput() - || dir->isOutput() - || dir->isBidirect()) { - if (port_names.find(port_name) == port_names.end()) - 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()); + || 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()); } } } VerilogDcl * VerilogReader::makeDcl(PortDirection *dir, - VerilogDclArgSeq *args, + VerilogDclArgSeq *args, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { if (dir->isInternal()) { // Prune wire declarations without assigns because they just eat memory. VerilogDclArgSeq *assign_args = nullptr; for (VerilogDclArg *arg : *args) { if (arg->assign()) { - if (assign_args == nullptr) - assign_args = new VerilogDclArgSeq; - assign_args->push_back(arg); + if (assign_args == nullptr) + assign_args = new VerilogDclArgSeq; + assign_args->push_back(arg); } else { - delete arg; - dcl_arg_count_--; + delete arg; + dcl_arg_count_--; } } delete args; @@ -395,7 +396,7 @@ VerilogReader::makeDcl(PortDirection *dir, return new VerilogDcl(dir, assign_args, attr_stmts, line); } else { - attr_stmts->deleteContents(); + deleteContents(attr_stmts); delete attr_stmts; return nullptr; } @@ -408,9 +409,9 @@ VerilogReader::makeDcl(PortDirection *dir, VerilogDcl * VerilogReader::makeDcl(PortDirection *dir, - VerilogDclArg *arg, + VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { dcl_count_++; return new VerilogDcl(dir, arg, attr_stmts, line); @@ -418,11 +419,11 @@ VerilogReader::makeDcl(PortDirection *dir, VerilogDclBus * VerilogReader::makeDclBus(PortDirection *dir, - int from_index, - int to_index, - VerilogDclArg *arg, + int from_index, + int to_index, + VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { dcl_bus_count_++; return new VerilogDclBus(dir, from_index, to_index, arg, attr_stmts, @@ -431,11 +432,11 @@ VerilogReader::makeDclBus(PortDirection *dir, VerilogDclBus * VerilogReader::makeDclBus(PortDirection *dir, - int from_index, - int to_index, - VerilogDclArgSeq *args, + int from_index, + int to_index, + VerilogDclArgSeq *args, VerilogAttrStmtSeq *attr_stmts, - int line) + int line) { dcl_bus_count_++; return new VerilogDclBus(dir, from_index, to_index, args, attr_stmts, @@ -461,8 +462,8 @@ VerilogReader::makeDclArg(VerilogAssign *assign) VerilogNetPartSelect * VerilogReader::makeNetPartSelect(const string *net_vname, - int from_index, - int to_index) + int from_index, + int to_index) { net_part_select_count_++; if (report_stmt_stats_) @@ -497,7 +498,7 @@ VerilogReader::makeNetScalar(const string *net_vname) VerilogNetBitSelect * VerilogReader::makeNetBitSelect(const string *net_vname, - int index) + int index) { net_bit_select_count_++; if (report_stmt_stats_) @@ -510,8 +511,8 @@ VerilogReader::makeNetBitSelect(const string *net_vname, VerilogAssign * VerilogReader::makeAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line) + VerilogNet *rhs, + int line) { assign_count_++; return new VerilogAssign(lhs, rhs, line); @@ -538,14 +539,14 @@ VerilogReader::makeModuleInst(const string *module_vname, StdStringSeq 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 string &net_name = vpin->netName(); Port *port = network_->findPort(cell, port_name); LibertyPort *lport = network_->libertyPort(port); if (lport->isBus()) { - LibertyPortMemberIterator member_iter(lport); - lport = member_iter.next(); + LibertyPortMemberIterator member_iter(lport); + lport = member_iter.next(); } int pin_index = lport->pinIndex(); net_names[pin_index] = net_name; @@ -553,7 +554,7 @@ VerilogReader::makeModuleInst(const string *module_vname, net_port_ref_scalar_net_count_--; } VerilogInst *inst = new VerilogLibertyInst(liberty_cell, inst_name, - net_names, attr_stmts, line); + net_names, attr_stmts, line); delete pins; if (report_stmt_stats_) { inst_names_ += inst_name.size() + 1; @@ -583,7 +584,7 @@ VerilogReader::makeModuleInst(const string *module_vname, bool VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, - VerilogNetSeq *pins) + VerilogNetSeq *pins) { if (pins && pins->size() > 0 @@ -592,12 +593,12 @@ VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, const char *port_name = vpin->name().c_str(); LibertyPort *port = liberty_cell->findLibertyPort(port_name); if (port) { - if (!(port->size() == 1 - && (vpin->isNamedPortRefScalarNet()))) - return false; + if (!(port->size() == 1 + && (vpin->isNamedPortRefScalarNet()))) + return false; } else - return false; + return false; } return true; } @@ -619,7 +620,7 @@ VerilogReader::makeNetNamedPortRefScalarNet(const string *port_vname) VerilogNetPortRef * VerilogReader::makeNetNamedPortRefScalarNet(const string *port_vname, - const string *net_vname) + const string *net_vname) { net_port_ref_scalar_net_count_++; if (report_stmt_stats_) { @@ -638,8 +639,8 @@ VerilogReader::makeNetNamedPortRefScalarNet(const string *port_vname, VerilogNetPortRef * VerilogReader::makeNetNamedPortRefBitSelect(const string *port_vname, - const string *bus_vname, - int index) + const string *bus_vname, + int index) { net_port_ref_scalar_net_count_++; const string bus_name = portVerilogToSta(bus_vname); @@ -658,7 +659,7 @@ VerilogReader::makeNetNamedPortRefBitSelect(const string *port_vname, VerilogNetPortRef * VerilogReader::makeNetNamedPortRefScalar(const string *port_vname, - VerilogNet *net) + VerilogNet *net) { net_port_ref_scalar_count_++; if (report_stmt_stats_) @@ -671,8 +672,8 @@ VerilogReader::makeNetNamedPortRefScalar(const string *port_vname, VerilogNetPortRef * VerilogReader::makeNetNamedPortRefBit(const string *port_vname, - int index, - VerilogNet *net) + int index, + VerilogNet *net) { net_port_ref_bit_count_++; const string port_name = portVerilogToSta(port_vname); @@ -684,9 +685,9 @@ VerilogReader::makeNetNamedPortRefBit(const string *port_vname, VerilogNetPortRef * VerilogReader::makeNetNamedPortRefPart(const string *port_vname, - int from_index, - int to_index, - VerilogNet *net) + int from_index, + int to_index, + VerilogNet *net) { net_port_ref_part_count_++; const string port_name = portVerilogToSta(port_vname); @@ -711,7 +712,7 @@ VerilogReader::makeNetConcat(VerilogNetSeq *nets) sizeof(class_name), \ (count * sizeof(class_name) * 1e-6)) -#define printStringMemory(name, count) \ +#define printStringMemory(name, count) \ report_->reportLine(" %-20s %6.1fMb", name, count * 1e-6) void @@ -727,17 +728,17 @@ VerilogReader::reportStmtCounts() printClassMemory("bus declarations", VerilogDclBus, dcl_bus_count_); printClassMemory("declaration args", VerilogDclArg, dcl_arg_count_); printClassMemory("port ref scalar", VerilogNetPortRefScalar, - net_port_ref_scalar_count_); + net_port_ref_scalar_count_); printClassMemory("port ref scalar net", VerilogNetPortRefScalarNet, - net_port_ref_scalar_net_count_); + net_port_ref_scalar_net_count_); printClassMemory("port ref bit", VerilogNetPortRefBit, - net_port_ref_bit_count_); + net_port_ref_bit_count_); printClassMemory("port ref part", VerilogNetPortRefPart, - net_port_ref_part_count_); + 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_); + net_part_select_count_); printClassMemory("constant nets", VerilogNetConstant, net_constant_count_); printClassMemory("concats", VerilogNetConcat, concat_count_); printClassMemory("assigns", VerilogAssign, assign_count_); @@ -752,8 +753,8 @@ VerilogReader::reportStmtCounts() void VerilogReader::error(int id, const char *filename, - int line, - const char *fmt, ...) + int line, + const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -764,8 +765,8 @@ VerilogReader::error(int id, void VerilogReader::warn(int id, const char *filename, - int line, - const char *fmt, ...) + int line, + const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -794,11 +795,11 @@ VerilogModule::VerilogModule(const string &name, VerilogModule::~VerilogModule() { - ports_->deleteContents(); + deleteContents(ports_); delete ports_; - stmts_->deleteContents(); + deleteContents(stmts_); delete stmts_; - attr_stmts_->deleteContents(); + deleteContents(attr_stmts_); delete attr_stmts_; } @@ -811,13 +812,13 @@ VerilogModule::parseStmts(VerilogReader *reader) parseDcl(dynamic_cast(stmt), reader); else if (stmt->isInstance()) checkInstanceName(dynamic_cast(stmt), inst_names, - reader); + reader); } } void VerilogModule::parseDcl(VerilogDcl *dcl, - VerilogReader *reader) + VerilogReader *reader) { for (VerilogDclArg *arg : *dcl->args()) { if (arg->isNamed()) { @@ -861,21 +862,21 @@ VerilogModule::parseDcl(VerilogDcl *dcl, // expansion so errors are only reported once. void VerilogModule::checkInstanceName(VerilogInst *inst, - StdStringSet &inst_names, - VerilogReader *reader) + StdStringSet &inst_names, + VerilogReader *reader) { string inst_name = inst->instanceName(); - if (inst_names.find(inst_name) != inst_names.end()) { + if (inst_names.contains(inst_name)) { int i = 1; string replacement_name; do { replacement_name = stdstrPrint("%s_%d", inst_name.c_str(), i++); - } while (inst_names.find(replacement_name) != inst_names.end()); + } while (inst_names.contains(replacement_name)); 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()); + "instance name %s duplicated - renamed to %s.", + inst_vname.c_str(), + replacement_name.c_str()); inst_name = replacement_name; inst->setInstanceName(inst_name); } @@ -885,7 +886,7 @@ VerilogModule::checkInstanceName(VerilogInst *inst, VerilogDcl * VerilogModule::declaration(const string &net_name) { - return dcl_map_.findKey(net_name.c_str()); + return findKey(dcl_map_, net_name.c_str()); } //////////////////////////////////////////////////////////////// @@ -906,7 +907,7 @@ VerilogInst::VerilogInst(const string &inst_name, VerilogInst::~VerilogInst() { - attr_stmts_->deleteContents(); + deleteContents(attr_stmts_); delete attr_stmts_; } @@ -930,7 +931,7 @@ VerilogModuleInst::VerilogModuleInst(const string &module_name, VerilogModuleInst::~VerilogModuleInst() { if (pins_) { - pins_->deleteContents(); + deleteContents(pins_); delete pins_; } } @@ -987,9 +988,9 @@ VerilogDcl::VerilogDcl(PortDirection *dir, VerilogDcl::~VerilogDcl() { - args_->deleteContents(); + deleteContents(args_); delete args_; - attr_stmts_->deleteContents(); + deleteContents(attr_stmts_); delete attr_stmts_; } @@ -1061,8 +1062,8 @@ VerilogDclArg::netName() } VerilogAssign::VerilogAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line) : + VerilogNet *rhs, + int line) : VerilogStmt(line), lhs_(lhs), rhs_(rhs) @@ -1126,8 +1127,8 @@ class VerilogBusNetNameIterator : public VerilogNetNameIterator { public: VerilogBusNetNameIterator(const string bus_name, - int from_index, - int to_index); + int from_index, + int to_index); virtual bool hasNext(); virtual const string &next(); @@ -1140,8 +1141,8 @@ class VerilogBusNetNameIterator : public VerilogNetNameIterator }; VerilogBusNetNameIterator::VerilogBusNetNameIterator(const string bus_name, - int from_index, - int to_index) : + int from_index, + int to_index) : bus_name_(bus_name), from_index_(from_index), to_index_(to_index), @@ -1153,9 +1154,9 @@ bool VerilogBusNetNameIterator::hasNext() { return (to_index_ > from_index_ - && index_ <= to_index_) + && index_ <= to_index_) || (to_index_ <= from_index_ - && index_ >= to_index_); + && index_ >= to_index_); } const string & @@ -1180,7 +1181,7 @@ class VerilogConstantNetNameIterator : public VerilogNetNameIterator { public: VerilogConstantNetNameIterator(VerilogConstantValue *value, - const string &zero, + const string &zero, const string &one); virtual bool hasNext(); virtual const string &next(); @@ -1194,8 +1195,8 @@ class VerilogConstantNetNameIterator : public VerilogNetNameIterator VerilogConstantNetNameIterator:: VerilogConstantNetNameIterator(VerilogConstantValue *value, - const string &zero, - const string &one) : + const string &zero, + const string &one) : value_(value), zero_(zero), one_(one), @@ -1219,8 +1220,8 @@ class VerilogNetConcatNameIterator : public VerilogNetNameIterator { public: VerilogNetConcatNameIterator(VerilogNetSeq *nets, - VerilogModule *module, - VerilogReader *reader); + VerilogModule *module, + VerilogReader *reader); virtual ~VerilogNetConcatNameIterator(); virtual bool hasNext(); virtual const string &next(); @@ -1228,21 +1229,25 @@ class VerilogNetConcatNameIterator : public VerilogNetNameIterator private: VerilogModule *module_; VerilogReader *reader_; - VerilogNetSeq::Iterator net_iter_; + VerilogNetSeq *nets_; + VerilogNetSeq::iterator net_iter_; VerilogNetNameIterator *net_name_iter_; }; VerilogNetConcatNameIterator:: VerilogNetConcatNameIterator(VerilogNetSeq *nets, - VerilogModule *module, - VerilogReader *reader) : + VerilogModule *module, + VerilogReader *reader) : module_(module), reader_(reader), - net_iter_(nets), + nets_(nets), + net_iter_(nets->begin()), net_name_iter_(nullptr) { - if (net_iter_.hasNext()) - net_name_iter_ = net_iter_.next()->nameIterator(module, reader); + if (net_iter_ != nets_->end()) { + VerilogNet *net = *net_iter_++; + net_name_iter_ = net->nameIterator(module, reader); + } } VerilogNetConcatNameIterator::~VerilogNetConcatNameIterator() @@ -1254,7 +1259,7 @@ bool VerilogNetConcatNameIterator::hasNext() { return (net_name_iter_ && net_name_iter_->hasNext()) - || net_iter_.hasNext(); + || net_iter_ != nets_->end(); } const string & @@ -1263,12 +1268,12 @@ VerilogNetConcatNameIterator::next() if (net_name_iter_ && net_name_iter_->hasNext()) return net_name_iter_->next(); else { - if (net_iter_.hasNext()) { - VerilogNet *net = net_iter_.next(); + if (net_iter_ != nets_->end()) { + VerilogNet *net = *net_iter_++; delete net_name_iter_; net_name_iter_ = net->nameIterator(module_, reader_); if (net_name_iter_ && net_name_iter_->hasNext()) - return net_name_iter_->next(); + return net_name_iter_->next(); } } static const string null; @@ -1296,7 +1301,7 @@ VerilogNetScalar::VerilogNetScalar(const string &name) : static int verilogNetScalarSize(const char *name, - VerilogModule *module) + VerilogModule *module) { VerilogDcl *dcl = module->declaration(name); if (dcl) @@ -1314,14 +1319,14 @@ VerilogNetScalar::size(VerilogModule *module) static VerilogNetNameIterator * verilogNetScalarNameIterator(const string &name, - VerilogModule *module) + VerilogModule *module) { if (!name.empty()) { VerilogDcl *dcl = module->declaration(name); if (dcl && dcl->isBus()) { VerilogDclBus *dcl_bus = dynamic_cast(dcl); return new VerilogBusNetNameIterator(name, dcl_bus->fromIndex(), - dcl_bus->toIndex()); + dcl_bus->toIndex()); } } return new VerilogOneNetNameIterator(name); @@ -1329,13 +1334,13 @@ verilogNetScalarNameIterator(const string &name, VerilogNetNameIterator * VerilogNetScalar::nameIterator(VerilogModule *module, - VerilogReader *) + VerilogReader *) { return verilogNetScalarNameIterator(name_.c_str(), module); } VerilogNetBitSelect::VerilogNetBitSelect(const string &name, - int index) : + int index) : VerilogNetNamed(verilogBusBitName(name, index)), index_(index) { @@ -1349,14 +1354,14 @@ VerilogNetBitSelect::size(VerilogModule *) VerilogNetNameIterator * VerilogNetBitSelect::nameIterator(VerilogModule *, - VerilogReader *) + VerilogReader *) { return new VerilogOneNetNameIterator(name_); } VerilogNetPartSelect::VerilogNetPartSelect(const string &name, - int from_index, - int to_index): + int from_index, + int to_index): VerilogNetNamed(name), from_index_(from_index), to_index_(to_index) @@ -1374,13 +1379,13 @@ VerilogNetPartSelect::size(VerilogModule *) VerilogNetNameIterator * VerilogNetPartSelect::nameIterator(VerilogModule *, - VerilogReader *) + VerilogReader *) { return new VerilogBusNetNameIterator(name_.c_str(), from_index_, to_index_); } VerilogNetConstant::VerilogNetConstant(const string *constant, - VerilogReader *reader, + VerilogReader *reader, int line) { parseConstant(constant, reader, line); @@ -1388,7 +1393,7 @@ VerilogNetConstant::VerilogNetConstant(const string *constant, void VerilogNetConstant::parseConstant(const string *constant, - VerilogReader *reader, + VerilogReader *reader, int line) { // Find constant size. @@ -1430,9 +1435,9 @@ VerilogNetConstant::parseConstant(const string *constant, void VerilogNetConstant::parseConstant(const string *constant, - size_t base_idx, - int base, - int digit_bit_count) + size_t base_idx, + int base, + int digit_bit_count) { // Scan the constant from LSD to MSD. size_t size = value_->size(); @@ -1449,9 +1454,9 @@ VerilogNetConstant::parseConstant(const string *constant, unsigned value_digit = strtoul(value_digit_str, &end, base); unsigned mask = 1; for (int b = 0; b < digit_bit_count && bit < size; b++) { - bool value_bit = (value_digit & mask) != 0; - (*value_)[bit++] = value_bit; - mask = mask << 1; + bool value_bit = (value_digit & mask) != 0; + (*value_)[bit++] = value_bit; + mask = mask << 1; } } } @@ -1460,7 +1465,7 @@ VerilogNetConstant::parseConstant(const string *constant, void VerilogNetConstant::parseConstant10(const string *constant, size_t base_idx, - VerilogReader *reader, + VerilogReader *reader, int line) { // Copy the constant skipping underscores. @@ -1479,8 +1484,8 @@ VerilogNetConstant::parseConstant10(const string *constant, || (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 %s not supported.", + constant10_max.c_str()); else { size_t *end = nullptr; VerilogConstant10 value = std::stoull(tmp, end, 10); @@ -1499,11 +1504,11 @@ VerilogNetConstant::~VerilogNetConstant() VerilogNetNameIterator * VerilogNetConstant::nameIterator(VerilogModule *, - VerilogReader *reader) + VerilogReader *reader) { return new VerilogConstantNetNameIterator(value_, - reader->zeroNetName(), - reader->oneNetName()); + reader->zeroNetName(), + reader->oneNetName()); } @@ -1521,25 +1526,22 @@ VerilogNetConcat::VerilogNetConcat(VerilogNetSeq *nets) : VerilogNetConcat::~VerilogNetConcat() { - nets_->deleteContents(); + deleteContents(nets_); delete nets_; } int VerilogNetConcat::size(VerilogModule *module) { - VerilogNetSeq::Iterator net_iter(nets_); int sz = 0; - while (net_iter.hasNext()) { - VerilogNet *net = net_iter.next(); + for (VerilogNet *net : *nets_) sz += net->size(module); - } return sz; } VerilogNetNameIterator * VerilogNetConcat::nameIterator(VerilogModule *module, - VerilogReader *reader) + VerilogReader *reader) { return new VerilogNetConcatNameIterator(nets_, module, reader); } @@ -1577,13 +1579,13 @@ VerilogNetPortRefScalarNet::size(VerilogModule *module) VerilogNetNameIterator * VerilogNetPortRefScalarNet::nameIterator(VerilogModule *module, - VerilogReader *) + VerilogReader *) { return verilogNetScalarNameIterator(net_name_, module); } VerilogNetPortRefScalar::VerilogNetPortRefScalar(const string &name, - VerilogNet *net) : + VerilogNet *net) : VerilogNetPortRef(name), net_(net) { @@ -1605,7 +1607,7 @@ VerilogNetPortRefScalar::size(VerilogModule *module) VerilogNetNameIterator * VerilogNetPortRefScalar::nameIterator(VerilogModule *module, - VerilogReader *reader) + VerilogReader *reader) { if (net_) return net_->nameIterator(module, reader); @@ -1614,17 +1616,17 @@ VerilogNetPortRefScalar::nameIterator(VerilogModule *module, } VerilogNetPortRefBit::VerilogNetPortRefBit(const string &name, - int index, - VerilogNet *net) : + int index, + VerilogNet *net) : VerilogNetPortRefScalar(name, net), bit_name_(verilogBusBitName(name, index)) { } VerilogNetPortRefPart::VerilogNetPortRefPart(const string &name, - int from_index, - int to_index, - VerilogNet *net) : + int from_index, + int to_index, + VerilogNet *net) : VerilogNetPortRefBit(name, from_index, net), to_index_(to_index) { @@ -1662,7 +1664,7 @@ VerilogAttrStmt::VerilogAttrStmt(VerilogAttrEntrySeq *attrs): VerilogAttrStmt::~VerilogAttrStmt() { - attrs_->deleteContents(); + deleteContents(attrs_); delete attrs_; } @@ -1680,20 +1682,20 @@ VerilogAttrStmt::attrs() //////////////////////////////////////////////////////////////// // Verilog net name to network net map. -typedef Map BindingMap; +using BindingMap = std::map; class VerilogBindingTbl { public: VerilogBindingTbl(const string &zero_net_name_, - const string &one_net_name_); + const string &one_net_name_); Net *ensureNetBinding(const char *net_name, - Instance *inst, - NetworkReader *network); + Instance *inst, + NetworkReader *network); Net *find(const char *name, - NetworkReader *network); + NetworkReader *network); void bind(const char *name, - Net *net); + Net *net); private: const string &zero_net_name_; @@ -1713,33 +1715,31 @@ VerilogReader::linkNetwork(const char *top_cell_name, // Seed the recursion for expansion with the top level instance. Instance *top_instance = network_->makeInstance(top_cell, top_cell_name, nullptr); VerilogBindingTbl bindings(zero_net_name_, one_net_name_); - VerilogNetSeq::Iterator port_iter(module->ports()); - while (port_iter.hasNext()) { - VerilogNet *mod_port = port_iter.next(); - VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module, - this); - while (net_name_iter->hasNext()) { - const 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_); - // Guard against repeated port name. - if (network_->findPin(top_instance, port) == nullptr) { - Pin *pin = network_->makePin(top_instance, port, nullptr); - network_->makeTerm(pin, net); - } - } - delete net_name_iter; + for (VerilogNet *mod_port : *module->ports()) { + VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module, + this); + while (net_name_iter->hasNext()) { + const 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_); + // Guard against repeated port name. + if (network_->findPin(top_instance, port) == nullptr) { + Pin *pin = network_->makePin(top_instance, port, nullptr); + network_->makeTerm(pin, net); + } + } + delete net_name_iter; } makeModuleInstBody(module, top_instance, &bindings, make_black_boxes); bool errors = reportLinkErrors(); if (delete_modules) deleteModules(); if (errors) { - network_->deleteInstance(top_instance); - return nullptr; + network_->deleteInstance(top_instance); + return nullptr; } else - return top_instance; + return top_instance; } else { report_->error(1398, "%s is not a verilog module.", top_cell_name); @@ -1754,50 +1754,46 @@ VerilogReader::linkNetwork(const char *top_cell_name, void VerilogReader::makeModuleInstBody(VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings, - bool make_black_boxes) + Instance *inst, + VerilogBindingTbl *bindings, + bool make_black_boxes) { - VerilogStmtSeq::Iterator stmt_iter(module->stmts()); - while (stmt_iter.hasNext()) { - VerilogStmt *stmt = stmt_iter.next(); + for (VerilogStmt *stmt : *module->stmts()) { if (stmt->isModuleInst()) makeModuleInstNetwork(dynamic_cast(stmt), - inst, module, bindings, make_black_boxes); + inst, module, bindings, make_black_boxes); else if (stmt->isLibertyInst()) makeLibertyInst(dynamic_cast(stmt), - inst, module, bindings); + inst, module, bindings); else if (stmt->isDeclaration()) { VerilogDcl *dcl = dynamic_cast(stmt); PortDirection *dir = dcl->direction(); - VerilogDclArgSeq::Iterator arg_iter(dcl->args()); - while (arg_iter.hasNext()) { - VerilogDclArg *arg = arg_iter.next(); - VerilogAssign *assign = arg->assign(); - if (assign) - mergeAssignNet(assign, module, inst, bindings); - if (dir->isGround()) { - 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_); - network_->addConstantNet(net, LogicValue::one); - } + 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_); + network_->addConstantNet(net, LogicValue::zero); + } + if (dir->isPower()) { + 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); + bindings); } } void VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool make_black_boxes) + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool make_black_boxes) { const string &module_name = mod_inst->moduleName(); Cell *cell = network_->findAnyCell(module_name.c_str()); @@ -1806,22 +1802,22 @@ 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 %s not found. Creating black box for %s.", + mod_inst->moduleName().c_str(), + inst_vname.c_str()); } 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 %s not found for instance %s.", + mod_inst->moduleName().c_str(), + inst_vname.c_str()); } 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); + parent); VerilogAttrStmtSeq *attr_stmts = mod_inst->attrStmts(); for (VerilogAttrStmt *stmt : *attr_stmts) { for (VerilogAttrEntry *entry : *stmt->attrs()) { @@ -1833,19 +1829,19 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, // Make all pins so timing arcs are built. LibertyCellPortBitIterator port_iter(lib_cell); while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); - network_->makePin(inst, reinterpret_cast(port), nullptr); + LibertyPort *port = port_iter.next(); + network_->makePin(inst, reinterpret_cast(port), nullptr); } } bool is_leaf = network_->isLeaf(cell); 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); @@ -1857,96 +1853,97 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, void VerilogReader::makeNamedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf) + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool is_leaf) { string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); - VerilogNetSeq::Iterator pin_iter(mod_inst->pins()); - while (pin_iter.hasNext()) { - VerilogNetPortRef *vpin = dynamic_cast(pin_iter.next()); + for (auto mpin : *mod_inst->pins()) { + 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)) { - 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), - vpin->size(parent_module)); + && 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), + vpin->size(parent_module)); } else { - VerilogNetNameIterator *net_name_iter = - 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); - } - delete port_iter; - } - else { - makeInstPin(inst, port, net_name_iter, bindings, - parent, parent_bindings, is_leaf); - } - delete net_name_iter; + VerilogNetNameIterator *net_name_iter = + 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); + } + delete port_iter; + } + else { + 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 %s port %s not found.", + inst_vname.c_str(), + port_name); } } void VerilogReader::makeOrderedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf) + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool is_leaf) { CellPortIterator *port_iter = network_->portIterator(cell); - VerilogNetSeq::Iterator pin_iter(mod_inst->pins()); - while (pin_iter.hasNext() && port_iter->hasNext()) { - VerilogNet *net = pin_iter.next(); + VerilogNetSeq *mod_pins = mod_inst->pins(); + VerilogNetSeq::iterator pin_iter = mod_pins->begin(); + 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)) { 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), - net->size(parent_module)); + "instance %s port %s size %d does not match net size %d.", + inst_vname.c_str(), + network_->name(port), + network_->size(port), + net->size(parent_module)); } else { VerilogNetNameIterator *net_name_iter=net->nameIterator(parent_module, - this); + 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); - } - delete member_iter; + 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); + } + 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; } } @@ -1955,28 +1952,28 @@ VerilogReader::makeOrderedInstPins(Cell *cell, void VerilogReader::makeInstPin(Instance *inst, - Port *port, - VerilogNetNameIterator *net_name_iter, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf) + Port *port, + VerilogNetNameIterator *net_name_iter, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf) { 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); + is_leaf); } void VerilogReader::makeInstPin(Instance *inst, - Port *port, - const string &net_name, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf) + Port *port, + const string &net_name, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf) { Net *net = nullptr; if (!net_name.empty()) @@ -1998,14 +1995,14 @@ VerilogReader::makeInstPin(Instance *inst, void VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings) + Instance *parent, + VerilogModule *parent_module, + 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); + parent); VerilogAttrStmtSeq *attr_stmts = lib_inst->attrStmts(); for (VerilogAttrStmt *stmt : *attr_stmts) { for (VerilogAttrEntry *entry : *stmt->attrs()) { @@ -2043,11 +2040,11 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, Cell * VerilogReader::makeBlackBox(VerilogModuleInst *mod_inst, - VerilogModule *parent_module) + VerilogModule *parent_module) { const string &module_name = mod_inst->moduleName(); Cell *cell = network_->makeCell(library_, module_name.c_str(), true, - parent_module->filename()); + parent_module->filename()); if (mod_inst->namedPins()) makeBlackBoxNamedPorts(cell, mod_inst, parent_module); else @@ -2057,12 +2054,11 @@ VerilogReader::makeBlackBox(VerilogModuleInst *mod_inst, void VerilogReader::makeBlackBoxNamedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module) + VerilogModuleInst *mod_inst, + VerilogModule *parent_module) { - VerilogNetSeq::Iterator pin_iter(mod_inst->pins()); - while (pin_iter.hasNext()) { - VerilogNetNamed *vpin = dynamic_cast(pin_iter.next()); + for (VerilogNet *mpin : *mod_inst->pins()) { + VerilogNetNamed *vpin = dynamic_cast(mpin); const char *port_name = vpin->name().c_str(); size_t size = vpin->size(parent_module); Port *port = (size == 1) @@ -2074,21 +2070,22 @@ VerilogReader::makeBlackBoxNamedPorts(Cell *cell, void VerilogReader::makeBlackBoxOrderedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module) + VerilogModuleInst *mod_inst, + VerilogModule *parent_module) { int port_index = 0; - VerilogNetSeq::Iterator pin_iter(mod_inst->pins()); - while (pin_iter.hasNext()) { - VerilogNet *net = pin_iter.next(); - size_t size = net->size(parent_module); - char *port_name = stringPrint("p_%d", port_index); - Port *port = (size == 1) - ? network_->makePort(cell, port_name) - : network_->makeBusPort(cell, port_name, size - 1, 0); - stringDelete(port_name); - network_->setDirection(port, PortDirection::unknown()); - port_index++; + VerilogNetSeq *nets = mod_inst->pins(); + if (nets) { + for (VerilogNet *net : *nets) { + size_t size = net->size(parent_module); + char *port_name = stringPrint("p_%d", port_index); + Port *port = (size == 1) + ? network_->makePort(cell, port_name) + : network_->makeBusPort(cell, port_name, size - 1, 0); + stringDelete(port_name); + network_->setDirection(port, PortDirection::unknown()); + port_index++; + } } } @@ -2102,9 +2099,9 @@ VerilogReader::isBlackBox(Cell *cell) void VerilogReader::mergeAssignNet(VerilogAssign *assign, - VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings) + VerilogModule *module, + Instance *inst, + VerilogBindingTbl *bindings) { VerilogNet *lhs = assign->lhs(); VerilogNet *rhs = assign->rhs(); @@ -2120,9 +2117,9 @@ VerilogReader::mergeAssignNet(VerilogAssign *assign, // instances from the bottom up does not reference deleted nets // by referencing the mergedInto field. if (hierarchyLevel(lhs_net,network_) >= hierarchyLevel(rhs_net,network_)) - network_->mergeInto(lhs_net, rhs_net); + network_->mergeInto(lhs_net, rhs_net); else - network_->mergeInto(rhs_net, lhs_net); + network_->mergeInto(rhs_net, lhs_net); // No need to update binding tables because the VerilogBindingTbl::find // finds the net that survives the merge. } @@ -2131,14 +2128,14 @@ 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 %d not equal right hand size %d.", + lhs->size(module), + rhs->size(module)); } static int hierarchyLevel(Net *net, - Network *network) + Network *network) { Instance *parent = network->instance(net); int level = 0; @@ -2152,7 +2149,7 @@ hierarchyLevel(Net *net, //////////////////////////////////////////////////////////////// VerilogBindingTbl::VerilogBindingTbl(const string &zero_net_name, - const string &one_net_name) : + const string &one_net_name) : zero_net_name_(zero_net_name), one_net_name_(one_net_name) { @@ -2164,7 +2161,7 @@ VerilogBindingTbl::VerilogBindingTbl(const string &zero_net_name, Net * VerilogBindingTbl::find(const char *name, NetworkReader *network) { - Net *net = map_.findKey(name); + Net *net = findKey(map_, name); while (net && network->mergedInto(net)) net = network->mergedInto(net); return net; @@ -2172,15 +2169,15 @@ VerilogBindingTbl::find(const char *name, NetworkReader *network) void VerilogBindingTbl::bind(const char *name, - Net *net) + Net *net) { map_[name] = net; } Net * VerilogBindingTbl::ensureNetBinding(const char *net_name, - Instance *inst, - NetworkReader *network) + Instance *inst, + NetworkReader *network) { Net *net = find(net_name, network); if (net == nullptr) { @@ -2199,8 +2196,8 @@ VerilogBindingTbl::ensureNetBinding(const char *net_name, void VerilogReader::linkWarn(int id, const char *filename, - int line, - const char *msg, ...) + int line, + const char *msg, ...) { va_list args; va_start(args, msg); @@ -2213,8 +2210,8 @@ VerilogReader::linkWarn(int id, void VerilogReader::linkError(int id, const char *filename, - int line, - const char *msg, ...) + int line, + const char *msg, ...) { va_list args; va_start(args, msg); @@ -2231,9 +2228,7 @@ VerilogReader::reportLinkErrors() // they are discovered. sort(link_errors_, VerilogErrorCmp()); bool errors = false; - VerilogErrorSeq::Iterator error_iter(link_errors_); - while (error_iter.hasNext()) { - VerilogError *error = error_iter.next(); + for (VerilogError *error : link_errors_) { // Report as warnings to avoid throwing. report_->fileWarn(error->id(), error->filename(), error->line(), "%s", error->msg()); errors |= !error->warn(); diff --git a/verilog/VerilogReader.hh b/verilog/VerilogReader.hh deleted file mode 100644 index aca56682d..000000000 --- a/verilog/VerilogReader.hh +++ /dev/null @@ -1,296 +0,0 @@ -// 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 - -#include "StringSet.hh" -#include "Vector.hh" -#include "Map.hh" -#include "NetworkClass.hh" - -namespace sta { - -class VerilogScanner; -class VerilogParse; -class Debug; -class Report; -class VerilogAttrEntry; -class VerilogAttrStmt; -class VerilogReader; -class VerilogStmt; -class VerilogNet; -class VerilogNetScalar; -class VerilogModule; -class VerilogInst; -class VerilogModuleInst; -class VerilogLibertyInst; -class VerilogDcl; -class VerilogDclBus; -class VerilogDclArg; -class VerilogAssign; -class VerilogNetConcat; -class VerilogNetConstant; -class VerilogNetBitSelect; -class VerilogNetPartSelect; -class StringRegistry; -class VerilogBindingTbl; -class VerilogNetNameIterator; -class VerilogNetPortRef; -class VerilogError; -class LibertyCell; - -typedef Map VerilogModuleMap; -typedef Vector VerilogStmtSeq; -typedef Vector VerilogNetSeq; -typedef Vector VerilogDclArgSeq; -typedef Vector VerilogAttrStmtSeq; -typedef Vector VerilogAttrEntrySeq; -typedef Vector VerilogErrorSeq; - -class VerilogReader -{ -public: - VerilogReader(NetworkReader *network); - ~VerilogReader(); - bool read(const char *filename); - - void makeModule(const std::string *module_name, - VerilogNetSeq *ports, - VerilogStmtSeq *stmts, - VerilogAttrStmtSeq *attr_stmts, - int line); - void makeModule(const std::string *module_name, - VerilogStmtSeq *port_dcls, - VerilogStmtSeq *stmts, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogDcl *makeDcl(PortDirection *dir, - VerilogDclArgSeq *args, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogDcl *makeDcl(PortDirection *dir, - VerilogDclArg *arg, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogDclArg *makeDclArg(const std::string *net_name); - VerilogDclArg*makeDclArg(VerilogAssign *assign); - VerilogDclBus *makeDclBus(PortDirection *dir, - int from_index, - int to_index, - VerilogDclArg *arg, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogDclBus *makeDclBus(PortDirection *dir, - int from_index, - int to_index, - VerilogDclArgSeq *args, - VerilogAttrStmtSeq *attr_stmts, - int line); - VerilogInst *makeModuleInst(const std::string *module_name, - const std::string *inst_name, - VerilogNetSeq *pins, - VerilogAttrStmtSeq *attr_stmts, - const int line); - VerilogAssign *makeAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line); - VerilogNetScalar *makeNetScalar(const std::string *name); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_vname); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_name, - const std::string *net_name); - VerilogNetPortRef *makeNetNamedPortRefBitSelect(const std::string *port_name, - const std::string *bus_name, - int index); - VerilogNetPortRef *makeNetNamedPortRefScalar(const std::string *port_name, - VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefBit(const std::string *port_name, - int index, - VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefPart(const std::string *port_name, - int from_index, - int to_index, - VerilogNet *net); - VerilogNetConcat *makeNetConcat(VerilogNetSeq *nets); - VerilogNetConstant *makeNetConstant(const std::string *constant, - int line); - VerilogNetBitSelect *makeNetBitSelect(const std::string *name, - int index); - VerilogNetPartSelect *makeNetPartSelect(const std::string *name, - int from_index, - int to_index); - VerilogModule *module(Cell *cell); - Instance *linkNetwork(const char *top_cell_name, - bool make_black_boxes, - bool delete_modules); - const char *filename() const { return filename_.c_str(); } - void incrLine(); - Report *report() const { return report_; } - void error(int id, - const char *filename, - int line, - const char *fmt, ...); - void warn(int id, - const char *filename, - int line, - const char *fmt, ...); - const std::string &zeroNetName() const { return zero_net_name_; } - const std::string &oneNetName() const { return one_net_name_; } - void deleteModules(); - void reportStmtCounts(); - const std::string &constant10Max() const { return constant10_max_; } - -protected: - void init(const char *filename); - void makeCellPorts(Cell *cell, - VerilogModule *module, - VerilogNetSeq *ports); - Port *makeCellPort(Cell *cell, - VerilogModule *module, - const std::string &port_name); - void makeNamedPortRefCellPorts(Cell *cell, - VerilogModule *module, - VerilogNet *mod_port, - StdStringSet &port_names); - void checkModuleDcls(VerilogModule *module, - std::set &port_names); - void makeModuleInstBody(VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings, - bool make_black_boxes); - void makeModuleInstNetwork(VerilogModuleInst *mod_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool make_black_boxes); - void makeLibertyInst(VerilogLibertyInst *lib_inst, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings); - void bindGlobalNets(VerilogBindingTbl *bindings); - void makeNamedInstPins1(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void makeNamedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void makeOrderedInstPins(Cell *cell, - Instance *inst, - VerilogModuleInst *mod_inst, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogModule *parent_module, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void mergeAssignNet(VerilogAssign *assign, - VerilogModule *module, - Instance *inst, - VerilogBindingTbl *bindings); - void makeInstPin(Instance *inst, - Port *port, - VerilogNetNameIterator *net_name_iter, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void makeInstPin(Instance *inst, - Port *port, - const std::string &net_name, - VerilogBindingTbl *bindings, - Instance *parent, - VerilogBindingTbl *parent_bindings, - bool is_leaf); - void linkWarn(int id, - const char *filename, - int line, - const char *msg, ...) - __attribute__((format (printf, 5, 6))); - void linkError(int id, - const char *filename, - int line, - const char *msg, ...) - __attribute__((format (printf, 5, 6))); - bool reportLinkErrors(); - bool haveLinkErrors(); - Cell *makeBlackBox(VerilogModuleInst *mod_inst, - VerilogModule *parent_module); - void makeBlackBoxNamedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module); - void makeBlackBoxOrderedPorts(Cell *cell, - VerilogModuleInst *mod_inst, - VerilogModule *parent_module); - bool isBlackBox(Cell *cell); - bool hasScalarNamedPortRefs(LibertyCell *liberty_cell, - VerilogNetSeq *pins); - - std::string filename_; - Report *report_; - Debug *debug_; - NetworkReader *network_; - - Library *library_; - int black_box_index_; - VerilogModuleMap module_map_; - VerilogErrorSeq link_errors_; - const std::string zero_net_name_; - const std::string one_net_name_; - std::string constant10_max_; - ViewType *view_type_; - bool report_stmt_stats_; - int module_count_; - int inst_mod_count_; - int inst_lib_count_; - int inst_lib_net_arrays_; - int port_names_; - int inst_module_names_; - int inst_names_; - int dcl_count_; - int dcl_bus_count_; - int dcl_arg_count_; - int net_scalar_count_; - int net_scalar_names_; - int net_bus_names_; - int net_part_select_count_; - int net_bit_select_count_; - int net_port_ref_scalar_count_; - int net_port_ref_scalar_net_count_; - int net_port_ref_bit_count_; - int net_port_ref_part_count_; - int net_constant_count_; - int assign_count_; - int concat_count_; -}; - -} // namespace sta diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index aa1dc207c..60498c7db 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -25,16 +25,16 @@ #pragma once #include +#include +#include -#include "Map.hh" -#include "Vector.hh" #include "StringSeq.hh" namespace sta { -typedef Map VerilogDclMap; -typedef Vector VerilogConstantValue; -typedef std::vector StdStringSeq; +using VerilogDclMap = std::map; +using VerilogConstantValue = std::vector; +using StdStringSeq = std::vector; class VerilogStmt { @@ -71,13 +71,13 @@ public: VerilogStmtSeq *stmts() { return stmts_; } VerilogDclMap *declarationMap() { return &dcl_map_; } void parseDcl(VerilogDcl *dcl, - VerilogReader *reader); + VerilogReader *reader); private: void parseStmts(VerilogReader *reader); void checkInstanceName(VerilogInst *inst, - StdStringSet &inst_names, - VerilogReader *reader); + StdStringSet &inst_names, + VerilogReader *reader); std::string name_; std::string filename_; @@ -160,8 +160,8 @@ class VerilogAssign : public VerilogStmt { public: VerilogAssign(VerilogNet *lhs, - VerilogNet *rhs, - int line); + VerilogNet *rhs, + int line); virtual ~VerilogAssign(); virtual bool isAssign() const { return true; } VerilogNet *lhs() const { return lhs_; } @@ -241,7 +241,7 @@ public: virtual bool isNamedPortRefScalarNet() const { return false; } virtual int size(VerilogModule *module) = 0; virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader) = 0; + VerilogReader *reader) = 0; }; class VerilogNetUnnamed : public VerilogNet @@ -276,19 +276,19 @@ public: virtual bool isScalar() const { return true; } virtual int size(VerilogModule *module); virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + VerilogReader *reader); }; class VerilogNetBitSelect : public VerilogNetNamed { public: VerilogNetBitSelect(const std::string &name, - int index); + int index); int index() { return index_; } virtual bool isScalar() const { return false; } virtual int size(VerilogModule *module); virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + VerilogReader *reader); private: int index_; }; @@ -297,12 +297,12 @@ class VerilogNetPartSelect : public VerilogNetNamed { public: VerilogNetPartSelect(const std::string &name, - int from_index, - int to_index); + int from_index, + int to_index); virtual bool isScalar() const { return false; } virtual int size(VerilogModule *module); virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + VerilogReader *reader); int fromIndex() const { return from_index_; } int toIndex() const { return to_index_; } @@ -315,24 +315,24 @@ class VerilogNetConstant : public VerilogNetUnnamed { public: VerilogNetConstant(const std::string *constant, - VerilogReader *reader, + VerilogReader *reader, int line); virtual ~VerilogNetConstant(); virtual int size(VerilogModule *module); virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + VerilogReader *reader); private: void parseConstant(const std::string *constant, - VerilogReader *reader, + VerilogReader *reader, int line); void parseConstant(const std::string *constant, - size_t base_idx, - int base, - int digit_bit_count); + size_t base_idx, + int base, + int digit_bit_count); void parseConstant10(const std::string *constant, size_t base_idx, - VerilogReader *reader, + VerilogReader *reader, int line); VerilogConstantValue *value_; @@ -345,7 +345,7 @@ public: virtual ~VerilogNetConcat(); virtual int size(VerilogModule *module); virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + VerilogReader *reader); private: VerilogNetSeq *nets_; @@ -369,12 +369,12 @@ class VerilogNetPortRefScalarNet : public VerilogNetPortRef public: VerilogNetPortRefScalarNet(const std::string &name); VerilogNetPortRefScalarNet(const std::string &name, - const std::string &net_name); + const std::string &net_name); virtual bool isScalar() const { return true; } virtual bool isNamedPortRefScalarNet() const { return true; } virtual int size(VerilogModule *module); virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + VerilogReader *reader); virtual bool hasNet() { return !net_name_.empty(); } const std::string &netName() const { return net_name_; } void setNetName(const std::string &net_name) { net_name_ = net_name; } @@ -387,12 +387,12 @@ class VerilogNetPortRefScalar : public VerilogNetPortRef { public: VerilogNetPortRefScalar(const std::string &name, - VerilogNet *net); + VerilogNet *net); virtual ~VerilogNetPortRefScalar(); virtual bool isScalar() const { return true; } virtual int size(VerilogModule *module); virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + VerilogReader *reader); virtual bool hasNet() { return net_ != nullptr; } private: @@ -403,8 +403,8 @@ class VerilogNetPortRefBit : public VerilogNetPortRefScalar { public: VerilogNetPortRefBit(const std::string &name, - int index, - VerilogNet *net); + int index, + VerilogNet *net); const std::string &name() const override { return bit_name_; } private: @@ -415,9 +415,9 @@ class VerilogNetPortRefPart : public VerilogNetPortRefBit { public: VerilogNetPortRefPart(const std::string &name, - int from_index, - int to_index, - VerilogNet *net); + int from_index, + int to_index, + VerilogNet *net); const std::string &name() const override; int toIndex() const { return to_index_; } diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index dd856d7b5..3ba02ba99 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -26,6 +26,7 @@ #include #include +#include #include "Error.hh" #include "Liberty.hh" @@ -45,10 +46,10 @@ class VerilogWriter { public: VerilogWriter(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells, - FILE *stream, - Network *network); + bool include_pwr_gnd, + CellSeq *remove_cells, + FILE *stream, + Network *network); void writeModules(); protected: @@ -64,14 +65,14 @@ class VerilogWriter void writeChildren(const Instance *inst); void writeChild(const Instance *child); void writeInstPin(const Instance *inst, - const Port *port, - bool &first_port); + const Port *port, + bool &first_port); void writeInstBusPin(const Instance *inst, - const Port *port, - bool &first_port); + const Port *port, + bool &first_port); void writeInstBusPinBit(const Instance *inst, - const Port *port, - bool &first_member); + const Port *port, + bool &first_member); void writeAssigns(const Instance *inst); int findUnconnectedNetCount(const Instance *inst); @@ -89,15 +90,15 @@ class VerilogWriter void writeVerilog(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells, - Network *network) + bool include_pwr_gnd, + CellSeq *remove_cells, + Network *network) { if (network->topInstance()) { FILE *stream = fopen(filename, "w"); if (stream) { VerilogWriter writer(filename, include_pwr_gnd, - remove_cells, stream, network); + remove_cells, stream, network); writer.writeModules(); fclose(stream); } @@ -107,10 +108,10 @@ writeVerilog(const char *filename, } VerilogWriter::VerilogWriter(const char *filename, - bool include_pwr_gnd, - CellSeq *remove_cells, - FILE *stream, - Network *network) : + bool include_pwr_gnd, + CellSeq *remove_cells, + FILE *stream, + Network *network) : filename_(filename), include_pwr_gnd_(include_pwr_gnd), remove_cells_(network), @@ -161,7 +162,7 @@ VerilogWriter::findHierChildren(const Instance *inst, const Instance *child = child_iter->next(); const Cell *cell = network_->cell(child); if (network_->isHierarchical(child) - && !cells.hasKey(cell)) { + && !cells.contains(cell)) { children.push_back(child); cells.insert(cell); findHierChildren(child, children, cells); @@ -262,14 +263,14 @@ VerilogWriter::verilogPortDir(PortDirection *dir) } } -typedef std::pair BusIndexRange; +using BusIndexRange = std::pair; void VerilogWriter::writeWireDcls(const Instance *inst) { Cell *cell = network_->cell(inst); char escape = network_->pathEscape(); - Map> bus_ranges; + std::map> bus_ranges; NetIterator *net_iter = network_->netIterator(inst); while (net_iter->hasNext()) { Net *net = net_iter->next(); @@ -313,7 +314,7 @@ VerilogWriter::writeWireDcls(const Instance *inst) void VerilogWriter::writeChildren(const Instance *inst) { - Vector children; + std::vector children; InstanceChildIterator *child_iter = network_->childIterator(inst); while (child_iter->hasNext()) { Instance *child = child_iter->next(); @@ -334,23 +335,23 @@ void VerilogWriter::writeChild(const Instance *child) { Cell *child_cell = network_->cell(child); - if (!remove_cells_.hasKey(child_cell)) { + if (!remove_cells_.contains(child_cell)) { const char *child_name = network_->name(child); string child_vname = instanceVerilogName(child_name); string child_cell_vname = cellVerilogName(network_->name(child_cell)); fprintf(stream_, " %s %s (", - child_cell_vname.c_str(), - child_vname.c_str()); + child_cell_vname.c_str(), + child_vname.c_str()); bool first_port = true; CellPortIterator *port_iter = network_->portIterator(child_cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); if (include_pwr_gnd_ - || !network_->direction(port)->isPowerGround()) { - if (network_->hasMembers(port)) - writeInstBusPin(child, port, first_port); - else - writeInstPin(child, port, first_port); + || !network_->direction(port)->isPowerGround()) { + if (network_->hasMembers(port)) + writeInstBusPin(child, port, first_port); + else + writeInstPin(child, port, first_port); } } delete port_iter; @@ -360,8 +361,8 @@ VerilogWriter::writeChild(const Instance *child) void VerilogWriter::writeInstPin(const Instance *inst, - const Port *port, - bool &first_port) + const Port *port, + bool &first_port) { Pin *pin = network_->findPin(inst, port); if (pin) { @@ -370,11 +371,11 @@ VerilogWriter::writeInstPin(const Instance *inst, const char *net_name = network_->name(net); string net_vname = netVerilogName(net_name); if (!first_port) - fprintf(stream_, ",\n "); + fprintf(stream_, ",\n "); string port_vname = portVerilogName(network_->name(port)); fprintf(stream_, ".%s(%s)", - port_vname.c_str(), - net_vname.c_str()); + port_vname.c_str(), + net_vname.c_str()); first_port = false; } } @@ -382,8 +383,8 @@ VerilogWriter::writeInstPin(const Instance *inst, void VerilogWriter::writeInstBusPin(const Instance *inst, - const Port *port, - bool &first_port) + const Port *port, + bool &first_port) { if (!first_port) fprintf(stream_, ",\n "); @@ -416,8 +417,8 @@ VerilogWriter::writeInstBusPin(const Instance *inst, void VerilogWriter::writeInstBusPinBit(const Instance *inst, - const Port *port, - bool &first_member) + const Port *port, + bool &first_member) { Pin *pin = network_->findPin(inst, port); Net *net = pin ? network_->net(pin) : nullptr; @@ -485,7 +486,7 @@ VerilogWriter::findChildNCcount(const Instance *child) { int nc_count = 0; Cell *child_cell = network_->cell(child); - if (!remove_cells_.hasKey(child_cell)) { + if (!remove_cells_.contains(child_cell)) { CellPortIterator *port_iter = network_->portIterator(child_cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); From 4fbe16e5a42baf59f74f83c384291a0b24affb58 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 4 Jan 2026 10:31:41 -0800 Subject: [PATCH 002/181] ApiChanges Signed-off-by: James Cherry --- dcalc/PrimaDelayCalc.cc | 3 ++- doc/ApiChanges.txt | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 6379b85ef..188d533d8 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -197,7 +197,8 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin, { 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); + ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, + scene, min_max); return dcalc_results[0]; } diff --git a/doc/ApiChanges.txt b/doc/ApiChanges.txt index 553db6b3f..34136d76f 100644 --- a/doc/ApiChanges.txt +++ b/doc/ApiChanges.txt @@ -24,7 +24,7 @@ This file summarizes STA API changes for each release. -Release 3.0.0 2025/11/26 +Release 3.0.0 2025/01/03 ------------------------ OpenSTA now requires c++ 20. @@ -33,7 +33,6 @@ Corner replaced by Scene mode() parasitics(min_max) DcalcAnalysisPt replaced by scene/min_min -DcalcAnalysisPt replaced by scene/min_min PathAnalysisPt replaced by scene/min_min StaState::sdc_ moved to Mode StaState::sim_ moved to Mode From e5ee0fdb24434e7a6c360fed677aff4c3539f113 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 5 Jan 2026 10:37:32 -0800 Subject: [PATCH 003/181] fmt Signed-off-by: James Cherry --- dcalc/PrimaDelayCalc.hh | 2 +- include/sta/ArcDelayCalc.hh | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index 20e84c241..a5f402c71 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -250,4 +250,4 @@ protected: using ArcDelayCalc::reduceParasitic; }; -} // namespacet +} // namespace diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index 59e431fc6..457de4cf8 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -168,14 +168,14 @@ public: virtual Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const Scene *scene, - const MinMax *min_max) = 0; + const MinMax *min_max) = 0; virtual bool reduceSupported() const = 0; // Reduce parasitic_network to a representation acceptable to the delay calculator. virtual Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, const Scene *scene, - const MinMax *min_max) = 0; + const MinMax *min_max) = 0; // Reduce parasitic_network to a representation acceptable to the delay calculator // for one or more scenes and min/max rise/fall. // Null scene means reduce all scenes. @@ -186,10 +186,10 @@ public: // Set the in_slew, load_cap, parasitic for gates. virtual void setDcalcArgParasiticSlew(ArcDcalcArg &gate, const Scene *scene, - const MinMax *min_max) = 0; + const MinMax *min_max) = 0; virtual void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, const Scene *scene, - const MinMax *min_max) = 0; + const MinMax *min_max) = 0; // Find the wire delays and slews for an input port without a driving cell. // This call primarily initializes the load delay/slew iterator. virtual ArcDcalcResult inputPortDelay(const Pin *port_pin, @@ -209,7 +209,7 @@ public: const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, const Scene *scene, - const MinMax *min_max) = 0; + const MinMax *min_max) = 0; // deprecated 2024-02-27 virtual void gateDelay(const TimingArc *arc, const Slew &in_slew, @@ -227,7 +227,7 @@ public: virtual ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args, const LoadPinIndexMap &load_pin_index_map, const Scene *scene, - const MinMax *min_max) = 0; + const MinMax *min_max) = 0; // Find the delay for a timing check arc given the arc's // from/clock, to/data slews and related output pin parasitic. From 5314489195fff52b501e397dbb2e3b3876706c57 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 13 Jan 2026 09:43:13 -0700 Subject: [PATCH 004/181] rebase Signed-off-by: James Cherry --- power/VcdReader.cc | 2 +- search/Bfs.cc | 2 +- spice/WriteSpice.cc | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 420e1449c..30b0078e0 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -478,7 +478,7 @@ ReadVcdActivities::checkClkPeriod(const Pin *pin, double clk_period = clk->period(); if (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(1453, "clock %s vcd period %s differs from SDC clock period %s", + report_->warn(1452, "clock %s vcd period %s differs from SDC clock period %s", clk->name(), delayAsString(sim_period, this), delayAsString(clk_period, this)); diff --git a/search/Bfs.cc b/search/Bfs.cc index 5ea3d470e..3ae832f55 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -196,7 +196,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( [=](int) { + dispatch_queue_->dispatch( [=, this](int) { for (size_t i = from; i < to; i++) { Vertex *vertex = level_vertices[i]; if (vertex) { diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 9ef7b05da..c6da6e22e 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -872,9 +872,8 @@ WriteSpice::gatePortValues(const Instance *, CUDD_VALUE_TYPE value; DdGen *cube_gen = Cudd_FirstCube(cudd_mgr, diff, &cube, &value); - FuncExprPortIterator port_iter(expr); - while (port_iter.hasNext()) { - const LibertyPort *port = port_iter.next(); + LibertyPortSet ports = expr->ports(); + for (const LibertyPort *port : ports) { if (port != input_port) { DdNode *port_node = bdd_.findNode(port); int var_index = Cudd_NodeReadIndex(port_node); From be56eadb47c7c8afd9c131d883f06521035107c2 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 15 Jan 2026 16:14:42 -0700 Subject: [PATCH 005/181] SearchPred0 not thru timing checks Signed-off-by: James Cherry --- include/sta/Search.hh | 1 - include/sta/SearchPred.hh | 6 ++++-- search/Search.cc | 1 - search/SearchPred.cc | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 9889be75f..09bc16879 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -673,7 +673,6 @@ protected: // Eval across latch D->Q edges. // SearchPred0 unless -// timing check edge // disabled loop // disabled converging clock edge (Xilinx) // clk source pin diff --git a/include/sta/SearchPred.hh b/include/sta/SearchPred.hh index c1e4c21a9..314c3dca7 100644 --- a/include/sta/SearchPred.hh +++ b/include/sta/SearchPred.hh @@ -34,10 +34,12 @@ namespace sta { // Class hierarchy: // SearchPred // SearchAdj (unless loop disabled, latch D->Q, timing check, dynamic loop) -// SearchPred0 (unless disabled or constant) -// EvalPred (unless timing check) +// SearchPred0 (unless timing check, disabled or constant) +// EvalPred (unless dynamic loop breaking) // SearchThru (unless latch D->Q) +// GenClkInsertionSearchPred // SearchPred1 (unless loop disabled) +// FanOutSrchPred // ClkTreeSearchPred (only wire or combinational) // Virtual base class for search predicates. diff --git a/search/Search.cc b/search/Search.cc index be52ad101..248477d4f 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -96,7 +96,6 @@ EvalPred::searchThru(Edge *edge, return SearchPred0::searchThru(edge, mode) && (sta_->variables()->dynamicLoopBreaking() || !edge->isDisabledLoop()) - && !role->isTimingCheck() && (search_thru_latches_ || role->isLatchDtoQ() || sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open); diff --git a/search/SearchPred.cc b/search/SearchPred.cc index da7d52364..e2654efdf 100644 --- a/search/SearchPred.cc +++ b/search/SearchPred.cc @@ -116,7 +116,8 @@ SearchPred0::searchThru(Edge *edge, const Variables *variables = sta_->variables(); const Sdc *sdc = mode->sdc(); const Sim *sim = mode->sim(); - return !(sdc->isDisabledConstraint(edge) + return !(role->isTimingCheck() + || sdc->isDisabledConstraint(edge) // Constants disable edge cond expression. || sim->isDisabledCond(edge) || sdc->isDisabledCondDefault(edge) From fc8d7fad67e8f103e6bb5ee1adb3bdc0dd602c9a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 15 Jan 2026 16:30:52 -0700 Subject: [PATCH 006/181] Sta::isConstant Signed-off-by: James Cherry --- include/sta/Sta.hh | 2 ++ search/Sta.cc | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index e0f307875..4026a59e8 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -538,6 +538,8 @@ public: Sdc *sdc); void removeDisable(TimingArcSet *arc_set, Sdc *sdc); + [[nodiscard]] bool isConstant(const Pin *pin, + const Mode *mode) const; // Edge is disabled by constant. [[nodiscard]] bool isDisabledConstant(Edge *edge, const Mode *mode); diff --git a/search/Sta.cc b/search/Sta.cc index b79a74c9a..92a57470a 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -1715,6 +1715,15 @@ Sta::isDisabledConstraint(Edge *edge, || sdc->isDisabledConstraint(edge); } +bool +Sta::isConstant(const Pin *pin, + const Mode *mode) const +{ + Sim *sim = mode->sim(); + sim->ensureConstantsPropagated(); + return sim->isConstant(pin); +} + bool Sta::isDisabledConstant(Edge *edge, const Mode *mode) From b04ffe737b8ca9363cee4f2c44dab862ec048bf1 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 20 Jan 2026 17:35:25 -0700 Subject: [PATCH 007/181] LibertyBuilder::makeRegLatchArcs null ref resolves #368 Signed-off-by: James Cherry --- liberty/LibertyBuilder.cc | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index fa29f5ecc..c0bc806e9 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -424,25 +424,27 @@ LibertyBuilder::makeRegLatchArcs(LibertyCell *cell, TimingArcAttrsPtr attrs) { FuncExpr *to_func = to_port->function(); - LibertyPortSet to_ports = to_func->ports(); - for (LibertyPort *func_port : to_ports) { - Sequential *seq = cell->outputPortSequential(func_port); - if (seq) { - if (seq->clock() && seq->clock()->hasPort(from_port)) { - const TimingRole *role = seq->isRegister() ? - TimingRole::regClkToQ() : TimingRole::latchEnToQ(); - return makeFromTransitionArcs(cell, from_port, to_port, nullptr, - from_rf, role, attrs); + if (to_func) { + LibertyPortSet to_ports = to_func->ports(); + for (LibertyPort *func_port : to_ports) { + Sequential *seq = cell->outputPortSequential(func_port); + if (seq) { + if (seq->clock() && seq->clock()->hasPort(from_port)) { + const TimingRole *role = seq->isRegister() ? + TimingRole::regClkToQ() : TimingRole::latchEnToQ(); + return makeFromTransitionArcs(cell, from_port, to_port, nullptr, + from_rf, role, attrs); + } + else if (seq->isLatch() + && seq->data() + && seq->data()->hasPort(from_port)) + return makeFromTransitionArcs(cell, from_port, to_port, nullptr, + from_rf, TimingRole::latchDtoQ(), attrs); + else if ((seq->clear() && seq->clear()->hasPort(from_port)) + || (seq->preset() && seq->preset()->hasPort(from_port))) + return makeFromTransitionArcs(cell, from_port, to_port, nullptr, + from_rf, TimingRole::regSetClr(), attrs); } - else if (seq->isLatch() - && seq->data() - && seq->data()->hasPort(from_port)) - return makeFromTransitionArcs(cell, from_port, to_port, nullptr, - from_rf, TimingRole::latchDtoQ(), attrs); - else if ((seq->clear() && seq->clear()->hasPort(from_port)) - || (seq->preset() && seq->preset()->hasPort(from_port))) - return makeFromTransitionArcs(cell, from_port, to_port, nullptr, - from_rf, TimingRole::regSetClr(), attrs); } } // No associated ff/latch - assume register clk->q. From cb8dde127e6752d6a211c32d36118fa5c7c07e67 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 20 Jan 2026 17:50:13 -0700 Subject: [PATCH 008/181] tcl [empty_model net_iterator] resolves @367 Signed-off-by: James Cherry --- network/ConcreteNetwork.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index 6f4b26320..57c419b97 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -112,16 +112,18 @@ class ConcreteInstanceNetIterator : public InstanceNetIterator ConcreteInstanceNetIterator:: ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets): nets_(nets), - iter_(nets->begin()), next_(nullptr) { - findNext(); + if (nets) { + iter_ = nets->begin(); + findNext(); + } } bool ConcreteInstanceNetIterator::hasNext() { - return next_ != nullptr; + return nets_ && next_ != nullptr; } // Skip nets that have been merged. From 7c15c64a484405bf16b9e1d9f7372e9ec065e236 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 21 Jan 2026 10:03:49 -0700 Subject: [PATCH 009/181] Sta::networkChanges/deleteParasitics crash resolves #369, #370 Signed-off-by: James Cherry --- include/sta/Sta.hh | 2 +- search/Sta.cc | 32 +++++++++----------------------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 4026a59e8..0c176ba4d 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -865,7 +865,7 @@ public: //////////////////////////////////////////////////////////////// // User visible but non SDC commands. - // Clear all state except network. + // Clear all state except network, scenes and liberty libraries. virtual void clear(); // Namespace used by command interpreter. CmdNamespace cmdNamespace(); diff --git a/search/Sta.cc b/search/Sta.cc index 92a57470a..07bd6c3c6 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -537,11 +537,6 @@ Sta::clear() { // Sdc holds search filter, so clear search first. search_->clear(); - for (Mode *mode : modes_) { - mode->sdc()->clear(); - mode->clkNetwork()->clkPinsInvalid(); - } - // scenes are NOT cleared because they are used to index liberty files. levelize_->clear(); deleteParasitics(); graph_delay_calc_->clear(); @@ -551,6 +546,13 @@ Sta::clear() if (check_min_periods_) check_min_periods_->clear(); clk_skews_->clear(); + + // scenes are NOT cleared because they are used to index liberty files. + for (Mode *mode : modes_) { + mode->sdc()->clear(); + mode->clkNetwork()->clkPinsInvalid(); + } + delete graph_; graph_ = nullptr; current_instance_ = nullptr; @@ -611,24 +613,7 @@ Sta::findModes(const std::string &name) const void Sta::networkChanged() { - // Everything else from clear(). - search_->clear(); - levelize_->clear(); - deleteContents(parasitics_name_map_); - graph_delay_calc_->clear(); - for (const Mode *mode : modes_) - mode->sim()->clear(); - for (Scene *scene : scenes_) - scene->setParasitics(nullptr, MinMaxAll::minMax()); - if (check_min_pulse_widths_) - check_min_pulse_widths_->clear(); - if (check_min_periods_) - check_min_periods_->clear(); - clk_skews_->clear(); - delete graph_; - graph_ = nullptr; - current_instance_ = nullptr; - updateComponentsState(); + clear(); } void @@ -4253,6 +4238,7 @@ Sta::deleteParasitics() parasitics_name_map_.clear(); parasitics_name_map_[parasitics_default->name()] = parasitics_default; + parasitics_default->clear(); for (Scene *scene : scenes_) scene->setParasitics(parasitics_default, MinMaxAll::minMax()); From 2f9bd750dbd4868b6a84b93695eead2be5625d18 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 23 Jan 2026 09:56:15 -0700 Subject: [PATCH 010/181] error include msg id Signed-off-by: James Cherry --- tcl/Util.tcl | 4 ++-- util/Report.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tcl/Util.tcl b/tcl/Util.tcl index 49028698c..dc038e443 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -209,9 +209,9 @@ proc sta_warn { msg_id msg } { proc sta_error { msg_id msg } { if { ! [is_suppressed $msg_id] } { if { [sdc_filename] != "" } { - error "Error: [file tail [sdc_filename]] line [sdc_file_line], $msg" + error "Error $msg_id: [file tail [sdc_filename]] line [sdc_file_line], $msg" } else { - error "Error: $msg" + error "Error $msg_id: $msg" } } } diff --git a/util/Report.cc b/util/Report.cc index f2d0955d2..0d8dac771 100644 --- a/util/Report.cc +++ b/util/Report.cc @@ -247,7 +247,7 @@ Report::error(int id, va_list args; va_start(args, fmt); // No prefix msg, no \n. - printToBuffer("%d", id); + printToBuffer("%d ", id); printToBufferAppend(fmt, args); va_end(args); throw ExceptionMsg(buffer_, isSuppressed(id)); @@ -259,7 +259,7 @@ Report::verror(int id, va_list args) { // No prefix msg, no \n. - printToBuffer("%d", id); + printToBuffer("%d ", id); printToBufferAppend(fmt, args); throw ExceptionMsg(buffer_, isSuppressed(id)); } From 74f756c77c90310a708264b11001006e708444ea Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 28 Jan 2026 12:18:22 -0700 Subject: [PATCH 011/181] merge Signed-off-by: James Cherry --- search/Sta.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/search/Sta.cc b/search/Sta.cc index 9bf833307..4df32c99d 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4908,8 +4908,6 @@ Sta::deletePinBefore(const Pin *pin) mode->sim()->deletePinBefore(pin); mode->clkNetwork()->deletePinBefore(pin); } - power_->deletePinBefore(pin); - clk_skews_->clear(); } void From 5605979049f9f3fa9154d6587b60e3bf2a145aa3 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 29 Jan 2026 16:35:54 -0700 Subject: [PATCH 012/181] Sta::netorkChangedNonSdc resolves #372 Signed-off-by: James Cherry --- include/sta/Sta.hh | 6 +++++- network/NetworkEdit.i | 7 +++++++ search/Sta.cc | 18 +++++++++++++++--- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 3bd008cfc..f5056ee03 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -866,7 +866,9 @@ public: // User visible but non SDC commands. // Clear all state except network, scenes and liberty libraries. - virtual void clear(); + void clear(); + // Clear all state except network, scenes liberty libraries, and sdc. + void clearNonSdc(); // Namespace used by command interpreter. CmdNamespace cmdNamespace(); void setCmdNamespace(CmdNamespace namespc); @@ -1261,6 +1263,8 @@ public: // editing API. For example, reading a netlist without using the // builtin network readers. void networkChanged(); + // Network changed but all SDC references to instance/net/pin/port are preserved. + void networkChangedNonSdc(); void deleteLeafInstanceBefore(const Instance *inst); void deleteInstancePinsBefore(const Instance *inst); diff --git a/network/NetworkEdit.i b/network/NetworkEdit.i index 8584ff0a4..4436c3aeb 100644 --- a/network/NetworkEdit.i +++ b/network/NetworkEdit.i @@ -104,4 +104,11 @@ network_changed() Sta::sta()->networkChanged(); } +// Notify STA of network change without touching SDC network references. +void +network_changed_non_sdc() +{ + Sta::sta()->networkChangedNonSdc(); +} + %} // inline diff --git a/search/Sta.cc b/search/Sta.cc index 4df32c99d..7b7a21825 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -534,6 +534,14 @@ Sta::~Sta() void Sta::clear() +{ + clearNonSdc(); + for (Mode *mode : modes_) + mode->sdc()->clear(); +} + +void +Sta::clearNonSdc() { // Sdc holds search filter, so clear search first. search_->clear(); @@ -548,10 +556,8 @@ Sta::clear() clk_skews_->clear(); // scenes are NOT cleared because they are used to index liberty files. - for (Mode *mode : modes_) { - mode->sdc()->clear(); + for (Mode *mode : modes_) mode->clkNetwork()->clkPinsInvalid(); - } delete graph_; graph_ = nullptr; @@ -616,6 +622,12 @@ Sta::networkChanged() clear(); } +void +Sta::networkChangedNonSdc() +{ + clearNonSdc(); +} + void Sta::setTclInterp(Tcl_Interp *interp) { From 78f579400c62297f76da4921d3f8e9aa86895628 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 3 Feb 2026 09:03:37 -0700 Subject: [PATCH 013/181] cursor coding-standards Signed-off-by: James Cherry --- .cursor/rules/cpp-coding-standards.mdc | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .cursor/rules/cpp-coding-standards.mdc diff --git a/.cursor/rules/cpp-coding-standards.mdc b/.cursor/rules/cpp-coding-standards.mdc new file mode 100644 index 000000000..34c6cfe6b --- /dev/null +++ b/.cursor/rules/cpp-coding-standards.mdc @@ -0,0 +1,33 @@ +--- +description: C++ coding standards and formatting for OpenSTA +globs: ["**/*.cc", "**/*.hh", "**/*.h"] +alwaysApply: false +--- + +# C++ Coding Standards + +## Line Width + +- **Keep lines under 90 characters** to match `.clang-format` (ColumnLimit: 90). +- Break long lines at logical points: after commas, before operators, after opening parens. + +## Naming Conventions + +- **Classes**: Upper camel case (`ClassName`) +- **Member functions**: Lower camel case (`memberFunction`) +- **Member variables**: Snake case with trailing underscore (`member_variable_`) +- **Functions**: Lower camel case (`functionName`) +- **Variables**: Snake case + +## Code Style + +- Use `#pragma once` for header guards. +- Return type on the line before the function name; arguments on separate lines when long. +- No braces for single-line if/for; use braces for multi-line bodies. +- Prefer `std::string` over `char*` for string members. +- Prefer pass-by-value and move for sink parameters (parameters that get stored). + +## File Extensions + +- C++ source: `.cc` +- C++ headers: `.hh` From 98d0f5605e65b222ba33106a9f7db089f34647a6 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 3 Feb 2026 11:32:15 -0700 Subject: [PATCH 014/181] enable LTO Signed-off-by: James Cherry --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index dda0ca9db..8ce05b3ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,16 @@ message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Build CXX_FLAGS: ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}") message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") +# Enable Link-Time Optimization (LTO) for Release builds. +include(CheckIPOSupported) +check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error) +if(ipo_supported) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) + message(STATUS "IPO/LTO: enabled for Release builds") +else() + message(STATUS "IPO/LTO: not supported - ${ipo_error}") +endif() + ################################################################ # # Source files. From 33e480a6c18151b9a79e43a363678f9aea92b705 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 4 Feb 2026 18:33:04 -0700 Subject: [PATCH 015/181] liberty memory management Signed-off-by: James Cherry --- dcalc/ArcDcalcWaveforms.cc | 8 +- dcalc/CcsCeffDelayCalc.cc | 21 +- dcalc/GraphDelayCalc.cc | 2 +- dcalc/PrimaDelayCalc.cc | 4 +- dcalc/PrimaDelayCalc.hh | 2 +- graph/Graph.i | 4 +- include/sta/ContainerHelpers.hh | 62 +- include/sta/FuncExpr.hh | 6 +- include/sta/InternalPower.hh | 49 +- include/sta/LeakagePower.hh | 1 + include/sta/Liberty.hh | 255 ++++--- include/sta/LibertyClass.hh | 2 +- include/sta/Sdc.hh | 4 +- include/sta/Sequential.hh | 6 +- include/sta/Sta.hh | 7 +- include/sta/StringUtil.hh | 2 + include/sta/TableModel.hh | 443 +++++------- include/sta/TimingArc.hh | 66 +- liberty/EquivCells.cc | 20 +- liberty/FuncExpr.cc | 35 +- liberty/InternalPower.cc | 91 +-- liberty/LeakagePower.cc | 30 + liberty/Liberty.cc | 647 ++++++++--------- liberty/Liberty.i | 32 +- liberty/LibertyBuilder.cc | 45 +- liberty/LibertyBuilder.hh | 8 - liberty/LibertyExt.cc | 64 +- liberty/LibertyLex.ll | 10 +- liberty/LibertyParse.yy | 83 +-- liberty/LibertyParser.cc | 179 ++--- liberty/LibertyParser.hh | 70 +- liberty/LibertyReader.cc | 587 ++++++++-------- liberty/LibertyReaderPvt.hh | 77 ++- liberty/LibertyWriter.cc | 32 +- liberty/Sequential.cc | 54 +- liberty/TableModel.cc | 1152 ++++++++++++++++--------------- liberty/TimingArc.cc | 108 ++- power/Power.cc | 57 +- power/Power.hh | 4 +- sdc/Sdc.cc | 8 +- sdf/ReportAnnotation.cc | 4 +- sdf/SdfReader.cc | 16 +- sdf/SdfReaderPvt.hh | 2 +- sdf/SdfWriter.cc | 24 +- search/FindRegister.cc | 68 +- search/MakeTimingModel.cc | 33 +- search/Sim.cc | 28 +- search/Sta.cc | 1 + spice/WriteSpice.cc | 2 +- spice/Xyce.cc | 2 +- spice/Xyce.hh | 2 +- tcl/StaTclTypes.i | 34 +- util/StringUtil.cc | 11 + 53 files changed, 2254 insertions(+), 2310 deletions(-) diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index 16595a28e..b927bf1a4 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -65,11 +65,11 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, report->error(1751, "VDD not defined in library %s", library->name()); Waveform in_waveform = driver_waveform->waveform(delayAsFloat(in_slew)); // Delay time axis. - FloatSeq *time_values = new FloatSeq; - for (float time : *in_waveform.axis1()->values()) - time_values->push_back(time + dcalc_arg.inputDelay()); + FloatSeq time_values; + for (float time : in_waveform.axis1()->values()) + time_values.push_back(time + dcalc_arg.inputDelay()); TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - time_values); + std::move(time_values)); // Scale the waveform from 0:vdd. FloatSeq *scaled_values = new FloatSeq; for (float value : *in_waveform.values()) { diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index e068a73da..14b3e639e 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -530,12 +530,13 @@ CcsCeffDelayCalc::drvrWaveform() } } TableAxisPtr drvr_time_axis = make_shared(TableAxisVariable::time, - drvr_times); - Table1 drvr_table(drvr_volts, drvr_time_axis); + std::move(*drvr_times)); + delete drvr_times; + Table drvr_table(drvr_volts, drvr_time_axis); return drvr_table; } else - return Table1(); + return Table(); } Waveform @@ -561,12 +562,13 @@ CcsCeffDelayCalc::loadWaveform(const Pin *load_pin) load_volts->push_back(v1); } TableAxisPtr load_time_axis = make_shared(TableAxisVariable::time, - load_times); - Table1 load_table(load_volts, load_time_axis); + std::move(*load_times)); + delete load_times; + Table load_table(load_volts, load_time_axis); return load_table; } } - return Table1(); + return Table(); } Waveform @@ -605,12 +607,13 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin, load_volts->push_back(v1); } TableAxisPtr load_time_axis = make_shared(TableAxisVariable::time, - load_times); - Table1 load_table(load_volts, load_time_axis); + std::move(*load_times)); + delete load_times; + Table load_table(load_volts, load_time_axis); return load_table; } } - return Table1(); + return Table(); } bool diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index f75883c9a..e94b3c8b6 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -564,7 +564,7 @@ GraphDelayCalc::driveCellDefaultFromPort(const LibertyCell *cell, { LibertyPort *from_port = 0; int from_port_index = 0; - for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, to_port)) { + for (TimingArcSet *arc_set : cell->timingArcSetsTo(to_port)) { LibertyPort *set_from_port = arc_set->from(); int set_from_port_index = findPortIndex(cell, set_from_port); if (from_port == nullptr diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 188d533d8..835d02c6c 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -960,8 +960,8 @@ PrimaDelayCalc::watchWaveform(const Pin *pin) { FloatSeq &voltages = watch_pin_values_[pin]; TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - new FloatSeq(times_)); - Table1 waveform(new FloatSeq(voltages), time_axis); + FloatSeq(times_)); + Table waveform(new FloatSeq(voltages), time_axis); return waveform; } diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index a5f402c71..361b23a43 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -46,7 +46,7 @@ using MatrixSd = Eigen::SparseMatrix; using PinLMap = std::map; using WatchPinValuesMap = std::map; -using Waveform = Table1; +using Waveform = Table; ArcDelayCalc * makePrimaDelayCalc(StaState *sta); diff --git a/graph/Graph.i b/graph/Graph.i index da7be0eef..ed9ad710d 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -271,13 +271,13 @@ cond() const char * mode_name() { - return self->timingArcSet()->modeName(); + return self->timingArcSet()->modeName().c_str(); } const char * mode_value() { - return self->timingArcSet()->modeValue(); + return self->timingArcSet()->modeValue().c_str(); } const char * diff --git a/include/sta/ContainerHelpers.hh b/include/sta/ContainerHelpers.hh index 49268ce88..abc87ffbd 100644 --- a/include/sta/ContainerHelpers.hh +++ b/include/sta/ContainerHelpers.hh @@ -143,8 +143,8 @@ struct find_return }; -// Find an value in a contaiiner of pointers. -// return nullptr if not found. +// Find an pointer value in a reference to a contaiiner of pointers. +// Return nullptr if not found. template auto findKey(const AssocContainer& c, @@ -166,11 +166,11 @@ findKey(const AssocContainer& c, return *it; // set } -// Find an value in a contaiiner of pointers. -// return nullptr if not found. +// Find an pointer value in a pointer to a contaiiner of pointers. +// Return nullptr if not found. template auto -findKey(const AssocContainer* c, +findKey(const AssocContainer *c, typename AssocContainer::key_type key) -> typename find_return::type { @@ -191,6 +191,29 @@ findKey(const AssocContainer* c, return *it; } +//////////////////////////////////////////////////////////////// + +// Find a value reference in a container. Returns reference to the value if found, +// otherwise returns reference to a static empty value of the same type. +template +auto +findKeyValue(const AssocContainer& c, + typename AssocContainer::key_type key) + -> const typename find_return::type & +{ + auto it = c.find(key); + if (it != c.end()) { + if constexpr (has_mapped_type::value) + return it->second; + else + return *it; + } + static const typename find_return::type empty{}; + return empty; +} + +// Find an value reference in a reference to a contaiiner of objects. +// Return exists. template void findKeyValue(const AssocContainer& c, @@ -205,7 +228,7 @@ findKeyValue(const AssocContainer& c, } if constexpr (has_mapped_type::value) { - // map + // map value = it->second; exists = true; } @@ -216,6 +239,8 @@ findKeyValue(const AssocContainer& c, } } +// Find an value reference in a pointer to a contaiiner of objects. +// Return exists. template void findKeyValue(const AssocContainer *c, @@ -241,6 +266,9 @@ findKeyValue(const AssocContainer *c, } } +//////////////////////////////////////////////////////////////// + +// Find an value pointer in a reference to a contaiiner of objects. template auto findKeyValuePtr(AssocContainer& c, @@ -252,7 +280,27 @@ findKeyValuePtr(AssocContainer& c, return nullptr; if constexpr (has_mapped_type::value) - // map + // map + return &it->second; + else + // set + return *it; +} + +// Find an pointger to a value in a const reference to a contaiiner objects. +// Return nullptr if not found. +template +auto +findKeyValuePtr(const AssocContainer& c, + typename AssocContainer::key_type key) + -> const typename find_return::type* +{ + auto it = c.find(key); + if (it == c.end()) + return nullptr; + + if constexpr (has_mapped_type::value) + // map return &it->second; else // set diff --git a/include/sta/FuncExpr.hh b/include/sta/FuncExpr.hh index 3f537f521..b32cf023d 100644 --- a/include/sta/FuncExpr.hh +++ b/include/sta/FuncExpr.hh @@ -47,6 +47,8 @@ public: FuncExpr *left, FuncExpr *right, LibertyPort *port); + ~FuncExpr(); + void shallowDelete(); static FuncExpr *makePort(LibertyPort *port); static FuncExpr *makeNot(FuncExpr *expr); static FuncExpr *makeAnd(FuncExpr *left, @@ -61,11 +63,11 @@ public: const FuncExpr *expr2); static bool less(const FuncExpr *expr1, const FuncExpr *expr2); + // Invert expr by deleting leading NOT if found. + FuncExpr *invert(); // Deep copy. FuncExpr *copy(); - // Delete expression and all of its subexpressions. - void deleteSubexprs(); // op == port LibertyPort *port() const; Op op() const { return op_; } diff --git a/include/sta/InternalPower.hh b/include/sta/InternalPower.hh index 408e946b3..f1f0df4c0 100644 --- a/include/sta/InternalPower.hh +++ b/include/sta/InternalPower.hh @@ -24,58 +24,46 @@ #pragma once +#include +#include +#include + #include "LibertyClass.hh" #include "Transition.hh" namespace sta { -class InternalPowerAttrs; class InternalPowerModel; -class InternalPowerAttrs -{ -public: - InternalPowerAttrs(); - virtual ~InternalPowerAttrs(); - void deleteContents(); - FuncExpr *when() const { return when_; } - void setWhen(FuncExpr *when); - void setModel(const RiseFall *rf, - InternalPowerModel *model); - InternalPowerModel *model(const RiseFall *rf) const; - const char *relatedPgPin() const { return related_pg_pin_; } - void setRelatedPgPin(const char *related_pg_pin); - -protected: - FuncExpr *when_; - InternalPowerModel *models_[RiseFall::index_count]; - const char *related_pg_pin_; -}; +using InternalPowerModels = + std::array, RiseFall::index_count>; class InternalPower { public: - InternalPower(LibertyCell *cell, - LibertyPort *port, + InternalPower(LibertyPort *port, LibertyPort *related_port, - InternalPowerAttrs *attrs); - ~InternalPower(); + const std::string &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_; } - const char *relatedPgPin() const { return related_pg_pin_; } + FuncExpr *when() const { return when_.get(); } + const std::string &relatedPgPin() const { return related_pg_pin_; } float power(const RiseFall *rf, const Pvt *pvt, float in_slew, - float load_cap); + float load_cap) const; + const InternalPowerModel *model(const RiseFall *rf) const; protected: LibertyPort *port_; LibertyPort *related_port_; - FuncExpr *when_; - const char *related_pg_pin_; - InternalPowerModel *models_[RiseFall::index_count]; + std::string related_pg_pin_; + std::shared_ptr when_; + InternalPowerModels models_; }; class InternalPowerModel @@ -92,6 +80,7 @@ public: float in_slew, float load_cap, int digits) const; + const TableModel *model() const { return model_; } protected: void findAxisValues(float in_slew, diff --git a/include/sta/LeakagePower.hh b/include/sta/LeakagePower.hh index 18f886c6b..f14c8db2f 100644 --- a/include/sta/LeakagePower.hh +++ b/include/sta/LeakagePower.hh @@ -36,6 +36,7 @@ public: LibertyPort *related_pg_port, FuncExpr *when, float power); + LeakagePower(LeakagePower &&other) noexcept; ~LeakagePower(); LibertyCell *libertyCell() const { return cell_; } LibertyPort *relatedPgPort() const { return related_pg_port_; } diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index c89a5a66e..41f6d4937 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -24,11 +24,13 @@ #pragma once +#include #include #include #include #include #include +#include #include #include "ContainerHelpers.hh" @@ -39,6 +41,8 @@ #include "MinMaxValues.hh" #include "Transition.hh" #include "Delay.hh" +#include "InternalPower.hh" +#include "LeakagePower.hh" #include "LibertyClass.hh" namespace sta { @@ -48,48 +52,96 @@ class LibertyCellIterator; class LibertyCellPortIterator; class LibertyCellPortBitIterator; class LibertyPortMemberIterator; -class ModeValueDef; class TestCell; class PatternMatch; -class LatchEnable; class Report; class Debug; class LibertyBuilder; class LibertyReader; class OcvDerate; class TimingArcAttrs; -class InternalPowerAttrs; class StaState; class Scene; class DriverWaveform; -using TableTemplateMap = std::map; +// Mode definition mode_value group (defined before ModeValueMap / ModeDef). +class ModeValueDef +{ +public: + ModeValueDef(std::string value, FuncExpr *cond, std::string sdf_cond); + ModeValueDef(ModeValueDef &&other) noexcept; + ~ModeValueDef(); + const std::string &value() const { return value_; } + FuncExpr *cond() const { return cond_; } + void setCond(FuncExpr *cond); + const std::string &sdfCond() const { return sdf_cond_; } + void setSdfCond(std::string sdf_cond); + +private: + std::string value_; + FuncExpr *cond_; + std::string sdf_cond_; +}; + +// Latch enable port/function for a latch D->Q timing arc set. +class LatchEnable +{ +public: + LatchEnable(LibertyPort *data, + LibertyPort *enable, + const RiseFall *enable_edge, + FuncExpr *enable_func, + LibertyPort *output, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check); + LibertyPort *data() const { return data_; } + LibertyPort *output() const { return output_; } + LibertyPort *enable() const { return enable_; } + FuncExpr *enableFunc() const { return enable_func_; } + const RiseFall *enableEdge() const { return enable_edge_; } + TimingArcSet *dToQ() const { return d_to_q_; } + TimingArcSet *enToQ() const { return en_to_q_; } + TimingArcSet *setupCheck() const { return setup_check_; } + +private: + LibertyPort *data_; + LibertyPort *enable_; + const RiseFall *enable_edge_; + FuncExpr *enable_func_; + LibertyPort *output_; + TimingArcSet *d_to_q_; + TimingArcSet *en_to_q_; + TimingArcSet *setup_check_; +}; + +using TableTemplateMap = std::map; using TableTemplateSeq = std::vector; -using BusDclMap = std::map; -using BusDclSeq = std::vector; -using ScaleFactorsMap = std::map; -using WireloadMap = std::map; -using WireloadSelectionMap = std::map; -using OperatingConditionsMap = std::map; -using PortToSequentialMap = std::map; +using BusDclMap = std::map; +using BusDclSeq = std::vector; +using ScaleFactorsMap = std::map; +using WireloadMap = std::map; +using WireloadSelectionMap = std::map; +using OperatingConditionsMap = std::map; +using PortToSequentialMap = std::map; using TimingArcSetSeq = std::vector; using TimingArcSetSet = std::set; -using LibertyPortPairTimingArcMap = std::map; -using InternalPowerSeq = std::vector; -using PortInternalPowerMap = std::map; -using LeakagePowerSeq = std::vector; -using LibertyPortTimingArcMap = std::map; +using InternalPowerSeq = std::vector; +using InternalPowerPtrSeq = std::vector; +using InternalPowerIndexSeq = std::vector; +using PortInternalPowerMap = std::map; +using LeakagePowerSeq = std::vector; using ScaledCellMap = std::map; using ScaledPortMap = std::map; -using ModeDefMap = std::map; -using ModeValueMap = std::map; -using LatchEnableMap = std::map; -using LatchEnableSeq = std::vector; -using OcvDerateMap = std::map; -using InternalPowerAttrsSeq = std::vector; +using ModeDefMap = std::map; +using ModeValueMap = std::map; +using LatchEnableIndexMap = std::map; +using LatchEnableSeq = std::vector; +using OcvDerateMap = std::map; using SupplyVoltageMap = std::map; -using DriverWaveformMap = std::map; +using DriverWaveformMap = std::map; using SceneSeq = std::vector; enum class ClockGateType { none, latch_posedge, latch_negedge, other }; @@ -162,14 +214,15 @@ public: DelayModelType delayModelType() const { return delay_model_type_; } void setDelayModelType(DelayModelType type); - void addBusDcl(BusDcl *bus_dcl); + BusDcl *makeBusDcl(std::string name, int from, int to); BusDcl *findBusDcl(const char *name) const; BusDclSeq busDcls() const; - void addTableTemplate(TableTemplate *tbl_template, - TableTemplateType type); + TableTemplate *makeTableTemplate(std::string name, + TableTemplateType type); TableTemplate *findTableTemplate(const char *name, TableTemplateType type); TableTemplateSeq tableTemplates() const; + TableTemplateSeq tableTemplates(TableTemplateType type) const; float nominalProcess() const { return nominal_process_; } void setNominalProcess(float process); float nominalVoltage() const { return nominal_voltage_; } @@ -178,8 +231,8 @@ public: void setNominalTemperature(float temperature); void setScaleFactors(ScaleFactors *scales); - // Add named scale factor group. - void addScaleFactors(ScaleFactors *scales); + // Make named scale factor group. Returns pointer to the inserted element. + ScaleFactors *makeScaleFactors(const char *name); ScaleFactors *findScaleFactors(const char *name); ScaleFactors *scaleFactors() const { return scale_factors_; } float scaleFactor(ScaleFactorType type, @@ -280,20 +333,20 @@ public: Units *units() { return units_; } const Units *units() const { return units_; } - Wireload *findWireload(const char *name) const; - void setDefaultWireload(Wireload *wireload); - Wireload *defaultWireload() const; - WireloadSelection *findWireloadSelection(const char *name) const; - WireloadSelection *defaultWireloadSelection() const; - void addWireload(Wireload *wireload); + Wireload *makeWireload(std::string name); + const Wireload *findWireload(const char *name) const; + void setDefaultWireload(const Wireload *wireload); + const Wireload *defaultWireload() const; + WireloadSelection *makeWireloadSelection(std::string name); + const WireloadSelection *findWireloadSelection(const char *name) const; + const WireloadSelection *defaultWireloadSelection() const; WireloadMode defaultWireloadMode() const; void setDefaultWireloadMode(WireloadMode mode); - void addWireloadSelection(WireloadSelection *selection); - void setDefaultWireloadSelection(WireloadSelection *selection); + void setDefaultWireloadSelection(const WireloadSelection *selection); + OperatingConditions *makeOperatingConditions(std::string name); OperatingConditions *findOperatingConditions(const char *name); OperatingConditions *defaultOperatingConditions() const; - void addOperatingConditions(OperatingConditions *op_cond); void setDefaultOperatingConditions(OperatingConditions *op_cond); // AOCV @@ -302,8 +355,8 @@ public: void setOcvArcDepth(float depth); OcvDerate *defaultOcvDerate() const; void setDefaultOcvDerate(OcvDerate *derate); + OcvDerate *makeOcvDerate(std::string name); OcvDerate *findOcvDerate(const char *derate_name); - void addOcvDerate(OcvDerate *derate); void addSupplyVoltage(const char *suppy_name, float voltage); bool supplyExists(const char *suppy_name) const; @@ -338,8 +391,9 @@ public: Report *report); DriverWaveform *findDriverWaveform(const char *name); - DriverWaveform *driverWaveformDefault() { return driver_waveform_default_; } - void addDriverWaveform(DriverWaveform *driver_waveform); + DriverWaveform *driverWaveformDefault() { return findDriverWaveform(""); } + DriverWaveform *makeDriverWaveform(const std::string &name, + TablePtr waveforms); protected: float degradeWireSlew(const TableModel *model, @@ -376,9 +430,9 @@ protected: float slew_upper_threshold_[RiseFall::index_count]; float slew_derate_from_library_; WireloadMap wireloads_; - Wireload *default_wire_load_; + const Wireload *default_wire_load_; WireloadMode default_wire_load_mode_; - WireloadSelection *default_wire_load_selection_; + const WireloadSelection *default_wire_load_selection_; WireloadSelectionMap wire_load_selections_; OperatingConditionsMap operating_conditions_; OperatingConditions *default_operating_conditions_; @@ -389,8 +443,6 @@ protected: LibertyCellSeq *buffers_; LibertyCellSeq *inverters_; DriverWaveformMap driver_waveform_map_; - // Unnamed driver waveform. - DriverWaveform *driver_waveform_default_; static constexpr float input_threshold_default_ = .5; static constexpr float output_threshold_default_ = .5; @@ -429,8 +481,8 @@ public: bool hasInternalPorts() const { return has_internal_ports_; } ScaleFactors *scaleFactors() const { return scale_factors_; } void setScaleFactors(ScaleFactors *scale_factors); - ModeDef *makeModeDef(const char *name); - ModeDef *findModeDef(const char *name); + ModeDef *makeModeDef(std::string name); + const ModeDef *findModeDef(const char *name) const; float area() const { return area_; } void setArea(float area); @@ -462,18 +514,20 @@ public: bool isClockGate() const; void setClockGateType(ClockGateType type); const TimingArcSetSeq &timingArcSets() const { return timing_arc_sets_; } + const TimingArcSetSeq &timingArcSetsFrom(const LibertyPort *from) const; + const TimingArcSetSeq &timingArcSetsTo(const LibertyPort *to) const; // from or to may be nullptr to wildcard. const TimingArcSetSeq &timingArcSets(const LibertyPort *from, const LibertyPort *to) const; size_t timingArcSetCount() const; // Find a timing arc set equivalent to key. TimingArcSet *findTimingArcSet(TimingArcSet *key) const; - TimingArcSet *findTimingArcSet(unsigned arc_set_index) const; + TimingArcSet *findTimingArcSet(size_t index) const; bool hasTimingArcs(LibertyPort *port) const; const InternalPowerSeq &internalPowers() const { return internal_powers_; } - const InternalPowerSeq &internalPowers(const LibertyPort *port); - LeakagePowerSeq *leakagePowers() { return &leakage_powers_; } + InternalPowerPtrSeq internalPowers(const LibertyPort *port) const; + const LeakagePowerSeq &leakagePowers() const { return leakage_powers_; } void leakagePower(// Return values. float &leakage, bool &exists) const; @@ -487,6 +541,7 @@ public: const Statetable *statetable() const { return statetable_; } // Find bus declaration local to this cell. + BusDcl *makeBusDcl(std::string name, int from, int to); BusDcl *findBusDcl(const char *name) const; // True when TimingArcSetBuilder::makeRegLatchArcs infers register // timing arcs. @@ -505,6 +560,7 @@ public: // AOCV float ocvArcDepth() const; OcvDerate *ocvDerate() const; + OcvDerate *makeOcvDerate(std::string name); OcvDerate *findOcvDerate(const char *derate_name); // Build helpers. @@ -521,18 +577,25 @@ public: void makeStatetable(LibertyPortSeq &input_ports, LibertyPortSeq &internal_ports, StatetableRows &table); - void addBusDcl(BusDcl *bus_dcl); // Add scaled cell after it is complete. void addScaledCell(OperatingConditions *op_cond, LibertyCell *scaled_cell); - unsigned addTimingArcSet(TimingArcSet *set); - void addInternalPower(InternalPower *power); - void addInternalPowerAttrs(InternalPowerAttrs *attrs); - void addLeakagePower(LeakagePower *power); + TimingArcSet *makeTimingArcSet(LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs); + void makeInternalPower(LibertyPort *port, + LibertyPort *related_port, + const std::string &related_pg_pin, + const std::shared_ptr &when, + InternalPowerModels &models); + void makeLeakagePower(LibertyPort *related_pg_port, + FuncExpr *when, + float power); void setLeakagePower(float leakage); void setOcvArcDepth(float depth); void setOcvDerate(OcvDerate *derate); - void addOcvDerate(OcvDerate *derate); void setTestCell(TestCell *test); void setHasInferedRegTimingArcs(bool infered); void setSceneCell(LibertyCell *scene_cell, @@ -585,8 +648,6 @@ protected: void translatePresetClrCheckRoles(); void inferLatchRoles(Report *report, Debug *debug); - void deleteInternalPowerAttrs(); - void makeTimingArcMap(Report *report); void makeTimingArcPortMaps(); bool hasBufferFunc(const LibertyPort *input, const LibertyPort *output) const; @@ -612,12 +673,9 @@ protected: TimingArcSetSeq timing_arc_sets_; TimingArcSetSet timing_arc_set_set_; LibertyPortPairTimingArcMap port_timing_arc_set_map_; - LibertyPortTimingArcMap timing_arc_set_from_map_; - LibertyPortTimingArcMap timing_arc_set_to_map_; bool has_infered_reg_timing_arcs_; InternalPowerSeq internal_powers_; PortInternalPowerMap port_internal_powers_; - InternalPowerAttrsSeq internal_power_attrs_; LeakagePowerSeq leakage_powers_; SequentialSeq sequentials_; PortToSequentialMap port_to_seq_map_; @@ -627,10 +685,10 @@ protected: ScaleFactors *scale_factors_; ScaledCellMap scaled_cells_; TestCell *test_cell_; - // Latch D->Q to LatchEnable. - LatchEnableMap latch_d_to_q_map_; - // Latch EN->D setup to LatchEnable. - LatchEnableMap latch_check_map_; + // Latch D->Q to LatchEnable index. + LatchEnableIndexMap latch_d_to_q_map_; + // Latch EN->D setup to LatchEnable index. + LatchEnableIndexMap latch_check_map_; LatchEnableSeq latch_enables_; // Ports that have latch D->Q timing arc sets from them. LibertyPortSet latch_data_ports_; @@ -834,10 +892,6 @@ public: float clkTreeDelay(float in_slew, const RiseFall *from_rf, const MinMax *min_max) const; - void setClkTreeDelay(const TableModel *model, - const RiseFall *from_rf, - const RiseFall *to_rf, - const MinMax *min_max); // deprecated 2024-06-22 RiseFallMinMax clkTreeDelays() const __attribute__ ((deprecated)); // deprecated 2024-02-27 @@ -898,8 +952,6 @@ protected: std::vector scene_ports_; ReceiverModelPtr receiver_model_; DriverWaveform *driver_waveform_[RiseFall::index_count]; - // Redundant with clock_tree_path_delay timing arcs but faster to access. - const TableModel *clk_tree_delay_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count]; unsigned int min_pulse_width_exists_:RiseFall::index_count; bool min_period_exists_:1; @@ -1009,10 +1061,10 @@ protected: class BusDcl { public: - BusDcl(const char *name, + BusDcl(std::string name, int from, int to); - const char *name() const { return name_.c_str(); } + const std::string &name() const { return name_; } int from() const { return from_; } int to() const { return to_; } @@ -1026,18 +1078,16 @@ protected: class ModeDef { public: - ~ModeDef(); - const char *name() const { return name_.c_str(); } + const std::string &name() const { return name_; } ModeValueDef *defineValue(const char *value, FuncExpr *cond, const char *sdf_cond); - ModeValueDef *findValueDef(const char *value); - ModeValueMap *values() { return &values_; } + const ModeValueDef *findValueDef(const char *value) const; + const ModeValueMap *values() const { return &values_; } -protected: - // Private to LibertyCell::makeModeDef. - ModeDef(const char *name); + explicit ModeDef(std::string name); +protected: std::string name_; ModeValueMap values_; @@ -1045,41 +1095,19 @@ private: friend class LibertyCell; }; -// Mode definition mode_value group. -class ModeValueDef -{ -public: - ~ModeValueDef(); - const char *value() const { return value_.c_str(); } - FuncExpr *cond() const { return cond_; } - void setCond(FuncExpr *cond); - const char *sdfCond() const { return sdf_cond_.c_str(); } - void setSdfCond(const char *sdf_cond); - -protected: - // Private to ModeDef::defineValue. - ModeValueDef(const char *value, - FuncExpr *cond, - const char *sdf_cond); - - std::string value_; - FuncExpr *cond_; - std::string sdf_cond_; - -private: - friend class ModeDef; -}; - class TableTemplate { public: - TableTemplate(const char *name); - TableTemplate(const char *name, + TableTemplate(std::string name); + TableTemplate(std::string name, + TableTemplateType type); + TableTemplate(std::string name, TableAxisPtr axis1, TableAxisPtr axis2, TableAxisPtr axis3); - const char *name() const { return name_.c_str(); } - void setName(const char *name); + const std::string &name() const { return name_; } + void setName(std::string name); + TableTemplateType type() const { return type_; } const TableAxis *axis1() const { return axis1_.get(); } TableAxisPtr axis1ptr() const { return axis1_; } void setAxis1(TableAxisPtr axis); @@ -1092,6 +1120,7 @@ public: protected: std::string name_; + TableTemplateType type_; TableAxisPtr axis1_; TableAxisPtr axis2_; TableAxisPtr axis3_; @@ -1101,8 +1130,8 @@ class TestCell : public LibertyCell { public: TestCell(LibertyLibrary *library, - const char *name, - const char *filename); + std::string name, + std::string filename); protected: }; @@ -1110,9 +1139,9 @@ protected: class OcvDerate { public: - OcvDerate(const char *name); + OcvDerate(std::string name); ~OcvDerate(); - const char *name() const { return name_; } + const std::string &name() const { return name_; } const Table *derateTable(const RiseFall *rf, const EarlyLate *early_late, PathType path_type); @@ -1122,7 +1151,7 @@ public: TablePtr derate); private: - const char *name_; + std::string name_; // [rf_type][derate_type][path_type] TablePtr derate_[RiseFall::index_count][EarlyLate::index_count][path_type_count]; }; diff --git a/include/sta/LibertyClass.hh b/include/sta/LibertyClass.hh index 1d200a762..5436f4807 100644 --- a/include/sta/LibertyClass.hh +++ b/include/sta/LibertyClass.hh @@ -68,7 +68,7 @@ class StatetableRow; using LibertyLibrarySeq = std::vector; using LibertyCellSeq = std::vector; -using SequentialSeq = std::vector; +using SequentialSeq = std::vector; using LibertyCellEquivMap = std::map; using LibertyPortSeq = std::vector; using LibertyPortSet = std::set; diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index 441db5d6d..31b363bdb 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -801,7 +801,7 @@ public: WireloadMode wireloadMode() const { return wireload_mode_; }; void setWireloadMode(WireloadMode mode); const WireloadSelection *wireloadSelection(const MinMax *min_max); - void setWireloadSelection(WireloadSelection *selection, + void setWireloadSelection(const WireloadSelection *selection, const MinMaxAll *min_max); // STA interface. @@ -1418,7 +1418,7 @@ protected: float max_area_; Wireload *wireload_[MinMax::index_count]; WireloadMode wireload_mode_; - WireloadSelection *wireload_selection_[MinMax::index_count]; + const WireloadSelection *wireload_selection_[MinMax::index_count]; private: friend class WriteSdc; diff --git a/include/sta/Sequential.hh b/include/sta/Sequential.hh index 1923c270a..67b62f3f1 100644 --- a/include/sta/Sequential.hh +++ b/include/sta/Sequential.hh @@ -76,7 +76,6 @@ public: LibertyPort *output() const { return output_; } LibertyPort *outputInv() const { return output_inv_; } -protected: // clock/data are: // clocked_on/next_state for registers // enable/data for latches @@ -89,7 +88,12 @@ protected: LogicValue clr_preset_out_inv, LibertyPort *output, LibertyPort *output_inv); + Sequential(Sequential &&other) noexcept; + Sequential(const Sequential &) = delete; + Sequential &operator=(Sequential &&) noexcept; + Sequential &operator=(const Sequential &) = delete; +protected: bool is_register_; FuncExpr *clock_; FuncExpr *data_; diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index f5056ee03..e01b12e36 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -153,6 +153,10 @@ public: Scene *scene, const MinMaxAll *min_max, bool infer_latches); + // tmp public + void readLibertyAfter(LibertyLibrary *liberty, + Scene *scene, + const MinMax *min_max); bool readVerilog(const char *filename); // Network readers call this to notify the Sta to delete any previously // linked network. @@ -1577,9 +1581,6 @@ protected: const Mode *mode); void findRegisterPreamble(const Mode *mode); bool crossesHierarchy(Edge *edge) const; - void readLibertyAfter(LibertyLibrary *liberty, - Scene *scene, - const MinMax *min_max); void powerPreamble(); void powerPreamble(const Scene *scene); virtual void replaceCell(Instance *inst, diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index 9523670b7..c222d53b9 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -192,6 +192,8 @@ char * makeTmpString(std::string &str); bool isTmpString(const char *str); +void +deleteTmpStrings(); //////////////////////////////////////////////////////////////// diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index a392e7d63..1d08d7517 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -24,8 +24,9 @@ #pragma once -#include +#include #include +#include #include #include "MinMax.hh" @@ -39,13 +40,15 @@ class Unit; class Units; class Report; class Table; +class TableModel; +class TableAxis; class OutputWaveforms; -class Table1; using FloatSeq = std::vector; -using FloatTable = std::vector; -using Table1Seq = std::vector; -using Waveform = Table1; +using FloatTable = std::vector; +// Sequence of 1D tables (order 1). +using Table1Seq = std::vector; +using Waveform = Table; TableAxisVariable stringTableAxisVariable(const char *variable); @@ -65,7 +68,7 @@ public: TableModel *slew_sigma_models[EarlyLate::index_count], ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms); - virtual ~GateTableModel(); + ~GateTableModel() override; void gateDelay(const Pvt *pvt, float in_slew, float load_cap, @@ -89,10 +92,12 @@ public: int digits) const override; float driveResistance(const Pvt *pvt) const override; - const TableModel *delayModel() const { return delay_model_; } - const TableModel *slewModel() const { return slew_model_; } + 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 ReceiverModel *receiverModel() const { return receiver_model_.get(); } - OutputWaveforms *outputWaveforms() const { return output_waveforms_; } + OutputWaveforms *outputWaveforms() const { return output_waveforms_.get(); } // Check the axes before making the model. // Return true if the model axes are supported. static bool checkAxes(const TablePtr &table); @@ -129,12 +134,12 @@ protected: float &axis_value3) const; static bool checkAxis(const TableAxis *axis); - TableModel *delay_model_; - TableModel *delay_sigma_models_[EarlyLate::index_count]; - TableModel *slew_model_; - TableModel *slew_sigma_models_[EarlyLate::index_count]; + std::unique_ptr delay_model_; + std::array, EarlyLate::index_count> delay_sigma_models_; + std::unique_ptr slew_model_; + std::array, EarlyLate::index_count> slew_sigma_models_; ReceiverModelPtr receiver_model_; - OutputWaveforms *output_waveforms_; + std::unique_ptr output_waveforms_; }; class CheckTableModel : public CheckTimingModel @@ -143,7 +148,7 @@ public: CheckTableModel(LibertyCell *cell, TableModel *model, TableModel *sigma_models[EarlyLate::index_count]); - virtual ~CheckTableModel(); + ~CheckTableModel() override; ArcDelay checkDelay(const Pvt *pvt, float from_slew, float to_slew, @@ -156,7 +161,8 @@ public: float related_out_cap, bool pocv_enabled, int digits) const override; - const TableModel *model() const { return model_; } + const TableModel *model() const { return model_.get(); } + const TableModel *sigmaModel(const EarlyLate *el) const; // Check the axes before making the model. // Return true if the model axes are supported. @@ -190,14 +196,156 @@ protected: int digits) const; static bool checkAxis(const TableAxis *axis); - TableModel *model_; - TableModel *sigma_models_[EarlyLate::index_count]; + std::unique_ptr model_; + std::array, EarlyLate::index_count> sigma_models_; +}; + +class TableAxis +{ +public: + TableAxis(TableAxisVariable variable, + FloatSeq &&values); + TableAxisVariable variable() const { return variable_; } + const char *variableString() const; + const Unit *unit(const Units *units); + size_t size() const { return values_.size(); } + bool inBounds(float value) const; + float axisValue(size_t index) const { return values_[index]; } + // Find the index for value such that axis[index] <= value < axis[index+1]. + size_t findAxisIndex(float value) const; + void findAxisIndex(float value, + // Return values. + size_t &index, + bool &exists) const; + size_t findAxisClosestIndex(float value) const; + const FloatSeq &values() const { return values_; } + float min() const; + float max() const; + +private: + TableAxisVariable variable_; + FloatSeq values_; +}; + +// 0, 1, 2, or 3 dimension float tables. +class Table +{ +public: + Table(); + explicit Table(float value); + Table(FloatSeq *values, + TableAxisPtr axis1); + Table(FloatSeq &&values, + TableAxisPtr axis1); + Table(FloatTable &&values, + TableAxisPtr axis1, + TableAxisPtr axis2); + Table(FloatTable &&values, + TableAxisPtr axis1, + TableAxisPtr axis2, + TableAxisPtr axis3); + Table(Table &&table); + Table(const Table &table); + Table &operator=(Table &&table); + + void setScaleFactorType(ScaleFactorType type); + int order() const { return order_; } + const TableAxis *axis1() const { return axis1_.get(); } + const TableAxis *axis2() const { return axis2_.get(); } + const TableAxis *axis3() const { return axis3_.get(); } + const TableAxisPtr axis1ptr() const { return axis1_; } + void setIsScaled(bool is_scaled); + + float value(size_t axis_idx1, + size_t axis_idx2, + size_t axis_idx3) const; + // Single-index value (order 1 only). + float value(size_t index1) const; + // Two-index value (order 2 and 3). + float value(size_t axis_index1, + size_t axis_index2) const; + + // Table interpolated lookup. + float findValue(float axis_value1, + float axis_value2, + float axis_value3) const; + // One-argument lookup (order 1). + void findValue(float axis_value1, + float &value, + bool &extrapolated) const; + float findValue(float axis_value1) const; + float findValueClip(float axis_value1) const; + // Table interpolated lookup with scale factor. + float findValue(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float axis_value1, + float axis_value2, + float axis_value3) const; + + std::string reportValue(const char *result_name, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + const char *comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const; + void report(const Units *units, + Report *report) const; + + // Order 1: pointer to value sequence (nullptr if not order 1). + FloatSeq *values() const; + // Order 2 and 3: pointer to value table (nullptr otherwise). + FloatTable *values3(); + const FloatTable *values3() const; + +private: + void clear(); + std::string reportValueOrder0(const char *result_name, + const char *comment1, + const Unit *table_unit, + int digits) const; + std::string reportValueOrder1(const char *result_name, + const LibertyCell *cell, + float value1, + const char *comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const; + std::string reportValueOrder2(const char *result_name, + const LibertyCell *cell, + float value1, + const char *comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const; + std::string reportValueOrder3(const char *result_name, + const LibertyCell *cell, + float value1, + const char *comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const; + + int order_; + float value_; // order 0 only + FloatSeq values1_; // order 1 only + FloatTable values_table_; // order 2 and 3 + TableAxisPtr axis1_; + TableAxisPtr axis2_; + TableAxisPtr axis3_; }; // Wrapper class for Table to apply scale factors. class TableModel { public: + TableModel(); TableModel(TablePtr table, TableTemplate *tbl_template, ScaleFactorType scale_factor_type, @@ -205,6 +353,9 @@ public: void setScaleFactorType(ScaleFactorType type); int order() const; TableTemplate *tblTemplate() const { return tbl_template_; } + const TablePtr &table() const { return table_; } + ScaleFactorType scaleFactorType() const; + int rfIndex() const { return rf_index_; } const TableAxis *axis1() const; const TableAxis *axis2() const; const TableAxis *axis3() const; @@ -249,243 +400,19 @@ protected: bool is_scaled_:1; }; -// Abstract base class for 0, 1, 2, or 3 dimesnion float tables. -class Table -{ -public: - Table() {} - virtual ~Table() {} - void setScaleFactorType(ScaleFactorType type); - virtual int order() const = 0; - virtual const TableAxis *axis1() const { return nullptr; } - virtual const TableAxis *axis2() const { return nullptr; } - virtual const TableAxis *axis3() const { return nullptr; } - void setIsScaled(bool is_scaled); - virtual float value(size_t axis_idx1, - size_t axis_idx2, - size_t axis_idx3) const = 0; - // Table interpolated lookup. - virtual float findValue(float axis_value1, - float axis_value2, - float axis_value3) const = 0; - // Table interpolated lookup with scale factor. - float findValue(const LibertyLibrary *library, - const LibertyCell *cell, - const Pvt *pvt, - float axis_value1, - float axis_value2, - float axis_value3) const; - virtual std::string reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const = 0; - virtual void report(const Units *units, - Report *report) const = 0; -}; - -// Zero dimension (scalar) table. -class Table0 : public Table -{ -public: - Table0(float value); - int order() const override { return 0; } - float value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const override; - float findValue(float axis_value1, - float axis_value2, - float axis_value3) const override; - std::string reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const override; - void report(const Units *units, - Report *report) const override; - using Table::findValue; - -private: - float value_; -}; - -// One dimensional table. -class Table1 : public Table -{ -public: - Table1(); - Table1(FloatSeq *values, - TableAxisPtr axis1); - virtual ~Table1(); - Table1(Table1 &&table); - Table1(const Table1 &table); - Table1 &operator= (Table1 &&table); - int order() const override { return 1; } - const TableAxis *axis1() const override { return axis1_.get(); } - const TableAxisPtr axis1ptr() const { return axis1_; } - float value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const override; - float findValue(float value1, - float value2, - float value3) const override; - std::string reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const override; - void report(const Units *units, - Report *report) const override; - - // Table1 specific functions. - float value(size_t index1) const; - void findValue(float axis_value1, - // Return values. - float &value, - bool &extrapolated) const; - float findValue(float axis_value1) const; - float findValueClip(float axis_value1) const; - FloatSeq *values() const { return values_; } - using Table::findValue; - -private: - FloatSeq *values_; - TableAxisPtr axis1_; -}; - -// Two dimensional table. -class Table2 : public Table -{ -public: - Table2(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2); - virtual ~Table2(); - int order() const override { return 2; } - const TableAxis *axis1() const override { return axis1_.get(); } - const TableAxis *axis2() const override { return axis2_.get(); } - float value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const override; - float findValue(float value1, - float value2, - float value3) const override; - std::string reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const override; - void report(const Units *units, - Report *report) const override; - - // Table2 specific functions. - float value(size_t axis_index1, - size_t axis_index2) const; - FloatTable *values3() { return values_; } - - using Table::findValue; - -protected: - FloatTable *values_; - // Row. - TableAxisPtr axis1_; - // Column. - TableAxisPtr axis2_; -}; - -// Three dimensional table. -class Table3 : public Table2 -{ -public: - Table3(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2, - TableAxisPtr axis3); - virtual ~Table3() {} - int order() const override { return 3; } - const TableAxis *axis1() const override { return axis1_.get(); } - const TableAxis *axis2() const override { return axis2_.get(); } - const TableAxis *axis3() const override { return axis3_.get(); } - float value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const override; - float findValue(float value1, - float value2, - float value3) const override; - std::string reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *pvt, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const override; - void report(const Units *units, - Report *report) const override; - using Table::findValue; - -private: - TableAxisPtr axis3_; -}; - -class TableAxis -{ -public: - TableAxis(TableAxisVariable variable, - FloatSeq *values); - ~TableAxis(); - TableAxisVariable variable() const { return variable_; } - const char *variableString() const; - const Unit *unit(const Units *units); - size_t size() const { return values_->size(); } - bool inBounds(float value) const; - float axisValue(size_t index) const { return (*values_)[index]; } - // Find the index for value such that axis[index] <= value < axis[index+1]. - size_t findAxisIndex(float value) const; - void findAxisIndex(float value, - // Return values. - size_t &index, - bool &exists) const; - size_t findAxisClosestIndex(float value) const; - FloatSeq *values() const { return values_; } - float min() const; - float max() const; - -private: - TableAxisVariable variable_; - FloatSeq *values_; -}; - //////////////////////////////////////////////////////////////// class ReceiverModel { public: ~ReceiverModel(); - void setCapacitanceModel(TableModel *table_model, + void setCapacitanceModel(TableModel table_model, size_t segment, const RiseFall *rf); static bool checkAxes(TablePtr table); private: - std::vector capacitance_models_; + std::vector capacitance_models_; }; // Two dimensional (slew/cap) table of one dimensional time/current tables. @@ -496,7 +423,7 @@ public: TableAxisPtr cap_axis, const RiseFall *rf, Table1Seq ¤t_waveforms, - Table1 *ref_times); + Table ref_times); ~OutputWaveforms(); const RiseFall *rf() const { return rf_; } const TableAxis *slewAxis() const { return slew_axis_.get(); } @@ -523,18 +450,18 @@ public: float cap); static bool checkAxes(const TableTemplate *tbl_template); - Table1 currentWaveform(float slew, - float cap); + Table currentWaveform(float slew, + float cap); // Waveform closest to slew/cap; no interpolation. - const Table1 *currentWaveformRaw(float slew, - float cap); - Table1 voltageWaveform(float in_slew, - float load_cap); + const Table *currentWaveformRaw(float slew, + float cap); + Table voltageWaveform(float in_slew, + float load_cap); // Waveform closest to slew/cap; no interpolation. - const Table1 *voltageWaveformRaw(float slew, - float cap); - Table1 voltageCurrentWaveform(float slew, - float cap); + const Table *voltageWaveformRaw(float slew, + float cap); + Table voltageCurrentWaveform(float slew, + float cap); // V/I for last segment of min slew/max cap. float finalResistance(); @@ -563,10 +490,10 @@ private: // Column. TableAxisPtr cap_axis_; const RiseFall *rf_; - Table1Seq current_waveforms_; // from liberty + Table1Seq current_waveforms_; // from liberty (1D tables) Table1Seq voltage_waveforms_; Table1Seq voltage_currents_; - Table1 *ref_times_; + Table ref_times_; float vdd_; static constexpr size_t voltage_waveform_step_count_ = 100; }; @@ -577,7 +504,7 @@ public: DriverWaveform(const std::string &name, TablePtr waveforms); const char *name() const { return name_.c_str(); } - Table1 waveform(float slew); + Table waveform(float slew); private: std::string name_; diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 080cf84d5..347e37f13 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include @@ -39,7 +40,6 @@ class WireTimingArc; class GateTableModel; class Scene; -using TimingArcIndex = int; using TimingArcSeq = std::vector; using ScaledTimingModelMap = std::map; @@ -106,16 +106,16 @@ public: void setTimingSense(TimingSense sense); FuncExpr *cond() const { return cond_; } void setCond(FuncExpr *cond); - const char *sdfCond() const { return sdf_cond_; } - void setSdfCond(const char *cond); - const char *sdfCondStart() const { return sdf_cond_start_; } - void setSdfCondStart(const char *cond); - const char *sdfCondEnd() const { return sdf_cond_end_; } - void setSdfCondEnd(const char *cond); - const char *modeName() const { return mode_name_; } - void setModeName(const char *name); - const char *modeValue() const { return mode_value_; } - void setModeValue(const char *value); + const std::string &sdfCond() const { return sdf_cond_; } + void setSdfCond(const std::string &cond); + const std::string &sdfCondStart() const { return sdf_cond_start_; } + void setSdfCondStart(const std::string &cond); + const std::string &sdfCondEnd() const { return sdf_cond_end_; } + void setSdfCondEnd(const std::string &cond); + const std::string &modeName() const { return mode_name_; } + void setModeName(const std::string &name); + const std::string &modeValue() const { return mode_value_; } + void setModeValue(const std::string &value); TimingModel *model(const RiseFall *rf) const; void setModel(const RiseFall *rf, TimingModel *model); @@ -126,11 +126,11 @@ protected: TimingType timing_type_; TimingSense timing_sense_; FuncExpr *cond_; - const char *sdf_cond_; - const char *sdf_cond_start_; - const char *sdf_cond_end_; - const char *mode_name_; - const char *mode_value_; + std::string sdf_cond_; + std::string sdf_cond_start_; + std::string sdf_cond_end_; + std::string mode_name_; + std::string mode_value_; float ocv_arc_depth_; TimingModel *models_[RiseFall::index_count]; }; @@ -142,13 +142,9 @@ protected: // See ~LibertyCell for delete of TimingArcSet members. class TimingArcSet { + friend class LibertyCell; + public: - TimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs); virtual ~TimingArcSet(); LibertyCell *libertyCell() const; LibertyPort *from() const { return from_; } @@ -156,7 +152,9 @@ public: bool isWire() const; LibertyPort *relatedOut() const { return related_out_; } const TimingRole *role() const { return role_; }; + TimingType timingType() const { return attrs_->timingType(); } TimingSense sense() const; + TimingModel *model(const RiseFall *rf) const { return attrs_->model(rf); } // Rise/fall if the arc set is rising_edge or falling_edge. const RiseFall *isRisingFallingEdge() const; size_t arcCount() const { return arcs_.size(); } @@ -168,7 +166,7 @@ public: TimingArc *&arc2) const; TimingArc *arcTo(const RiseFall *to_rf) const; const TimingArcSeq &arcs() const { return arcs_; } - TimingArcIndex addTimingArc(TimingArc *arc); + size_t addTimingArc(TimingArc *arc); void deleteTimingArc(TimingArc *arc); TimingArc *findTimingArc(unsigned arc_index); void setRole(const TimingRole *role); @@ -179,14 +177,15 @@ public: void setIsCondDefault(bool is_default); // SDF IOPATHs match sdfCond. // sdfCond (IOPATH) reuses sdfCondStart (timing check) variable. - const char *sdfCond() const { return attrs_->sdfCondStart(); } + const std::string &sdfCond() const { return attrs_->sdfCondStart(); } // SDF timing checks match sdfCondStart/sdfCondEnd. - const char *sdfCondStart() const { return attrs_->sdfCondStart(); } - const char *sdfCondEnd() const { return attrs_->sdfCondEnd(); } - const char *modeName() const { return attrs_->modeName(); } - const char *modeValue() const { return attrs_->modeValue(); } + const std::string &sdfCondStart() const { return attrs_->sdfCondStart(); } + const std::string &sdfCondEnd() const { return attrs_->sdfCondEnd(); } + const std::string &modeName() const { return attrs_->modeName(); } + const std::string &modeValue() const { return attrs_->modeValue(); } // Timing arc set index in cell. - TimingArcIndex index() const { return index_; } + size_t index() const { return index_; } + void setIndex(size_t index); // OCV arc depth from timing/cell/library. float ocvArcDepth() const; @@ -206,6 +205,13 @@ public: protected: TimingArcSet(const TimingRole *role, TimingArcAttrsPtr attrs); + TimingArcSet(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs, + size_t index); LibertyPort *from_; LibertyPort *to_; @@ -215,7 +221,7 @@ protected: TimingArcAttrsPtr attrs_; TimingArcSeq arcs_; bool is_cond_default_; - unsigned index_; + size_t index_; TimingArc *from_arc1_[RiseFall::index_count]; TimingArc *from_arc2_[RiseFall::index_count]; TimingArc *to_arc_[RiseFall::index_count]; diff --git a/liberty/EquivCells.cc b/liberty/EquivCells.cc index c39553ecc..f081435b7 100644 --- a/liberty/EquivCells.cc +++ b/liberty/EquivCells.cc @@ -218,8 +218,8 @@ static unsigned hashCellSequentials(const LibertyCell *cell) { unsigned hash = 0; - for (const Sequential *seq : cell->sequentials()) - hash += hashSequential(seq); + for (const Sequential &seq : cell->sequentials()) + hash += hashSequential(&seq); const Statetable *statetable = cell->statetable(); if (statetable) hash += hashStatetable(statetable); @@ -380,14 +380,14 @@ equivCellSequentials(const LibertyCell *cell1, for (; seq_itr1 != seqs1.end() && seq_itr2 != seqs2.end(); seq_itr1++, seq_itr2++) { - const Sequential *seq1 = *seq_itr1; - const Sequential *seq2 = *seq_itr2; - if (!(FuncExpr::equiv(seq1->clock(), seq2->clock()) - && FuncExpr::equiv(seq1->data(), seq2->data()) - && LibertyPort::equiv(seq1->output(), seq2->output()) - && LibertyPort::equiv(seq1->outputInv(), seq2->outputInv()) - && FuncExpr::equiv(seq1->clear(), seq2->clear()) - && FuncExpr::equiv(seq1->preset(), seq2->preset()))) + const Sequential &seq1 = *seq_itr1; + const Sequential &seq2 = *seq_itr2; + if (!(FuncExpr::equiv(seq1.clock(), seq2.clock()) + && FuncExpr::equiv(seq1.data(), seq2.data()) + && LibertyPort::equiv(seq1.output(), seq2.output()) + && LibertyPort::equiv(seq1.outputInv(), seq2.outputInv()) + && FuncExpr::equiv(seq1.clear(), seq2.clear()) + && FuncExpr::equiv(seq1.preset(), seq2.preset()))) return false; } return seq_itr1 == seqs1.end() && seq_itr2 == seqs2.end(); diff --git a/liberty/FuncExpr.cc b/liberty/FuncExpr.cc index 41faa2f68..38f3faea0 100644 --- a/liberty/FuncExpr.cc +++ b/liberty/FuncExpr.cc @@ -89,13 +89,17 @@ FuncExpr::FuncExpr(Op op, { } +FuncExpr::~FuncExpr() +{ + delete left_; + delete right_; +} + void -FuncExpr::deleteSubexprs() +FuncExpr::shallowDelete() { - if (left_) - left_->deleteSubexprs(); - if (right_) - right_->deleteSubexprs(); + left_ = nullptr; + right_ = nullptr; delete this; } @@ -209,7 +213,7 @@ FuncExpr::to_string(bool with_parens) const return port_->name(); case Op::not_: { string result = "!"; - result += left_->to_string(true); + result += left_ ? left_->to_string(true) : "?"; return result; } case Op::or_: @@ -235,9 +239,9 @@ FuncExpr::to_string(bool with_parens, string result; if (with_parens) result += '('; - result += left_->to_string(true); + result += left_ ? left_->to_string(true) : "?"; result += op; - result += right_->to_string(true); + result += right_ ? right_->to_string(true) : "?"; if (with_parens) result += ')'; return result; @@ -277,8 +281,9 @@ FuncExpr::bitSubExpr(int bit_offset) return makeXor(left_->bitSubExpr(bit_offset), right_->bitSubExpr(bit_offset)); case Op::one: + return makeOne(); case Op::zero: - return this; + return makeZero(); } // Prevent warnings from lame compilers. return nullptr; @@ -335,15 +340,15 @@ FuncExpr::checkSize(size_t size) } FuncExpr * -funcExprNot(FuncExpr *expr) +FuncExpr::invert() { - if (expr->op() == FuncExpr::Op::not_) { - FuncExpr *not_expr = expr->left(); - delete expr; - return not_expr; + if (op_ == FuncExpr::Op::not_) { + FuncExpr *inv = left_; + shallowDelete() ; + return inv; } else - return FuncExpr::makeNot(expr); + return FuncExpr::makeNot(this); } LibertyPortSet diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index 407b152e1..11492bf4c 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -24,6 +24,8 @@ #include "InternalPower.hh" +#include + #include "FuncExpr.hh" #include "TableModel.hh" #include "Liberty.hh" @@ -31,79 +33,17 @@ namespace sta { -using std::string; - -InternalPowerAttrs::InternalPowerAttrs() : - when_(nullptr), - models_{nullptr, nullptr}, - related_pg_pin_(nullptr) -{ -} - -InternalPowerAttrs::~InternalPowerAttrs() -{ -} - -void -InternalPowerAttrs::deleteContents() -{ - InternalPowerModel *rise_model = models_[RiseFall::riseIndex()]; - InternalPowerModel *fall_model = models_[RiseFall::fallIndex()]; - delete rise_model; - if (fall_model != rise_model) - delete fall_model; - if (when_) - when_->deleteSubexprs(); - stringDelete(related_pg_pin_); -} - -InternalPowerModel * -InternalPowerAttrs::model(const RiseFall *rf) const -{ - return models_[rf->index()]; -} - -void -InternalPowerAttrs::setWhen(FuncExpr *when) -{ - when_ = when; -} - -void -InternalPowerAttrs::setModel(const RiseFall *rf, - InternalPowerModel *model) -{ - models_[rf->index()] = model; -} - -void -InternalPowerAttrs::setRelatedPgPin(const char *related_pg_pin) -{ - stringDelete(related_pg_pin_); - related_pg_pin_ = stringCopy(related_pg_pin); -} - -//////////////////////////////////////////////////////////////// - -InternalPower::InternalPower(LibertyCell *cell, - LibertyPort *port, +InternalPower::InternalPower(LibertyPort *port, LibertyPort *related_port, - InternalPowerAttrs *attrs) : + const std::string &related_pg_pin, + const std::shared_ptr &when, + InternalPowerModels &models) : port_(port), related_port_(related_port), - when_(attrs->when()), - related_pg_pin_(attrs->relatedPgPin()) + related_pg_pin_(related_pg_pin), + when_(when), + models_(models) { - for (auto rf : RiseFall::range()) { - int rf_index = rf->index(); - models_[rf_index] = attrs->model(rf); - } - cell->addInternalPower(this); -} - -InternalPower::~InternalPower() -{ - // models_, when_ and related_pg_pin_ are owned by InternalPowerAttrs. } LibertyCell * @@ -116,15 +56,22 @@ float InternalPower::power(const RiseFall *rf, const Pvt *pvt, float in_slew, - float load_cap) + float load_cap) const { - InternalPowerModel *model = models_[rf->index()]; + 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(); +} + //////////////////////////////////////////////////////////////// InternalPowerModel::InternalPowerModel(TableModel *model) : @@ -153,7 +100,7 @@ InternalPowerModel::power(const LibertyCell *cell, return 0.0; } -string +std::string InternalPowerModel::reportPower(const LibertyCell *cell, const Pvt *pvt, float in_slew, diff --git a/liberty/LeakagePower.cc b/liberty/LeakagePower.cc index 3ff8c5ac8..07e8b7cec 100644 --- a/liberty/LeakagePower.cc +++ b/liberty/LeakagePower.cc @@ -41,6 +41,36 @@ LeakagePower::LeakagePower(LibertyCell *cell, { } +LeakagePower::LeakagePower(LeakagePower &&other) noexcept +{ + cell_ = other.cell_; + related_pg_port_ = other.related_pg_port_; + when_ = other.when_; + other.when_ = nullptr; + power_ = other.power_; +} + +LeakagePower::~LeakagePower() +{ + delete when_; +} + +} // namespace +>>>>>>> + +namespace sta { + +LeakagePower::LeakagePower(LibertyCell *cell, + LibertyPort *related_pg_port, + FuncExpr *when, + float power) : + cell_(cell), + related_pg_port_(related_pg_port), + when_(when), + power_(power) +{ +} + LeakagePower::~LeakagePower() { if (when_) diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index a95d7a140..a6e7be96d 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -92,15 +92,12 @@ LibertyLibrary::LibertyLibrary(const char *name, ocv_arc_depth_(0.0), default_ocv_derate_(nullptr), buffers_(nullptr), - inverters_(nullptr), - driver_waveform_default_(nullptr) + inverters_(nullptr) { // Scalar templates are builtin. for (int i = 0; i != table_template_type_count; i++) { TableTemplateType type = static_cast(i); - TableTemplate *scalar_template = new TableTemplate("scalar", nullptr, - nullptr, nullptr); - addTableTemplate(scalar_template, type); + makeTableTemplate("scalar", type); } for (auto rf_index : RiseFall::rangeIndex()) { @@ -114,27 +111,17 @@ LibertyLibrary::LibertyLibrary(const char *name, LibertyLibrary::~LibertyLibrary() { - deleteContents(bus_dcls_); - for (int i = 0; i < table_template_type_count; i++) - deleteContents(template_maps_[i]); - deleteContents(scale_factors_map_); delete scale_factors_; for (auto rf_index : RiseFall::rangeIndex()) { TableModel *model = wire_slew_degradation_tbls_[rf_index]; delete model; } - deleteContents(operating_conditions_); - deleteContents(wireloads_); - deleteContents(wire_load_selections_); delete units_; - // Also deletes default_ocv_derate_ - deleteContents(ocv_derate_map_); + // default_ocv_derate_ points into ocv_derate_map_; no separate delete delete buffers_; delete inverters_; - deleteContents(driver_waveform_map_); - delete driver_waveform_default_; } LibertyCell * @@ -194,39 +181,47 @@ LibertyLibrary::setDelayModelType(DelayModelType type) delay_model_type_ = type; } -void -LibertyLibrary::addBusDcl(BusDcl *bus_dcl) +BusDcl * +LibertyLibrary::makeBusDcl(std::string name, + int from, + int to) { - bus_dcls_[bus_dcl->name()] = bus_dcl; + std::string key = name; + auto [it, inserted] = bus_dcls_.try_emplace(std::move(key), std::move(name), from, to); + return &it->second; } BusDcl * LibertyLibrary::findBusDcl(const char *name) const { - return findKey(bus_dcls_, name); + auto it = bus_dcls_.find(name); + return it != bus_dcls_.end() ? const_cast(&it->second) : nullptr; } BusDclSeq LibertyLibrary::busDcls() const { BusDclSeq dcls; - for (auto [name, dcl] : bus_dcls_) - dcls.push_back(dcl); + for (auto &[key, dcl] : bus_dcls_) + dcls.push_back(const_cast(&dcl)); return dcls; } -void -LibertyLibrary::addTableTemplate(TableTemplate *tbl_template, - TableTemplateType type) +TableTemplate * +LibertyLibrary::makeTableTemplate(std::string name, + TableTemplateType type) { - template_maps_[int(type)][tbl_template->name()] = tbl_template; + std::string key = name; + auto [it, inserted] = template_maps_[int(type)].try_emplace( + std::move(key), std::move(name), type); + return &it->second; } TableTemplate * LibertyLibrary::findTableTemplate(const char *name, TableTemplateType type) { - return findKey(template_maps_[int(type)], name); + return findKeyValuePtr(template_maps_[int(type)], name); } TableTemplateSeq @@ -234,12 +229,21 @@ LibertyLibrary::tableTemplates() const { TableTemplateSeq tbl_templates; for (int type = 0; type < table_template_type_count; type++) { - for (auto [name, tbl_template] : template_maps_[type]) - tbl_templates.push_back(tbl_template); + for (auto &[key, tbl_template] : template_maps_[type]) + tbl_templates.push_back(const_cast(&tbl_template)); } return tbl_templates; } +TableTemplateSeq +LibertyLibrary::tableTemplates(TableTemplateType type) const +{ + TableTemplateSeq tbl_templates; + for (auto &[key, tbl_template] : template_maps_[int(type)]) + tbl_templates.push_back(const_cast(&tbl_template)); + return tbl_templates; +} + void LibertyLibrary::setNominalProcess(float process) { @@ -264,16 +268,17 @@ LibertyLibrary::setScaleFactors(ScaleFactors *scales) scale_factors_ = scales; } -void -LibertyLibrary::addScaleFactors(ScaleFactors *scales) +ScaleFactors * +LibertyLibrary::makeScaleFactors(const char *name) { - scale_factors_map_[scales->name()] = scales; + auto [it, inserted] = scale_factors_map_.emplace(std::string(name), name); + return &it->second; } ScaleFactors * LibertyLibrary::findScaleFactors(const char *name) { - return scale_factors_map_[name]; + return findKeyValuePtr(scale_factors_map_, std::string(name)); } float @@ -562,50 +567,55 @@ LibertyLibrary::setDefaultOutputPinRes(const RiseFall *rf, default_output_pin_res_.setValue(rf, value); } -void -LibertyLibrary::addWireload(Wireload *wireload) +Wireload * +LibertyLibrary::makeWireload(std::string name) { - wireloads_[wireload->name()] = wireload; + std::string key = name; + auto [it, inserted] = wireloads_.try_emplace( + std::move(key), name.c_str(), this); + return &it->second; } -Wireload * +const Wireload * LibertyLibrary::findWireload(const char *name) const { - return findKey(wireloads_, name); + return findKeyValuePtr(wireloads_, name); } void -LibertyLibrary::setDefaultWireload(Wireload *wireload) +LibertyLibrary::setDefaultWireload(const Wireload *wireload) { default_wire_load_ = wireload; } -Wireload * +const Wireload * LibertyLibrary::defaultWireload() const { return default_wire_load_; } -void -LibertyLibrary::addWireloadSelection(WireloadSelection *selection) +WireloadSelection * +LibertyLibrary::makeWireloadSelection(std::string name) { - wire_load_selections_[selection->name()] = selection; + std::string key = name; + auto [it, inserted] = wire_load_selections_.try_emplace(std::move(key), name.c_str()); + return &it->second; } -WireloadSelection * +const WireloadSelection * LibertyLibrary::findWireloadSelection(const char *name) const { - return findKey(wire_load_selections_, name); + return findKeyValuePtr(wire_load_selections_, name); } -WireloadSelection * +const WireloadSelection * LibertyLibrary::defaultWireloadSelection() const { return default_wire_load_selection_; } void -LibertyLibrary::setDefaultWireloadSelection(WireloadSelection *selection) +LibertyLibrary::setDefaultWireloadSelection(const WireloadSelection *selection) { default_wire_load_selection_ = selection; } @@ -622,16 +632,19 @@ LibertyLibrary::setDefaultWireloadMode(WireloadMode mode) default_wire_load_mode_ = mode; } -void -LibertyLibrary::addOperatingConditions(OperatingConditions *op_cond) +OperatingConditions * +LibertyLibrary::makeOperatingConditions(std::string name) { - operating_conditions_[op_cond->name()] = op_cond; + std::string key = name; + auto [it, inserted] = operating_conditions_.try_emplace( + std::move(key), name.c_str()); + return &it->second; } OperatingConditions * LibertyLibrary::findOperatingConditions(const char *name) { - return findKey(operating_conditions_, name); + return findKeyValuePtr(operating_conditions_, name); } OperatingConditions * @@ -848,15 +861,18 @@ LibertyLibrary::setDefaultOcvDerate(OcvDerate *derate) } OcvDerate * -LibertyLibrary::findOcvDerate(const char *derate_name) +LibertyLibrary::makeOcvDerate(std::string name) { - return findKey(ocv_derate_map_, derate_name); + std::string key = name; + auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::move(name)); + return &it->second; } -void -LibertyLibrary::addOcvDerate(OcvDerate *derate) +OcvDerate * +LibertyLibrary::findOcvDerate(const char *derate_name) { - ocv_derate_map_[derate->name()] = derate; + auto it = ocv_derate_map_.find(derate_name); + return it != ocv_derate_map_.end() ? &it->second : nullptr; } void @@ -892,18 +908,18 @@ LibertyLibrary::supplyExists(const char *supply_name) const DriverWaveform * LibertyLibrary::findDriverWaveform(const char *name) { - return driver_waveform_map_[name]; + auto it = driver_waveform_map_.find(name); + if (it != driver_waveform_map_.end()) + return &it->second; + return nullptr; } -void -LibertyLibrary::addDriverWaveform(DriverWaveform *driver_waveform) +DriverWaveform * +LibertyLibrary::makeDriverWaveform(const std::string &name, + TablePtr waveforms) { - if (driver_waveform->name()) - driver_waveform_map_[driver_waveform->name()] = driver_waveform; - else { - delete driver_waveform_default_; - driver_waveform_default_ = driver_waveform; - } + auto it = driver_waveform_map_.emplace(name, DriverWaveform(name, waveforms)); + return &it.first->second; } //////////////////////////////////////////////////////////////// @@ -961,24 +977,11 @@ LibertyCell::LibertyCell(LibertyLibrary *library, LibertyCell::~LibertyCell() { - deleteContents(mode_defs_); - deleteContents(latch_enables_); - deleteContents(timing_arc_sets_); - deleteContents(port_timing_arc_set_map_); - deleteContents(timing_arc_set_from_map_); - deleteContents(timing_arc_set_to_map_); - - deleteInternalPowerAttrs(); - deleteContents(internal_powers_); - deleteContents(leakage_powers_); - deleteContents(sequentials_); delete statetable_; - deleteContents(bus_dcls_); deleteContents(scaled_cells_); - deleteContents(ocv_derate_map_); delete test_cell_; } @@ -1025,17 +1028,17 @@ LibertyCell::setHasInternalPorts(bool has_internal) } ModeDef * -LibertyCell::makeModeDef(const char *name) +LibertyCell::makeModeDef(std::string name) { - ModeDef *mode = new ModeDef(name); - mode_defs_[mode->name()] = mode; - return mode; + std::string key = name; + auto [it, inserted] = mode_defs_.try_emplace(std::move(key), std::move(name)); + return &it->second; } -ModeDef * -LibertyCell::findModeDef(const char *name) +const ModeDef * +LibertyCell::findModeDef(const char *name) const { - return findKey(mode_defs_, name); + return findKeyValuePtr(mode_defs_, name); } void @@ -1044,16 +1047,21 @@ LibertyCell::setScaleFactors(ScaleFactors *scale_factors) scale_factors_ = scale_factors; } -void -LibertyCell::addBusDcl(BusDcl *bus_dcl) +BusDcl * +LibertyCell::makeBusDcl(std::string name, + int from, + int to) { - bus_dcls_[bus_dcl->name()] = bus_dcl; + std::string key = name; + auto [it, inserted] = bus_dcls_.try_emplace(std::move(key), std::move(name), from, to); + return &it->second; } BusDcl * LibertyCell::findBusDcl(const char *name) const { - return findKey(bus_dcls_, name); + auto it = bus_dcls_.find(name); + return it != bus_dcls_.end() ? const_cast(&it->second) : nullptr; } void @@ -1240,57 +1248,48 @@ LibertyCell::bufferPorts(// Return values. } } -unsigned -LibertyCell::addTimingArcSet(TimingArcSet *arc_set) -{ - int set_index = timing_arc_sets_.size(); +TimingArcSet * +LibertyCell::makeTimingArcSet(LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs) +{ + size_t set_index = timing_arc_sets_.size(); + TimingArcSet *arc_set = new TimingArcSet(this, from, to, related_out, role, + attrs, set_index); timing_arc_sets_.push_back(arc_set); - - LibertyPort *from = arc_set->from(); - LibertyPort *to = arc_set->to(); - const TimingRole *role = arc_set->role(); - if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) { - from->setIsRegClk(true); - to->setIsRegOutput(true); - } - if (role->isTimingCheck()) - from->setIsCheckClk(true); - return set_index; -} - -void -LibertyCell::addInternalPower(InternalPower *power) -{ - internal_powers_.push_back(power); - port_internal_powers_[power->port()].push_back(power); -} - -const InternalPowerSeq & -LibertyCell::internalPowers(const LibertyPort *port) -{ - return port_internal_powers_[port]; + return arc_set; } void -LibertyCell::addInternalPowerAttrs(InternalPowerAttrs *attrs) +LibertyCell::makeInternalPower(LibertyPort *port, + LibertyPort *related_port, + const std::string &related_pg_pin, + const std::shared_ptr &when, + InternalPowerModels &models) { - internal_power_attrs_.push_back(attrs); + internal_powers_.emplace_back(port, related_port, related_pg_pin, + when, models); + port_internal_powers_[port].push_back(internal_powers_.size() - 1); } -void -LibertyCell::deleteInternalPowerAttrs() +InternalPowerPtrSeq +LibertyCell::internalPowers(const LibertyPort *port) const { - for (InternalPowerAttrs *attrs : internal_power_attrs_) { - attrs->deleteContents(); - delete attrs; - } + InternalPowerPtrSeq result; + const InternalPowerIndexSeq &pwrs = findKeyValue(port_internal_powers_,port); + for (size_t idx : pwrs) + result.push_back(&internal_powers_[idx]); + return result; } void -LibertyCell::addLeakagePower(LeakagePower *power) +LibertyCell::makeLeakagePower(LibertyPort *related_pg_port, + FuncExpr *when, + float power) { - leakage_powers_.push_back(power); + leakage_powers_.emplace_back(this, related_pg_port, when, power); } void @@ -1315,7 +1314,6 @@ LibertyCell::finish(bool infer_latches, Debug *debug) { translatePresetClrCheckRoles(); - makeTimingArcMap(report); makeTimingArcPortMaps(); findDefaultCondArcs(); makeLatchEnables(report, debug); @@ -1328,14 +1326,14 @@ LibertyCell::findDefaultCondArcs() { for (auto [port_pair, sets] : port_timing_arc_set_map_) { bool has_cond_arcs = false; - for (auto set : *sets) { + for (auto set : sets) { if (set->cond()) { has_cond_arcs = true; break; } } if (has_cond_arcs) { - for (auto set : *sets) { + for (auto set : sets) { if (!set->cond()) set->setIsCondDefault(true); } @@ -1367,92 +1365,60 @@ LibertyCell::translatePresetClrCheckRoles() } } -void -LibertyCell::makeTimingArcMap(Report *) -{ - // Filter duplicate timing arcs, keeping the later definition. - for (auto arc_set : timing_arc_sets_) - // The last definition will be left in the set. - timing_arc_set_set_.insert(arc_set); - - // Prune the arc sets not in the map. - int j = 0; - for (size_t i = 0; i < timing_arc_sets_.size(); i++) { - TimingArcSet *arc_set = timing_arc_sets_[i]; - TimingArcSet *match = findKey(timing_arc_set_set_, arc_set); - if (match != arc_set) { - // Unfortunately these errors are common in some brain damaged - // libraries. - // report->warn("cell %s/%s has duplicate %s -> %s %s timing groups.", - // library_->name(), - // name_, - // match->from()->name(), - // match->to()->name(), - // match->role()->asString()); - delete arc_set; - } - else - // Shift arc sets down to fill holes left by removed duplicates. - timing_arc_sets_[j++] = arc_set; - } - timing_arc_sets_.resize(j); - - if (timing_arc_sets_.size() != timing_arc_sets_.size()) - criticalError(1121, "timing arc count mismatch"); -} - void LibertyCell::makeTimingArcPortMaps() { - for (auto arc_set : timing_arc_sets_) { + for (TimingArcSet *arc_set : timing_arc_sets_) { LibertyPort *from = arc_set->from(); LibertyPort *to = arc_set->to(); - LibertyPortPair port_pair(from, to); - TimingArcSetSeq *sets = - findKey(port_timing_arc_set_map_, port_pair); - if (sets == nullptr) { - // First arc set for from/to ports. - sets = new TimingArcSetSeq; - port_timing_arc_set_map_[port_pair] = sets; + if (from && to) { + LibertyPortPair from_to_pair(from, to); + TimingArcSetSeq &sets = port_timing_arc_set_map_[from_to_pair]; + sets.push_back(arc_set); } - sets->push_back(arc_set); - sets = findKey(timing_arc_set_from_map_, from); - if (sets == nullptr) { - sets = new TimingArcSetSeq; - timing_arc_set_from_map_[from] = sets; - } - sets->push_back(arc_set); + LibertyPortPair from_pair(from, nullptr); + TimingArcSetSeq &from_sets = port_timing_arc_set_map_[from_pair]; + from_sets.push_back(arc_set); - sets = findKey(timing_arc_set_to_map_, to); - if (sets == nullptr) { - sets = new TimingArcSetSeq; - timing_arc_set_to_map_[to] = sets; + LibertyPortPair to_pair(nullptr, to); + TimingArcSetSeq &to_sets = port_timing_arc_set_map_[to_pair]; + to_sets.push_back(arc_set); + + const TimingRole *role = arc_set->role(); + if (role == TimingRole::regClkToQ() + || role == TimingRole::latchEnToQ()) { + from->setIsRegClk(true); + if (to) + to->setIsRegOutput(true); } - sets->push_back(arc_set); + if (role->isTimingCheck()) + from->setIsCheckClk(true); + + timing_arc_set_set_.insert(arc_set); } } +const TimingArcSetSeq & +LibertyCell::timingArcSetsFrom(const LibertyPort *from) const +{ + return timingArcSets(from, nullptr); +} + +const TimingArcSetSeq & +LibertyCell::timingArcSetsTo(const LibertyPort *to) const +{ + return timingArcSets(nullptr, to); +} + const TimingArcSetSeq & LibertyCell::timingArcSets(const LibertyPort *from, const LibertyPort *to) const { - TimingArcSetSeq *arc_sets = nullptr; - if (from && to) { - LibertyPortPair port_pair(from, to); - arc_sets = findKey(port_timing_arc_set_map_, port_pair); - } - else if (from) - arc_sets = findKey(timing_arc_set_from_map_, from); - else if (to) - arc_sets = findKey(timing_arc_set_to_map_, to); - - if (arc_sets) - return *arc_sets; - else { - static TimingArcSetSeq null_set; - return null_set; - } + static const TimingArcSetSeq null_set; + const LibertyPortPair port_pair(from, to); + auto itr = port_timing_arc_set_map_.find(port_pair); + return (itr == port_timing_arc_set_map_.end()) ? null_set : itr->second; } TimingArcSet * @@ -1462,9 +1428,9 @@ LibertyCell::findTimingArcSet(TimingArcSet *arc_set) const } TimingArcSet * -LibertyCell::findTimingArcSet(unsigned arc_set_index) const +LibertyCell::findTimingArcSet(size_t index) const { - return timing_arc_sets_[arc_set_index]; + return timing_arc_sets_[index]; } size_t @@ -1476,8 +1442,8 @@ LibertyCell::timingArcSetCount() const bool LibertyCell::hasTimingArcs(LibertyPort *port) const { - return findKey(timing_arc_set_from_map_, port) - || findKey(timing_arc_set_to_map_, port); + return port_timing_arc_set_map_.contains(LibertyPortPair(port, nullptr)) + || port_timing_arc_set_map_.contains(LibertyPortPair(nullptr, port)); } void @@ -1511,20 +1477,23 @@ LibertyCell::makeSequential(int size, LibertyPort *out_inv_bit = output_inv; if (output_inv && output_inv->hasMembers()) out_inv_bit = output_inv->findLibertyMember(bit); - Sequential *seq = new Sequential(is_register, clk_bit, data_bit, - clear_bit,preset_bit, - clr_preset_out, clr_preset_out_inv, - out_bit, out_inv_bit); - sequentials_.push_back(seq); - port_to_seq_map_[seq->output()] = seq; - port_to_seq_map_[seq->outputInv()] = seq; + sequentials_.emplace_back(is_register, clk_bit, data_bit, + clear_bit, preset_bit, + clr_preset_out, clr_preset_out_inv, + out_bit, out_inv_bit); + size_t idx = sequentials_.size() - 1; + port_to_seq_map_[sequentials_.back().output()] = idx; + port_to_seq_map_[sequentials_.back().outputInv()] = idx; } } Sequential * LibertyCell::outputPortSequential(LibertyPort *port) { - return findKey(port_to_seq_map_, port); + auto it = port_to_seq_map_.find(port); + if (it != port_to_seq_map_.end()) + return &sequentials_[it->second]; + return nullptr; } bool @@ -1667,51 +1636,22 @@ LibertyCell::setOcvDerate(OcvDerate *derate) } OcvDerate * -LibertyCell::findOcvDerate(const char *derate_name) +LibertyCell::makeOcvDerate(std::string name) { - return findKey(ocv_derate_map_, derate_name); + std::string key = name; + auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::move(name)); + return &it->second; } -void -LibertyCell::addOcvDerate(OcvDerate *derate) +OcvDerate * +LibertyCell::findOcvDerate(const char *derate_name) { - ocv_derate_map_[derate->name()] = derate; + auto it = ocv_derate_map_.find(derate_name); + return it != ocv_derate_map_.end() ? &it->second : nullptr; } //////////////////////////////////////////////////////////////// -// Latch enable port/function for a latch D->Q timing arc set. -class LatchEnable -{ -public: - LatchEnable(LibertyPort *data, - LibertyPort *enable, - const RiseFall *enable_edge, - FuncExpr *enable_func, - LibertyPort *output, - TimingArcSet *d_to_q, - TimingArcSet *en_to_q, - TimingArcSet *setup_check); - LibertyPort *data() const { return data_; } - LibertyPort *output() const { return output_; } - LibertyPort *enable() const { return enable_; } - FuncExpr *enableFunc() const { return enable_func_; } - const RiseFall *enableEdge() const { return enable_edge_; } - TimingArcSet *dToQ() const { return d_to_q_; } - TimingArcSet *enToQ() const { return en_to_q_; } - TimingArcSet *setupCheck() const { return setup_check_; } - -private: - LibertyPort *data_; - LibertyPort *enable_; - const RiseFall *enable_edge_; - FuncExpr *enable_func_; - LibertyPort *output_; - TimingArcSet *d_to_q_; - TimingArcSet *en_to_q_; - TimingArcSet *setup_check_; -}; - LatchEnable::LatchEnable(LibertyPort *data, LibertyPort *enable, const RiseFall *enable_edge, @@ -1744,7 +1684,7 @@ LibertyCell::makeLatchEnables(Report *report, if (en_to_q->role() == TimingRole::latchEnToQ()) { LibertyPort *en = en_to_q->from(); LibertyPort *q = en_to_q->to(); - for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { + for (TimingArcSet *d_to_q : timingArcSetsTo(q)) { if (d_to_q->role() == TimingRole::latchDtoQ() && condMatch(en_to_q, d_to_q)) { LibertyPort *d = d_to_q->from(); @@ -1842,19 +1782,19 @@ LibertyCell::findLatchEnableFunc(const LibertyPort *d, const LibertyPort *en, const RiseFall *en_rf) const { - for (auto seq : sequentials_) { - if (seq->isLatch() - && seq->data() - && seq->data()->hasPort(d) - && seq->clock() - && seq->clock()->hasPort(en)) { - FuncExpr *en_func = seq->clock(); + for (const auto &seq : sequentials_) { + if (seq.isLatch() + && seq.data() + && seq.data()->hasPort(d) + && seq.clock() + && seq.clock()->hasPort(en)) { + FuncExpr *en_func = seq.clock(); TimingSense en_sense = en_func->portTimingSense(en); if ((en_sense == TimingSense::positive_unate && en_rf == RiseFall::rise()) || (en_sense == TimingSense::negative_unate && en_rf == RiseFall::fall())) - return seq->clock(); + return seq.clock(); } } return nullptr; @@ -1871,11 +1811,11 @@ LibertyCell::makeLatchEnable(LibertyPort *d, Debug *debug) { FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf); - LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q, - d_to_q, en_to_q, setup_check); - latch_enables_.push_back(latch_enable); - latch_d_to_q_map_[d_to_q] = latch_enable; - latch_check_map_[setup_check] = latch_enable; + latch_enables_.emplace_back(d, en, en_rf, en_func, q, d_to_q, en_to_q, + setup_check); + size_t idx = latch_enables_.size() - 1; + latch_d_to_q_map_[d_to_q] = idx; + latch_check_map_[setup_check] = idx; d->setIsLatchData(true); debugPrint(debug, "liberty_latch", 1, "latch %s -> %s | %s %s -> %s | %s %s -> %s setup", @@ -1887,7 +1827,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d, en->name(), setup_check->arcs()[0]->fromEdge()->asRiseFall()->shortName(), q->name()); - return latch_enable; + return &latch_enables_.back(); } void @@ -1896,13 +1836,12 @@ LibertyCell::inferLatchRoles(Report *report, { if (hasInferedRegTimingArcs()) { // Hunt down potential latch D/EN/Q triples. - std::set latch_enables; for (TimingArcSet *en_to_q : timingArcSets()) { // Locate potential d->q arcs from reg clk->q arcs. if (en_to_q->role() == TimingRole::regClkToQ()) { LibertyPort *en = en_to_q->from(); LibertyPort *q = en_to_q->to(); - for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { + for (TimingArcSet *d_to_q : timingArcSetsTo(q)) { // Look for combinational d->q arcs. const TimingRole *d_to_q_role = d_to_q->role(); if (((d_to_q_role == TimingRole::combinational() @@ -1935,11 +1874,12 @@ LibertyCell::latchEnable(const TimingArcSet *d_to_q_set, const FuncExpr *&enable_func, const RiseFall *&enable_edge) const { - LatchEnable *latch_enable = findKey(latch_d_to_q_map_, d_to_q_set); - if (latch_enable) { - enable_port = latch_enable->enable(); - enable_func = latch_enable->enableFunc(); - enable_edge = latch_enable->enableEdge(); + auto it = latch_d_to_q_map_.find(d_to_q_set); + if (it != latch_d_to_q_map_.end()) { + const LatchEnable &latch_enable = latch_enables_[it->second]; + enable_port = latch_enable.enable(); + enable_func = latch_enable.enableFunc(); + enable_edge = latch_enable.enableEdge(); } else { enable_port = nullptr; @@ -1951,9 +1891,9 @@ LibertyCell::latchEnable(const TimingArcSet *d_to_q_set, const RiseFall * LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set) { - LatchEnable *latch_enable = findKey(latch_check_map_, check_set); - if (latch_enable) - return latch_enable->enableEdge(); + auto it = latch_check_map_.find(check_set); + if (it != latch_check_map_.end()) + return latch_enables_[it->second].enableEdge(); else return nullptr; } @@ -2108,20 +2048,12 @@ LibertyPort::LibertyPort(LibertyCell *cell, liberty_port_ = this; min_pulse_width_[RiseFall::riseIndex()] = 0.0; min_pulse_width_[RiseFall::fallIndex()] = 0.0; - for (auto from_rf_index : RiseFall::rangeIndex()) { - for (auto to_rf_index : RiseFall::rangeIndex()) { - for (auto mm_index : MinMax::rangeIndex()) - clk_tree_delay_[from_rf_index][to_rf_index][mm_index] = nullptr; - } - } } LibertyPort::~LibertyPort() { - if (function_) - function_->deleteSubexprs(); - if (tristate_enable_) - tristate_enable_->deleteSubexprs(); + delete function_; + delete tristate_enable_; delete scaled_ports_; } @@ -2324,7 +2256,7 @@ LibertyPort::driveResistance(const RiseFall *rf, { float max_drive = min_max->initValue(); bool found_drive = false; - for (TimingArcSet *arc_set : liberty_cell_->timingArcSets(nullptr, this)) { + for (TimingArcSet *arc_set : liberty_cell_->timingArcSetsTo(this)) { if (!arc_set->role()->isTimingCheck()) { for (TimingArc *arc : arc_set->arcs()) { if (rf == nullptr @@ -2358,7 +2290,7 @@ LibertyPort::intrinsicDelay(const RiseFall *rf, { ArcDelay max_delay = min_max->initValue(); bool found_delay = false; - for (TimingArcSet *arc_set : liberty_cell_->timingArcSets(nullptr, this)) { + for (TimingArcSet *arc_set : liberty_cell_->timingArcSetsTo(this)) { if (!arc_set->role()->isTimingCheck()) { for (TimingArc *arc : arc_set->arcs()) { if (rf == nullptr @@ -2801,6 +2733,8 @@ LibertyPort::setDriverWaveform(DriverWaveform *driver_waveform, driver_waveform_[rf->index()] = driver_waveform; } +//////////////////////////////////////////////////////////////// + RiseFallMinMax LibertyPort::clockTreePathDelays() const { @@ -2820,12 +2754,8 @@ LibertyPort::clkTreeDelays1() const for (const RiseFall *from_rf : RiseFall::range()) { for (const RiseFall *to_rf : RiseFall::range()) { for (const MinMax *min_max : MinMax::range()) { - const TableModel *model = - clk_tree_delay_[from_rf->index()][to_rf->index()][min_max->index()]; - if (model) { - float delay = model->findValue(0.0, 0.0, 0.0); - delays.setValue(from_rf, min_max, delay); - } + float delay = clkTreeDelay(0.0, from_rf, to_rf, min_max); + delays.setValue(from_rf, min_max, delay); } } } @@ -2837,11 +2767,7 @@ LibertyPort::clkTreeDelay(float in_slew, const RiseFall *rf, const MinMax *min_max) const { - const TableModel *model = clk_tree_delay_[rf->index()][rf->index()][min_max->index()]; - if (model) - return model->findValue(in_slew, 0.0, 0.0); - else - return 0.0; + return clkTreeDelay(in_slew, rf, rf, min_max); } float @@ -2850,22 +2776,23 @@ LibertyPort::clkTreeDelay(float in_slew, const RiseFall *to_rf, const MinMax *min_max) const { - const TableModel *model = - clk_tree_delay_[from_rf->index()][to_rf->index()][min_max->index()]; - if (model) - return model->findValue(in_slew, 0.0, 0.0); - else - return 0.0; + for (TimingArcSet *arc_set : liberty_cell_->timingArcSetsTo(this)) { + if ((arc_set->role() == TimingRole::clockTreePathMin() + && min_max == MinMax::min()) + || (arc_set->role() == TimingRole::clockTreePathMax() + && min_max == MinMax::max())) { + const TimingArc *arc = arc_set->arcTo(to_rf); + if (arc->fromEdge()->asRiseFall() == from_rf) { + const GateTableModel *gate_model = dynamic_cast(arc->model()); + if (gate_model) + return gate_model->delayModel()->findValue(in_slew, 0.0, 0.0); + } + } + } + return 0.0; } -void -LibertyPort::setClkTreeDelay(const TableModel *model, - const RiseFall *from_rf, - const RiseFall *to_rf, - const MinMax *min_max) -{ - clk_tree_delay_[from_rf->index()][to_rf->index()][min_max->index()] = model; -} +//////////////////////////////////////////////////////////////// void LibertyPort::setMemberFlag(bool value, @@ -2941,11 +2868,13 @@ bool LibertyPortPairLess::operator()(const LibertyPortPair &pair1, const LibertyPortPair &pair2) const { - ObjectId id1 = pair1.first ? pair1.first->id() : 0; - ObjectId id2 = pair2.first ? pair2.first->id() : 0; - return id1 < id2 - || (id1 == id2 - && pair1.second->id() < pair2.second->id()); + ObjectId id11 = pair1.first ? pair1.first->id() : 0; + ObjectId id12 = pair1.second ? pair1.second->id() : 0; + ObjectId id21 = pair2.first ? pair2.first->id() : 0; + ObjectId id22 = pair2.second ? pair2.second->id() : 0; + return id11 < id21 + || (id11 == id21 + && id12 < id22); } //////////////////////////////////////////////////////////////// @@ -2974,10 +2903,10 @@ LibertyPortMemberIterator::next() //////////////////////////////////////////////////////////////// -BusDcl::BusDcl(const char *name, +BusDcl::BusDcl(std::string name, int from, int to) : - name_(name), + name_(std::move(name)), from_(from), to_(to) { @@ -2985,14 +2914,9 @@ BusDcl::BusDcl(const char *name, //////////////////////////////////////////////////////////////// -ModeDef::ModeDef(const char *name) : - name_(name) -{ -} - -ModeDef::~ModeDef() +ModeDef::ModeDef(std::string name) : + name_(std::move(name)) { - deleteContents(values_); } ModeValueDef * @@ -3000,32 +2924,40 @@ ModeDef::defineValue(const char *value, FuncExpr *cond, const char *sdf_cond) { - ModeValueDef *val_def = new ModeValueDef(value, cond, sdf_cond); - values_[val_def->value()] = val_def; - return val_def; + std::string key = value; + std::string sdf = sdf_cond ? std::string(sdf_cond) : std::string(); + auto [it, inserted] = values_.try_emplace(key, key, cond, std::move(sdf)); + return &it->second; } -ModeValueDef * -ModeDef::findValueDef(const char *value) +const ModeValueDef * +ModeDef::findValueDef(const char *value) const { - return values_[value]; + return findKeyValuePtr(values_, value); } //////////////////////////////////////////////////////////////// -ModeValueDef::ModeValueDef(const char *value, +ModeValueDef::ModeValueDef(std::string value, FuncExpr *cond, - const char *sdf_cond) : - value_(value), + std::string sdf_cond) : + value_(std::move(value)), cond_(cond), - sdf_cond_(sdf_cond ? sdf_cond : "") + sdf_cond_(std::move(sdf_cond)) +{ +} + +ModeValueDef::ModeValueDef(ModeValueDef &&other) noexcept : + value_(std::move(other.value_)), + cond_(other.cond_), + sdf_cond_(std::move(other.sdf_cond_)) { + other.cond_ = nullptr; } ModeValueDef::~ModeValueDef() { - if (cond_) - cond_->deleteSubexprs(); + delete cond_; } void @@ -3035,26 +2967,38 @@ ModeValueDef::setCond(FuncExpr *cond) } void -ModeValueDef::setSdfCond(const char *sdf_cond) +ModeValueDef::setSdfCond(std::string sdf_cond) { - sdf_cond_ = sdf_cond; + sdf_cond_ = std::move(sdf_cond); } //////////////////////////////////////////////////////////////// -TableTemplate::TableTemplate(const char *name) : - name_(name), +TableTemplate::TableTemplate(std::string name) : + name_(std::move(name)), + type_(TableTemplateType::delay), axis1_(nullptr), axis2_(nullptr), axis3_(nullptr) { } -TableTemplate::TableTemplate(const char *name, +TableTemplate::TableTemplate(std::string name, + TableTemplateType type) : + name_(std::move(name)), + type_(type), + axis1_(nullptr), + axis2_(nullptr), + axis3_(nullptr) +{ +} + +TableTemplate::TableTemplate(std::string name, TableAxisPtr axis1, TableAxisPtr axis2, TableAxisPtr axis3) : - name_(name), + name_(std::move(name)), + type_(TableTemplateType::delay), axis1_(axis1), axis2_(axis2), axis3_(axis3) @@ -3062,9 +3006,9 @@ TableTemplate::TableTemplate(const char *name, } void -TableTemplate::setName(const char *name) +TableTemplate::setName(std::string name) { - name_ = name; + name_ = std::move(name); } void @@ -3299,16 +3243,16 @@ ScaleFactors::print() } TestCell::TestCell(LibertyLibrary *library, - const char *name, - const char *filename) : - LibertyCell(library, name, filename) + std::string name, + std::string filename) : + LibertyCell(library, name.c_str(), filename.c_str()) { } //////////////////////////////////////////////////////////////// -OcvDerate::OcvDerate(const char *name) : - name_(name) +OcvDerate::OcvDerate(std::string name) : + name_(std::move(name)) { for (auto el_index : EarlyLate::rangeIndex()) { for (auto rf_index : RiseFall::rangeIndex()) { @@ -3320,7 +3264,6 @@ OcvDerate::OcvDerate(const char *name) : OcvDerate::~OcvDerate() { - stringDelete(name_); } const Table * diff --git a/liberty/Liberty.i b/liberty/Liberty.i index 10bb9963d..962231705 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -237,13 +237,13 @@ find_liberty_cells_matching(const char *pattern, return self->findLibertyCellsMatching(&matcher); } -Wireload * +const Wireload * find_wireload(const char *model_name) { return self->findWireload(model_name); } -WireloadSelection * +const WireloadSelection * find_wireload_selection(const char *selection_name) { return self->findWireloadSelection(selection_name); @@ -364,7 +364,7 @@ scan_signal_type() LibertyPort *from() { return self->from(); } LibertyPort *to() { return self->to(); } const TimingRole *role() { return self->role(); } -const char *sdf_cond() { return self->sdfCond(); } +const char *sdf_cond() { return self->sdfCond().c_str(); } const char * full_name() @@ -448,7 +448,7 @@ voltage_time(float in_slew, return 0.0; } -Table1 +Table voltage_waveform(float in_slew, float load_cap) { @@ -456,14 +456,14 @@ voltage_waveform(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - Table1 waveform = waveforms->voltageWaveform(in_slew, load_cap); + Table waveform = waveforms->voltageWaveform(in_slew, load_cap); return waveform; } } - return Table1(); + return Table(); } -const Table1 * +const Table * voltage_waveform_raw(float in_slew, float load_cap) { @@ -471,14 +471,14 @@ voltage_waveform_raw(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - const Table1 *waveform = waveforms->voltageWaveformRaw(in_slew, load_cap); + const Table *waveform = waveforms->voltageWaveformRaw(in_slew, load_cap); return waveform; } } return nullptr; } -Table1 +Table current_waveform(float in_slew, float load_cap) { @@ -486,14 +486,14 @@ current_waveform(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - Table1 waveform = waveforms->currentWaveform(in_slew, load_cap); + Table waveform = waveforms->currentWaveform(in_slew, load_cap); return waveform; } } - return Table1(); + return Table(); } -const Table1 * +const Table * current_waveform_raw(float in_slew, float load_cap) { @@ -501,14 +501,14 @@ current_waveform_raw(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - const Table1 *waveform = waveforms->currentWaveformRaw(in_slew, load_cap); + const Table *waveform = waveforms->currentWaveformRaw(in_slew, load_cap); return waveform; } } return nullptr; } -Table1 +Table voltage_current_waveform(float in_slew, float load_cap) { @@ -516,11 +516,11 @@ voltage_current_waveform(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - Table1 waveform = waveforms->voltageCurrentWaveform(in_slew, load_cap); + Table waveform = waveforms->voltageCurrentWaveform(in_slew, load_cap); return waveform; } } - return Table1(); + return Table(); } float diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index 5afedc099..b20d8c620 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -30,8 +30,6 @@ #include "TimingArc.hh" #include "TimingModel.hh" #include "TableModel.hh" -#include "InternalPower.hh" -#include "LeakagePower.hh" #include "Sequential.hh" #include "Liberty.hh" @@ -263,28 +261,24 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, attrs); case TimingType::non_seq_setup_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), - TimingRole::nonSeqSetup(), attrs); + RiseFall::rise(), TimingRole::nonSeqSetup(), + attrs); case TimingType::non_seq_setup_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), - TimingRole::nonSeqSetup(), attrs); + RiseFall::fall(), TimingRole::nonSeqSetup(), + attrs); case TimingType::non_seq_hold_rising: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::rise(), - TimingRole::nonSeqHold(), + RiseFall::rise(), TimingRole::nonSeqHold(), attrs); case TimingType::non_seq_hold_falling: return makeFromTransitionArcs(cell, from_port, to_port, related_out, - RiseFall::fall(), - TimingRole::nonSeqHold(), + RiseFall::fall(), TimingRole::nonSeqHold(), attrs); case TimingType::min_clock_tree_path: - return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMin(), - MinMax::min(), attrs); + return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMin(), attrs); case TimingType::max_clock_tree_path: - return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMax(), - MinMax::max(), attrs); + return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMax(), attrs); case TimingType::min_pulse_width: return makeMinPulseWidthArcs(cell, from_port, to_port, related_out, TimingRole::width(), attrs); @@ -652,30 +646,24 @@ TimingArcSet * LibertyBuilder::makeClockTreePathArcs(LibertyCell *cell, LibertyPort *to_port, const TimingRole *role, - const MinMax *min_max, TimingArcAttrsPtr attrs) { TimingArcSet *arc_set = makeTimingArcSet(cell, nullptr, to_port, role, attrs); - for (auto to_rf : RiseFall::range()) { + for (const RiseFall *to_rf : RiseFall::range()) { TimingModel *model = attrs->model(to_rf); if (model) { - const GateTableModel *gate_model = dynamic_cast(model); const RiseFall *opp_rf = to_rf->opposite(); switch (attrs->timingSense()) { case TimingSense::positive_unate: makeTimingArc(arc_set, to_rf, to_rf, model); - to_port->setClkTreeDelay(gate_model->delayModel(), to_rf, to_rf, min_max); break; case TimingSense::negative_unate: makeTimingArc(arc_set, opp_rf, to_rf, model); - to_port->setClkTreeDelay(gate_model->delayModel(), opp_rf, to_rf, min_max); break; case TimingSense::non_unate: case TimingSense::unknown: makeTimingArc(arc_set, to_rf, to_rf, model); makeTimingArc(arc_set, opp_rf, to_rf, model); - to_port->setClkTreeDelay(gate_model->delayModel(), to_rf, to_rf, min_max); - to_port->setClkTreeDelay(gate_model->delayModel(), opp_rf, to_rf, min_max); break; case TimingSense::none: break; @@ -714,7 +702,7 @@ LibertyBuilder::makeTimingArcSet(LibertyCell *cell, const TimingRole *role, TimingArcAttrsPtr attrs) { - return new TimingArcSet(cell, from, to, nullptr, role, attrs); + return cell->makeTimingArcSet(from, to, nullptr, role, attrs); } TimingArcSet * @@ -725,7 +713,7 @@ LibertyBuilder::makeTimingArcSet(LibertyCell *cell, const TimingRole *role, TimingArcAttrsPtr attrs) { - return new TimingArcSet(cell, from, to, related_out, role, attrs); + return cell->makeTimingArcSet(from, to, related_out, role, attrs); } TimingArc * @@ -747,15 +735,4 @@ LibertyBuilder::makeTimingArc(TimingArcSet *set, return new TimingArc(set, from_rf, to_rf, model); } -//////////////////////////////////////////////////////////////// - -InternalPower * -LibertyBuilder::makeInternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs) -{ - return new InternalPower(cell, port, related_port, attrs); -} - } // namespace diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh index 8511b1084..dacbbb416 100644 --- a/liberty/LibertyBuilder.hh +++ b/liberty/LibertyBuilder.hh @@ -32,8 +32,6 @@ namespace sta { class TimingArcAttrs; -class InternalPowerAttrs; -class LeakagePowerAttrs; class Debug; class Report; @@ -66,11 +64,6 @@ public: LibertyPort *related_out, TimingArcAttrsPtr attrs, int line); - InternalPower *makeInternalPower(LibertyCell *cell, - LibertyPort *port, - LibertyPort *related_port, - InternalPowerAttrs *attrs); - TimingArcSet *makeFromTransitionArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, @@ -87,7 +80,6 @@ public: TimingArcSet *makeClockTreePathArcs(LibertyCell *cell, LibertyPort *to_port, const TimingRole *role, - const MinMax *min_max, TimingArcAttrsPtr attrs); TimingArcSet *makeMinPulseWidthArcs(LibertyCell *cell, LibertyPort *from_port, diff --git a/liberty/LibertyExt.cc b/liberty/LibertyExt.cc index 10a214b12..d45a7420a 100644 --- a/liberty/LibertyExt.cc +++ b/liberty/LibertyExt.cc @@ -61,6 +61,11 @@ class BigcoCell : public LibertyCell public: BigcoCell(LibertyLibrary *library, const char *name, const char *filename); void setThingy(const char *thingy); + TimingArcSet *makeTimingArcSet(LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs) override; protected: const char *thingy_; @@ -79,6 +84,28 @@ BigcoCell::setThingy(const char *thingy) thingy_ = thingy; } +TimingArcSet * +BigcoCell::makeTimingArcSet(LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs) +{ + size_t set_index = timing_arc_sets_.size(); + TimingArcSet *arc_set = new BigcoTimingArcSet(this, from, to, related_out, + role, attrs, set_index); + timing_arc_sets_.push_back(arc_set); + + if (role == TimingRole::regClkToQ() + || role == TimingRole::latchEnToQ()) { + from->setIsRegClk(true); + to->setIsRegOutput(true); + } + if (role->isTimingCheck()) + from->setIsCheckClk(true); + return arc_set; +} + //////////////////////////////////////////////////////////////// class BigcoTimingGroup : public TimingGroup @@ -109,21 +136,22 @@ BigcoTimingGroup::setFrob(const char *frob) class BigcoTimingArcSet : public TimingArcSet { public: - BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, LibertyPort *to, - LibertyPort *related_out, TimingRole *role, - TimingArcAttrs *attrs); + BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, LibertyPort *to, + LibertyPort *related_out, const TimingRole *role, + TimingArcAttrsPtr attrs, size_t index); protected: const char *frob_; }; -BigcoTimingArcSet::BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, TimingRole *role, - TimingArcAttrs *attrs) : - TimingArcSet(cell, from, to, related_out, role, attrs) +BigcoTimingArcSet::BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + const TimingRole *role, + TimingArcAttrsPtr attrs, size_t index) : + TimingArcSet(cell, from, to, related_out, role, attrs, index) { - const char *frob = static_cast(attrs)->frob(); + const char *frob = static_cast(attrs.get())->frob(); if (frob) frob_ = stringCopy(frob); } @@ -138,11 +166,11 @@ class BigcoLibertyBuilder : public LibertyBuilder const char *filename); protected: - virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, + virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, LibertyPort *from, + LibertyPort *to, LibertyPort *related_out, - TimingRole *role, - TimingArcAttrs *attrs); + const TimingRole *role, + TimingArcAttrsPtr attrs) override; }; LibertyCell * @@ -155,13 +183,13 @@ BigcoLibertyBuilder::makeCell(LibertyLibrary *library, const char *name, } TimingArcSet * -BigcoLibertyBuilder::makeTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, +BigcoLibertyBuilder::makeTimingArcSet(LibertyCell *cell, LibertyPort *from, + LibertyPort *to, LibertyPort *related_out, - TimingRole *role, - TimingArcAttrs *attrs) + const TimingRole *role, + TimingArcAttrsPtr attrs) { - return new BigcoTimingArcSet(cell, from, to, related_out, role, attrs); + return cell->makeTimingArcSet(from, to, related_out, role, attrs); } //////////////////////////////////////////////////////////////// diff --git a/liberty/LibertyLex.ll b/liberty/LibertyLex.ll index ccf2aea5e..6df7d07b5 100644 --- a/liberty/LibertyLex.ll +++ b/liberty/LibertyLex.ll @@ -87,14 +87,14 @@ EOL \r?\n {FLOAT}{TOKEN_END} { /* Push back the TOKEN_END character. */ yyless(yyleng - 1); - yylval->number = strtod(yytext, nullptr); + yylval->emplace(strtod(yytext, nullptr)); return token::FLOAT; } {ALPHA}({ALPHA}|_|{DIGIT})*{TOKEN_END} { /* Push back the TOKEN_END character. */ yyless(yyleng - 1); - yylval->string = sta::stringCopy(yytext); + yylval->emplace(yytext); return token::KEYWORD; } @@ -107,7 +107,7 @@ EOL \r?\n {TOKEN}{TOKEN_END} { /* Push back the TOKEN_END character. */ yyless(yyleng - 1); - yylval->string = sta::stringCopy(yytext); + yylval->emplace(yytext); return token::STRING; } @@ -134,14 +134,14 @@ EOL \r?\n \" { BEGIN(INITIAL); - yylval->string = stringCopy(token_.c_str()); + yylval->emplace(token_); return token::STRING; } {EOL} { error("unterminated string constant"); BEGIN(INITIAL); - yylval->string = stringCopy(token_.c_str()); + yylval->emplace(token_); return token::STRING; } diff --git a/liberty/LibertyParse.yy b/liberty/LibertyParse.yy index 7cc067e34..7423c4fe0 100644 --- a/liberty/LibertyParse.yy +++ b/liberty/LibertyParse.yy @@ -24,6 +24,9 @@ %{ #include +#include +#include +#include #include "Report.hh" #include "liberty/LibertyParser.hh" @@ -57,32 +60,23 @@ sta::LibertyParse::error(const location_type &loc, %parse-param { LibertyScanner *scanner } %parse-param { LibertyParser *reader } %define api.parser.class {LibertyParse} +%define api.value.type variant %expect 2 -%union { - char *string; - float number; - char ch; - sta::LibertyAttrValue *attr_value; - sta::LibertyAttrValueSeq *attr_values; - sta::LibertyGroup *group; - sta::LibertyStmt *stmt; -} +%token STRING KEYWORD +%token FLOAT %left '+' '-' '|' %left '*' '/' '&' %left '^' %left '!' -%token FLOAT -%token STRING KEYWORD - -%type statement complex_attr simple_attr variable group file -%type attr_values -%type attr_value -%type string expr expr_term expr_term1 volt_expr -%type expr_op volt_op +%type statement complex_attr simple_attr variable group file +%type attr_values +%type attr_value +%type string expr expr_term expr_term1 volt_expr +%type expr_op volt_op %start file @@ -94,19 +88,19 @@ file: group: KEYWORD '(' ')' '{' - { reader->groupBegin($1, nullptr, loc_line(@1)); } + { reader->groupBegin(std::move($1), nullptr, loc_line(@1)); } '}' semi_opt { $$ = reader->groupEnd(); } | KEYWORD '(' ')' '{' - { reader->groupBegin($1, nullptr, loc_line(@1)); } + { reader->groupBegin(std::move($1), nullptr, loc_line(@1)); } statements '}' semi_opt { $$ = reader->groupEnd(); } | KEYWORD '(' attr_values ')' '{' - { reader->groupBegin($1, $3, loc_line(@1)); } + { reader->groupBegin(std::move($1), $3, loc_line(@1)); } '}' semi_opt { $$ = reader->groupEnd(); } | KEYWORD '(' attr_values ')' '{' - { reader->groupBegin($1, $3, loc_line(@1)); } + { reader->groupBegin(std::move($1), $3, loc_line(@1)); } statements '}' semi_opt { $$ = reader->groupEnd(); } ; @@ -125,14 +119,14 @@ statement: simple_attr: KEYWORD ':' attr_value semi_opt - { $$ = reader->makeSimpleAttr($1, $3, loc_line(@1)); } + { $$ = reader->makeSimpleAttr(std::move($1), $3, loc_line(@1)); } ; complex_attr: KEYWORD '(' ')' semi_opt - { $$ = reader->makeComplexAttr($1, nullptr, loc_line(@1)); } + { $$ = reader->makeComplexAttr(std::move($1), nullptr, loc_line(@1)); } | KEYWORD '(' attr_values ')' semi_opt - { $$ = reader->makeComplexAttr($1, $3, loc_line(@1)); } + { $$ = reader->makeComplexAttr(std::move($1), $3, loc_line(@1)); } ; attr_values: @@ -152,7 +146,7 @@ attr_values: variable: string '=' FLOAT semi_opt - { $$ = reader->makeVariable($1, $3, loc_line(@1)); } + { $$ = reader->makeVariable(std::move($1), $3, loc_line(@1)); } ; string: @@ -166,28 +160,22 @@ attr_value: FLOAT { $$ = reader->makeFloatAttrValue($1); } | expr - { $$ = reader->makeStringAttrValue($1); } + { $$ = reader->makeStringAttrValue(std::move($1)); } | volt_expr - { $$ = reader->makeStringAttrValue($1); } + { $$ = reader->makeStringAttrValue(std::move($1)); } ; /* Voltage expressions are ignored. */ /* Crafted to avoid conflicts with expr */ volt_expr: FLOAT volt_op FLOAT - { $$ = sta::stringPrint("%e%c%e", $1, $2, $3); } + { $$ = sta::stdstrPrint("%e%c%e", $1, $2, $3); } | string volt_op FLOAT - { $$ = sta::stringPrint("%s%c%e", $1, $2, $3); - sta::stringDelete($1); - } + { $$ = sta::stdstrPrint("%s%c%e", $1.c_str(), $2, $3); } | FLOAT volt_op string - { $$ = sta::stringPrint("%e%c%s", $1, $2, $3); - sta::stringDelete($3); - } + { $$ = sta::stdstrPrint("%e%c%s", $1, $2, $3.c_str()); } | volt_expr volt_op FLOAT - { $$ = sta::stringPrint("%s%c%e", $1, $2, $3); - sta::stringDelete($1); - } + { $$ = sta::stdstrPrint("%s%c%e", $1.c_str(), $2, $3); } ; volt_op: @@ -204,34 +192,25 @@ volt_op: expr: expr_term1 | expr_term1 expr_op expr - { $$ = sta::stringPrint("%s%c%s", $1, $2, $3); - sta::stringDelete($1); - sta::stringDelete($3); - } + { $$ = sta::stdstrPrint("%s%c%s", $1.c_str(), $2, $3.c_str()); } ; expr_term: string | '0' - { $$ = sta::stringPrint("0"); } + { $$ = std::string("0"); } | '1' - { $$ = sta::stringPrint("1"); } + { $$ = std::string("1"); } | '(' expr ')' - { $$ = sta::stringPrint("(%s)", $2); - sta::stringDelete($2); - } + { $$ = "(" + $2 + ")"; } ; expr_term1: expr_term | '!' expr_term - { $$ = sta::stringPrint("!%s", $2); - sta::stringDelete($2); - } + { $$ = "!" + $2; } | expr_term '\'' - { $$ = sta::stringPrint("%s'", $1); - sta::stringDelete($1); - } + { $$ = $1 + "'"; } ; expr_op: diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index 4dff3a40f..9cf527021 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -76,15 +76,15 @@ LibertyParser::makeDefine(LibertyAttrValueSeq *values, { LibertyDefine *define = nullptr; if (values->size() == 3) { - const char *define_name = (*values)[0]->stringValue(); - const char *group_type_name = (*values)[1]->stringValue(); - const char *value_type_name = (*values)[2]->stringValue(); - LibertyAttrType value_type = attrValueType(value_type_name); - LibertyGroupType group_type = groupType(group_type_name); - define = new LibertyDefine(define_name, group_type, + std::string define_name = (*values)[0]->stringValue(); + const std::string &group_type_name = (*values)[1]->stringValue(); + const std::string &value_type_name = (*values)[2]->stringValue(); + LibertyAttrType value_type = attrValueType(value_type_name.c_str()); + LibertyGroupType group_type = groupType(group_type_name.c_str()); + define = new LibertyDefine(std::move(define_name), group_type, value_type, line); LibertyGroup *group = this->group(); - group->addDefine(define); + group->addStmt(define); } else report_->fileWarn(24, filename_.c_str(), line, @@ -126,12 +126,11 @@ LibertyParser::groupType(const char *group_type_name) } void -LibertyParser::groupBegin(const char *type, +LibertyParser::groupBegin(std::string type, LibertyAttrValueSeq *params, int line) { - LibertyGroup *group = new LibertyGroup(type, params, line); - stringDelete(type); + LibertyGroup *group = new LibertyGroup(std::move(type), params, line); group_visitor_->begin(group); group_stack_.push_back(group); } @@ -145,9 +144,11 @@ LibertyParser::groupEnd() LibertyGroup *parent = group_stack_.empty() ? nullptr : group_stack_.back(); if (parent && group_visitor_->save(group)) { - parent->addSubgroup(group); + parent->addStmt(group); return group; } + else if (group_visitor_->save(group)) + return group; else { delete group; return nullptr; @@ -167,16 +168,15 @@ LibertyParser::deleteGroups() } LibertyStmt * -LibertyParser::makeSimpleAttr(const char *name, +LibertyParser::makeSimpleAttr(std::string name, LibertyAttrValue *value, int line) { - LibertyAttr *attr = new LibertySimpleAttr(name, value, line); - stringDelete(name); + LibertyAttr *attr = new LibertySimpleAttr(std::move(name), value, line); group_visitor_->visitAttr(attr); LibertyGroup *group = this->group(); if (group && group_visitor_->save(attr)) { - group->addAttribute(attr); + group->addStmt(attr); return attr; } else { @@ -186,26 +186,24 @@ LibertyParser::makeSimpleAttr(const char *name, } LibertyStmt * -LibertyParser::makeComplexAttr(const char *name, +LibertyParser::makeComplexAttr(std::string name, LibertyAttrValueSeq *values, int line) { // Defines have the same syntax as complex attributes. // Detect and convert them. - if (stringEq(name, "define")) { + if (name == "define") { LibertyStmt *define = makeDefine(values, line); - stringDelete(name); deleteContents(values); delete values; return define; } else { - LibertyAttr *attr = new LibertyComplexAttr(name, values, line); - stringDelete(name); + LibertyAttr *attr = new LibertyComplexAttr(std::move(name), values, line); group_visitor_->visitAttr(attr); if (group_visitor_->save(attr)) { LibertyGroup *group = this->group(); - group->addAttribute(attr); + group->addStmt(attr); return attr; } delete attr; @@ -214,12 +212,11 @@ LibertyParser::makeComplexAttr(const char *name, } LibertyStmt * -LibertyParser::makeVariable(const char *var, +LibertyParser::makeVariable(std::string var, float value, int line) { - LibertyVariable *variable = new LibertyVariable(var, value, line); - stringDelete(var); + LibertyVariable *variable = new LibertyVariable(std::move(var), value, line); group_visitor_->visitVariable(variable); if (group_visitor_->save(variable)) return variable; @@ -230,11 +227,9 @@ LibertyParser::makeVariable(const char *var, } LibertyAttrValue * -LibertyParser::makeStringAttrValue(char *value) +LibertyParser::makeStringAttrValue(std::string value) { - LibertyAttrValue *attr = new LibertyStringAttrValue(value); - stringDelete(value); - return attr; + return new LibertyStringAttrValue(std::move(value)); } LibertyAttrValue * @@ -243,6 +238,14 @@ LibertyParser::makeFloatAttrValue(float value) return new LibertyFloatAttrValue(value); } +const std::string & +LibertyFloatAttrValue::stringValue() const +{ + criticalError(1127, "LibertyStringAttrValue called for float value"); + static std::string null; + return null; +} + //////////////////////////////////////////////////////////////// LibertyStmt::LibertyStmt(int line) : @@ -250,49 +253,22 @@ LibertyStmt::LibertyStmt(int line) : { } -LibertyGroup::LibertyGroup(const char *type, +LibertyGroup::LibertyGroup(std::string type, LibertyAttrValueSeq *params, int line) : LibertyStmt(line), - type_(type), + type_(std::move(type)), params_(params), - attrs_(nullptr), - attr_map_(nullptr), - subgroups_(nullptr), - define_map_(nullptr) + stmts_(nullptr) { } void -LibertyGroup::addSubgroup(LibertyGroup *subgroup) +LibertyGroup::addStmt(LibertyStmt *stmt) { - if (subgroups_ == nullptr) - subgroups_ = new LibertyGroupSeq; - subgroups_->push_back(subgroup); -} - -void -LibertyGroup::addDefine(LibertyDefine *define) -{ - if (define_map_ == nullptr) - define_map_ = new LibertyDefineMap; - const char *define_name = define->name(); - LibertyDefine *prev_define = findKey(define_map_, define_name); - if (prev_define) { - define_map_->erase(define_name); - delete prev_define; - } - (*define_map_)[define_name] = define; -} - -void -LibertyGroup::addAttribute(LibertyAttr *attr) -{ - if (attrs_ == nullptr) - attrs_ = new LibertyAttrSeq; - attrs_->push_back(attr); - if (attr_map_) - (*attr_map_)[attr->name()] = attr; + if (stmts_ == nullptr) + stmts_ = new LibertyStmtSeq; + stmts_->push_back(stmt); } LibertyGroup::~LibertyGroup() @@ -301,18 +277,9 @@ LibertyGroup::~LibertyGroup() deleteContents(params_); delete params_; } - if (attrs_) { - deleteContents(attrs_); - delete attrs_; - delete attr_map_; - } - if (subgroups_) { - deleteContents(subgroups_); - delete subgroups_; - } - if (define_map_) { - deleteContents(define_map_); - delete define_map_; + if (stmts_) { + deleteContents(stmts_); + delete stmts_; } } @@ -322,7 +289,7 @@ LibertyGroup::firstName() if (params_ && params_->size() > 0) { LibertyAttrValue *value = (*params_)[0]; if (value->isString()) - return value->stringValue(); + return value->stringValue().c_str(); } return nullptr; } @@ -333,39 +300,24 @@ LibertyGroup::secondName() if (params_ && params_->size() > 1) { LibertyAttrValue *value = (*params_)[1]; if (value->isString()) - return value->stringValue(); + return value->stringValue().c_str(); } return nullptr; } -LibertyAttr * -LibertyGroup::findAttr(const char *name) -{ - if (attrs_) { - if (attr_map_ == nullptr) { - // Build attribute name map on demand. - for (LibertyAttr *attr : *attrs_) - (*attr_map_)[attr->name()] = attr; - } - return findKey(attr_map_, name); - } - else - return nullptr; -} - //////////////////////////////////////////////////////////////// -LibertyAttr::LibertyAttr(const char *name, +LibertyAttr::LibertyAttr(std::string name, int line) : LibertyStmt(line), - name_(name) + name_(std::move(name)) { } -LibertySimpleAttr::LibertySimpleAttr(const char *name, +LibertySimpleAttr::LibertySimpleAttr(std::string name, LibertyAttrValue *value, int line) : - LibertyAttr(name, line), + LibertyAttr(std::move(name), line), value_(value) { } @@ -382,10 +334,12 @@ LibertySimpleAttr::values() const return nullptr; } -LibertyComplexAttr::LibertyComplexAttr(const char *name, +//////////////////////////////////////////////////////////////// + +LibertyComplexAttr::LibertyComplexAttr(std::string name, LibertyAttrValueSeq *values, int line) : - LibertyAttr(name, line), + LibertyAttr(std::move(name), line), values_(values) { } @@ -407,9 +361,9 @@ LibertyComplexAttr::firstValue() return nullptr; } -LibertyStringAttrValue::LibertyStringAttrValue(const char *value) : +LibertyStringAttrValue::LibertyStringAttrValue(std::string value) : LibertyAttrValue(), - value_(value) + value_(std::move(value)) { } @@ -420,38 +374,19 @@ LibertyStringAttrValue::floatValue() const return 0.0; } -const char * -LibertyStringAttrValue::stringValue() const -{ - return value_.c_str(); -} - LibertyFloatAttrValue::LibertyFloatAttrValue(float value) : value_(value) { } -float -LibertyFloatAttrValue::floatValue() const -{ - return value_; -} - -const char * -LibertyFloatAttrValue::stringValue() const -{ - criticalError(1127, "LibertyStringAttrValue called for float value"); - return nullptr; -} - //////////////////////////////////////////////////////////////// -LibertyDefine::LibertyDefine(const char *name, +LibertyDefine::LibertyDefine(std::string name, LibertyGroupType group_type, LibertyAttrType value_type, int line) : LibertyStmt(line), - name_(name), + name_(std::move(name)), group_type_(group_type), value_type_(value_type) { @@ -459,11 +394,11 @@ LibertyDefine::LibertyDefine(const char *name, //////////////////////////////////////////////////////////////// -LibertyVariable::LibertyVariable(const char *var, +LibertyVariable::LibertyVariable(std::string var, float value, int line) : LibertyStmt(line), - var_(var), + var_(std::move(var)), value_(value) { } diff --git a/liberty/LibertyParser.hh b/liberty/LibertyParser.hh index 9b1fce2ca..e27d859af 100644 --- a/liberty/LibertyParser.hh +++ b/liberty/LibertyParser.hh @@ -34,15 +34,12 @@ namespace sta { class Report; class LibertyGroupVisitor; -class LibertyAttrVisitor; class LibertyStmt; class LibertyGroup; class LibertyAttr; class LibertyDefine; class LibertyAttrValue; class LibertyVariable; -class LibertySubgroupIterator; -class LibertyAttrIterator; class LibertyScanner; using LibertyStmtSeq = std::vector; @@ -72,21 +69,21 @@ public: int line); LibertyAttrType attrValueType(const char *value_type_name); LibertyGroupType groupType(const char *group_type_name); - void groupBegin(const char *type, + void groupBegin(std::string type, LibertyAttrValueSeq *params, int line); LibertyGroup *groupEnd(); LibertyGroup *group(); void deleteGroups(); - LibertyStmt *makeSimpleAttr(const char *name, + LibertyStmt *makeSimpleAttr(std::string name, LibertyAttrValue *value, int line); - LibertyStmt *makeComplexAttr(const char *name, + LibertyStmt *makeComplexAttr(std::string name, LibertyAttrValueSeq *values, int line); - LibertyAttrValue *makeStringAttrValue(char *value); + LibertyAttrValue *makeStringAttrValue(std::string value); LibertyAttrValue *makeFloatAttrValue(float value); - LibertyStmt *makeVariable(const char *var, + LibertyStmt *makeVariable(std::string var, float value, int line); @@ -106,6 +103,8 @@ public: int line() const { return line_; } virtual bool isGroup() const { return false; } virtual bool isAttribute() const { return false; } + virtual bool isSimpleAttr() const { return false; } + virtual bool isComplexAttr() const { return false; } virtual bool isDefine() const { return false; } virtual bool isVariable() const { return false; } @@ -119,46 +118,35 @@ protected: class LibertyGroup : public LibertyStmt { public: - LibertyGroup(const char *type, + LibertyGroup(std::string type, LibertyAttrValueSeq *params, int line); virtual ~LibertyGroup(); virtual bool isGroup() const { return true; } - const char *type() const { return type_.c_str(); } + const std::string &type() const { return type_; } + LibertyAttrValueSeq *params() const { return params_; } // First param as a string. const char *firstName(); // Second param as a string. const char *secondName(); - LibertyAttr *findAttr(const char *name); - void addSubgroup(LibertyGroup *subgroup); - void addDefine(LibertyDefine *define); - void addAttribute(LibertyAttr *attr); - void addVariable(LibertyVariable *var); - LibertyGroupSeq *subgroups() const { return subgroups_; } - LibertyAttrSeq *attrs() const { return attrs_; } - LibertyAttrValueSeq *params() const { return params_; } + void addStmt(LibertyStmt *stmt); + LibertyStmtSeq *stmts() const { return stmts_; } protected: void parseNames(LibertyAttrValueSeq *values); std::string type_; LibertyAttrValueSeq *params_; - LibertyAttrSeq *attrs_; - LibertyAttrMap *attr_map_; - LibertyGroupSeq *subgroups_; - LibertyDefineMap *define_map_; + LibertyStmtSeq *stmts_; }; // Abstract base class for attributes. class LibertyAttr : public LibertyStmt { public: - LibertyAttr(const char *name, + LibertyAttr(std::string name, int line); - const char *name() const { return name_.c_str(); } - virtual bool isAttribute() const { return true; } - virtual bool isSimple() const = 0; - virtual bool isComplex() const = 0; + const std::string &name() const { return name_; } virtual LibertyAttrValueSeq *values() const = 0; virtual LibertyAttrValue *firstValue() = 0; @@ -171,12 +159,11 @@ protected: class LibertySimpleAttr : public LibertyAttr { public: - LibertySimpleAttr(const char *name, + LibertySimpleAttr(std::string name, LibertyAttrValue *value, int line); virtual ~LibertySimpleAttr(); - bool isSimple() const override { return true; }; - bool isComplex() const override { return false; }; + bool isSimpleAttr() const override { return true; }; LibertyAttrValue *firstValue() override { return value_; }; LibertyAttrValueSeq *values() const override; @@ -189,12 +176,11 @@ private: class LibertyComplexAttr : public LibertyAttr { public: - LibertyComplexAttr(const char *name, + LibertyComplexAttr(std::string name, LibertyAttrValueSeq *values, int line); virtual ~LibertyComplexAttr(); - bool isSimple() const override { return false; } - bool isComplex() const override { return true; } + bool isComplexAttr() const override { return true; }; LibertyAttrValue *firstValue() override ; LibertyAttrValueSeq *values() const override { return values_; } @@ -211,18 +197,18 @@ public: virtual bool isString() const = 0; virtual bool isFloat() const = 0; virtual float floatValue() const = 0; - virtual const char *stringValue() const = 0; + virtual const std::string &stringValue() const = 0; }; class LibertyStringAttrValue : public LibertyAttrValue { public: - LibertyStringAttrValue(const char *value); + LibertyStringAttrValue(std::string value); virtual ~LibertyStringAttrValue() {} bool isFloat() const override { return false; } bool isString() const override { return true; } float floatValue() const override ; - const char *stringValue() const override; + const std::string &stringValue() const override { return value_; } private: std::string value_; @@ -235,8 +221,8 @@ public: virtual ~LibertyFloatAttrValue() {} bool isString() const override { return false; } bool isFloat() const override { return true; } - float floatValue() const override; - const char *stringValue() const override; + float floatValue() const override { return value_; } + const std::string &stringValue() const override; private: float value_; @@ -248,12 +234,12 @@ private: class LibertyDefine : public LibertyStmt { public: - LibertyDefine(const char *name, + LibertyDefine(std::string name, LibertyGroupType group_type, LibertyAttrType value_type, int line); virtual bool isDefine() const { return true; } - const char *name() const { return name_.c_str(); } + const std::string &name() const { return name_; } LibertyGroupType groupType() const { return group_type_; } LibertyAttrType valueType() const { return value_type_; } @@ -270,11 +256,11 @@ private: class LibertyVariable : public LibertyStmt { public: - LibertyVariable(const char *var, + LibertyVariable(std::string var, float value, int line); bool isVariable() const override { return true; } - const char *variable() const { return var_.c_str(); } + const std::string &variable() const { return var_; } float value() const { return value_; } private: diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 5a2ca07d9..f2676531a 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -59,7 +59,7 @@ using std::make_shared; using std::string; static void -scaleFloats(FloatSeq *floats, +scaleFloats(FloatSeq &floats, float scale); LibertyLibrary * @@ -703,7 +703,7 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group) // These attributes reference named groups in the library so // wait until the end of the library to resolve them. if (default_wireload_) { - Wireload *wireload = library_->findWireload(default_wireload_); + const Wireload *wireload = library_->findWireload(default_wireload_); if (wireload) library_->setDefaultWireload(wireload); else @@ -713,7 +713,7 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group) } if (default_wireload_selection_) { - WireloadSelection *selection = + const WireloadSelection *selection = library_->findWireloadSelection(default_wireload_selection_); if (selection) library_->setDefaultWireloadSelection(selection); @@ -818,17 +818,17 @@ LibertyReader::parseUnits(LibertyAttr *attr, float &scale_var, Unit *unit) { - string units = getAttrString(attr); - if (!units.empty()) { + const char *units = getAttrString(attr); + if (units) { // Unit format is . // Find the multiplier digits. - string units = getAttrString(attr); - size_t mult_end = units.find_first_not_of("0123456789"); + string units1 = units; + size_t mult_end = units1.find_first_not_of("0123456789"); float mult = 1.0F; string scale_suffix; - if (mult_end != units.npos) { - string unit_mult = units.substr(0, mult_end); - scale_suffix = units.substr(mult_end); + if (mult_end != units1.npos) { + string unit_mult = units1.substr(0, mult_end); + scale_suffix = units1.substr(mult_end); if (unit_mult == "1") mult = 1.0F; else if (unit_mult == "10") @@ -875,7 +875,7 @@ void LibertyReader::visitCapacitiveLoadUnit(LibertyAttr *attr) { if (library_) { - if (attr->isComplex()) { + if (attr->isComplexAttr()) { LibertyAttrValueSeq *values = attr->values(); if (values->size() == 2) { LibertyAttrValue *value = (*values)[0]; @@ -898,10 +898,10 @@ LibertyReader::visitCapacitiveLoadUnit(LibertyAttr *attr) if (valid) { value = (*values)[1]; if (value->isString()) { - const char *suffix = value->stringValue(); - if (stringEqual(suffix, "ff")) + const std::string suffix = value->stringValue(); + if (stringEqual(suffix.c_str(), "ff")) cap_scale_ = scale * 1E-15F; - else if (stringEqual(suffix, "pf")) + else if (stringEqual(suffix.c_str(), "pf")) cap_scale_ = scale * 1E-12F; else libWarn(1154, attr, "capacitive_load_units are not ff or pf."); @@ -979,12 +979,12 @@ void LibertyReader::visitVoltageMap(LibertyAttr *attr) { if (library_) { - if (attr->isComplex()) { + if (attr->isComplexAttr()) { LibertyAttrValueSeq *values = attr->values(); if (values->size() >= 1) { LibertyAttrValue *value = (*values)[0]; if (value->isString()) { - const char *supply_name = value->stringValue(); + const std::string &supply_name = value->stringValue(); if (values->size() == 2) { value = (*values)[1]; bool valid = false; @@ -1004,7 +1004,7 @@ LibertyReader::visitVoltageMap(LibertyAttr *attr) } if (valid) - library_->addSupplyVoltage(supply_name, voltage); + library_->addSupplyVoltage(supply_name.c_str(), voltage); else libWarn(1166, attr, "voltage_map voltage is not a float."); } @@ -1418,8 +1418,7 @@ LibertyReader::beginTableTemplate(LibertyGroup *group, if (library_) { const char *name = group->firstName(); if (name) { - tbl_template_ = new TableTemplate(name); - library_->addTableTemplate(tbl_template_, type); + tbl_template_ = library_->makeTableTemplate(name, type); } else libWarn(1175, group, "table template missing name."); @@ -1431,7 +1430,9 @@ LibertyReader::beginTableTemplate(LibertyGroup *group, void LibertyReader::clearAxisValues() { - axis_values_[0] = axis_values_[1] = axis_values_[2] = nullptr; + axis_values_[0].clear(); + axis_values_[1].clear(); + axis_values_[2].clear(); } void @@ -1457,30 +1458,31 @@ LibertyReader::makeAxis(int index, LibertyGroup *group) { TableAxisVariable axis_var = axis_var_[index]; - FloatSeq *axis_values = axis_values_[index]; if (axis_var != TableAxisVariable::unknown) { - if (axis_values) { + FloatSeq values; + if (!axis_values_[index].empty()) { const Units *units = library_->units(); float scale = tableVariableUnit(axis_var, units)->scale(); - scaleFloats(axis_values, scale); + values = std::move(axis_values_[index]); + scaleFloats(values, scale); } - return make_shared(axis_var, axis_values); + return make_shared(axis_var, std::move(values)); } - else if (axis_values) { + else if (!axis_values_[index].empty()) { libWarn(1176, group, "missing variable_%d attribute.", index + 1); - delete axis_values; - axis_values_[index] = nullptr; + axis_values_[index].clear(); } // No warning for missing index_xx attributes because they are not required. return nullptr; } static void -scaleFloats(FloatSeq *floats, float scale) +scaleFloats(FloatSeq &floats, + float scale) { - size_t count = floats->size(); + size_t count = floats.size(); for (size_t i = 0; i < count; i++) - (*floats)[i] *= scale; + floats[i] *= scale; } void @@ -1538,22 +1540,21 @@ LibertyReader::visitIndex(int index, LibertyAttr *attr) { if (tbl_template_ - // Ignore index_xx in ecsm_waveform groups. + // Ignore index_* in ecsm_waveform groups. && !in_ecsm_waveform_) { - FloatSeq *axis_values = readFloatSeq(attr, 1.0F); - if (axis_values) { - if (axis_values->empty()) - libWarn(1177, attr, "missing table index values."); - else { - float prev = (*axis_values)[0]; - for (size_t i = 1; i < axis_values->size(); i++) { - float value = (*axis_values)[i]; - if (value <= prev) - libWarn(1178, attr, "non-increasing table index values."); - prev = value; - } + FloatSeq axis_values = readFloatSeq(attr, 1.0F); + if (axis_values.empty()) + libWarn(1177, 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(1178, attr, "non-increasing table index values."); + prev = value; } - axis_values_[index] = axis_values; + axis_values_[index] = std::move(axis_values); } } } @@ -1573,11 +1574,10 @@ LibertyReader::endType(LibertyGroup *group) const char *name = group->firstName(); if (name) { if (type_bit_from_exists_ && type_bit_to_exists_) { - BusDcl *bus_dcl = new BusDcl(name, type_bit_from_, type_bit_to_); if (cell_) - cell_->addBusDcl(bus_dcl); + cell_->makeBusDcl(name, type_bit_from_, type_bit_to_); else if (library_) - library_->addBusDcl(bus_dcl); + library_->makeBusDcl(name, type_bit_from_, type_bit_to_); } else { if (!type_bit_from_exists_) @@ -1610,8 +1610,7 @@ LibertyReader::beginScalingFactors(LibertyGroup *group) const char *name = group->firstName(); if (name) { save_scale_factors_ = scale_factors_; - scale_factors_ = new ScaleFactors(name); - library_->addScaleFactors(scale_factors_); + scale_factors_ = library_->makeScaleFactors(name); } else libWarn(1182, group, "scaling_factors do not have a name."); @@ -1631,7 +1630,7 @@ LibertyReader::visitScaleFactorSuffix(LibertyAttr *attr) ScaleFactorType type = ScaleFactorType::unknown; const RiseFall *rf = nullptr; // Parse the attribute name. - TokenParser parser(attr->name(), "_"); + TokenParser parser(attr->name().c_str(), "_"); if (parser.hasNext()) parser.next(); if (parser.hasNext()) { @@ -1669,7 +1668,7 @@ LibertyReader::visitScaleFactorPrefix(LibertyAttr *attr) ScaleFactorType type = ScaleFactorType::unknown; const RiseFall *rf = nullptr; // Parse the attribute name. - TokenParser parser(attr->name(), "_"); + TokenParser parser(attr->name().c_str(), "_"); if (parser.hasNext()) parser.next(); if (parser.hasNext()) { @@ -1710,7 +1709,7 @@ LibertyReader::visitScaleFactorHiLow(LibertyAttr *attr) const char *type_name = nullptr; const char *tr_name = nullptr; // Parse the attribute name. - TokenParser parser(attr->name(), "_"); + TokenParser parser(attr->name().c_str(), "_"); if (parser.hasNext()) parser.next(); if (parser.hasNext()) { @@ -1749,7 +1748,7 @@ LibertyReader::visitScaleFactor(LibertyAttr *attr) const char *pvt_name = nullptr; const char *type_name = nullptr; // Parse the attribute name. - TokenParser parser(attr->name(), " "); + TokenParser parser(attr->name().c_str(), " "); if (parser.hasNext()) parser.next(); if (parser.hasNext()) { @@ -1778,10 +1777,8 @@ LibertyReader::beginOpCond(LibertyGroup *group) { if (library_) { const char *name = group->firstName(); - if (name) { - op_cond_ = new OperatingConditions(name); - library_->addOperatingConditions(op_cond_); - } + if (name) + op_cond_ = library_->makeOperatingConditions(name); else libWarn(1183, group, "operating_conditions missing name."); } @@ -1848,10 +1845,8 @@ LibertyReader::beginWireload(LibertyGroup *group) { if (library_) { const char *name = group->firstName(); - if (name) { - wireload_ = new Wireload(name, library_); - library_->addWireload(wireload_); - } + if (name) + wireload_ = library_->makeWireload(name); } else libWarn(1184, group, "wire_load missing name."); @@ -1906,10 +1901,8 @@ LibertyReader::beginWireloadSelection(LibertyGroup *group) { if (library_) { const char *name = group->firstName(); - if (name) { - wireload_selection_ = new WireloadSelection(name); - library_->addWireloadSelection(wireload_selection_); - } + if (name) + wireload_selection_ = library_->makeWireloadSelection(name); } else libWarn(1186, group, "wire_load_selection missing name."); @@ -1925,7 +1918,7 @@ void LibertyReader::visitWireloadFromArea(LibertyAttr *attr) { if (wireload_selection_) { - if (attr->isComplex()) { + if (attr->isComplexAttr()) { LibertyAttrValueSeq *values = attr->values(); if (values->size() == 3) { LibertyAttrValue *value = (*values)[0]; @@ -1937,14 +1930,14 @@ LibertyReader::visitWireloadFromArea(LibertyAttr *attr) value = (*values)[2]; if (value->isString()) { - const char *wireload_name = value->stringValue(); + const std::string &wireload_name = value->stringValue(); const Wireload *wireload = - library_->findWireload(wireload_name); + library_->findWireload(wireload_name.c_str()); if (wireload) wireload_selection_->addWireloadFromArea(min_area, max_area, wireload); else - libWarn(1187, attr, "wireload %s not found.", wireload_name); + libWarn(1187, attr, "wireload %s not found.", wireload_name.c_str()); } else libWarn(1188, attr, @@ -2077,7 +2070,7 @@ LibertyReader::makeScalarCheckModel(float value, ScaleFactorType scale_factor_type, const RiseFall *rf) { - TablePtr table = make_shared(value); + TablePtr table = make_shared

(value); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *table_model = new TableModel(table, tbl_template, @@ -2103,7 +2096,6 @@ LibertyReader::makeInternalPowers(PortGroup *port_group) for (InternalPowerGroup *power_group : port_group->internalPowerGroups()) { for (LibertyPort *port : *port_group->ports()) makeInternalPowers(port, power_group); - cell_->addInternalPowerAttrs(power_group); } } @@ -2134,7 +2126,7 @@ LibertyReader::makeCellSequential(SequentialGroup *seq) clk_expr = parseFunc(clk, clk_attr, line); if (clk_expr && clk_expr->checkSize(size)) { libWarn(1196, line, "%s %s bus width mismatch.", type, clk_attr); - clk_expr->deleteSubexprs(); + delete clk_expr; clk_expr = nullptr; } } @@ -2145,7 +2137,7 @@ LibertyReader::makeCellSequential(SequentialGroup *seq) data_expr = parseFunc(data, data_attr, line); if (data_expr && data_expr->checkSize(size)) { libWarn(1197, line, "%s %s bus width mismatch.", type, data_attr); - data_expr->deleteSubexprs(); + delete data_expr; data_expr = nullptr; } } @@ -2155,7 +2147,7 @@ LibertyReader::makeCellSequential(SequentialGroup *seq) clr_expr = parseFunc(clr, "clear", line); if (clr_expr && clr_expr->checkSize(size)) { libWarn(1198, line, "%s %s bus width mismatch.", type, "clear"); - clr_expr->deleteSubexprs(); + delete clr_expr; clr_expr = nullptr; } } @@ -2165,7 +2157,7 @@ LibertyReader::makeCellSequential(SequentialGroup *seq) preset_expr = parseFunc(preset, "preset", line); if (preset_expr && preset_expr->checkSize(size)) { libWarn(1199, line, "%s %s bus width mismatch.", type, "preset"); - preset_expr->deleteSubexprs(); + delete preset_expr; preset_expr = nullptr; } } @@ -2177,14 +2169,10 @@ LibertyReader::makeCellSequential(SequentialGroup *seq) checkLatchEnableSense(clk_expr, line); // The expressions used in the sequentials are copied by bitSubExpr. - if (clk_expr) - clk_expr->deleteSubexprs(); - if (data_expr) - data_expr->deleteSubexprs(); - if (clr_expr) - clr_expr->deleteSubexprs(); - if (preset_expr) - preset_expr->deleteSubexprs(); + delete clk_expr; + delete data_expr; + delete clr_expr; + delete preset_expr; } void @@ -2245,6 +2233,15 @@ void LibertyReader::makeLeakagePowers() { for (LeakagePowerGroup *power_group : leakage_powers_) { +<<<<<<< +======= + LibertyPort *related_pg_pin = + cell_->findLibertyPort(power_group->relatedPgPin().c_str()); + cell_->makeLeakagePower(related_pg_pin, power_group->when(), power_group->power()); + delete power_group; + } + leakage_powers_.clear(); +>>>>>>> LibertyPort *related_pg_pin = cell_->findLibertyPort(power_group->relatedPgPin().c_str()); LeakagePower *leakage = new LeakagePower(cell_, related_pg_pin, power_group->when(), @@ -2274,15 +2271,8 @@ LibertyReader::parseCellFuncs() { for (LibertyFunc *func : cell_funcs_) { FuncExpr *expr = parseFunc(func->expr(), func->attrName(), func->line()); - if (func->invert() && expr) { - if (expr->op() == FuncExpr::Op::not_) { - FuncExpr *inv = expr; - expr = expr->left(); - delete inv; - } - else - expr = FuncExpr::makeNot(expr); - } + if (func->invert() && expr) + expr = expr->invert(); if (expr) func->setFunc()(expr); delete func; @@ -2441,7 +2431,7 @@ void TimingGroup::makeTableModels(LibertyCell *cell, LibertyReader *reader) { - for (auto rf : RiseFall::range()) { + for (const RiseFall *rf : RiseFall::range()) { int rf_index = rf->index(); TableModel *delay = cell_[rf_index]; TableModel *transition = transition_[rf_index]; @@ -2475,6 +2465,9 @@ TimingGroup::makeTableModels(LibertyCell *cell, else if (constraint) attrs_->setModel(rf, new CheckTableModel(cell, constraint, constraint_sigma_[rf_index])); + cell_[rf_index] = nullptr; + transition_[rf_index] = nullptr; + constraint_[rf_index] = nullptr; } } @@ -2664,14 +2657,14 @@ LibertyReader::endReceiverCapacitanceRiseFall(LibertyGroup *group) { if (table_) { if (ReceiverModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); if (receiver_model_ == nullptr) { receiver_model_ = make_shared(); if (timing_) timing_->setReceiverModel(receiver_model_); } - receiver_model_->setCapacitanceModel(table_model, index_, rf_); + receiver_model_->setCapacitanceModel(TableModel(table_, tbl_template_, + scale_factor_type_, rf_), + index_, rf_); } else libWarn(1219, group, "unsupported model axis."); @@ -2711,28 +2704,31 @@ LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) { if (timing_) { std::set slew_set, cap_set; - FloatSeq *slew_values = new FloatSeq; - FloatSeq *cap_values = new FloatSeq; + FloatSeq slew_values; + FloatSeq cap_values; for (OutputWaveform *waveform : output_currents_) { float slew = waveform->slew(); if (!slew_set.contains(slew)) { slew_set.insert(slew); - slew_values->push_back(slew); + slew_values.push_back(slew); } float cap = waveform->cap(); if (!cap_set.contains(cap)) { cap_set.insert(cap); - cap_values->push_back(cap); + cap_values.push_back(cap); } } sort(slew_values, std::less()); sort(cap_values, std::less()); - TableAxisPtr slew_axis = make_shared(TableAxisVariable::input_net_transition, - slew_values); - TableAxisPtr cap_axis = make_shared(TableAxisVariable::total_output_net_capacitance, - cap_values); - FloatSeq *ref_times = new FloatSeq(slew_values->size()); - Table1Seq current_waveforms(slew_axis->size() * cap_axis->size()); + size_t slew_size = slew_values.size(); + size_t cap_size = cap_values.size(); + TableAxisPtr slew_axis=make_shared(TableAxisVariable::input_net_transition, + std::move(slew_values)); + TableAxisPtr cap_axis = + make_shared(TableAxisVariable::total_output_net_capacitance, + std::move(cap_values)); + FloatSeq ref_times(slew_size); + Table1Seq current_waveforms(slew_size * cap_size); for (OutputWaveform *waveform : output_currents_) { size_t slew_index, cap_index; bool slew_exists, cap_exists; @@ -2741,17 +2737,17 @@ LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) if (slew_exists && cap_exists) { size_t index = slew_index * cap_axis->size() + cap_index; current_waveforms[index] = waveform->stealCurrents(); - (*ref_times)[slew_index] = waveform->referenceTime(); + ref_times[slew_index] = waveform->referenceTime(); } else libWarn(1221, group, "output current waveform %.2e %.2e not found.", waveform->slew(), waveform->cap()); } - Table1 *ref_time_tbl = new Table1(ref_times, slew_axis); + Table ref_time_tbl(std::move(ref_times), slew_axis); OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, rf_, current_waveforms, - ref_time_tbl); + std::move(ref_time_tbl)); timing_->setOutputWaveforms(rf_, output_current); deleteContents(output_currents_); } @@ -2781,28 +2777,26 @@ void LibertyReader::endVector(LibertyGroup *group) { if (timing_ && tbl_template_) { - FloatSeq *slew_values = axis_values_[0]; - FloatSeq *cap_values = axis_values_[1]; + TableAxisPtr slew_axis, cap_axis; // Canonicalize axis order. if (tbl_template_->axis1()->variable() == TableAxisVariable::input_net_transition) { - slew_values = axis_values_[0]; - cap_values = axis_values_[1]; + slew_axis = axis_[0]; + cap_axis = axis_[1]; } else { - slew_values = axis_values_[1]; - cap_values = axis_values_[0]; - } - - if (slew_values->size() == 1 && cap_values->size() == 1) { - // Convert 1x1xN Table3 to Table1. - float slew = (*slew_values)[0]; - float cap = (*cap_values)[0]; - Table3 *table3 = dynamic_cast(table_.get()); - FloatTable *values3 = table3->values3(); - // Steal the values. - FloatSeq *values = (*values3)[0]; - (*values3)[0] = nullptr; - Table1 *table1 = new Table1(values, axis_[2]); + slew_axis = axis_[1]; + cap_axis = axis_[0]; + } + + if (slew_axis->size() == 1 && cap_axis->size() == 1) { + // Convert 1x1xN Table (order 3) to 1D Table. + float slew = slew_axis->axisValue(0); + float cap = cap_axis->axisValue(0); + Table *table_ptr = table_.get(); + FloatTable *values3 = table_ptr->values3(); + FloatSeq row = std::move((*values3)[0]); + values3->erase(values3->begin()); + Table *table1 = new Table(std::move(row), axis_[2]); OutputWaveform *waveform = new OutputWaveform(slew, cap, table1, reference_time_); output_currents_.push_back(waveform); } @@ -2837,9 +2831,7 @@ LibertyReader::endNormalizedDriverWaveform(LibertyGroup *group) if (table_->axis1()->variable() == TableAxisVariable::input_net_transition) { if (table_->axis2()->variable() == TableAxisVariable::normalized_voltage) { // Null driver_waveform_name_ means it is the default unnamed waveform. - DriverWaveform *driver_waveform = new DriverWaveform(driver_waveform_name_, - table_); - library_->addDriverWaveform(driver_waveform); + library_->makeDriverWaveform(driver_waveform_name_, table_); } else @@ -2896,15 +2888,18 @@ LibertyReader::makeInternalPowers(LibertyPort *port, } } else { + const std::string &related_pg_pin = power_group->relatedPgPin(); if (port->hasMembers()) { LibertyPortMemberIterator bit_iter(port); while (bit_iter.hasNext()) { LibertyPort *port_bit = bit_iter.next(); - builder_.makeInternalPower(cell_, port_bit, nullptr, power_group); + cell_->makeInternalPower(port_bit, nullptr, related_pg_pin, + power_group->when(), power_group->models()); } } else - builder_.makeInternalPower(cell_, port, nullptr, power_group); + cell_->makeInternalPower(port, nullptr, related_pg_pin, power_group->when(), + power_group->models()); } } @@ -2914,18 +2909,21 @@ LibertyReader::makeInternalPowers(LibertyPort *port, PortNameBitIterator &related_port_iter, InternalPowerGroup *power_group) { + const std::string &related_pg_pin = power_group->relatedPgPin(); + const auto &when = power_group->when(); + InternalPowerModels &models = power_group->models(); if (related_port_iter.size() == 1 && !port->hasMembers()) { // one -> one if (related_port_iter.hasNext()) { LibertyPort *related_port = related_port_iter.next(); - builder_.makeInternalPower(cell_, port, related_port, power_group); + cell_->makeInternalPower(port, related_port, related_pg_pin, when, models); } } else if (related_port_iter.size() > 1 && !port->hasMembers()) { // bus -> one while (related_port_iter.hasNext()) { LibertyPort *related_port = related_port_iter.next(); - builder_.makeInternalPower(cell_, port, related_port, power_group); + cell_->makeInternalPower(port, related_port, related_pg_pin, when, models); } } else if (related_port_iter.size() == 1 && port->hasMembers()) { @@ -2935,7 +2933,7 @@ LibertyReader::makeInternalPowers(LibertyPort *port, LibertyPortMemberIterator bit_iter(port); while (bit_iter.hasNext()) { LibertyPort *port_bit = bit_iter.next(); - builder_.makeInternalPower(cell_, port_bit, related_port, power_group); + cell_->makeInternalPower(port_bit, related_port, related_pg_pin, when, models); } } } @@ -2947,7 +2945,8 @@ LibertyReader::makeInternalPowers(LibertyPort *port, while (related_port_iter.hasNext() && to_iter.hasNext()) { LibertyPort *related_port_bit = related_port_iter.next(); LibertyPort *port_bit = to_iter.next(); - builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); + cell_->makeInternalPower(port_bit, related_port_bit, related_pg_pin, + when, models); } } else @@ -2962,7 +2961,8 @@ LibertyReader::makeInternalPowers(LibertyPort *port, LibertyPortMemberIterator to_iter(port); while (to_iter.hasNext()) { LibertyPort *port_bit = to_iter.next(); - builder_.makeInternalPower(cell_, port_bit, related_port_bit, power_group); + cell_->makeInternalPower(port_bit, related_port_bit, related_pg_pin, + when, models); } } } @@ -3180,9 +3180,9 @@ LibertyReader::beginPin(LibertyGroup *group) ports_ = new LibertyPortSeq; for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { - const char *port_name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " port %s", port_name); - PortNameBitIterator port_iter(cell_, port_name, this, group->line()); + const std::string &port_name = param->stringValue(); + debugPrint(debug_, "liberty", 1, " port %s", port_name.c_str()); + PortNameBitIterator port_iter(cell_, port_name.c_str(), this, group->line()); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); ports_->push_back(port); @@ -3198,7 +3198,7 @@ LibertyReader::beginPin(LibertyGroup *group) ports_ = new LibertyPortSeq; for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { - const char *name = param->stringValue(); + const char *name = param->stringValue().c_str(); debugPrint(debug_, "liberty", 1, " port %s", name); LibertyPort *port = findPort(name); if (port == nullptr) @@ -3214,7 +3214,7 @@ LibertyReader::beginPin(LibertyGroup *group) // Multiple port names can share group def. for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { - const char *name = param->stringValue(); + const char *name = param->stringValue().c_str(); debugPrint(debug_, "liberty", 1, " port %s", name); LibertyPort *port = makePort(cell_, name); ports_->push_back(port); @@ -3323,9 +3323,8 @@ LibertyReader::beginBusOrBundle(LibertyGroup *group) // Multiple port names can share group def. for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { - const char *name = param->stringValue(); - if (name) - bus_names_.push_back(stringCopy(name)); + const string &name = param->stringValue(); + bus_names_.push_back(stringCopy(name.c_str())); } } ports_ = new LibertyPortSeq; @@ -3394,13 +3393,13 @@ void LibertyReader::visitMembers(LibertyAttr *attr) { if (cell_) { - if (attr->isComplex()) { + if (attr->isComplexAttr()) { for (const char *name : bus_names_) { debugPrint(debug_, "liberty", 1, " bundle %s", name); ConcretePortSeq *members = new ConcretePortSeq; for (LibertyAttrValue *value : *attr->values()) { if (value->isString()) { - const char *port_name = value->stringValue(); + const char *port_name = value->stringValue().c_str(); LibertyPort *port = findPort(port_name); if (port == nullptr) port = makePort(cell_, port_name); @@ -4054,7 +4053,7 @@ LibertyReader::seqPortNames(LibertyGroup *group, else { // in_port (ignored), out_port, out_port_inv out_name = group->secondName(); - out_inv_name = third_value->stringValue(); + out_inv_name = third_value->stringValue().c_str(); } } } @@ -4422,7 +4421,7 @@ void LibertyReader::visitMode(LibertyAttr *attr) { if (timing_) { - if (attr->isComplex()) { + if (attr->isComplexAttr()) { LibertyAttrValueSeq *values = attr->values(); if (values->size() == 2) { LibertyAttrValue *value = (*values)[0]; @@ -4624,7 +4623,7 @@ LibertyReader::beginTimingTableModel(LibertyGroup *group, beginTableModel(group, TableTemplateType::delay, rf, time_scale_, scale_factor_type); else - libWarn(1255, group, "%s group not in timing group.", group->firstName()); + libWarn(1255, group, "%s group not in timing group.", group->type().c_str()); } void @@ -4709,7 +4708,7 @@ void LibertyReader::makeTable(LibertyAttr *attr, float scale) { - if (attr->isComplex()) { + if (attr->isComplexAttr()) { makeTableAxis(0, attr); makeTableAxis(1, attr); makeTableAxis(2, attr); @@ -4717,80 +4716,67 @@ LibertyReader::makeTable(LibertyAttr *attr, // 3D table // Column index1*size(index2) + index2 // Row index3 - FloatTable *table = makeFloatTable(attr, - axis_[0]->size()*axis_[1]->size(), - axis_[2]->size(), scale); - table_ = make_shared(table, axis_[0], axis_[1], axis_[2]); + table_ = make_shared
(makeFloatTable(attr, + axis_[0]->size() * axis_[1]->size(), + axis_[2]->size(), scale), + axis_[0], axis_[1], axis_[2]); } else if (axis_[0] && axis_[1]) { // 2D table // Row variable1/axis[0] // Column variable2/axis[1] - FloatTable *table = makeFloatTable(attr, axis_[0]->size(), - axis_[1]->size(), scale); - table_ = make_shared(table, axis_[0], axis_[1]); + table_ = make_shared
(makeFloatTable(attr, axis_[0]->size(), + axis_[1]->size(), scale), + axis_[0], axis_[1]); } else if (axis_[0]) { // 1D table - FloatTable *table = makeFloatTable(attr, 1, axis_[0]->size(), scale); - FloatSeq *values = (*table)[0]; - delete table; - table_ = make_shared(values, axis_[0]); + FloatTable table = makeFloatTable(attr, 1, axis_[0]->size(), scale); + table_ = make_shared
(std::move(table[0]), axis_[0]); } else if (axis_[0] == nullptr && axis_[1] == nullptr && axis_[2] == nullptr) { // scalar - FloatTable *table = makeFloatTable(attr, 1, 1, scale); - float value = (*(*table)[0])[0]; - delete (*table)[0]; - delete table; - table_ = make_shared(value); + FloatTable table = makeFloatTable(attr, 1, 1, scale); + float value = table[0][0]; + table_ = make_shared
(value); } } else - libWarn(1257, attr, "%s is missing values.", attr->name()); + libWarn(1257, attr, "%s is missing values.", attr->name().c_str()); } -FloatTable * +FloatTable LibertyReader::makeFloatTable(LibertyAttr *attr, size_t rows, size_t cols, float scale) { - FloatTable *table = new FloatTable; - table->reserve(rows); + FloatTable table; + table.reserve(rows); for (LibertyAttrValue *value : *attr->values()) { - FloatSeq *row = new FloatSeq; - row->reserve(cols); - table->push_back(row); - if (value->isString()) { - const char *values_list = value->stringValue(); - parseStringFloatList(values_list, scale, row, attr); - } + FloatSeq row; + if (value->isString()) + row = parseStringFloatList(value->stringValue(), scale, attr); else if (value->isFloat()) - // Scalar value. - row->push_back(value->floatValue() * scale); + row.push_back(value->floatValue() * scale); else - libWarn(1258, attr, "%s is not a list of floats.", attr->name()); - if (row->size() != cols) { + libWarn(1258, attr, "%s is not a list of floats.", attr->name().c_str()); + if (row.size() != cols) { libWarn(1259, attr, "table row has %zu columns but axis has %zu.", - row->size(), + row.size(), cols); - // Fill out row columns with zeros. - for (size_t c = row->size(); c < cols; c++) - row->push_back(0.0); + for (size_t c = row.size(); c < cols; c++) + row.push_back(0.0); } + table.push_back(std::move(row)); } - if (table->size() != rows) { + if (table.size() != rows) { libWarn(1260, attr, "table has %zu rows but axis has %zu.", - table->size(), + table.size(), rows); - // Fill with zero'd rows. - for (size_t r = table->size(); r < rows; r++) { - FloatSeq *row = new FloatSeq; - table->push_back(row); - // Fill out row with zeros. - for (size_t c = row->size(); c < cols; c++) - row->push_back(0.0); + for (size_t r = table.size(); r < rows; r++) { + FloatSeq row(cols, 0.0); + table.push_back(std::move(row)); } } return table; @@ -4800,18 +4786,18 @@ void LibertyReader::makeTableAxis(int index, LibertyAttr *attr) { - if (axis_values_[index]) { + if (axis_[index] && !axis_values_[index].empty()) { TableAxisVariable var = axis_[index]->variable(); - FloatSeq *values = axis_values_[index]; const Units *units = library_->units(); float scale = tableVariableUnit(var, units)->scale(); + FloatSeq values = std::move(axis_values_[index]); scaleFloats(values, scale); - axis_[index] = make_shared(var, values); + axis_[index] = make_shared(var, std::move(values)); } - else if (axis_[index] && axis_[index]->values() == nullptr) { + else if (axis_[index] && axis_[index]->values().empty()) { libWarn(1344, attr, "Table axis and template missing values."); axis_[index] = nullptr; - axis_values_[index] = nullptr; + axis_values_[index].clear(); } } @@ -4825,9 +4811,9 @@ LibertyReader::beginLut(LibertyGroup *group) if (cell_) { for (LibertyAttrValue *param : *group->params()) { if (param->isString()) { - const char *names = param->stringValue(); + const std::string &names = param->stringValue(); // Parse space separated list of related port names. - TokenParser parser(names, " "); + TokenParser parser(names.c_str(), " "); while (parser.hasNext()) { char *name = parser.next(); if (name[0] != '\0') { @@ -4857,7 +4843,7 @@ LibertyReader::beginTestCell(LibertyGroup *group) else { string name = cell_->name(); name += "/test_cell"; - test_cell_ = new TestCell(cell_->libertyLibrary(), name.c_str(), + test_cell_ = new TestCell(cell_->libertyLibrary(), std::move(name), cell_->filename()); cell_->setTestCell(test_cell_); @@ -4956,7 +4942,9 @@ LibertyReader::visitWhen(LibertyAttr *attr) if (func) { InternalPowerGroup *internal_pwr = internal_power_; makeLibertyFunc(func, - [internal_pwr] (FuncExpr *expr) { internal_pwr->setWhen(expr);}, + [internal_pwr] (FuncExpr *expr) { + internal_pwr->setWhen(std::shared_ptr(expr)); + }, false, "when", attr); } } @@ -4992,15 +4980,15 @@ LibertyReader::visitSdfCond(LibertyAttr *attr) const char * LibertyReader::getAttrString(LibertyAttr *attr) { - if (attr->isSimple()) { + if (attr->isSimpleAttr()) { LibertyAttrValue *value = attr->firstValue(); if (value->isString()) - return value->stringValue(); + return value->stringValue().c_str(); else - libWarn(1266, attr, "%s attribute is not a string.", attr->name()); + libWarn(1266, attr, "%s attribute is not a string.", attr->name().c_str()); } else - libWarn(1267, attr, "%s is not a simple attribute.", attr->name()); + libWarn(1267, attr, "%s is not a simple attribute.", attr->name().c_str()); return nullptr; } @@ -5012,7 +5000,7 @@ LibertyReader::getAttrInt(LibertyAttr *attr, { value = 0; exists = false; - if (attr->isSimple()) { + if (attr->isSimpleAttr()) { LibertyAttrValue *attr_value = attr->firstValue(); if (attr_value->isFloat()) { float float_val = attr_value->floatValue(); @@ -5020,10 +5008,10 @@ LibertyReader::getAttrInt(LibertyAttr *attr, exists = true; } else - libWarn(1268, attr, "%s attribute is not an integer.",attr->name()); + libWarn(1268, attr, "%s attribute is not an integer.",attr->name().c_str()); } else - libWarn(1269, attr, "%s is not a simple attribute.", attr->name()); + libWarn(1269, attr, "%s is not a simple attribute.", attr->name().c_str()); } void @@ -5033,10 +5021,10 @@ LibertyReader::getAttrFloat(LibertyAttr *attr, bool &valid) { valid = false; - if (attr->isSimple()) + if (attr->isSimpleAttr()) getAttrFloat(attr, attr->firstValue(), value, valid); else - libWarn(1270, attr, "%s is not a simple attribute.", attr->name()); + libWarn(1270, attr, "%s is not a simple attribute.", attr->name().c_str()); } void @@ -5051,20 +5039,20 @@ LibertyReader::getAttrFloat(LibertyAttr *attr, value = attr_value->floatValue(); } else if (attr_value->isString()) { - const char *string = attr_value->stringValue(); + const std::string &str = attr_value->stringValue(); // See if attribute string is a variable. - variableValue(string, value, valid); + variableValue(str.c_str(), value, valid); if (!valid) { // For some reason area attributes for pads are quoted floats. // Check that the string is a valid double. char *end; - value = strtof(string, &end); + value = strtof(str.c_str(), &end); if ((*end && !isspace(*end)) // strtof support INF as a valid float. - || stringEqual(string, "inf")) + || str == "inf") libWarn(1271, attr, "%s value %s is not a float.", - attr->name(), - string); + attr->name().c_str(), + str.c_str()); valid = true; } } @@ -5080,85 +5068,94 @@ LibertyReader::getAttrFloat2(LibertyAttr *attr, bool &exists) { exists = false; - if (attr->isComplex()) { + if (attr->isComplexAttr()) { LibertyAttrValueSeq *values = attr->values(); if (values->size() == 2) { LibertyAttrValue *value = (*values)[0]; getAttrFloat(attr, value, value1, exists); if (!exists) - libWarn(1272, attr, "%s is not a float.", attr->name()); + libWarn(1272, attr, "%s is not a float.", attr->name().c_str()); value = (*values)[1]; getAttrFloat(attr, value, value2, exists); if (!exists) - libWarn(1273, attr, "%s is not a float.", attr->name()); + libWarn(1273, attr, "%s is not a float.", attr->name().c_str()); } else - libWarn(1274, attr, "%s requires 2 valules.", attr->name()); + libWarn(1274, attr, "%s requires 2 valules.", attr->name().c_str()); } else - libWarn(1345, attr, "%s requires 2 valules.", attr->name()); + libWarn(1345, attr, "%s requires 2 valules.", attr->name().c_str()); } // Parse string of comma separated floats. // Note that some brain damaged vendors (that used to "Think") are not // consistent about including the delimiters. -void -LibertyReader::parseStringFloatList(const char *float_list, +FloatSeq +LibertyReader::parseStringFloatList(const std::string &float_list, float scale, - FloatSeq *values, LibertyAttr *attr) { - const char *delimiters = ", "; - TokenParser parser(float_list, delimiters); - while (parser.hasNext()) { - char *token = parser.next(); + FloatSeq values; + const char *token = float_list.c_str(); + while (*token != '\0') { // Some (brain dead) libraries enclose floats in brackets. if (*token == '{') token++; char *end; float value = strtof(token, &end) * scale; if (end == token - || (end && !(*end == '\0' - || isspace(*end) - || strchr(delimiters, *end) != nullptr - || *end == '}'))) - libWarn(1275, attr, "%s is not a float.", token); - values->push_back(value); + || !(*end == '\0' + || isspace(*end) + || *end == ',' + || *end == '}')) { + std::string token_end = token; + if (end != token) { + token_end.clear(); + for (const char *t = token; t <= end; t++) + token_end += *t; + } + libWarn(1275, attr, "%s is not a float.", token_end.c_str()); + token += token_end.size(); + } + else { + values.push_back(value); + token = end; + } + while (*token == ',' || *token == ' ' || *token == '}') + token++; } + return values; } -FloatSeq * +FloatSeq LibertyReader::readFloatSeq(LibertyAttr *attr, float scale) { - FloatSeq *values = nullptr; - if (attr->isComplex()) { + FloatSeq values; + if (attr->isComplexAttr()) { LibertyAttrValueSeq *attr_values = attr->values(); if (attr_values->size() == 1) { LibertyAttrValue *value = (*attr_values)[0]; if (value->isString()) { - values = new FloatSeq; - parseStringFloatList(value->stringValue(), scale, values, attr); + values = parseStringFloatList(value->stringValue(), scale, attr); } else if (value->isFloat()) { - values = new FloatSeq; - values->push_back(value->floatValue()); + values.push_back(value->floatValue()); } else - libWarn(1276, attr, "%s is missing values.", attr->name()); + libWarn(1276, attr, "%s is missing values.", attr->name().c_str()); } else - libWarn(1277, attr, "%s has more than one string.", attr->name()); + libWarn(1277, attr, "%s has more than one string.", attr->name().c_str()); } else { LibertyAttrValue *value = attr->firstValue(); if (value->isString()) { - values = new FloatSeq; - parseStringFloatList(value->stringValue(), scale, values, attr); + values = parseStringFloatList(value->stringValue(), scale, attr); } else - libWarn(1278, attr, "%s is missing values.", attr->name()); + libWarn(1278, attr, "%s is missing values.", attr->name().c_str()); } return values; } @@ -5170,26 +5167,26 @@ LibertyReader::getAttrBool(LibertyAttr *attr, bool &exists) { exists = false; - if (attr->isSimple()) { + if (attr->isSimpleAttr()) { LibertyAttrValue *val = attr->firstValue(); if (val->isString()) { - const char *str = val->stringValue(); - if (stringEqual(str, "true")) { + const std::string &str = val->stringValue(); + if (stringEqual(str.c_str(), "true")) { value = true; exists = true; } - else if (stringEqual(str, "false")) { + else if (stringEqual(str.c_str(), "false")) { value = false; exists = true; } else - libWarn(1279, attr, "%s attribute is not boolean.", attr->name()); + libWarn(1279, attr, "%s attribute is not boolean.", attr->name().c_str()); } else - libWarn(1280, attr, "%s attribute is not boolean.", attr->name()); + libWarn(1280, attr, "%s attribute is not boolean.", attr->name().c_str()); } else - libWarn(1281, attr, "%s is not a simple attribute.", attr->name()); + libWarn(1281, attr, "%s is not a simple attribute.", attr->name().c_str()); } // Read L/H/X string attribute values as bool. @@ -5206,7 +5203,7 @@ LibertyReader::getAttrLogicValue(LibertyAttr *attr) return LogicValue::unknown; else libWarn(1282, attr, "attribute %s value %s not recognized.", - attr->name(), str); + attr->name().c_str(), str); // fall thru } return LogicValue::unknown; @@ -5248,17 +5245,11 @@ LibertyReader::visitVariable(LibertyVariable *var) { if (var_map_ == nullptr) var_map_ = new LibertyVariableMap; - const char *var_name = var->variable(); - string key; + const string &var_name = var->variable(); float value; bool exists; findKeyValue(var_map_, var_name, value, exists); - if (exists) { - // Duplicate variable name. - (*var_map_)[key] = var->value(); - } - else - (*var_map_)[var_name] = var->value(); + (*var_map_)[var_name] = var->value(); } void @@ -5337,17 +5328,11 @@ void LibertyReader::beginInternalPower(LibertyGroup *group) { if (port_group_) { - internal_power_ = makeInternalPowerGroup(group->line()); + internal_power_ = new InternalPowerGroup(group->line()); port_group_->addInternalPowerGroup(internal_power_); } } -InternalPowerGroup * -LibertyReader::makeInternalPowerGroup(int line) -{ - return new InternalPowerGroup(line); -} - void LibertyReader::endInternalPower(LibertyGroup *) { @@ -5378,7 +5363,7 @@ LibertyReader::endRiseFallPower(LibertyGroup *) if (table_) { TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); - internal_power_->setModel(rf_, new InternalPowerModel(table_model)); + internal_power_->setModel(rf_, std::make_shared(table_model)); } endTableModel(); } @@ -5390,7 +5375,7 @@ LibertyReader::endPower(LibertyGroup *) TableModel *table_model = new TableModel(table_, tbl_template_, scale_factor_type_, rf_); // Share the model for rise/fall. - InternalPowerModel *power_model = new InternalPowerModel(table_model); + auto power_model = std::make_shared(table_model); internal_power_->setModel(RiseFall::rise(), power_model); internal_power_->setModel(RiseFall::fall(), power_model); } @@ -5472,7 +5457,7 @@ LibertyReader::beginOcvDerate(LibertyGroup *group) { const char *name = group->firstName(); if (name) - ocv_derate_ = new OcvDerate(stringCopy(name)); + ocv_derate_ = library_->makeOcvDerate(name); else libWarn(1285, group, "ocv_derate missing name."); } @@ -5480,10 +5465,6 @@ LibertyReader::beginOcvDerate(LibertyGroup *group) void LibertyReader::endOcvDerate(LibertyGroup *) { - if (cell_) - library_->addOcvDerate(ocv_derate_); - else if (library_) - library_->addOcvDerate(ocv_derate_); ocv_derate_ = nullptr; } @@ -5779,6 +5760,7 @@ PortGroup::~PortGroup() { deleteContents(timings_); delete ports_; + deleteContents(internal_power_groups_); } void @@ -6074,13 +6056,29 @@ TimingGroup::setOutputWaveforms(const RiseFall *rf, //////////////////////////////////////////////////////////////// InternalPowerGroup::InternalPowerGroup(int line) : - InternalPowerAttrs(), - RelatedPortGroup(line) + RelatedPortGroup(line), + when_(), + models_{} +{ +} + +void +InternalPowerGroup::setWhen(std::shared_ptr when) { + when_ = std::move(when); } -InternalPowerGroup::~InternalPowerGroup() +void +InternalPowerGroup::setModel(const RiseFall *rf, + std::shared_ptr model) { + models_[rf->index()] = std::move(model); +} + +void +InternalPowerGroup::setRelatedPgPin(std::string related_pg_pin) +{ + related_pg_pin_ = std::move(related_pg_pin); } //////////////////////////////////////////////////////////////// @@ -6094,8 +6092,27 @@ LeakagePowerGroup::LeakagePowerGroup(int line) : void LeakagePowerGroup::setRelatedPgPin(std::string pin_name) +<<<<<<< +======= +{ + related_pg_pin_ = std::move(pin_name); +} + +void +LeakagePowerGroup::setWhen(FuncExpr *when) +{ + when_ = when; +} + +void +LeakagePowerGroup::setPower(float power) +>>>>>>> { +<<<<<<< related_pg_pin_ = std::move(pin_name); +======= + power_ = power; +>>>>>>> } void @@ -6258,7 +6275,7 @@ PortNameBitIterator::findRangeBusNameNext() OutputWaveform::OutputWaveform(float slew, float cap, - Table1 *currents, + Table *currents, float reference_time) : slew_(slew), cap_(cap), @@ -6272,10 +6289,10 @@ OutputWaveform::~OutputWaveform() delete currents_; } -Table1 * +Table * OutputWaveform::stealCurrents() { - Table1 *currents = currents_; + Table *currents = currents_; currents_ = nullptr; return currents; } diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 26c74129d..9016fc0a8 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -25,8 +25,9 @@ #pragma once #include +#include #include -#include +#include #include "StringSeq.hh" #include "MinMax.hh" @@ -61,8 +62,8 @@ class OutputWaveform; using LibraryAttrVisitor = void (LibertyReader::*)(LibertyAttr *attr); using LibraryGroupVisitor = void (LibertyReader::*)(LibertyGroup *group); -using LibraryAttrMap = std::map; -using LibraryGroupMap = std::map; +using LibraryAttrMap = std::unordered_map; +using LibraryGroupMap = std::unordered_map; using PortGroupSeq = std::vector; using SequentialGroupSeq = std::vector; using LibertyFuncSeq = std::vector; @@ -82,10 +83,12 @@ public: Network *network); virtual ~LibertyReader(); virtual LibertyLibrary *readLibertyFile(const char *filename); + LibertyLibrary *library() { return library_; } + const LibertyLibrary *library() const { return library_; } + virtual void init(const char *filename, bool infer_latches, Network *network); - LibertyLibrary *library() const { return library_; } virtual bool save(LibertyGroup *) { return false; } virtual bool save(LibertyAttr *) { return false; } virtual bool save(LibertyVariable *) { return false; } @@ -384,10 +387,10 @@ public: virtual void endTable(); virtual void makeTable(LibertyAttr *attr, float scale); - virtual FloatTable *makeFloatTable(LibertyAttr *attr, - size_t rows, - size_t cols, - float scale); + virtual FloatTable makeFloatTable(LibertyAttr *attr, + size_t rows, + size_t cols, + float scale); virtual void beginLut(LibertyGroup *group); virtual void endLut(LibertyGroup *group); @@ -408,7 +411,6 @@ public: virtual void endLeakagePower(LibertyGroup *group); virtual void beginInternalPower(LibertyGroup *group); virtual void endInternalPower(LibertyGroup *group); - virtual InternalPowerGroup *makeInternalPowerGroup(int line); virtual void beginFallPower(LibertyGroup *group); virtual void beginRisePower(LibertyGroup *group); virtual void endRiseFallPower(LibertyGroup *group); @@ -496,6 +498,8 @@ public: void endEcsmWaveform(LibertyGroup *group); LibertyPort *findPort(LibertyCell *cell, const char *port_name); + virtual void begin(LibertyGroup *group); + virtual void end(LibertyGroup *group); protected: LibertyPort *makePort(LibertyCell *cell, @@ -513,8 +517,6 @@ protected: int line); void setEnergyScale(); void defineVisitors(); - virtual void begin(LibertyGroup *group); - virtual void end(LibertyGroup *group); void defineGroupVisitor(const char *type, LibraryGroupVisitor begin_visitor, LibraryGroupVisitor end_visitor); @@ -556,10 +558,9 @@ protected: float &value1, float &value2, bool &exists); - void parseStringFloatList(const char *float_list, - float scale, - FloatSeq *values, - LibertyAttr *attr); + FloatSeq parseStringFloatList(const std::string &float_list, + float scale, + LibertyAttr *attr); LogicValue getAttrLogicValue(LibertyAttr *attr); void getAttrBool(LibertyAttr *attr, // Return values. @@ -571,8 +572,8 @@ protected: LibertyAttr *attr); TableAxisPtr makeAxis(int index, LibertyGroup *group); - FloatSeq *readFloatSeq(LibertyAttr *attr, - float scale); + FloatSeq readFloatSeq(LibertyAttr *attr, + float scale); void variableValue(const char *var, float &value, bool &exists); @@ -631,7 +632,7 @@ protected: bool in_ccsn_; bool in_ecsm_waveform_; TableAxisVariable axis_var_[3]; - FloatSeq *axis_values_[3]; + FloatSeq axis_values_[3]; int type_bit_from_; bool type_bit_from_exists_; int type_bit_to_; @@ -889,17 +890,45 @@ protected: ReceiverModelPtr receiver_model_; }; -class InternalPowerGroup : public InternalPowerAttrs, public RelatedPortGroup +class InternalPowerGroup : public RelatedPortGroup { public: InternalPowerGroup(int line); - virtual ~InternalPowerGroup(); + const std::string &relatedPgPin() const { return related_pg_pin_; } + void setRelatedPgPin(std::string related_pg_pin); + const std::shared_ptr &when() const { return when_; } + void setWhen(std::shared_ptr when); + void setModel(const RiseFall *rf, + std::shared_ptr model); + InternalPowerModels &models() { return models_; } + +private: + std::string related_pg_pin_; + std::shared_ptr when_; + InternalPowerModels models_; }; class LeakagePowerGroup { public: LeakagePowerGroup(int line); +<<<<<<< +======= + const std::string &relatedPgPin() const { return related_pg_pin_; } + void setRelatedPgPin(std::string pin_name); + FuncExpr *when() const { return when_; } + void setWhen(FuncExpr *when); + float power() const { return power_; } + void setPower(float power); + +protected: + std::string related_pg_pin_; + FuncExpr *when_; + float power_; + int line_; +}; + +>>>>>>> const std::string &relatedPgPin() const { return related_pg_pin_; } void setRelatedPgPin(std::string pin_name); FuncExpr *when() const { return when_; } @@ -953,19 +982,19 @@ class OutputWaveform public: OutputWaveform(float axis_value1, float axis_value2, - Table1 *currents, + Table *currents, float reference_time); ~OutputWaveform(); float slew() const { return slew_; } float cap() const { return cap_; } - Table1 *currents() const { return currents_; } - Table1 *stealCurrents(); + Table *currents() const { return currents_; } + Table *stealCurrents(); float referenceTime() { return reference_time_; } private: float slew_; float cap_; - Table1 *currents_; + Table *currents_; float reference_time_; }; diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 4afa66f00..6c4b757f4 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -218,7 +218,7 @@ 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()); + fprintf(stream_, " lu_table_template(%s) {\n", tbl_template->name().c_str()); fprintf(stream_, " variable_1 : %s;\n", tableVariableString(axis1->variable())); if (axis2) @@ -227,11 +227,11 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template) if (axis3) fprintf(stream_, " variable_3 : %s;\n", tableVariableString(axis3->variable())); - if (axis1 && axis1->values()) + if (axis1 && !axis1->values().empty()) writeTableAxis4(axis1, 1); - if (axis2 && axis2->values()) + if (axis2 && !axis2->values().empty()) writeTableAxis4(axis2, 2); - if (axis3 && axis3->values()) + if (axis3 && !axis3->values().empty()) writeTableAxis4(axis3, 3); fprintf(stream_, " }\n"); } @@ -268,7 +268,7 @@ LibertyWriter::writeBusDcls() { BusDclSeq dcls = library_->busDcls(); for (BusDcl *dcl : dcls) { - fprintf(stream_, " type (\"%s\") {\n", dcl->name()); + 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", abs(dcl->from() - dcl->to() + 1)); @@ -333,7 +333,7 @@ LibertyWriter::writeBusPort(const LibertyPort *port) { fprintf(stream_, " bus(\"%s\") {\n", port->name()); if (port->busDcl()) - fprintf(stream_, " bus_type : %s;\n", port->busDcl()->name()); + fprintf(stream_, " bus_type : %s;\n", port->busDcl()->name().c_str()); writePortAttrs(port); LibertyPortMemberIterator member_iter(port); @@ -370,10 +370,10 @@ LibertyWriter::writePortAttrs(const LibertyPort *port) three_state->to_string().c_str()); } else { - FuncExpr three_state(FuncExpr::Op::not_, tristate_enable, - nullptr, nullptr); + FuncExpr *three_state = tristate_enable->copy()->invert(); fprintf(stream_, " three_state : \"%s\";\n", - three_state.to_string().c_str()); + three_state->to_string().c_str()); + delete three_state; } } if (port->isClock()) @@ -392,7 +392,7 @@ LibertyWriter::writePortAttrs(const LibertyPort *port) fprintf(stream_, " max_capacitance : %s;\n", cap_unit_->asString(limit, 3)); - for (TimingArcSet *arc_set : port->libertyCell()->timingArcSets(nullptr,port)) { + for (TimingArcSet *arc_set : port->libertyCell()->timingArcSetsTo(port)) { if (!isAutoWidthArc(port, arc_set)) writeTimingArcSet(arc_set); } @@ -460,23 +460,23 @@ LibertyWriter::writeTimingModels(const TimingArc *arc, const CheckTableModel *check_model = dynamic_cast(model); if (gate_model) { const TableModel *delay_model = gate_model->delayModel(); - const char *template_name = delay_model->tblTemplate()->name(); - fprintf(stream_, " cell_%s(%s) {\n", rf->name(), template_name); + const std::string &template_name = delay_model->tblTemplate()->name(); + fprintf(stream_, " cell_%s(%s) {\n", rf->name(), template_name.c_str()); writeTableModel(delay_model); fprintf(stream_, " }\n"); const TableModel *slew_model = gate_model->slewModel(); if (slew_model) { - template_name = slew_model->tblTemplate()->name(); - fprintf(stream_, " %s_transition(%s) {\n", rf->name(), template_name); + const std::string &slew_template_name = slew_model->tblTemplate()->name(); + 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 char *template_name = model->tblTemplate()->name(); - fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name); + const std::string &template_name = model->tblTemplate()->name(); + fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name.c_str()); writeTableModel(model); fprintf(stream_, " }\n"); } diff --git a/liberty/Sequential.cc b/liberty/Sequential.cc index a89ae99b1..782a22dce 100644 --- a/liberty/Sequential.cc +++ b/liberty/Sequential.cc @@ -49,16 +49,54 @@ Sequential::Sequential(bool is_register, { } +Sequential::Sequential(Sequential &&other) noexcept : + is_register_(other.is_register_), + clock_(other.clock_), + data_(other.data_), + clear_(other.clear_), + preset_(other.preset_), + clr_preset_out_(other.clr_preset_out_), + clr_preset_out_inv_(other.clr_preset_out_inv_), + output_(other.output_), + output_inv_(other.output_inv_) +{ + other.clock_ = nullptr; + other.data_ = nullptr; + other.clear_ = nullptr; + other.preset_ = nullptr; +} + +Sequential & +Sequential::operator=(Sequential &&other) noexcept +{ + if (this != &other) { + delete clock_; + delete data_; + delete clear_; + delete preset_; + is_register_ = other.is_register_; + clock_ = other.clock_; + data_ = other.data_; + clear_ = other.clear_; + preset_ = other.preset_; + clr_preset_out_ = other.clr_preset_out_; + clr_preset_out_inv_ = other.clr_preset_out_inv_; + output_ = other.output_; + output_inv_ = other.output_inv_; + other.clock_ = nullptr; + other.data_ = nullptr; + other.clear_ = nullptr; + other.preset_ = nullptr; + } + return *this; +} + Sequential::~Sequential() { - if (clock_) - clock_->deleteSubexprs(); - if (data_) - data_->deleteSubexprs(); - if (clear_) - clear_->deleteSubexprs(); - if (preset_) - preset_->deleteSubexprs(); + delete clock_; + delete data_; + delete clear_; + delete preset_; } //////////////////////////////////////////////////////////////// diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 287416302..27512dc52 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -45,7 +45,9 @@ size_t findValueIndex(float value, const FloatSeq *values); static void -deleteSigmaModels(TableModel *models[EarlyLate::index_count]); +sigmaModelsMvOwner(TableModel *models[EarlyLate::index_count], + std::array, + EarlyLate::index_count> &out); static string reportPvt(const LibertyCell *cell, const Pvt *pvt, @@ -72,35 +74,29 @@ GateTableModel::GateTableModel(LibertyCell *cell, receiver_model_(receiver_model), output_waveforms_(output_waveforms) { - for (auto el_index : EarlyLate::rangeIndex()) { - slew_sigma_models_[el_index] = slew_sigma_models - ? slew_sigma_models[el_index] - : nullptr; - delay_sigma_models_[el_index] = delay_sigma_models - ? delay_sigma_models[el_index] - : nullptr; - } + sigmaModelsMvOwner(delay_sigma_models, delay_sigma_models_); + sigmaModelsMvOwner(slew_sigma_models, slew_sigma_models_); } -GateTableModel::~GateTableModel() -{ - delete delay_model_; - delete slew_model_; - delete output_waveforms_; - deleteSigmaModels(slew_sigma_models_); - deleteSigmaModels(delay_sigma_models_); -} +GateTableModel::~GateTableModel() = default; static void -deleteSigmaModels(TableModel *models[EarlyLate::index_count]) -{ - 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; +sigmaModelsMvOwner(TableModel *models[EarlyLate::index_count], + std::array, + EarlyLate::index_count> &out) +{ + TableModel *early_model = models ? models[EarlyLate::earlyIndex()] : nullptr; + TableModel *late_model = models ? models[EarlyLate::lateIndex()] : nullptr; + if (early_model) { + out[EarlyLate::earlyIndex()].reset(early_model); + if (late_model && late_model != early_model) { + out[EarlyLate::lateIndex()].reset(late_model); + } else if (late_model == early_model) { + out[EarlyLate::lateIndex()] = + std::make_unique(*out[EarlyLate::earlyIndex()]); + } + } else if (late_model) { + out[EarlyLate::lateIndex()].reset(late_model); } } @@ -122,23 +118,23 @@ GateTableModel::gateDelay(const Pvt *pvt, ArcDelay &gate_delay, Slew &drvr_slew) const { - float delay = findValue(pvt, delay_model_, in_slew, load_cap, 0.0); + 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()], + sigma_early = findValue(pvt, delay_sigma_models_[EarlyLate::earlyIndex()].get(), in_slew, load_cap, 0.0); if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(pvt, delay_sigma_models_[EarlyLate::lateIndex()], + sigma_late = findValue(pvt, delay_sigma_models_[EarlyLate::lateIndex()].get(), in_slew, load_cap, 0.0); gate_delay = makeDelay(delay, sigma_early, sigma_late); - float slew = findValue(pvt, slew_model_, in_slew, load_cap, 0.0); + 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()], + sigma_early = findValue(pvt, slew_sigma_models_[EarlyLate::earlyIndex()].get(), in_slew, load_cap, 0.0); if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(pvt, slew_sigma_models_[EarlyLate::lateIndex()], + sigma_late = findValue(pvt, slew_sigma_models_[EarlyLate::lateIndex()].get(), in_slew, load_cap, 0.0); // Clip negative slews to zero. if (slew < 0.0) @@ -166,28 +162,28 @@ GateTableModel::reportGateDelay(const Pvt *pvt, int digits) const { string result = reportPvt(cell_, pvt, digits); - result += reportTableLookup("Delay", pvt, delay_model_, in_slew, + result += reportTableLookup("Delay", pvt, delay_model_.get(), 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()], + delay_sigma_models_[EarlyLate::earlyIndex()].get(), 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()], + delay_sigma_models_[EarlyLate::lateIndex()].get(), in_slew, load_cap, 0.0, digits); result += '\n'; - result += reportTableLookup("Slew", pvt, slew_model_, in_slew, + 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()], + slew_sigma_models_[EarlyLate::earlyIndex()].get(), 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()], + slew_sigma_models_[EarlyLate::lateIndex()].get(), in_slew, load_cap, 0.0, digits); - float drvr_slew = findValue(pvt, slew_model_, in_slew, load_cap, 0.0); + float drvr_slew = findValue(pvt, slew_model_.get(), in_slew, load_cap, 0.0); if (drvr_slew < 0.0) result += "Negative slew clipped to 0.0\n"; return result; @@ -286,29 +282,46 @@ GateTableModel::driveResistance(const Pvt *pvt) const return slew / cap; } +const TableModel * +GateTableModel::delaySigmaModel(const EarlyLate *el) const +{ + return delay_sigma_models_[el->index()].get(); +} + +const TableModel * +GateTableModel::slewSigmaModel(const EarlyLate *el) const +{ + return slew_sigma_models_[el->index()].get(); +} + 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(); if (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance) { cap = axis1->axisValue(axis1->size() - 1); - slew = findValue(pvt, slew_model_, in_slew, cap, 0.0); + slew = findValue(pvt, slew_model_.get(), 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_, in_slew, cap, 0.0); + slew = findValue(pvt, slew_model_.get(), 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_, in_slew, cap, 0.0); + slew = findValue(pvt, slew_model_.get(), in_slew, cap, 0.0); } else { // Table not dependent on capacitance. @@ -368,21 +381,17 @@ GateTableModel::checkAxis(const TableAxis *axis) //////////////////////////////////////////////////////////////// -ReceiverModel::~ReceiverModel() -{ - for (TableModel *model : capacitance_models_) - delete model; -} +ReceiverModel::~ReceiverModel() = default; void -ReceiverModel::setCapacitanceModel(TableModel *table_model, +ReceiverModel::setCapacitanceModel(TableModel table_model, size_t segment, const RiseFall *rf) { if ((segment + 1) * RiseFall::index_count > capacitance_models_.size()) capacitance_models_.resize((segment + 1) * RiseFall::index_count); size_t idx = segment * RiseFall::index_count + rf->index(); - capacitance_models_[idx] = table_model; + capacitance_models_[idx] = std::move(table_model); } bool @@ -410,20 +419,22 @@ CheckTableModel::CheckTableModel(LibertyCell *cell, CheckTimingModel(cell), model_(model) { - for (auto el_index : EarlyLate::rangeIndex()) - sigma_models_[el_index] = sigma_models ? sigma_models[el_index] : nullptr; + sigmaModelsMvOwner(sigma_models, sigma_models_); } -CheckTableModel::~CheckTableModel() -{ - delete model_; - deleteSigmaModels(sigma_models_); -} +CheckTableModel::~CheckTableModel() = default; void CheckTableModel::setIsScaled(bool is_scaled) { - model_->setIsScaled(is_scaled); + if (model_) + model_->setIsScaled(is_scaled); +} + +const TableModel * +CheckTableModel::sigmaModel(const EarlyLate *el) const +{ + return sigma_models_[el->index()].get(); } ArcDelay @@ -434,14 +445,14 @@ CheckTableModel::checkDelay(const Pvt *pvt, bool pocv_enabled) const { if (model_) { - float mean = findValue(pvt, model_, from_slew, to_slew, related_out_cap); + 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()], + sigma_early = findValue(pvt, sigma_models_[EarlyLate::earlyIndex()].get(), from_slew, to_slew, related_out_cap); if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) - sigma_late = findValue(pvt, sigma_models_[EarlyLate::lateIndex()], + sigma_late = findValue(pvt, sigma_models_[EarlyLate::lateIndex()].get(), from_slew, to_slew, related_out_cap); return makeDelay(mean, sigma_early, sigma_late); } @@ -475,17 +486,17 @@ CheckTableModel::reportCheckDelay(const Pvt *pvt, bool pocv_enabled, int digits) const { - string result = reportTableDelay("Check", pvt, model_, + 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()], + sigma_models_[EarlyLate::earlyIndex()].get(), 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()], + sigma_models_[EarlyLate::lateIndex()].get(), from_slew, from_slew_annotation, to_slew, related_out_cap, digits); return result; @@ -602,6 +613,15 @@ CheckTableModel::checkAxis(const TableAxis *axis) //////////////////////////////////////////////////////////////// +TableModel::TableModel() : + table_(nullptr), + tbl_template_(nullptr), + scale_factor_type_(0), + rf_index_(0), + is_scaled_(false) +{ +} + TableModel::TableModel(TablePtr table, TableTemplate *tbl_template, ScaleFactorType scale_factor_type, @@ -620,6 +640,12 @@ TableModel::order() const return table_->order(); } +ScaleFactorType +TableModel::scaleFactorType() const +{ + return static_cast(scale_factor_type_); +} + void TableModel::setScaleFactorType(ScaleFactorType type) { @@ -751,177 +777,394 @@ TableModel::reportPvtScaleFactor(const LibertyCell *cell, //////////////////////////////////////////////////////////////// -Table0::Table0(float value) : - Table(), - value_(value) +void +Table::clear() { + if (order_ == 1) + values1_.clear(); + else if (order_ == 2 || order_ == 3) + values_table_.clear(); } -float -Table0::value(size_t, - size_t, - size_t) const +FloatSeq * +Table::values() const { - return value_; + return (order_ == 1) ? const_cast(&values1_) : nullptr; } -float -Table0::findValue(float, - float, - float) const +FloatTable * +Table::values3() { - return value_; + return (order_ >= 2) ? &values_table_ : nullptr; } -string -Table0::reportValue(const char *result_name, - const LibertyCell *, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const +const FloatTable * +Table::values3() const { - string result = result_name; - result += " constant = "; - result += table_unit->asString(findValue(value1, value2, value3), digits); - if (comment1) - result += comment1; - result += '\n'; - return result; + return (order_ >= 2) ? &values_table_ : nullptr; } -void -Table0::report(const Units *units, - Report *report) const +Table::Table() : + order_(0), + value_(0.0) { - int digits = 4; - const Unit *table_unit = units->timeUnit(); - report->reportLine("%s", table_unit->asString(value_, digits)); } -//////////////////////////////////////////////////////////////// +Table::Table(float value) : + order_(0), + value_(value) +{ +} -Table1::Table1() : - Table(), - values_(nullptr), - axis1_(nullptr) +Table::Table(FloatSeq *values, + TableAxisPtr axis1) : + order_(1), + value_(0.0), + values1_(std::move(*values)), + axis1_(axis1) { + delete values; } -Table1::Table1(FloatSeq *values, - TableAxisPtr axis1) : - Table(), - values_(values), +Table::Table(FloatSeq &&values, + TableAxisPtr axis1) : + order_(1), + value_(0.0), + values1_(std::move(values)), axis1_(axis1) { } -Table1::Table1(Table1 &&table) : - Table(), - values_(table.values_), - axis1_(table.axis1_) +Table::Table(FloatTable &&values, + TableAxisPtr axis1, + TableAxisPtr axis2) : + order_(2), + value_(0.0), + values_table_(std::move(values)), + axis1_(axis1), + axis2_(axis2) +{ +} + +Table::Table(FloatTable &&values, + TableAxisPtr axis1, + TableAxisPtr axis2, + TableAxisPtr axis3) : + order_(3), + value_(0.0), + values_table_(std::move(values)), + axis1_(axis1), + axis2_(axis2), + axis3_(axis3) { - table.values_ = nullptr; - table.axis1_ = nullptr; } -Table1::Table1(const Table1 &table) : - Table(), - values_(new FloatSeq(*table.values_)), - axis1_(table.axis1_) +Table::Table(Table &&table) : + order_(table.order_), + value_(table.value_), + values1_(std::move(table.values1_)), + values_table_(std::move(table.values_table_)), + axis1_(table.axis1_), + axis2_(table.axis2_), + axis3_(table.axis3_) { } -Table1::~Table1() +Table::Table(const Table &table) : + order_(table.order_), + value_(table.value_), + values1_(table.values1_), + values_table_(table.values_table_), + axis1_(table.axis1_), + axis2_(table.axis2_), + axis3_(table.axis3_) { - delete values_; } -Table1 & -Table1::operator=(Table1 &&table) +Table & +Table::operator=(Table &&table) { - values_ = table.values_; - axis1_ = table.axis1_; - table.values_ = nullptr; - table.axis1_ = nullptr; + if (this != &table) { + order_ = table.order_; + value_ = table.value_; + values1_ = std::move(table.values1_); + values_table_ = std::move(table.values_table_); + axis1_ = table.axis1_; + axis2_ = table.axis2_; + axis3_ = table.axis3_; + } return *this; } -float -Table1::value(size_t axis_index1, - size_t, - size_t) const +void +Table::setScaleFactorType(ScaleFactorType) +{ +} + +void +Table::setIsScaled(bool) { - return value(axis_index1); } float -Table1::value(size_t axis_index1) const +Table::value(size_t axis_idx1, + size_t axis_idx2, + size_t axis_idx3) const { - return (*values_)[axis_index1]; + if (order_ == 0) + return value_; + if (order_ == 1) + return values1_[axis_idx1]; + if (order_ == 2) + return values_table_[axis_idx1][axis_idx2]; + // order_ == 3 + size_t row = axis_idx1 * axis2_->size() + axis_idx2; + return values_table_[row][axis_idx3]; } float -Table1::findValue(float axis_value1, - float, - float) const +Table::value(size_t index1) const { - return findValue(axis_value1); + if (order_ != 1 || values1_.empty()) + return value_; + return values1_[index1]; } float -Table1::findValue(float axis_value1) const +Table::value(size_t axis_index1, + size_t axis_index2) const { - if (axis1_->size() == 1) - return this->value(axis_value1); - else { + return values_table_[axis_index1][axis_index2]; +} + +float +Table::findValue(float axis_value1, + float axis_value2, + float axis_value3) const +{ + if (order_ == 0) + 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); + 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 y1 = this->value(axis_index1); - double y2 = this->value(axis_index1 + 1); double dx1 = (x1 - x1l) / (x1u - x1l); - return (1 - dx1) * y1 + dx1 * y2; + 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; } + // order_ == 3 - trilinear interpolation + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + size_t axis_index2 = axis2_->findAxisIndex(axis_value2); + size_t axis_index3 = axis3_->findAxisIndex(axis_value3); + double x1 = axis_value1; + double x2 = axis_value2; + double x3 = axis_value3; + double dx1 = 0.0; + double dx2 = 0.0; + double dx3 = 0.0; + double y000 = value(axis_index1, axis_index2, axis_index3); + double y001 = 0.0; + double y010 = 0.0; + double y011 = 0.0; + double y100 = 0.0; + double y101 = 0.0; + double y110 = 0.0; + double y111 = 0.0; + + if (axis1_->size() != 1) { + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + dx1 = (x1 - x1l) / (x1u - x1l); + y100 = value(axis_index1 + 1, axis_index2, axis_index3); + if (axis3_->size() != 1) + y101 = value(axis_index1 + 1, axis_index2, axis_index3 + 1); + if (axis2_->size() != 1) { + y110 = value(axis_index1 + 1, axis_index2 + 1, axis_index3); + if (axis3_->size() != 1) + y111 = value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1); + } + } + if (axis2_->size() != 1) { + double x2l = axis2_->axisValue(axis_index2); + double x2u = axis2_->axisValue(axis_index2 + 1); + dx2 = (x2 - x2l) / (x2u - x2l); + y010 = value(axis_index1, axis_index2 + 1, axis_index3); + if (axis3_->size() != 1) + y011 = value(axis_index1, axis_index2 + 1, axis_index3 + 1); + } + if (axis3_->size() != 1) { + double x3l = axis3_->axisValue(axis_index3); + double x3u = axis3_->axisValue(axis_index3 + 1); + dx3 = (x3 - x3l) / (x3u - x3l); + y001 = value(axis_index1, axis_index2, axis_index3 + 1); + } + + 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; +} + +void +Table::findValue(float axis_value1, + float &value, + bool &extrapolated) const +{ + if (axis1_->size() == 1) { + value = this->value(0); + extrapolated = false; + return; + } + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + double x1 = axis_value1; + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + double y1 = this->value(axis_index1); + double y2 = this->value(axis_index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + value = (1 - dx1) * y1 + dx1 * y2; + extrapolated = (axis_value1 < x1l || axis_value1 > x1u); } float -Table1::findValueClip(float axis_value1) const +Table::findValue(float axis_value1) const { if (axis1_->size() == 1) - return this->value(axis_value1); - else { - size_t axis_index1 = axis1_->findAxisIndex(axis_value1); - double x1 = axis_value1; - double x1l = axis1_->axisValue(axis_index1); - double x1u = axis1_->axisValue(axis_index1 + 1); - if (x1 < x1l) - return 0.0; - else if (x1 > x1u) - return this->value(axis1_->size() - 1); - else { - double y1 = this->value(axis_index1); - double y2 = this->value(axis_index1 + 1); - double dx1 = (x1 - x1l) / (x1u - x1l); - return (1 - dx1) * y1 + dx1 * y2; - } + return value(0); + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + double x1 = axis_value1; + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + double y1 = value(axis_index1); + double y2 = value(axis_index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + return (1 - dx1) * y1 + dx1 * y2; +} + +float +Table::findValueClip(float axis_value1) const +{ + if (axis1_->size() == 1) + return value(0); + size_t axis_index1 = axis1_->findAxisIndex(axis_value1); + double x1 = axis_value1; + double x1l = axis1_->axisValue(axis_index1); + double x1u = axis1_->axisValue(axis_index1 + 1); + if (x1 < x1l) + return 0.0; + if (x1 > x1u) + return value(axis1_->size() - 1); + double y1 = value(axis_index1); + double y2 = value(axis_index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + return (1 - dx1) * y1 + dx1 * y2; +} + +float +Table::findValue(const LibertyLibrary *, + const LibertyCell *, + const Pvt *, + float axis_value1, + float axis_value2, + float axis_value3) const +{ + return findValue(axis_value1, axis_value2, axis_value3); +} + +std::string +Table::reportValue(const char *result_name, + const LibertyCell *cell, + const Pvt *, + float value1, + const char *comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const +{ + switch (order_) { + case 0: + return reportValueOrder0(result_name, comment1, table_unit, digits); + case 1: + 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); + case 3: + return reportValueOrder3(result_name, cell, value1, comment1, + value2, value3, table_unit, digits); + default: + return ""; } } -string -Table1::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const +std::string +Table::reportValueOrder0(const char *result_name, + const char *comment1, + const Unit *table_unit, + int digits) const +{ + string result = result_name; + result += " constant = "; + result += table_unit->asString(value_, digits); + if (comment1) + result += comment1; + result += '\n'; + return result; +} + +std::string +Table::reportValueOrder1(const char *result_name, + const LibertyCell *cell, + float value1, + const char *comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); @@ -932,7 +1175,6 @@ Table1::reportValue(const char *result_name, if (comment1) result += comment1; result += '\n'; - if (axis1_->size() != 1) { size_t index1 = axis1_->findAxisIndex(value1); result += " "; @@ -940,17 +1182,13 @@ Table1::reportValue(const char *result_name, result += " "; result += unit1->asString(axis1_->axisValue(index1 + 1), digits); result += '\n'; - result += " --------------------\n"; - result += "| "; result += table_unit->asString(value(index1), digits); result += " "; - result += table_unit->asString(value(index1 + 1), - digits); + result += table_unit->asString(value(index1 + 1), digits); result += '\n'; } - result += result_name; result += " = "; result += table_unit->asString(findValue(value1, value2, value3), digits); @@ -958,154 +1196,31 @@ Table1::reportValue(const char *result_name, return result; } -void -Table1::report(const Units *units, - Report *report) const -{ - int digits = 4; - const Unit *unit1 = axis1_->unit(units); - const Unit *table_unit = units->timeUnit(); - report->reportLine("%s", tableVariableString(axis1_->variable())); - report->reportLine("------------------------------"); - string line; - for (size_t index1 = 0; index1 < axis1_->size(); index1++) { - line += unit1->asString(axis1_->axisValue(index1), digits); - line += " "; - } - report->reportLineString(line); - - line.clear(); - for (size_t index1 = 0; index1 < axis1_->size(); index1++) { - line += table_unit->asString(value(index1), digits); - line += " "; - } - report->reportLineString(line); -} - -//////////////////////////////////////////////////////////////// - -Table2::Table2(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2) : - Table(), - values_(values), - axis1_(axis1), - axis2_(axis2) -{ -} - -Table2::~Table2() -{ - deleteContents(*values_); - delete values_; -} - -float -Table2::value(size_t axis_index1, - size_t axis_index2, - size_t) const -{ - return value(axis_index1, axis_index2); -} - -float -Table2::value(size_t axis_index1, - size_t axis_index2) const -{ - FloatSeq *row = (*values_)[axis_index1]; - return (*row)[axis_index2]; -} - -// Bilinear Interpolation. -float -Table2::findValue(float axis_value1, - float axis_value2, - float) const -{ - size_t size1 = axis1_->size(); - size_t size2 = axis2_->size(); - if (size1 == 1) { - if (size2 == 1) - return value(0, 0); - else { - 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); - double tbl_value - = (1 - dx2) * y00 - + dx2 * y01; - return tbl_value; - } - } - else 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); - double tbl_value - = (1 - dx1) * y00 - + dx1 * y10; - return tbl_value; - } - else { - 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); - double tbl_value - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; - return tbl_value; - } -} - -string -Table2::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const +std::string +Table::reportValueOrder2(const char *result_name, + const LibertyCell *cell, + float value1, + const char *comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); string result = "------- "; - result += axis1_->variableString(), + result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); if (comment1) result += comment1; result += '\n'; - result += "| "; result += axis2_->variableString(); result += " = "; result += unit2->asString(value2, digits); result += '\n'; - size_t index1 = axis1_->findAxisIndex(value1); size_t index2 = axis2_->findAxisIndex(value2); result += "| "; @@ -1115,29 +1230,25 @@ Table2::reportValue(const char *result_name, result += unit2->asString(axis2_->axisValue(index2 + 1), digits); } result += '\n'; - result += "v --------------------\n"; result += unit1->asString(axis1_->axisValue(index1), digits); result += " | "; - result += table_unit->asString(value(index1, index2), digits); if (axis2_->size() != 1) { result += " "; result += table_unit->asString(value(index1, index2 + 1), digits); } result += '\n'; - if (axis1_->size() != 1) { result += unit1->asString(axis1_->axisValue(index1 + 1), digits); result += " | "; result += table_unit->asString(value(index1 + 1, index2), digits); if (axis2_->size() != 1) { result += " "; - result +=table_unit->asString(value(index1 + 1, index2 + 1),digits); + result += table_unit->asString(value(index1 + 1, index2 + 1), digits); } } result += '\n'; - result += result_name; result += " = "; result += table_unit->asString(findValue(value1, value2, value3), digits); @@ -1145,169 +1256,40 @@ Table2::reportValue(const char *result_name, return result; } -void -Table2::report(const Units *units, - Report *report) const -{ - int digits = 4; - const Unit *table_unit = units->timeUnit(); - const Unit *unit1 = axis1_->unit(units); - const Unit *unit2 = axis2_->unit(units); - report->reportLine("%s", tableVariableString(axis2_->variable())); - report->reportLine(" ------------------------------"); - string line = " "; - for (size_t index2 = 0; index2 < axis2_->size(); index2++) { - line += unit2->asString(axis2_->axisValue(index2), digits); - line += " "; - } - report->reportLineString(line); - - for (size_t index1 = 0; index1 < axis1_->size(); index1++) { - line = unit1->asString(axis1_->axisValue(index1), digits); - line += " |"; - for (size_t index2 = 0; index2 < axis2_->size(); index2++) { - line += table_unit->asString(value(index1, index2), digits); - line += " "; - } - report->reportLineString(line); - } -} - -//////////////////////////////////////////////////////////////// - -Table3::Table3(FloatTable *values, - TableAxisPtr axis1, - TableAxisPtr axis2, - TableAxisPtr axis3) : - Table2(values, axis1, axis2), - axis3_(axis3) -{ -} - -float -Table3::value(size_t axis_index1, - size_t axis_index2, - size_t axis_index3) const -{ - size_t row = axis_index1 * axis2_->size() + axis_index2; - return values_->operator[](row)->operator[](axis_index3); -} - -// Bilinear Interpolation. -float -Table3::findValue(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); - double x1 = axis_value1; - double x2 = axis_value2; - double x3 = axis_value3; - double dx1 = 0.0; - double dx2 = 0.0; - double dx3 = 0.0; - double y000 = value(axis_index1, axis_index2, axis_index3); - double y001 = 0.0; - double y010 = 0.0; - double y011 = 0.0; - double y100 = 0.0; - double y101 = 0.0; - double y110 = 0.0; - double y111 = 0.0; - - if (axis1_->size() != 1) { - double x1l = axis1_->axisValue(axis_index1); - double x1u = axis1_->axisValue(axis_index1 + 1); - dx1 = (x1 - x1l) / (x1u - x1l); - y100 = value(axis_index1 + 1, axis_index2, axis_index3); - if (axis3_->size() != 1) - y101 = value(axis_index1 + 1, axis_index2, axis_index3 + 1); - if (axis2_->size() != 1) { - y110 = value(axis_index1 + 1, axis_index2 + 1, axis_index3); - if (axis3_->size() != 1) - y111 = value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1); - } - } - if (axis2_->size() != 1) { - double x2l = axis2_->axisValue(axis_index2); - double x2u = axis2_->axisValue(axis_index2 + 1); - dx2 = (x2 - x2l) / (x2u - x2l); - y010 = value(axis_index1, axis_index2 + 1, axis_index3); - if (axis3_->size() != 1) - y011 = value(axis_index1, axis_index2 + 1, axis_index3 + 1); - } - if (axis3_->size() != 1) { - double x3l = axis3_->axisValue(axis_index3); - double x3u = axis3_->axisValue(axis_index3 + 1); - dx3 = (x3 - x3l) / (x3u - x3l); - y001 = value(axis_index1, axis_index2, axis_index3 + 1); - } - - double tbl_value - = (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; - return tbl_value; -} - -// Sample output. -// -// --------- input_net_transition = 0.00 -// | ---- total_output_net_capacitance = 0.20 -// | | related_out_total_output_net_capacitance = 0.10 -// | | 0.00 0.30 -// v | -------------------- -// 0.01 v / 0.23 0.25 -// 0.00 0.20 | 0.10 0.20 -// |/ 0.30 0.32 -// 0.40 | 0.20 0.30 -string -Table3::reportValue(const char *result_name, - const LibertyCell *cell, - const Pvt *, - float value1, - const char *comment1, - float value2, - float value3, - const Unit *table_unit, - int digits) const +std::string +Table::reportValueOrder3(const char *result_name, + const LibertyCell *cell, + float value1, + const char *comment1, + float value2, + float value3, + const Unit *table_unit, + int digits) const { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); const Unit *unit3 = axis3_->unit(units); - string result = " --------- "; - result += axis1_->variableString(), + result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); if (comment1) result += comment1; result += '\n'; - result += " | ---- "; - result += axis2_->variableString(), + result += axis2_->variableString(); result += " = "; result += unit2->asString(value2, digits); result += '\n'; - result += " | | "; result += axis3_->variableString(); result += " = "; result += unit3->asString(value3, digits); result += '\n'; - size_t axis_index1 = axis1_->findAxisIndex(value1); size_t axis_index2 = axis2_->findAxisIndex(value2); size_t axis_index3 = axis3_->findAxisIndex(value3); - result += " | | "; result += unit3->asString(axis3_->axisValue(axis_index3), digits); if (axis3_->size() != 1) { @@ -1315,66 +1297,60 @@ Table3::reportValue(const char *result_name, result += unit3->asString(axis3_->axisValue(axis_index3 + 1), digits); } result += '\n'; - result += " v | --------------------\n"; - if (axis1_->size() != 1) { result += " "; - result += unit1->asString(axis1_->axisValue(axis_index1+1), digits); + 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 { - appendSpaces(result, digits+3); + appendSpaces(result, digits + 3); result += " v / "; } result += '\n'; - result += unit1->asString(axis1_->axisValue(axis_index1), digits); 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); + if (axis1_->size() != 1 && axis2_->size() != 1) { + 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'; - result += " "; 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'; - result += result_name; result += " = "; result += table_unit->asString(findValue(value1, value2, value3), digits); @@ -1382,27 +1358,63 @@ Table3::reportValue(const char *result_name, return result; } -static void -appendSpaces(string &result, - int count) -{ - while (count--) - result += ' '; -} - void -Table3::report(const Units *units, - Report *report) const +Table::report(const Units *units, + Report *report) const { int digits = 4; const Unit *table_unit = units->timeUnit(); + if (order_ == 0) { + report->reportLine("%s", table_unit->asString(value_, digits)); + return; + } + if (order_ == 1) { + const Unit *unit1 = axis1_->unit(units); + report->reportLine("%s", tableVariableString(axis1_->variable())); + report->reportLine("------------------------------"); + string line; + for (size_t index1 = 0; index1 < axis1_->size(); index1++) { + line += unit1->asString(axis1_->axisValue(index1), digits); + line += " "; + } + report->reportLineString(line); + line.clear(); + for (size_t index1 = 0; index1 < axis1_->size(); index1++) { + line += table_unit->asString(value(index1), digits); + line += " "; + } + report->reportLineString(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(" ------------------------------"); + string line = " "; + for (size_t index2 = 0; index2 < axis2_->size(); index2++) { + line += unit2->asString(axis2_->axisValue(index2), digits); + line += " "; + } + report->reportLineString(line); + for (size_t index1 = 0; index1 < axis1_->size(); index1++) { + line = unit1->asString(axis1_->axisValue(index1), digits); + line += " |"; + for (size_t index2 = 0; index2 < axis2_->size(); index2++) { + line += table_unit->asString(value(index1, index2), digits); + line += " "; + } + report->reportLineString(line); + } + return; + } + // order_ == 3 const Unit *unit1 = axis1_->unit(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)); - + unit1->asString(axis1_->axisValue(axis_index1), digits)); report->reportLine("%s", tableVariableString(axis3_->variable())); report->reportLine(" ------------------------------"); string line = " "; @@ -1411,12 +1423,11 @@ Table3::report(const Units *units, line += " "; } report->reportLineString(line); - for (size_t axis_index2 = 0; axis_index2 < axis2_->size(); axis_index2++) { - line = unit2->asString(axis2_->axisValue(axis_index2),digits); + 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); @@ -1424,25 +1435,28 @@ Table3::report(const Units *units, } } +static void +appendSpaces(string &result, + int count) +{ + while (count--) + result += ' '; +} + //////////////////////////////////////////////////////////////// TableAxis::TableAxis(TableAxisVariable variable, - FloatSeq *values) : + FloatSeq &&values) : variable_(variable), - values_(values) -{ -} - -TableAxis::~TableAxis() + values_(std::move(values)) { - delete values_; } float TableAxis::min() const { - if (!values_->empty()) - return (*values_)[0]; + if (!values_.empty()) + return values_[0]; else return 0.0; } @@ -1450,9 +1464,9 @@ TableAxis::min() const float TableAxis::max() const { - size_t size = values_->size(); + size_t size = values_.size(); if (size > 0) - return (*values_)[values_->size() - 1]; + return values_[values_.size() - 1]; else return 0.0; } @@ -1460,16 +1474,16 @@ TableAxis::max() const bool TableAxis::inBounds(float value) const { - size_t size = values_->size(); + size_t size = values_.size(); return size > 1 - && value >= (*values_)[0] - && value <= (*values_)[size - 1]; + && value >= values_[0] + && value <= values_[size - 1]; } size_t TableAxis::findAxisIndex(float value) const { - return findValueIndex(value, values_); + return findValueIndex(value, &values_); } // Bisection search. @@ -1504,20 +1518,20 @@ TableAxis::findAxisIndex(float value, size_t &index, bool &exists) const { - size_t size = values_->size(); + size_t size = values_.size(); if (size != 0 - && value >= (*values_)[0] - && value <= (*values_)[size - 1]) { + && value >= values_[0] + && value <= values_[size - 1]) { int lower = -1; int upper = size; while (upper - lower > 1) { int mid = (upper + lower) >> 1; - if (value == (*values_)[mid]) { + if (value == values_[mid]) { index = mid; exists = true; return; } - if (value > (*values_)[mid]) + if (value > values_[mid]) lower = mid; else upper = mid; @@ -1529,22 +1543,22 @@ TableAxis::findAxisIndex(float value, size_t TableAxis::findAxisClosestIndex(float value) const { - size_t size = values_->size(); - if (size <= 1 || value <= (*values_)[0]) + size_t size = values_.size(); + if (size <= 1 || value <= values_[0]) return 0; - else if (value >= (*values_)[size - 1]) + else if (value >= values_[size - 1]) return size - 1; else { int lower = -1; int upper = size; while (upper - lower > 1) { int mid = (upper + lower) >> 1; - if (value >= (*values_)[mid]) + if (value >= values_[mid]) lower = mid; else upper = mid; } - if ((value - (*values_)[lower]) < ((*values_)[upper] - value)) + if ((value - values_[lower]) < (values_[upper] - value)) return lower; else return upper; @@ -1574,7 +1588,8 @@ static EnumNameMap table_axis_variable_map = {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::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"}, @@ -1638,22 +1653,21 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, TableAxisPtr cap_axis, const RiseFall *rf, Table1Seq ¤t_waveforms, - Table1 *ref_times) : + Table ref_times) : slew_axis_(slew_axis), cap_axis_(cap_axis), rf_(rf), current_waveforms_(current_waveforms), - ref_times_(ref_times), + ref_times_(std::move(ref_times)), vdd_(0.0) { } OutputWaveforms::~OutputWaveforms() { - deleteContents(current_waveforms_);; + deleteContents(current_waveforms_); deleteContents(voltage_waveforms_); deleteContents(voltage_currents_); - delete ref_times_; } bool @@ -1697,13 +1711,13 @@ OutputWaveforms::findVoltages(size_t wave_index, { // Integrate current waveform to find voltage waveform. // i = C dv/dt - FloatSeq *volts = new FloatSeq; - Table1 *currents = current_waveforms_[wave_index]; + FloatSeq volts; + Table *currents = current_waveforms_[wave_index]; const TableAxis *time_axis = currents->axis1(); float prev_time = time_axis->axisValue(0); float prev_current = currents->value(0); float voltage = 0.0; - volts->push_back(voltage); + volts.push_back(voltage); bool always_rise = true; bool invert = (always_rise && rf_ == RiseFall::fall()); for (size_t i = 1; i < time_axis->size(); i++) { @@ -1711,24 +1725,24 @@ OutputWaveforms::findVoltages(size_t wave_index, float current = currents->value(i); float dv = (current + prev_current) / 2.0 * (time - prev_time) / cap; voltage += invert ? -dv : dv; - volts->push_back(voltage); + volts.push_back(voltage); prev_time = time; prev_current = current; } - (*volts)[volts->size() - 1] = vdd_; - Table1 *volt_table = new Table1(volts, currents->axis1ptr()); + volts[volts.size() - 1] = vdd_; + Table *volt_table = new Table(new FloatSeq(volts), currents->axis1ptr()); voltage_waveforms_[wave_index] = volt_table; // Make voltage -> current table. - FloatSeq *axis_volts = new FloatSeq(*volts); + FloatSeq axis_volts = volts; TableAxisPtr volt_axis = - make_shared(TableAxisVariable::input_voltage, axis_volts); + make_shared(TableAxisVariable::input_voltage, std::move(axis_volts)); FloatSeq *currents1 = new FloatSeq(*currents->values()); - Table1 *volt_currents = new Table1(currents1, volt_axis); + Table *volt_currents = new Table(currents1, volt_axis); voltage_currents_[wave_index] = volt_currents; } -Table1 +Table OutputWaveforms::currentWaveform(float slew, float cap) { @@ -1741,11 +1755,12 @@ OutputWaveforms::currentWaveform(float slew, times->push_back(time); currents->push_back(current); } - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, times); - return Table1(currents, time_axis); + TableAxisPtr time_axis = make_shared(TableAxisVariable::time, std::move(*times)); + delete times; + return Table(currents, time_axis); } -const Table1 * +const Table * OutputWaveforms::currentWaveformRaw(float slew, float cap) { @@ -1834,7 +1849,7 @@ float OutputWaveforms::voltageTime2(float volt, size_t wave_index) { - const Table1 *voltage_waveform = voltage_waveforms_[wave_index]; + const Table *voltage_waveform = voltage_waveforms_[wave_index]; const FloatSeq *voltages = voltage_waveform->values(); size_t index1 = findValueIndex(volt, voltages); float volt_lo = (*voltages)[index1]; @@ -1870,10 +1885,10 @@ OutputWaveforms::waveformValue(float slew, size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); - const Table1 *waveform00 = waveforms[wave_index00]; - const Table1 *waveform01 = waveforms[wave_index01]; - const Table1 *waveform10 = waveforms[wave_index10]; - const Table1 *waveform11 = waveforms[wave_index11]; + const Table *waveform00 = waveforms[wave_index00]; + const Table *waveform01 = waveforms[wave_index01]; + const Table *waveform10 = waveforms[wave_index10]; + const Table *waveform11 = waveforms[wave_index11]; // Interpolate waveform samples at voltage steps. size_t index1 = slew_index; @@ -1902,26 +1917,27 @@ OutputWaveforms::waveformValue(float slew, float OutputWaveforms::referenceTime(float slew) { - return ref_times_->findValue(slew); + return ref_times_.findValue(slew); } -Table1 +Table OutputWaveforms::voltageWaveform(float slew, float cap) { - FloatSeq *times = new FloatSeq; - FloatSeq *volts = new FloatSeq; + FloatSeq times; + FloatSeq volts; for (size_t i = 0; i <= voltage_waveform_step_count_; i++) { float volt = i * vdd_ / voltage_waveform_step_count_; float time = voltageTime(slew, cap, volt); - times->push_back(time); - volts->push_back(volt); + times.push_back(time); + volts.push_back(volt); } - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, times); - return Table1(volts, time_axis); + TableAxisPtr time_axis = make_shared(TableAxisVariable::time, + std::move(times)); + return Table(std::move(volts), time_axis); } -const Table1 * +const Table * OutputWaveforms::voltageWaveformRaw(float slew, float cap) { @@ -1989,10 +2005,10 @@ OutputWaveforms::beginEndTime(float slew, size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); - const Table1 *waveform00 = current_waveforms_[wave_index00]; - const Table1 *waveform01 = current_waveforms_[wave_index01]; - const Table1 *waveform10 = current_waveforms_[wave_index10]; - const Table1 *waveform11 = current_waveforms_[wave_index11]; + const Table *waveform00 = current_waveforms_[wave_index00]; + const Table *waveform01 = current_waveforms_[wave_index01]; + const Table *waveform10 = current_waveforms_[wave_index10]; + const Table *waveform11 = current_waveforms_[wave_index11]; // Interpolate waveform samples at voltage steps. size_t index1 = slew_index; @@ -2028,7 +2044,7 @@ OutputWaveforms::beginEndTime(float slew, return wave_value; } -Table1 +Table OutputWaveforms::voltageCurrentWaveform(float slew, float cap) { @@ -2041,8 +2057,9 @@ OutputWaveforms::voltageCurrentWaveform(float slew, currents->push_back(current); } TableAxisPtr volt_axis = - make_shared(TableAxisVariable::input_voltage, volts); - return Table1(currents, volt_axis); + make_shared(TableAxisVariable::input_voltage, std::move(*volts)); + delete volts; + return Table(currents, volt_axis); } // Incremental resistance at final value of waveform. @@ -2055,11 +2072,11 @@ OutputWaveforms::finalResistance() size_t cap_count = cap_axis_->size(); size_t cap_index = cap_count - 1; size_t wave_index = slew_index * cap_count + cap_index; - const Table1 *voltage_currents = voltage_currents_[wave_index]; - FloatSeq *voltages = voltage_currents->axis1()->values(); + const Table *voltage_currents = voltage_currents_[wave_index]; + const FloatSeq &voltages = voltage_currents->axis1()->values(); FloatSeq *currents = voltage_currents->values(); - size_t idx_last1 = voltages->size() - 2; - return (vdd_ - (*voltages)[idx_last1]) / abs((*currents)[idx_last1]); + size_t idx_last1 = voltages.size() - 2; + return (vdd_ - voltages[idx_last1]) / abs((*currents)[idx_last1]); } //////////////////////////////////////////////////////////////// @@ -2071,20 +2088,21 @@ DriverWaveform::DriverWaveform(const string &name, { } -Table1 +Table DriverWaveform::waveform(float slew) { const TableAxis *volt_axis = waveforms_->axis2(); FloatSeq *time_values = new FloatSeq; FloatSeq *volt_values = new FloatSeq; - for (float volt : *volt_axis->values()) { + for (float volt : volt_axis->values()) { float time = waveforms_->findValue(slew, volt, 0.0); time_values->push_back(time); volt_values->push_back(volt); } TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - time_values); - Table1 waveform(volt_values, time_axis); + std::move(*time_values)); + delete time_values; + Table waveform(volt_values, time_axis); return waveform; } diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 0a7acff5c..347c56fef 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -51,11 +51,6 @@ TimingArcAttrs::TimingArcAttrs() : timing_type_(TimingType::combinational), timing_sense_(TimingSense::unknown), cond_(nullptr), - sdf_cond_(nullptr), - sdf_cond_start_(nullptr), - sdf_cond_end_(nullptr), - mode_name_(nullptr), - mode_value_(nullptr), ocv_arc_depth_(0.0), models_{nullptr, nullptr} { @@ -65,11 +60,6 @@ TimingArcAttrs::TimingArcAttrs(TimingSense sense) : timing_type_(TimingType::combinational), timing_sense_(sense), cond_(nullptr), - sdf_cond_(nullptr), - sdf_cond_start_(nullptr), - sdf_cond_end_(nullptr), - mode_name_(nullptr), - mode_value_(nullptr), ocv_arc_depth_(0.0), models_{nullptr, nullptr} { @@ -77,15 +67,7 @@ TimingArcAttrs::TimingArcAttrs(TimingSense sense) : TimingArcAttrs::~TimingArcAttrs() { - if (cond_) - cond_->deleteSubexprs(); - if (sdf_cond_start_ != sdf_cond_) - stringDelete(sdf_cond_start_); - if (sdf_cond_end_ != sdf_cond_) - stringDelete(sdf_cond_end_); - stringDelete(sdf_cond_); - stringDelete(mode_name_); - stringDelete(mode_value_); + delete cond_; delete models_[RiseFall::riseIndex()]; delete models_[RiseFall::fallIndex()]; } @@ -109,39 +91,34 @@ TimingArcAttrs::setCond(FuncExpr *cond) } void -TimingArcAttrs::setSdfCond(const char *cond) +TimingArcAttrs::setSdfCond(const std::string &cond) { - stringDelete(sdf_cond_); - sdf_cond_ = stringCopy(cond); + sdf_cond_ = cond; sdf_cond_start_ = sdf_cond_end_ = sdf_cond_; } void -TimingArcAttrs::setSdfCondStart(const char *cond) +TimingArcAttrs::setSdfCondStart(const std::string &cond) { - stringDelete(sdf_cond_start_); - sdf_cond_start_ = stringCopy(cond); + sdf_cond_start_ = cond; } void -TimingArcAttrs::setSdfCondEnd(const char *cond) +TimingArcAttrs::setSdfCondEnd(const std::string &cond) { - stringDelete(sdf_cond_end_); - sdf_cond_end_ = stringCopy(cond); + sdf_cond_end_ = cond; } void -TimingArcAttrs::setModeName(const char *name) +TimingArcAttrs::setModeName(const std::string &name) { - stringDelete(mode_name_); - mode_name_ = stringCopy(name); + mode_name_ = name; } void -TimingArcAttrs::setModeValue(const char *value) +TimingArcAttrs::setModeValue(const std::string &value) { - stringDelete(mode_value_); - mode_value_ = stringCopy(value); + mode_value_ = value; } TimingModel * @@ -192,19 +169,20 @@ TimingArc::intrinsicDelay() const TimingArcAttrsPtr TimingArcSet::wire_timing_arc_attrs_ = nullptr; TimingArcSet *TimingArcSet::wire_timing_arc_set_ = nullptr; -TimingArcSet::TimingArcSet(LibertyCell *cell, +TimingArcSet::TimingArcSet(LibertyCell *, LibertyPort *from, LibertyPort *to, LibertyPort *related_out, const TimingRole *role, - TimingArcAttrsPtr attrs) : + TimingArcAttrsPtr attrs, + size_t index) : from_(from), to_(to), related_out_(related_out), role_(role), attrs_(attrs), is_cond_default_(false), - index_(cell->addTimingArcSet(this)), + index_(index), from_arc1_{nullptr, nullptr}, from_arc2_{nullptr, nullptr}, to_arc_{nullptr, nullptr} @@ -247,10 +225,10 @@ TimingArcSet::libertyCell() const return nullptr; } -TimingArcIndex +size_t TimingArcSet::addTimingArc(TimingArc *arc) { - TimingArcIndex arc_index = arcs_.size(); + size_t arc_index = arcs_.size(); // Rise/fall to rise/fall. if (arc_index > RiseFall::index_count * RiseFall::index_count) criticalError(243, "timing arc max index exceeded\n"); @@ -303,6 +281,12 @@ TimingArcSet::setRole(const TimingRole *role) role_ = role; } +void +TimingArcSet::setIndex(size_t index) +{ + index_ = index; +} + void TimingArcSet::setIsCondDefault(bool is_default) { @@ -379,9 +363,9 @@ TimingArcSet::equiv(const TimingArcSet *set1, && LibertyPort::equiv(set1->to(), set2->to()) && set1->role() == set2->role() && FuncExpr::equiv(set1->cond(), set2->cond()) - && stringEqIf(set1->sdfCond(), set2->sdfCond()) - && stringEqIf(set1->sdfCondStart(), set2->sdfCondStart()) - && stringEqIf(set1->sdfCondEnd(), set2->sdfCondEnd()) + && set1->sdfCond() == set2->sdfCond() + && set1->sdfCondStart() == set2->sdfCondStart() + && set1->sdfCondEnd() == set2->sdfCondEnd() && timingArcsEquiv(set1, set2); } @@ -428,36 +412,36 @@ timingArcSetLess(const TimingArcSet *set1, const FuncExpr *cond1 = set1->cond(); const FuncExpr *cond2 = set2->cond(); if (FuncExpr::equiv(cond1, cond2)) { - const char *sdf_cond1 = set1->sdfCond(); - const char *sdf_cond2 = set2->sdfCond(); - if (stringEqIf(sdf_cond1, sdf_cond2)) { - const char *sdf_cond_start1 = set1->sdfCondStart(); - const char *sdf_cond_start2 = set2->sdfCondStart(); - if (stringEqIf(sdf_cond_start1, sdf_cond_start2)) { - const char *sdf_cond_end1 = set1->sdfCondEnd(); - const char *sdf_cond_end2 = set2->sdfCondEnd(); - if (stringEqIf(sdf_cond_end1, sdf_cond_end2)) { - const char *mode_name1 = set1->modeName(); - const char *mode_name2 = set2->modeName(); - if (stringEqIf(mode_name1, mode_name2)) { - const char *mode_value1 = set1->modeValue(); - const char *mode_value2 = set2->modeValue(); - if (stringEqIf(mode_value1, mode_value2)) + const std::string &sdf_cond1 = set1->sdfCond(); + const std::string &sdf_cond2 = set2->sdfCond(); + if (sdf_cond1 == sdf_cond2) { + const std::string &sdf_cond_start1 = set1->sdfCondStart(); + const std::string &sdf_cond_start2 = set2->sdfCondStart(); + if (sdf_cond_start1 == sdf_cond_start2) { + const std::string &sdf_cond_end1 = set1->sdfCondEnd(); + const std::string &sdf_cond_end2 = set2->sdfCondEnd(); + if (sdf_cond_end1 == sdf_cond_end2) { + const std::string &mode_name1 = set1->modeName(); + const std::string &mode_name2 = set2->modeName(); + if (mode_name1 == mode_name2) { + const std::string &mode_value1 = set1->modeValue(); + const std::string &mode_value2 = set2->modeValue(); + if (mode_value1 == mode_value2) return timingArcsLess(set1, set2); else - return stringLessIf(mode_value1, mode_value2); + return mode_value1 < mode_value2; } else - return stringLessIf(mode_name1, mode_name2); + return mode_name1 < mode_name2; } else - return stringLessIf(sdf_cond_end1, sdf_cond_end2); + return sdf_cond_end1 < sdf_cond_end2; } else - return stringLessIf(sdf_cond_start1, sdf_cond_start2); + return sdf_cond_start1 < sdf_cond_start2; } else - return stringLessIf(sdf_cond1, sdf_cond2); + return sdf_cond1 < sdf_cond2; } else return FuncExpr::less(cond1, cond2); diff --git a/power/Power.cc b/power/Power.cc index a10f1199d..9c638c72f 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -998,9 +998,9 @@ Power::seedRegOutputActivities(const Instance *inst, const SequentialSeq &seqs, BfsFwdIterator &bfs) { - for (Sequential *seq : seqs) { - seedRegOutputActivities(inst, seq, seq->output(), false); - seedRegOutputActivities(inst, seq, seq->outputInv(), true); + for (const Sequential &seq : seqs) { + seedRegOutputActivities(inst, seq, seq.output(), false); + seedRegOutputActivities(inst, seq, seq.outputInv(), true); // Enqueue register output pins with functions that reference // the sequential internal pins (IQ, IQN). InstancePinIterator *pin_iter = network_->pinIterator(inst); @@ -1014,8 +1014,8 @@ Power::seedRegOutputActivities(const Instance *inst, Vertex *vertex = graph_->pinDrvrVertex(pin); if (vertex && func - && (func->port() == seq->output() - || func->port() == seq->outputInv())) { + && (func->port() == seq.output() + || func->port() == seq.outputInv())) { debugPrint(debug_, "power_reg", 1, "enqueue reg output %s", vertex->to_string(this).c_str()); bfs.enqueue(vertex); @@ -1028,27 +1028,27 @@ Power::seedRegOutputActivities(const Instance *inst, void Power::seedRegOutputActivities(const Instance *reg, - Sequential *seq, + const Sequential &seq, LibertyPort *output, bool invert) { const Pin *out_pin = network_->findPin(reg, output); if (!hasUserActivity(out_pin)) { - PwrActivity in_activity = evalActivity(seq->data(), reg); + PwrActivity in_activity = evalActivity(seq.data(), reg); float in_density = in_activity.density(); float in_duty = in_activity.duty(); // Default propagates input density/duty thru reg/latch. float out_density = in_density; float out_duty = in_duty; - PwrActivity clk_activity = evalActivity(seq->clock(), reg); + PwrActivity clk_activity = evalActivity(seq.clock(), reg); float clk_density = clk_activity.density(); if (in_density > clk_density / 2) { - if (seq->isRegister()) + if (seq.isRegister()) out_density = 2 * in_duty * (1 - in_duty) * clk_density; - else if (seq->isLatch()) { - PwrActivity clk_activity = evalActivity(seq->clock(), reg); + else if (seq.isLatch()) { + PwrActivity clk_activity = evalActivity(seq.clock(), reg); float clk_duty = clk_activity.duty(); - FuncExpr *clk_func = seq->clock(); + FuncExpr *clk_func = seq.clock(); bool clk_invert = clk_func && clk_func->op() == FuncExpr::Op::not_ && clk_func->left()->op() == FuncExpr::Op::port; @@ -1164,7 +1164,7 @@ Power::findInputInternalPower(const Pin *pin, LibertyCell *scene_cell = cell->sceneCell(scene, min_max); const LibertyPort *scene_port = port->scenePort(scene, min_max); if (scene_cell && scene_port) { - const InternalPowerSeq &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), @@ -1174,8 +1174,8 @@ Power::findInputInternalPower(const Pin *pin, const Pvt *pvt = scene->sdc()->operatingConditions(MinMax::max()); Vertex *vertex = graph_->pinLoadVertex(pin); float internal = 0.0; - for (InternalPower *pwr : internal_pwrs) { - const char *related_pg_pin = pwr->relatedPgPin(); + for (const InternalPower *pwr : internal_pwrs) { + const char *related_pg_pin = pwr->relatedPgPin().c_str(); float energy = 0.0; int rf_count = 0; for (const RiseFall *rf : RiseFall::range()) { @@ -1313,13 +1313,13 @@ Power::findOutputInternalPower(const LibertyPort *to_port, FuncExpr *func = to_port->function(); map pg_duty_sum; - for (InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { + for (const InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { const LibertyPort *from_scene_port = pwr->relatedPort(); if (from_scene_port) { const Pin *from_pin = findLinkPin(inst, from_scene_port); float from_density = findActivity(from_pin).density(); float duty = findInputDuty(inst, func, pwr); - const char *related_pg_pin = pwr->relatedPgPin(); + const char *related_pg_pin = pwr->relatedPgPin().c_str(); // Note related_pg_pin may be null. pg_duty_sum[related_pg_pin] += from_density * duty; } @@ -1328,9 +1328,9 @@ Power::findOutputInternalPower(const LibertyPort *to_port, debugPrint(debug_, "power", 2, " when act/ns duty wgt energy power"); float internal = 0.0; - for (InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { + for (const InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { FuncExpr *when = pwr->when(); - const char *related_pg_pin = pwr->relatedPgPin(); + const char *related_pg_pin = pwr->relatedPgPin().c_str(); float duty = findInputDuty(inst, func, pwr); Vertex *from_vertex = nullptr; bool positive_unate = true; @@ -1386,8 +1386,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, float Power::findInputDuty(const Instance *inst, FuncExpr *func, - InternalPower *pwr) - + const InternalPower *pwr) { const LibertyPort *from_scene_port = pwr->relatedPort(); if (from_scene_port) { @@ -1494,11 +1493,16 @@ Power::findLeakagePower(const Instance *inst, float uncond_leakage = 0.0; bool found_uncond = false; float cond_duty_sum = 0.0; +<<<<<<< for (LeakagePower *leak : *scene_cell->leakagePowers()) { LibertyPort *pg_port = leak->relatedPgPort(); if (pg_port == nullptr || pg_port->pwrGndType() == PwrGndType::primary_power) { FuncExpr *when = leak->when(); +======= + for (const LeakagePower &leak : scene_cell->leakagePowers()) { + FuncExpr *when = leak.when(); +>>>>>>> if (when) { PwrActivity cond_activity = evalActivity(when, inst); float cond_duty = cond_activity.duty(); @@ -1506,19 +1510,24 @@ Power::findLeakagePower(const Instance *inst, cell->name(), leak->relatedPgPort()->name(), when->to_string().c_str(), - leak->power(), + leak.power(), cond_duty); - cond_leakage += leak->power() * cond_duty; - if (leak->power() > 0.0) + cond_leakage += leak.power() * cond_duty; + if (leak.power() > 0.0) cond_duty_sum += cond_duty; found_cond = true; } else { debugPrint(debug_, "power", 2, "leakage %s %s -- %.3e", cell->name(), +<<<<<<< leak->relatedPgPort()->name(), leak->power()); uncond_leakage += leak->power(); +======= + leak.power()); + uncond_leakage += leak.power(); +>>>>>>> found_uncond = true; } } diff --git a/power/Power.hh b/power/Power.hh index 661f6e1f9..1082af53b 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -215,7 +215,7 @@ protected: const MinMax *min_max); void seedActivities(BfsFwdIterator &bfs); void seedRegOutputActivities(const Instance *reg, - Sequential *seq, + const Sequential &seq, LibertyPort *output, bool invert); void seedRegOutputActivities(const Instance *inst, @@ -233,7 +233,7 @@ protected: LibertyPort *findExprOutPort(FuncExpr *expr); float findInputDuty(const Instance *inst, FuncExpr *func, - InternalPower *pwr); + const InternalPower *pwr); float evalDiffDuty(FuncExpr *expr, LibertyPort *from_port, const Instance *inst); diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 0c11320af..85ee1950a 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -3870,7 +3870,7 @@ Sdc::isExceptionEndpoint(const Pin *pin) const // Look for timing checks to the pin witihout using the graph because // it may not exist. LibertyCell *cell = port->libertyCell(); - for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { + for (TimingArcSet *arc_set : cell->timingArcSetsTo(port)) { if (arc_set->role()->isTimingCheck()) { has_checks = true; break; @@ -4046,7 +4046,7 @@ Sdc::hasLibertyCheckTo(const Pin *pin) if (cell) { LibertyPort *port = network_->libertyPort(pin); if (port) { - for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { + for (TimingArcSet *arc_set : cell->timingArcSetsTo(port)) { if (arc_set->role()->isTimingCheckBetween()) return true; } @@ -5645,7 +5645,7 @@ Sdc::wireloadSelection(const MinMax *min_max) // Look for a default. LibertyLibrary *lib = network_->defaultLibertyLibrary(); if (lib) { - WireloadSelection *default_sel = lib->defaultWireloadSelection(); + const WireloadSelection *default_sel = lib->defaultWireloadSelection(); if (default_sel) { sel = default_sel; setWireloadSelection(default_sel, MinMaxAll::all()); @@ -5656,7 +5656,7 @@ Sdc::wireloadSelection(const MinMax *min_max) } void -Sdc::setWireloadSelection(WireloadSelection *selection, +Sdc::setWireloadSelection(const WireloadSelection *selection, const MinMaxAll *min_max) { for (auto mm_index : min_max->rangeIndex()) diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc index 63ecbcc66..c4e875b18 100644 --- a/sdf/ReportAnnotation.cc +++ b/sdf/ReportAnnotation.cc @@ -519,12 +519,12 @@ ReportAnnotated::reportArcs(Vertex *vertex, } else role_name = "delay"; - const char *cond = edge->timingArcSet()->sdfCond(); + const std::string &cond = edge->timingArcSet()->sdfCond(); report_->reportLine(" %-18s %s -> %s %s", role_name, network_->pathName(from_pin), network_->pathName(to_pin), - cond ? cond : ""); + cond.c_str()); i++; } } diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 8f6952969..9e9451306 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -371,16 +371,16 @@ SdfReader::iopath(SdfPortSpec *from_edge, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); TimingArcSet *arc_set = edge->timingArcSet(); - const char *lib_cond = arc_set->sdfCond(); + const std::string &lib_cond = arc_set->sdfCond(); const TimingRole *edge_role = arc_set->role(); - bool cond_use_flag = cond_use_ && cond && lib_cond == nullptr + bool cond_use_flag = cond_use_ && cond && lib_cond.empty() && !(!is_incremental_only_ && in_incremental_); if (edge->from(graph_)->pin() == from_pin && edge_role->sdfRole() == TimingRole::sdfIopath() && (cond_use_flag || (!condelse && condMatch(cond, lib_cond)) // condelse matches the default (unconditional) arc. - || (condelse && lib_cond == nullptr))) { + || (condelse && lib_cond.empty()))) { matched = true; for (TimingArc *arc : arc_set->arcs()) { if ((from_edge->transition() == Transition::riseFall()) @@ -526,8 +526,8 @@ SdfReader::annotateCheckEdges(Pin *data_pin, if (edge->from(graph_)->pin() == clk_pin) { TimingArcSet *arc_set = edge->timingArcSet(); const TimingRole *edge_role = arc_set->role(); - const char *lib_cond_start = arc_set->sdfCondStart(); - const char *lib_cond_end = arc_set->sdfCondEnd(); + 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); if (((!match_generic && edge_role->sdfRole() == sdf_role) @@ -800,15 +800,15 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, bool SdfReader::condMatch(const string *sdf_cond, - const char *lib_cond) + const std::string &lib_cond) { // If the sdf is not conditional it matches any library condition. if (sdf_cond == nullptr) return true; - else if (sdf_cond && lib_cond) { + else if (sdf_cond && !lib_cond.empty()) { // Match sdf_cond and lib_cond ignoring blanks. const char *c1 = sdf_cond->c_str(); - const char *c2 = lib_cond; + const char *c2 = lib_cond.c_str(); char ch1, ch2; do { ch1 = *c1++; diff --git a/sdf/SdfReaderPvt.hh b/sdf/SdfReaderPvt.hh index 52bc1cf38..d5a623ab6 100644 --- a/sdf/SdfReaderPvt.hh +++ b/sdf/SdfReaderPvt.hh @@ -167,7 +167,7 @@ private: Edge *findWireEdge(Pin *from_pin, Pin *to_pin); bool condMatch(const std::string *sdf_cond, - const char *lib_cond); + const std::string &lib_cond); void timingCheck1(const TimingRole *role, Port *data_port, SdfPortSpec *data_edge, diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 23b055cb4..f00950170 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -387,9 +387,9 @@ SdfWriter::writeIopaths(const Instance *inst, writeIopathHeader(); iopath_header = true; } - const char *sdf_cond = edge->timingArcSet()->sdfCond(); - if (sdf_cond) { - gzprintf(stream_, " (COND %s\n", sdf_cond); + const std::string &sdf_cond = edge->timingArcSet()->sdfCond(); + if (!sdf_cond.empty()) { + gzprintf(stream_, " (COND %s\n", sdf_cond.c_str()); gzprintf(stream_, " "); } string from_pin_name = sdfPortName(from_pin); @@ -398,7 +398,7 @@ SdfWriter::writeIopaths(const Instance *inst, from_pin_name.c_str(), to_pin_name.c_str()); writeArcDelays(edge); - if (sdf_cond) + if (!sdf_cond.empty()) gzprintf(stream_, ")"); gzprintf(stream_, ")\n"); } @@ -653,13 +653,13 @@ SdfWriter::writeCheck(Edge *edge, TimingArcSet *arc_set = edge->timingArcSet(); Pin *from_pin = edge->from(graph_)->pin(); Pin *to_pin = edge->to(graph_)->pin(); - const char *sdf_cond_start = arc_set->sdfCondStart(); - const char *sdf_cond_end = arc_set->sdfCondEnd(); + const std::string &sdf_cond_start = arc_set->sdfCondStart(); + const std::string &sdf_cond_end = arc_set->sdfCondEnd(); gzprintf(stream_, " (%s ", sdf_check); - if (sdf_cond_start) - gzprintf(stream_, "(COND %s ", sdf_cond_start); + if (!sdf_cond_start.empty()) + gzprintf(stream_, "(COND %s ", sdf_cond_start.c_str()); string to_pin_name = sdfPortName(to_pin); if (use_data_edge) { @@ -670,13 +670,13 @@ SdfWriter::writeCheck(Edge *edge, else gzprintf(stream_, "%s", to_pin_name.c_str()); - if (sdf_cond_start) + if (!sdf_cond_start.empty()) gzprintf(stream_, ")"); gzprintf(stream_, " "); - if (sdf_cond_end) - gzprintf(stream_, "(COND %s ", sdf_cond_end); + if (!sdf_cond_end.empty()) + gzprintf(stream_, "(COND %s ", sdf_cond_end.c_str()); string from_pin_name = sdfPortName(from_pin); if (use_clk_edge) @@ -686,7 +686,7 @@ SdfWriter::writeCheck(Edge *edge, else gzprintf(stream_, "%s", from_pin_name.c_str()); - if (sdf_cond_end) + if (!sdf_cond_end.empty()) gzprintf(stream_, ")"); gzprintf(stream_, " "); diff --git a/search/FindRegister.cc b/search/FindRegister.cc index f45f23917..a3607f61c 100644 --- a/search/FindRegister.cc +++ b/search/FindRegister.cc @@ -97,7 +97,7 @@ class FindRegVisitor : public StaState bool latches); virtual void visitReg(Instance *inst) = 0; virtual void visitSequential(Instance *inst, - Sequential *seq) = 0; + const Sequential *seq) = 0; void visitFanoutRegs(Vertex *from_vertex, TimingSense from_sense, const RiseFallBoth *clk_rf, @@ -241,17 +241,17 @@ FindRegVisitor::findSequential(const Pin *clk_pin, { has_seqs = false; matches = false; - for (Sequential *seq : cell->sequentials()) { + for (const Sequential &seq : cell->sequentials()) { has_seqs = true; - if ((seq->isRegister() && edge_triggered) - || (seq->isLatch() && latches)) { + if ((seq.isRegister() && edge_triggered) + || (seq.isLatch() && latches)) { if (clk_rf == RiseFallBoth::riseFall()) { - visitSequential(inst, seq); + visitSequential(inst, &seq); matches = true; break; } else { - FuncExpr *clk_func = seq->clock(); + FuncExpr *clk_func = seq.clock(); LibertyPort *port = network_->libertyPort(clk_pin); TimingSense port_sense = clk_func->portTimingSense(port); TimingSense path_sense = pathSenseThru(clk_sense, port_sense); @@ -259,7 +259,7 @@ FindRegVisitor::findSequential(const Pin *clk_pin, && clk_rf == RiseFallBoth::rise()) || (path_sense == TimingSense::negative_unate && clk_rf == RiseFallBoth::fall())) { - visitSequential(inst, seq); + visitSequential(inst, &seq); matches = true; break; } @@ -324,7 +324,7 @@ class FindRegInstances : public FindRegVisitor private: virtual void visitReg(Instance *inst); virtual void visitSequential(Instance *inst, - Sequential *seq); + const Sequential *seq); InstanceSet regs_; }; @@ -348,7 +348,7 @@ FindRegInstances::findRegs(ClockSet *clks, void FindRegInstances::visitSequential(Instance *, - Sequential *) + const Sequential *) { } @@ -385,14 +385,14 @@ class FindRegPins : public FindRegVisitor protected: virtual void visitReg(Instance *inst); virtual void visitSequential(Instance *inst, - Sequential *seq); + const Sequential *seq); virtual bool matchPin(Pin *pin); void visitExpr(FuncExpr *expr, Instance *inst, - Sequential *seq); + const Sequential *seq); // Sequential expressions to find instance pins. - virtual FuncExpr *seqExpr1(Sequential *seq) = 0; - virtual FuncExpr *seqExpr2(Sequential *seq) = 0; + virtual FuncExpr *seqExpr1(const Sequential *seq) = 0; + virtual FuncExpr *seqExpr2(const Sequential *seq) = 0; PinSet pins_; }; @@ -416,7 +416,7 @@ FindRegPins::findPins(ClockSet *clks, void FindRegPins::visitSequential(Instance *inst, - Sequential *seq) + const Sequential *seq) { visitExpr(seqExpr1(seq), inst, seq); visitExpr(seqExpr2(seq), inst, seq); @@ -425,7 +425,7 @@ FindRegPins::visitSequential(Instance *inst, void FindRegPins::visitExpr(FuncExpr *expr, Instance *inst, - Sequential *) + const Sequential *) { if (expr) { LibertyPortSet ports = expr->ports(); @@ -462,8 +462,8 @@ class FindRegDataPins : public FindRegPins private: virtual bool matchPin(Pin *pin); - virtual FuncExpr *seqExpr1(Sequential *seq); - virtual FuncExpr *seqExpr2(Sequential *seq); + virtual FuncExpr *seqExpr1(const Sequential *seq); + virtual FuncExpr *seqExpr2(const Sequential *seq); }; FindRegDataPins::FindRegDataPins(const StaState *sta) : @@ -472,13 +472,13 @@ FindRegDataPins::FindRegDataPins(const StaState *sta) : } FuncExpr * -FindRegDataPins::seqExpr1(Sequential *seq) +FindRegDataPins::seqExpr1(const Sequential *seq) { return seq->data(); } FuncExpr * -FindRegDataPins::seqExpr2(Sequential *) +FindRegDataPins::seqExpr2(const Sequential *) { return nullptr; } @@ -529,8 +529,8 @@ class FindRegClkPins : public FindRegPins private: virtual bool matchPin(Pin *pin); - virtual FuncExpr *seqExpr1(Sequential *seq); - virtual FuncExpr *seqExpr2(Sequential *seq); + virtual FuncExpr *seqExpr1(const Sequential *seq); + virtual FuncExpr *seqExpr2(const Sequential *seq); }; FindRegClkPins::FindRegClkPins(const StaState *sta) : @@ -544,7 +544,7 @@ FindRegClkPins::matchPin(Pin *pin) // Liberty port clock attribute is not present in latches (for nlc18 anyway). LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); - for (TimingArcSet *arc_set : cell->timingArcSets(port, nullptr)) { + for (TimingArcSet *arc_set : cell->timingArcSetsFrom(port)) { const TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ()) @@ -555,13 +555,13 @@ FindRegClkPins::matchPin(Pin *pin) FuncExpr * -FindRegClkPins::seqExpr1(Sequential *seq) +FindRegClkPins::seqExpr1(const Sequential *seq) { return seq->clock(); } FuncExpr * -FindRegClkPins::seqExpr2(Sequential *) +FindRegClkPins::seqExpr2(const Sequential *) { return nullptr; } @@ -587,8 +587,8 @@ class FindRegAsyncPins : public FindRegPins private: virtual bool matchPin(Pin *pin); - virtual FuncExpr *seqExpr1(Sequential *seq) { return seq->clear(); } - virtual FuncExpr *seqExpr2(Sequential *seq) { return seq->preset(); } + virtual FuncExpr *seqExpr1(const Sequential *seq) { return seq->clear(); } + virtual FuncExpr *seqExpr2(const Sequential *seq) { return seq->preset(); } }; FindRegAsyncPins::FindRegAsyncPins(const StaState *sta) : @@ -601,7 +601,7 @@ FindRegAsyncPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); - for (TimingArcSet *arc_set : cell->timingArcSets(port, nullptr)) { + for (TimingArcSet *arc_set : cell->timingArcSetsFrom(port)) { const TimingRole *role = arc_set->role(); if (role == TimingRole::regSetClr()) return true; @@ -631,12 +631,12 @@ class FindRegOutputPins : public FindRegPins private: virtual bool matchPin(Pin *pin); virtual void visitSequential(Instance *inst, - Sequential *seq); + const Sequential *seq); void visitOutput(LibertyPort *port, Instance *inst); // Unused. - virtual FuncExpr *seqExpr1(Sequential *seq); - virtual FuncExpr *seqExpr2(Sequential *seq); + virtual FuncExpr *seqExpr1(const Sequential *seq); + virtual FuncExpr *seqExpr2(const Sequential *seq); }; FindRegOutputPins::FindRegOutputPins(const StaState *sta) : @@ -649,7 +649,7 @@ FindRegOutputPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); - for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { + for (TimingArcSet *arc_set : cell->timingArcSetsTo( port)) { const TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ() @@ -661,7 +661,7 @@ FindRegOutputPins::matchPin(Pin *pin) void FindRegOutputPins::visitSequential(Instance *inst, - Sequential *seq) + const Sequential *seq) { visitOutput(seq->output(), inst); visitOutput(seq->outputInv(), inst); @@ -690,13 +690,13 @@ FindRegOutputPins::visitOutput(LibertyPort *port, } FuncExpr * -FindRegOutputPins::seqExpr1(Sequential *) +FindRegOutputPins::seqExpr1(const Sequential *) { return nullptr; } FuncExpr * -FindRegOutputPins::seqExpr2(Sequential *) +FindRegOutputPins::seqExpr2(const Sequential *) { return nullptr; } diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 40e27ae27..7e3f89b80 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -196,8 +196,7 @@ MakeTimingModel::makePorts() if (network_->isBus(port)) { int from_index = network_->fromIndex(port); int to_index = network_->toIndex(port); - BusDcl *bus_dcl = new BusDcl(port_name, from_index, to_index); - library_->addBusDcl(bus_dcl); + 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); @@ -590,7 +589,7 @@ MakeTimingModel::makeClkTreePaths(LibertyPort *lib_port, const TimingRole *role = (min_max == MinMax::min()) ? TimingRole::clockTreePathMin() : TimingRole::clockTreePathMax(); - lib_builder_->makeClockTreePathArcs(cell_, lib_port, role, min_max, attrs); + lib_builder_->makeClockTreePathArcs(cell_, lib_port, role, attrs); } } @@ -607,7 +606,7 @@ MakeTimingModel::makeScalarCheckModel(float value, ScaleFactorType scale_factor_type, const RiseFall *rf) { - TablePtr table = make_shared(value); + TablePtr table = make_shared
(value); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *table_model = new TableModel(table, tbl_template, @@ -621,8 +620,8 @@ MakeTimingModel::makeGateModelScalar(Delay delay, Slew slew, const RiseFall *rf) { - TablePtr delay_table = make_shared(delayAsFloat(delay)); - TablePtr slew_table = make_shared(delayAsFloat(slew)); + TablePtr delay_table = make_shared
(delayAsFloat(delay)); + TablePtr slew_table = make_shared
(delayAsFloat(slew)); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *delay_model = new TableModel(delay_table, tbl_template, @@ -639,7 +638,7 @@ TimingModel * MakeTimingModel::makeGateModelScalar(Delay delay, const RiseFall *rf) { - TablePtr delay_table = make_shared(delayAsFloat(delay)); + TablePtr delay_table = make_shared
(delayAsFloat(delay)); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *delay_model = new TableModel(delay_table, tbl_template, @@ -664,7 +663,7 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, const LibertyPort *drvr_port = network_->libertyPort(drvr_pin); if (drvr_port) { const LibertyCell *drvr_cell = drvr_port->libertyCell(); - for (TimingArcSet *arc_set : drvr_cell->timingArcSets(nullptr, drvr_port)) { + for (TimingArcSet *arc_set : drvr_cell->timingArcSetsTo(drvr_port)) { for (TimingArc *drvr_arc : arc_set->arcs()) { // Use the first timing arc to simplify life. if (drvr_arc->toEdge()->asRiseFall() == rf) { @@ -692,11 +691,11 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, const TableTemplate *drvr_template = drvr_table->tblTemplate(); const TableAxis *drvr_load_axis = loadCapacitanceAxis(drvr_table); if (drvr_load_axis) { - const FloatSeq *drvr_axis_values = drvr_load_axis->values(); + const FloatSeq &drvr_axis_values = drvr_load_axis->values(); FloatSeq *load_values = new FloatSeq; FloatSeq *slew_values = new FloatSeq; - for (size_t i = 0; i < drvr_axis_values->size(); i++) { - float load_cap = (*drvr_axis_values)[i]; + 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; @@ -708,13 +707,13 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, slew_values->push_back(delayAsFloat(gate_slew)); } - FloatSeq *axis_values = new FloatSeq(*drvr_axis_values); + FloatSeq axis_values = drvr_axis_values; TableAxisPtr load_axis = std::make_shared(TableAxisVariable::total_output_net_capacitance, - axis_values); + std::move(axis_values)); - TablePtr delay_table = make_shared(load_values, load_axis); - TablePtr slew_table = make_shared(slew_values, load_axis); + TablePtr delay_table = make_shared
(load_values, load_axis); + TablePtr slew_table = make_shared
(slew_values, load_axis); TableTemplate *model_template = ensureTableTemplate(drvr_template, load_axis); @@ -748,9 +747,9 @@ MakeTimingModel::ensureTableTemplate(const TableTemplate *drvr_template, string template_name = "template_"; template_name += std::to_string(tbl_template_index_++); - model_template = new TableTemplate(template_name.c_str()); + model_template = library_->makeTableTemplate(template_name, + TableTemplateType::delay); model_template->setAxis1(load_axis); - library_->addTableTemplate(model_template, TableTemplateType::delay); template_map_[drvr_template] = model_template; } return model_template; diff --git a/search/Sim.cc b/search/Sim.cc index 70b6384eb..19e47a2de 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -894,13 +894,13 @@ Sim::isDisabledMode(Edge *edge, is_disabled = false; disable_cond = 0; TimingArcSet *arc_set = edge->timingArcSet(); - const char *mode_name = arc_set->modeName(); - const char *mode_value = arc_set->modeValue(); - if (mode_name && mode_value) { + const std::string &mode_name = arc_set->modeName(); + const std::string &mode_value = arc_set->modeValue(); + if (!mode_name.empty() && !mode_value.empty()) { LibertyCell *cell = network_->libertyCell(inst); - ModeDef *mode_def = cell->findModeDef(mode_name); + const ModeDef *mode_def = cell->findModeDef(mode_name.c_str()); if (mode_def) { - ModeValueDef *value_def = mode_def->findValueDef(mode_value); + const ModeValueDef *value_def = mode_def->findValueDef(mode_value.c_str()); if (value_def) { FuncExpr *cond = value_def->cond(); if (cond) { @@ -908,16 +908,14 @@ Sim::isDisabledMode(Edge *edge, if (cond_value == LogicValue::zero) { // For a mode value to be disabled by having a value of // logic zero one mode value must logic one. - for (const auto [name, value_def] : *mode_def->values()) { - if (value_def) { - FuncExpr *cond1 = value_def->cond(); - if (cond1) { - LogicValue cond_value1 = evalExpr(cond1, inst); - if (cond_value1 == LogicValue::one) { - disable_cond = cond; - is_disabled = true; - break; - } + for (const auto &[name, value_def] : *mode_def->values()) { + FuncExpr *cond1 = value_def.cond(); + if (cond1) { + LogicValue cond_value1 = evalExpr(cond1, inst); + if (cond_value1 == LogicValue::one) { + disable_cond = cond; + is_disabled = true; + break; } } } diff --git a/search/Sta.cc b/search/Sta.cc index 7b7a21825..2257e95fd 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -254,6 +254,7 @@ deleteAllMemory() deleteDelayCalcs(); PortDirection::destroy(); deleteLiberty(); + deleteTmpStrings(); } //////////////////////////////////////////////////////////////// diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index c6da6e22e..cce1989b4 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -760,7 +760,7 @@ WriteSpice::writeWaveformVoltSource(const Pin *pin, volt_index_++, network_->pathName(pin)); streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); - Table1 waveform = drvr_waveform->waveform(slew); + 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++) { float time = delay + time_axis->axisValue(time_index); diff --git a/spice/Xyce.cc b/spice/Xyce.cc index 618b65b18..f198df705 100644 --- a/spice/Xyce.cc +++ b/spice/Xyce.cc @@ -74,7 +74,7 @@ readXyceCsv(const char *csv_filename, } file.close(); TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - new FloatSeq(values[0])); + std::move(values[0])); for (size_t var = 1; var < values.size(); var++) waveforms.emplace_back(new FloatSeq(values[var]), time_axis); } diff --git a/spice/Xyce.hh b/spice/Xyce.hh index c13a918bc..1c27cb4ef 100644 --- a/spice/Xyce.hh +++ b/spice/Xyce.hh @@ -32,7 +32,7 @@ namespace sta { using StdStringSeq = std::vector; -using WaveformSeq = std::vector; +using WaveformSeq = std::vector
; void readXyceCsv(const char *csv_filename, diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index e5b68051a..a7a2d6ebb 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -837,19 +837,21 @@ using namespace sta; $1 = ints; } -%typemap(out) Table1 { - Table1 &table = $1; +%typemap(out) Table { + Table &table = $1; if (table.axis1()) { Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); - for (float f : *table.axis1()->values()) { + for (float f : table.axis1()->values()) { Tcl_Obj *obj = Tcl_NewDoubleObj(f); Tcl_ListObjAppendElement(interp, list1, obj); } Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr); - for (float f : *table.values()) { - Tcl_Obj *obj = Tcl_NewDoubleObj(f); - Tcl_ListObjAppendElement(interp, list2, obj); + if (table.values()) { + for (float f : *table.values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list2, obj); + } } Tcl_ListObjAppendElement(interp, list3, list1); Tcl_ListObjAppendElement(interp, list3, list2); @@ -857,19 +859,23 @@ using namespace sta; } } -%typemap(out) const Table1* { - const Table1 *table = $1; +%typemap(out) const Table* { + const Table *table = $1; Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); if (table) { Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); - for (float f : *table->axis1()->values()) { - Tcl_Obj *obj = Tcl_NewDoubleObj(f); - Tcl_ListObjAppendElement(interp, list1, obj); + if (table->axis1()) { + for (float f : table->axis1()->values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list1, obj); + } } Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr); - for (float f : *table->values()) { - Tcl_Obj *obj = Tcl_NewDoubleObj(f); - Tcl_ListObjAppendElement(interp, list2, obj); + if (table->values()) { + for (float f : *table->values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list2, obj); + } } Tcl_ListObjAppendElement(interp, list3, list1); Tcl_ListObjAppendElement(interp, list3, list2); diff --git a/util/StringUtil.cc b/util/StringUtil.cc index 162373315..a0b8c1547 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -186,6 +186,17 @@ thread_local static std::array tmp_strings; thread_local static std::array tmp_string_lengths; thread_local static int tmp_string_next = 0; +void +deleteTmpStrings() +{ + for (size_t i = 0; i < tmp_string_count; i++) { + stringDelete(tmp_strings[i]); + tmp_string_lengths[i] = 0; + tmp_strings[i] = nullptr; + } + tmp_string_next = 0; +} + static void getTmpString(// Return values. char *&str, From 7cb71fe766835cf5aeff79375e53fb29cdd7b08e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 4 Feb 2026 18:33:32 -0700 Subject: [PATCH 016/181] liberty memory management Signed-off-by: James Cherry --- liberty/LeakagePower.cc | 22 --------------- liberty/LibertyReader.cc | 30 -------------------- liberty/LibertyReaderPvt.hh | 17 ----------- power/Power.cc | 56 +++++++++++++++---------------------- 4 files changed, 23 insertions(+), 102 deletions(-) diff --git a/liberty/LeakagePower.cc b/liberty/LeakagePower.cc index 07e8b7cec..a5751c15f 100644 --- a/liberty/LeakagePower.cc +++ b/liberty/LeakagePower.cc @@ -56,25 +56,3 @@ LeakagePower::~LeakagePower() } } // namespace ->>>>>>> - -namespace sta { - -LeakagePower::LeakagePower(LibertyCell *cell, - LibertyPort *related_pg_port, - FuncExpr *when, - float power) : - cell_(cell), - related_pg_port_(related_pg_port), - when_(when), - power_(power) -{ -} - -LeakagePower::~LeakagePower() -{ - if (when_) - when_->deleteSubexprs(); -} - -} // namespace diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index f2676531a..c75240b01 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -2233,23 +2233,12 @@ void LibertyReader::makeLeakagePowers() { for (LeakagePowerGroup *power_group : leakage_powers_) { -<<<<<<< -======= LibertyPort *related_pg_pin = cell_->findLibertyPort(power_group->relatedPgPin().c_str()); cell_->makeLeakagePower(related_pg_pin, power_group->when(), power_group->power()); delete power_group; } leakage_powers_.clear(); ->>>>>>> - LibertyPort *related_pg_pin = - cell_->findLibertyPort(power_group->relatedPgPin().c_str()); - LeakagePower *leakage = new LeakagePower(cell_, related_pg_pin, power_group->when(), - power_group->power()); - cell_->addLeakagePower(leakage); - delete power_group; - } - leakage_powers_.clear(); } // Record a reference to a function that will be parsed at the end of @@ -6092,27 +6081,8 @@ LeakagePowerGroup::LeakagePowerGroup(int line) : void LeakagePowerGroup::setRelatedPgPin(std::string pin_name) -<<<<<<< -======= -{ - related_pg_pin_ = std::move(pin_name); -} - -void -LeakagePowerGroup::setWhen(FuncExpr *when) { - when_ = when; -} - -void -LeakagePowerGroup::setPower(float power) ->>>>>>> -{ -<<<<<<< related_pg_pin_ = std::move(pin_name); -======= - power_ = power; ->>>>>>> } void diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 9016fc0a8..81109a68d 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -912,23 +912,6 @@ class LeakagePowerGroup { public: LeakagePowerGroup(int line); -<<<<<<< -======= - const std::string &relatedPgPin() const { return related_pg_pin_; } - void setRelatedPgPin(std::string pin_name); - FuncExpr *when() const { return when_; } - void setWhen(FuncExpr *when); - float power() const { return power_; } - void setPower(float power); - -protected: - std::string related_pg_pin_; - FuncExpr *when_; - float power_; - int line_; -}; - ->>>>>>> const std::string &relatedPgPin() const { return related_pg_pin_; } void setRelatedPgPin(std::string pin_name); FuncExpr *when() const { return when_; } diff --git a/power/Power.cc b/power/Power.cc index 9c638c72f..61df780c9 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -1493,45 +1493,35 @@ Power::findLeakagePower(const Instance *inst, float uncond_leakage = 0.0; bool found_uncond = false; float cond_duty_sum = 0.0; -<<<<<<< - for (LeakagePower *leak : *scene_cell->leakagePowers()) { - LibertyPort *pg_port = leak->relatedPgPort(); + for (const LeakagePower &leak : scene_cell->leakagePowers()) { + LibertyPort *pg_port = leak.relatedPgPort(); if (pg_port == nullptr || pg_port->pwrGndType() == PwrGndType::primary_power) { - FuncExpr *when = leak->when(); -======= - for (const LeakagePower &leak : scene_cell->leakagePowers()) { - FuncExpr *when = leak.when(); ->>>>>>> - if (when) { - PwrActivity cond_activity = evalActivity(when, inst); - float cond_duty = cond_activity.duty(); + FuncExpr *when = leak.when(); + if (when) { + PwrActivity cond_activity = evalActivity(when, inst); + float cond_duty = cond_activity.duty(); debugPrint(debug_, "power", 2, "leakage %s %s %s %.3e * %.2f", - cell->name(), - leak->relatedPgPort()->name(), - when->to_string().c_str(), - leak.power(), - cond_duty); - cond_leakage += leak.power() * cond_duty; - if (leak.power() > 0.0) - cond_duty_sum += cond_duty; - found_cond = true; - } - else { + cell->name(), + leak.relatedPgPort()->name(), + when->to_string().c_str(), + leak.power(), + cond_duty); + cond_leakage += leak.power() * cond_duty; + if (leak.power() > 0.0) + cond_duty_sum += cond_duty; + found_cond = true; + } + else { debugPrint(debug_, "power", 2, "leakage %s %s -- %.3e", - cell->name(), -<<<<<<< - leak->relatedPgPort()->name(), - leak->power()); - uncond_leakage += leak->power(); -======= - leak.power()); - uncond_leakage += leak.power(); ->>>>>>> - found_uncond = true; + cell->name(), + leak.relatedPgPort()->name(), + leak.power()); + uncond_leakage += leak.power(); + found_uncond = true; + } } } - } float leakage = 0.0; float cell_leakage; bool cell_leakage_exists; From 4689eecacdb2ac1d2b9ebbacc2c127c39a920a77 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 11 Feb 2026 08:12:12 -0700 Subject: [PATCH 017/181] clang-format Signed-off-by: James Cherry --- .clang-format | 2 +- .cursor/rules/cpp-indentation.mdc | 43 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .cursor/rules/cpp-indentation.mdc diff --git a/.clang-format b/.clang-format index d50ce987e..26ac15c1c 100644 --- a/.clang-format +++ b/.clang-format @@ -10,7 +10,7 @@ AllowAllParametersOfDeclarationOnNextLine: false AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: TopLevel -BinPackArguments: false +BinPackArguments: true # fails BinPackParameters: AlwaysOnePerLine BraceWrapping: diff --git a/.cursor/rules/cpp-indentation.mdc b/.cursor/rules/cpp-indentation.mdc new file mode 100644 index 000000000..54bdb3755 --- /dev/null +++ b/.cursor/rules/cpp-indentation.mdc @@ -0,0 +1,43 @@ +--- +description: C++ function call indentation for OpenSTA +globs: ["**/*.cc", "**/*.hh", "**/*.h"] +alwaysApply: false +--- + +# C++ Function Call Indentation + +## Short Calls — Single Line + +When arguments fit within the column limit (90 chars), keep them on one line: + +```cpp +// ✅ GOOD +adjusted_data_arrival = delaySum(required, data_shift_to_enable_clk, this); + +// ❌ BAD +adjusted_data_arrival = delaySum(required, + data_shift_to_enable_clk, + this); +``` + +## Nested Function Calls — Align Under Inner Call + +When breaking nested calls across lines: +- Indent continuation lines of the inner call under its first argument (align with content after `innerFunc(`). +- Place remaining outer arguments on the same line as the inner call's closing `)`, indented under the outer function. + +```cpp +// ✅ GOOD +required = delayDiff(delaySum(max_delay, + search_->clkPathArrival(disable_path), + this), + margin, this); + +// ❌ BAD +required = delayDiff( + delaySum(max_delay, + search_->clkPathArrival(disable_path), + this), + margin, + this); +``` From 4c157f46eeb78f607156383ec570966093bf8ada Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 11 Feb 2026 08:33:09 -0700 Subject: [PATCH 018/181] InternalPower::related_pg_port string->port Signed-off-by: James Cherry --- include/sta/InternalPower.hh | 6 +++--- include/sta/Liberty.hh | 2 +- liberty/InternalPower.cc | 2 +- liberty/Liberty.cc | 2 +- liberty/LibertyReader.cc | 9 ++++++--- liberty/LibertyReaderPvt.hh | 1 + power/Power.cc | 20 ++++++++++---------- 7 files changed, 23 insertions(+), 19 deletions(-) diff --git a/include/sta/InternalPower.hh b/include/sta/InternalPower.hh index f1f0df4c0..0f1836625 100644 --- a/include/sta/InternalPower.hh +++ b/include/sta/InternalPower.hh @@ -43,7 +43,7 @@ class InternalPower public: InternalPower(LibertyPort *port, LibertyPort *related_port, - const std::string &related_pg_pin, + LibertyPort *related_pg_pin, const std::shared_ptr &when, InternalPowerModels &models); //InternalPower(InternalPower &&other) noexcept; @@ -51,7 +51,7 @@ public: LibertyPort *port() const { return port_; } LibertyPort *relatedPort() const { return related_port_; } FuncExpr *when() const { return when_.get(); } - const std::string &relatedPgPin() const { return related_pg_pin_; } + LibertyPort *relatedPgPin() const { return related_pg_pin_; } float power(const RiseFall *rf, const Pvt *pvt, float in_slew, @@ -61,7 +61,7 @@ public: protected: LibertyPort *port_; LibertyPort *related_port_; - std::string related_pg_pin_; + LibertyPort *related_pg_pin_; std::shared_ptr when_; InternalPowerModels models_; }; diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 41f6d4937..68d43bb30 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -587,7 +587,7 @@ public: TimingArcAttrsPtr attrs); void makeInternalPower(LibertyPort *port, LibertyPort *related_port, - const std::string &related_pg_pin, + LibertyPort *related_pg_pin, const std::shared_ptr &when, InternalPowerModels &models); void makeLeakagePower(LibertyPort *related_pg_port, diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index 11492bf4c..2516a36db 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -35,7 +35,7 @@ namespace sta { InternalPower::InternalPower(LibertyPort *port, LibertyPort *related_port, - const std::string &related_pg_pin, + LibertyPort *related_pg_pin, const std::shared_ptr &when, InternalPowerModels &models) : port_(port), diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index a6e7be96d..d83d06b7d 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1265,7 +1265,7 @@ LibertyCell::makeTimingArcSet(LibertyPort *from, void LibertyCell::makeInternalPower(LibertyPort *port, LibertyPort *related_port, - const std::string &related_pg_pin, + LibertyPort *related_pg_pin, const std::shared_ptr &when, InternalPowerModels &models) { diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index c75240b01..edcde894c 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -2865,6 +2865,9 @@ LibertyReader::makeInternalPowers(LibertyPort *port, InternalPowerGroup *power_group) { int line = power_group->line(); + const std::string &related_pg_pin_name = power_group->relatedPgPin(); + LibertyPort *related_pg_pin = cell_->findLibertyPort(related_pg_pin_name.c_str()); + StringSeq *related_port_names = power_group->relatedPortNames(); if (related_port_names) { for (const char *related_port_name : *related_port_names) { @@ -2872,12 +2875,12 @@ LibertyReader::makeInternalPowers(LibertyPort *port, if (related_port_iter.hasNext()) { debugPrint(debug_, "liberty", 2, " power %s -> %s", related_port_name, port->name()); - makeInternalPowers(port, related_port_name, related_port_iter, power_group); + makeInternalPowers(port, related_port_name, related_port_iter, + related_pg_pin, power_group); } } } else { - const std::string &related_pg_pin = power_group->relatedPgPin(); if (port->hasMembers()) { LibertyPortMemberIterator bit_iter(port); while (bit_iter.hasNext()) { @@ -2896,9 +2899,9 @@ void LibertyReader::makeInternalPowers(LibertyPort *port, const char *related_port_name, PortNameBitIterator &related_port_iter, + LibertyPort *related_pg_pin, InternalPowerGroup *power_group) { - const std::string &related_pg_pin = power_group->relatedPgPin(); const auto &when = power_group->when(); InternalPowerModels &models = power_group->models(); if (related_port_iter.size() == 1 && !port->hasMembers()) { diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 81109a68d..b4a8e6044 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -423,6 +423,7 @@ public: virtual void makeInternalPowers(LibertyPort *port, const char *related_port_name, PortNameBitIterator &related_port_iter, + LibertyPort *related_pg_pin, InternalPowerGroup *power_group); // AOCV attributes. diff --git a/power/Power.cc b/power/Power.cc index 286b43e75..db3ef0538 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -1176,7 +1176,7 @@ Power::findInputInternalPower(const Pin *pin, Vertex *vertex = graph_->pinLoadVertex(pin); float internal = 0.0; for (const InternalPower *pwr : internal_pwrs) { - const char *related_pg_pin = pwr->relatedPgPin().c_str(); + LibertyPort *related_pg_pin = pwr->relatedPgPin(); float energy = 0.0; int rf_count = 0; for (const RiseFall *rf : RiseFall::range()) { @@ -1214,7 +1214,7 @@ Power::findInputInternalPower(const Pin *pin, duty, energy, port_internal, - related_pg_pin ? related_pg_pin : "no pg_pin"); + related_pg_pin ? related_pg_pin->name() : "no pg_pin"); internal += port_internal; } result.incrInternal(internal); @@ -1313,14 +1313,14 @@ Power::findOutputInternalPower(const LibertyPort *to_port, const LibertyPort *to_scene_port = to_port->scenePort(scene, min_max); FuncExpr *func = to_port->function(); - 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) { const Pin *from_pin = findLinkPin(inst, from_scene_port); float from_density = findActivity(from_pin).density(); float duty = findInputDuty(inst, func, pwr); - const char *related_pg_pin = pwr->relatedPgPin().c_str(); + LibertyPort *related_pg_pin = pwr->relatedPgPin(); // Note related_pg_pin may be null. pg_duty_sum[related_pg_pin] += from_density * duty; } @@ -1331,7 +1331,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, float internal = 0.0; for (const InternalPower *pwr : scene_cell->internalPowers(to_scene_port)) { FuncExpr *when = pwr->when(); - const char *related_pg_pin = pwr->relatedPgPin().c_str(); + LibertyPort *related_pg_pin = pwr->relatedPgPin(); float duty = findInputDuty(inst, func, pwr); Vertex *from_vertex = nullptr; bool positive_unate = true; @@ -1378,7 +1378,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, weight, energy, port_internal, - related_pg_pin ? related_pg_pin : "no pg_pin"); + related_pg_pin ? related_pg_pin->name() : "no pg_pin"); internal += port_internal; } result.incrInternal(internal); @@ -1517,13 +1517,13 @@ Power::findLeakagePower(const Instance *inst, LibertyCell *scene_cell = cell->sceneCell(scene, MinMax::max()); std::map leakage_summaries; Sim *sim = scene->mode()->sim(); - for (const LeakagePower &leak : scene_cell->leakagePowers()) { - LibertyPort *pg_port = leak.relatedPgPort(); + for (const LeakagePower &pwr : scene_cell->leakagePowers()) { + LibertyPort *pg_port = pwr.relatedPgPort(); if (pg_port == nullptr || pg_port->pwrGndType() == PwrGndType::primary_power) { LeakageSummary &sum = leakage_summaries[pg_port]; - float leakage = leak.power(); - FuncExpr *when = leak.when(); + float leakage = pwr.power(); + FuncExpr *when = pwr.when(); if (when) { LogicValue when_value = sim->evalExpr(when, inst); if (when_value == LogicValue::one) { From 52b49f823f4d4d329f5698371d2433b4530c9531 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 11 Feb 2026 12:41:41 -0700 Subject: [PATCH 019/181] centos docker Signed-off-by: James Cherry --- CMakeLists.txt | 14 ++++++++++++++ Dockerfile.centos7 | 7 +++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ce05b3ac..60932b82b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -322,6 +322,20 @@ bison_target(SaifParse ${STA_HOME}/power/SaifParse.yy ${CMAKE_CURRENT_BINARY_DIR}/SaifParse.cc) add_flex_bison_dependency(SaifLex SaifParse) +# Suppress -Wsign-compare in flex-generated code (yyleng vs int loop counter). +# Only needed with older GCC (e.g. CentOS 7 stock 4.8.5); newer GCC/flex handle it. +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_source_files_properties( + ${FLEX_VerilogLex_OUTPUTS} + ${FLEX_LibertyLex_OUTPUTS} + ${FLEX_LibExprLex_OUTPUTS} + ${FLEX_SdfLex_OUTPUTS} + ${FLEX_SpefLex_OUTPUTS} + ${FLEX_SaifLex_OUTPUTS} + PROPERTIES COMPILE_FLAGS "-Wno-sign-compare" + ) +endif() + ################################################################ set(STA_TCL_INIT ${CMAKE_CURRENT_BINARY_DIR}/StaTclInitVar.cc) diff --git a/Dockerfile.centos7 b/Dockerfile.centos7 index abe822119..8857a99ce 100644 --- a/Dockerfile.centos7 +++ b/Dockerfile.centos7 @@ -14,6 +14,8 @@ RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \ && yum install -y devtoolset-11 wget cmake3 make eigen3-devel tcl swig3 flex zlib-devel valgrind \ && yum clean -y all +RUN ln -sf /usr/bin/cmake3 /usr/bin/cmake + # Download Bison RUN wget https://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.gz && \ tar -xvf bison-3.8.2.tar.gz && \ @@ -60,8 +62,9 @@ WORKDIR /OpenSTA RUN rm -rf build && mkdir build RUN source /opt/rh/devtoolset-11/enable && \ cd build && \ - cmake3 .. && \ - make -j`nproc` + cmake .. && \ + # LTO fails with -j + make # Run sta on entry ENTRYPOINT ["/OpenSTA/build/sta"] From bb23f157321642796fcea3a8ba5c2603e58c5d7f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 13 Feb 2026 08:01:26 -0700 Subject: [PATCH 020/181] Sdc::deleteDeratingFactors resolves #381 Signed-off-by: James Cherry --- sdc/Sdc.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 85ee1950a..9fec7507d 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -702,9 +702,9 @@ Sdc::swapDeratingFactors(Sdc *sdc1, void Sdc::deleteDeratingFactors() { - deleteContents(net_derating_factors_); - deleteContents(inst_derating_factors_); - deleteContents(cell_derating_factors_); + deleteContentsClear(net_derating_factors_); + deleteContentsClear(inst_derating_factors_); + deleteContentsClear(cell_derating_factors_); delete derating_factors_; derating_factors_ = nullptr; From 54a3cc0721765abc82b6bdf7020de65527cdfe45 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 13 Feb 2026 09:02:22 -0700 Subject: [PATCH 021/181] revert bb23f157 Signed-off-by: James Cherry --- sdc/Sdc.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 9fec7507d..85ee1950a 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -702,9 +702,9 @@ Sdc::swapDeratingFactors(Sdc *sdc1, void Sdc::deleteDeratingFactors() { - deleteContentsClear(net_derating_factors_); - deleteContentsClear(inst_derating_factors_); - deleteContentsClear(cell_derating_factors_); + deleteContents(net_derating_factors_); + deleteContents(inst_derating_factors_); + deleteContents(cell_derating_factors_); delete derating_factors_; derating_factors_ = nullptr; From aca993e9eee97732fcc7222d64b8e12ed9acd009 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 17 Feb 2026 11:03:05 -0700 Subject: [PATCH 022/181] Sta::clearNonSdc clear sim resolves #382 Signed-off-by: James Cherry --- search/SearchPred.cc | 2 +- search/Sta.cc | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/search/SearchPred.cc b/search/SearchPred.cc index e2654efdf..d9e829cd1 100644 --- a/search/SearchPred.cc +++ b/search/SearchPred.cc @@ -135,7 +135,7 @@ SearchPred0::searchThru(Edge *edge, bool SearchPred0::searchTo(const Vertex *to_vertex, - const Mode *mode) const + const Mode *mode) const { return !mode->sim()->isConstant(to_vertex); } diff --git a/search/Sta.cc b/search/Sta.cc index 2257e95fd..450a6130a 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -173,9 +173,9 @@ StaSimObserver::valueChangeAfter(const Pin *pin) { Vertex *vertex = graph_->pinDrvrVertex(pin); if (vertex) { - search_->arrivalInvalid(vertex); - search_->requiredInvalid(vertex); - search_->endpointInvalid(vertex); + search_->arrivalInvalid(vertex); + search_->requiredInvalid(vertex); + search_->endpointInvalid(vertex); } } @@ -545,7 +545,6 @@ void Sta::clearNonSdc() { // Sdc holds search filter, so clear search first. - search_->clear(); levelize_->clear(); deleteParasitics(); graph_delay_calc_->clear(); @@ -557,8 +556,11 @@ Sta::clearNonSdc() clk_skews_->clear(); // scenes are NOT cleared because they are used to index liberty files. - for (Mode *mode : modes_) + for (Mode *mode : modes_) { mode->clkNetwork()->clkPinsInvalid(); + mode->sim()->clear(); + } + search_->clear(); delete graph_; graph_ = nullptr; From d19d99dc195d880084136d44b7df5ce4f080ba20 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 17 Feb 2026 11:03:17 -0700 Subject: [PATCH 023/181] init Parasitics::coupling_cap_factor_ resolves #383 --- parasitics/Parasitics.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index cd7da4e3a..d9a04ffed 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -39,7 +39,8 @@ namespace sta { Parasitics::Parasitics(StaState *sta) : - StaState(sta) + StaState(sta), + coupling_cap_factor_(1.0) { } From b69645ed44dd84d10cffe3ff5551f85183f8ed86 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 24 Feb 2026 14:48:16 -0800 Subject: [PATCH 024/181] make_scene missing mode error resolves #384 Signed-off-by: James Cherry --- search/Sta.cc | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/search/Sta.cc b/search/Sta.cc index 450a6130a..91491228d 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2498,27 +2498,31 @@ Sta::makeScene(const std::string &name, const std::string &spef_max_file) { Mode *mode = findMode(mode_name); - Parasitics *parasitics_default = findParasitics("default"); - Parasitics *parasitics_min = parasitics_default; - Parasitics *parasitics_max = parasitics_default; - if (!spef_min_file.empty() && !spef_max_file.empty()) { - 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()); - } + if (mode) { + Parasitics *parasitics_default = findParasitics("default"); + Parasitics *parasitics_min = parasitics_default; + Parasitics *parasitics_max = parasitics_default; + if (!spef_min_file.empty() && !spef_max_file.empty()) { + 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()); + } - mode->sdc()->makeSceneBefore(); - Scene *scene = makeScene(name, mode, parasitics_min, parasitics_max); - updateComponentsState(); - if (graph_) - graph_->makeSceneAfter(); - updateSceneLiberty(scene, liberty_min_files, MinMax::min()); - updateSceneLiberty(scene, liberty_max_files, MinMax::max()); - cmd_scene_ = scene; + mode->sdc()->makeSceneBefore(); + Scene *scene = makeScene(name, mode, parasitics_min, parasitics_max); + updateComponentsState(); + if (graph_) + graph_->makeSceneAfter(); + updateSceneLiberty(scene, liberty_min_files, MinMax::min()); + updateSceneLiberty(scene, liberty_max_files, MinMax::max()); + cmd_scene_ = scene; + } + else + report_->error(1572, "mode %s not found.", mode_name.c_str()); } Scene * From 795c190edbdb8adfbb459a0a4ce97799ba219537 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 24 Feb 2026 15:07:33 -0800 Subject: [PATCH 025/181] ClkLatency::findClkDelays unclocked regs resolves #385 Signed-off-by: James Cherry --- search/ClkLatency.cc | 6 +++--- search/Sta.cc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc index 5fc244e82..09d09bd56 100644 --- a/search/ClkLatency.cc +++ b/search/ClkLatency.cc @@ -162,14 +162,14 @@ ClkLatency::findClkDelays(ConstClockSeq &clks, VertexPathIterator path_iter(clk_vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); - const ClockEdge *path_clk_edge = path->clkEdge(this); const Scene *path_scene = path->scene(this); - const Clock *path_clk = path_clk_edge->clock(); - if (path_clk_edge + const Clock *path_clk = path->clock(this); + 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); ClkDelays &clk_delays = delays_itr->second; const RiseFall *clk_rf = path_clk_edge->transition(); const MinMax *min_max = path->minMax(this); diff --git a/search/Sta.cc b/search/Sta.cc index 91491228d..060697e8c 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -3107,7 +3107,7 @@ Sta::required(Vertex *vertex, Slack Sta::slack(const Net *net, - const MinMax *min_max) + const MinMax *min_max) { ensureGraph(); Slack min_slack = MinMax::min()->initValue(); @@ -3129,7 +3129,7 @@ Slack Sta::slack(const Pin *pin, const RiseFallBoth *rf, const SceneSeq &scenes, - const MinMax *min_max) + const MinMax *min_max) { ensureGraph(); Vertex *vertex, *bidirect_drvr_vertex; From 79ebc3469b4833733dee308ac49a7ed53d986481 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 24 Feb 2026 16:45:03 -0800 Subject: [PATCH 026/181] report_path -path_group w/-default resolves openroad issue #9439 Signed-off-by: James Cherry --- search/PathGroup.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 6ac127f03..ca842db5f 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -436,8 +436,10 @@ PathGroups::pathGroups(const PathEnd *path_end) const // GroupPaths have precedence. else if (!group_paths.empty()) { for (ExceptionPath *group_path : group_paths) { - if (group_path->isDefault()) - path_groups.push_back(path_delay_[mm_index]); + if (group_path->isDefault()) { + if (path_delay_[mm_index]) + path_groups.push_back(path_delay_[mm_index]); + } else { const char *group_name = group_path->name(); PathGroup *group = findPathGroup(group_name, min_max); From d1bfb1df2ec1959820262f6be7fc62fb0a900249 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 25 Feb 2026 07:29:18 -0800 Subject: [PATCH 027/181] define_scene use filename or library name resolves #386 Signed-off-by: James Cherry --- doc/ChangeLog.txt | 7 + doc/OpenSTA.fodt | 2584 +++++++++++++++++++------------------ doc/OpenSTA.pdf | Bin 1424807 -> 1402478 bytes examples/multi_corner.tcl | 6 +- include/sta/Sta.hh | 5 +- network/Network.cc | 1 - search/Sta.cc | 48 +- 7 files changed, 1327 insertions(+), 1324 deletions(-) diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index 9c8c82209..ee123999f 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -3,6 +3,13 @@ OpenSTA Timing Analyzer Release Notes This file summarizes user visible changes for each release. +2025/02/24 +---------- + +The define_scene -library argument now takes a the library name or a +library filename. If a filename is used, it must be the same as the +filename used to read the library with read_liberty. + Release 3.0.0 2025/11/26 ------------------------ diff --git a/doc/OpenSTA.fodt b/doc/OpenSTA.fodt index 39de2a674..344e45a6a 100644 --- a/doc/OpenSTA.fodt +++ b/doc/OpenSTA.fodt @@ -1,11 +1,11 @@ - Parallax STA documentationJames Cherry5112025-03-17T12:59:52.4638705382010-07-31T21:07:002025-12-25T18:12:25.212818000P122DT13H54M10SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse + Parallax STA documentationJames Cherry5142025-03-17T12:59:52.4638705382010-07-31T21:07:002026-02-25T07:28:47.891834000P123DT1H12M13SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse - 2475563 - 0 + 348148 + 534 19290 17736 true @@ -13,12 +13,12 @@ view2 - 3041 - 2477826 - 0 - 2475563 - 19288 - 2493298 + 8324 + 356965 + 534 + 348148 + 19823 + 365882 0 1 false @@ -89,7 +89,7 @@ false true false - 26291860 + 26480554 0 false @@ -536,10 +536,10 @@ - + - + @@ -5314,1041 +5314,1047 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -6484,82 +6490,82 @@ 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 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. + 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. + 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 + 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. + 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. + 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. + 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 + 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 + > 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_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. + 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. + 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. + 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. + 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. + 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 + Commands - all_clocks + all_clocks @@ -6572,7 +6578,7 @@ - all_inputs + all_inputs [-no_clocks] @@ -6594,7 +6600,7 @@ - all_outputs + all_outputs @@ -6607,15 +6613,15 @@ - 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. @@ -6678,22 +6684,22 @@ - 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. @@ -6701,7 +6707,7 @@ - -unconstrained_endpoints + -unconstrained_endpoints Check path endpoints for timing constraints (timing check or set_output_delay). @@ -6709,7 +6715,7 @@ - -multiple_clock + -multiple_clock Check register/latch clock pins for multiple clocks. @@ -6725,7 +6731,7 @@ - -no_input_delay + -no_input_delay Check for inputs that do not have a set_input_delay command. @@ -6748,16 +6754,16 @@ - 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 @@ -6785,7 +6791,7 @@ - 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. @@ -6827,7 +6833,7 @@ -add - Add this clock to the clocks on pin_list. + Add this clock to the clocks on pin_list. @@ -6840,7 +6846,7 @@ 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 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. @@ -6853,10 +6859,10 @@ - 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 @@ -6872,36 +6878,36 @@ -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). @@ -6917,15 +6923,15 @@ - -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. @@ -6936,7 +6942,7 @@ -add - Add this clock to the existing clocks on pin_list. + Add this clock to the existing clocks on pin_list. @@ -6944,7 +6950,7 @@ pin_list - A list of pins driven by the generated clock. + A list of pins driven by the generated clock. @@ -6969,10 +6975,10 @@ - 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 @@ -6982,7 +6988,7 @@ - current_design + current_design [design] @@ -6995,7 +7001,7 @@ - current_instance + current_instance [instance] @@ -7016,23 +7022,23 @@ - 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. @@ -7047,17 +7053,17 @@ - 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. - 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 @@ -7075,7 +7081,7 @@ - delete_from_list + delete_from_list list objects @@ -7086,7 +7092,7 @@ list - A list of objects. + A list of objects. @@ -7102,15 +7108,15 @@ + - delete_generated_clock + delete_generated_clock - [-all] clocks + [-all] clocks - clocks @@ -7126,7 +7132,7 @@ - delete_instance + delete_instance instance @@ -7137,7 +7143,7 @@ instance - Instance to delete. + Instance to delete. @@ -7147,7 +7153,7 @@ - delete_net + delete_net net @@ -7155,7 +7161,7 @@ - net + net Net to delete. @@ -7168,10 +7174,10 @@ - disconnect_pin + disconnect_pin - netport | pin | -all + netport | pin | -all @@ -7213,7 +7219,7 @@ - elapsed_run_time + elapsed_run_time @@ -7227,82 +7233,82 @@ - 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. + Return rising paths through a list of instances, pins or nets. - -fall_through through_list + -fall_through through_list - Return falling paths through a list of instances, pins or nets. + Return falling paths through a list of instances, pins or nets. - -to to_list + -to to_list - Return paths to a list of clocks, instances, ports or pins. + Return paths to a list of clocks, instances, ports or pins. - -rise_to to_list + -rise_to to_list - Return rising paths to a list of clocks, instances, ports or pins. + Return rising paths to a list of clocks, instances, ports or pins. - -fall_to to_list + -fall_to to_list - Return falling paths to a list of clocks, instances, ports or pins. + Return falling paths to a list of clocks, instances, ports or pins. @@ -7310,7 +7316,7 @@ -unconstrained - Report unconstrained paths also. + Report unconstrained paths also. @@ -7372,18 +7378,18 @@ - -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. @@ -7396,7 +7402,7 @@ - -scene scene + -scene scene Return paths for one process corner. @@ -7404,18 +7410,18 @@ - -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. @@ -7423,15 +7429,15 @@ -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. @@ -7442,10 +7448,10 @@ - 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] @@ -7458,18 +7464,18 @@ - -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. @@ -7477,7 +7483,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -7498,7 +7504,7 @@ - -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. @@ -7519,10 +7525,10 @@ - get_clocks + get_clocks - [-regexp][-nocase][-filter expr][-quiet]patterns + [-regexp][-nocase][-filter expr][-quiet]patterns @@ -7530,7 +7536,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -7544,10 +7550,10 @@ - -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. @@ -7573,15 +7579,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. @@ -7589,7 +7595,7 @@ - -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. @@ -7597,7 +7603,7 @@ - -only_cells + -only_cells Return the instances connected to the pins in the fanin. @@ -7605,7 +7611,7 @@ - -startpoints_only + -startpoints_only Only return pins that are startpoints. @@ -7613,7 +7619,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7629,15 +7635,15 @@ - -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. @@ -7645,7 +7651,7 @@ - -trace_arcs all + -trace_arcs all Trace through all arcs, including disabled ones. @@ -7659,15 +7665,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. @@ -7675,7 +7681,7 @@ - -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. @@ -7683,7 +7689,7 @@ - -only_cells + -only_cells Return the instances connected to the pins in the fanout. @@ -7691,7 +7697,7 @@ - -endpoints_only + -endpoints_only Only return pins that are endpoints. @@ -7699,7 +7705,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7715,15 +7721,15 @@ - -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. @@ -7731,7 +7737,7 @@ - -trace_arcs all + -trace_arcs all Trace through all arcs, including disabled ones. @@ -7744,7 +7750,7 @@ - get_full_name + get_full_name object @@ -7769,12 +7775,12 @@ 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. @@ -7785,15 +7791,15 @@ -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. @@ -7801,7 +7807,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -7822,7 +7828,7 @@ - patterns + patterns A list of library cell name patterns of the form library_name/cell_name. @@ -7835,18 +7841,18 @@ - 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. @@ -7854,16 +7860,16 @@ -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. @@ -7871,7 +7877,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -7905,18 +7911,18 @@ - 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. @@ -7924,7 +7930,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -7952,17 +7958,17 @@ - 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] @@ -7978,15 +7984,15 @@ -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. @@ -7994,7 +8000,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -8036,7 +8042,7 @@ - get_name + get_name object @@ -8058,10 +8064,10 @@ - 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] @@ -8077,15 +8083,15 @@ -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. @@ -8109,7 +8115,7 @@ -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. @@ -8129,19 +8135,19 @@ - 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. @@ -8149,7 +8155,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -8170,10 +8176,10 @@ - -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. @@ -8191,18 +8197,18 @@ - 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 @@ -8210,7 +8216,7 @@ 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. @@ -8224,27 +8230,27 @@ The properties for different objects types are shown below. cell (SDC lib_cell) - base_namefilenamefull_namelibraryname + base_namefilenamefull_namelibraryname clock - full_nameis_generatedis_propagatedis_virtualnameperiodsources + 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 + 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 + filename (Liberty library only)namefull_name net - full_namename + full_namename path (PathEnd) endpointendpoint_clockendpoint_clock_pinslackstartpointstartpoint_clockpoints pin - activity (activity in transitions per second, duty cycle, origin)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 + activity (activity in transitions per second, duty cycle, origin)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 + activityslew_max_fallslew_max_riseslew_min_fallslew_min_risedirectionfull_nameliberty_portnameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise point (PathRef) arrivalpinrequiredslack @@ -8252,15 +8258,15 @@ - get_scenes + get_scenes - [-mode mode_name]scene_name + [-mode mode_name]scene_name - mode_name + mode_name Get the scenes for mode_name. @@ -8268,74 +8274,74 @@ - scene_name + scene_name 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. @@ -8343,7 +8349,7 @@ - -weight weight + -weight weight Not supported. @@ -8351,7 +8357,7 @@ - -critical_range range + -critical_range range Not supported. @@ -8359,74 +8365,74 @@ - -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. @@ -8434,7 +8440,7 @@ -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. @@ -8444,15 +8450,15 @@ - 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. @@ -8460,7 +8466,7 @@ - -verbose|-v + -verbose|-v Print each command before evaluating it as well as the result it returns. @@ -8476,7 +8482,7 @@ - > log_filename + > log_filename Redirect command output to log_filename. @@ -8484,7 +8490,7 @@ - >> log_filename + >> log_filename Redirect command output and append log_filename. @@ -8492,29 +8498,29 @@ 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. + 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. @@ -8529,7 +8535,7 @@ - make_instance + make_instance inst_pathlib_cell @@ -8553,13 +8559,13 @@ - 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 @@ -8580,18 +8586,18 @@ - 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. @@ -8615,12 +8621,12 @@ 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. @@ -8629,18 +8635,18 @@ - 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 ‘/’. @@ -8648,26 +8654,26 @@ 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. @@ -8699,15 +8705,15 @@ - read_sdf + read_sdf - [-scene scene][-unescaped_dividers]filename + [-scene scene][-unescaped_dividers]filename - scene + scene Scene delays to annotate. @@ -8715,7 +8721,7 @@ - -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. @@ -8730,7 +8736,7 @@ - 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. @@ -8740,10 +8746,10 @@ - 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 @@ -8759,7 +8765,7 @@ path - Hierarchical block instance path to annotate with parasitics. + Hierarchical block instance path to annotate with parasitics. @@ -8775,7 +8781,7 @@ ‑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. @@ -8787,29 +8793,29 @@ - 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 + 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. + 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 ‘/’. @@ -8828,17 +8834,17 @@ 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 @@ -8853,8 +8859,8 @@ - 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. @@ -8862,16 +8868,16 @@ - replace_cell + replace_cell - instance_listreplacement_cell + instance_listreplacement_cell - instance_list + instance_list A list of instances to swap the cell. @@ -8879,57 +8885,57 @@ - replacement_cell + replacement_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. @@ -8937,7 +8943,7 @@ - -hold + -hold Report annotated hold checks. @@ -8945,7 +8951,7 @@ - -recovery + -recovery Report annotated recovery checks. @@ -8953,7 +8959,7 @@ - -removal + -removal Report annotated removal checks. @@ -8961,7 +8967,7 @@ - -nochange + -nochange Report annotated nochange checks. @@ -8969,7 +8975,7 @@ - -width + -width Report annotated width checks. @@ -8977,7 +8983,7 @@ - -period + -period Report annotated period checks. @@ -8985,7 +8991,7 @@ - -max_skew + -max_skew Report annotated max skew checks. @@ -8994,26 +9000,26 @@ - -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. @@ -9025,21 +9031,21 @@ - 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. @@ -9047,7 +9053,7 @@ - -net + -net Report annotated internal net delays. @@ -9055,7 +9061,7 @@ - -from_in_ports + -from_in_ports Report annotated delays from input ports. @@ -9063,7 +9069,7 @@ - -to_out_ports + -to_out_ports Report annotated delays to output ports. @@ -9071,26 +9077,26 @@ - -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. @@ -9102,90 +9108,90 @@ - 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. @@ -9193,7 +9199,7 @@ -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. @@ -9254,7 +9260,7 @@ - -group_path_count path_count + -group_path_count path_count The number of paths to report in each path group. The default is 1. @@ -9262,7 +9268,7 @@ - -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. @@ -9273,15 +9279,15 @@ ‑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. @@ -9289,7 +9295,7 @@ 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. @@ -9319,10 +9325,10 @@ - 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. @@ -9375,10 +9381,10 @@ - -format json + -format json - Report in json format. -fields is ignored. + Report in json format. -fields is ignored. @@ -9386,7 +9392,7 @@ 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 @@ -9406,7 +9412,7 @@ - The report_checks command reports paths in the design. Paths are reported in groups by capture clock, unclocked path delays, gated clocks and unconstrained. + 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. @@ -9414,10 +9420,10 @@ - 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][-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] @@ -9430,7 +9436,7 @@ - -violators + -violators Report all violated timing and design rule constraints. @@ -9446,18 +9452,18 @@ - -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. @@ -9551,7 +9557,7 @@ - -digits digits + -digits digits The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. @@ -9572,10 +9578,10 @@ - report_clock_latency + report_clock_latency - [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] + [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] @@ -9583,7 +9589,7 @@ clocks - The clocks to report. The default value is all c + The clocks to report. The default value is all c @@ -9591,7 +9597,7 @@ 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. @@ -9617,10 +9623,10 @@ - report_clock_min_period + report_clock_min_period - [-clocks clocks][-scenes scenes][-include_port_paths] + [-clocks clocks][-scenes scenes][-include_port_paths] @@ -9640,17 +9646,17 @@ - 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] @@ -9668,15 +9674,15 @@ - 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. @@ -9695,7 +9701,7 @@ 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. @@ -9703,7 +9709,7 @@ scenes - Report clocks for scenes. The default value is all scenes. + Report clocks for scenes. The default value is all scenes. @@ -9716,23 +9722,23 @@ - -digits digits + -digits digits 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] @@ -9740,7 +9746,7 @@ 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. @@ -9748,7 +9754,7 @@ to_pin - Report delay calculations for timing arcs to instance output pin to_pin. + Report delay calculations for timing arcs to instance output pin to_pin. @@ -9756,29 +9762,29 @@ scene - Report paths for process scene. The -scene keyword is required if more than one process corner is defined. + Report paths for process scene. The -scene keyword is required if more than one process corner is defined. - -min + -min - Report delay calculation for min delays. + Report delay calculation for min delays. - -max + -max - Report delay calculation for max delays. + Report delay calculation for max delays. - -digits digits + -digits digits The number of digits after the decimal point to report. The default is sta_report_default_digits. @@ -9791,7 +9797,7 @@ - report_disabled_edges + report_disabled_edges @@ -9799,13 +9805,13 @@ 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] @@ -9813,31 +9819,31 @@ - -from from_pin + -from from_pin - Report edges/timing arcs from pin from_pin. + Report edges/timing arcs from pin from_pin. - -to to_pin + -to to_pin - Report edges/timing arcs to pin to_pin. + Report edges/timing arcs to pin to_pin. - 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] @@ -9855,10 +9861,10 @@ - report_lib_cell + report_lib_cell - cell_name[> filename][>> filename] + cell_name[> filename][>> filename] @@ -9871,21 +9877,21 @@ - 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 digits The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. @@ -9906,10 +9912,10 @@ - report_parasitic_annotation + report_parasitic_annotation - [-report_unannotated][> filename][>> filename] + [-report_unannotated][> filename][>> filename] @@ -9927,49 +9933,49 @@ - 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 + -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. + 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. - -highest_power_instances count + -highest_power_instances count - Report the power for the count highest power instances. + Report the power for the count highest power instances. - -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 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]pin @@ -9977,7 +9983,7 @@ scenes - Report slews for process for scenes process corners.. + Report slews for process for scenes process corners.. @@ -9995,10 +10001,10 @@ - report_tns + report_tns - [-min][-max][-digits digits] + [-min][-max][-digits digits] @@ -10006,7 +10012,7 @@ -max - Report the total max/setup slack. + Report the total max/setup slack. @@ -10014,12 +10020,12 @@ -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. @@ -10032,24 +10038,24 @@ - report_units + report_units - Report the units used for command arguments and reporting. + 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] @@ -10057,7 +10063,7 @@ -max - Report the worst max/setup slack. + Report the worst max/setup slack. @@ -10065,12 +10071,12 @@ -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. @@ -10083,10 +10089,10 @@ - report_worst_slack + report_worst_slack - [-min][-max][-digits digits] + [-min][-max][-digits digits] @@ -10094,7 +10100,7 @@ -max - Report the worst max/setup slack. + Report the worst max/setup slack. @@ -10102,12 +10108,12 @@ -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. @@ -10121,10 +10127,10 @@ - set_assigned_check + 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|-hold|-recovery|-removal[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins][-clock rise|fall][-cond sdf_cond][-worst]margin @@ -10132,7 +10138,7 @@ -setup - Annotate setup timing checks. + Annotate setup timing checks. @@ -10140,7 +10146,7 @@ -hold - Annotate hold timing checks. + Annotate hold timing checks. @@ -10148,7 +10154,7 @@ -recovery - Annotate recovery timing checks. + Annotate recovery timing checks. @@ -10156,7 +10162,7 @@ -removal - Annotate removal timing checks. + Annotate removal timing checks. @@ -10217,10 +10223,10 @@ - -clock rise|fall + -clock rise|fall - The timing check clock pin transition. + The timing check clock pin transition. @@ -10228,7 +10234,7 @@ margin - The timing check margin. + The timing check margin. @@ -10239,10 +10245,10 @@ - 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 @@ -10332,10 +10338,10 @@ - set_assigned_transition + set_assigned_transition - [-rise][-fall][-scene scene][-min][-max]slewpin_list + [-rise][-fall][-scene scene][-min][-max]slewpin_list @@ -10360,7 +10366,7 @@ scene - Annotate delays for scene. + Annotate delays for scene. @@ -10402,10 +10408,10 @@ - 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 @@ -10424,15 +10430,15 @@ - set_clock_gating_check + set_clock_gating_check - [-setup setup_time][-hold hold_time][-rise][-fall][-high][-low][objects] + [-setup setup_time][-hold hold_time][-rise][-fall][-high][-low][objects] - -setup setup_time + -setup setup_time Clock enable setup margin. @@ -10440,7 +10446,7 @@ - -hold hold_time + -hold hold_time Clock enable hold margin. @@ -10498,18 +10504,18 @@ - 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. @@ -10517,7 +10523,7 @@ -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. @@ -10525,7 +10531,7 @@ -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. + 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. @@ -10533,7 +10539,7 @@ -asynchronous - The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. + The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. @@ -10546,7 +10552,7 @@ - clocks + clocks A list of clocks in the group. @@ -10559,15 +10565,15 @@ - 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. @@ -10575,7 +10581,7 @@ - -clock clock + -clock clock If multiple clocks are defined at a pin this use this option to specify the latency for a specific clock. @@ -10636,10 +10642,10 @@ - set_clock_transition + set_clock_transition - [-rise][-fall][-min][-max]transitionclocks + [-rise][-fall][-min][-max]transitionclocks @@ -10647,7 +10653,7 @@ -rise - Set the transition time for the rising edge of the clock. + Set the transition time for the rising edge of the clock. @@ -10655,7 +10661,7 @@ -fall - Set the transition time for the falling edge of the clock. + Set the transition time for the falling edge of the clock. @@ -10664,7 +10670,7 @@ -min - Set the min transition time. + Set the min transition time. @@ -10672,7 +10678,7 @@ -max - Set the min transition time. + Set the min transition time. @@ -10698,15 +10704,15 @@ - 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. @@ -10714,7 +10720,7 @@ - -to to_clock + -to to_clock Inter-clock uncertainty target clock. @@ -10725,7 +10731,7 @@ -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. @@ -10733,7 +10739,7 @@ -fall - Inter-clock target clock rise edge, alternative to ‑fall_to. + Inter-clock target clock rise edge, alternative to ‑fall_to. @@ -10741,7 +10747,7 @@ -setup - uncertainty is for setup checks. + uncertainty is for setup checks. @@ -10749,7 +10755,7 @@ -hold - uncertainty is for hold checks. + uncertainty is for hold checks. @@ -10769,26 +10775,26 @@ - 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'. @@ -10796,7 +10802,7 @@ - -resistance res_unit + -resistance res_unit The resistance scale factor followed by 'ohm'. @@ -10804,7 +10810,7 @@ - -time time_unit + -time time_unit The time scale factor followed by 's'. @@ -10812,15 +10818,15 @@ - -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'. @@ -10828,7 +10834,7 @@ - -power power_unit + -power power_unit The power scale factor followed by 'w'. @@ -10836,14 +10842,14 @@ - -distance distance_unit + -distance distance_unit 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 An example of the set_units command is shown below. @@ -10853,15 +10859,15 @@ - 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. @@ -10869,7 +10875,7 @@ - -to to_pin + -to to_pin A pin that the setup/hold check is applied to. @@ -10893,7 +10899,7 @@ - -clock clock + -clock clock The setup/hold check clock. @@ -10914,7 +10920,7 @@ - set_disable_inferred_clock_gating + set_disable_inferred_clock_gating objects @@ -10929,21 +10935,21 @@ - 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 @@ -10951,7 +10957,7 @@ - -to to_port + -to to_port @@ -10962,12 +10968,12 @@ 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 @@ -10980,10 +10986,10 @@ - set_drive + set_drive - [-rise][-fall][-max][-min]resistanceports + [-rise][-fall][-max][-min]resistanceports @@ -11007,7 +11013,7 @@ -max - Set the maximum resistance. + Set the maximum resistance. @@ -11015,7 +11021,7 @@ -min - Set the minimum resistance. + Set the minimum resistance. @@ -11028,7 +11034,7 @@ - ports + ports A list of ports. @@ -11041,15 +11047,15 @@ - 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. @@ -11057,10 +11063,10 @@ - -library library + -library library - The driving cell library. + The driving cell library. @@ -11068,7 +11074,7 @@ -rise - Set the driving cell for a rising edge. + Set the driving cell for a rising edge. @@ -11076,7 +11082,7 @@ -fall - Set the driving cell for a falling edge. + Set the driving cell for a falling edge. @@ -11084,7 +11090,7 @@ -max - Set the driving cell for max delays. + Set the driving cell for max delays. @@ -11092,12 +11098,12 @@ -min - Set the driving cell for min delays. + Set the driving cell for min delays. - -pin pin + -pin pin The output port of the driving cell. @@ -11105,31 +11111,31 @@ - -from_pin from_pin + -from_pin from_pin - Use timing arcs from from_pin to the output pin. + Use timing arcs from from_pin to the output pin. - -input_transition_rise trans_rise + -input_transition_rise trans_rise - The transition time for a rising input at from_pin. + The transition time for a rising input at from_pin. - -input_transition_fall trans_fall + -input_transition_fall trans_fall - The transition time for a falling input at from_pin. + The transition time for a falling input at from_pin. - ports + ports A list of ports. @@ -11143,10 +11149,10 @@ - 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] @@ -11154,7 +11160,7 @@ -setup - Apply to setup checks. + Apply to setup checks. @@ -11162,7 +11168,7 @@ -hold - Apply to hold checks. + Apply to hold checks. @@ -11170,7 +11176,7 @@ -rise - Apply to rising path edges. + Apply to rising path edges. @@ -11178,7 +11184,7 @@ -fall - Apply to falling path edges. + Apply to falling path edges. @@ -11191,7 +11197,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11199,7 +11205,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11207,7 +11213,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11224,10 +11230,10 @@ - set_fanout_load + set_fanout_load - fanoutport_list + fanoutport_list @@ -11237,7 +11243,7 @@ - set_hierarchy_separator + set_hierarchy_separator separator @@ -11258,7 +11264,7 @@ - set_ideal_latency + set_ideal_latency [-rise] [-fall] [-min] [-max] delay objects @@ -11271,7 +11277,7 @@ - set_ideal_network + set_ideal_network [-no_propagation] objects @@ -11284,7 +11290,7 @@ - set_ideal_transition + set_ideal_transition [-rise] [-fall] [-min] [-max] transition_time objects @@ -11297,10 +11303,10 @@ - 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 @@ -11308,7 +11314,7 @@ -rise - Set the arrival time for the rising edge of the input. + Set the arrival time for the rising edge of the input. @@ -11316,7 +11322,7 @@ -fall - Set the arrival time for the falling edge of the input. + Set the arrival time for the falling edge of the input. @@ -11325,7 +11331,7 @@ -max - Set the maximum arrival time. + Set the maximum arrival time. @@ -11333,15 +11339,15 @@ -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. @@ -11349,12 +11355,12 @@ -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. @@ -11365,7 +11371,7 @@ -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. @@ -11373,7 +11379,7 @@ -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. @@ -11402,22 +11408,22 @@ 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. + 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] + 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. + 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 @@ -11425,7 +11431,7 @@ -rise - Set the rising edge transition. + Set the rising edge transition. @@ -11433,7 +11439,7 @@ -fall - Set the falling edge transition. + Set the falling edge transition. @@ -11441,7 +11447,7 @@ -max - Set the minimum transition time. + Set the minimum transition time. @@ -11449,7 +11455,7 @@ -min - Set the maximum transition time. + Set the maximum transition time. @@ -11475,10 +11481,10 @@ - set_level_shifter_strategy + set_level_shifter_strategy - [-rule rule_type] + [-rule rule_type] @@ -11488,10 +11494,10 @@ - set_level_shifter_threshold + set_level_shifter_threshold - [-voltage voltage] + [-voltage voltage] @@ -11501,10 +11507,10 @@ - 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 @@ -11512,7 +11518,7 @@ -rise - Set the external port rising capacitance (ports only). + Set the external port rising capacitance (ports only). @@ -11520,7 +11526,7 @@ -fall - Set the external port falling capacitance (ports only). + Set the external port falling capacitance (ports only). @@ -11529,7 +11535,7 @@ -max - Set the max capacitance. + Set the max capacitance. @@ -11537,7 +11543,7 @@ -min - Set the min capacitance. + Set the min capacitance. @@ -11545,7 +11551,7 @@ -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. @@ -11581,24 +11587,24 @@ - 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. @@ -11611,58 +11617,58 @@ - set_logic_one + set_logic_one - port_list + port_list - port_pin_list + port_pin_list 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. - 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 @@ -11675,15 +11681,15 @@ - set_max_capacitance + set_max_capacitance - capacitanceobjects + capacitanceobjects - capacitance + capacitance @@ -11691,7 +11697,7 @@ - objects + objects List of ports or cells. @@ -11704,10 +11710,10 @@ - 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 @@ -11715,7 +11721,7 @@ -rise - Set max delay for rising paths. + Set max delay for rising paths. @@ -11723,13 +11729,13 @@ -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. @@ -11737,7 +11743,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11745,7 +11751,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11761,7 +11767,7 @@ - -probe + -probe Do not break paths at internal pins (non startpoints). @@ -11791,10 +11797,10 @@ - set_max_dynamic_power + set_max_dynamic_power - power [unit] + power [unit] @@ -11804,15 +11810,15 @@ - set_max_fanout + set_max_fanout - fanoutobjects + fanoutobjects - fanout + fanout @@ -11820,7 +11826,7 @@ - objects + objects List of ports or cells. @@ -11833,10 +11839,10 @@ - set_max_leakage_power + set_max_leakage_power - power [unit] + power [unit] @@ -11846,16 +11852,16 @@ - set_max_time_borrow + set_max_time_borrow - delayobjects + delayobjects - delay + delay The maximum time the latches can borrow. @@ -11863,23 +11869,23 @@ - objects + objects 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 @@ -11916,22 +11922,22 @@ - transition + transition - The maximum slew/transition time. + The maximum slew/transition time. - objects + objects 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. @@ -11939,23 +11945,23 @@ - set_min_capacitance + set_min_capacitance - capacitanceobjects + capacitanceobjects - capacitance + capacitance - Minimum capacitance. + Minimum capacitance. - objects + objects List of ports or cells. @@ -11969,10 +11975,10 @@ - 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 @@ -11980,7 +11986,7 @@ -rise - Set min delay for rising paths. + Set min delay for rising paths. @@ -11988,12 +11994,12 @@ -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. @@ -12001,7 +12007,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12009,7 +12015,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12025,7 +12031,7 @@ - -probe + -probe Do not break paths at internal pins (non startpoints). @@ -12044,7 +12050,7 @@ delay - The minimum delay. + The minimum delay. @@ -12056,10 +12062,10 @@ - set_min_pulse_width + set_min_pulse_width - [-high][-low]min_widthobjects + [-high][-low]min_widthobjects @@ -12080,7 +12086,7 @@ - min_width + min_width @@ -12088,7 +12094,7 @@ - objects + objects List of pins, instances or clocks. @@ -12101,23 +12107,23 @@ - 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 @@ -12125,7 +12131,7 @@ -setup - Set cycle count for setup checks. + Set cycle count for setup checks. @@ -12133,7 +12139,7 @@ -hold - Set cycle count for hold checks. + Set cycle count for hold checks. @@ -12141,7 +12147,7 @@ -rise - Set cycle count for rising path edges. + Set cycle count for rising path edges. @@ -12150,7 +12156,7 @@ -fall - Set cycle count for falling path edges. + Set cycle count for falling path edges. @@ -12171,7 +12177,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -12179,7 +12185,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12187,7 +12193,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12216,7 +12222,7 @@ - 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] @@ -12248,7 +12254,7 @@ - -library lib + -library lib The name of the library that contains condition. @@ -12264,7 +12270,7 @@ - -min min_condition + -min min_condition The operating condition to use for min paths and hold checks. @@ -12273,7 +12279,7 @@ - -max max_condition + -max max_condition The operating condition to use for max paths and setup checks. @@ -12281,18 +12287,18 @@ - -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. @@ -12302,10 +12308,10 @@ - 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 @@ -12313,7 +12319,7 @@ -rise - Set the output delay for the rising edge of the input. + Set the output delay for the rising edge of the input. @@ -12321,7 +12327,7 @@ -fall - Set the output delay for the falling edge of the input. + Set the output delay for the falling edge of the input. @@ -12329,7 +12335,7 @@ -max - Set the maximum output delay. + Set the maximum output delay. @@ -12337,15 +12343,15 @@ -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. @@ -12353,15 +12359,15 @@ -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. @@ -12369,7 +12375,7 @@ -add_delay - Add this output delay to any existing output delays. + Add this output delay to any existing output delays. @@ -12377,7 +12383,7 @@ delay - The external delay to the check clocked by clock. + The external delay to the check clocked by clock. @@ -12389,33 +12395,33 @@ - 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. @@ -12441,15 +12447,15 @@ - 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. @@ -12465,7 +12471,7 @@ - -input_ports input_ports + -input_ports input_ports Set the input port activity/duty. @@ -12473,7 +12479,7 @@ - -pins pins + -pins pins Set the pin activity/duty. @@ -12482,15 +12488,15 @@ - -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. @@ -12498,30 +12504,30 @@ - -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 @@ -12542,11 +12548,11 @@ - set_pvt + set_pvt - [-min][-max][-process process][-voltage voltage] - [-temperature temperature]instances + [-min][-max][-process process][-voltage voltage] + [-temperature temperature]instances @@ -12554,7 +12560,7 @@ -min - Set the PVT values for max delays. + Set the PVT values for max delays. @@ -12562,12 +12568,12 @@ -max - Set the PVT values for min delays. + Set the PVT values for min delays. - -process process + -process process A process value (float). @@ -12575,7 +12581,7 @@ - -voltage voltage + -voltage voltage A voltage value (float). @@ -12584,7 +12590,7 @@ - -temperature temperature + -temperature temperature A temperature value (float). @@ -12605,15 +12611,15 @@ - 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. @@ -12621,7 +12627,7 @@ - -type data + -type data Set the sense for data paths (not supported). @@ -12629,34 +12635,34 @@ - -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. @@ -12664,7 +12670,7 @@ clocks - A list of clocks to apply the sense. + A list of clocks to apply the sense. @@ -12676,22 +12682,22 @@ - 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. @@ -12699,7 +12705,7 @@ - -fall + -fall Set the derating for falling delays. @@ -12707,7 +12713,7 @@ - -early + -early Derate early (min) paths. @@ -12715,7 +12721,7 @@ - -late + -late Derate late (max) paths. @@ -12723,7 +12729,7 @@ - -clock + -clock Derate paths in the clock network. @@ -12731,7 +12737,7 @@ - -data + -data Derate data paths. @@ -12739,7 +12745,7 @@ - -net_delay + -net_delay Derate net (interconnect) delays. @@ -12747,7 +12753,7 @@ - -cell_delay + -cell_delay Derate cell delays. @@ -12755,7 +12761,7 @@ - -cell_check + -cell_check Derate cell timing check margins. @@ -12766,7 +12772,7 @@ derate - The derating factor to apply to delays. + The derating factor to apply to delays. @@ -12785,16 +12791,16 @@ - set_resistance + set_resistance - [-max][-min]resistancenets + [-max][-min]resistancenets - -min + -min The resistance for minimum path delay calculation. @@ -12802,7 +12808,7 @@ - -max + -max The resistance for maximum path delay calculation. @@ -12818,7 +12824,7 @@ - nets + nets A list of nets. @@ -12831,10 +12837,10 @@ - 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] @@ -12886,7 +12892,7 @@ - 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 An example of the set_units command is shown below. @@ -12896,7 +12902,7 @@ - set_wire_load_min_block_size + set_wire_load_min_block_size size @@ -12909,10 +12915,10 @@ - set_wire_load_mode + set_wire_load_mode - top|enclosed|segmented + top|enclosed|segmented @@ -12946,15 +12952,15 @@ - 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. @@ -12962,7 +12968,7 @@ - -library library + -library library Library to look for model_name. @@ -12999,10 +13005,10 @@ - set_wire_load_selection_group + set_wire_load_selection_group - [-library library][-max][-min]group_name[objects] + [-library library][-max][-min]group_name[objects] @@ -13010,7 +13016,7 @@ library - Library to look for group_name. + Library to look for group_name. @@ -13035,7 +13041,7 @@ group_name - A wire load selection group name. + A wire load selection group name. @@ -13053,28 +13059,28 @@ - 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 @@ -13095,18 +13101,18 @@ - 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). @@ -13124,7 +13130,7 @@ - unset_clock_transition + unset_clock_transition clocks @@ -13146,15 +13152,15 @@ - 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 @@ -13162,7 +13168,7 @@ - -to to_clock + -to to_clock @@ -13189,7 +13195,7 @@ -setup - uncertainty is the setup check uncertainty. + uncertainty is the setup check uncertainty. @@ -13197,7 +13203,7 @@ -hold - uncertainty is the hold uncertainty. + uncertainty is the hold uncertainty. @@ -13223,15 +13229,15 @@ - 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. @@ -13276,7 +13282,7 @@ - unset_disable_inferred_clock_gating + unset_disable_inferred_clock_gating objects @@ -13291,16 +13297,16 @@ - 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 @@ -13334,10 +13340,10 @@ - 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 @@ -13345,7 +13351,7 @@ -rise - Unset the arrival time for the rising edge of the input. + Unset the arrival time for the rising edge of the input. @@ -13353,7 +13359,7 @@ -fall - Unset the arrival time for the falling edge of the input. + Unset the arrival time for the falling edge of the input. @@ -13361,7 +13367,7 @@ -max - Unset the minimum arrival time. + Unset the minimum arrival time. @@ -13369,7 +13375,7 @@ -min - Unset the maximum arrival time. + Unset the maximum arrival time. @@ -13377,7 +13383,7 @@ clock - Unset the arrival time from clock. + Unset the arrival time from clock. @@ -13385,7 +13391,7 @@ -clock_fall - Unset the arrival time from the falling edge of clock + Unset the arrival time from the falling edge of clock @@ -13404,10 +13410,10 @@ - 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 @@ -13455,7 +13461,7 @@ -clock_fall - The arrival time is from the falling edge of clock + The arrival time is from the falling edge of clock @@ -13473,10 +13479,10 @@ - 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] @@ -13484,7 +13490,7 @@ -setup - Unset path exceptions for setup checks. + Unset path exceptions for setup checks. @@ -13492,7 +13498,7 @@ -hold - Unset path exceptions for hold checks. + Unset path exceptions for hold checks. @@ -13500,7 +13506,7 @@ -rise - Unset path exceptions for rising path edges. + Unset path exceptions for rising path edges. @@ -13509,12 +13515,12 @@ -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. @@ -13522,7 +13528,7 @@ - -through through + -through through A list of instances, pins or nets. @@ -13530,7 +13536,7 @@ - -to to + -to to A list of clocks, instances, ports or pins. @@ -13544,15 +13550,15 @@ - 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. @@ -13568,7 +13574,7 @@ - -input_ports input_ports + -input_ports input_ports Set the input port activity/duty. @@ -13576,7 +13582,7 @@ - -pins pins + -pins pins Set the pin activity/duty. @@ -13584,20 +13590,20 @@ - -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 @@ -13618,7 +13624,7 @@ - unset_timing_derate + unset_timing_derate @@ -13632,28 +13638,28 @@ - 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 @@ -13666,10 +13672,10 @@ - with_output_to_variable + with_output_to_variable - var { commands } + var { commands } @@ -13689,16 +13695,16 @@ - 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] @@ -13714,7 +13720,7 @@ spice_directory - Directory for spice to write output files. + Directory for spice to write output files. @@ -13730,7 +13736,7 @@ model_file - Transistor model definitions .included by spice_file. + Transistor model definitions .included by spice_file. @@ -13751,7 +13757,7 @@ - -simulator + -simulator Simulator that will read the spice netlist. @@ -13769,10 +13775,10 @@ - write_sdc + write_sdc - [-digits digits][-gzip][-no_timestamp]filename + [-digits digits][-gzip][-no_timestamp]filename @@ -13788,7 +13794,7 @@ -gzip - Compress the SDC with gzip. + Compress the SDC with gzip. @@ -13814,18 +13820,18 @@ - 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. @@ -13847,7 +13853,7 @@ - -digits digits + -digits digits The number of digits after the decimal point to report. The default is 4. @@ -13858,7 +13864,7 @@ -gzip - Compress the SDF using gzip. + Compress the SDF using gzip. @@ -13882,42 +13888,42 @@ 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. @@ -13928,16 +13934,16 @@ 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 + 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. @@ -13945,10 +13951,10 @@ - write_verilog + write_verilog - [-include_pwr_gnd][-remove_cells lib_cells]filename + [-include_pwr_gnd][-remove_cells lib_cells]filename @@ -13961,10 +13967,10 @@ - -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. @@ -13972,12 +13978,12 @@ 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 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. @@ -13987,12 +13993,12 @@ 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. @@ -14000,7 +14006,7 @@ - property=~pattern + property=~pattern Return objects with property value that matches pattern. @@ -14008,7 +14014,7 @@ - property!=value + property!=value Return objects with property value not equal to value. @@ -14016,15 +14022,15 @@ - 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. @@ -14032,24 +14038,24 @@ - expr1||expr2 + expr1||expr2 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. @@ -14059,36 +14065,36 @@ - 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 @@ -14098,10 +14104,10 @@ - sta_crpr_enabled + sta_crpr_enabled - 0|1 + 0|1 @@ -14111,10 +14117,10 @@ - sta_dynamic_loop_breaking + sta_dynamic_loop_breaking - 0|1 + 0|1 @@ -14124,23 +14130,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 @@ -14150,10 +14156,10 @@ - sta_internal_bidirect_instance_paths_enabled + sta_internal_bidirect_instance_paths_enabled - 0|1 + 0|1 @@ -14163,10 +14169,10 @@ - sta_pocv_enabled + sta_pocv_enabled - 0|1 + 0|1 @@ -14176,14 +14182,14 @@ - 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. @@ -14191,36 +14197,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 @@ -14231,10 +14237,10 @@ - sta_preset_clear_arcs_enabled + sta_preset_clear_arcs_enabled - 0|1 + 0|1 @@ -14443,7 +14449,7 @@ - Version 2.6.0, Sep 23, 2024Copyright (c) 2024, Parallax Software, Inc. + Version 2.6.0, Sep 23, 2024Copyright (c) 2024, 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 bb81eb068165936bdfdd8c5814ea5bef9d593e59..c084d80861e41deab524afab6145837c92811e4d 100644 GIT binary patch delta 344348 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%0A&nOBj(XI(iC@CJ%#=`nIgUz{tC~K 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

ks1OBWewJL|AM86R9$%fry4CzoDxDjGB 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 delta 359601 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

tr6^Ht=Y11wu9p?eB11v+%LBFwk0;1F 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 YmWig_)a|)nS6e|HLj6<(fm?a;1TMlY1poj5 diff --git a/examples/multi_corner.tcl b/examples/multi_corner.tcl index f75c15283..4900d867c 100644 --- a/examples/multi_corner.tcl +++ b/examples/multi_corner.tcl @@ -9,9 +9,9 @@ set_timing_derate -late 1.1 create_clock -name clk -period 10 {clk1 clk2 clk3} set_input_delay -clock clk 0 {in1 in2} -define_scene ss -liberty nangate45_slow -define_scene tt -liberty nangate45_typ -define_scene ff -liberty nangate45_fast +define_scene ss -liberty NangateOpenCellLibrary_slow +define_scene tt -liberty NangateOpenCellLibrary +define_scene ff -liberty NangateOpenCellLibrary_fast # report all scenes report_checks -path_delay min_max diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index e01b12e36..15df9a856 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1591,9 +1591,8 @@ protected: void setThreadCount1(int thread_count); void updateLibertyScenes(); void updateSceneLiberty(Scene *scene, - const StdStringSeq &liberty_files, - const MinMax *min_max); - LibertyLibrary *findLibertyFileBasename(const std::string &filename) const; + const StdStringSeq &liberty_min_files, + const StdStringSeq &liberty_max_files); Scene *makeScene(const std::string &name, Mode *mode, diff --git a/network/Network.cc b/network/Network.cc index 8e03a7045..3b6bad62e 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -208,7 +208,6 @@ Network::checkNetworkLibertyScenes() } } -// Only used by Sta::setMinLibrary so linear search is acceptable. LibertyLibrary * Network::findLibertyFilename(const char *filename) { diff --git a/search/Sta.cc b/search/Sta.cc index 060697e8c..baa4ab074 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2517,8 +2517,7 @@ Sta::makeScene(const std::string &name, updateComponentsState(); if (graph_) graph_->makeSceneAfter(); - updateSceneLiberty(scene, liberty_min_files, MinMax::min()); - updateSceneLiberty(scene, liberty_max_files, MinMax::max()); + updateSceneLiberty(scene, liberty_min_files, liberty_max_files); cmd_scene_ = scene; } else @@ -2601,34 +2600,27 @@ Sta::findScenes(const std::string &name, void Sta::updateSceneLiberty(Scene *scene, - const StdStringSeq &liberty_files, - const MinMax *min_max) -{ - for (const std::string &lib_file : liberty_files) { - LibertyLibrary *lib = findLibertyFileBasename(lib_file); - if (lib) - LibertyLibrary::makeSceneMap(lib, scene->libertyIndex(min_max), - network_, report_); - else - report_->warn(1555, "liberty filename %s not found.", lib_file.c_str()); - } -} - -LibertyLibrary * -Sta::findLibertyFileBasename(const std::string &filename) const -{ - LibertyLibraryIterator *lib_iter = network_->libertyLibraryIterator(); - while (lib_iter->hasNext()) { - LibertyLibrary *lib = lib_iter->next(); - auto lib_file = std::filesystem::path(lib->filename()).filename().stem(); - auto stem = lib_file.stem(); - if (stem.string() == filename) { - delete lib_iter; - return lib; + const StdStringSeq &liberty_min_files, + const StdStringSeq &liberty_max_files) +{ + StdStringSet warned_files; + for (const MinMax *min_max : MinMax::range()) { + const StdStringSeq &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) + lib = network_->findLibertyFilename(lib_file.c_str()); + if (lib) + 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()); + warned_files.insert(lib_file); + } } } - delete lib_iter; - return nullptr; } void From f0c9971015015fa9cded7d2c1427bee55718ddfb Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Thu, 26 Feb 2026 05:13:51 +1300 Subject: [PATCH 028/181] Expose 'TimingType' from `TimingArc`. (#380) When using the OpenSTA Liberty file parser as a general-purpose Liberty reader, it's useful to be able to extract the original `TimingType` for a timing arc, and trivial to support. --- include/sta/TimingArc.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 347e37f13..caccedf17 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -170,6 +170,7 @@ public: void deleteTimingArc(TimingArc *arc); TimingArc *findTimingArc(unsigned arc_index); void setRole(const TimingRole *role); + TimingType timingType() const { return attrs_->timingType(); } FuncExpr *cond() const { return attrs_->cond(); } // Cond default is the timing arcs with no condition when there are // other conditional timing arcs between the same pins. From a2cd40f57c53d4a6a6a71d5fa3df400b877334f1 Mon Sep 17 00:00:00 2001 From: Kazuto Iris <78157415+kazutoiris@users.noreply.github.com> Date: Thu, 26 Feb 2026 10:08:47 +0800 Subject: [PATCH 029/181] fix: Revert "Expose 'TimingType' from `TimingArc`. (#380)" (#390) Now the `TimingArcSet` class in `TimingArc.hh` has two identical declarations of the `timingType()` member function with the same signature, return type and implementation, which causes an overload conflict error during the build process. This reverts commit f0c9971015015fa9cded7d2c1427bee55718ddfb. --- include/sta/TimingArc.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index caccedf17..347e37f13 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -170,7 +170,6 @@ public: void deleteTimingArc(TimingArc *arc); TimingArc *findTimingArc(unsigned arc_index); void setRole(const TimingRole *role); - TimingType timingType() const { return attrs_->timingType(); } FuncExpr *cond() const { return attrs_->cond(); } // Cond default is the timing arcs with no condition when there are // other conditional timing arcs between the same pins. From 95d5ebb47cda31c6f4fe360fe4f29becc623ada8 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 26 Feb 2026 09:32:20 -0800 Subject: [PATCH 030/181] test/helpers.tcl Signed-off-by: James Cherry --- test/helpers.tcl | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/helpers.tcl diff --git a/test/helpers.tcl b/test/helpers.tcl new file mode 100644 index 000000000..46eb84c9b --- /dev/null +++ b/test/helpers.tcl @@ -0,0 +1,41 @@ +# Helper functions common to multiple regressions. + +set test_dir [file dirname [file normalize [info script]]] +set result_dir [file join $test_dir "results"] + +# puts [exec cat $file] without forking. +proc report_file { file } { + set stream [open $file r] + if { [file extension $file] == ".gz" } { + zlib push gunzip $stream + } + gets $stream line + while { ![eof $stream] } { + puts $line + gets $stream line + } + close $stream +} + +proc report_file_filter { file filter } { + set stream [open $file r] + gets $stream line + while { ![eof $stream] } { + set index [string first $filter $line] + if { $index != -1 } { + set line [string replace $line $index [expr $index + [string length $filter] - 1]] + } + puts $line + gets $stream line + } + close $stream +} + +proc make_result_file { filename } { + variable result_dir + return [file join $result_dir $filename] +} + +proc sort_objects { objects } { + return [sta::sort_by_full_name $objects] +} From a47a8dd8315f6e8d55a90c3f55d4c433e55bff7d Mon Sep 17 00:00:00 2001 From: Kazuto Iris <78157415+kazutoiris@users.noreply.github.com> Date: Sat, 28 Feb 2026 05:51:28 +0800 Subject: [PATCH 031/181] Add GitHub Action for CI and fix broken testcases (#391) * feat(ci): add GitHub Action for push and pull request * fix(ci): fix broken testcases * chore: add CODEOWNERS for ci * ci: bump actions/upload-artifact from 6 to 7 --- .github/dependabot.yml | 6 +++++ .github/workflows/ci.yml | 54 ++++++++++++++++++++++++++++++++++++++++ CODEOWNERS | 2 ++ test/get_filter.ok | 2 +- test/report_json1.ok | 10 ++++---- test/suppress_msg.ok | 8 +++--- 6 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 CODEOWNERS diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..c7fc735ee --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..74255543d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +name: CI + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: true + + - name: Set up dependencies + run: | + sudo apt-get update && sudo apt-get install -y flex libfl-dev bison tcl-dev tcl-tclreadline libeigen3-dev ninja-build + + - name: Set up cudd-3.0.0 + run: | + wget https://github.com/oscc-ip/artifact/releases/download/cudd-3.0.0/build.tar.gz + mkdir -p cudd + tar -zxvf build.tar.gz -Ccudd + + - name: Build + run: | + mkdir build + cd build + cmake .. -G Ninja -DCUDD_DIR=$(pwd)/../cudd -DCMAKE_INSTALL_PREFIX=$(pwd)/install -DCMAKE_BUILD_TYPE=Release + cmake --build . --target all -- -j $(nproc) + cmake --install . + tar -zcvf build.tar.gz -Cinstall . + + - name: Test + run: | + cd test + ./regression + + - name: Upload Artifacts + uses: actions/upload-artifact@v7 + if: ${{ !cancelled() }} + with: + name: artifact + path: | + build/install/* + + - name: Upload Test Result + uses: actions/upload-artifact@v7 + if: ${{ !cancelled() }} + with: + name: result + path: | + test/results/* diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..c6c2b793d --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# For CI/CD issues +/.github/ @kazutoiris diff --git a/test/get_filter.ok b/test/get_filter.ok index deaa54f33..b66978cd3 100644 --- a/test/get_filter.ok +++ b/test/get_filter.ok @@ -53,4 +53,4 @@ in2 [get_ports -filter direction==output *] out [get_cells -filter {name ~= *r1*} *] -Error: get_filter.tcl line 48, unknown filter operand. +Error 336: get_filter.tcl line 48, unknown filter operand. diff --git a/test/report_json1.ok b/test/report_json1.ok index 7f70fbf70..51e8ae793 100644 --- a/test/report_json1.ok +++ b/test/report_json1.ok @@ -36,7 +36,7 @@ "net": "mid", "arrival": 3.296e-10, "capacitance": 1.949e-15, - "slew": 4.035e-11 + "slew": 3.612e-11 }, { "instance": "_1416_[0]", @@ -45,7 +45,7 @@ "pin": "_1416_[0]/D", "net": "mid", "arrival": 3.296e-10, - "slew": 4.035e-11 + "slew": 3.612e-11 } ], "target_clock": "clk", @@ -72,9 +72,9 @@ ], "data_arrival_time": 3.296e-10, "crpr": 0.000e+00, - "margin": 1.225e-10, - "required_time": 9.877e-09, - "slack": 9.548e-09 + "margin": 1.207e-10, + "required_time": 9.879e-09, + "slack": 9.550e-09 } ] } diff --git a/test/suppress_msg.ok b/test/suppress_msg.ok index 0fd4d548b..2673ab256 100644 --- a/test/suppress_msg.ok +++ b/test/suppress_msg.ok @@ -1,12 +1,12 @@ Warning 1: suppress_msg.tcl line 18, cmd warn 1 -caught Error: suppress_msg.tcl line 18, cmd error 1 +caught Error 2: suppress_msg.tcl line 18, cmd error 1 Warning 1: cmd warn 2 -caught Error: 2cmd error 2 +caught Error: 2 cmd error 2 after error caught caught after error Warning 1: suppress_msg.tcl line 51, cmd warn 7 -caught Error: suppress_msg.tcl line 51, cmd error 7 +caught Error 2: suppress_msg.tcl line 51, cmd error 7 Warning 1: cmd warn 8 -caught Error: 2cmd error 8 +caught Error: 2 cmd error 8 From c010a0f99e10d4bfb39849a6c964e6ded7b5cbda Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 27 Feb 2026 16:57:08 -0800 Subject: [PATCH 032/181] liberty reader rewrite Signed-off-by: James Cherry --- examples/multi_corner.tcl | 1 - graph/Graph.cc | 2 + graph/Graph.i | 1 + include/sta/Liberty.hh | 4 +- include/sta/TableModel.hh | 26 +- include/sta/TimingArc.hh | 9 +- include/sta/TokenParser.hh | 10 + include/sta/Transition.hh | 1 + liberty/LibExprReader.cc | 6 +- liberty/LibExprReader.hh | 2 +- liberty/LibExprReaderPvt.hh | 4 +- liberty/Liberty.cc | 56 +- liberty/Liberty.i | 1 + liberty/Liberty.tcl | 16 + liberty/LibertyBuilder.cc | 71 +- liberty/LibertyBuilder.hh | 57 +- liberty/LibertyExt.cc | 49 +- liberty/LibertyLex.ll | 8 +- liberty/LibertyParse.yy | 10 +- liberty/LibertyParser.cc | 568 ++- liberty/LibertyParser.hh | 248 +- liberty/LibertyReader.cc | 8225 +++++++++++--------------------- liberty/LibertyReaderPvt.hh | 1160 ++--- liberty/TableModel.cc | 111 +- liberty/TimingArc.cc | 12 +- power/Power.cc | 2 +- search/MakeTimingModel.cc | 18 +- search/PathEnd.cc | 2 +- test/get_is_memory.tcl | 2 +- test/gf180mcu_sram.lib.gz | Bin 7015 -> 6997 bytes test/liberty_arcs_one2one_1.ok | 1 + test/liberty_arcs_one2one_2.ok | 1 + util/TokenParser.cc | 23 + 33 files changed, 3993 insertions(+), 6714 deletions(-) diff --git a/examples/multi_corner.tcl b/examples/multi_corner.tcl index 4900d867c..fa5fab183 100644 --- a/examples/multi_corner.tcl +++ b/examples/multi_corner.tcl @@ -17,4 +17,3 @@ define_scene ff -liberty NangateOpenCellLibrary_fast report_checks -path_delay min_max # report typical scene report_checks -scene tt - diff --git a/graph/Graph.cc b/graph/Graph.cc index 2fe4d9344..9e0b189dc 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -1236,6 +1236,8 @@ Edge::to_string(const StaState *sta) const string str = from(graph)->to_string(sta); str += " -> "; str += to(graph)->to_string(sta); + str += " "; + str += role()->to_string(); FuncExpr *when = arc_set_->cond(); if (when) { str += " "; diff --git a/graph/Graph.i b/graph/Graph.i index ed9ad710d..b6f3c3c53 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -171,6 +171,7 @@ path_iterator(const RiseFall *rf, } // Vertex methods %extend Edge { +std::string to_string() { return self->to_string(Sta::sta()); }; Vertex *from() { return self->from(Sta::sta()->graph()); } Vertex *to() { return self->to(Sta::sta()->graph()); } Pin *from_pin() { return self->from(Sta::sta()->graph())->pin(); } diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 68d43bb30..02769b848 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -253,7 +253,7 @@ public: float wire_delay) const; // Check for supported axis variables. // Return true if axes are supported. - static bool checkSlewDegradationAxes(const TablePtr &table); + static bool checkSlewDegradationAxes(const TableModel *table_model); float defaultInputPinCap() const { return default_input_pin_cap_; } void setDefaultInputPinCap(float cap); @@ -1051,7 +1051,7 @@ public: void setScale(ScaleFactorType type, ScaleFactorPvt pvt, float scale); - void print(); + void report(Report *report); protected: std::string name_; diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 1d08d7517..1e88faf6e 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -49,6 +49,7 @@ using FloatTable = std::vector; // Sequence of 1D tables (order 1). using Table1Seq = std::vector; using Waveform = Table; +using TableModelsEarlyLate = std::array; TableAxisVariable stringTableAxisVariable(const char *variable); @@ -63,11 +64,14 @@ class GateTableModel : public GateTimingModel public: GateTableModel(LibertyCell *cell, TableModel *delay_model, - TableModel *delay_sigma_models[EarlyLate::index_count], + TableModelsEarlyLate delay_sigma_models, TableModel *slew_model, - TableModel *slew_sigma_models[EarlyLate::index_count], + TableModelsEarlyLate slew_sigma_models, ReceiverModelPtr receiver_model, OutputWaveforms *output_waveforms); + GateTableModel(LibertyCell *cell, + TableModel *delay_model, + TableModel *slew_model); ~GateTableModel() override; void gateDelay(const Pvt *pvt, float in_slew, @@ -100,7 +104,7 @@ public: OutputWaveforms *outputWaveforms() const { return output_waveforms_.get(); } // Check the axes before making the model. // Return true if the model axes are supported. - static bool checkAxes(const TablePtr &table); + static bool checkAxes(const TableModel *table); protected: void maxCapSlew(float in_slew, @@ -135,9 +139,9 @@ protected: static bool checkAxis(const TableAxis *axis); std::unique_ptr delay_model_; - std::array, EarlyLate::index_count> delay_sigma_models_; + TableModelsEarlyLate delay_sigma_models_; std::unique_ptr slew_model_; - std::array, EarlyLate::index_count> slew_sigma_models_; + TableModelsEarlyLate slew_sigma_models_; ReceiverModelPtr receiver_model_; std::unique_ptr output_waveforms_; }; @@ -147,7 +151,9 @@ class CheckTableModel : public CheckTimingModel public: CheckTableModel(LibertyCell *cell, TableModel *model, - TableModel *sigma_models[EarlyLate::index_count]); + TableModelsEarlyLate sigma_models); + CheckTableModel(LibertyCell *cell, + TableModel *model); ~CheckTableModel() override; ArcDelay checkDelay(const Pvt *pvt, float from_slew, @@ -166,7 +172,7 @@ public: // Check the axes before making the model. // Return true if the model axes are supported. - static bool checkAxes(const TablePtr table); + static bool checkAxes(const TableModel *table); protected: void setIsScaled(bool is_scaled) override; @@ -197,7 +203,7 @@ protected: static bool checkAxis(const TableAxis *axis); std::unique_ptr model_; - std::array, EarlyLate::index_count> sigma_models_; + TableModelsEarlyLate sigma_models_; }; class TableAxis @@ -254,6 +260,8 @@ public: const TableAxis *axis2() const { return axis2_.get(); } const TableAxis *axis3() const { return axis3_.get(); } const TableAxisPtr axis1ptr() const { return axis1_; } + const TableAxisPtr axis2ptr() const { return axis2_; } + const TableAxisPtr axis3ptr() const { return axis3_; } void setIsScaled(bool is_scaled); float value(size_t axis_idx1, @@ -409,7 +417,7 @@ public: void setCapacitanceModel(TableModel table_model, size_t segment, const RiseFall *rf); - static bool checkAxes(TablePtr table); + static bool checkAxes(const TableModel *table); private: std::vector capacitance_models_; diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 347e37f13..63c04d49e 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -99,7 +99,7 @@ class TimingArcAttrs public: TimingArcAttrs(); TimingArcAttrs(TimingSense sense); - virtual ~TimingArcAttrs(); + ~TimingArcAttrs(); TimingType timingType() const { return timing_type_; } void setTimingType(TimingType type); TimingSense timingSense() const { return timing_sense_; } @@ -145,7 +145,8 @@ class TimingArcSet friend class LibertyCell; public: - virtual ~TimingArcSet(); + ~TimingArcSet(); + std::string to_string(); LibertyCell *libertyCell() const; LibertyPort *from() const { return from_; } LibertyPort *to() const { return to_; } @@ -249,7 +250,7 @@ public: TimingArcSet *set() const { return set_; } TimingSense sense() const; // Index in TimingArcSet. - unsigned index() const { return index_; } + size_t index() const { return index_; } TimingModel *model() const { return model_; } GateTimingModel *gateModel(const Scene *scene, const MinMax *min_max) const; @@ -270,7 +271,7 @@ public: protected: TimingModel *model(const Scene *scene, const MinMax *min_max) const; - void setIndex(unsigned index); + void setIndex(size_t index); void addScaledModel(const OperatingConditions *op_cond, TimingModel *scaled_model); diff --git a/include/sta/TokenParser.hh b/include/sta/TokenParser.hh index a339a6a8c..27db37f9d 100644 --- a/include/sta/TokenParser.hh +++ b/include/sta/TokenParser.hh @@ -24,8 +24,13 @@ #pragma once +#include +#include + namespace sta { +using StdStringSeq = std::vector; + // Iterate over the tokens in str separated by character sep. // Similar in functionality to strtok, but does not leave the string // side-effected. This is preferable to using strtok because it leaves @@ -49,4 +54,9 @@ private: bool first_; }; +// Parse delimiter separated tokens and skipp spaces. +StdStringSeq +parseTokens(const std::string &s, + const char delimiter); + } // namespace diff --git a/include/sta/Transition.hh b/include/sta/Transition.hh index 39190c6cf..3f3e03c6d 100644 --- a/include/sta/Transition.hh +++ b/include/sta/Transition.hh @@ -48,6 +48,7 @@ public: static const RiseFall *fall() { return &fall_; } static int riseIndex() { return rise_.sdf_triple_index_; } static int fallIndex() { return fall_.sdf_triple_index_; } + const std::string &to_string_long() const { return name_; } const std::string &to_string() const { return short_name_; } const char *name() const { return name_.c_str(); } const char *shortName() const { return short_name_.c_str(); } diff --git a/liberty/LibExprReader.cc b/liberty/LibExprReader.cc index c9bc84bc2..dfb2d679c 100644 --- a/liberty/LibExprReader.cc +++ b/liberty/LibExprReader.cc @@ -37,7 +37,7 @@ namespace sta { FuncExpr * parseFuncExpr(const char *func, - LibertyCell *cell, + const LibertyCell *cell, const char *error_msg, Report *report) { @@ -56,7 +56,7 @@ parseFuncExpr(const char *func, } LibExprReader::LibExprReader(const char *func, - LibertyCell *cell, + const LibertyCell *cell, const char *error_msg, Report *report) : func_(func), @@ -69,7 +69,7 @@ LibExprReader::LibExprReader(const char *func, // defined in LibertyReader.cc LibertyPort * -libertyReaderFindPort(LibertyCell *cell, +libertyReaderFindPort(const LibertyCell *cell, const char *port_name); FuncExpr * diff --git a/liberty/LibExprReader.hh b/liberty/LibExprReader.hh index 79d6d35f9..fa36aaa33 100644 --- a/liberty/LibExprReader.hh +++ b/liberty/LibExprReader.hh @@ -32,7 +32,7 @@ class LibertyCell; FuncExpr * parseFuncExpr(const char *func, - LibertyCell *cell, + const LibertyCell *cell, const char *error_msg, Report *report); diff --git a/liberty/LibExprReaderPvt.hh b/liberty/LibExprReaderPvt.hh index 4532b2e87..06a65ea27 100644 --- a/liberty/LibExprReaderPvt.hh +++ b/liberty/LibExprReaderPvt.hh @@ -35,7 +35,7 @@ class LibExprReader { public: LibExprReader(const char *func, - LibertyCell *cell, + const LibertyCell *cell, const char *error_msg, Report *report); FuncExpr *makeFuncExprPort(const char *port_name); @@ -55,7 +55,7 @@ public: private: const char *func_; - LibertyCell *cell_; + const LibertyCell *cell_; const char *error_msg_; Report *report_; FuncExpr *result_; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index d83d06b7d..5df311432 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -111,8 +111,6 @@ LibertyLibrary::LibertyLibrary(const char *name, LibertyLibrary::~LibertyLibrary() { - delete scale_factors_; - for (auto rf_index : RiseFall::rangeIndex()) { TableModel *model = wire_slew_degradation_tbls_[rf_index]; delete model; @@ -271,14 +269,14 @@ LibertyLibrary::setScaleFactors(ScaleFactors *scales) ScaleFactors * LibertyLibrary::makeScaleFactors(const char *name) { - auto [it, inserted] = scale_factors_map_.emplace(std::string(name), name); + auto [it, inserted] = scale_factors_map_.emplace(name, name); return &it->second; } ScaleFactors * LibertyLibrary::findScaleFactors(const char *name) { - return findKeyValuePtr(scale_factors_map_, std::string(name)); + return findKeyValuePtr(scale_factors_map_, name); } float @@ -400,20 +398,20 @@ LibertyLibrary::degradeWireSlew(const TableModel *model, // Check for supported axis variables. // Return true if axes are supported. bool -LibertyLibrary::checkSlewDegradationAxes(const TablePtr &table) +LibertyLibrary::checkSlewDegradationAxes(const TableModel *table_model) { - switch (table->order()) { + switch (table_model->order()) { case 0: return true; case 1: { - const TableAxis *axis1 = table->axis1(); + const TableAxis *axis1 = table_model->axis1(); TableAxisVariable var1 = axis1->variable(); return var1 == TableAxisVariable::output_pin_transition || var1 == TableAxisVariable::connect_delay; } case 2: { - const TableAxis *axis1 = table->axis1(); - const TableAxis *axis2 = table->axis2(); + const TableAxis *axis1 = table_model->axis1(); + const TableAxis *axis2 = table_model->axis2(); TableAxisVariable var1 = axis1->variable(); TableAxisVariable var2 = axis2->variable(); return (var1 == TableAxisVariable::output_pin_transition @@ -1269,8 +1267,7 @@ LibertyCell::makeInternalPower(LibertyPort *port, const std::shared_ptr &when, InternalPowerModels &models) { - internal_powers_.emplace_back(port, related_port, related_pg_pin, - when, models); + internal_powers_.emplace_back(port, related_port, related_pg_pin, when, models); port_internal_powers_[port].push_back(internal_powers_.size() - 1); } @@ -1485,6 +1482,10 @@ LibertyCell::makeSequential(int size, port_to_seq_map_[sequentials_.back().output()] = idx; port_to_seq_map_[sequentials_.back().outputInv()] = idx; } + delete clk; + delete data; + delete clear; + delete preset; } Sequential * @@ -3087,7 +3088,8 @@ OperatingConditions::setWireloadTree(WireloadTree tree) static EnumNameMap scale_factor_type_map = {{ScaleFactorType::pin_cap, "pin_cap"}, - {ScaleFactorType::wire_cap, "wire_res"}, + {ScaleFactorType::wire_cap, "wire_cap"}, + {ScaleFactorType::wire_res, "wire_res"}, {ScaleFactorType::min_period, "min_period"}, {ScaleFactorType::cell, "cell"}, {ScaleFactorType::hold, "hold"}, @@ -3124,7 +3126,9 @@ scaleFactorTypeRiseFallSuffix(ScaleFactorType type) || type == ScaleFactorType::recovery || type == ScaleFactorType::removal || type == ScaleFactorType::nochange - || type == ScaleFactorType::skew; + || type == ScaleFactorType::skew + || type == ScaleFactorType::leakage_power + || type == ScaleFactorType::internal_power; } bool @@ -3144,7 +3148,8 @@ scaleFactorTypeLowHighSuffix(ScaleFactorType type) EnumNameMap scale_factor_pvt_names = {{ScaleFactorPvt::process, "process"}, {ScaleFactorPvt::volt, "volt"}, - {ScaleFactorPvt::temp, "temp"} + {ScaleFactorPvt::temp, "temp"}, + {ScaleFactorPvt::unknown, "unknown"} }; ScaleFactorPvt @@ -3214,31 +3219,32 @@ ScaleFactors::scale(ScaleFactorType type, } void -ScaleFactors::print() +ScaleFactors::report(Report *report) { - printf("%10s", " "); + std::string line = " "; for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { ScaleFactorPvt pvt = (ScaleFactorPvt) pvt_index; - printf("%10s", scaleFactorPvtName(pvt)); + stringAppend(line, "%10s", scaleFactorPvtName(pvt)); } - printf("\n"); + report->reportLineString(line); + for (int type_index = 0; type_index < scale_factor_type_count; type_index++) { ScaleFactorType type = (ScaleFactorType) type_index; - printf("%10s ", scaleFactorTypeName(type)); + stringPrint(line, "%10s ", scaleFactorTypeName(type)); for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { if (scaleFactorTypeRiseFallSuffix(type) || scaleFactorTypeRiseFallPrefix(type) || scaleFactorTypeLowHighSuffix(type)) { - printf(" %.3f,%.3f", - scales_[type_index][pvt_index][RiseFall::riseIndex()], - scales_[type_index][pvt_index][RiseFall::fallIndex()]); + stringAppend(line, " %.3f,%.3f", + scales_[type_index][pvt_index][RiseFall::riseIndex()], + scales_[type_index][pvt_index][RiseFall::fallIndex()]); } else { - printf(" %.3f", - scales_[type_index][pvt_index][0]); + stringAppend(line, " %.3f", + scales_[type_index][pvt_index][0]); } } - printf("\n"); + report->reportLineString(line); } } diff --git a/liberty/Liberty.i b/liberty/Liberty.i index 962231705..2257b2eb7 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -363,6 +363,7 @@ scan_signal_type() %extend TimingArcSet { LibertyPort *from() { return self->from(); } LibertyPort *to() { return self->to(); } +std::string to_string() { return self->to_string(); } const TimingRole *role() { return self->role(); } const char *sdf_cond() { return self->sdfCond().c_str(); } diff --git a/liberty/Liberty.tcl b/liberty/Liberty.tcl index 672e876fa..fd75a8bf1 100644 --- a/liberty/Liberty.tcl +++ b/liberty/Liberty.tcl @@ -74,6 +74,11 @@ proc report_lib_cell_ { cell scene } { if { $filename != "" } { report_line "File $filename" } + report_lib_ports $cell $scene + report_timing_arcs $cell +} + +proc report_lib_ports { cell scene } { set iter [$cell liberty_port_iterator] while {[$iter has_next]} { set port [$iter next] @@ -115,5 +120,16 @@ proc report_lib_port { port scene } { report_line " ${indent}$port_name [liberty_port_direction $port]$enable$func[port_capacitance_str $port $scene $sta_report_default_digits]" } +proc report_timing_arcs { cell } { + set timing_arcs [$cell timing_arc_sets] + if { [llength $timing_arcs] > 0 } { + puts "" + puts "Timing arcs" + foreach timing_arc $timing_arcs { + puts [$timing_arc to_string] + } + } +} + # sta namespace end } diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index b20d8c620..988c32e9a 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -35,14 +35,11 @@ namespace sta { -using std::string; - -void -LibertyBuilder::init(Debug *debug, - Report *report) +LibertyBuilder::LibertyBuilder(Debug *debug, + Report *report) : + debug_(debug), + report_(report) { - debug_ = debug; - report_ = report; } LibertyCell * @@ -105,7 +102,7 @@ LibertyBuilder::makeBusPortBit(ConcreteLibrary *library, const char *bus_name, int bit_index) { - string bit_name; + std::string bit_name; stringPrint(bit_name, "%s%c%d%c", bus_name, library->busBrktLeft(), @@ -189,6 +186,7 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, case TimingType::combinational: if (seq && seq->isLatch() + && seq->data() && seq->data()->hasPort(from_port)) // Latch D->Q timing arcs. return makeLatchDtoQArcs(cell, from_port, to_port, @@ -307,8 +305,9 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, { FuncExpr *func = to_port->function(); FuncExpr *enable = to_port->tristateEnable(); - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::combinational(), attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::combinational(), + attrs); TimingSense sense = attrs->timingSense(); if (sense == TimingSense::unknown) { // Timing sense not specified - find it from function. @@ -388,8 +387,9 @@ LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell, TimingSense sense, TimingArcAttrsPtr attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::latchDtoQ(), attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::latchDtoQ(), + attrs); TimingModel *model; const RiseFall *to_rf = RiseFall::rise(); model = attrs->model(to_rf); @@ -456,8 +456,8 @@ LibertyBuilder::makeFromTransitionArcs(LibertyCell *cell, const TimingRole *role, TimingArcAttrsPtr attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - related_out, role, attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, + related_out, role, attrs); for (auto to_rf : RiseFall::range()) { TimingModel *model = attrs->model(to_rf); if (model) @@ -476,8 +476,8 @@ LibertyBuilder::makePresetClrArcs(LibertyCell *cell, TimingArcSet *arc_set = nullptr; TimingModel *model = attrs->model(to_rf); if (model) { - arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::regSetClr(), attrs); + arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::regSetClr(), attrs); const RiseFall *opp_rf = to_rf->opposite(); switch (attrs->timingSense()) { case TimingSense::positive_unate: @@ -509,8 +509,9 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, bool to_fall, TimingArcAttrsPtr attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::tristateEnable(), attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::tristateEnable(), + attrs); FuncExpr *tristate_enable = to_port->tristateEnable(); TimingSense sense = attrs->timingSense(); if (sense == TimingSense::unknown && tristate_enable) @@ -579,9 +580,9 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, bool to_fall, TimingArcAttrsPtr attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, - TimingRole::tristateDisable(), - attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, + TimingRole::tristateDisable(), + attrs); TimingSense sense = attrs->timingSense(); FuncExpr *tristate_enable = to_port->tristateEnable(); if (sense == TimingSense::unknown && tristate_enable) @@ -648,7 +649,8 @@ LibertyBuilder::makeClockTreePathArcs(LibertyCell *cell, const TimingRole *role, TimingArcAttrsPtr attrs) { - TimingArcSet *arc_set = makeTimingArcSet(cell, nullptr, to_port, role, attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(nullptr, to_port, nullptr, + role, attrs); for (const RiseFall *to_rf : RiseFall::range()) { TimingModel *model = attrs->model(to_rf); if (model) { @@ -683,8 +685,8 @@ LibertyBuilder::makeMinPulseWidthArcs(LibertyCell *cell, { if (from_port == nullptr) from_port = to_port; - TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, related_out, - role, attrs); + TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, related_out, + role, attrs); for (const RiseFall *from_rf : RiseFall::range()) { TimingModel *model = attrs->model(from_rf); if (model) @@ -695,27 +697,6 @@ LibertyBuilder::makeMinPulseWidthArcs(LibertyCell *cell, //////////////////////////////////////////////////////////////// -TimingArcSet * -LibertyBuilder::makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - const TimingRole *role, - TimingArcAttrsPtr attrs) -{ - return cell->makeTimingArcSet(from, to, nullptr, role, attrs); -} - -TimingArcSet * -LibertyBuilder::makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs) -{ - return cell->makeTimingArcSet(from, to, related_out, role, attrs); -} - TimingArc * LibertyBuilder::makeTimingArc(TimingArcSet *set, const RiseFall *from_rf, diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh index dacbbb416..065386876 100644 --- a/liberty/LibertyBuilder.hh +++ b/liberty/LibertyBuilder.hh @@ -38,23 +38,21 @@ class Report; class LibertyBuilder { public: - LibertyBuilder() {} - virtual ~LibertyBuilder() {} - void init(Debug *debug, - Report *report); - virtual LibertyCell *makeCell(LibertyLibrary *library, - const char *name, - const char *filename); - virtual LibertyPort *makePort(LibertyCell *cell, - const char *name); - virtual LibertyPort *makeBusPort(LibertyCell *cell, - const char *bus_name, - int from_index, - int to_index, - BusDcl *bus_dcl); - virtual LibertyPort *makeBundlePort(LibertyCell *cell, - const char *name, - ConcretePortSeq *members); + LibertyBuilder(Debug *debug, + Report *report); + LibertyCell *makeCell(LibertyLibrary *library, + const char *name, + const char *filename); + LibertyPort *makePort(LibertyCell *cell, + const char *name); + LibertyPort *makeBusPort(LibertyCell *cell, + const char *bus_name, + int from_index, + int to_index, + BusDcl *bus_dcl); + LibertyPort *makeBundlePort(LibertyCell *cell, + const char *name, + ConcretePortSeq *members); // Build timing arc sets and their arcs given a type and sense. // Port functions and cell latches are also used by this builder // to get the correct roles. @@ -100,29 +98,18 @@ protected: int from_index, int to_index); // Bus port bit (internal to makeBusPortBits). - virtual LibertyPort *makePort(LibertyCell *cell, - const char *bit_name, - int bit_index); + LibertyPort *makePort(LibertyCell *cell, + const char *bit_name, + int bit_index); void makeBusPortBit(ConcreteLibrary *library, LibertyCell *cell, ConcretePort *bus_port, const char *bus_name, int index); - virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - const TimingRole *role, - TimingArcAttrsPtr attrs); - virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs); - virtual TimingArc *makeTimingArc(TimingArcSet *set, - const Transition *from_rf, - const Transition *to_rf, - TimingModel *model); + TimingArc *makeTimingArc(TimingArcSet *set, + const Transition *from_rf, + const Transition *to_rf, + TimingModel *model); TimingArc *makeTimingArc(TimingArcSet *set, const RiseFall *from_rf, const RiseFall *to_rf, diff --git a/liberty/LibertyExt.cc b/liberty/LibertyExt.cc index d45a7420a..32c10b5e7 100644 --- a/liberty/LibertyExt.cc +++ b/liberty/LibertyExt.cc @@ -42,8 +42,8 @@ using sta::Report; using sta::Debug; using sta::Network; using sta::LibertyReader; -using sta::LibertyAttr; using sta::LibertyGroup; +using sta::LibertySimpleAttr; using sta::TimingGroup; using sta::LibertyCell; using sta::LibertyPort; @@ -164,13 +164,6 @@ class BigcoLibertyBuilder : public LibertyBuilder public: virtual LibertyCell *makeCell(LibertyLibrary *library, const char *name, const char *filename); - -protected: - virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs) override; }; LibertyCell * @@ -182,16 +175,6 @@ BigcoLibertyBuilder::makeCell(LibertyLibrary *library, const char *name, return cell; } -TimingArcSet * -BigcoLibertyBuilder::makeTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs) -{ - return cell->makeTimingArcSet(from, to, related_out, role, attrs); -} - //////////////////////////////////////////////////////////////// // Liberty reader to parse Bigco attributes. @@ -201,22 +184,18 @@ class BigcoLibertyReader : public LibertyReader BigcoLibertyReader(LibertyBuilder *builder); protected: - virtual void visitAttr1(LibertyAttr *attr); - virtual void visitAttr2(LibertyAttr *attr); - virtual void beginLibrary(LibertyGroup *group); + virtual void visitAttr1(const LibertySimpleAttr *attr); + virtual void visitAttr2(const LibertySimpleAttr *attr); + virtual void beginLibrary(const LibertyGroup *group, + const LibertyGroup *library_group); virtual TimingGroup *makeTimingGroup(int line); - virtual void beginCell(LibertyGroup *group); + virtual void beginCell(const LibertyGroup *group, + const LibertyGroup *library_group); }; BigcoLibertyReader::BigcoLibertyReader(LibertyBuilder *builder) : LibertyReader(builder) { - // Define a visitor for the "thingy" attribute. - // Note that the function descriptor passed to defineAttrVisitor - // must be defined by the LibertyVisitor class, so a number of - // extra visitor functions are pre-defined for extensions. - defineAttrVisitor("thingy", &LibertyReader::visitAttr1); - defineAttrVisitor("frob", &LibertyReader::visitAttr2); } bool @@ -228,12 +207,13 @@ libertyCellRequired(const char *) // Prune cells from liberty file based on libertyCellRequired predicate. void -BigcoLibertyReader::beginCell(LibertyGroup *group) +BigcoLibertyReader::beginCell(const LibertyGroup *group, + const LibertyGroup *library_group) { const char *name = group->firstName(); if (name && libertyCellRequired(name)) - LibertyReader::beginCell(group); + LibertyReader::beginCell(group, library_group); } TimingGroup * @@ -244,15 +224,16 @@ BigcoLibertyReader::makeTimingGroup(int line) // Called at the beginning of a library group. void -BigcoLibertyReader::beginLibrary(LibertyGroup *group) +BigcoLibertyReader::beginLibrary(const LibertyGroup *group, + const LibertyGroup *library_group) { - LibertyReader::beginLibrary(group); + LibertyReader::beginLibrary(group, library_group); // Do Bigco stuff here. printf("Bigco was here.\n"); } void -BigcoLibertyReader::visitAttr1(LibertyAttr *attr) +BigcoLibertyReader::visitAttr1(const LibertySimpleAttr *attr) { const char *thingy = getAttrString(attr); if (thingy) { @@ -263,7 +244,7 @@ BigcoLibertyReader::visitAttr1(LibertyAttr *attr) } void -BigcoLibertyReader::visitAttr2(LibertyAttr *attr) +BigcoLibertyReader::visitAttr2(const LibertySimpleAttr *attr) { const char *frob = getAttrString(attr); if (frob) { diff --git a/liberty/LibertyLex.ll b/liberty/LibertyLex.ll index 6df7d07b5..22edd6967 100644 --- a/liberty/LibertyLex.ll +++ b/liberty/LibertyLex.ll @@ -87,14 +87,14 @@ EOL \r?\n {FLOAT}{TOKEN_END} { /* Push back the TOKEN_END character. */ yyless(yyleng - 1); - yylval->emplace(strtod(yytext, nullptr)); + yylval->emplace(strtof(yytext, nullptr)); return token::FLOAT; } {ALPHA}({ALPHA}|_|{DIGIT})*{TOKEN_END} { /* Push back the TOKEN_END character. */ yyless(yyleng - 1); - yylval->emplace(yytext); + yylval->emplace(yytext, yyleng); return token::KEYWORD; } @@ -107,7 +107,7 @@ EOL \r?\n {TOKEN}{TOKEN_END} { /* Push back the TOKEN_END character. */ yyless(yyleng - 1); - yylval->emplace(yytext); + yylval->emplace(yytext, yyleng); return token::STRING; } @@ -141,7 +141,7 @@ EOL \r?\n {EOL} { error("unterminated string constant"); BEGIN(INITIAL); - yylval->emplace(token_); + yylval->emplace(token_); return token::STRING; } diff --git a/liberty/LibertyParse.yy b/liberty/LibertyParse.yy index 7423c4fe0..a3d4b7159 100644 --- a/liberty/LibertyParse.yy +++ b/liberty/LibertyParse.yy @@ -52,7 +52,7 @@ sta::LibertyParse::error(const location_type &loc, %require "3.2" %skeleton "lalr1.cc" -%debug +//%debug %define api.namespace {sta} %locations %define api.location.file "LibertyLocation.hh" @@ -72,7 +72,7 @@ sta::LibertyParse::error(const location_type &loc, %left '^' %left '!' -%type statement complex_attr simple_attr variable group file +%type statement complex_attr simple_attr variable group file %type attr_values %type attr_value %type string expr expr_term expr_term1 volt_expr @@ -158,11 +158,11 @@ string: attr_value: FLOAT - { $$ = reader->makeFloatAttrValue($1); } + { $$ = reader->makeAttrValueFloat($1); } | expr - { $$ = reader->makeStringAttrValue(std::move($1)); } + { $$ = reader->makeAttrValueString(std::move($1)); } | volt_expr - { $$ = reader->makeStringAttrValue(std::move($1)); } + { $$ = reader->makeAttrValueString(std::move($1)); } ; /* Voltage expressions are ignored. */ diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index 9cf527021..4660aa421 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -70,21 +70,23 @@ LibertyParser::setFilename(const string &filename) filename_ = filename; } -LibertyStmt * -LibertyParser::makeDefine(LibertyAttrValueSeq *values, +LibertyDefine * +LibertyParser::makeDefine(const LibertyAttrValueSeq *values, int line) { LibertyDefine *define = nullptr; if (values->size() == 3) { - std::string define_name = (*values)[0]->stringValue(); + const std::string &define_name = (*values)[0]->stringValue(); const std::string &group_type_name = (*values)[1]->stringValue(); const std::string &value_type_name = (*values)[2]->stringValue(); - LibertyAttrType value_type = attrValueType(value_type_name.c_str()); - LibertyGroupType group_type = groupType(group_type_name.c_str()); - define = new LibertyDefine(std::move(define_name), group_type, - value_type, line); + LibertyAttrType value_type = attrValueType(value_type_name); + LibertyGroupType group_type = groupType(group_type_name); + define = new LibertyDefine(std::move(define_name), group_type, value_type, line); LibertyGroup *group = this->group(); - group->addStmt(define); + group->addDefine(define); + for (auto value : *values) + delete value; + delete values; } else report_->fileWarn(24, filename_.c_str(), line, @@ -96,42 +98,47 @@ LibertyParser::makeDefine(LibertyAttrValueSeq *values, // used to define valid attribute types. Beyond "string" these are // guesses. LibertyAttrType -LibertyParser::attrValueType(const char *value_type_name) +LibertyParser::attrValueType(const std::string &value_type_name) { - if (stringEq(value_type_name, "string")) + if (value_type_name == "string") return LibertyAttrType::attr_string; - else if (stringEq(value_type_name, "integer")) + else if (value_type_name == "integer") return LibertyAttrType::attr_int; - else if (stringEq(value_type_name, "float")) + else if (value_type_name == "float") return LibertyAttrType::attr_double; - else if (stringEq(value_type_name, "boolean")) + else if (value_type_name == "boolean") return LibertyAttrType::attr_boolean; else return LibertyAttrType::attr_unknown; } LibertyGroupType -LibertyParser::groupType(const char *group_type_name) +LibertyParser::groupType(const std::string &group_type_name) { - if (stringEq(group_type_name, "library")) + if (group_type_name == "library") return LibertyGroupType::library; - else if (stringEq(group_type_name, "cell")) + else if (group_type_name == "cell") return LibertyGroupType::cell; - else if (stringEq(group_type_name, "pin")) + else if (group_type_name == "pin") return LibertyGroupType::pin; - else if (stringEq(group_type_name, "timing")) + else if (group_type_name == "timing") return LibertyGroupType::timing; else return LibertyGroupType::unknown; } void -LibertyParser::groupBegin(std::string type, +LibertyParser::groupBegin(const std::string type, LibertyAttrValueSeq *params, int line) { - LibertyGroup *group = new LibertyGroup(std::move(type), params, line); - group_visitor_->begin(group); + 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); group_stack_.push_back(group); } @@ -139,20 +146,13 @@ LibertyGroup * LibertyParser::groupEnd() { LibertyGroup *group = this->group(); - group_visitor_->end(group); group_stack_.pop_back(); LibertyGroup *parent = group_stack_.empty() ? nullptr : group_stack_.back(); - if (parent && group_visitor_->save(group)) { - parent->addStmt(group); - return group; - } - else if (group_visitor_->save(group)) - return group; - else { - delete group; - return nullptr; - } + if (parent) + parent->addSubgroup(group); + group_visitor_->end(group, parent); + return group; } LibertyGroup * @@ -167,308 +167,452 @@ LibertyParser::deleteGroups() deleteContents(group_stack_); } -LibertyStmt * -LibertyParser::makeSimpleAttr(std::string name, - LibertyAttrValue *value, +LibertySimpleAttr * +LibertyParser::makeSimpleAttr(const std::string name, + const LibertyAttrValue *value, int line) { - LibertyAttr *attr = new LibertySimpleAttr(std::move(name), value, line); - group_visitor_->visitAttr(attr); + LibertySimpleAttr *attr = new LibertySimpleAttr(std::move(name), + std::move(*value), line); + delete value; LibertyGroup *group = this->group(); - if (group && group_visitor_->save(attr)) { - group->addStmt(attr); - return attr; - } - else { - delete attr; - return nullptr; - } + group->addAttr(attr); + group_visitor_->visitAttr(attr); + return attr; } -LibertyStmt * -LibertyParser::makeComplexAttr(std::string name, - LibertyAttrValueSeq *values, +LibertyComplexAttr * +LibertyParser::makeComplexAttr(const std::string name, + const LibertyAttrValueSeq *values, int line) { // Defines have the same syntax as complex attributes. // Detect and convert them. if (name == "define") { - LibertyStmt *define = makeDefine(values, line); - deleteContents(values); - delete values; - return define; + makeDefine(values, line); + return nullptr; // Define is not a complex attr; already added to group } else { - LibertyAttr *attr = new LibertyComplexAttr(std::move(name), values, line); + LibertyComplexAttr *attr = new LibertyComplexAttr(std::move(name), + std::move(*values), + line); + delete values; + LibertyGroup *group = this->group(); + group->addAttr(attr); group_visitor_->visitAttr(attr); - if (group_visitor_->save(attr)) { - LibertyGroup *group = this->group(); - group->addStmt(attr); - return attr; - } - delete attr; - return nullptr; + return attr; } } -LibertyStmt * -LibertyParser::makeVariable(std::string var, +LibertyVariable * +LibertyParser::makeVariable(const std::string var, float value, int line) { LibertyVariable *variable = new LibertyVariable(std::move(var), value, line); + LibertyGroup *group = this->group(); + group->addVariable(variable); group_visitor_->visitVariable(variable); - if (group_visitor_->save(variable)) - return variable; - else { - delete variable; - return nullptr; - } + return variable; } LibertyAttrValue * -LibertyParser::makeStringAttrValue(std::string value) +LibertyParser::makeAttrValueString(std::string value) { - return new LibertyStringAttrValue(std::move(value)); + return new LibertyAttrValue(std::move(value)); } LibertyAttrValue * -LibertyParser::makeFloatAttrValue(float value) +LibertyParser::makeAttrValueFloat(float value) { - return new LibertyFloatAttrValue(value); + return new LibertyAttrValue(value); } -const std::string & -LibertyFloatAttrValue::stringValue() const +//////////////////////////////////////////////////////////////// + +LibertyScanner::LibertyScanner(std::istream *stream, + const char *filename, + LibertyParser *reader, + Report *report) : + yyFlexLexer(stream), + stream_(stream), + filename_(filename), + reader_(reader), + report_(report), + stream_prev_(nullptr) { - criticalError(1127, "LibertyStringAttrValue called for float value"); - static std::string null; - return null; } -//////////////////////////////////////////////////////////////// +bool +LibertyScanner::includeBegin() +{ + if (stream_prev_ != nullptr) + error("nested include_file's are not supported"); + else { + // include_file(filename); + static const std::regex include_regexp("include_file *\\( *([^)]+) *\\) *;?"); + std::cmatch matches; + if (std::regex_match(yytext, matches, include_regexp)) { + string filename = matches[1].str(); + gzstream::igzstream *stream = new gzstream::igzstream(filename.c_str()); + if (stream->is_open()) { + yypush_buffer_state(yy_create_buffer(stream, 16384)); -LibertyStmt::LibertyStmt(int line) : - line_(line) + filename_prev_ = filename_; + stream_prev_ = stream_; + + filename_ = filename; + reader_->setFilename(filename); + stream_ = stream; + return true; + } + else { + report_->fileWarn(25, filename_.c_str(), yylineno, + "cannot open include file %s.", filename.c_str()); + delete stream; + } + } + else + error("include_file syntax error."); + } + return false; +} + +void +LibertyScanner::fileEnd() { + if (stream_prev_) + delete stream_; + stream_ = stream_prev_; + filename_ = filename_prev_; + stream_prev_ = nullptr; + + yypop_buffer_state(); } +void +LibertyScanner::error(const char *msg) +{ + report_->fileError(1866, filename_.c_str(), lineno(), "%s", msg); +} + +//////////////////////////////////////////////////////////////// + LibertyGroup::LibertyGroup(std::string type, - LibertyAttrValueSeq *params, + LibertyAttrValueSeq params, int line) : - LibertyStmt(line), type_(std::move(type)), - params_(params), - stmts_(nullptr) + params_(std::move(params)), + line_(line) { } +LibertyGroup::~LibertyGroup() +{ + clear(); +} + void -LibertyGroup::addStmt(LibertyStmt *stmt) +LibertyGroup::clear() { - if (stmts_ == nullptr) - stmts_ = new LibertyStmtSeq; - stmts_->push_back(stmt); + deleteContents(params_); + deleteContents(simple_attr_map_); + for (auto &attr : complex_attr_map_) + deleteContents(attr.second); + complex_attr_map_.clear(); + deleteContents(subgroups_); + subgroup_map_.clear(); + deleteContents(define_map_); + deleteContents(variables_); } -LibertyGroup::~LibertyGroup() +void +LibertyGroup::addSubgroup(LibertyGroup *subgroup) { - if (params_) { - deleteContents(params_); - delete params_; - } - if (stmts_) { - deleteContents(stmts_); - delete stmts_; + subgroups_.push_back(subgroup); + subgroup_map_[subgroup->type()].push_back(subgroup); +} + +void +LibertyGroup::deleteSubgroup(const LibertyGroup *subgroup) +{ + if (subgroup == subgroups_.back()) { + subgroups_.pop_back(); + subgroup_map_[subgroup->type()].pop_back(); + delete subgroup; } + else + criticalError(1128, "LibertyAttrValue::floatValue() called on string"); } -const char * -LibertyGroup::firstName() +void +LibertyGroup::addDefine(LibertyDefine *define) { - if (params_ && params_->size() > 0) { - LibertyAttrValue *value = (*params_)[0]; - if (value->isString()) - return value->stringValue().c_str(); + const string &define_name = define->name(); + LibertyDefine *prev_define = findKey(define_map_, define_name); + if (prev_define) { + define_map_.erase(define_name); + delete prev_define; } - return nullptr; + define_map_[define_name] = define; +} + +void +LibertyGroup::addAttr(LibertySimpleAttr *attr) +{ + // Only keep the most recent simple attribute value. + const auto &itr = simple_attr_map_.find(attr->name()); + if (itr != simple_attr_map_.end()) + delete itr->second; + simple_attr_map_[attr->name()] = attr; +} + +void +LibertyGroup::addAttr(LibertyComplexAttr *attr) +{ + complex_attr_map_[attr->name()].push_back(attr); +} + +void +LibertyGroup::addVariable(LibertyVariable *var) +{ + variables_.push_back(var); } const char * -LibertyGroup::secondName() +LibertyGroup::firstName() const { - if (params_ && params_->size() > 1) { - LibertyAttrValue *value = (*params_)[1]; + if (params_.size() >= 1) { + LibertyAttrValue *value = params_[0]; if (value->isString()) return value->stringValue().c_str(); } return nullptr; } -//////////////////////////////////////////////////////////////// - -LibertyAttr::LibertyAttr(std::string name, - int line) : - LibertyStmt(line), - name_(std::move(name)) +const char * +LibertyGroup::secondName() const { + LibertyAttrValue *value = params_[1]; + if (value->isString()) + return value->stringValue().c_str(); + else + return nullptr; } -LibertySimpleAttr::LibertySimpleAttr(std::string name, - LibertyAttrValue *value, - int line) : - LibertyAttr(std::move(name), line), - value_(value) +const LibertyGroupSeq & +LibertyGroup::findSubgroups(const std::string type) const { + return findKeyValue(subgroup_map_, type); } -LibertySimpleAttr::~LibertySimpleAttr() +const LibertyGroup * +LibertyGroup::findSubgroup(const std::string type) const { - delete value_; + const LibertyGroupSeq &groups = findKeyValue(subgroup_map_, type); + if (groups.size() >= 1) + return groups[0]; + else + return nullptr; } -LibertyAttrValueSeq * -LibertySimpleAttr::values() const +const LibertySimpleAttr * +LibertyGroup::findSimpleAttr(const std::string attr_name) const { - criticalError(1125, "valueIterator called for LibertySimpleAttribute"); - return nullptr; + return findKeyValue(simple_attr_map_, attr_name); } -//////////////////////////////////////////////////////////////// - -LibertyComplexAttr::LibertyComplexAttr(std::string name, - LibertyAttrValueSeq *values, - int line) : - LibertyAttr(std::move(name), line), - values_(values) +const LibertyComplexAttrSeq & +LibertyGroup::findComplexAttrs(const std::string attr_name) const { + return findKeyValue(complex_attr_map_, attr_name); } -LibertyComplexAttr::~LibertyComplexAttr() +const LibertyComplexAttr * +LibertyGroup::findComplexAttr(const std::string attr_name) const { - if (values_) { - deleteContents(values_); - delete values_; - } + const LibertyComplexAttrSeq &attrs = findKeyValue(complex_attr_map_, attr_name); + if (attrs.size() >= 1) + return attrs[0]; + else + return nullptr; } -LibertyAttrValue * -LibertyComplexAttr::firstValue() +const std::string * +LibertyGroup::findAttrString(const std::string attr_name) const { - if (values_ && values_->size() > 0) - return (*values_)[0]; + const LibertySimpleAttr *attr = findSimpleAttr(attr_name); + if (attr) + return &attr->value().stringValue(); else return nullptr; } -LibertyStringAttrValue::LibertyStringAttrValue(std::string value) : - LibertyAttrValue(), - value_(std::move(value)) -{ +void +LibertyGroup::findAttrFloat(const std::string attr_name, + // Return values. + float &value, + bool &exists) const +{ + const LibertySimpleAttr *attr = findSimpleAttr(attr_name); + if (attr) { + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isFloat()) { + value = attr_value.floatValue(); + exists = true; + return; + } + else { + // Possibly quoted string float. + const std::string &float_str = attr_value.stringValue(); + char *end = nullptr; + value = std::strtof(float_str.c_str(), &end); + if (end) { + exists = true; + return; + } + } + } + exists = false; } -float -LibertyStringAttrValue::floatValue() const +void +LibertyGroup::findAttrInt(const std::string attr_name, + // Return values. + int &value, + bool &exists) const +{ + const LibertySimpleAttr *attr = findSimpleAttr(attr_name); + if (attr) { + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isFloat()) { + value = static_cast(attr_value.floatValue()); + exists = true; + return; + } + } + exists = false; +} + +//////////////////////////////////////////////////////////////// + +LibertySimpleAttr::LibertySimpleAttr(const std::string name, + const LibertyAttrValue value, + int line) : + name_(std::move(name)), + line_(line), + value_(std::move(value)) { - criticalError(1126, "LibertyStringAttrValue called for float value"); - return 0.0; } -LibertyFloatAttrValue::LibertyFloatAttrValue(float value) : - value_(value) +const std::string * +LibertySimpleAttr::stringValue() const { + return &value().stringValue(); } //////////////////////////////////////////////////////////////// -LibertyDefine::LibertyDefine(std::string name, - LibertyGroupType group_type, - LibertyAttrType value_type, - int line) : - LibertyStmt(line), +LibertyComplexAttr::LibertyComplexAttr(std::string name, + const LibertyAttrValueSeq values, + int line) : name_(std::move(name)), - group_type_(group_type), - value_type_(value_type) + values_(std::move(values)), + line_(line) { } -//////////////////////////////////////////////////////////////// +LibertyComplexAttr::~LibertyComplexAttr() +{ + deleteContents(values_); +} -LibertyVariable::LibertyVariable(std::string var, - float value, - int line) : - LibertyStmt(line), - var_(std::move(var)), - value_(value) +const LibertyAttrValue * +LibertyComplexAttr::firstValue() const { + if (values_.size() > 0) + return values_[0]; + else + return nullptr; } //////////////////////////////////////////////////////////////// -LibertyScanner::LibertyScanner(std::istream *stream, - const char *filename, - LibertyParser *reader, - Report *report) : - yyFlexLexer(stream), - stream_(stream), - filename_(filename), - reader_(reader), - report_(report), - stream_prev_(nullptr) +LibertyAttrValue::LibertyAttrValue(std::string value) : + string_value_(std::move(value)) +{ +} + +LibertyAttrValue::LibertyAttrValue(float value) : + float_value_(value) { } bool -LibertyScanner::includeBegin() +LibertyAttrValue::isFloat() const { - if (stream_prev_ != nullptr) - error("nested include_file's are not supported"); - else { - // include_file(filename); - std::regex include_regexp("include_file *\\( *([^)]+) *\\) *;?"); - std::cmatch matches; - if (std::regex_match(yytext, matches, include_regexp)) { - string filename = matches[1].str(); - gzstream::igzstream *stream = new gzstream::igzstream(filename.c_str()); - if (stream->is_open()) { - yypush_buffer_state(yy_create_buffer(stream, 256)); + return string_value_.empty(); +} - filename_prev_ = filename_; - stream_prev_ = stream_; +bool +LibertyAttrValue::isString() const +{ + return !string_value_.empty(); +} - filename_ = filename; - reader_->setFilename(filename); - stream_ = stream; - return true; - } - else { - report_->fileWarn(25, filename_.c_str(), yylineno, - "cannot open include file %s.", filename.c_str()); - delete stream; - } +float +LibertyAttrValue::floatValue() const +{ + if (!string_value_.empty()) + criticalError(1127, "LibertyAttrValue::floatValue() called on string"); + return float_value_; +} + +void +LibertyAttrValue::floatValue(// Return values. + float &value, + bool &valid) const +{ + valid = false; + if (string_value_.empty()) { + value = float_value_; + valid = true; + } + else { + // Some floats are enclosed in quotes. + char *end; + value = strtof(string_value_.c_str(), &end); + if ((*end == '\0' + || isspace(*end)) + // strtof support INF as a valid float. + && string_value_ != "inf") { + valid = true; } - else - error("include_file syntax error."); } - return false; } -void -LibertyScanner::fileEnd() -{ - if (stream_prev_) - delete stream_; - stream_ = stream_prev_; - filename_ = filename_prev_; - stream_prev_ = nullptr; +//////////////////////////////////////////////////////////////// - yypop_buffer_state(); +LibertyDefine::LibertyDefine(std::string name, + LibertyGroupType group_type, + LibertyAttrType value_type, + int line) : + name_(std::move(name)), + group_type_(group_type), + value_type_(value_type), + line_(line) +{ } -void -LibertyScanner::error(const char *msg) +//////////////////////////////////////////////////////////////// + +LibertyVariable::LibertyVariable(std::string var, + float value, + int line) : + var_(std::move(var)), + value_(value), + line_(line) { - report_->fileError(1866, filename_.c_str(), lineno(), "%s", msg); } } // namespace diff --git a/liberty/LibertyParser.hh b/liberty/LibertyParser.hh index e27d859af..2f533c364 100644 --- a/liberty/LibertyParser.hh +++ b/liberty/LibertyParser.hh @@ -34,20 +34,22 @@ namespace sta { class Report; class LibertyGroupVisitor; -class LibertyStmt; class LibertyGroup; -class LibertyAttr; class LibertyDefine; +class LibertySimpleAttr; +class LibertyComplexAttr; class LibertyAttrValue; class LibertyVariable; class LibertyScanner; -using LibertyStmtSeq = std::vector; using LibertyGroupSeq = std::vector; -using LibertyAttrSeq = std::vector; -using LibertyAttrMap = std::map; +using LibertySubGroupMap = std::map; +using LibertySimpleAttrMap = std::map; +using LibertyComplexAttrSeq = std::vector; +using LibertyComplexAttrMap = std::map; using LibertyDefineMap = std::map; using LibertyAttrValueSeq = std::vector; +using LibertyVariableSeq = std::vector; using LibertyVariableMap = std::map; using LibertyGroupVisitorMap = std::map; @@ -65,27 +67,27 @@ public: const std::string &filename() const { return filename_; } void setFilename(const std::string &filename); Report *report() const { return report_; } - LibertyStmt *makeDefine(LibertyAttrValueSeq *values, - int line); - LibertyAttrType attrValueType(const char *value_type_name); - LibertyGroupType groupType(const char *group_type_name); - void groupBegin(std::string type, + LibertyDefine *makeDefine(const LibertyAttrValueSeq *values, + int line); + LibertyAttrType attrValueType(const std::string &value_type_name); + LibertyGroupType groupType(const std::string &group_type_name); + void groupBegin(const std::string type, LibertyAttrValueSeq *params, int line); LibertyGroup *groupEnd(); LibertyGroup *group(); void deleteGroups(); - LibertyStmt *makeSimpleAttr(std::string name, - LibertyAttrValue *value, - int line); - LibertyStmt *makeComplexAttr(std::string name, - LibertyAttrValueSeq *values, - int line); - LibertyAttrValue *makeStringAttrValue(std::string value); - LibertyAttrValue *makeFloatAttrValue(float value); - LibertyStmt *makeVariable(std::string var, - float value, - int line); + LibertySimpleAttr *makeSimpleAttr(const std::string name, + const LibertyAttrValue *value, + int line); + LibertyComplexAttr *makeComplexAttr(const std::string name, + const LibertyAttrValueSeq *values, + int line); + LibertyAttrValue *makeAttrValueString(const std::string value); + LibertyAttrValue *makeAttrValueFloat(float value); + LibertyVariable *makeVariable(const std::string var, + float value, + int line); private: std::string filename_; @@ -94,178 +96,171 @@ private: LibertyGroupSeq group_stack_; }; -// Abstract base class for liberty statements. -class LibertyStmt +// Attribute values are a string or float. +class LibertyAttrValue { public: - LibertyStmt(int line); - virtual ~LibertyStmt() {} - int line() const { return line_; } - virtual bool isGroup() const { return false; } - virtual bool isAttribute() const { return false; } - virtual bool isSimpleAttr() const { return false; } - virtual bool isComplexAttr() const { return false; } - virtual bool isDefine() const { return false; } - virtual bool isVariable() const { return false; } + LibertyAttrValue() {} + LibertyAttrValue(float value); + LibertyAttrValue(std::string value); + bool isString() const; + bool isFloat() const; + float floatValue() const; + void floatValue(// Return values. + float &value, + bool &valid) const; + const std::string &stringValue() const { return string_value_; } -protected: - int line_; +private: + float float_value_; + std::string string_value_; }; // Groups are a type keyword with a set of parameters and statements // enclosed in brackets. // type([param1][, param2]...) { stmts.. } -class LibertyGroup : public LibertyStmt +class LibertyGroup { public: - LibertyGroup(std::string type, - LibertyAttrValueSeq *params, + LibertyGroup(const std::string type, + const LibertyAttrValueSeq params, int line); - virtual ~LibertyGroup(); - virtual bool isGroup() const { return true; } + ~LibertyGroup(); + void clear(); const std::string &type() const { return type_; } - LibertyAttrValueSeq *params() const { return params_; } + const LibertyAttrValueSeq ¶ms() const { return params_; } // First param as a string. - const char *firstName(); + const char *firstName() const; // Second param as a string. - const char *secondName(); - void addStmt(LibertyStmt *stmt); - LibertyStmtSeq *stmts() const { return stmts_; } + const char *secondName() const; + int line() const { return line_; } -protected: - void parseNames(LibertyAttrValueSeq *values); + const LibertyGroupSeq &findSubgroups(const std::string type) const; + const LibertyGroup *findSubgroup(const std::string type) const; + const LibertySimpleAttr *findSimpleAttr(const std::string attr_name) const; + const LibertyComplexAttrSeq &findComplexAttrs(const std::string attr_name) const; + const LibertyComplexAttr *findComplexAttr(const std::string attr_name) const; + const std::string *findAttrString(const std::string attr_name) const; + void findAttrFloat(const std::string attr_name, + // Return values. + float &value, + bool &exists) const; + void findAttrInt(const std::string attr_name, + // Return values. + int &value, + bool &exists) const; + + const LibertyGroupSeq &subgroups() const { return subgroups_; } + const LibertyDefineMap &defineMap() const { return define_map_; } + + void addSubgroup(LibertyGroup *subgroup); + void deleteSubgroup(const LibertyGroup *subgroup); + void addAttr(LibertySimpleAttr *attr); + void addAttr(LibertyComplexAttr *attr); + void addDefine(LibertyDefine *define); + void addVariable(LibertyVariable *var); +protected: std::string type_; - LibertyAttrValueSeq *params_; - LibertyStmtSeq *stmts_; + LibertyAttrValueSeq params_; + int line_; + + LibertySimpleAttrMap simple_attr_map_; + LibertyComplexAttrMap complex_attr_map_; + LibertyGroupSeq subgroups_; + LibertySubGroupMap subgroup_map_; + LibertyDefineMap define_map_; + LibertyVariableSeq variables_; }; -// Abstract base class for attributes. -class LibertyAttr : public LibertyStmt +class LibertyGroupLineLess { public: - LibertyAttr(std::string name, - int line); - const std::string &name() const { return name_; } - virtual LibertyAttrValueSeq *values() const = 0; - virtual LibertyAttrValue *firstValue() = 0; - -protected: - std::string name_; + bool + operator()(const LibertyGroup *group1, + const LibertyGroup *group2) const { + return group1->line() < group2->line(); + } }; -// Abstract base class for simple attributes. -// name : value; -class LibertySimpleAttr : public LibertyAttr +// Simple attributes: name : value; +class LibertySimpleAttr { public: - LibertySimpleAttr(std::string name, - LibertyAttrValue *value, + LibertySimpleAttr(const std::string name, + const LibertyAttrValue value, int line); - virtual ~LibertySimpleAttr(); - bool isSimpleAttr() const override { return true; }; - LibertyAttrValue *firstValue() override { return value_; }; - LibertyAttrValueSeq *values() const override; + const std::string &name() const { return name_; } + const LibertyAttrValue &value() const { return value_; }; + const std::string *stringValue() const; + int line() const { return line_; } private: - LibertyAttrValue *value_; + std::string name_; + int line_; + LibertyAttrValue value_; }; // Complex attributes have multiple values. // name(attr_value1[, attr_value2]...); -class LibertyComplexAttr : public LibertyAttr +class LibertyComplexAttr { public: - LibertyComplexAttr(std::string name, - LibertyAttrValueSeq *values, + LibertyComplexAttr(const std::string name, + const LibertyAttrValueSeq values, int line); - virtual ~LibertyComplexAttr(); - bool isComplexAttr() const override { return true; }; - LibertyAttrValue *firstValue() override ; - LibertyAttrValueSeq *values() const override { return values_; } - -private: - LibertyAttrValueSeq *values_; -}; - -// Attribute values are a string or float. -class LibertyAttrValue -{ -public: - LibertyAttrValue() {} - virtual ~LibertyAttrValue() {} - virtual bool isString() const = 0; - virtual bool isFloat() const = 0; - virtual float floatValue() const = 0; - virtual const std::string &stringValue() const = 0; -}; - -class LibertyStringAttrValue : public LibertyAttrValue -{ -public: - LibertyStringAttrValue(std::string value); - virtual ~LibertyStringAttrValue() {} - bool isFloat() const override { return false; } - bool isString() const override { return true; } - float floatValue() const override ; - const std::string &stringValue() const override { return value_; } - -private: - std::string value_; -}; - -class LibertyFloatAttrValue : public LibertyAttrValue -{ -public: - LibertyFloatAttrValue(float value); - virtual ~LibertyFloatAttrValue() {} - bool isString() const override { return false; } - bool isFloat() const override { return true; } - float floatValue() const override { return value_; } - const std::string &stringValue() const override; + ~LibertyComplexAttr(); + const std::string &name() const { return name_; } + const LibertyAttrValue *firstValue() const; + const LibertyAttrValueSeq &values() const { return values_; } + int line() const { return line_; } private: - float value_; + std::string name_; + LibertyAttrValueSeq values_; + int line_; }; // Define statements define new simple attributes. // define(attribute_name, group_name, attribute_type); // attribute_type is string|integer|float. -class LibertyDefine : public LibertyStmt +class LibertyDefine { public: LibertyDefine(std::string name, LibertyGroupType group_type, LibertyAttrType value_type, int line); - virtual bool isDefine() const { return true; } const std::string &name() const { return name_; } LibertyGroupType groupType() const { return group_type_; } LibertyAttrType valueType() const { return value_type_; } + int line() const { return line_; } private: std::string name_; LibertyGroupType group_type_; LibertyAttrType value_type_; + int line_; }; // The Liberty User Guide Version 2003.12 fails to document variables. // var = value; // The only example I have only uses float values, so I am assuming // that is all that is supported (which is probably wrong). -class LibertyVariable : public LibertyStmt +class LibertyVariable { public: LibertyVariable(std::string var, float value, int line); - bool isVariable() const override { return true; } + int line() const { return line_; } const std::string &variable() const { return var_; } float value() const { return value_; } private: std::string var_; float value_; + int line_; }; class LibertyGroupVisitor @@ -273,14 +268,13 @@ class LibertyGroupVisitor public: LibertyGroupVisitor() {} virtual ~LibertyGroupVisitor() {} - virtual void begin(LibertyGroup *group) = 0; - virtual void end(LibertyGroup *group) = 0; - virtual void visitAttr(LibertyAttr *attr) = 0; + virtual void begin(const LibertyGroup *group, + LibertyGroup *parent_group) = 0; + virtual void end(const LibertyGroup *group, + LibertyGroup *parent_group) = 0; + virtual void visitAttr(const LibertySimpleAttr *attr) = 0; + virtual void visitAttr(const LibertyComplexAttr *attr) = 0; virtual void visitVariable(LibertyVariable *variable) = 0; - // Predicates to save parse structure after visits. - virtual bool save(LibertyGroup *group) = 0; - virtual bool save(LibertyAttr *attr) = 0; - virtual bool save(LibertyVariable *variable) = 0; }; void diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index edcde894c..974ab7ec5 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -26,6 +26,7 @@ #include #include +#include #include #include "ContainerHelpers.hh" @@ -55,9 +56,6 @@ extern int LibertyParse_debug; namespace sta { -using std::make_shared; -using std::string; - static void scaleFloats(FloatSeq &floats, float scale); @@ -74,76 +72,19 @@ readLibertyFile(const char *filename, LibertyReader::LibertyReader(const char *filename, bool infer_latches, Network *network) : - LibertyGroupVisitor() + LibertyGroupVisitor(), + filename_(filename), + infer_latches_(infer_latches), + report_(network->report()), + debug_(network->debug()), + network_(network), + builder_(debug_, report_), + library_(nullptr), + first_cell_(true) { - init(filename, infer_latches, network); defineVisitors(); } -void -LibertyReader::init(const char *filename, - bool infer_latches, - Network *network) -{ - filename_ = filename; - infer_latches_ = infer_latches; - report_ = network->report(); - debug_ = network->debug(); - network_ = network; - var_map_ = nullptr; - library_ = nullptr; - wireload_ = nullptr; - wireload_selection_ = nullptr; - default_wireload_ = nullptr; - default_wireload_selection_ = nullptr; - scale_factors_ = nullptr; - save_scale_factors_ = nullptr; - tbl_template_ = nullptr; - cell_ = nullptr; - save_cell_ = nullptr; - scaled_cell_owner_ = nullptr; - test_cell_ = nullptr; - ocv_derate_name_ = nullptr; - op_cond_ = nullptr; - ports_ = nullptr; - port_group_ = nullptr; - saved_ports_ = nullptr; - saved_port_group_ = nullptr; - in_bus_ = false; - in_bundle_ = false; - in_ccsn_ = false; - in_ecsm_waveform_ = false; - sequential_ = nullptr; - statetable_ = nullptr; - timing_ = nullptr; - internal_power_ = nullptr; - leakage_power_ = nullptr; - table_ = nullptr; - rf_ = nullptr; - index_ = 0; - table_model_scale_ = 1.0; - mode_def_ = nullptr; - mode_value_ = nullptr; - ocv_derate_ = nullptr; - pg_port_ = nullptr; - default_operating_condition_ = nullptr; - receiver_model_ = nullptr; - - builder_.init(debug_, report_); - - for (auto rf_index : RiseFall::rangeIndex()) { - have_input_threshold_[rf_index] = false; - have_output_threshold_[rf_index] = false; - have_slew_lower_threshold_[rf_index] = false; - have_slew_upper_threshold_[rf_index] = false; - } -} - -LibertyReader::~LibertyReader() -{ - delete var_map_; -} - LibertyLibrary * LibertyReader::readLibertyFile(const char *filename) { @@ -157,498 +98,179 @@ LibertyReader::defineGroupVisitor(const char *type, LibraryGroupVisitor begin_visitor, LibraryGroupVisitor end_visitor) { - group_begin_map_[type] = begin_visitor; - group_end_map_[type] = end_visitor; -} - -void -LibertyReader::defineAttrVisitor(const char *attr_name, - LibraryAttrVisitor visitor) -{ - attr_visitor_map_[attr_name] = visitor; + if (begin_visitor) + group_begin_map_[type] = begin_visitor; + if (end_visitor) + group_end_map_[type] = end_visitor; } void LibertyReader::defineVisitors() { - // Library defineGroupVisitor("library", &LibertyReader::beginLibrary, &LibertyReader::endLibrary); - defineAttrVisitor("time_unit", &LibertyReader::visitTimeUnit); - defineAttrVisitor("pulling_resistance_unit", - &LibertyReader::visitPullingResistanceUnit); - defineAttrVisitor("resistance_unit", &LibertyReader::visitResistanceUnit); - defineAttrVisitor("capacitive_load_unit", - &LibertyReader::visitCapacitiveLoadUnit); - defineAttrVisitor("voltage_unit", &LibertyReader::visitVoltageUnit); - defineAttrVisitor("current_unit", &LibertyReader::visitCurrentUnit); - defineAttrVisitor("leakage_power_unit", &LibertyReader::visitPowerUnit); - defineAttrVisitor("distance_unit", &LibertyReader::visitDistanceUnit); - defineAttrVisitor("delay_model", &LibertyReader::visitDelayModel); - defineAttrVisitor("bus_naming_style", &LibertyReader::visitBusStyle); - defineAttrVisitor("voltage_map", &LibertyReader::visitVoltageMap); - defineAttrVisitor("nom_temperature", &LibertyReader::visitNomTemp); - defineAttrVisitor("nom_voltage", &LibertyReader::visitNomVolt); - defineAttrVisitor("nom_process", &LibertyReader::visitNomProc); - defineAttrVisitor("default_inout_pin_cap", - &LibertyReader::visitDefaultInoutPinCap); - defineAttrVisitor("default_input_pin_cap", - &LibertyReader::visitDefaultInputPinCap); - defineAttrVisitor("default_output_pin_cap", - &LibertyReader::visitDefaultOutputPinCap); - defineAttrVisitor("default_max_transition", - &LibertyReader::visitDefaultMaxTransition); - defineAttrVisitor("default_max_fanout", - &LibertyReader::visitDefaultMaxFanout); - defineAttrVisitor("default_intrinsic_rise", - &LibertyReader::visitDefaultIntrinsicRise); - defineAttrVisitor("default_intrinsic_fall", - &LibertyReader::visitDefaultIntrinsicFall); - defineAttrVisitor("default_inout_pin_rise_res", - &LibertyReader::visitDefaultInoutPinRiseRes); - defineAttrVisitor("default_inout_pin_fall_res", - &LibertyReader::visitDefaultInoutPinFallRes); - defineAttrVisitor("default_output_pin_rise_res", - &LibertyReader::visitDefaultOutputPinRiseRes); - defineAttrVisitor("default_output_pin_fall_res", - &LibertyReader::visitDefaultOutputPinFallRes); - defineAttrVisitor("default_fanout_load", - &LibertyReader::visitDefaultFanoutLoad); - defineAttrVisitor("default_wire_load", - &LibertyReader::visitDefaultWireLoad); - defineAttrVisitor("default_wire_load_mode", - &LibertyReader::visitDefaultWireLoadMode); - defineAttrVisitor("default_wire_load_selection", - &LibertyReader::visitDefaultWireLoadSelection); - defineAttrVisitor("default_operating_conditions", - &LibertyReader::visitDefaultOperatingConditions); - defineAttrVisitor("input_threshold_pct_fall", - &LibertyReader::visitInputThresholdPctFall); - defineAttrVisitor("input_threshold_pct_rise", - &LibertyReader::visitInputThresholdPctRise); - defineAttrVisitor("output_threshold_pct_fall", - &LibertyReader::visitOutputThresholdPctFall); - defineAttrVisitor("output_threshold_pct_rise", - &LibertyReader::visitOutputThresholdPctRise); - defineAttrVisitor("slew_lower_threshold_pct_fall", - &LibertyReader::visitSlewLowerThresholdPctFall); - defineAttrVisitor("slew_lower_threshold_pct_rise", - &LibertyReader::visitSlewLowerThresholdPctRise); - defineAttrVisitor("slew_upper_threshold_pct_fall", - &LibertyReader::visitSlewUpperThresholdPctFall); - defineAttrVisitor("slew_upper_threshold_pct_rise", - &LibertyReader::visitSlewUpperThresholdPctRise); - defineAttrVisitor("slew_derate_from_library", - &LibertyReader::visitSlewDerateFromLibrary); - - defineGroupVisitor("lu_table_template", - &LibertyReader::beginTableTemplateDelay, - &LibertyReader::endTableTemplate); - defineGroupVisitor("output_current_template", - &LibertyReader::beginTableTemplateOutputCurrent, - &LibertyReader::endTableTemplate); - defineAttrVisitor("variable_1", &LibertyReader::visitVariable1); - defineAttrVisitor("variable_2", &LibertyReader::visitVariable2); - defineAttrVisitor("variable_3", &LibertyReader::visitVariable3); - defineAttrVisitor("index_1", &LibertyReader::visitIndex1); - defineAttrVisitor("index_2", &LibertyReader::visitIndex2); - defineAttrVisitor("index_3", &LibertyReader::visitIndex3); - - defineGroupVisitor("technology", - &LibertyReader::beginTechnology, - &LibertyReader::endTechnology); - defineGroupVisitor("rise_transition_degradation", - &LibertyReader::beginRiseTransitionDegredation, - &LibertyReader::endRiseFallTransitionDegredation); - defineGroupVisitor("fall_transition_degradation", - &LibertyReader::beginFallTransitionDegredation, - &LibertyReader::endRiseFallTransitionDegredation); - - defineGroupVisitor("type", &LibertyReader::beginType, - &LibertyReader::endType); - defineAttrVisitor("bit_from", &LibertyReader::visitBitFrom); - defineAttrVisitor("bit_to", &LibertyReader::visitBitTo); - - defineGroupVisitor("scaling_factors", &LibertyReader::beginScalingFactors, - &LibertyReader::endScalingFactors); - defineScalingFactorVisitors(); - - defineGroupVisitor("operating_conditions", &LibertyReader::beginOpCond, - &LibertyReader::endOpCond); - defineAttrVisitor("process", &LibertyReader::visitProc); - defineAttrVisitor("voltage", &LibertyReader::visitVolt); - defineAttrVisitor("temperature", &LibertyReader::visitTemp); - defineAttrVisitor("tree_type", &LibertyReader::visitTreeType); - - defineGroupVisitor("wire_load", &LibertyReader::beginWireload, - &LibertyReader::endWireload); - defineAttrVisitor("resistance", &LibertyReader::visitResistance); - defineAttrVisitor("slope", &LibertyReader::visitSlope); - defineAttrVisitor("fanout_length", &LibertyReader::visitFanoutLength); - - defineGroupVisitor("wire_load_selection", - &LibertyReader::beginWireloadSelection, - &LibertyReader::endWireloadSelection); - defineAttrVisitor("wire_load_from_area", - &LibertyReader::visitWireloadFromArea); - - // Cells - defineGroupVisitor("cell", &LibertyReader::beginCell, - &LibertyReader::endCell); - defineGroupVisitor("scaled_cell", &LibertyReader::beginScaledCell, - &LibertyReader::endScaledCell); - defineAttrVisitor("clock_gating_integrated_cell", - &LibertyReader::visitClockGatingIntegratedCell); - defineAttrVisitor("area", &LibertyReader::visitArea); - defineAttrVisitor("dont_use", &LibertyReader::visitDontUse); - defineAttrVisitor("is_macro_cell", &LibertyReader::visitIsMacro); - defineAttrVisitor("is_memory", &LibertyReader::visitIsMemory); - defineAttrVisitor("pad_cell", &LibertyReader::visitIsPadCell); - defineAttrVisitor("is_pad", &LibertyReader::visitIsPad); - defineAttrVisitor("is_clock_cell", &LibertyReader::visitIsClockCell); - defineAttrVisitor("is_level_shifter", &LibertyReader::visitIsLevelShifter); - defineAttrVisitor("level_shifter_type", &LibertyReader::visitLevelShifterType); - defineAttrVisitor("is_isolation_cell", &LibertyReader::visitIsIsolationCell); - defineAttrVisitor("always_on", &LibertyReader::visitAlwaysOn); - defineAttrVisitor("switch_cell_type", &LibertyReader::visitSwitchCellType); - defineAttrVisitor("interface_timing", &LibertyReader::visitInterfaceTiming); - defineAttrVisitor("scaling_factors", &LibertyReader::visitScalingFactors); - defineAttrVisitor("cell_footprint", &LibertyReader::visitCellFootprint); - defineAttrVisitor("user_function_class", - &LibertyReader::visitCellUserFunctionClass); - - // Pins - defineGroupVisitor("pin", &LibertyReader::beginPin,&LibertyReader::endPin); - defineGroupVisitor("bus", &LibertyReader::beginBus,&LibertyReader::endBus); - defineGroupVisitor("bundle", &LibertyReader::beginBundle, - &LibertyReader::endBundle); - defineAttrVisitor("direction", &LibertyReader::visitDirection); - defineAttrVisitor("clock", &LibertyReader::visitClock); - defineAttrVisitor("bus_type", &LibertyReader::visitBusType); - defineAttrVisitor("members", &LibertyReader::visitMembers); - defineAttrVisitor("function", &LibertyReader::visitFunction); - defineAttrVisitor("three_state", &LibertyReader::visitThreeState); - defineAttrVisitor("capacitance", &LibertyReader::visitCapacitance); - defineAttrVisitor("rise_capacitance", &LibertyReader::visitRiseCap); - defineAttrVisitor("fall_capacitance", &LibertyReader::visitFallCap); - defineAttrVisitor("rise_capacitance_range", - &LibertyReader::visitRiseCapRange); - defineAttrVisitor("fall_capacitance_range", - &LibertyReader::visitFallCapRange); - defineAttrVisitor("fanout_load", &LibertyReader::visitFanoutLoad); - defineAttrVisitor("max_fanout", &LibertyReader::visitMaxFanout); - defineAttrVisitor("min_fanout", &LibertyReader::visitMinFanout); - defineAttrVisitor("max_transition", &LibertyReader::visitMaxTransition); - defineAttrVisitor("min_transition", &LibertyReader::visitMinTransition); - defineAttrVisitor("max_capacitance", &LibertyReader::visitMaxCapacitance); - defineAttrVisitor("min_capacitance", &LibertyReader::visitMinCapacitance); - defineAttrVisitor("min_period", &LibertyReader::visitMinPeriod); - defineAttrVisitor("min_pulse_width_low", - &LibertyReader::visitMinPulseWidthLow); - defineAttrVisitor("min_pulse_width_high", - &LibertyReader::visitMinPulseWidthHigh); - defineAttrVisitor("pulse_clock", - &LibertyReader::visitPulseClock); - defineAttrVisitor("clock_gate_clock_pin", - &LibertyReader::visitClockGateClockPin); - defineAttrVisitor("clock_gate_enable_pin", - &LibertyReader::visitClockGateEnablePin); - defineAttrVisitor("clock_gate_out_pin", - &LibertyReader::visitClockGateOutPin); - defineAttrVisitor("is_pll_feedback_pin", - &LibertyReader::visitIsPllFeedbackPin); - defineAttrVisitor("signal_type", &LibertyReader::visitSignalType); - - defineAttrVisitor("isolation_cell_data_pin", - &LibertyReader::visitIsolationCellDataPin); - defineAttrVisitor("isolation_cell_enable_pin", - &LibertyReader::visitIsolationCellEnablePin); - defineAttrVisitor("level_shifter_data_pin", - &LibertyReader::visitLevelShifterDataPin); - defineAttrVisitor("switch_pin", &LibertyReader::visitSwitchPin); - - // Memory - defineGroupVisitor("memory", &LibertyReader::beginMemory, - &LibertyReader::endMemory); - - // Register/latch - defineGroupVisitor("ff", &LibertyReader::beginFF, &LibertyReader::endFF); - defineGroupVisitor("ff_bank", &LibertyReader::beginFFBank, - &LibertyReader::endFFBank); - defineGroupVisitor("latch", &LibertyReader::beginLatch, - &LibertyReader::endLatch); - defineGroupVisitor("latch_bank", &LibertyReader::beginLatchBank, - &LibertyReader::endLatchBank); - defineAttrVisitor("clocked_on", &LibertyReader::visitClockedOn); - defineAttrVisitor("enable", &LibertyReader::visitClockedOn); - defineAttrVisitor("data_in", &LibertyReader::visitDataIn); - defineAttrVisitor("next_state", &LibertyReader::visitDataIn); - defineAttrVisitor("clear", &LibertyReader::visitClear); - defineAttrVisitor("preset", &LibertyReader::visitPreset); - defineAttrVisitor("clear_preset_var1", &LibertyReader::visitClrPresetVar1); - defineAttrVisitor("clear_preset_var2", &LibertyReader::visitClrPresetVar2); - - // Statetable - defineGroupVisitor("statetable", &LibertyReader::beginStatetable, - &LibertyReader::endStatetable); - defineAttrVisitor("table", &LibertyReader::visitTable); - - defineGroupVisitor("timing", &LibertyReader::beginTiming, - &LibertyReader::endTiming); - defineAttrVisitor("related_pin", &LibertyReader::visitRelatedPin); - defineAttrVisitor("related_bus_pins", &LibertyReader::visitRelatedBusPins); - defineAttrVisitor("related_output_pin", - &LibertyReader::visitRelatedOutputPin); - defineAttrVisitor("timing_type", &LibertyReader::visitTimingType); - defineAttrVisitor("timing_sense", &LibertyReader::visitTimingSense); - defineAttrVisitor("sdf_cond_start", &LibertyReader::visitSdfCondStart); - defineAttrVisitor("sdf_cond_end", &LibertyReader::visitSdfCondEnd); - defineAttrVisitor("mode", &LibertyReader::visitMode); - defineAttrVisitor("intrinsic_rise", &LibertyReader::visitIntrinsicRise); - defineAttrVisitor("intrinsic_fall", &LibertyReader::visitIntrinsicFall); - defineAttrVisitor("rise_resistance", &LibertyReader::visitRiseResistance); - defineAttrVisitor("fall_resistance", &LibertyReader::visitFallResistance); - defineGroupVisitor("cell_rise", &LibertyReader::beginCellRise, - &LibertyReader::endCellRiseFall); - defineGroupVisitor("cell_fall", &LibertyReader::beginCellFall, - &LibertyReader::endCellRiseFall); - defineGroupVisitor("rise_transition", &LibertyReader::beginRiseTransition, - &LibertyReader::endRiseFallTransition); - defineGroupVisitor("fall_transition", &LibertyReader::beginFallTransition, - &LibertyReader::endRiseFallTransition); - defineGroupVisitor("rise_constraint", &LibertyReader::beginRiseConstraint, - &LibertyReader::endRiseFallConstraint); - defineGroupVisitor("fall_constraint", &LibertyReader::beginFallConstraint, - &LibertyReader::endRiseFallConstraint); - defineAttrVisitor("value", &LibertyReader::visitValue); - defineAttrVisitor("values", &LibertyReader::visitValues); - - defineGroupVisitor("lut", &LibertyReader::beginLut,&LibertyReader::endLut); - - defineGroupVisitor("test_cell", &LibertyReader::beginTestCell, - &LibertyReader::endTestCell); - - defineGroupVisitor("mode_definition", &LibertyReader::beginModeDef, - &LibertyReader::endModeDef); - defineGroupVisitor("mode_value", &LibertyReader::beginModeValue, - &LibertyReader::endModeValue); - defineAttrVisitor("when", &LibertyReader::visitWhen); - defineAttrVisitor("sdf_cond", &LibertyReader::visitSdfCond); - - // Power attributes. - defineGroupVisitor("power_lut_template", - &LibertyReader::beginTableTemplatePower, - &LibertyReader::endTableTemplate); - defineGroupVisitor("leakage_power", &LibertyReader::beginLeakagePower, - &LibertyReader::endLeakagePower); - defineGroupVisitor("internal_power", &LibertyReader::beginInternalPower, - &LibertyReader::endInternalPower); - // power group for both rise/fall - defineGroupVisitor("power", &LibertyReader::beginRisePower, - &LibertyReader::endPower); - defineGroupVisitor("fall_power", &LibertyReader::beginFallPower, - &LibertyReader::endRiseFallPower); - defineGroupVisitor("rise_power", &LibertyReader::beginRisePower, - &LibertyReader::endRiseFallPower); - defineAttrVisitor("related_ground_pin",&LibertyReader::visitRelatedGroundPin); - defineAttrVisitor("related_power_pin", &LibertyReader::visitRelatedPowerPin); - defineAttrVisitor("related_pg_pin", &LibertyReader::visitRelatedPgPin); - - // AOCV attributes. - defineAttrVisitor("ocv_arc_depth", &LibertyReader::visitOcvArcDepth); - defineAttrVisitor("default_ocv_derate_group", - &LibertyReader::visitDefaultOcvDerateGroup); - defineAttrVisitor("ocv_derate_group", &LibertyReader::visitOcvDerateGroup); - defineGroupVisitor("ocv_table_template", - &LibertyReader::beginTableTemplateOcv, - &LibertyReader::endTableTemplate); - defineGroupVisitor("ocv_derate", - &LibertyReader::beginOcvDerate, - &LibertyReader::endOcvDerate); - defineGroupVisitor("ocv_derate_factors", - &LibertyReader::beginOcvDerateFactors, - &LibertyReader::endOcvDerateFactors); - defineAttrVisitor("rf_type", &LibertyReader::visitRfType); - defineAttrVisitor("derate_type", &LibertyReader::visitDerateType); - defineAttrVisitor("path_type", &LibertyReader::visitPathType); - - // POCV attributes. - defineGroupVisitor("ocv_sigma_cell_rise", &LibertyReader::beginOcvSigmaCellRise, - &LibertyReader::endOcvSigmaCell); - defineGroupVisitor("ocv_sigma_cell_fall", &LibertyReader::beginOcvSigmaCellFall, - &LibertyReader::endOcvSigmaCell); - defineGroupVisitor("ocv_sigma_rise_transition", - &LibertyReader::beginOcvSigmaRiseTransition, - &LibertyReader::endOcvSigmaTransition); - defineGroupVisitor("ocv_sigma_fall_transition", - &LibertyReader::beginOcvSigmaFallTransition, - &LibertyReader::endOcvSigmaTransition); - defineGroupVisitor("ocv_sigma_rise_constraint", - &LibertyReader::beginOcvSigmaRiseConstraint, - &LibertyReader::endOcvSigmaConstraint); - defineGroupVisitor("ocv_sigma_fall_constraint", - &LibertyReader::beginOcvSigmaFallConstraint, - &LibertyReader::endOcvSigmaConstraint); - defineAttrVisitor("sigma_type", &LibertyReader::visitSigmaType); - defineAttrVisitor("cell_leakage_power", &LibertyReader::visitCellLeakagePower); - - defineGroupVisitor("pg_pin", &LibertyReader::beginPgPin, - &LibertyReader::endPgPin); - defineAttrVisitor("pg_type", &LibertyReader::visitPgType); - defineAttrVisitor("voltage_name", &LibertyReader::visitVoltageName); - - // ccs receiver capacitance - defineGroupVisitor("receiver_capacitance", - &LibertyReader::beginReceiverCapacitance, - &LibertyReader::endReceiverCapacitance); - - defineGroupVisitor("receiver_capacitance_rise", - &LibertyReader::beginReceiverCapacitance1Rise, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineGroupVisitor("receiver_capacitance_fall", - &LibertyReader::beginReceiverCapacitance1Fall, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineAttrVisitor("segment", &LibertyReader::visitSegement); - - defineGroupVisitor("receiver_capacitance1_rise", - &LibertyReader::beginReceiverCapacitance1Rise, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineGroupVisitor("receiver_capacitance1_fall", - &LibertyReader::beginReceiverCapacitance1Fall, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineGroupVisitor("receiver_capacitance2_rise", - &LibertyReader::beginReceiverCapacitance2Rise, - &LibertyReader::endReceiverCapacitanceRiseFall); - defineGroupVisitor("receiver_capacitance2_fall", - &LibertyReader::beginReceiverCapacitance2Fall, - &LibertyReader::endReceiverCapacitanceRiseFall); - // ccs - defineGroupVisitor("output_current_rise", - &LibertyReader::beginOutputCurrentRise, - &LibertyReader::endOutputCurrentRiseFall); - defineGroupVisitor("output_current_fall", - &LibertyReader::beginOutputCurrentFall, - &LibertyReader::endOutputCurrentRiseFall); - defineGroupVisitor("vector", &LibertyReader::beginVector, &LibertyReader::endVector); - defineAttrVisitor("reference_time", &LibertyReader::visitReferenceTime); - defineGroupVisitor("normalized_driver_waveform", - &LibertyReader::beginNormalizedDriverWaveform, - &LibertyReader::endNormalizedDriverWaveform); - defineAttrVisitor("driver_waveform_name", &LibertyReader::visitDriverWaveformName); - defineAttrVisitor("driver_waveform_rise", &LibertyReader::visitDriverWaveformRise); - defineAttrVisitor("driver_waveform_fall", &LibertyReader::visitDriverWaveformFall); - - // ccsn (not implemented, this is needed to properly ignore ccsn groups) - defineGroupVisitor("ccsn_first_stage", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("ccsn_last_stage", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("output_voltage_rise", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("output_voltage_fall", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("propagated_noise_low", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("propagated_noise_high", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("input_ccb", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - defineGroupVisitor("output_ccb", &LibertyReader::beginCcsn, - &LibertyReader::endCcsn); - - defineGroupVisitor("ecsm_waveform", &LibertyReader::beginEcsmWaveform, - &LibertyReader::endEcsmWaveform); - defineGroupVisitor("ecsm_waveform_set", &LibertyReader::beginEcsmWaveform, - &LibertyReader::endEcsmWaveform); - defineGroupVisitor("ecsm_capacitance", &LibertyReader::beginEcsmWaveform, - &LibertyReader::endEcsmWaveform); + defineGroupVisitor("cell", nullptr, &LibertyReader::endCell); + defineGroupVisitor("scaled_cell", nullptr, &LibertyReader::endScaledCell); } void -LibertyReader::defineScalingFactorVisitors() +LibertyReader::visitAttr(const LibertySimpleAttr *) { - for (int type_index = 0; type_index < scale_factor_type_count; type_index++) { - ScaleFactorType type = static_cast(type_index); - const char *type_name = scaleFactorTypeName(type); - for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { - ScaleFactorPvt pvt = static_cast(pvt_index); - const char *pvt_name = scaleFactorPvtName(pvt); - if (scaleFactorTypeRiseFallSuffix(type)) { - for (auto tr : RiseFall::range()) { - const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; - string attr_name; - stringPrint(attr_name, "k_%s_%s_%s", - pvt_name, - type_name, - tr_name); - defineAttrVisitor(attr_name.c_str() ,&LibertyReader::visitScaleFactorSuffix); - } - } - else if (scaleFactorTypeRiseFallPrefix(type)) { - for (auto tr : RiseFall::range()) { - const char *tr_name = (tr == RiseFall::rise()) ? "rise":"fall"; - string attr_name; - stringPrint(attr_name, "k_%s_%s_%s", - pvt_name, - tr_name, - type_name); - defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorPrefix); - } - } - else if (scaleFactorTypeLowHighSuffix(type)) { - for (auto tr : RiseFall::range()) { - const char *tr_name = (tr == RiseFall::rise()) ? "high":"low"; - string attr_name; - stringPrint(attr_name, "k_%s_%s_%s", - pvt_name, - type_name, - tr_name); - defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactorHiLow); - } - } - else { - string attr_name; - stringPrint(attr_name, "k_%s_%s", - pvt_name, - type_name); - defineAttrVisitor(attr_name.c_str(),&LibertyReader::visitScaleFactor); - } - } - } } void -LibertyReader::visitAttr(LibertyAttr *attr) +LibertyReader::visitAttr(const LibertyComplexAttr *) { - LibraryAttrVisitor *visitor = findKeyValuePtr(attr_visitor_map_, attr->name()); - if (visitor) - (this->**visitor)(attr); } void -LibertyReader::begin(LibertyGroup *group) +LibertyReader::begin(const LibertyGroup *group, + LibertyGroup *parent_group) { LibraryGroupVisitor *visitor = findKeyValuePtr(group_begin_map_, group->type()); if (visitor) - (this->**visitor)(group); + (this->**visitor)(group, parent_group); } void -LibertyReader::end(LibertyGroup *group) +LibertyReader::end(const LibertyGroup *group, + LibertyGroup *parent_group) { LibraryGroupVisitor *visitor = findKeyValuePtr(group_end_map_, group->type()); if (visitor) - (this->**visitor)(group); + (this->**visitor)(group, parent_group); +} + +void +LibertyReader::beginLibrary(const LibertyGroup *library_group, + LibertyGroup *) +{ + makeLibrary(library_group); +} + +void +LibertyReader::endLibrary(const LibertyGroup *group, + LibertyGroup *) +{ + // If a library hasno cells endCell is not called. + if (first_cell_) + readLibraryAttributes(group); + delete group; +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::endCell(const LibertyGroup *cell_group, + LibertyGroup *library_group) +{ + // Read library groups defined since the last cell was read. + // Normally they are all defined by the first cell, but there + // are libraries that define table templates and bus tyupes + // between cells. + if (first_cell_) + readLibraryAttributes(library_group); + else { + readTableTemplates(library_group); + readBusTypes(nullptr, library_group); + } + + const char *name = cell_group->firstName(); + if (name) { + debugPrint(debug_, "liberty", 1, "cell %s", name); + LibertyCell *cell = builder_.makeCell(library_, name, filename_); + readCell(cell, cell_group); + } + else + libWarn(1193, cell_group, "cell missing name."); + library_group->clear(); + first_cell_ = false; } void -LibertyReader::beginLibrary(LibertyGroup *group) +LibertyReader::endScaledCell(const LibertyGroup *scaled_cell_group, + LibertyGroup *library_group) { - const char *name = group->firstName(); + readLibraryAttributes(library_group); + readScaledCell(scaled_cell_group); + library_group->deleteSubgroup(scaled_cell_group); +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::readLibraryAttributes(const LibertyGroup *library_group) +{ + readTechnology(library_group); + readLibraryUnits(library_group); + readThresholds(library_group); + readDelayModel(library_group); + readBusStyle(library_group); + readBusTypes(nullptr, library_group); + readTableTemplates(library_group); + readVoltateMaps(library_group); + readWireloads(library_group); + readWireloadSelection(library_group); + readDefaultWireLoad(library_group); + readDefaultWireLoadMode(library_group); + readDefaultWireLoadSelection(library_group); + readOperatingConds(library_group); + readScaleFactors(library_group); + readOcvDerateFactors(nullptr, library_group); + readDefaultOcvDerateGroup(library_group); + readGroupAttrFloat("ocv_arc_depth", library_group, + [this](float v) { library_->setOcvArcDepth(v); }); + readNormalizedDriverWaveform(library_group); + readSlewDegradations(library_group); + + readLibAttrFloat(library_group, "nom_temperature", + &LibertyLibrary::setNominalTemperature, 1.0F); + readLibAttrFloat(library_group, "nom_voltage", &LibertyLibrary::setNominalVoltage, + volt_scale_); + readLibAttrFloat(library_group, "nom_process", + &LibertyLibrary::setNominalProcess, 1.0F); + readLibAttrFloat(library_group, "default_inout_pin_cap", + &LibertyLibrary::setDefaultBidirectPinCap, cap_scale_); + readLibAttrFloat(library_group, "default_input_pin_cap", + &LibertyLibrary::setDefaultInputPinCap, cap_scale_); + readLibAttrFloat(library_group, "default_output_pin_cap", + &LibertyLibrary::setDefaultOutputPinCap, cap_scale_); + readLibAttrFloatWarnZero(library_group, "default_max_transition", + &LibertyLibrary::setDefaultMaxSlew, time_scale_); + readLibAttrFloatWarnZero(library_group, "default_max_fanout", + &LibertyLibrary::setDefaultMaxFanout, 1.0F); + readLibAttrFloat(library_group, "default_intrinsic_rise", + &LibertyLibrary::setDefaultIntrinsic, RiseFall::rise(), + time_scale_); + readLibAttrFloat(library_group, "default_intrinsic_fall", + &LibertyLibrary::setDefaultIntrinsic, RiseFall::fall(), + time_scale_); + readLibAttrFloat(library_group, "default_inout_pin_rise_res", + &LibertyLibrary::setDefaultBidirectPinRes, RiseFall::rise(), + res_scale_); + readLibAttrFloat(library_group, "default_inout_pin_fall_res", + &LibertyLibrary::setDefaultBidirectPinRes, RiseFall::fall(), + res_scale_); + readLibAttrFloat(library_group, "default_output_pin_rise_res", + &LibertyLibrary::setDefaultOutputPinRes, RiseFall::rise(), + res_scale_); + readLibAttrFloat(library_group, "default_output_pin_fall_res", + &LibertyLibrary::setDefaultOutputPinRes, RiseFall::fall(), + res_scale_); + readLibAttrFloatWarnZero(library_group, "default_fanout_load", + &LibertyLibrary::setDefaultFanoutLoad, 1.0F); + readLibAttrFloat(library_group, "slew_derate_from_library", + &LibertyLibrary::setSlewDerateFromLibrary, 1.0F); +} + +void +LibertyReader::makeLibrary(const LibertyGroup *libary_group) +{ + const char *name = libary_group->firstName(); if (name) { LibertyLibrary *library = network_->findLiberty(name); if (library) - libWarn(1140, group, "library %s already exists.", name); + libWarn(1140, libary_group, "library %s 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_); @@ -664,8 +286,6 @@ LibertyReader::beginLibrary(LibertyGroup *group) current_scale_ = 1E-3F; // Default is 1; power_scale_ = 1; - // Default is fJ. - setEnergyScale(); // Default is 1 micron. distance_scale_ = 1e-6; @@ -677,1841 +297,2288 @@ LibertyReader::beginLibrary(LibertyGroup *group) library_->units()->distanceUnit()->setScale(distance_scale_); library_->setDelayModelType(DelayModelType::cmos_linear); - scale_factors_ = new ScaleFactors(""); - library_->setScaleFactors(scale_factors_); } else - libError(1141, group, "library missing name."); + libError(1141, libary_group, "library missing name."); } -// Energy scale is derived. -void -LibertyReader::setEnergyScale() +// Energy scale is derived from other units. +float +LibertyReader::energyScale() { - energy_scale_ = volt_scale_ * volt_scale_ * cap_scale_; + return volt_scale_ * volt_scale_ * cap_scale_; } void -LibertyReader::endLibrary(LibertyGroup *group) +LibertyReader::readTechnology(const LibertyGroup *library_group) { - endLibraryAttrs(group); + const LibertyComplexAttr *tech_attr = library_group->findComplexAttr("technology"); + if (tech_attr) { + const LibertyAttrValue *tech_value = tech_attr->firstValue(); + if (tech_value) { + const std::string &tech = tech_value->stringValue(); + if (tech == "fpga") + library_->setDelayModelType(DelayModelType::cmos_linear); + } + } } void -LibertyReader::endLibraryAttrs(LibertyGroup *group) +LibertyReader::readLibraryUnits(const LibertyGroup *library_group) { - // These attributes reference named groups in the library so - // wait until the end of the library to resolve them. - if (default_wireload_) { - const Wireload *wireload = library_->findWireload(default_wireload_); - if (wireload) - library_->setDefaultWireload(wireload); - else - libWarn(1142, group, "default_wire_load %s not found.", default_wireload_); - stringDelete(default_wireload_); - default_wireload_ = nullptr; - } + readUnit("time_unit", "s", time_scale_, library_->units()->timeUnit(), library_group); + readUnit("pulling_resistance_unit", "ohm", res_scale_, + library_->units()->resistanceUnit(), library_group); + readUnit("voltage_unit", "V", volt_scale_, library_->units()->voltageUnit(), + library_group); + readUnit("current_unit", "A", current_scale_, library_->units()->currentUnit(), + library_group); + readUnit("leakage_power_unit", "W", power_scale_, library_->units()->powerUnit(), + library_group); + readUnit("distance_unit", "m", distance_scale_, library_->units()->distanceUnit(), + library_group); - if (default_wireload_selection_) { - const WireloadSelection *selection = - library_->findWireloadSelection(default_wireload_selection_); - if (selection) - library_->setDefaultWireloadSelection(selection); + const LibertyComplexAttr *cap_attr = + library_group->findComplexAttr("capacitive_load_unit"); + if (cap_attr) { + const LibertyAttrValueSeq &values = cap_attr->values(); + if (values.size() == 2) { + LibertyAttrValue *value = values[0]; + bool valid = false; + float scale; + if (value->isFloat()) { + scale = value->floatValue(); + valid = true; + } + else if (value->isString()) { + try { + scale = std::stof(value->stringValue()); + valid = true; + } + catch (...) { + valid = false; + } + } + + if (valid) { + value = values[1]; + if (value->isString()) { + const std::string suffix = value->stringValue(); + if (stringEqual(suffix.c_str(), "ff")) + cap_scale_ = scale * 1E-15F; + 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."); + } + else + libWarn(1155, cap_attr, "capacitive_load_units are not a string."); + } + else + libWarn(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."); else - libWarn(1143, group, "default_wire_selection %s not found.", - default_wireload_selection_); - stringDelete(default_wireload_selection_); - default_wireload_selection_ = nullptr; + libWarn(1158, cap_attr, "capacitive_load_units missing scale and suffix."); + library_->units()->capacitanceUnit()->setScale(cap_scale_); } +} - if (default_operating_condition_) { - OperatingConditions *op_cond = - library_->findOperatingConditions(default_operating_condition_); - if (op_cond) - library_->setDefaultOperatingConditions(op_cond); - else - libWarn(1144, group, "default_operating_condition %s not found.", - default_operating_condition_); - stringDelete(default_operating_condition_); - default_operating_condition_ = nullptr; +void +LibertyReader::readUnit(const char *unit_attr_name, + const char *unit_suffix, + float &scale_var, + Unit *unit, + const LibertyGroup *library_group) +{ + const LibertySimpleAttr *unit_attr = library_group->findSimpleAttr(unit_attr_name); + if (unit_attr) { + const std::string *units = unit_attr->stringValue(); + if (units) { + // Unit format is . + // Find the multiplier digits. + std::string units1 = *units; + size_t mult_end = units1.find_first_not_of("0123456789"); + float mult = 1.0F; + std::string scale_suffix; + if (mult_end != units1.npos) { + std::string unit_mult = units1.substr(0, mult_end); + scale_suffix = units1.substr(mult_end); + if (unit_mult == "1") + mult = 1.0F; + else if (unit_mult == "10") + mult = 10.0F; + else if (unit_mult == "100") + mult = 100.0F; + else + libWarn(1150, unit_attr, "unknown unit multiplier %s.", unit_mult.c_str()); + } + else + scale_suffix = *units; + + float scale_mult = 1.0F; + if (scale_suffix.size() == strlen(unit_suffix) + 1) { + std::string suffix = scale_suffix.substr(1); + if (stringEqual(suffix.c_str(), unit_suffix)) { + char scale_char = tolower(scale_suffix[0]); + if (scale_char == 'k') + scale_mult = 1E+3F; + else if (scale_char == 'm') + scale_mult = 1E-3F; + else if (scale_char == 'u') + scale_mult = 1E-6F; + else if (scale_char == 'n') + scale_mult = 1E-9F; + else if (scale_char == 'p') + scale_mult = 1E-12F; + else if (scale_char == 'f') + scale_mult = 1E-15F; + else + libWarn(1151, unit_attr, "unknown unit scale %c.", scale_char); + } + else + libWarn(1152, unit_attr, "unknown unit suffix %s.", suffix.c_str()); + } + else if (!stringEqual(scale_suffix.c_str(), unit_suffix)) + libWarn(1153, unit_attr, "unknown unit suffix %s.", scale_suffix.c_str()); + scale_var = scale_mult * mult; + unit->setScale(scale_var); + } } +} - bool missing_threshold = false; - for (auto rf : RiseFall::range()) { - int rf_index = rf->index(); - if (!have_input_threshold_[rf_index]) { - libWarn(1145, group, "input_threshold_pct_%s not found.", rf->name()); - missing_threshold = true; +void +LibertyReader::readDelayModel(const LibertyGroup *library_group) +{ + const std::string *type_name = library_group->findAttrString("delay_model"); + if (type_name) { + if (*type_name == "table_lookup") + library_->setDelayModelType(DelayModelType::table); + else if (*type_name == "generic_cmos") + 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()); } - if (!have_output_threshold_[rf_index]) { - libWarn(1146, group, "output_threshold_pct_%s not found.", rf->name()); - missing_threshold = true; + else if (*type_name == "cmos2") { + library_->setDelayModelType(DelayModelType::cmos2); + libWarn(1161, library_group, "delay_model %s not supported.", type_name->c_str()); } - if (!have_slew_lower_threshold_[rf_index]) { - libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", rf->name()); - missing_threshold = true; + else if (*type_name == "polynomial") { + library_->setDelayModelType(DelayModelType::polynomial); + libWarn(1162, library_group, "delay_model %s not supported.", type_name->c_str()); } - if (!have_slew_upper_threshold_[rf_index]) { - libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", rf->name()); - missing_threshold = true; + // 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()); } + else + libWarn(1164, library_group, "unknown delay_model %s.", type_name->c_str()); } - if (missing_threshold) - libError(1149, group, "Library %s is missing one or more thresholds.", - library_->name()); } void -LibertyReader::visitTimeUnit(LibertyAttr *attr) +LibertyReader::readBusStyle(const LibertyGroup *library_group) { - if (library_) - parseUnits(attr, "s", time_scale_, library_->units()->timeUnit()); + const std::string *bus_style = library_group->findAttrString("bus_naming_style"); + if (bus_style) { + // Assume bus style is of the form "%s[%d]". + if (bus_style->size() == 6 + && (*bus_style)[0] == '%' + && (*bus_style)[1] == 's' + && (*bus_style)[3] == '%' + && (*bus_style)[4] == 'd') + library_->setBusBrkts((*bus_style)[2], (*bus_style)[5]); + else + libWarn(1165, library_group, "unknown bus_naming_style format."); + } } void -LibertyReader::visitPullingResistanceUnit(LibertyAttr *attr) +LibertyReader::readBusTypes(LibertyCell *cell, + const LibertyGroup *group) { - if (library_) - parseUnits(attr, "ohm", res_scale_, - library_->units()->resistanceUnit()); + for (const LibertyGroup *type_group : group->findSubgroups("type")) { + const char *name = type_group->firstName(); + if (name) { + int from, to; + bool from_exists, to_exists; + type_group->findAttrInt("bit_from", from, from_exists); + type_group->findAttrInt("bit_to", to, to_exists); + if (from_exists && to_exists) { + if (cell) + cell->makeBusDcl(name, from, to); + else + library_->makeBusDcl(name, from, to); + } + else if (!from_exists) + libWarn(1179, type_group, "bus type missing bit_from."); + else if (!to_exists) + libWarn(1180, type_group, "bus type missing bit_to."); + } + } } void -LibertyReader::visitResistanceUnit(LibertyAttr *attr) +LibertyReader::readThresholds(const LibertyGroup *library_group) { - if (library_) - parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit()); + for (const RiseFall *rf : RiseFall::range()) { + std::string suffix = rf->to_string_long(); + readLibAttrFloat(library_group, ("input_threshold_pct_" + suffix).c_str(), + &LibertyLibrary::setInputThreshold, rf, 0.01F); + if (library_->inputThreshold(rf) == 0.0) + libWarn(1145, library_group, "input_threshold_pct_%s not found.", rf->name()); + + readLibAttrFloat(library_group, ("output_threshold_pct_" + suffix).c_str(), + &LibertyLibrary::setOutputThreshold, rf, 0.01F); + if (library_->outputThreshold(rf) == 0.0) + libWarn(1146, library_group, "output_threshold_pct_%s not found.", rf->name()); + + readLibAttrFloat(library_group, ("slew_lower_threshold_pct_" + suffix).c_str(), + &LibertyLibrary::setSlewLowerThreshold, rf, 0.01F); + if (library_->slewLowerThreshold(rf) == 0.0) + libWarn(1147, library_group, "slew_lower_threshold_pct_%s not found.", rf->name()); + + readLibAttrFloat(library_group, ("slew_upper_threshold_pct_" + suffix).c_str(), + &LibertyLibrary::setSlewUpperThreshold, rf, 0.01F); + if (library_->slewUpperThreshold(rf) == 0.0) + libWarn(1148, library_group, "slew_upper_threshold_pct_%s not found.", rf->name()); + } } void -LibertyReader::visitCurrentUnit(LibertyAttr *attr) +LibertyReader::readTableTemplates(const LibertyGroup *library_group) { - if (library_) - parseUnits(attr, "A", current_scale_, library_->units()->currentUnit()); + readTableTemplates(library_group, "lu_table_template", TableTemplateType::delay); + readTableTemplates(library_group, "output_current_template", + TableTemplateType::output_current); + readTableTemplates(library_group, "power_lut_template", TableTemplateType::power); + readTableTemplates(library_group, "ocv_table_template", TableTemplateType::ocv); } void -LibertyReader::visitVoltageUnit(LibertyAttr *attr) +LibertyReader::readTableTemplates(const LibertyGroup *library_group, + const char *group_name, + TableTemplateType type) { - if (library_) - parseUnits(attr, "V", volt_scale_, library_->units()->voltageUnit()); - setEnergyScale(); + for (const LibertyGroup *template_group : library_group->findSubgroups(group_name)) { + const char *name = template_group->firstName(); + if (name) { + TableTemplate *tbl_template = library_->makeTableTemplate(name, type); + TableAxisPtr axis1 = makeTableTemplateAxis(template_group, 1); + if (axis1) + tbl_template->setAxis1(axis1); + TableAxisPtr axis2 = makeTableTemplateAxis(template_group, 2); + if (axis2) + tbl_template->setAxis2(axis2); + TableAxisPtr axis3 = makeTableTemplateAxis(template_group, 3); + if (axis3) + tbl_template->setAxis3(axis3); + } + else + libWarn(1175, template_group, "table template missing name."); + } } -void -LibertyReader::visitPowerUnit(LibertyAttr *attr) -{ - if (library_) - parseUnits(attr, "W", power_scale_, library_->units()->powerUnit()); +TableAxisPtr +LibertyReader::makeTableTemplateAxis(const LibertyGroup *template_group, + int axis_index) +{ + std::string var_attr_name = "variable_" + std::to_string(axis_index); + const std::string *var_name = template_group->findAttrString(var_attr_name); + 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()); + else { + std::string index_attr_name = "index_" + std::to_string(axis_index); + const LibertyComplexAttr *index_attr = + template_group->findComplexAttr(index_attr_name); + FloatSeq axis_values; + if (index_attr) { + axis_values = readFloatSeq(index_attr, 1.0F); + if (!axis_values.empty()) { + float prev = axis_values[0]; + 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."); + break; + } + prev = value; + } + } + } + const Units *units = library_->units(); + float scale = tableVariableUnit(axis_var, units)->scale(); + scaleFloats(axis_values, scale); + return make_shared(axis_var, std::move(axis_values)); + } + } + return nullptr; } -void -LibertyReader::visitDistanceUnit(LibertyAttr *attr) +static void +scaleFloats(FloatSeq &floats, + float scale) { - if (library_) - parseUnits(attr, "m", distance_scale_, library_->units()->distanceUnit()); + size_t count = floats.size(); + for (size_t i = 0; i < count; i++) + floats[i] *= scale; } void -LibertyReader::parseUnits(LibertyAttr *attr, - const char *unit_suffix, - float &scale_var, - Unit *unit) -{ - const char *units = getAttrString(attr); - if (units) { - // Unit format is . - // Find the multiplier digits. - string units1 = units; - size_t mult_end = units1.find_first_not_of("0123456789"); - float mult = 1.0F; - string scale_suffix; - if (mult_end != units1.npos) { - string unit_mult = units1.substr(0, mult_end); - scale_suffix = units1.substr(mult_end); - if (unit_mult == "1") - mult = 1.0F; - else if (unit_mult == "10") - mult = 10.0F; - else if (unit_mult == "100") - mult = 100.0F; +LibertyReader::readVoltateMaps(const LibertyGroup *library_group) +{ + for (const LibertyComplexAttr *volt_attr : + library_group->findComplexAttrs("voltage_map")) { + const LibertyAttrValueSeq &values = volt_attr->values(); + if (values.size() == 2) { + const std::string &volt_name = values[0]->stringValue(); + float volt; + bool valid; + values[1]->floatValue(volt, valid); + if (valid) + library_->addSupplyVoltage(volt_name.c_str(), volt); else - libWarn(1150, attr, "unknown unit multiplier %s.", unit_mult.c_str()); + libWarn(1166, volt_attr, "voltage_map voltage is not a float."); } - else - scale_suffix = units; - - float scale_mult = 1.0F; - if (scale_suffix.size() == strlen(unit_suffix) + 1) { - string suffix = scale_suffix.substr(1); - if (stringEqual(suffix.c_str(), unit_suffix)) { - char scale_char = tolower(scale_suffix[0]); - if (scale_char == 'k') - scale_mult = 1E+3F; - else if (scale_char == 'm') - scale_mult = 1E-3F; - else if (scale_char == 'u') - scale_mult = 1E-6F; - else if (scale_char == 'n') - scale_mult = 1E-9F; - else if (scale_char == 'p') - scale_mult = 1E-12F; - else if (scale_char == 'f') - scale_mult = 1E-15F; - else - libWarn(1151, attr, "unknown unit scale %c.", scale_char); + } +} + +void +LibertyReader::readOperatingConds(const LibertyGroup *library_group) +{ + for (const LibertyGroup *opcond_group : + library_group->findSubgroups("operating_conditions")) { + const char *name = opcond_group->firstName(); + if (name) { + OperatingConditions *op_cond = library_->makeOperatingConditions(name); + float value; + bool exists; + opcond_group->findAttrFloat("process", value, exists); + if (exists) + op_cond->setProcess(value); + opcond_group->findAttrFloat("temperature", value, exists); + if (exists) + op_cond->setTemperature(value); + opcond_group->findAttrFloat("voltage", value, exists); + if (exists) + op_cond->setVoltage(value); + const std::string *tree_type = opcond_group->findAttrString("tree_type"); + if (tree_type) { + WireloadTree wireload_tree = stringWireloadTree(tree_type->c_str()); + op_cond->setWireloadTree(wireload_tree); } - else - libWarn(1152, attr, "unknown unit suffix %s.", suffix.c_str()); } - else if (!stringEqual(scale_suffix.c_str(), unit_suffix)) - libWarn(1153, attr, "unknown unit suffix %s.", scale_suffix.c_str()); - scale_var = scale_mult * mult; - unit->setScale(scale_var); + } + + const std::string *default_op_cond = + library_group->findAttrString("default_operating_conditions"); + if (default_op_cond) { + OperatingConditions *op_cond = + library_->findOperatingConditions(default_op_cond->c_str()); + if (op_cond) + library_->setDefaultOperatingConditions(op_cond); + else + libWarn(1144, library_group, "default_operating_condition %s not found.", + default_op_cond->c_str()); } } void -LibertyReader::visitCapacitiveLoadUnit(LibertyAttr *attr) -{ - if (library_) { - if (attr->isComplexAttr()) { - LibertyAttrValueSeq *values = attr->values(); - if (values->size() == 2) { - LibertyAttrValue *value = (*values)[0]; - bool valid = false; - float scale; - if (value->isFloat()) { - scale = value->floatValue(); - valid = true; - } - else if (value->isString()) { - try { - scale = std::stof(value->stringValue()); - valid = true; - } - catch (...) { - valid = false; - } - } +LibertyReader::readScaleFactors(const LibertyGroup *library_group) +{ + // Top level scale factors. + ScaleFactors *scale_factors = library_->makeScaleFactors(""); + library_->setScaleFactors(scale_factors); + readScaleFactors(library_group, scale_factors); - if (valid) { - value = (*values)[1]; - if (value->isString()) { - const std::string suffix = value->stringValue(); - if (stringEqual(suffix.c_str(), "ff")) - cap_scale_ = scale * 1E-15F; - else if (stringEqual(suffix.c_str(), "pf")) - cap_scale_ = scale * 1E-12F; - else - libWarn(1154, attr, "capacitive_load_units are not ff or pf."); - } - else - libWarn(1155, attr, "capacitive_load_units are not a string."); - } - else - libWarn(1157, attr, "capacitive_load_units scale is not a float."); - } - else if (values->size() == 1) - libWarn(1156, attr, "capacitive_load_units missing suffix."); - else - libWarn(1158, attr, "capacitive_load_units missing scale and suffix."); + // Named scale factors. + for (const LibertyGroup *scale_group : library_group->findSubgroups("scaling_factors")){ + const char *name = scale_group->firstName(); + if (name) { + ScaleFactors *scale_factors = library_->makeScaleFactors(name); + readScaleFactors(scale_group, scale_factors); } - else - libWarn(1159, attr, "capacitive_load_unit missing values suffix."); - library_->units()->capacitanceUnit()->setScale(cap_scale_); - setEnergyScale(); } } void -LibertyReader::visitDelayModel(LibertyAttr *attr) +LibertyReader::readScaleFactors(const LibertyGroup *scale_group, + ScaleFactors *scale_factors) { - if (library_) { - const char *type_name = getAttrString(attr); - if (type_name) { - if (stringEq(type_name, "table_lookup")) - library_->setDelayModelType(DelayModelType::table); - else if (stringEq(type_name, "generic_cmos")) - library_->setDelayModelType(DelayModelType::cmos_linear); - else if (stringEq(type_name, "piecewise_cmos")) { - library_->setDelayModelType(DelayModelType::cmos_pwl); - libWarn(1160, attr, "delay_model %s not supported.", type_name); - } - else if (stringEq(type_name, "cmos2")) { - library_->setDelayModelType(DelayModelType::cmos2); - libWarn(1161, attr, "delay_model %s not supported.", type_name); - } - else if (stringEq(type_name, "polynomial")) { - library_->setDelayModelType(DelayModelType::polynomial); - libWarn(1162, attr, "delay_model %s not supported.", type_name); - } - // Evil IBM garbage. - else if (stringEq(type_name, "dcm")) { - library_->setDelayModelType(DelayModelType::dcm); - libWarn(1163, attr, "delay_model %s not supported..", type_name); + // Skip unknown type. + for (int type_index = 0; type_index < scale_factor_type_count - 1; type_index++) { + ScaleFactorType type = static_cast(type_index); + const char *type_name = scaleFactorTypeName(type); + // Skip unknown pvt. + for (int pvt_index = 0; pvt_index < scale_factor_pvt_count - 1; pvt_index++) { + ScaleFactorPvt pvt = static_cast(pvt_index); + const std::string pvt_name = scaleFactorPvtName(pvt); + std::string attr_name; + for (const RiseFall *rf : RiseFall::range()) { + if (scaleFactorTypeRiseFallSuffix(type)) { + const std::string rf_name = (rf == RiseFall::rise()) ? "rise" : "fall"; + attr_name = "k_" + pvt_name + "_" + type_name + "_" + rf_name; + } + else if (scaleFactorTypeRiseFallPrefix(type)) { + const char *rf_name = (rf == RiseFall::rise()) ? "rise" : "fall"; + attr_name = "k_" + pvt_name + "_" + rf_name + "_" + type_name; + } + else if (scaleFactorTypeLowHighSuffix(type)) { + const char *rf_name = (rf == RiseFall::rise()) ? "high":"low"; + attr_name = "k_" + pvt_name + "_" + type_name + "_" + rf_name; + } + else + attr_name = "k_" + pvt_name + "_" + type_name; + float value; + bool exists; + scale_group->findAttrFloat(attr_name, value, exists); + if (exists) + scale_factors->setScale(type, pvt, rf, value); } - else - libWarn(1164, attr, "unknown delay_model %s.", type_name); } } } void -LibertyReader::visitBusStyle(LibertyAttr *attr) +LibertyReader::readWireloads(const LibertyGroup *library_group) { - if (library_) { - const char *bus_style = getAttrString(attr); - // Assume bus style is of the form "%s[%d]". - if (bus_style - && strlen(bus_style) == 6 - && bus_style[0] == '%' - && bus_style[1] == 's' - && bus_style[3] == '%' - && bus_style[4] == 'd') - library_->setBusBrkts(bus_style[2], bus_style[5]); + for (const LibertyGroup *wl_group : library_group->findSubgroups("wire_load")) { + const char *name = wl_group->firstName(); + if (name) { + Wireload *wireload = library_->makeWireload(name); + float value; + bool exists; + wl_group->findAttrFloat("resistance", value, exists); + if (exists) + wireload->setResistance(value * res_scale_); + + wl_group->findAttrFloat("capacitance", value, exists); + if (exists) + wireload->setCapacitance(value * cap_scale_); + + wl_group->findAttrFloat("slope", value, exists); + if (exists) + wireload->setSlope(value); + + for (const LibertyComplexAttr *fanout_attr : + wl_group->findComplexAttrs("fanout_length")) { + float fanout, length; + bool exists; + getAttrFloat2(fanout_attr, fanout, length, exists); + if (exists) + wireload->addFanoutLength(fanout, length); + else + libWarn(1185, fanout_attr, "fanout_length is missing length and fanout."); + } + } else - libWarn(1165, attr, "unknown bus_naming_style format."); + libWarn(1184, wl_group, "wire_load missing name."); } } void -LibertyReader::visitVoltageMap(LibertyAttr *attr) +LibertyReader::readWireloadSelection(const LibertyGroup *library_group) { - if (library_) { - if (attr->isComplexAttr()) { - LibertyAttrValueSeq *values = attr->values(); - if (values->size() >= 1) { - LibertyAttrValue *value = (*values)[0]; - if (value->isString()) { - const std::string &supply_name = value->stringValue(); - if (values->size() == 2) { - value = (*values)[1]; - bool valid = false; - float voltage; - if (value->isFloat()) { - voltage = value->floatValue(); - valid = true; - } - else if (value->isString()) { - try { - voltage = std::stof(value->stringValue()); - valid = true; - } - catch (...) { - valid = false; - } + const LibertyGroup *sel_group = library_group->findSubgroup("wire_load_selection"); + if (sel_group) { + const char *name = sel_group->firstName(); + if (name == nullptr) + name = ""; + WireloadSelection *wireload_selection = library_->makeWireloadSelection(name); + for (const LibertyComplexAttr *area_attr : + sel_group->findComplexAttrs("wire_load_from_area")) { + const LibertyAttrValueSeq &values = area_attr->values(); + if (values.size() == 3) { + LibertyAttrValue *value = values[0]; + if (value->isFloat()) { + float min_area = value->floatValue(); + value = values[1]; + if (value->isFloat()) { + float max_area = value->floatValue(); + value = values[2]; + if (value->isString()) { + const std::string &wireload_name = value->stringValue(); + const Wireload *wireload = + library_->findWireload(wireload_name.c_str()); + if (wireload) + wireload_selection->addWireloadFromArea(min_area, max_area, + wireload); + else + libWarn(1187, area_attr, "wireload %s not found.", wireload_name.c_str()); } - - if (valid) - library_->addSupplyVoltage(supply_name.c_str(), voltage); else - libWarn(1166, attr, "voltage_map voltage is not a float."); + libWarn(1188, area_attr, + "wire_load_from_area wireload name not a string."); } else - libWarn(1167, attr, "voltage_map missing voltage."); + libWarn(1189, area_attr, "wire_load_from_area min not a float."); } else - libWarn(1168, attr, "voltage_map supply name is not a string."); + libWarn(1190, area_attr, "wire_load_from_area max not a float."); } else - libWarn(1169, attr, "voltage_map missing supply name and voltage."); + libWarn(1191, area_attr, "wire_load_from_area missing parameters."); } - else - libWarn(1170, attr, "voltage_map missing values suffix."); } } void -LibertyReader::visitNomTemp(LibertyAttr *attr) -{ - if (library_) { - float value; - bool valid; - getAttrFloat(attr, value, valid); - if (valid) - library_->setNominalTemperature(value); +LibertyReader::readDefaultWireLoad(const LibertyGroup *library_group) +{ + const std::string *wireload_name = library_group->findAttrString("default_wire_load"); + if (wireload_name) { + const Wireload *wireload = library_->findWireload(wireload_name->c_str()); + if (wireload) + library_->setDefaultWireload(wireload); + else + libWarn(1142, library_group, "default_wire_load %s not found.", + wireload_name->c_str()); } } void -LibertyReader::visitNomProc(LibertyAttr *attr) -{ - if (library_) { - float value; - bool valid; - getAttrFloat(attr, value, valid); - if (valid) - library_->setNominalProcess(value); +LibertyReader::readDefaultWireLoadMode(const LibertyGroup *library_group) +{ + const std::string *wire_load_mode = + library_group->findAttrString("default_wire_load_mode"); + if (wire_load_mode) { + WireloadMode mode = stringWireloadMode(wire_load_mode->c_str()); + if (mode != WireloadMode::unknown) + library_->setDefaultWireloadMode(mode); + else + libWarn(1174, library_group, "default_wire_load_mode %s not found.", + wire_load_mode->c_str()); } } void -LibertyReader::visitNomVolt(LibertyAttr *attr) -{ - if (library_) { - float value; - bool valid; - getAttrFloat(attr, value, valid); - if (valid) - library_->setNominalVoltage(value); +LibertyReader::readDefaultWireLoadSelection(const LibertyGroup *library_group) +{ + const std::string *selection_name = + library_group->findAttrString("default_wire_load_selection"); + if (selection_name) { + const WireloadSelection *selection = + library_->findWireloadSelection(selection_name->c_str()); + if (selection) + library_->setDefaultWireloadSelection(selection); + else + libWarn(1143, library_group, "default_wire_selection %s not found.", + selection_name->c_str()); } } void -LibertyReader::visitDefaultInoutPinCap(LibertyAttr *attr) +LibertyReader::readModeDefs(LibertyCell *cell, + const LibertyGroup *cell_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultBidirectPinCap(value * cap_scale_); + for (const LibertyGroup *mode_group : cell_group->findSubgroups("mode_definition")) { + const char *name = mode_group->firstName(); + if (name) { + ModeDef *mode_def = cell->makeModeDef(name); + for (const LibertyGroup *value_group : mode_group->findSubgroups("mode_value")) { + const char *value_name = value_group->firstName(); + if (value_name) { + ModeValueDef *mode_value = mode_def->defineValue(value_name, nullptr, nullptr); + const std::string *sdf_cond = value_group->findAttrString("sdf_cond"); + if (sdf_cond) + mode_value->setSdfCond(sdf_cond->c_str()); + const std::string *when = value_group->findAttrString("when"); + if (when) { + // line + FuncExpr *when_expr = parseFunc(when->c_str(), "when", cell, + value_group->line()); + mode_value->setCond(when_expr); + } + } + else + libWarn(1264, value_group, "mode value missing name."); + } + } + else + libWarn(1263, mode_group, "mode definition missing name."); } } void -LibertyReader::visitDefaultInputPinCap(LibertyAttr *attr) +LibertyReader::readSlewDegradations(const LibertyGroup *library_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultInputPinCap(value * cap_scale_); + for (const RiseFall *rf : RiseFall::range()) { + const std::string group_name = rf->to_string_long() + "_transition_degradation"; + const LibertyGroup *degradation_group = + library_group->findSubgroup(group_name.c_str()); + if (degradation_group) { + TableModel *table_model = readTableModel(degradation_group, rf, + TableTemplateType::delay, + time_scale_, + ScaleFactorType::transition); + if (LibertyLibrary::checkSlewDegradationAxes(table_model)) + library_->setWireSlewDegradationTable(table_model, rf); + else + libWarn(1254, degradation_group, "unsupported model axis."); + } } } void -LibertyReader::visitDefaultOutputPinCap(LibertyAttr *attr) +LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, + const char *attr_name, + void (LibertyLibrary::*set_func)(float value), + float scale) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultOutputPinCap(value * cap_scale_); - } + float value; + bool exists; + library_group->findAttrFloat(attr_name, value, exists); + if (exists) + (library_->*set_func)(value * scale); } void -LibertyReader::visitDefaultMaxTransition(LibertyAttr *attr) +LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, + const char *attr_name, + void (LibertyLibrary::*set_func)(const RiseFall *rf, + float value), + const RiseFall *rf, + float scale) { - if (library_){ - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (value == 0.0) - libWarn(1171, attr, "default_max_transition is 0.0."); - library_->setDefaultMaxSlew(value * time_scale_); - } - } + float value; + bool exists; + library_group->findAttrFloat(attr_name, value, exists); + if (exists) + (library_->*set_func)(rf, value * scale); } void -LibertyReader::visitDefaultMaxFanout(LibertyAttr *attr) +LibertyReader::readLibAttrFloatWarnZero(const LibertyGroup *library_group, + const char *attr_name, + void (LibertyLibrary::*set_func)(float value), + float scale) { - if (library_){ - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (value == 0.0) - libWarn(1172, attr, "default_max_fanout is 0.0."); - library_->setDefaultMaxFanout(value); + float value; + bool exists; + library_group->findAttrFloat(attr_name, value, exists); + if (exists) { + if (value == 0.0F) { + const LibertySimpleAttr *attr = library_group->findSimpleAttr(attr_name); + if (attr) + libWarn(1171, attr, "%s is 0.0.", attr_name); + else + libWarn(1172, library_group, "%s is 0.0.", attr_name); } + (library_->*set_func)(value * scale); } } +//////////////////////////////////////////////////////////////// + void -LibertyReader::visitDefaultIntrinsicRise(LibertyAttr *attr) +LibertyReader::readCell(LibertyCell *cell, + const LibertyGroup *cell_group) { - visitDefaultIntrinsic(attr, RiseFall::rise()); -} + readBusTypes(cell, cell_group); + // Make ports first because they are referenced by functions, timing arcs, etc. + LibertyPortGroupMap port_group_map = makeCellPorts(cell, cell_group); -void -LibertyReader::visitDefaultIntrinsicFall(LibertyAttr *attr) -{ - visitDefaultIntrinsic(attr, RiseFall::fall()); -} + // Make ff/latch output ports. + makeSequentials(cell, cell_group); -void -LibertyReader::visitDefaultIntrinsic(LibertyAttr *attr, - const RiseFall *rf) -{ - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultIntrinsic(rf, value * time_scale_); + readCellAttributes(cell, cell_group); + + // Set port directions before making timing arcs etc. + for (auto const &[port_group, ports] : port_group_map) + readPortDir(ports, port_group); + + for (auto const &[port_group, ports] : port_group_map) { + readPortAttributes(cell, ports, port_group); + makePortFuncs(cell, ports, port_group); + makeTimingArcs(cell, ports, port_group); + readInternalPowerGroups(cell, ports, port_group); } -} -void -LibertyReader::visitDefaultInoutPinRiseRes(LibertyAttr *attr) -{ - visitDefaultInoutPinRes(attr, RiseFall::rise()); + readTestCell(cell, cell_group); + + cell->finish(infer_latches_, report_, debug_); } void -LibertyReader::visitDefaultInoutPinFallRes(LibertyAttr *attr) +LibertyReader::readScaledCell(const LibertyGroup *scaled_cell_group) { - visitDefaultInoutPinRes(attr, RiseFall::fall()); + const char *name = scaled_cell_group->firstName(); + if (name) { + LibertyCell *owner = library_->findLibertyCell(name); + if (owner) { + const char *op_cond_name = scaled_cell_group->secondName(); + if (op_cond_name) { + OperatingConditions *op_cond = library_->findOperatingConditions(op_cond_name); + if (op_cond) { + debugPrint(debug_, "liberty", 1, "scaled cell %s %s", + name, op_cond_name); + LibertyCell *scaled_cell = library_->makeScaledCell(name, filename_); + readCell(scaled_cell, scaled_cell_group); + checkScaledCell(scaled_cell, owner, scaled_cell_group, op_cond_name); + // Add scaled cell AFTER ports and timing arcs are defined. + owner->addScaledCell(op_cond, scaled_cell); + } + else + libWarn(1202, scaled_cell_group, "operating conditions %s not found.", + op_cond_name); + } + else + libWarn(1203, scaled_cell_group, "scaled_cell missing operating condition."); + } + else + libWarn(1204, scaled_cell_group, "scaled_cell cell %s has not been defined.", name); + } + else + libWarn(1205, scaled_cell_group, "scaled_cell missing name."); } +// Minimal check that is not very specific about where the discrepancies are. void -LibertyReader::visitDefaultInoutPinRes(LibertyAttr *attr, - const RiseFall *rf) -{ - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultBidirectPinRes(rf, value * res_scale_); +LibertyReader::checkScaledCell(LibertyCell *scaled_cell, + LibertyCell *owner, + const LibertyGroup *scaled_cell_group, + const char *op_cond_name) +{ + 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", + 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.", + scaled_cell->name(), + op_cond_name); } + else + libWarn(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.", + scaled_cell->name(), + op_cond_name); } -void -LibertyReader::visitDefaultOutputPinRiseRes(LibertyAttr *attr) +LibertyPortGroupMap +LibertyReader::makeCellPorts(LibertyCell *cell, + const LibertyGroup *cell_group) { - visitDefaultOutputPinRes(attr, RiseFall::rise()); + LibertyPortGroupMap port_group_map; + for (const LibertyGroup *subgroup : cell_group->subgroups()) { + const std::string &type = subgroup->type(); + if (type == "pin") + makePinPort(cell, subgroup, port_group_map); + else if (type == "bus") + makeBusPort(cell, subgroup, port_group_map); + else if (type == "bundle") + makeBundlePort(cell, subgroup, port_group_map); + else if (type == "pg_pin") + makePgPinPort(cell, subgroup); + } + return port_group_map; } void -LibertyReader::visitDefaultOutputPinFallRes(LibertyAttr *attr) +LibertyReader::makePinPort(LibertyCell *cell, + const LibertyGroup *pin_group, + LibertyPortGroupMap &port_group_map) { - visitDefaultOutputPinRes(attr, RiseFall::fall()); + for (const LibertyAttrValue *port_value : pin_group->params()) { + const std::string &port_name = port_value->stringValue(); + LibertyPort *port = makePort(cell, port_name.c_str()); + port_group_map[pin_group].push_back(port); + } } void -LibertyReader::visitDefaultOutputPinRes(LibertyAttr *attr, - const RiseFall *rf) -{ - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setDefaultOutputPinRes(rf, value * res_scale_); +LibertyReader::makeBusPort(LibertyCell *cell, + const LibertyGroup *bus_group, + LibertyPortGroupMap &port_group_map) +{ + for (const LibertyAttrValue *port_value : bus_group->params()) { + const std::string &port_name = port_value->stringValue(); + const LibertySimpleAttr *bus_type_attr = bus_group->findSimpleAttr("bus_type"); + if (bus_type_attr) { + const std::string *bus_type = bus_type_attr->stringValue(); + if (bus_type) { + // Look for bus dcl local to cell first. + BusDcl *bus_dcl = cell->findBusDcl(bus_type->c_str()); + 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()); + LibertyPort *bus_port = makeBusPort(cell, port_name.c_str(), + bus_dcl->from(), bus_dcl->to(), + bus_dcl); + port_group_map[bus_group].push_back(bus_port); + // Make ports for pin groups inside the bus group. + makeBusPinPorts(cell, bus_group, port_group_map); + } + else + libWarn(1235, bus_type_attr, "bus_type %s not found.", bus_type->c_str()); + } + } + else + libWarn(1236, bus_type_attr, "bus_type not found."); } } void -LibertyReader::visitDefaultFanoutLoad(LibertyAttr *attr) +LibertyReader::makeBusPinPorts(LibertyCell *cell, + const LibertyGroup *bus_group, + LibertyPortGroupMap &port_group_map) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (value == 0.0) - libWarn(1173, attr, "default_fanout_load is 0.0."); - library_->setDefaultFanoutLoad(value); + for (const LibertyGroup *pin_group : bus_group->findSubgroups("pin")) { + 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()); + // Expand foo[3:0] port names. + PortNameBitIterator name_iter(cell, pin_name.c_str(), this, pin_group->line()); + while (name_iter.hasNext()) { + LibertyPort *pin_port = name_iter.next(); + if (pin_port) { + port_group_map[pin_group].push_back(pin_port); + } + else + libWarn(1232, pin_group, "pin %s not found.", pin_name.c_str()); + } + } + else + libWarn(1233, pin_group, "pin name is not a string."); } } } void -LibertyReader::visitDefaultWireLoad(LibertyAttr *attr) +LibertyReader::makeBundlePort(LibertyCell *cell, + const LibertyGroup *bundle_group, + LibertyPortGroupMap &port_group_map) { - if (library_) { - const char *value = getAttrString(attr); - if (value) { - stringDelete(default_wireload_); - default_wireload_ = stringCopy(value); + const std::string &bundle_name = bundle_group->firstName(); + debugPrint(debug_, "liberty", 1, " bundle %s", bundle_name.c_str()); + + const LibertyComplexAttr *member_attr = bundle_group->findComplexAttr("members"); + ConcretePortSeq *members = new ConcretePortSeq; + for (const LibertyAttrValue *member_value : member_attr->values()) { + if (member_value->isString()) { + const char *member_name = member_value->stringValue().c_str(); + LibertyPort *member = cell->findLibertyPort(member_name); + if (member == nullptr) + member = makePort(cell, member_name); + members->push_back(member); } } + LibertyPort *bundle_port = builder_.makeBundlePort(cell, bundle_name.c_str(), + members); + port_group_map[bundle_group].push_back(bundle_port); + // Make ports for pin groups inside the bundle group. + makeBundlePinPorts(cell, bundle_group, port_group_map); } void -LibertyReader::visitDefaultWireLoadMode(LibertyAttr *attr) -{ - if (library_) { - const char *wire_load_mode = getAttrString(attr); - if (wire_load_mode) { - WireloadMode mode = stringWireloadMode(wire_load_mode); - if (mode != WireloadMode::unknown) - library_->setDefaultWireloadMode(mode); +LibertyReader::makeBundlePinPorts(LibertyCell *cell, + const LibertyGroup *bundle_group, + LibertyPortGroupMap &port_group_map) +{ + for (const LibertyGroup *pin_group : bundle_group->findSubgroups("pin")) { + 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()); + 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(1174, attr, "default_wire_load_mode %s not found.", - wire_load_mode); + libWarn(1234, pin_group, "pin name is not a string."); } } } void -LibertyReader::visitDefaultWireLoadSelection(LibertyAttr *attr) +LibertyReader::makePgPinPort(LibertyCell *cell, + const LibertyGroup *pg_pin_group) { - if (library_) { - const char *value = getAttrString(attr); - if (value) { - stringDelete(default_wireload_selection_); - default_wireload_selection_ = stringCopy(value); + const std::string &port_name = pg_pin_group->firstName(); + LibertyPort *pg_port = makePort(cell, port_name.c_str()); + + const std::string *type_name = pg_pin_group->findAttrString("pg_type"); + if (type_name) { + PwrGndType type = findPwrGndType(type_name->c_str()); + PortDirection *dir = PortDirection::unknown(); + switch (type) { + case PwrGndType::primary_ground: + case PwrGndType::backup_ground: + case PwrGndType::internal_ground: + dir = PortDirection::ground(); + break; + case PwrGndType::primary_power: + case PwrGndType::backup_power: + case PwrGndType::internal_power: + dir = PortDirection::power(); + break; + case PwrGndType::none: + libError(1291, pg_pin_group, "unknown pg_type."); + break; + default: + break; } + pg_port->setPwrGndType(type); + pg_port->setDirection(dir); } + + const std::string *voltate_name = pg_pin_group->findAttrString("voltage_name"); + if (voltate_name) + pg_port->setVoltageName(voltate_name->c_str()); } +//////////////////////////////////////////////////////////////// + void -LibertyReader::visitDefaultOperatingConditions(LibertyAttr *attr) +LibertyReader::readPortAttributes(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) +{ + readCapacitance(ports, port_group); + readMinPulseWidth(cell, ports, port_group); + readPortAttrFloat("min_period", &LibertyPort::setMinPeriod, ports, + port_group, time_scale_); + readPortAttrBool("clock", &LibertyPort::setIsClock, ports, port_group); + readPortAttrFloat("fanout_load", &LibertyPort::setFanoutLoad, ports, + port_group, 1.0F); + readPortAttrFloatMinMax("max_fanout", &LibertyPort::setFanoutLimit, ports, + port_group, MinMax::max(), 1.0F); + readPortAttrFloatMinMax("min_fanout", &LibertyPort::setFanoutLimit, ports, + port_group, MinMax::min(), 1.0F); + readPulseClock(ports, port_group); + readPortAttrBool("clock_gate_clock_pin", &LibertyPort::setIsClockGateClock, + ports, port_group); + readPortAttrBool("clock_gate_enable_pin", &LibertyPort::setIsClockGateEnable, + ports, port_group); + readPortAttrBool("clock_gate_out_pin", &LibertyPort::setIsClockGateOut, + ports, port_group); + readPortAttrBool("is_pll_feedback_pin", &LibertyPort::setIsPllFeedback, + ports, port_group); + readSignalType(cell, ports, port_group); + readPortAttrBool("isolation_cell_data_pin", + &LibertyPort::setIsolationCellData, ports, port_group); + readPortAttrBool("isolation_cell_enable_pin", + &LibertyPort::setIsolationCellEnable, ports, port_group); + readPortAttrBool("level_shifter_data_pin", + &LibertyPort::setLevelShifterData, ports, port_group); + readPortAttrBool("switch_pin", &LibertyPort::setIsSwitch, ports, port_group); + readPortAttrString("related_ground_pin", &LibertyPort::setRelatedGroundPin, + ports, port_group); + readPortAttrString("related_power_pin", &LibertyPort::setRelatedPowerPin, + ports, port_group); + readDriverWaveform(ports, port_group); +} + +void +LibertyReader::readDriverWaveform(const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - if (library_) { - const char *value = getAttrString(attr); - if (value) { - stringDelete(default_operating_condition_); - default_operating_condition_ = stringCopy(value); + for (const RiseFall *rf : RiseFall::range()) { + const char *attr_name = rf == RiseFall::rise() + ? "driver_waveform_rise" : "driver_waveform_fall"; + const std::string *name = port_group->findAttrString(attr_name); + if (name) { + DriverWaveform *waveform = library_->findDriverWaveform(name->c_str()); + if (waveform) { + for (LibertyPort *port : ports) + port->setDriverWaveform(waveform, rf); + } } } } void -LibertyReader::visitInputThresholdPctFall(LibertyAttr *attr) +LibertyReader::readPortAttrString(const char *attr_name, + void (LibertyPort::*set_func)(const char *value), + const LibertyPortSeq &ports, + const LibertyGroup *group) { - visitInputThresholdPct(attr, RiseFall::fall()); + const std::string *value = group->findAttrString(attr_name); + if (value) { + for (LibertyPort *port : ports) + (port->*set_func)(value->c_str()); + } } void -LibertyReader::visitInputThresholdPctRise(LibertyAttr *attr) +LibertyReader::readPortAttrFloat(const char *attr_name, + void (LibertyPort::*set_func)(float value), + const LibertyPortSeq &ports, + const LibertyGroup *group, + float scale) { - visitInputThresholdPct(attr, RiseFall::rise()); + float value; + bool exists; + group->findAttrFloat(attr_name, value, exists); + if (exists) { + for (LibertyPort *port : ports) + (port->*set_func)(value * scale); + } } void -LibertyReader::visitInputThresholdPct(LibertyAttr *attr, - const RiseFall *rf) +LibertyReader::readPortAttrBool(const char *attr_name, + void (LibertyPort::*set_func)(bool value), + const LibertyPortSeq &ports, + const LibertyGroup *group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setInputThreshold(rf, value / 100.0F); + const LibertySimpleAttr *attr = group->findSimpleAttr(attr_name); + if (attr) { + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isString()) { + const std::string &value = attr_value.stringValue(); + if (stringEqual(value.c_str(), "true")) { + for (LibertyPort *port : ports) + (port->*set_func)(true); + } + else if (stringEqual(value.c_str(), "false")) { + for (LibertyPort *port : ports) + (port->*set_func)(false); + } + else + libWarn(1238, attr, "%s attribute is not boolean.", attr_name); + } + else + libWarn(1239, attr, "%s attribute is not boolean.", attr_name); } - have_input_threshold_[rf->index()] = true; } void -LibertyReader::visitOutputThresholdPctFall(LibertyAttr *attr) +LibertyReader::readPortAttrFloatMinMax(const char *attr_name, + void (LibertyPort::*set_func)(float value, + const MinMax *min_max), + const LibertyPortSeq &ports, + const LibertyGroup *group, + const MinMax *min_max, + float scale) { - visitOutputThresholdPct(attr, RiseFall::fall()); + float value; + bool exists; + group->findAttrFloat(attr_name, value, exists); + if (exists) { + for (LibertyPort *port : ports) + (port->*set_func)(value * scale, min_max); + } } void -LibertyReader::visitOutputThresholdPctRise(LibertyAttr *attr) +LibertyReader::readPulseClock(const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - visitOutputThresholdPct(attr, RiseFall::rise()); + const std::string *pulse_clk = port_group->findAttrString("pulse_clock"); + if (pulse_clk) { + const RiseFall *trigger = nullptr; + const RiseFall *sense = nullptr; + if (*pulse_clk == "rise_triggered_high_pulse") { + trigger = RiseFall::rise(); + sense = RiseFall::rise(); + } + else if (*pulse_clk == "rise_triggered_low_pulse") { + trigger = RiseFall::rise(); + sense = RiseFall::fall(); + } + else if (*pulse_clk == "fall_triggered_high_pulse") { + trigger = RiseFall::fall(); + sense = RiseFall::rise(); + } + else if (*pulse_clk == "fall_triggered_low_pulse") { + trigger = RiseFall::fall(); + sense = RiseFall::fall(); + } + else + libWarn(1242, port_group, "pulse_latch unknown pulse type."); + if (trigger) { + for (LibertyPort *port : ports) + port->setPulseClk(trigger, sense); + } + } +} + +void +LibertyReader::readSignalType(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) +{ + if (!dynamic_cast(cell)) + return; + const std::string *type = port_group->findAttrString("signal_type"); + if (!type) + return; + ScanSignalType signal_type = ScanSignalType::none; + if (*type == "test_scan_enable") + signal_type = ScanSignalType::enable; + else if (*type == "test_scan_enable_inverted") + signal_type = ScanSignalType::enable_inverted; + else if (*type == "test_scan_clock") + signal_type = ScanSignalType::clock; + else if (*type == "test_scan_clock_a") + signal_type = ScanSignalType::clock_a; + else if (*type == "test_scan_clock_b") + signal_type = ScanSignalType::clock_b; + else if (*type == "test_scan_in") + signal_type = ScanSignalType::input; + else if (*type == "test_scan_in_inverted") + signal_type = ScanSignalType::input_inverted; + else if (*type == "test_scan_out") + signal_type = ScanSignalType::output; + else if (*type == "test_scan_out_inverted") + signal_type = ScanSignalType::output_inverted; + else { + libWarn(1299, port_group, "unknown signal_type %s.", type->c_str()); + return; + } + for (LibertyPort *port : ports) + port->setScanSignalType(signal_type); } void -LibertyReader::visitOutputThresholdPct(LibertyAttr *attr, - const RiseFall *rf) +LibertyReader::readPortDir(const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setOutputThreshold(rf, value / 100.0F); + const LibertySimpleAttr *dir_attr = port_group->findSimpleAttr("direction"); + // Note missing direction attribute is not an error because a bus group + // can have pin groups for the bus bits that have direcitons. + if (dir_attr) { + const std::string *dir = dir_attr->stringValue(); + if (dir) { + PortDirection *port_dir = PortDirection::unknown(); + if (*dir == "input") + port_dir = PortDirection::input(); + else if (*dir == "output") + port_dir = PortDirection::output(); + else if (*dir == "inout") + port_dir = PortDirection::bidirect(); + else if (*dir == "internal") + port_dir = PortDirection::internal(); + else + libWarn(1240, dir_attr, "unknown port direction."); + for (LibertyPort *port : ports) + port->setDirection(port_dir); + } } - have_output_threshold_[rf->index()] = true; } void -LibertyReader::visitSlewLowerThresholdPctFall(LibertyAttr *attr) +LibertyReader::readCapacitance(const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - visitSlewLowerThresholdPct(attr, RiseFall::fall()); -} + // capacitance + readPortAttrFloat("capacitance", &LibertyPort::setCapacitance, ports, + port_group, cap_scale_); -void -LibertyReader::visitSlewLowerThresholdPctRise(LibertyAttr *attr) -{ - visitSlewLowerThresholdPct(attr, RiseFall::rise()); -} + for (LibertyPort *port : ports) { + // rise/fall_capacitance + for (const RiseFall *rf : RiseFall::range()) { + std::string attr_name = rf->to_string_long() + "_capacitance"; + float cap; + bool exists; + port_group->findAttrFloat(attr_name, cap, exists); + if (exists) { + for (const MinMax *min_max : MinMax::range()) + port->setCapacitance(rf, min_max, cap * cap_scale_); + } + + // rise/fall_capacitance_range(min_cap, max_cap); + attr_name = rf->to_string_long() + "_capacitance_range"; + const LibertyComplexAttrSeq &range_attrs = port_group->findComplexAttrs(attr_name); + if (!range_attrs.empty()) { + const LibertyComplexAttr *attr = range_attrs[0]; + const LibertyAttrValueSeq &values = attr->values(); + if (values.size() == 2) { + float cap_min = values[0]->floatValue(); + float cap_max = values[1]->floatValue(); + port->setCapacitance(rf, MinMax::min(), cap_min * cap_scale_); + port->setCapacitance(rf, MinMax::max(), cap_max * cap_scale_); + } + } + } + if (!(port->isBus() || port->isBundle())) + setPortCapDefault(port); -void -LibertyReader::visitSlewLowerThresholdPct(LibertyAttr *attr, - const RiseFall *rf) -{ - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setSlewLowerThreshold(rf, value / 100.0F); + for (const MinMax *min_max : MinMax::range()) { + // min/max_capacitance + std::string attr_name = min_max->to_string() + "_capacitance"; + float limit; + bool exists; + port_group->findAttrFloat(attr_name, limit, exists); + if (exists) + port->setCapacitanceLimit(limit * cap_scale_, min_max); + + // min/max_transition + attr_name = min_max->to_string() + "_transition"; + port_group->findAttrFloat(attr_name, limit, exists); + if (exists) + port->setSlewLimit(limit * time_scale_, min_max); + } + + // Default capacitance. + if (port->isBus() || port->isBundle()) { + // Do not clobber member port capacitances by setting the capacitance + // on a bus or bundle. + LibertyPortMemberIterator member_iter(port); + while (member_iter.hasNext()) { + LibertyPort *member = member_iter.next(); + setPortCapDefault(member); + } + } + else + setPortCapDefault(port); } - have_slew_lower_threshold_[rf->index()] = true; } void -LibertyReader::visitSlewUpperThresholdPctFall(LibertyAttr *attr) +LibertyReader::setPortCapDefault(LibertyPort *port) { - visitSlewUpperThresholdPct(attr, RiseFall::fall()); + for (const MinMax *min_max : MinMax::range()) { + for (const RiseFall *rf : RiseFall::range()) { + float cap; + bool exists; + port->capacitance(rf, min_max, cap, exists); + if (!exists) + port->setCapacitance(rf, min_max, defaultCap(port)); + } + } } void -LibertyReader::visitSlewUpperThresholdPctRise(LibertyAttr *attr) +LibertyReader::readMinPulseWidth(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - visitSlewUpperThresholdPct(attr, RiseFall::rise()); + for (LibertyPort *port : ports) { + TimingArcAttrsPtr timing_attrs = nullptr; + for (const RiseFall *rf : RiseFall::range()) { + const char *mpw_attr_name = rf == RiseFall::rise() + ? "min_pulse_width_high" + : "min_pulse_width_low"; + float mpw; + bool exists; + port_group->findAttrFloat(mpw_attr_name, mpw, exists); + if (exists) { + mpw *= time_scale_; + port->setMinPulseWidth(rf, mpw); + + // Make timing arcs for the port min_pulse_width_low/high attributes. + // This is redundant but makes sdf annotation consistent. + if (timing_attrs == nullptr) { + timing_attrs = std::make_shared(); + timing_attrs->setTimingType(TimingType::min_pulse_width); + } + TimingModel *check_model = + makeScalarCheckModel(cell, mpw, ScaleFactorType::min_pulse_width, rf); + timing_attrs->setModel(rf, check_model); + } + } + if (timing_attrs) + builder_.makeTimingArcs(cell, port, port, nullptr, timing_attrs, + port_group->line()); + } } void -LibertyReader::visitSlewUpperThresholdPct(LibertyAttr *attr, - const RiseFall *rf) +LibertyReader::makePortFuncs(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) { - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setSlewUpperThreshold(rf, value / 100.0F); + const LibertySimpleAttr *func_attr = port_group->findSimpleAttr("function"); + if (func_attr) { + const std::string *func = func_attr->stringValue(); + if (func) { + FuncExpr *func_expr = parseFunc(func->c_str(), "function", cell, func_attr->line()); + 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.", + port->name()); + } + } + } } - have_slew_upper_threshold_[rf->index()] = true; -} -void -LibertyReader::visitSlewDerateFromLibrary(LibertyAttr *attr) -{ - if (library_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - library_->setSlewDerateFromLibrary(value); + const LibertySimpleAttr *tri_attr = port_group->findSimpleAttr("three_state"); + if (tri_attr) { + const std::string *tri_disable = tri_attr->stringValue(); + if (tri_disable) { + FuncExpr *tri_disable_expr = parseFunc(tri_disable->c_str(), + "three_state", cell, + tri_attr->line()); + FuncExpr *tri_enable_expr = tri_disable_expr->invert(); + for (LibertyPort *port : ports) { + port->setTristateEnable(tri_enable_expr); + if (port->direction() == PortDirection::output()) + port->setDirection(PortDirection::tristate()); + } + } } } //////////////////////////////////////////////////////////////// void -LibertyReader::beginTechnology(LibertyGroup *group) +LibertyReader::makeSequentials(LibertyCell *cell, + const LibertyGroup *cell_group) { - if (library_) { - const char *tech = group->firstName(); - if (stringEq(tech, "fpga")) - library_->setDelayModelType(DelayModelType::cmos_linear); + makeSequentials(cell, cell_group, true, "ff", "clocked_on", "next_state"); + makeSequentials(cell, cell_group, true, "ff_bank", "clocked_on", "next_state"); + makeSequentials(cell, cell_group, false, "latch", "enable", "data_in"); + makeSequentials(cell, cell_group, false, "latch_bank", "enable", "data_in"); + + const LibertyGroup *lut_group = cell_group->findSubgroup("lut");; + if (lut_group) { + LibertyPort *out_port = nullptr; + LibertyPort *out_port_inv = nullptr; + size_t size; + makeSeqPorts(cell, lut_group, out_port, out_port_inv, size); } } void -LibertyReader::endTechnology(LibertyGroup *) +LibertyReader::makeSequentials(LibertyCell *cell, + const LibertyGroup *cell_group, + bool is_register, + const char *seq_group_name, + const char *clk_attr_name, + const char *data_attr_name) { -} + for (const LibertyGroup *seq_group : cell_group->findSubgroups(seq_group_name)) { + LibertyPort *out_port = nullptr; + LibertyPort *out_port_inv = nullptr; + size_t size; + makeSeqPorts(cell, seq_group, out_port, out_port_inv, size); + FuncExpr *clk_expr = makeSeqFunc(cell, seq_group, clk_attr_name, size); + FuncExpr *data_expr = makeSeqFunc(cell, seq_group, data_attr_name, size); + FuncExpr *clr_expr = makeSeqFunc(cell, seq_group, "clear", size); + FuncExpr *preset_expr = makeSeqFunc(cell, seq_group, "preset", size); -void -LibertyReader::beginTableTemplateDelay(LibertyGroup *group) -{ - beginTableTemplate(group, TableTemplateType::delay); -} + LogicValue clr_preset_var1 = LogicValue::unknown; + const LibertySimpleAttr *var1 = seq_group->findSimpleAttr("clear_preset_var1"); + if (var1) + clr_preset_var1 = getAttrLogicValue(var1); -void -LibertyReader::beginTableTemplateOutputCurrent(LibertyGroup *group) -{ - beginTableTemplate(group, TableTemplateType::output_current); -} + LogicValue clr_preset_var2 = LogicValue::unknown; + const LibertySimpleAttr *var2 = seq_group->findSimpleAttr("clear_preset_var2"); + if (var2) + clr_preset_var2 = getAttrLogicValue(var2); -void -LibertyReader::beginTableTemplate(LibertyGroup *group, - TableTemplateType type) -{ - if (library_) { - const char *name = group->firstName(); - if (name) { - tbl_template_ = library_->makeTableTemplate(name, type); - } - else - libWarn(1175, group, "table template missing name."); - axis_var_[0] = axis_var_[1] = axis_var_[2] = TableAxisVariable::unknown; - clearAxisValues(); + cell->makeSequential(size, is_register, clk_expr, data_expr, clr_expr, + preset_expr, clr_preset_var1, clr_preset_var2, + out_port, out_port_inv); } } -void -LibertyReader::clearAxisValues() +FuncExpr * +LibertyReader::makeSeqFunc(LibertyCell *cell, + const LibertyGroup *seq_group, + const char *attr_name, + int size) { - axis_values_[0].clear(); - axis_values_[1].clear(); - axis_values_[2].clear(); + FuncExpr *expr = nullptr; + const std::string *attr = seq_group->findAttrString(attr_name); + 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); + delete expr; + expr = nullptr; + } + } + return expr; } void -LibertyReader::endTableTemplate(LibertyGroup *group) -{ - if (tbl_template_) { - TableAxisPtr axis1 = makeAxis(0, group); - if (axis1) - tbl_template_->setAxis1(axis1); - TableAxisPtr axis2 = makeAxis(1, group); - if (axis2) - tbl_template_->setAxis2(axis2); - TableAxisPtr axis3 = makeAxis(2, group); - if (axis3) - tbl_template_->setAxis3(axis3); - tbl_template_ = nullptr; - axis_var_[0] = axis_var_[1] = axis_var_[2] = TableAxisVariable::unknown; +LibertyReader::makeSeqPorts(LibertyCell *cell, + const LibertyGroup *seq_group, + // Return values. + LibertyPort *&out_port, + LibertyPort *&out_port_inv, + size_t &size) +{ + const char *out_name, *out_inv_name; + bool has_size; + seqPortNames(seq_group, out_name, out_inv_name, has_size, size); + if (out_name) { + if (has_size) + out_port = makeBusPort(cell, out_name, size - 1, 0, nullptr); + else + out_port = makePort(cell, out_name); + out_port->setDirection(PortDirection::internal()); + } + if (out_inv_name) { + if (has_size) + out_port_inv = makeBusPort(cell, out_inv_name, size - 1, 0, nullptr); + else + out_port_inv = makePort(cell, out_inv_name); + out_port_inv->setDirection(PortDirection::internal()); } } -TableAxisPtr -LibertyReader::makeAxis(int index, - LibertyGroup *group) +void +LibertyReader::seqPortNames(const LibertyGroup *group, + const char *&out_name, + const char *&out_inv_name, + bool &has_size, + size_t &size) { - TableAxisVariable axis_var = axis_var_[index]; - if (axis_var != TableAxisVariable::unknown) { - FloatSeq values; - if (!axis_values_[index].empty()) { - const Units *units = library_->units(); - float scale = tableVariableUnit(axis_var, units)->scale(); - values = std::move(axis_values_[index]); - scaleFloats(values, scale); - } - return make_shared(axis_var, std::move(values)); + out_name = nullptr; + out_inv_name = nullptr; + if (group->params().size() == 1) { + // out_port + out_name = group->firstName(); + size = 1; + has_size = false; } - else if (!axis_values_[index].empty()) { - libWarn(1176, group, "missing variable_%d attribute.", index + 1); - axis_values_[index].clear(); + if (group->params().size() == 2) { + // out_port, out_port_inv + out_name = group->firstName(); + out_inv_name = group->secondName(); + size = 1; + has_size = false; + } + else if (group->params().size() == 3) { + LibertyAttrValue *third_value = group->params()[2]; + if (third_value->isFloat()) { + // out_port, out_port_inv, bus_size + out_name = group->firstName(); + out_inv_name = group->secondName(); + size = static_cast(third_value->floatValue()); + has_size = true; + } + else { + // in_port (ignored), out_port, out_port_inv + out_name = group->secondName(); + out_inv_name = third_value->stringValue().c_str(); + has_size = true; + size = 1; + } } - // No warning for missing index_xx attributes because they are not required. - return nullptr; } -static void -scaleFloats(FloatSeq &floats, - float scale) -{ - size_t count = floats.size(); - for (size_t i = 0; i < count; i++) - floats[i] *= scale; -} +//////////////////////////////////////////////////////////////// void -LibertyReader::visitVariable1(LibertyAttr *attr) -{ - visitVariable(0, attr); +LibertyReader::readCellAttributes(LibertyCell *cell, + const LibertyGroup *cell_group) +{ + readCellAttrFloat("area", &LibertyCell::setArea, cell, cell_group, 1.0); + readCellAttrString("cell_footprint", &LibertyCell::setFootprint, cell, cell_group); + readCellAttrBool("dont_use", &LibertyCell::setDontUse, cell, cell_group); + readCellAttrBool("is_macro_cell", &LibertyCell::setIsMacro, cell, cell_group); + readCellAttrBool("is_pad", &LibertyCell::setIsPad, cell, cell_group); + readCellAttrBool("is_level_shifter", &LibertyCell::setIsLevelShifter, cell, cell_group); + readCellAttrBool("is_clock_cell", &LibertyCell::setIsClockCell, cell, cell_group); + readCellAttrBool("is_isolation_cell", &LibertyCell::setIsIsolationCell,cell,cell_group); + readCellAttrBool("always_on", &LibertyCell::setAlwaysOn,cell,cell_group); + readCellAttrBool("interface_timing", &LibertyCell::setInterfaceTiming,cell,cell_group); + readCellAttrFloat("cell_leakage_power", &LibertyCell::setLeakagePower, cell, + cell_group, power_scale_); + + readCellAttrBool("is_memory", &LibertyCell::setIsMemory, cell, cell_group); + if (cell_group->findSubgroup("memory")) + cell->setIsMemory(true); + + readCellAttrBool("pad_cell", &LibertyCell::setIsPad, cell, cell_group); + readLevelShifterType(cell, cell_group); + readSwitchCellType(cell, cell_group); + readCellAttrString("user_function_class", &LibertyCell::setUserFunctionClass, + cell, cell_group); + + readOcvDerateFactors(cell, cell_group); + readCellOcvDerateGroup(cell, cell_group); + readGroupAttrFloat("ocv_arc_depth", cell_group, + [cell](float v) { cell->setOcvArcDepth(v); }); + + const std::string *clock_gate_type = + cell_group->findAttrString("clock_gating_integrated_cell"); + if (clock_gate_type) { + if (stringBeginEqual(clock_gate_type->c_str(), "latch_posedge")) + cell->setClockGateType(ClockGateType::latch_posedge); + else if (stringBeginEqual(clock_gate_type->c_str(), "latch_negedge")) + cell->setClockGateType(ClockGateType::latch_negedge); + else + cell->setClockGateType(ClockGateType::other); + } + + readScaleFactors(cell, cell_group); + readLeagageGrouops(cell, cell_group); + readStatetable(cell, cell_group); + readModeDefs(cell, cell_group); } void -LibertyReader::visitVariable2(LibertyAttr *attr) +LibertyReader::readScaleFactors(LibertyCell *cell, + const LibertyGroup *cell_group) { - visitVariable(1, attr); + const std::string *scale_factors_name = cell_group->findAttrString("scaling_factors"); + if (scale_factors_name) { + ScaleFactors *scale_factors = library_->findScaleFactors(scale_factors_name->c_str()); + if (scale_factors) + cell->setScaleFactors(scale_factors); + else + libWarn(1230, cell_group, "scaling_factors %s not found.", + scale_factors_name->c_str()); + } } void -LibertyReader::visitVariable3(LibertyAttr *attr) +LibertyReader::readCellAttrString(const char *attr_name, + void (LibertyCell::*set_func)(const char *value), + LibertyCell *cell, + const LibertyGroup *group) { - visitVariable(2, attr); + const std::string *value = group->findAttrString(attr_name); + if (value) + (cell->*set_func)(value->c_str()); } void -LibertyReader::visitVariable(int index, - LibertyAttr *attr) -{ - if (tbl_template_) { - const char *type = getAttrString(attr); - TableAxisVariable var = stringTableAxisVariable(type); - if (var == TableAxisVariable::unknown) - libWarn(1297, attr, "axis type %s not supported.", type); +LibertyReader::readCellAttrFloat(const char *attr_name, + void (LibertyCell::*set_func)(float value), + LibertyCell *cell, + const LibertyGroup *group, + float scale) +{ + float value; + bool exists; + group->findAttrFloat(attr_name, value, exists); + if (exists) + (cell->*set_func)(value * scale); +} + +void +LibertyReader::readCellAttrBool(const char *attr_name, + void (LibertyCell::*set_func)(bool value), + LibertyCell *cell, + const LibertyGroup *group) +{ + const LibertySimpleAttr *attr = group->findSimpleAttr(attr_name); + if (attr) { + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isString()) { + const std::string &value = attr_value.stringValue(); + if (stringEqual(value.c_str(), "true")) + (cell->*set_func)(true); + else if (stringEqual(value.c_str(), "false")) + (cell->*set_func)(false); + else + libWarn(1279, attr, "%s attribute is not boolean.", attr_name); + } else - axis_var_[index] = var; + libWarn(1280, attr, "%s attribute is not boolean.", attr_name); } } +//////////////////////////////////////////////////////////////// + void -LibertyReader::visitIndex1(LibertyAttr *attr) -{ - visitIndex(0, attr); +LibertyReader::makeTimingArcs(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) +{ + for (const LibertyGroup *timing_group : port_group->findSubgroups("timing")) { + TimingArcAttrsPtr timing_attrs = std::make_shared(); + readTimingArcAttrs(cell, timing_group, timing_attrs); + makeTimingModels(cell, timing_group, timing_attrs); + + LibertyPort *related_output_port = findLibertyPort(cell, timing_group, + "related_output_pin"); + StdStringSeq related_port_names = findAttributStrings(timing_group, "related_pin"); + StdStringSeq related_bus_names=findAttributStrings(timing_group,"related_bus_pins"); + TimingType timing_type = timing_attrs->timingType(); + + for (LibertyPort *to_port : ports) { + if (timing_type == TimingType::combinational && + to_port->direction()->isInput()) + libWarn(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()); + 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()); + makeTimingArcs(cell, from_port_name, to_port, related_output_port, false, + timing_attrs, timing_group->line()); + } + } + else if (!(timing_type == TimingType::min_pulse_width + || 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."); + else + makeTimingArcs(cell, to_port, related_output_port, + timing_attrs, timing_group->line()); + } + } } void -LibertyReader::visitIndex2(LibertyAttr *attr) +LibertyReader::readTimingArcAttrs(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs) { - visitIndex(1, attr); + readTimingSense(timing_group, timing_attrs); + readTimingType(timing_group, timing_attrs); + readTimingWhen(cell, timing_group, timing_attrs); + readTimingMode(timing_group, timing_attrs); + readGroupAttrFloat("ocv_arc_depth", timing_group, + [timing_attrs](float v) { timing_attrs->setOcvArcDepth(v); }); } void -LibertyReader::visitIndex3(LibertyAttr *attr) +LibertyReader::readGroupAttrFloat(const char *attr_name, + const LibertyGroup *group, + const std::function &set_func, + float scale) { - visitIndex(2, attr); + float value; + bool exists; + group->findAttrFloat(attr_name, value, exists); + if (exists) + set_func(value * scale); } void -LibertyReader::visitIndex(int index, - LibertyAttr *attr) +LibertyReader::readTimingSense(const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs) { - if (tbl_template_ - // Ignore index_* in ecsm_waveform groups. - && !in_ecsm_waveform_) { - FloatSeq axis_values = readFloatSeq(attr, 1.0F); - if (axis_values.empty()) - libWarn(1177, 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(1178, attr, "non-increasing table index values."); - prev = value; - } - axis_values_[index] = std::move(axis_values); + const LibertySimpleAttr *sense_attr = timing_group->findSimpleAttr("timing_sense"); + if (sense_attr) { + const std::string *sense_name = sense_attr->stringValue(); + if (sense_name) { + if (*sense_name == "non_unate") + timing_attrs->setTimingSense(TimingSense::non_unate); + else if (*sense_name == "positive_unate") + timing_attrs->setTimingSense(TimingSense::positive_unate); + 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()); } } } -//////////////////////////////////////////////////////////////// - void -LibertyReader::beginType(LibertyGroup *) +LibertyReader::readTimingType(const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs) { - type_bit_from_exists_ = false; - type_bit_to_exists_ = false; + TimingType type = TimingType::combinational; + const LibertySimpleAttr *type_attr = timing_group->findSimpleAttr("timing_type"); + if (type_attr) { + const std::string *type_name = type_attr->stringValue(); + 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()); + type = TimingType::combinational; + } + } + } + timing_attrs->setTimingType(type); } void -LibertyReader::endType(LibertyGroup *group) +LibertyReader::readTimingWhen(const LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs) { - const char *name = group->firstName(); - if (name) { - if (type_bit_from_exists_ && type_bit_to_exists_) { - if (cell_) - cell_->makeBusDcl(name, type_bit_from_, type_bit_to_); - else if (library_) - library_->makeBusDcl(name, type_bit_from_, type_bit_to_); - } - else { - if (!type_bit_from_exists_) - libWarn(1179, group, "bus type %s missing bit_from.", name); - if (!type_bit_to_exists_) - libWarn(1180, group, "bus type %s missing bit_to.", name); + const LibertySimpleAttr *when_attr = timing_group->findSimpleAttr("when"); + if (when_attr) { + const std::string *when = when_attr->stringValue(); + if (when) { + FuncExpr *when_expr = parseFunc(when->c_str(), "when", cell, when_attr->line()); + timing_attrs->setCond(when_expr); } } - else - libWarn(1181, group, "type missing name."); -} - -void -LibertyReader::visitBitFrom(LibertyAttr *attr) -{ - getAttrInt(attr, type_bit_from_, type_bit_from_exists_); -} - -void -LibertyReader::visitBitTo(LibertyAttr *attr) -{ - getAttrInt(attr, type_bit_to_, type_bit_to_exists_); -} - -//////////////////////////////////////////////////////////////// -void -LibertyReader::beginScalingFactors(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) { - save_scale_factors_ = scale_factors_; - scale_factors_ = library_->makeScaleFactors(name); + const LibertySimpleAttr *cond_attr = timing_group->findSimpleAttr("sdf_cond"); + if (cond_attr) { + const std::string *cond = cond_attr->stringValue(); + if (cond) + timing_attrs->setSdfCond(cond->c_str()); } - else - libWarn(1182, group, "scaling_factors do not have a name."); -} - -void -LibertyReader::endScalingFactors(LibertyGroup *) -{ - scale_factors_ = save_scale_factors_; -} - -void -LibertyReader::visitScaleFactorSuffix(LibertyAttr *attr) -{ - if (scale_factors_) { - ScaleFactorPvt pvt = ScaleFactorPvt::unknown; - ScaleFactorType type = ScaleFactorType::unknown; - const RiseFall *rf = nullptr; - // Parse the attribute name. - TokenParser parser(attr->name().c_str(), "_"); - if (parser.hasNext()) - parser.next(); - if (parser.hasNext()) { - const char *pvt_name = parser.next(); - pvt = findScaleFactorPvt(pvt_name); - } - if (parser.hasNext()) { - const char *type_name = parser.next(); - type = findScaleFactorType(type_name); - } - if (parser.hasNext()) { - const char *tr_name = parser.next(); - if (stringEq(tr_name, "rise")) - rf = RiseFall::rise(); - else if (stringEq(tr_name, "fall")) - rf = RiseFall::fall(); - } - if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown - && rf) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - scale_factors_->setScale(type, pvt, rf, value); - } + cond_attr = timing_group->findSimpleAttr("sdf_cond_start"); + if (cond_attr) { + const std::string *cond = cond_attr->stringValue(); + if (cond) + timing_attrs->setSdfCondStart(cond->c_str()); } -} - -void -LibertyReader::visitScaleFactorPrefix(LibertyAttr *attr) -{ - if (scale_factors_) { - ScaleFactorPvt pvt = ScaleFactorPvt::unknown; - ScaleFactorType type = ScaleFactorType::unknown; - const RiseFall *rf = nullptr; - // Parse the attribute name. - TokenParser parser(attr->name().c_str(), "_"); - if (parser.hasNext()) - parser.next(); - if (parser.hasNext()) { - const char *pvt_name = parser.next(); - pvt = findScaleFactorPvt(pvt_name); - } - if (parser.hasNext()) { - const char *tr_name = parser.next(); - if (stringEq(tr_name, "rise")) - rf = RiseFall::rise(); - else if (stringEq(tr_name, "fall")) - rf = RiseFall::fall(); - } - if (parser.hasNext()) { - const char *type_name = parser.next(); - type = findScaleFactorType(type_name); - } - if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown - && rf) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - scale_factors_->setScale(type, pvt, rf, value); - } + cond_attr = timing_group->findSimpleAttr("sdf_cond_end"); + if (cond_attr) { + const std::string *cond = cond_attr->stringValue(); + if (cond) + timing_attrs->setSdfCondEnd(cond->c_str()); } } void -LibertyReader::visitScaleFactorHiLow(LibertyAttr *attr) -{ - if (scale_factors_) { - ScaleFactorPvt pvt = ScaleFactorPvt::unknown; - ScaleFactorType type = ScaleFactorType::unknown; - const RiseFall *rf = nullptr; - const char *pvt_name = nullptr; - const char *type_name = nullptr; - const char *tr_name = nullptr; - // Parse the attribute name. - TokenParser parser(attr->name().c_str(), "_"); - if (parser.hasNext()) - parser.next(); - if (parser.hasNext()) { - pvt_name = parser.next(); - pvt = findScaleFactorPvt(pvt_name); - } - if (parser.hasNext()) { - type_name = parser.next(); - type = findScaleFactorType(type_name); - } - if (parser.hasNext()) { - tr_name = parser.next(); - if (stringEq(tr_name, "high")) - rf = RiseFall::rise(); - else if (stringEq(tr_name, "low")) - rf = RiseFall::fall(); - } - if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown - && rf) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - scale_factors_->setScale(type, pvt, rf, value); - } - } -} +LibertyReader::readTimingMode(const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs) +{ + const LibertyComplexAttrSeq &mode_attrs = timing_group->findComplexAttrs("mode"); + if (!mode_attrs.empty()) { + const LibertyComplexAttr *mode_attr = mode_attrs[0]; + const LibertyAttrValueSeq &mode_values = mode_attr->values(); + if (mode_values.size() == 2) { + LibertyAttrValue *value = mode_values[0]; + if (value->isString()) + timing_attrs->setModeName(value->stringValue()); + else + libWarn(1248, mode_attr, "mode name is not a string."); -void -LibertyReader::visitScaleFactor(LibertyAttr *attr) -{ - if (scale_factors_) { - ScaleFactorPvt pvt = ScaleFactorPvt::unknown; - ScaleFactorType type = ScaleFactorType::unknown; - const char *pvt_name = nullptr; - const char *type_name = nullptr; - // Parse the attribute name. - TokenParser parser(attr->name().c_str(), " "); - if (parser.hasNext()) - parser.next(); - if (parser.hasNext()) { - pvt_name = parser.next(); - pvt = findScaleFactorPvt(pvt_name); - } - if (parser.hasNext()) { - type_name = parser.next(); - type = findScaleFactorType(type_name); - } - if (pvt != ScaleFactorPvt::unknown - && type != ScaleFactorType::unknown) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - scale_factors_->setScale(type, pvt, value); + value = mode_values[1]; + if (value->isString()) + timing_attrs->setModeValue(value->stringValue()); + else + libWarn(1246, mode_attr, "mode value is not a string."); } - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginOpCond(LibertyGroup *group) -{ - if (library_) { - const char *name = group->firstName(); - if (name) - op_cond_ = library_->makeOperatingConditions(name); else - libWarn(1183, group, "operating_conditions missing name."); + libWarn(1249, mode_attr, "mode requirees 2 values."); } } void -LibertyReader::visitProc(LibertyAttr *attr) +LibertyReader::makeTimingModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs) { - if (op_cond_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - op_cond_->setProcess(value); + switch (cell->libertyLibrary()->delayModelType()) { + case DelayModelType::cmos_linear: + makeLinearModels(cell, timing_group, timing_attrs); + break; + case DelayModelType::table: + makeTableModels(cell, timing_group, timing_attrs); + break; + case DelayModelType::cmos_pwl: + case DelayModelType::cmos2: + case DelayModelType::polynomial: + case DelayModelType::dcm: + break; } } void -LibertyReader::visitVolt(LibertyAttr *attr) +LibertyReader::makeLinearModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs) { - if (op_cond_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - op_cond_->setVoltage(value * volt_scale_); + LibertyLibrary *library = cell->libertyLibrary(); + for (const RiseFall *rf : RiseFall::range()) { + std::string intr_attr_name = "intrinsic_" + rf->to_string_long(); + float intr = 0.0; + bool intr_exists; + timing_group->findAttrFloat(intr_attr_name, intr, intr_exists); + if (intr_exists) + intr *= time_scale_; + else + library->defaultIntrinsic(rf, intr, intr_exists); + TimingModel *model = nullptr; + if (intr_exists) { + if (timingTypeIsCheck(timing_attrs->timingType())) + model = new CheckLinearModel(cell, intr); + else { + std::string res_attr_name = rf->to_string_long() + "_resistance"; + float res = 0.0; + bool res_exists; + timing_group->findAttrFloat(res_attr_name, res, res_exists); + if (res_exists) + res *= res_scale_; + else + library->defaultPinResistance(rf, PortDirection::output(), + res, res_exists); + model = new GateLinearModel(cell, intr, res); + } + timing_attrs->setModel(rf, model); + } } } void -LibertyReader::visitTemp(LibertyAttr *attr) +LibertyReader::makeTableModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs) { - if (op_cond_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - op_cond_->setTemperature(value); - } -} + for (const RiseFall *rf : RiseFall::range()) { + std::string delay_attr_name = "cell_" + rf->to_string_long(); + 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_long() + "_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_long(); + TableModelsEarlyLate delay_sigmas = + readEarlyLateTableModels(timing_group, + delay_sigma_attr_name.c_str(), + rf, TableTemplateType::delay, + time_scale_, + ScaleFactorType::unknown); + + std::string slew_sigma_attr_name = "ocv_sigma_" + rf->to_string_long() + + "_transition"; + TableModelsEarlyLate slew_sigmas = + readEarlyLateTableModels(timing_group, + slew_sigma_attr_name.c_str(), + rf, TableTemplateType::delay, + time_scale_, + ScaleFactorType::unknown); + + 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), + receiver_model, + output_waveforms)); + TimingType timing_type = timing_attrs->timingType(); + if (isGateTimingType(timing_type)) { + if (transition == nullptr) + libWarn(1210, timing_group, "missing %s_transition.", rf->name()); + if (delay == nullptr) + libWarn(1211, timing_group, "missing cell_%s.", rf->name()); + } + } -void -LibertyReader::visitTreeType(LibertyAttr *attr) -{ - if (op_cond_) { - const char *tree_type = getAttrString(attr); - if (tree_type) { - WireloadTree wire_load_tree = stringWireloadTree(tree_type); - op_cond_->setWireloadTree(wire_load_tree); + std::string constraint_attr_name = rf->to_string_long() + "_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_long() + + "_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))); } } } -void -LibertyReader::endOpCond(LibertyGroup *) -{ - op_cond_ = nullptr; +bool +LibertyReader::isGateTimingType(TimingType timing_type) +{ + return timing_type == TimingType::clear + || timing_type == TimingType::combinational + || timing_type == TimingType::combinational_fall + || timing_type == TimingType::combinational_rise + || timing_type == TimingType::falling_edge + || timing_type == TimingType::preset + || timing_type == TimingType::rising_edge + || timing_type == TimingType::three_state_disable + || timing_type == TimingType::three_state_disable_rise + || timing_type == TimingType::three_state_disable_fall + || timing_type == TimingType::three_state_enable + || timing_type == TimingType::three_state_enable_fall + || timing_type == TimingType::three_state_enable_rise; } -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginWireload(LibertyGroup *group) -{ - if (library_) { - const char *name = group->firstName(); - if (name) - wireload_ = library_->makeWireload(name); +TableModel * +LibertyReader::readGateTableModel(const LibertyGroup *timing_group, + const char *table_group_name, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type) +{ + 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; } - else - libWarn(1184, group, "wire_load missing name."); -} - -void -LibertyReader::endWireload(LibertyGroup *) -{ - wireload_ = nullptr; + return nullptr; } -void -LibertyReader::visitResistance(LibertyAttr *attr) -{ - if (wireload_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - wireload_->setResistance(value * res_scale_); +TableModel * +LibertyReader::readCheckTableModel(const LibertyGroup *timing_group, + const char *table_group_name, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type) +{ + 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; } + return nullptr; } -void -LibertyReader::visitSlope(LibertyAttr *attr) +TableModelsEarlyLate +LibertyReader::readEarlyLateTableModels(const LibertyGroup *timing_group, + const char *table_group_name, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type) { - if (wireload_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - wireload_->setSlope(value); - } -} + 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); + const std::string *early_late = table_group->findAttrString("sigma_type"); + if (early_late == nullptr + || *early_late == "early_and_late") { + models[EarlyLate::early()->index()] = model; + models[EarlyLate::late()->index()] = model; + } + else if (*early_late == "early") + models[EarlyLate::early()->index()] = model; + else if (*early_late == "late") + models[EarlyLate::late()->index()] = model; -void -LibertyReader::visitFanoutLength(LibertyAttr *attr) -{ - if (wireload_) { - float fanout, length; - bool exists; - getAttrFloat2(attr, fanout, length, exists); - if (exists) - wireload_->addFanoutLength(fanout, length); - else - libWarn(1185, attr, "fanout_length is missing length and fanout."); + //if (model && !GateTableModel::checkAxes(model)) + // libWarn(1182, table_group, "unsupported model axis."); } + return models; } -void -LibertyReader::beginWireloadSelection(LibertyGroup *group) +ReceiverModelPtr +LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, + const RiseFall *rf) { - if (library_) { - const char *name = group->firstName(); - if (name) - wireload_selection_ = library_->makeWireloadSelection(name); + ReceiverModelPtr receiver_model = nullptr; + readReceiverCapacitance(timing_group, "receiver_capacitance", 0, rf, + receiver_model); + readReceiverCapacitance(timing_group, "receiver_capacitance1", 0, rf, + receiver_model); + readReceiverCapacitance(timing_group, "receiver_capacitance2", 1, rf, + receiver_model); + return receiver_model; +} + +void +LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, + const char *cap_group_name, + int index, + const RiseFall *rf, + ReceiverModelPtr &receiver_model) +{ + std::string cap_group_name1 = cap_group_name; + cap_group_name1 += "_" + rf->to_string_long(); + const LibertyGroup *cap_group = timing_group->findSubgroup(cap_group_name1); + if (cap_group) { + const LibertySimpleAttr *segment_attr = cap_group->findSimpleAttr("segment"); + if (segment_attr) { + // For receiver_capacitance groups with mulitiple segments this + // overrides the index passed in beginReceiverCapacitance1Rise/Fall. + int segment; + bool exists; + getAttrInt(segment_attr, segment, exists); + if (exists) + index = segment; + } + TableModel *model = readTableModel(cap_group, rf, TableTemplateType::delay, + cap_scale_, ScaleFactorType::pin_cap); + if (ReceiverModel::checkAxes(model)) { + if (receiver_model == nullptr) + receiver_model = std::make_shared(); + receiver_model->setCapacitanceModel(std::move(*model), index, rf); + } + else + libWarn(1219, cap_group, "unsupported model axis."); + delete model; } - else - libWarn(1186, group, "wire_load_selection missing name."); -} - -void -LibertyReader::endWireloadSelection(LibertyGroup *) -{ - wireload_selection_ = nullptr; } -void -LibertyReader::visitWireloadFromArea(LibertyAttr *attr) -{ - if (wireload_selection_) { - if (attr->isComplexAttr()) { - LibertyAttrValueSeq *values = attr->values(); - if (values->size() == 3) { - LibertyAttrValue *value = (*values)[0]; - if (value->isFloat()) { - float min_area = value->floatValue(); - value = (*values)[1]; - if (value->isFloat()) { - float max_area = value->floatValue(); +OutputWaveforms * +LibertyReader::readOutputWaveforms(const LibertyGroup *timing_group, + const RiseFall *rf) +{ + const std::string current_group_name = "output_current_" + rf->to_string_long(); + const LibertyGroup *current_group = timing_group->findSubgroup(current_group_name); + if (current_group) { + OutputWaveformSeq output_currents; + for (const LibertyGroup *vector_group : current_group->findSubgroups("vector")) { + float ref_time; + bool ref_time_exists; + vector_group->findAttrFloat("reference_time", ref_time, ref_time_exists); + if (ref_time_exists) { + ref_time *= time_scale_; + TableModel *table = readTableModel(vector_group, rf, + TableTemplateType::output_current, + current_scale_, ScaleFactorType::unknown); + if (table) { + TableTemplate *tbl_template = table->tblTemplate(); + const TableAxis *slew_axis, *cap_axis; + // Canonicalize axis order. + if (tbl_template->axis1()->variable()==TableAxisVariable::input_net_transition){ + slew_axis = table->axis1(); + cap_axis = table->axis2(); + } + else { + slew_axis = table->axis2(); + cap_axis = table->axis1(); + } - value = (*values)[2]; - if (value->isString()) { - const std::string &wireload_name = value->stringValue(); - const Wireload *wireload = - library_->findWireload(wireload_name.c_str()); - if (wireload) - wireload_selection_->addWireloadFromArea(min_area, max_area, - wireload); - else - libWarn(1187, attr, "wireload %s not found.", wireload_name.c_str()); - } - else - libWarn(1188, attr, - "wire_load_from_area wireload name not a string."); + if (slew_axis->size() == 1 && cap_axis->size() == 1) { + // Convert 1x1xN Table (order 3) to 1D Table. + float slew = slew_axis->axisValue(0); + float cap = cap_axis->axisValue(0); + TablePtr table_ptr = table->table(); + FloatTable *values3 = table_ptr->values3(); + FloatSeq row = std::move((*values3)[0]); + values3->erase(values3->begin()); + Table *table1 = new Table(std::move(row), table->table()->axis3ptr()); + output_currents.emplace_back(slew, cap, table1, ref_time); } else - libWarn(1189, attr, "wire_load_from_area min not a float."); + libWarn(1223, vector_group, + "vector index_1 and index_2 must have exactly one value."); } - else - libWarn(1190, attr, "wire_load_from_area max not a float."); + delete table; } else - libWarn(1191, attr, "wire_load_from_area missing parameters."); - } - else - libWarn(1192, attr, "wire_load_from_area missing parameters."); - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginCell(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) { - debugPrint(debug_, "liberty", 1, "cell %s", name); - if (library_) { - cell_ = builder_.makeCell(library_, name, filename_); - in_bus_ = false; - in_bundle_ = false; + libWarn(1224, vector_group, "vector reference_time not found."); } + if (!output_currents.empty()) + return makeOutputWaveforms(current_group, output_currents, rf); } - else - libWarn(1193, group, "cell missing name."); + return nullptr; } -void -LibertyReader::endCell(LibertyGroup *group) -{ - if (cell_) { - // Sequentials and leakage powers reference expressions outside of port definitions - // so they do not require LibertyFunc's. - makeCellSequentials(); - makeStatetable(); - // Parse functions defined inside of port groups that reference other ports - // and replace the references with the parsed expressions. - parseCellFuncs(); - makeLeakagePowers(); - finishPortGroups(); - - if (ocv_derate_name_) { - OcvDerate *derate = cell_->findOcvDerate(ocv_derate_name_); - if (derate == nullptr) - derate = library_->findOcvDerate(ocv_derate_name_); - if (derate) - cell_->setOcvDerate(derate); - else - libWarn(1194, group, "cell %s ocv_derate_group %s not found.", - cell_->name(), ocv_derate_name_); - stringDelete(ocv_derate_name_); - ocv_derate_name_ = nullptr; +OutputWaveforms * +LibertyReader::makeOutputWaveforms(const LibertyGroup *current_group, + OutputWaveformSeq &output_currents, + const RiseFall *rf) +{ + std::set slew_set, cap_set; + FloatSeq slew_values; + FloatSeq cap_values; + for (const OutputWaveform &waveform : output_currents) { + float slew = waveform.slew(); + // Filter duplilcate slews and capacitances. + if (!slew_set.contains(slew)) { + slew_set.insert(slew); + slew_values.push_back(slew); + } + float cap = waveform.cap(); + if (!cap_set.contains(cap)) { + cap_set.insert(cap); + cap_values.push_back(cap); + } + } + sort(slew_values, std::less()); + sort(cap_values, std::less()); + size_t slew_size = slew_values.size(); + size_t cap_size = cap_values.size(); + TableAxisPtr slew_axis = + make_shared(TableAxisVariable::input_net_transition, + std::move(slew_values)); + TableAxisPtr cap_axis = + make_shared(TableAxisVariable::total_output_net_capacitance, + std::move(cap_values)); + FloatSeq ref_times(slew_size); + Table1Seq current_waveforms(slew_size * cap_size); + for (OutputWaveform &waveform : output_currents) { + size_t slew_index, cap_index; + bool slew_exists, cap_exists; + slew_axis->findAxisIndex(waveform.slew(), slew_index, slew_exists); + cap_axis->findAxisIndex(waveform.cap(), cap_index, cap_exists); + if (slew_exists && cap_exists) { + size_t index = slew_index * cap_axis->size() + cap_index; + current_waveforms[index] = waveform.releaseCurrents(); + ref_times[slew_index] = waveform.referenceTime(); } - cell_->finish(infer_latches_, report_, debug_); - cell_ = nullptr; + else + libWarn(1221, current_group, "output current waveform %.2e %.2e not found.", + waveform.slew(), + waveform.cap()); } + Table ref_time_tbl(std::move(ref_times), slew_axis); + OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, rf, + current_waveforms, + std::move(ref_time_tbl)); + return output_current; } -void -LibertyReader::finishPortGroups() +TableModel * +LibertyReader::readTableModel(const LibertyGroup *table_group, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type) { - for (PortGroup *port_group : cell_port_groups_) { - int line = port_group->line(); - for (LibertyPort *port : *port_group->ports()) { - checkPort(port, line); - makeMinPulseWidthArcs(port, line); + const char *template_name = table_group->firstName(); + if (library_ && template_name) { + TableTemplate *tbl_template = library_->findTableTemplate(template_name, + template_type); + if (tbl_template) { + TablePtr table = readTableModel(table_group, tbl_template, scale); + if (table) { + TableModel *table_model = new TableModel(table, tbl_template, + scale_factor_type, rf); + return table_model; + } } - makeTimingArcs(port_group); - makeInternalPowers(port_group); - delete port_group; + else + libWarn(1253, table_group, "table template %s not found.", template_name); } - cell_port_groups_.clear(); + return nullptr; } -void -LibertyReader::checkPort(LibertyPort *port, - int line) +TablePtr +LibertyReader::readTableModel(const LibertyGroup *table_group, + const TableTemplate *tbl_template, + float scale) { - FuncExpr *func_expr = port->function(); - if (func_expr) { - if (func_expr->checkSize(port)) { - libWarn(1195, line, "port %s function size does not match port size.", - port->name()); + const LibertyComplexAttr *values_attr = table_group->findComplexAttr("values"); + if (values_attr) { + TableAxisPtr axis1 = makeTableAxis(table_group, "index_1", tbl_template->axis1ptr()); + TableAxisPtr axis2 = makeTableAxis(table_group, "index_2", tbl_template->axis2ptr()); + TableAxisPtr axis3 = makeTableAxis(table_group, "index_3", tbl_template->axis3ptr()); + if (axis1 && axis2 && axis3) { + // 3D table + FloatTable float_table = makeFloatTable(values_attr, table_group, + axis1->size() * axis2->size(), + axis3->size(), scale); + return make_shared

oyfcZsc{>0d8zHLbyFM8 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 z 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

(std::move(float_table), axis1, axis2, axis3); + } + else if (axis1 && axis2) { + FloatTable float_table = makeFloatTable(values_attr, table_group, + axis1->size(), axis2->size(), scale); + return make_shared
(std::move(float_table), axis1, axis2); + } + else if (axis1) { + FloatTable table = makeFloatTable(values_attr, table_group, 1, + axis1->size(), scale); + return make_shared
(std::move(table[0]), axis1); + } + else if (axis1 == nullptr && axis2 == nullptr && axis3 == nullptr) { + FloatTable table = makeFloatTable(values_attr, table_group, 1, 1, scale); + float value = table[0][0]; + return std::make_shared
(value); } } - if (port->tristateEnable() - && port->direction() == PortDirection::output()) - port->setDirection(PortDirection::tristate()); + else + libWarn(1257, table_group, "%s is missing values.", table_group->type().c_str()); + return nullptr; } -// Make timing arcs for the port min_pulse_width_low/high attributes. -// This is redundant but makes sdf annotation consistent. -void -LibertyReader::makeMinPulseWidthArcs(LibertyPort *port, - int line) +TableAxisPtr +LibertyReader::makeTableAxis(const LibertyGroup *table_group, + const char *index_attr_name, + TableAxisPtr template_axis) { - TimingArcAttrsPtr attrs = nullptr; - for (auto hi_low : RiseFall::range()) { - float min_width; - bool exists; - port->minPulseWidth(hi_low, min_width, exists); - if (exists) { - if (attrs == nullptr) { - attrs = make_shared(); - attrs->setTimingType(TimingType::min_pulse_width); + const LibertyComplexAttr *index_attr = table_group->findComplexAttr(index_attr_name); + if (index_attr) { + FloatSeq axis_values = readFloatSeq(index_attr, 1.0F); + if (axis_values.empty()) + libWarn(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."); + prev = value; } - // rise/fall_constraint model is on the leading edge of the pulse. - const RiseFall *model_rf = hi_low; - TimingModel *check_model = - makeScalarCheckModel(min_width, ScaleFactorType::min_pulse_width, model_rf); - attrs->setModel(model_rf, check_model); - } - } - if (attrs) - builder_.makeTimingArcs(cell_, port, port, nullptr, attrs, line); -} -TimingModel * -LibertyReader::makeScalarCheckModel(float value, - ScaleFactorType scale_factor_type, - const RiseFall *rf) -{ - TablePtr table = 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, nullptr); - return check_model; -} - -void -LibertyReader::makeTimingArcs(PortGroup *port_group) -{ - for (TimingGroup *timing : port_group->timingGroups()) { - timing->makeTimingModels(cell_, this); - - for (LibertyPort *port : *port_group->ports()) - makeTimingArcs(port, timing); + TableAxisVariable axis_var = template_axis->variable(); + const Units *units = library_->units(); + float scale = tableVariableUnit(axis_var, units)->scale(); + scaleFloats(axis_values, scale); + return make_shared(axis_var, std::move(axis_values)); + } } + return template_axis; } -void -LibertyReader::makeInternalPowers(PortGroup *port_group) -{ - for (InternalPowerGroup *power_group : port_group->internalPowerGroups()) { - for (LibertyPort *port : *port_group->ports()) - makeInternalPowers(port, power_group); - } -} +//////////////////////////////////////////////////////////////// void -LibertyReader::makeCellSequentials() +LibertyReader::makeTimingArcs(LibertyCell *cell, + const std::string &from_port_name, + LibertyPort *to_port, + LibertyPort *related_out_port, + bool one_to_one, + TimingArcAttrsPtr timing_attrs, + int timing_line) { - for (SequentialGroup *seq : cell_sequentials_) { - makeCellSequential(seq); - delete seq; - } - cell_sequentials_.clear(); -} - -void -LibertyReader::makeCellSequential(SequentialGroup *seq) -{ - int line = seq->line(); - int size = seq->size(); - bool is_register = seq->isRegister(); - bool is_bank = seq->isBank(); - const char *type = is_register - ? (is_bank ? "ff_bank" : "ff") - : (is_bank ? "latch_bank" : "latch"); - const char *clk = seq->clock(); - FuncExpr *clk_expr = nullptr; - if (clk) { - const char *clk_attr = is_register ? "clocked_on" : "enable"; - clk_expr = parseFunc(clk, clk_attr, line); - if (clk_expr && clk_expr->checkSize(size)) { - libWarn(1196, line, "%s %s bus width mismatch.", type, clk_attr); - delete clk_expr; - clk_expr = nullptr; + PortNameBitIterator from_port_iter(cell, from_port_name.c_str(), this, timing_line); + if (from_port_iter.size() == 1 && !to_port->hasMembers()) { + // one -> one + 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."); + builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, + timing_attrs, timing_line); } } - const char *data = seq->data(); - FuncExpr *data_expr = nullptr; - if (data) { - const char *data_attr = is_register ? "next_state" : "data_in"; - data_expr = parseFunc(data, data_attr, line); - if (data_expr && data_expr->checkSize(size)) { - libWarn(1197, line, "%s %s bus width mismatch.", type, data_attr); - delete data_expr; - data_expr = nullptr; + else if (from_port_iter.size() > 1 && !to_port->hasMembers()) { + // bus -> one + 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."); + builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, + timing_attrs, timing_line); } } - const char *clr = seq->clear(); - FuncExpr *clr_expr = nullptr; - if (clr) { - clr_expr = parseFunc(clr, "clear", line); - if (clr_expr && clr_expr->checkSize(size)) { - libWarn(1198, line, "%s %s bus width mismatch.", type, "clear"); - delete clr_expr; - clr_expr = nullptr; - } - } - const char *preset = seq->preset(); - FuncExpr *preset_expr = nullptr; - if (preset) { - preset_expr = parseFunc(preset, "preset", line); - if (preset_expr && preset_expr->checkSize(size)) { - libWarn(1199, line, "%s %s bus width mismatch.", type, "preset"); - delete preset_expr; - preset_expr = nullptr; - } - } - cell_->makeSequential(size, is_register, clk_expr, data_expr, clr_expr, - preset_expr, seq->clrPresetVar1(), - seq->clrPresetVar2(), - seq->outPort(), seq->outInvPort()); - if (!is_register) - checkLatchEnableSense(clk_expr, line); - - // The expressions used in the sequentials are copied by bitSubExpr. - delete clk_expr; - delete data_expr; - delete clr_expr; - delete preset_expr; -} - -void -LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, - int line) -{ - LibertyPortSet enable_ports = enable_func->ports(); - for (LibertyPort *enable_port : enable_ports) { - TimingSense enable_sense = enable_func->portTimingSense(enable_port); - switch (enable_sense) { - case TimingSense::positive_unate: - case TimingSense::negative_unate: - break; - case TimingSense::non_unate: - libWarn(1200, line, "latch enable function is non-unate for port %s.", - enable_port->name()); - break; - case TimingSense::none: - case TimingSense::unknown: - libWarn(1201, line, "latch enable function is unknown for port %s.", - enable_port->name()); - break; - } - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::makeStatetable() -{ - if (statetable_) { - LibertyPortSeq input_ports; - for (const string &input : statetable_->inputPorts()) { - LibertyPort *port = cell_->findLibertyPort(input.c_str()); - if (port) - input_ports.push_back(port); - else - libWarn(1298, statetable_->line(), "statetable input port %s not found.", - input.c_str()); - } - LibertyPortSeq internal_ports; - for (const string &internal : statetable_->internalPorts()) { - LibertyPort *port = cell_->findLibertyPort(internal.c_str()); - if (port == nullptr) - port = makePort(cell_, internal.c_str()); - internal_ports.push_back(port); - } - cell_->makeStatetable(input_ports, internal_ports, statetable_->table()); - delete statetable_; - statetable_ = nullptr; - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::makeLeakagePowers() -{ - for (LeakagePowerGroup *power_group : leakage_powers_) { - LibertyPort *related_pg_pin = - cell_->findLibertyPort(power_group->relatedPgPin().c_str()); - cell_->makeLeakagePower(related_pg_pin, power_group->when(), power_group->power()); - delete power_group; - } - leakage_powers_.clear(); -} - -// Record a reference to a function that will be parsed at the end of -// the cell definition when all of the ports are defined. -void -LibertyReader::makeLibertyFunc(const char *expr, - LibertySetFunc set_func, - bool invert, - const char *attr_name, - LibertyStmt *stmt) -{ - LibertyFunc *func = new LibertyFunc(expr, set_func, - invert, attr_name, stmt->line()); - cell_funcs_.push_back(func); -} - -void -LibertyReader::parseCellFuncs() -{ - for (LibertyFunc *func : cell_funcs_) { - FuncExpr *expr = parseFunc(func->expr(), func->attrName(), func->line()); - if (func->invert() && expr) - expr = expr->invert(); - if (expr) - func->setFunc()(expr); - delete func; - } - cell_funcs_.clear(); -} - -void -LibertyReader::beginScaledCell(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) { - scaled_cell_owner_ = library_->findLibertyCell(name); - if (scaled_cell_owner_) { - const char *op_cond_name = group->secondName(); - if (op_cond_name) { - op_cond_ = library_->findOperatingConditions(op_cond_name); - if (op_cond_) { - debugPrint(debug_, "liberty", 1, "scaled cell %s %s", - name, op_cond_name); - cell_ = library_->makeScaledCell(name, filename_); - } - else - libWarn(1202, group, "operating conditions %s not found.", op_cond_name); - } - else - libWarn(1203, group, "scaled_cell missing operating condition."); - } - else - libWarn(1204, group, "scaled_cell cell %s has not been defined.", name); - } - else - libWarn(1205, group, "scaled_cell missing name."); -} - -void -LibertyReader::endScaledCell(LibertyGroup *group) -{ - if (cell_) { - makeCellSequentials(); - parseCellFuncs(); - finishPortGroups(); - cell_->finish(infer_latches_, report_, debug_); - checkScaledCell(group); - // Add scaled cell AFTER ports and timing arcs are defined. - scaled_cell_owner_->addScaledCell(op_cond_, cell_); - cell_ = nullptr; - scaled_cell_owner_ = nullptr; - op_cond_ = nullptr; - } -} - -// Minimal check that is not very specific about where the discrepancies are. -void -LibertyReader::checkScaledCell(LibertyGroup *group) -{ - if (equivCellPorts(cell_, scaled_cell_owner_)) { - if (!equivCellPorts(cell_, scaled_cell_owner_)) - libWarn(1206, group, "scaled_cell %s, %s ports do not match cell ports", - cell_->name(), - op_cond_->name()); - if (!equivCellFuncs(cell_, scaled_cell_owner_)) - libWarn(1206, group, - "scaled_cell %s, %s port functions do not match cell port functions.", - cell_->name(), - op_cond_->name()); - } - else - libWarn(1207, group, "scaled_cell ports do not match cell ports."); - if (!equivCellTimingArcSets(cell_, scaled_cell_owner_)) - libWarn(1208, group, "scaled_cell %s, %s timing does not match cell timing.", - cell_->name(), - op_cond_->name()); -} - -void -LibertyReader::makeTimingArcs(LibertyPort *to_port, - TimingGroup *timing) -{ - LibertyPort *related_out_port = nullptr; - const char *related_out_port_name = timing->relatedOutputPortName(); - if (related_out_port_name) - related_out_port = findPort(related_out_port_name); - int line = timing->line(); - PortDirection *to_port_dir = to_port->direction(); - // Checks should be more comprehensive (timing checks on inputs, etc). - TimingType type = timing->attrs()->timingType(); - if (type == TimingType::combinational && - to_port_dir->isInput()) - libWarn(1209, line, "combinational timing to an input port."); - if (timing->relatedPortNames()) { - for (const char *from_port_name : *timing->relatedPortNames()) { - PortNameBitIterator from_port_iter(cell_, from_port_name, this, line); - if (from_port_iter.hasNext()) { - debugPrint(debug_, "liberty", 2, " timing %s -> %s", - from_port_name, to_port->name()); - makeTimingArcs(from_port_name, from_port_iter, to_port, - related_out_port, timing); - } - } - } - else - makeTimingArcs(to_port, related_out_port, timing); -} - -void -TimingGroup::makeTimingModels(LibertyCell *cell, - LibertyReader *visitor) -{ - switch (cell->libertyLibrary()->delayModelType()) { - case DelayModelType::cmos_linear: - makeLinearModels(cell); - break; - case DelayModelType::table: - makeTableModels(cell, visitor); - break; - case DelayModelType::cmos_pwl: - case DelayModelType::cmos2: - case DelayModelType::polynomial: - case DelayModelType::dcm: - break; - } -} - -void -TimingGroup::makeLinearModels(LibertyCell *cell) -{ - LibertyLibrary *library = cell->libertyLibrary(); - for (auto rf : RiseFall::range()) { - int rf_index = rf->index(); - float intr = intrinsic_[rf_index]; - bool intr_exists = intrinsic_exists_[rf_index]; - if (!intr_exists) - library->defaultIntrinsic(rf, intr, intr_exists); - TimingModel *model = nullptr; - if (timingTypeIsCheck(attrs_->timingType())) { - if (intr_exists) - model = new CheckLinearModel(cell, intr); - } - else { - float res = resistance_[rf_index]; - bool res_exists = resistance_exists_[rf_index]; - if (!res_exists) - library->defaultPinResistance(rf, PortDirection::output(), - res, res_exists); - if (!res_exists) - res = 0.0F; - if (intr_exists) - model = new GateLinearModel(cell, intr, res); - } - attrs_->setModel(rf, model); - } -} - -void -TimingGroup::makeTableModels(LibertyCell *cell, - LibertyReader *reader) -{ - for (const RiseFall *rf : RiseFall::range()) { - int rf_index = rf->index(); - TableModel *delay = cell_[rf_index]; - TableModel *transition = transition_[rf_index]; - TableModel *constraint = constraint_[rf_index]; - if (delay || transition) { - attrs_->setModel(rf, new GateTableModel(cell, delay, delay_sigma_[rf_index], - transition, - slew_sigma_[rf_index], - receiver_model_, - output_waveforms_[rf_index])); - TimingType timing_type = attrs_->timingType(); - if (timing_type == TimingType::clear - || timing_type == TimingType::combinational - || timing_type == TimingType::combinational_fall - || timing_type == TimingType::combinational_rise - || timing_type == TimingType::falling_edge - || timing_type == TimingType::preset - || timing_type == TimingType::rising_edge - || timing_type == TimingType::three_state_disable - || timing_type == TimingType::three_state_disable_rise - || timing_type == TimingType::three_state_disable_fall - || timing_type == TimingType::three_state_enable - || timing_type == TimingType::three_state_enable_fall - || timing_type == TimingType::three_state_enable_rise) { - if (transition == nullptr) - reader->libWarn(1210, line_, "missing %s_transition.", rf->name()); - if (delay == nullptr) - reader->libWarn(1211, line_, "missing cell_%s.", rf->name()); - } - } - else if (constraint) - attrs_->setModel(rf, new CheckTableModel(cell, constraint, - constraint_sigma_[rf_index])); - cell_[rf_index] = nullptr; - transition_[rf_index] = nullptr; - constraint_[rf_index] = nullptr; - } -} - -void -LibertyReader::makeTimingArcs(const char *from_port_name, - PortNameBitIterator &from_port_iter, - LibertyPort *to_port, - LibertyPort *related_out_port, - TimingGroup *timing) -{ - if (from_port_iter.size() == 1 && !to_port->hasMembers()) { - // one -> one - 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."); - builder_.makeTimingArcs(cell_, from_port, to_port, related_out_port, - timing->attrs(), timing->line()); - } - } - else if (from_port_iter.size() > 1 && !to_port->hasMembers()) { - // bus -> one - 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."); - builder_.makeTimingArcs(cell_, from_port, to_port, related_out_port, - timing->attrs(), timing->line()); - } - } - else if (from_port_iter.size() == 1 && to_port->hasMembers()) { - // one -> bus - 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."); - LibertyPortMemberIterator bit_iter(to_port); - while (bit_iter.hasNext()) { - LibertyPort *to_port_bit = bit_iter.next(); - builder_.makeTimingArcs(cell_, from_port, to_port_bit, related_out_port, - timing->attrs(), timing->line()); - } + else if (from_port_iter.size() == 1 && to_port->hasMembers()) { + // one -> bus + 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."); + LibertyPortMemberIterator bit_iter(to_port); + while (bit_iter.hasNext()) { + LibertyPort *to_port_bit = bit_iter.next(); + builder_.makeTimingArcs(cell, from_port, to_port_bit, related_out_port, + timing_attrs, timing_line); + } } } else { // bus -> bus - if (timing->isOneToOne()) { + if (one_to_one) { int from_size = from_port_iter.size(); int to_size = to_port->size(); LibertyPortMemberIterator to_port_iter(to_port); // warn about different sizes if (from_size != to_size) - libWarn(1216, timing->line(), + libWarn(1216, timing_line, "timing port %s and related port %s are different sizes.", - from_port_name, + from_port_name.c_str(), to_port->name()); // align to/from iterators for one-to-one mapping while (from_size > to_size) { @@ -2527,23 +2594,22 @@ LibertyReader::makeTimingArcs(const char *from_port_name, 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."); - builder_.makeTimingArcs(cell_, from_port_bit, to_port_bit, - related_out_port, timing->attrs(), - timing->line()); + libWarn(1215, timing_line, "timing group from output port."); + builder_.makeTimingArcs(cell, from_port_bit, to_port_bit, + related_out_port, timing_attrs, + timing_line); } } else { + // cross product while (from_port_iter.hasNext()) { LibertyPort *from_port_bit = from_port_iter.next(); - if (from_port_bit->direction()->isOutput()) - libWarn(1217, timing->line(), "timing group from output port."); - LibertyPortMemberIterator to_iter(to_port); - while (to_iter.hasNext()) { - LibertyPort *to_port_bit = to_iter.next(); - builder_.makeTimingArcs(cell_, from_port_bit, to_port_bit, - related_out_port, timing->attrs(), - timing->line()); + LibertyPortMemberIterator to_port_iter(to_port); + while (to_port_iter.hasNext()) { + LibertyPort *to_port_bit = to_port_iter.next(); + builder_.makeTimingArcs(cell, from_port_bit, to_port_bit, + related_out_port, timing_attrs, + timing_line); } } } @@ -2551,3553 +2617,1021 @@ LibertyReader::makeTimingArcs(const char *from_port_name, } void -LibertyReader::makeTimingArcs(LibertyPort *to_port, +LibertyReader::makeTimingArcs(LibertyCell *cell, + LibertyPort *to_port, LibertyPort *related_out_port, - TimingGroup *timing) + TimingArcAttrsPtr timing_attrs, + int timing_line) { if (to_port->hasMembers()) { LibertyPortMemberIterator bit_iter(to_port); while (bit_iter.hasNext()) { LibertyPort *to_port_bit = bit_iter.next(); - builder_.makeTimingArcs(cell_, nullptr, to_port_bit, - related_out_port, timing->attrs(), - timing->line()); + builder_.makeTimingArcs(cell, nullptr, to_port_bit, + related_out_port, timing_attrs, + timing_line); } } else - builder_.makeTimingArcs(cell_, nullptr, to_port, - related_out_port, timing->attrs(), - timing->line()); + builder_.makeTimingArcs(cell, nullptr, to_port, + related_out_port, timing_attrs, + timing_line); } //////////////////////////////////////////////////////////////// -// Group that encloses receiver_capacitance1/2 etc groups. -void -LibertyReader::beginReceiverCapacitance(LibertyGroup *) -{ - receiver_model_ = make_shared(); -} - -void -LibertyReader::endReceiverCapacitance(LibertyGroup *) -{ - if (ports_) { - for (LibertyPort *port : *ports_) - port->setReceiverModel(receiver_model_); - } - receiver_model_ = nullptr; -} - -// For receiver_capacitance groups with mulitiple segments this -// overrides the index passed in beginReceiverCapacitance1Rise/Fall. void -LibertyReader::visitSegement(LibertyAttr *attr) +LibertyReader::readLeagageGrouops(LibertyCell *cell, + const LibertyGroup *cell_group) { - if (receiver_model_) { - int segment; + for (const LibertyGroup *leak_group : cell_group->findSubgroups("leakage_power")) { + FuncExpr *when = readFuncExpr(cell, leak_group, "when"); + float power; bool exists; - getAttrInt(attr, segment, exists); - if (exists) - index_ = segment; - } -} - -void -LibertyReader::beginReceiverCapacitance1Rise(LibertyGroup *group) -{ - beginReceiverCapacitance(group, 0, RiseFall::rise()); -} - -void -LibertyReader::beginReceiverCapacitance1Fall(LibertyGroup *group) -{ - beginReceiverCapacitance(group, 0, RiseFall::fall()); -} - -void -LibertyReader::beginReceiverCapacitance2Rise(LibertyGroup *group) -{ - beginReceiverCapacitance(group, 1, RiseFall::rise()); -} - -void -LibertyReader::beginReceiverCapacitance2Fall(LibertyGroup *group) -{ - beginReceiverCapacitance(group, 1, RiseFall::fall()); -} - -void -LibertyReader::beginReceiverCapacitance(LibertyGroup *group, - int index, - const RiseFall *rf) -{ - if (timing_ || ports_) { - beginTableModel(group, TableTemplateType::delay, rf, 1.0, - ScaleFactorType::pin_cap); - index_ = index; - } - else - libWarn(1218, group, "receiver_capacitance group not in timing or pin group."); -} - -void -LibertyReader::endReceiverCapacitanceRiseFall(LibertyGroup *group) -{ - if (table_) { - if (ReceiverModel::checkAxes(table_)) { - if (receiver_model_ == nullptr) { - receiver_model_ = make_shared(); - if (timing_) - timing_->setReceiverModel(receiver_model_); - } - receiver_model_->setCapacitanceModel(TableModel(table_, tbl_template_, - scale_factor_type_, rf_), - index_, rf_); - } - else - libWarn(1219, group, "unsupported model axis."); - endTableModel(); - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginOutputCurrentRise(LibertyGroup *group) -{ - beginOutputCurrent(RiseFall::rise(), group); -} - -void -LibertyReader::beginOutputCurrentFall(LibertyGroup *group) -{ - beginOutputCurrent(RiseFall::fall(), group); -} - -void -LibertyReader::beginOutputCurrent(const RiseFall *rf, - LibertyGroup *group) -{ - if (timing_) { - rf_ = rf; - output_currents_.clear(); - } - else - libWarn(1220, group, "output_current_%s group not in timing group.", - rf->name()); -} - -void -LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) -{ - if (timing_) { - std::set slew_set, cap_set; - FloatSeq slew_values; - FloatSeq cap_values; - for (OutputWaveform *waveform : output_currents_) { - float slew = waveform->slew(); - if (!slew_set.contains(slew)) { - slew_set.insert(slew); - slew_values.push_back(slew); - } - float cap = waveform->cap(); - if (!cap_set.contains(cap)) { - cap_set.insert(cap); - cap_values.push_back(cap); - } - } - sort(slew_values, std::less()); - sort(cap_values, std::less()); - size_t slew_size = slew_values.size(); - size_t cap_size = cap_values.size(); - TableAxisPtr slew_axis=make_shared(TableAxisVariable::input_net_transition, - std::move(slew_values)); - TableAxisPtr cap_axis = - make_shared(TableAxisVariable::total_output_net_capacitance, - std::move(cap_values)); - FloatSeq ref_times(slew_size); - Table1Seq current_waveforms(slew_size * cap_size); - for (OutputWaveform *waveform : output_currents_) { - size_t slew_index, cap_index; - bool slew_exists, cap_exists; - slew_axis->findAxisIndex(waveform->slew(), slew_index, slew_exists); - cap_axis->findAxisIndex(waveform->cap(), cap_index, cap_exists); - if (slew_exists && cap_exists) { - size_t index = slew_index * cap_axis->size() + cap_index; - current_waveforms[index] = waveform->stealCurrents(); - ref_times[slew_index] = waveform->referenceTime(); - } - else - libWarn(1221, group, "output current waveform %.2e %.2e not found.", - waveform->slew(), - waveform->cap()); - } - Table ref_time_tbl(std::move(ref_times), slew_axis); - OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, rf_, - current_waveforms, - std::move(ref_time_tbl)); - timing_->setOutputWaveforms(rf_, output_current); - deleteContents(output_currents_); - } -} - -void -LibertyReader::beginVector(LibertyGroup *group) -{ - if (timing_ && !in_ccsn_) { - beginTable(group, TableTemplateType::output_current, current_scale_); - scale_factor_type_ = ScaleFactorType::unknown; - reference_time_exists_ = false; - if (tbl_template_ && !OutputWaveforms::checkAxes(tbl_template_)) - libWarn(1222, group, "unsupported model axis."); - } -} - -void -LibertyReader::visitReferenceTime(LibertyAttr *attr) -{ - getAttrFloat(attr, reference_time_, reference_time_exists_); - if (reference_time_exists_) - reference_time_ *= time_scale_; -} - -void -LibertyReader::endVector(LibertyGroup *group) -{ - if (timing_ && tbl_template_) { - TableAxisPtr slew_axis, cap_axis; - // Canonicalize axis order. - if (tbl_template_->axis1()->variable() == TableAxisVariable::input_net_transition) { - slew_axis = axis_[0]; - cap_axis = axis_[1]; - } - else { - slew_axis = axis_[1]; - cap_axis = axis_[0]; - } - - if (slew_axis->size() == 1 && cap_axis->size() == 1) { - // Convert 1x1xN Table (order 3) to 1D Table. - float slew = slew_axis->axisValue(0); - float cap = cap_axis->axisValue(0); - Table *table_ptr = table_.get(); - FloatTable *values3 = table_ptr->values3(); - FloatSeq row = std::move((*values3)[0]); - values3->erase(values3->begin()); - Table *table1 = new Table(std::move(row), axis_[2]); - OutputWaveform *waveform = new OutputWaveform(slew, cap, table1, reference_time_); - output_currents_.push_back(waveform); + leak_group->findAttrFloat("value", power, exists); + if (exists) { + LibertyPort *related_pg_port = findLibertyPort(cell, leak_group, "related_pg_pin"); + cell->makeLeakagePower(related_pg_port, when, power * power_scale_); } else - libWarn(1223,group->line(), "vector index_1 and index_2 must have exactly one value."); - if (!reference_time_exists_) - libWarn(1224, group->line(), "vector reference_time not found."); - reference_time_exists_ = false; - tbl_template_ = nullptr; - } -} - -/////////////////////////////////////////////////////////////// - -void -LibertyReader::beginNormalizedDriverWaveform(LibertyGroup *group) -{ - beginTable(group, TableTemplateType::delay, time_scale_); - driver_waveform_name_.clear(); -} - -void -LibertyReader::visitDriverWaveformName(LibertyAttr *attr) -{ - driver_waveform_name_ = getAttrString(attr); -} - -void -LibertyReader::endNormalizedDriverWaveform(LibertyGroup *group) -{ - if (table_) { - if (table_->axis1()->variable() == TableAxisVariable::input_net_transition) { - if (table_->axis2()->variable() == TableAxisVariable::normalized_voltage) { - // Null driver_waveform_name_ means it is the default unnamed waveform. - library_->makeDriverWaveform(driver_waveform_name_, table_); - - } - else - libWarn(1225, group, "normalized_driver_waveform variable_2 must be normalized_voltage"); - } - else - libWarn(1226, group, "normalized_driver_waveform variable_1 must be input_net_transition"); - } - endTableModel(); -} - -void -LibertyReader::visitDriverWaveformRise(LibertyAttr *attr) -{ - visitDriverWaveformRiseFall(attr, RiseFall::rise()); -} - -void -LibertyReader::visitDriverWaveformFall(LibertyAttr *attr) -{ - visitDriverWaveformRiseFall(attr, RiseFall::fall()); -} - -void -LibertyReader::visitDriverWaveformRiseFall(LibertyAttr *attr, - const RiseFall *rf) -{ - if (ports_) { - const char *driver_waveform_name = getAttrString(attr); - DriverWaveform *driver_waveform = library_->findDriverWaveform(driver_waveform_name); - if (driver_waveform) { - for (LibertyPort *port : *ports_) - port->setDriverWaveform(driver_waveform, rf); - } - } -} - -/////////////////////////////////////////////////////////////// - -void -LibertyReader::makeInternalPowers(LibertyPort *port, - InternalPowerGroup *power_group) -{ - int line = power_group->line(); - const std::string &related_pg_pin_name = power_group->relatedPgPin(); - LibertyPort *related_pg_pin = cell_->findLibertyPort(related_pg_pin_name.c_str()); - - StringSeq *related_port_names = power_group->relatedPortNames(); - if (related_port_names) { - for (const char *related_port_name : *related_port_names) { - PortNameBitIterator related_port_iter(cell_, related_port_name, this, line); - if (related_port_iter.hasNext()) { - debugPrint(debug_, "liberty", 2, " power %s -> %s", - related_port_name, port->name()); - makeInternalPowers(port, related_port_name, related_port_iter, - related_pg_pin, power_group); - } - } - } - else { - if (port->hasMembers()) { - LibertyPortMemberIterator bit_iter(port); - while (bit_iter.hasNext()) { - LibertyPort *port_bit = bit_iter.next(); - cell_->makeInternalPower(port_bit, nullptr, related_pg_pin, - power_group->when(), power_group->models()); - } - } - else - cell_->makeInternalPower(port, nullptr, related_pg_pin, power_group->when(), - power_group->models()); - } -} - -void -LibertyReader::makeInternalPowers(LibertyPort *port, - const char *related_port_name, - PortNameBitIterator &related_port_iter, - LibertyPort *related_pg_pin, - InternalPowerGroup *power_group) -{ - const auto &when = power_group->when(); - InternalPowerModels &models = power_group->models(); - if (related_port_iter.size() == 1 && !port->hasMembers()) { - // one -> one - if (related_port_iter.hasNext()) { - LibertyPort *related_port = related_port_iter.next(); - cell_->makeInternalPower(port, related_port, related_pg_pin, when, models); - } - } - else if (related_port_iter.size() > 1 && !port->hasMembers()) { - // bus -> one - while (related_port_iter.hasNext()) { - LibertyPort *related_port = related_port_iter.next(); - cell_->makeInternalPower(port, related_port, related_pg_pin, when, models); - } - } - else if (related_port_iter.size() == 1 && port->hasMembers()) { - // one -> bus - if (related_port_iter.hasNext()) { - LibertyPort *related_port = related_port_iter.next(); - LibertyPortMemberIterator bit_iter(port); - while (bit_iter.hasNext()) { - LibertyPort *port_bit = bit_iter.next(); - cell_->makeInternalPower(port_bit, related_port, related_pg_pin, when, models); - } - } - } - else { - // bus -> bus - if (power_group->isOneToOne()) { - if (static_cast(related_port_iter.size()) == port->size()) { - LibertyPortMemberIterator to_iter(port); - while (related_port_iter.hasNext() && to_iter.hasNext()) { - LibertyPort *related_port_bit = related_port_iter.next(); - LibertyPort *port_bit = to_iter.next(); - cell_->makeInternalPower(port_bit, related_port_bit, related_pg_pin, - when, models); - } - } - else - libWarn(1227, power_group->line(), - "internal_power port %s and related port %s are different sizes.", - related_port_name, - port->name()); - } - else { - while (related_port_iter.hasNext()) { - LibertyPort *related_port_bit = related_port_iter.next(); - LibertyPortMemberIterator to_iter(port); - while (to_iter.hasNext()) { - LibertyPort *port_bit = to_iter.next(); - cell_->makeInternalPower(port_bit, related_port_bit, related_pg_pin, - when, models); - } - } - } - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::visitArea(LibertyAttr *attr) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - cell_->setArea(value); - } - if (wireload_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - wireload_->setArea(value); - } -} - -void -LibertyReader::visitDontUse(LibertyAttr *attr) -{ - if (cell_) { - bool dont_use, exists; - getAttrBool(attr, dont_use, exists); - if (exists) - cell_->setDontUse(dont_use); - } -} - -void -LibertyReader::visitIsMacro(LibertyAttr *attr) -{ - if (cell_) { - bool is_macro, exists; - getAttrBool(attr, is_macro, exists); - if (exists) - cell_->setIsMacro(is_macro); - } -} - -void -LibertyReader::visitIsMemory(LibertyAttr *attr) -{ - if (cell_) { - bool is_memory, exists; - getAttrBool(attr, is_memory, exists); - if (exists) - cell_->setIsMemory(is_memory); - } -} - -void -LibertyReader::visitIsPadCell(LibertyAttr *attr) -{ - if (cell_) { - bool pad_cell, exists; - getAttrBool(attr, pad_cell, exists); - if (exists) - cell_->setIsPad(pad_cell); - } -} - -void -LibertyReader::visitIsClockCell(LibertyAttr *attr) -{ - if (cell_) { - bool is_clock_cell, exists; - getAttrBool(attr, is_clock_cell, exists); - if (exists) - cell_->setIsClockCell(is_clock_cell); - } -} - -void -LibertyReader::visitIsLevelShifter(LibertyAttr *attr) -{ - if (cell_) { - bool is_level_shifter, exists; - getAttrBool(attr, is_level_shifter, exists); - if (exists) - cell_->setIsLevelShifter(is_level_shifter); - } -} - -void -LibertyReader::visitLevelShifterType(LibertyAttr *attr) -{ - if (cell_) { - const char *level_shifter_type = getAttrString(attr); - if (stringEq(level_shifter_type, "HL")) - cell_->setLevelShifterType(LevelShifterType::HL); - else if (stringEq(level_shifter_type, "LH")) - cell_->setLevelShifterType(LevelShifterType::LH); - else if (stringEq(level_shifter_type, "HL_LH")) - cell_->setLevelShifterType(LevelShifterType::HL_LH); - else - libWarn(1228, attr, "level_shifter_type must be HL, LH, or HL_LH"); - } -} - -void -LibertyReader::visitIsIsolationCell(LibertyAttr *attr) -{ - if (cell_) { - bool is_isolation_cell, exists; - getAttrBool(attr, is_isolation_cell, exists); - if (exists) - cell_->setIsIsolationCell(is_isolation_cell); - } -} - -void -LibertyReader::visitAlwaysOn(LibertyAttr *attr) -{ - if (cell_) { - bool always_on, exists; - getAttrBool(attr, always_on, exists); - if (exists) - cell_->setAlwaysOn(always_on); - } -} - -void -LibertyReader::visitSwitchCellType(LibertyAttr *attr) -{ - if (cell_) { - const char *switch_cell_type = getAttrString(attr); - if (stringEq(switch_cell_type, "coarse_grain")) - cell_->setSwitchCellType(SwitchCellType::coarse_grain); - else if (stringEq(switch_cell_type, "fine_grain")) - cell_->setSwitchCellType(SwitchCellType::fine_grain); - else - libWarn(1229, attr, "switch_cell_type must be coarse_grain or fine_grain"); - } -} - -void -LibertyReader::visitInterfaceTiming(LibertyAttr *attr) -{ - if (cell_) { - bool value, exists; - getAttrBool(attr, value, exists); - if (exists) - cell_->setInterfaceTiming(value); - } -} - -void -LibertyReader::visitScalingFactors(LibertyAttr *attr) -{ - if (cell_) { - const char *scale_factors_name = getAttrString(attr); - ScaleFactors *scales = library_->findScaleFactors(scale_factors_name); - if (scales) - cell_->setScaleFactors(scales); - else - libWarn(1230, attr, "scaling_factors %s not found.", scale_factors_name); - } -} - -void -LibertyReader::visitClockGatingIntegratedCell(LibertyAttr *attr) -{ - if (cell_) { - const char *clock_gate_type = getAttrString(attr); - if (clock_gate_type) { - if (stringBeginEqual(clock_gate_type, "latch_posedge")) - cell_->setClockGateType(ClockGateType::latch_posedge); - else if (stringBeginEqual(clock_gate_type, "latch_negedge")) - cell_->setClockGateType(ClockGateType::latch_negedge); - else - cell_->setClockGateType(ClockGateType::other); - } - } -} - -void -LibertyReader::visitCellFootprint(LibertyAttr *attr) -{ - if (cell_) { - const char *footprint = getAttrString(attr); - if (footprint) - cell_->setFootprint(footprint); - } -} - -void -LibertyReader::visitCellUserFunctionClass(LibertyAttr *attr) -{ - if (cell_) { - const char *user_function_class = getAttrString(attr); - if (user_function_class) - cell_->setUserFunctionClass(user_function_class); - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginPin(LibertyGroup *group) -{ - if (cell_) { - if (in_bus_) { - saved_ports_ = ports_; - saved_port_group_ = port_group_; - ports_ = new LibertyPortSeq; - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const std::string &port_name = param->stringValue(); - debugPrint(debug_, "liberty", 1, " port %s", port_name.c_str()); - PortNameBitIterator port_iter(cell_, port_name.c_str(), this, group->line()); - while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); - ports_->push_back(port); - } - } - else - libWarn(1231, group, "pin name is not a string."); - } - } - else if (in_bundle_) { - saved_ports_ = ports_; - saved_port_group_ = port_group_; - ports_ = new LibertyPortSeq; - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *name = param->stringValue().c_str(); - debugPrint(debug_, "liberty", 1, " port %s", name); - LibertyPort *port = findPort(name); - if (port == nullptr) - port = makePort(cell_, name); - ports_->push_back(port); - } - else - libWarn(1232, group, "pin name is not a string."); - } - } - else { - ports_ = new LibertyPortSeq; - // Multiple port names can share group def. - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const char *name = param->stringValue().c_str(); - debugPrint(debug_, "liberty", 1, " port %s", name); - LibertyPort *port = makePort(cell_, name); - ports_->push_back(port); - } - else - libWarn(1233, group, "pin name is not a string."); - } - } - port_group_ = new PortGroup(ports_, group->line()); - cell_port_groups_.push_back(port_group_); - } -} - -LibertyPort * -LibertyReader::makePort(LibertyCell *cell, - const char *port_name) -{ - string sta_name = portLibertyToSta(port_name); - return builder_.makePort(cell, sta_name.c_str()); -} - -LibertyPort * -LibertyReader::makeBusPort(LibertyCell *cell, - const char *bus_name, - int from_index, - int to_index, - BusDcl *bus_dcl) -{ - string sta_name = portLibertyToSta(bus_name); - return builder_.makeBusPort(cell, bus_name, from_index, to_index, bus_dcl); -} - -void -LibertyReader::endPin(LibertyGroup *) -{ - if (cell_) { - endPorts(); - if (in_bus_ || in_bundle_) { - ports_ = saved_ports_; - port_group_ = saved_port_group_; - } - } -} - -void -LibertyReader::endPorts() -{ - // Capacitances default based on direction so wait until the end - // of the pin group to set them. - if (ports_) { - for (LibertyPort *port : *ports_) { - if (in_bus_ || in_bundle_) { - // Do not clobber member port capacitances by setting the capacitance - // on a bus or bundle. - LibertyPortMemberIterator member_iter(port); - while (member_iter.hasNext()) { - LibertyPort *member = member_iter.next(); - setPortCapDefault(member); - } - } - else - setPortCapDefault(port); - } - ports_ = nullptr; - port_group_ = nullptr; - } -} - -void -LibertyReader::setPortCapDefault(LibertyPort *port) -{ - for (auto min_max : MinMax::range()) { - for (auto tr : RiseFall::range()) { - float cap; - bool exists; - port->capacitance(tr, min_max, cap, exists); - if (!exists) - port->setCapacitance(tr, min_max, defaultCap(port)); - } - } -} - -void -LibertyReader::beginBus(LibertyGroup *group) -{ - if (cell_) { - beginBusOrBundle(group); - in_bus_ = true; - } -} - -void -LibertyReader::endBus(LibertyGroup *group) -{ - if (cell_) { - if (ports_->empty()) - libWarn(1234, group, "bus %s bus_type not found.", group->firstName()); - endBusOrBundle(); - in_bus_ = false; - } -} - -void -LibertyReader::beginBusOrBundle(LibertyGroup *group) -{ - // Multiple port names can share group def. - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const string &name = param->stringValue(); - bus_names_.push_back(stringCopy(name.c_str())); - } - } - ports_ = new LibertyPortSeq; - port_group_ = new PortGroup(ports_, group->line()); - cell_port_groups_.push_back(port_group_); -} - -void -LibertyReader::endBusOrBundle() -{ - endPorts(); - deleteContents(&bus_names_); - bus_names_.clear(); - ports_ = nullptr; - port_group_ = nullptr; -} - -// Bus port are not made until the bus_type is specified. -void -LibertyReader::visitBusType(LibertyAttr *attr) -{ - if (cell_) { - const char *bus_type = getAttrString(attr); - if (bus_type) { - // Look for bus dcl local to cell first. - BusDcl *bus_dcl = cell_->findBusDcl(bus_type); - if (bus_dcl == nullptr) - bus_dcl = library_->findBusDcl(bus_type); - if (bus_dcl) { - for (const char *name : bus_names_) { - debugPrint(debug_, "liberty", 1, " bus %s", name); - LibertyPort *port = makeBusPort(cell_, name, bus_dcl->from(), - bus_dcl->to(), bus_dcl); - ports_->push_back(port); - } - } - else - libWarn(1235, attr, "bus_type %s not found.", bus_type); - } - else - libWarn(1236, attr, "bus_type is not a string."); - } -} - -void -LibertyReader::beginBundle(LibertyGroup *group) -{ - if (cell_) { - beginBusOrBundle(group); - in_bundle_ = true; - } -} - -void -LibertyReader::endBundle(LibertyGroup *group) -{ - if (cell_) { - if (ports_ && ports_->empty()) - libWarn(1237, group, "bundle %s member not found.", group->firstName()); - endBusOrBundle(); - in_bundle_ = false; - } -} - -void -LibertyReader::visitMembers(LibertyAttr *attr) -{ - if (cell_) { - if (attr->isComplexAttr()) { - for (const char *name : bus_names_) { - debugPrint(debug_, "liberty", 1, " bundle %s", name); - ConcretePortSeq *members = new ConcretePortSeq; - for (LibertyAttrValue *value : *attr->values()) { - if (value->isString()) { - const char *port_name = value->stringValue().c_str(); - LibertyPort *port = findPort(port_name); - if (port == nullptr) - port = makePort(cell_, port_name); - members->push_back(port); - } - else - libWarn(1238, attr, "member is not a string."); - } - LibertyPort *port = builder_.makeBundlePort(cell_, name, members); - ports_->push_back(port); - } - } - else - libWarn(1239, attr,"members attribute is missing values."); - } -} - -LibertyPort * -LibertyReader::findPort(const char *port_name) -{ - return findPort(cell_, port_name); -} - -// Also used by LibExprParser::makeFuncExprPort. -LibertyPort * -libertyReaderFindPort(LibertyCell *cell, - const char *port_name) -{ - LibertyPort *port = cell->findLibertyPort(port_name); - if (port == nullptr) { - const LibertyLibrary *library = cell->libertyLibrary(); - char brkt_left = library->busBrktLeft(); - char brkt_right = library->busBrktRight(); - const char escape = '\\'; - // Pins at top level with bus names have escaped brackets. - string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right, escape); - port = cell->findLibertyPort(escaped_port_name.c_str()); - } - return port; -} - -LibertyPort * -LibertyReader::findPort(LibertyCell *cell, - const char *port_name) -{ - return libertyReaderFindPort(cell, port_name); -} - -void -LibertyReader::visitDirection(LibertyAttr *attr) -{ - if (ports_) { - const char *dir = getAttrString(attr); - if (dir) { - PortDirection *port_dir = PortDirection::unknown(); - if (stringEq(dir, "input")) - port_dir = PortDirection::input(); - else if (stringEq(dir, "output")) - port_dir = PortDirection::output(); - else if (stringEq(dir, "inout")) - port_dir = PortDirection::bidirect(); - else if (stringEq(dir, "internal")) - port_dir = PortDirection::internal(); - else - libWarn(1240, attr, "unknown port direction."); - - for (LibertyPort *port : *ports_) { - // Tristate enable function sets direction to tristate; don't - // clobber it. - if (!port->direction()->isTristate()) - port->setDirection(port_dir); - } - } - } -} - -void -LibertyReader::visitFunction(LibertyAttr *attr) -{ - if (ports_) { - const char *func = getAttrString(attr); - if (func) { - for (LibertyPort *port : *ports_) - makeLibertyFunc(func, - [port] (FuncExpr *expr) { port->setFunction(expr); }, - false, "function", attr); - } - } -} - -void -LibertyReader::visitThreeState(LibertyAttr *attr) -{ - if (ports_) { - const char *three_state = getAttrString(attr); - if (three_state) { - for (LibertyPort *port : *ports_) - makeLibertyFunc(three_state, - [port] (FuncExpr *expr) { port->setTristateEnable(expr); }, - true, "three_state", attr); - } - } -} - -void -LibertyReader::visitPorts(std::function func) -{ - for (LibertyPort *port : *ports_) { - func(port); - LibertyPortMemberIterator member_iter(port); - while (member_iter.hasNext()) { - LibertyPort *member = member_iter.next(); - func(member); - } - } -} - -void -LibertyReader::visitClock(LibertyAttr *attr) -{ - if (ports_) { - bool is_clk, exists; - getAttrBool(attr, is_clk, exists); - if (exists) { - for (LibertyPort *port : *ports_) - port->setIsClock(is_clk); - } - } -} - -void -LibertyReader::visitIsPad(LibertyAttr *attr) -{ - if (ports_) { - bool is_pad, exists; - getAttrBool(attr, is_pad, exists); - if (exists) { - for (LibertyPort *port : *ports_) - port->setIsPad(is_pad); - } - } -} - -void -LibertyReader::visitCapacitance(LibertyAttr *attr) -{ - if (ports_) { - float cap; - bool exists; - getAttrFloat(attr, cap, exists); - if (exists) { - cap *= cap_scale_; - for (LibertyPort *port : *ports_) - port->setCapacitance(cap); - } - } - if (wireload_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - wireload_->setCapacitance(value * cap_scale_); - } -} - -void -LibertyReader::visitRiseCap(LibertyAttr *attr) -{ - if (ports_) { - float cap; - bool exists; - getAttrFloat(attr, cap, exists); - if (exists) { - cap *= cap_scale_; - for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::rise(), MinMax::min(), cap); - port->setCapacitance(RiseFall::rise(), MinMax::max(), cap); - } - } - } -} - -void -LibertyReader::visitFallCap(LibertyAttr *attr) -{ - if (ports_) { - float cap; - bool exists; - getAttrFloat(attr, cap, exists); - if (exists) { - cap *= cap_scale_; - for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::fall(), MinMax::min(), cap); - port->setCapacitance(RiseFall::fall(), MinMax::max(), cap); - } - } - } -} - -void -LibertyReader::visitRiseCapRange(LibertyAttr *attr) -{ - if (ports_) { - bool exists; - float min, max; - getAttrFloat2(attr, min, max, exists); - if (exists) { - min *= cap_scale_; - max *= cap_scale_; - for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::rise(), MinMax::min(), min); - port->setCapacitance(RiseFall::rise(), MinMax::max(), max); - } - } - } -} - -void -LibertyReader::visitFallCapRange(LibertyAttr *attr) -{ - if (ports_) { - bool exists; - float min, max; - getAttrFloat2(attr, min, max, exists); - if (exists) { - min *= cap_scale_; - max *= cap_scale_; - for (LibertyPort *port : *ports_) { - port->setCapacitance(RiseFall::fall(), MinMax::min(), min); - port->setCapacitance(RiseFall::fall(), MinMax::max(), max); - } - } - } -} - -float -LibertyReader::defaultCap(LibertyPort *port) -{ - PortDirection *dir = port->direction(); - float cap = 0.0; - if (dir->isInput()) - cap = library_->defaultInputPinCap(); - else if (dir->isOutput() - || dir->isTristate()) - cap = library_->defaultOutputPinCap(); - else if (dir->isBidirect()) - cap = library_->defaultBidirectPinCap(); - return cap; -} - -void -LibertyReader::visitFanoutLoad(LibertyAttr *attr) -{ - if (ports_) { - float fanout; - bool exists; - getAttrFloat(attr, fanout, exists); - if (exists) { - visitPorts([&] (LibertyPort *port) { - port->setFanoutLoad(fanout); - }); - } - } -} - -void -LibertyReader::visitMaxFanout(LibertyAttr *attr) -{ - visitFanout(attr, MinMax::max()); -} - -void -LibertyReader::visitMinFanout(LibertyAttr *attr) -{ - visitFanout(attr, MinMax::min()); -} - -void -LibertyReader::visitFanout(LibertyAttr *attr, - const MinMax *min_max) -{ - if (ports_) { - float fanout; - bool exists; - getAttrFloat(attr, fanout, exists); - if (exists) { - visitPorts([&] (LibertyPort *port) { - port->setFanoutLimit(fanout, min_max); - }); - } - } -} - -void -LibertyReader::visitMaxTransition(LibertyAttr *attr) -{ - visitMinMaxTransition(attr, MinMax::max()); -} - -void -LibertyReader::visitMinTransition(LibertyAttr *attr) -{ - visitMinMaxTransition(attr, MinMax::min()); -} - -void -LibertyReader::visitMinMaxTransition(LibertyAttr *attr, - const MinMax *min_max) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (min_max == MinMax::max() && value == 0.0) - libWarn(1241, attr, "max_transition is 0.0."); - value *= time_scale_; - visitPorts([&] (LibertyPort *port) { - port->setSlewLimit(value, min_max); - }); - } - } -} - -void -LibertyReader::visitMaxCapacitance(LibertyAttr *attr) -{ - visitMinMaxCapacitance(attr, MinMax::max()); -} - -void -LibertyReader::visitMinCapacitance(LibertyAttr *attr) -{ - visitMinMaxCapacitance(attr, MinMax::min()); -} - -void -LibertyReader::visitMinMaxCapacitance(LibertyAttr *attr, - const MinMax *min_max) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - value *= cap_scale_; - visitPorts([&] (LibertyPort *port) { - port->setCapacitanceLimit(value, min_max); - }); - } - } -} - -void -LibertyReader::visitMinPeriod(LibertyAttr *attr) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - for (LibertyPort *port : *ports_) - port->setMinPeriod(value * time_scale_); - } - } -} - -void -LibertyReader::visitMinPulseWidthLow(LibertyAttr *attr) -{ - visitMinPulseWidth(attr, RiseFall::fall()); -} - -void -LibertyReader::visitMinPulseWidthHigh(LibertyAttr *attr) -{ - visitMinPulseWidth(attr, RiseFall::rise()); -} - -void -LibertyReader::visitMinPulseWidth(LibertyAttr *attr, - const RiseFall *rf) -{ - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - value *= time_scale_; - for (LibertyPort *port : *ports_) - port->setMinPulseWidth(rf, value); - } - } -} - -void -LibertyReader::visitPulseClock(LibertyAttr *attr) -{ - if (cell_) { - const char *pulse_clk = getAttrString(attr); - if (pulse_clk) { - const RiseFall *trigger = nullptr; - const RiseFall *sense = nullptr; - if (stringEq(pulse_clk, "rise_triggered_high_pulse")) { - trigger = RiseFall::rise(); - sense = RiseFall::rise(); - } - else if (stringEq(pulse_clk, "rise_triggered_low_pulse")) { - trigger = RiseFall::rise(); - sense = RiseFall::fall(); - } - else if (stringEq(pulse_clk, "fall_triggered_high_pulse")) { - trigger = RiseFall::fall(); - sense = RiseFall::rise(); - } - else if (stringEq(pulse_clk, "fall_triggered_low_pulse")) { - trigger = RiseFall::fall(); - sense = RiseFall::fall(); - } - else - libWarn(1242,attr, "pulse_latch unknown pulse type."); - if (trigger) { - for (LibertyPort *port : *ports_) - port->setPulseClk(trigger, sense); - } - } - } -} - -void -LibertyReader::visitClockGateClockPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsClockGateClock); -} - -void -LibertyReader::visitClockGateEnablePin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsClockGateEnable); -} - -void -LibertyReader::visitClockGateOutPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsClockGateOut); -} - -void -LibertyReader::visitIsPllFeedbackPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsPllFeedback); -} - -void -LibertyReader::visitSignalType(LibertyAttr *attr) -{ - if (test_cell_ && ports_) { - const char *type = getAttrString(attr); - if (type) { - ScanSignalType signal_type = ScanSignalType::none; - if (stringEq(type, "test_scan_enable")) - signal_type = ScanSignalType::enable; - else if (stringEq(type, "test_scan_enable_inverted")) - signal_type = ScanSignalType::enable_inverted; - else if (stringEq(type, "test_scan_clock")) - signal_type = ScanSignalType::clock; - else if (stringEq(type, "test_scan_clock_a")) - signal_type = ScanSignalType::clock_a; - else if (stringEq(type, "test_scan_clock_b")) - signal_type = ScanSignalType::clock_b; - else if (stringEq(type, "test_scan_in")) - signal_type = ScanSignalType::input; - else if (stringEq(type, "test_scan_in_inverted")) - signal_type = ScanSignalType::input_inverted; - else if (stringEq(type, "test_scan_out")) - signal_type = ScanSignalType::output; - else if (stringEq(type, "test_scan_out_inverted")) - signal_type = ScanSignalType::output_inverted; - else { - libWarn(1299, attr, "unknown signal_type %s.", type); - return; - } - for (LibertyPort *port : *ports_) - port->setScanSignalType(signal_type); - } - } -} - -void -LibertyReader::visitIsolationCellDataPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsolationCellData); -} - -void -LibertyReader::visitIsolationCellEnablePin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsolationCellEnable); -} - -void -LibertyReader::visitLevelShifterDataPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setLevelShifterData); -} - -void -LibertyReader::visitSwitchPin(LibertyAttr *attr) -{ - visitPortBoolAttr(attr, &LibertyPort::setIsSwitch); -} - -void -LibertyReader::visitPortBoolAttr(LibertyAttr *attr, - LibertyPortBoolSetter setter) -{ - if (cell_) { - bool value, exists; - getAttrBool(attr, value, exists); - if (exists) { - for (LibertyPort *port : *ports_) - (port->*setter)(value); - } - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginMemory(LibertyGroup *) -{ - if (cell_) { - cell_->setIsMemory(true); - } -} - -void -LibertyReader::endMemory(LibertyGroup *) -{ -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginFF(LibertyGroup *group) -{ - beginSequential(group, true, false); -} - -void -LibertyReader::endFF(LibertyGroup *) -{ - sequential_ = nullptr; -} - -void -LibertyReader::beginFFBank(LibertyGroup *group) -{ - beginSequential(group, true, true); -} - -void -LibertyReader::endFFBank(LibertyGroup *) -{ - sequential_ = nullptr; -} - -void -LibertyReader::beginLatch(LibertyGroup *group) -{ - beginSequential(group, false, false); -} - -void -LibertyReader::endLatch(LibertyGroup *) -{ - sequential_ = nullptr; -} - -void -LibertyReader::beginLatchBank(LibertyGroup *group) -{ - beginSequential(group, false, true); -} - -void -LibertyReader::endLatchBank(LibertyGroup *) -{ - sequential_ = nullptr; -} - -void -LibertyReader::beginSequential(LibertyGroup *group, - bool is_register, - bool is_bank) -{ - if (cell_) { - // Define ff/latch state variables as internal ports. - const char *out_name, *out_inv_name; - int size; - bool has_size; - seqPortNames(group, out_name, out_inv_name, has_size, size); - LibertyPort *out_port = nullptr; - LibertyPort *out_port_inv = nullptr; - if (out_name) { - if (has_size) - out_port = makeBusPort(cell_, out_name, size - 1, 0, nullptr); - else - out_port = makePort(cell_, out_name); - out_port->setDirection(PortDirection::internal()); - } - if (out_inv_name) { - if (has_size) - out_port_inv = makeBusPort(cell_, out_inv_name, size - 1, 0, nullptr); - else - out_port_inv = makePort(cell_, out_inv_name); - out_port_inv->setDirection(PortDirection::internal()); - } - sequential_ = new SequentialGroup(is_register, is_bank, - out_port, out_port_inv, size, - group->line()); - cell_sequentials_.push_back(sequential_); - } -} - -void -LibertyReader::seqPortNames(LibertyGroup *group, - const char *&out_name, - const char *&out_inv_name, - bool &has_size, - int &size) -{ - out_name = nullptr; - out_inv_name = nullptr; - size = 1; - has_size = false; - if (group->params()->size() == 2) { - // out_port, out_port_inv - out_name = group->firstName(); - out_inv_name = group->secondName(); - } - else if (group->params()->size() == 3) { - LibertyAttrValue *third_value = (*group->params())[2]; - if (third_value->isFloat()) { - // out_port, out_port_inv, bus_size - out_name = group->firstName(); - out_inv_name = group->secondName(); - size = static_cast(third_value->floatValue()); - has_size = true; - } - else { - // in_port (ignored), out_port, out_port_inv - out_name = group->secondName(); - out_inv_name = third_value->stringValue().c_str(); - } - } -} - -void -LibertyReader::visitClockedOn(LibertyAttr *attr) -{ - if (sequential_) { - const char *func = getAttrString(attr); - if (func) - sequential_->setClock(stringCopy(func)); - } -} - -void -LibertyReader::visitDataIn(LibertyAttr *attr) -{ - if (sequential_) { - const char *func = getAttrString(attr); - if (func) - sequential_->setData(stringCopy(func)); - } -} - -void -LibertyReader::visitClear(LibertyAttr *attr) -{ - if (sequential_) { - const char *func = getAttrString(attr); - if (func) - sequential_->setClear(stringCopy(func)); - } -} - -void -LibertyReader::visitPreset(LibertyAttr *attr) -{ - if (sequential_) { - const char *func = getAttrString(attr); - if (func) - sequential_->setPreset(stringCopy(func)); - } -} - -void -LibertyReader::visitClrPresetVar1(LibertyAttr *attr) -{ - if (sequential_) { - LogicValue var = getAttrLogicValue(attr); - sequential_->setClrPresetVar1(var); - } -} - -void -LibertyReader::visitClrPresetVar2(LibertyAttr *attr) -{ - if (sequential_) { - LogicValue var = getAttrLogicValue(attr); - sequential_->setClrPresetVar2(var); - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginStatetable(LibertyGroup *group) -{ - if (cell_) { - const char *input_ports_arg = group->firstName(); - StdStringSeq input_ports; - if (input_ports_arg) - input_ports = parseTokenList(input_ports_arg, ' '); - - const char *internal_ports_arg = group->secondName(); - StdStringSeq internal_ports; - if (internal_ports_arg) - internal_ports = parseTokenList(internal_ports_arg, ' '); - statetable_ = new StatetableGroup(input_ports, internal_ports, group->line()); - } -} - -void -LibertyReader::visitTable(LibertyAttr *attr) -{ - if (statetable_) { - const char *table_str = getAttrString(attr); - StdStringSeq table_rows = parseTokenList(table_str, ','); - size_t input_count = statetable_->inputPorts().size(); - size_t internal_count = statetable_->internalPorts().size(); - for (string row : table_rows) { - StdStringSeq row_groups = parseTokenList(row.c_str(), ':'); - if (row_groups.size() != 3) { - libWarn(1300, attr, "table row must have 3 groups separated by ':'."); - break; - } - StdStringSeq inputs = parseTokenList(row_groups[0].c_str(), ' '); - if (inputs.size() != input_count) { - libWarn(1301, attr, "table row has %zu input values but %zu are required.", - inputs.size(), - input_count); - break; - } - StdStringSeq currents = parseTokenList(row_groups[1].c_str(), ' '); - if (currents.size() != internal_count) { - libWarn(1302, attr, "table row has %zu current values but %zu are required.", - currents.size(), - internal_count); - break; - } - StdStringSeq nexts = parseTokenList(row_groups[2].c_str(), ' '); - if (nexts.size() != internal_count) { - libWarn(1303, attr, "table row has %zu next values but %zu are required.", - nexts.size(), - internal_count); - break; - } - - StateInputValues input_values = parseStateInputValues(inputs, attr); - StateInternalValues current_values=parseStateInternalValues(currents,attr); - StateInternalValues next_values = parseStateInternalValues(nexts, attr); - statetable_->addRow(input_values, current_values, next_values); - } - } -} - -static EnumNameMap state_input_value_name_map = - {{StateInputValue::low, "L"}, - {StateInputValue::high, "H"}, - {StateInputValue::dont_care, "-"}, - {StateInputValue::low_high, "L/H"}, - {StateInputValue::high_low, "H/L"}, - {StateInputValue::rise, "R"}, - {StateInputValue::fall, "F"}, - {StateInputValue::not_rise, "~R"}, - {StateInputValue::not_fall, "~F"} - }; - -static EnumNameMap state_internal_value_name_map = - {{StateInternalValue::low, "L"}, - {StateInternalValue::high, "H"}, - {StateInternalValue::unspecified, "-"}, - {StateInternalValue::low_high, "L/H"}, - {StateInternalValue::high_low, "H/L"}, - {StateInternalValue::unknown, "X"}, - {StateInternalValue::hold, "N"} - }; - -StateInputValues -LibertyReader::parseStateInputValues(StdStringSeq &inputs, - LibertyAttr *attr) -{ - StateInputValues input_values; - for (string input : inputs) { - bool exists; - 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()); - value = StateInputValue::dont_care; - } - input_values.push_back(value); - } - return input_values; -} - -StateInternalValues -LibertyReader::parseStateInternalValues(StdStringSeq &states, - LibertyAttr *attr) -{ - StateInternalValues state_values; - for (string state : states) { - bool exists; - 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()); - value = StateInternalValue::unknown; - } - state_values.push_back(value); - } - return state_values; -} - -void -LibertyReader::endStatetable(LibertyGroup *) -{ -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTiming(LibertyGroup *group) -{ - if (port_group_) { - timing_ = new TimingGroup(group->line()); - port_group_->addTimingGroup(timing_); - } -} - -void -LibertyReader::endTiming(LibertyGroup *group) -{ - if (timing_) { - // Set scale factor type in constraint tables. - for (auto rf : RiseFall::range()) { - TableModel *model = timing_->constraint(rf); - if (model) { - ScaleFactorType type=timingTypeScaleFactorType(timing_->attrs()->timingType()); - model->setScaleFactorType(type); - } - } - TimingType timing_type = timing_->attrs()->timingType(); - if (timing_->relatedPortNames() == nullptr - && !(timing_type == TimingType::min_pulse_width - || timing_type == TimingType::minimum_period - || timing_type == TimingType::min_clock_tree_path - || timing_type == TimingType::max_clock_tree_path)) - libWarn(1243, group, "timing group missing related_pin/related_bus_pin."); - } - timing_ = nullptr; - receiver_model_ = nullptr; -} - -void -LibertyReader::visitRelatedPin(LibertyAttr *attr) -{ - if (timing_) - visitRelatedPin(attr, timing_); - if (internal_power_) - visitRelatedPin(attr, internal_power_); -} - -void -LibertyReader::visitRelatedPin(LibertyAttr *attr, - RelatedPortGroup *group) -{ - const char *port_names = getAttrString(attr); - if (port_names) { - group->setRelatedPortNames(parseNameList(port_names)); - group->setIsOneToOne(true); - } -} - -StringSeq * -LibertyReader::parseNameList(const char *name_list) -{ - StringSeq *names = new StringSeq; - // Parse space separated list of names. - TokenParser parser(name_list, " "); - while (parser.hasNext()) { - char *token = parser.next(); - // Skip extra spaces. - if (token[0] != '\0') { - const char *name = token; - names->push_back(stringCopy(name)); - } - } - return names; -} - -StdStringSeq -LibertyReader::parseTokenList(const char *token_str, - const char separator) -{ - StdStringSeq tokens; - // Parse space separated list of names. - char separators[2] = {separator, '\0'}; - TokenParser parser(token_str, separators); - while (parser.hasNext()) { - char *token = parser.next(); - // Skip extra spaces. - if (token[0] != '\0') { - tokens.push_back(token); - } - } - return tokens; -} - -void -LibertyReader::visitRelatedBusPins(LibertyAttr *attr) -{ - if (timing_) - visitRelatedBusPins(attr, timing_); - if (internal_power_) - visitRelatedBusPins(attr, internal_power_); -} - -void -LibertyReader::visitRelatedBusPins(LibertyAttr *attr, - RelatedPortGroup *group) -{ - const char *port_names = getAttrString(attr); - if (port_names) { - group->setRelatedPortNames(parseNameList(port_names)); - group->setIsOneToOne(false); - } -} - -void -LibertyReader::visitRelatedOutputPin(LibertyAttr *attr) -{ - if (timing_) { - const char *pin_name = getAttrString(attr); - if (pin_name) - timing_->setRelatedOutputPortName(pin_name); - } -} - -void -LibertyReader::visitTimingType(LibertyAttr *attr) -{ - if (timing_) { - const char *type_name = getAttrString(attr); - if (type_name) { - TimingType type = findTimingType(type_name); - if (type == TimingType::unknown) - libWarn(1244, attr, "unknown timing_type %s.", type_name); - else - timing_->attrs()->setTimingType(type); - } - } -} - -void -LibertyReader::visitTimingSense(LibertyAttr *attr) -{ - if (timing_) { - const char *sense_name = getAttrString(attr); - if (sense_name) { - if (stringEq(sense_name, "non_unate")) - timing_->attrs()->setTimingSense(TimingSense::non_unate); - else if (stringEq(sense_name, "positive_unate")) - timing_->attrs()->setTimingSense(TimingSense::positive_unate); - else if (stringEq(sense_name, "negative_unate")) - timing_->attrs()->setTimingSense(TimingSense::negative_unate); - else - libWarn(1245, attr, "unknown timing_sense %s.", sense_name); - } - } -} - -void -LibertyReader::visitSdfCondStart(LibertyAttr *attr) -{ - if (timing_) { - const char *cond = getAttrString(attr); - if (cond) - timing_->attrs()->setSdfCondStart(cond); - } -} - -void -LibertyReader::visitSdfCondEnd(LibertyAttr *attr) -{ - if (timing_) { - const char *cond = getAttrString(attr); - if (cond) - timing_->attrs()->setSdfCondEnd(cond); - } -} - -void -LibertyReader::visitMode(LibertyAttr *attr) -{ - if (timing_) { - if (attr->isComplexAttr()) { - LibertyAttrValueSeq *values = attr->values(); - if (values->size() == 2) { - LibertyAttrValue *value = (*values)[0]; - if (value->isString()) - timing_->attrs()->setModeName(value->stringValue()); - else - libWarn(1248, attr, "mode name is not a string."); - - value = (*values)[1]; - if (value->isString()) - timing_->attrs()->setModeValue(value->stringValue()); - else - libWarn(1246, attr, "mode value is not a string."); - } - else - libWarn(1249, attr, "mode requirees 2 values."); - } - else - libWarn(1250, attr, "mode missing mode name and value."); - } -} - -void -LibertyReader::visitIntrinsicRise(LibertyAttr *attr) -{ - visitIntrinsic(attr, RiseFall::rise()); -} - -void -LibertyReader::visitIntrinsicFall(LibertyAttr *attr) -{ - visitIntrinsic(attr, RiseFall::fall()); -} - -void -LibertyReader::visitIntrinsic(LibertyAttr *attr, - const RiseFall *rf) -{ - if (timing_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - timing_->setIntrinsic(rf, value * time_scale_); - } -} - -void -LibertyReader::visitRiseResistance(LibertyAttr *attr) -{ - visitRiseFallResistance(attr, RiseFall::rise()); -} - -void -LibertyReader::visitFallResistance(LibertyAttr *attr) -{ - visitRiseFallResistance(attr, RiseFall::fall()); -} - -void -LibertyReader::visitRiseFallResistance(LibertyAttr *attr, - const RiseFall *rf) -{ - if (timing_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - timing_->setResistance(rf, value * res_scale_); - } -} - -void -LibertyReader::beginCellRise(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::cell); -} - -void -LibertyReader::beginCellFall(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::cell); -} - -void -LibertyReader::endCellRiseFall(LibertyGroup *group) -{ - if (table_) { - if (GateTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - timing_->setCell(rf_, table_model); - } - else - libWarn(1251, group, "unsupported model axis."); - } - endTableModel(); -} - -void -LibertyReader::beginRiseTransition(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::transition); -} - -void -LibertyReader::beginFallTransition(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::transition); -} - -void -LibertyReader::endRiseFallTransition(LibertyGroup *group) -{ - if (table_) { - if (GateTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - timing_->setTransition(rf_, table_model); - } - else - libWarn(1252, group, "unsupported model axis."); - } - endTableModel(); -} - -void -LibertyReader::beginRiseConstraint(LibertyGroup *group) -{ - // Scale factor depends on timing_type, which may follow this stmt. - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); -} - -void -LibertyReader::beginFallConstraint(LibertyGroup *group) -{ - // Scale factor depends on timing_type, which may follow this stmt. - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); -} - -void -LibertyReader::endRiseFallConstraint(LibertyGroup *group) -{ - if (table_) { - if (CheckTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - timing_->setConstraint(rf_, table_model); - } - else - libWarn(1253, group, "unsupported model axis."); - } - endTableModel(); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginRiseTransitionDegredation(LibertyGroup *group) -{ - if (library_) - beginTableModel(group, TableTemplateType::delay, - RiseFall::rise(), time_scale_, - ScaleFactorType::transition); -} - -void -LibertyReader::beginFallTransitionDegredation(LibertyGroup *group) -{ - if (library_) - beginTableModel(group, TableTemplateType::delay, - RiseFall::fall(), time_scale_, - ScaleFactorType::transition); -} - -void -LibertyReader::endRiseFallTransitionDegredation(LibertyGroup *group) -{ - if (table_) { - if (LibertyLibrary::checkSlewDegradationAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - library_->setWireSlewDegradationTable(table_model, rf_); - } - else - libWarn(1254, group, "unsupported model axis."); - } - endTableModel(); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTimingTableModel(LibertyGroup *group, - const RiseFall *rf, - ScaleFactorType scale_factor_type) -{ - if (timing_) - beginTableModel(group, TableTemplateType::delay, rf, - time_scale_, scale_factor_type); - else - libWarn(1255, group, "%s group not in timing group.", group->type().c_str()); -} - -void -LibertyReader::beginTableModel(LibertyGroup *group, - TableTemplateType type, - const RiseFall *rf, - float scale, - ScaleFactorType scale_factor_type) -{ - beginTable(group, type, scale); - rf_ = rf; - scale_factor_type_ = scale_factor_type; - sigma_type_ = EarlyLateAll::all(); -} - -void -LibertyReader::endTableModel() -{ - endTable(); - scale_factor_type_ = ScaleFactorType::unknown; - sigma_type_ = nullptr; - index_ = 0; -} - -void -LibertyReader::beginTable(LibertyGroup *group, - TableTemplateType type, - float scale) -{ - const char *template_name = group->firstName(); - if (library_ && template_name) { - tbl_template_ = library_->findTableTemplate(template_name, type); - if (tbl_template_) { - axis_[0] = tbl_template_->axis1ptr(); - axis_[1] = tbl_template_->axis2ptr(); - axis_[2] = tbl_template_->axis3ptr(); - } - else { - libWarn(1256, group, "table template %s not found.", template_name); - axis_[0] = nullptr; - axis_[1] = nullptr; - axis_[2] = nullptr; - } - clearAxisValues(); - table_ = nullptr; - table_model_scale_ = scale; - } -} - -void -LibertyReader::endTable() -{ - table_ = nullptr; - tbl_template_ = nullptr; - axis_[0] = nullptr; - axis_[1] = nullptr; - axis_[2] = nullptr; -} - -void -LibertyReader::visitValue(LibertyAttr *attr) -{ - if (leakage_power_) { - float value; - bool valid; - getAttrFloat(attr, value, valid); - if (valid) - leakage_power_->setPower(value * power_scale_); - } -} - -void -LibertyReader::visitValues(LibertyAttr *attr) -{ - if (tbl_template_ - // Ignore values in ecsm_waveform groups. - && !in_ecsm_waveform_) - makeTable(attr, table_model_scale_); -} - -void -LibertyReader::makeTable(LibertyAttr *attr, - float scale) -{ - if (attr->isComplexAttr()) { - makeTableAxis(0, attr); - makeTableAxis(1, attr); - makeTableAxis(2, attr); - if (axis_[0] && axis_[1] && axis_[2]) { - // 3D table - // Column index1*size(index2) + index2 - // Row index3 - table_ = make_shared
(makeFloatTable(attr, - axis_[0]->size() * axis_[1]->size(), - axis_[2]->size(), scale), - axis_[0], axis_[1], axis_[2]); - } - else if (axis_[0] && axis_[1]) { - // 2D table - // Row variable1/axis[0] - // Column variable2/axis[1] - table_ = make_shared
(makeFloatTable(attr, axis_[0]->size(), - axis_[1]->size(), scale), - axis_[0], axis_[1]); - } - else if (axis_[0]) { - // 1D table - FloatTable table = makeFloatTable(attr, 1, axis_[0]->size(), scale); - table_ = make_shared
(std::move(table[0]), axis_[0]); - } - else if (axis_[0] == nullptr && axis_[1] == nullptr && axis_[2] == nullptr) { - // scalar - FloatTable table = makeFloatTable(attr, 1, 1, scale); - float value = table[0][0]; - table_ = make_shared
(value); - } - } - else - libWarn(1257, attr, "%s is missing values.", attr->name().c_str()); -} - -FloatTable -LibertyReader::makeFloatTable(LibertyAttr *attr, - size_t rows, - size_t cols, - float scale) -{ - FloatTable table; - table.reserve(rows); - for (LibertyAttrValue *value : *attr->values()) { - FloatSeq row; - if (value->isString()) - row = parseStringFloatList(value->stringValue(), scale, attr); - else if (value->isFloat()) - row.push_back(value->floatValue() * scale); - else - libWarn(1258, attr, "%s is not a list of floats.", attr->name().c_str()); - if (row.size() != cols) { - libWarn(1259, attr, "table row has %zu columns but axis has %zu.", - row.size(), - cols); - for (size_t c = row.size(); c < cols; c++) - row.push_back(0.0); - } - table.push_back(std::move(row)); - } - if (table.size() != rows) { - libWarn(1260, attr, "table has %zu rows but axis has %zu.", - table.size(), - rows); - for (size_t r = table.size(); r < rows; r++) { - FloatSeq row(cols, 0.0); - table.push_back(std::move(row)); - } - } - return table; -} - -void -LibertyReader::makeTableAxis(int index, - LibertyAttr *attr) -{ - if (axis_[index] && !axis_values_[index].empty()) { - TableAxisVariable var = axis_[index]->variable(); - const Units *units = library_->units(); - float scale = tableVariableUnit(var, units)->scale(); - FloatSeq values = std::move(axis_values_[index]); - scaleFloats(values, scale); - axis_[index] = make_shared(var, std::move(values)); - } - else if (axis_[index] && axis_[index]->values().empty()) { - libWarn(1344, attr, "Table axis and template missing values."); - axis_[index] = nullptr; - axis_values_[index].clear(); - } -} - -//////////////////////////////////////////////////////////////// - -// Define lut output variables as internal ports. -// I can't find any documentation for this group. -void -LibertyReader::beginLut(LibertyGroup *group) -{ - if (cell_) { - for (LibertyAttrValue *param : *group->params()) { - if (param->isString()) { - const std::string &names = param->stringValue(); - // Parse space separated list of related port names. - TokenParser parser(names.c_str(), " "); - while (parser.hasNext()) { - char *name = parser.next(); - if (name[0] != '\0') { - LibertyPort *port = makePort(cell_, name); - port->setDirection(PortDirection::internal()); - } - } - } - else - libWarn(1261, group, "lut output is not a string."); - } - } -} - -void -LibertyReader::endLut(LibertyGroup *) -{ -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTestCell(LibertyGroup *group) -{ - if (cell_ && cell_->testCell()) - libWarn(1262, group, "cell %s test_cell redefinition.", cell_->name()); - else { - string name = cell_->name(); - name += "/test_cell"; - test_cell_ = new TestCell(cell_->libertyLibrary(), std::move(name), - cell_->filename()); - cell_->setTestCell(test_cell_); - - // Do a recursive parse of cell into the test_cell because it has - // pins, buses, bundles, and sequentials just like a cell. - save_cell_ = cell_; - save_cell_port_groups_ = std::move(cell_port_groups_); - save_statetable_ = statetable_; - statetable_ = nullptr; - save_cell_sequentials_ = std::move(cell_sequentials_); - save_cell_funcs_ = std::move(cell_funcs_); - cell_ = test_cell_; - } -} - -void -LibertyReader::endTestCell(LibertyGroup *) -{ - makeCellSequentials(); - makeStatetable(); - parseCellFuncs(); - finishPortGroups(); - - // Restore reader state to enclosing cell. - cell_port_groups_ = std::move(save_cell_port_groups_); - statetable_ = save_statetable_; - cell_sequentials_ = std::move(save_cell_sequentials_); - cell_funcs_= std::move(save_cell_funcs_); - cell_ = save_cell_; - - test_cell_ = nullptr; - save_statetable_ = nullptr; -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginModeDef(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) - mode_def_ = cell_->makeModeDef(name); - else - libWarn(1263, group, "mode definition missing name."); -} - -void -LibertyReader::endModeDef(LibertyGroup *) -{ - mode_def_ = nullptr; -} - -void -LibertyReader::beginModeValue(LibertyGroup *group) -{ - if (mode_def_) { - const char *name = group->firstName(); - if (name) - mode_value_ = mode_def_->defineValue(name, nullptr, nullptr); - else - libWarn(1264, group, "mode value missing name."); - } -} - -void - LibertyReader::endModeValue(LibertyGroup *) -{ - mode_value_ = nullptr; -} - -void -LibertyReader::visitWhen(LibertyAttr *attr) -{ - if (tbl_template_) - libWarn(1265, attr, "when attribute inside table model."); - if (mode_value_) { - const char *func = getAttrString(attr); - if (func) { - ModeValueDef *mode_value = mode_value_; - makeLibertyFunc(func, - [mode_value] (FuncExpr *expr) {mode_value->setCond(expr);}, - false, "when", attr); - } - } - if (timing_ && !in_ccsn_) { - const char *func = getAttrString(attr); - if (func) { - TimingArcAttrs *attrs = timing_->attrs().get(); - makeLibertyFunc(func, - [attrs] (FuncExpr *expr) { attrs->setCond(expr);}, - false, "when", attr); - } - } - if (internal_power_) { - const char *func = getAttrString(attr); - if (func) { - InternalPowerGroup *internal_pwr = internal_power_; - makeLibertyFunc(func, - [internal_pwr] (FuncExpr *expr) { - internal_pwr->setWhen(std::shared_ptr(expr)); - }, - false, "when", attr); - } - } - if (leakage_power_) { - const char *func = getAttrString(attr); - if (func) { - LeakagePowerGroup *leakage_pwr = leakage_power_; - makeLibertyFunc(func, - [leakage_pwr] (FuncExpr *expr) { leakage_pwr->setWhen(expr);}, - false, "when", attr); - } - } -} - -void -LibertyReader::visitSdfCond(LibertyAttr *attr) -{ - if (mode_value_) { - const char *cond = getAttrString(attr); - if (cond) - mode_value_->setSdfCond(cond); - } - else if (timing_) { - const char *cond = getAttrString(attr); - if (cond) - timing_->attrs()->setSdfCond(cond); - } - // sdf_cond can also appear inside minimum_period groups. -} - -//////////////////////////////////////////////////////////////// - -const char * -LibertyReader::getAttrString(LibertyAttr *attr) -{ - if (attr->isSimpleAttr()) { - LibertyAttrValue *value = attr->firstValue(); - if (value->isString()) - return value->stringValue().c_str(); - else - libWarn(1266, attr, "%s attribute is not a string.", attr->name().c_str()); - } - else - libWarn(1267, attr, "%s is not a simple attribute.", attr->name().c_str()); - return nullptr; -} - -void -LibertyReader::getAttrInt(LibertyAttr *attr, - // Return values. - int &value, - bool &exists) -{ - value = 0; - exists = false; - if (attr->isSimpleAttr()) { - LibertyAttrValue *attr_value = attr->firstValue(); - if (attr_value->isFloat()) { - float float_val = attr_value->floatValue(); - value = static_cast(float_val); - exists = true; - } - else - libWarn(1268, attr, "%s attribute is not an integer.",attr->name().c_str()); - } - else - libWarn(1269, attr, "%s is not a simple attribute.", attr->name().c_str()); -} - -void -LibertyReader::getAttrFloat(LibertyAttr *attr, - // Return values. - float &value, - bool &valid) -{ - valid = false; - if (attr->isSimpleAttr()) - getAttrFloat(attr, attr->firstValue(), value, valid); - else - libWarn(1270, attr, "%s is not a simple attribute.", attr->name().c_str()); -} - -void -LibertyReader::getAttrFloat(LibertyAttr *attr, - LibertyAttrValue *attr_value, - // Return values. - float &value, - bool &valid) -{ - if (attr_value->isFloat()) { - valid = true; - value = attr_value->floatValue(); - } - else if (attr_value->isString()) { - const std::string &str = attr_value->stringValue(); - // See if attribute string is a variable. - variableValue(str.c_str(), value, valid); - if (!valid) { - // For some reason area attributes for pads are quoted floats. - // Check that the string is a valid double. - char *end; - value = strtof(str.c_str(), &end); - if ((*end && !isspace(*end)) - // strtof support INF as a valid float. - || str == "inf") - libWarn(1271, attr, "%s value %s is not a float.", - attr->name().c_str(), - str.c_str()); - valid = true; - } - } -} - -// Get two floats in a complex attribute. -// attr(float1, float2); -void -LibertyReader::getAttrFloat2(LibertyAttr *attr, - // Return values. - float &value1, - float &value2, - bool &exists) -{ - exists = false; - if (attr->isComplexAttr()) { - LibertyAttrValueSeq *values = attr->values(); - if (values->size() == 2) { - LibertyAttrValue *value = (*values)[0]; - getAttrFloat(attr, value, value1, exists); - if (!exists) - libWarn(1272, attr, "%s is not a float.", attr->name().c_str()); - - value = (*values)[1]; - getAttrFloat(attr, value, value2, exists); - if (!exists) - libWarn(1273, attr, "%s is not a float.", attr->name().c_str()); - } - else - libWarn(1274, attr, "%s requires 2 valules.", attr->name().c_str()); - } - else - libWarn(1345, attr, "%s requires 2 valules.", attr->name().c_str()); -} - -// Parse string of comma separated floats. -// Note that some brain damaged vendors (that used to "Think") are not -// consistent about including the delimiters. -FloatSeq -LibertyReader::parseStringFloatList(const std::string &float_list, - float scale, - LibertyAttr *attr) -{ - FloatSeq values; - const char *token = float_list.c_str(); - while (*token != '\0') { - // Some (brain dead) libraries enclose floats in brackets. - if (*token == '{') - token++; - char *end; - float value = strtof(token, &end) * scale; - if (end == token - || !(*end == '\0' - || isspace(*end) - || *end == ',' - || *end == '}')) { - std::string token_end = token; - if (end != token) { - token_end.clear(); - for (const char *t = token; t <= end; t++) - token_end += *t; - } - libWarn(1275, attr, "%s is not a float.", token_end.c_str()); - token += token_end.size(); - } - else { - values.push_back(value); - token = end; - } - while (*token == ',' || *token == ' ' || *token == '}') - token++; - } - return values; -} - -FloatSeq -LibertyReader::readFloatSeq(LibertyAttr *attr, - float scale) -{ - FloatSeq values; - if (attr->isComplexAttr()) { - LibertyAttrValueSeq *attr_values = attr->values(); - if (attr_values->size() == 1) { - LibertyAttrValue *value = (*attr_values)[0]; - if (value->isString()) { - values = parseStringFloatList(value->stringValue(), scale, attr); - } - else if (value->isFloat()) { - values.push_back(value->floatValue()); - } - else - libWarn(1276, attr, "%s is missing values.", attr->name().c_str()); - } - else - libWarn(1277, attr, "%s has more than one string.", attr->name().c_str()); - } - else { - LibertyAttrValue *value = attr->firstValue(); - if (value->isString()) { - values = parseStringFloatList(value->stringValue(), scale, attr); - } - else - libWarn(1278, attr, "%s is missing values.", attr->name().c_str()); - } - return values; -} - -void -LibertyReader::getAttrBool(LibertyAttr *attr, - // Return values. - bool &value, - bool &exists) -{ - exists = false; - if (attr->isSimpleAttr()) { - LibertyAttrValue *val = attr->firstValue(); - if (val->isString()) { - const std::string &str = val->stringValue(); - if (stringEqual(str.c_str(), "true")) { - value = true; - exists = true; - } - else if (stringEqual(str.c_str(), "false")) { - value = false; - exists = true; - } - else - libWarn(1279, attr, "%s attribute is not boolean.", attr->name().c_str()); - } - else - libWarn(1280, attr, "%s attribute is not boolean.", attr->name().c_str()); - } - else - libWarn(1281, attr, "%s is not a simple attribute.", attr->name().c_str()); -} - -// Read L/H/X string attribute values as bool. -LogicValue -LibertyReader::getAttrLogicValue(LibertyAttr *attr) -{ - const char *str = getAttrString(attr); - if (str) { - if (stringEq(str, "L")) - return LogicValue::zero; - else if (stringEq(str, "H")) - return LogicValue::one; - else if (stringEq(str, "X")) - return LogicValue::unknown; - else - libWarn(1282, attr, "attribute %s value %s not recognized.", - attr->name().c_str(), str); - // fall thru - } - return LogicValue::unknown; -} - -FuncExpr * -LibertyReader::parseFunc(const char *func, - const char *attr_name, - int line) -{ - string error_msg; - stringPrint(error_msg, "%s, line %d %s", - filename_, - line, - attr_name); - return parseFuncExpr(func, cell_, error_msg.c_str(), report_); -} - -const EarlyLateAll * -LibertyReader::getAttrEarlyLate(LibertyAttr *attr) -{ - const char *value = getAttrString(attr); - if (stringEq(value, "early")) - return EarlyLateAll::early(); - else if (stringEq(value, "late")) - return EarlyLateAll::late(); - else if (stringEq(value, "early_and_late")) - return EarlyLateAll::all(); - else { - libWarn(1283, attr, "unknown early/late value."); - return EarlyLateAll::all(); - } -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::visitVariable(LibertyVariable *var) -{ - if (var_map_ == nullptr) - var_map_ = new LibertyVariableMap; - const string &var_name = var->variable(); - float value; - bool exists; - findKeyValue(var_map_, var_name, value, exists); - (*var_map_)[var_name] = var->value(); -} - -void -LibertyReader::variableValue(const char *var, - float &value, - bool &exists) -{ - if (var_map_) - findKeyValue(var_map_, var, value, exists); - else - exists = false; -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::libWarn(int id, - LibertyStmt *stmt, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_, stmt->line(), fmt, args); - va_end(args); -} - -void -LibertyReader::libWarn(int id, - int line, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileWarn(id, filename_, line, fmt, args); - va_end(args); -} - -void -LibertyReader::libError(int id, - LibertyStmt *stmt, - const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - report_->vfileError(id, filename_, stmt->line(), fmt, args); - va_end(args); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTableTemplatePower(LibertyGroup *group) -{ - beginTableTemplate(group, TableTemplateType::power); -} - -void -LibertyReader::beginLeakagePower(LibertyGroup *group) -{ - if (cell_) { - leakage_power_ = new LeakagePowerGroup(group->line()); - leakage_powers_.push_back(leakage_power_); - } -} - -void -LibertyReader::endLeakagePower(LibertyGroup *) -{ - leakage_power_ = nullptr; -} - -void -LibertyReader::beginInternalPower(LibertyGroup *group) -{ - if (port_group_) { - internal_power_ = new InternalPowerGroup(group->line()); - port_group_->addInternalPowerGroup(internal_power_); - } -} - -void -LibertyReader::endInternalPower(LibertyGroup *) -{ - internal_power_ = nullptr; -} - -void -LibertyReader::beginFallPower(LibertyGroup *group) -{ - if (internal_power_) - beginTableModel(group, TableTemplateType::power, - RiseFall::fall(), energy_scale_, - ScaleFactorType::internal_power); -} - -void -LibertyReader::beginRisePower(LibertyGroup *group) -{ - if (internal_power_) - beginTableModel(group, TableTemplateType::power, - RiseFall::rise(), energy_scale_, - ScaleFactorType::internal_power); -} - -void -LibertyReader::endRiseFallPower(LibertyGroup *) -{ - if (table_) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - internal_power_->setModel(rf_, std::make_shared(table_model)); - } - endTableModel(); -} - -void -LibertyReader::endPower(LibertyGroup *) -{ - if (table_) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - // Share the model for rise/fall. - auto power_model = std::make_shared(table_model); - internal_power_->setModel(RiseFall::rise(), power_model); - internal_power_->setModel(RiseFall::fall(), power_model); - } - endTableModel(); -} - -void -LibertyReader::visitRelatedGroundPin(LibertyAttr *attr) -{ - if (ports_) { - const char *related_ground_pin = getAttrString(attr); - for (LibertyPort *port : *ports_) - port->setRelatedGroundPin(related_ground_pin); - } -} - -void -LibertyReader::visitRelatedPowerPin(LibertyAttr *attr) -{ - if (ports_) { - const char *related_power_pin = getAttrString(attr); - for (LibertyPort *port : *ports_) - port->setRelatedPowerPin(related_power_pin); - } -} - -void -LibertyReader::visitRelatedPgPin(LibertyAttr *attr) -{ - if (internal_power_) - internal_power_->setRelatedPgPin(getAttrString(attr)); - else if (leakage_power_) - leakage_power_->setRelatedPgPin(getAttrString(attr)); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginTableTemplateOcv(LibertyGroup *group) -{ - beginTableTemplate(group, TableTemplateType::ocv); -} - -void -LibertyReader::visitOcvArcDepth(LibertyAttr *attr) -{ - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) { - if (timing_) - timing_->attrs()->setOcvArcDepth(value); - else if (cell_) - cell_->setOcvArcDepth(value); - else - library_->setOcvArcDepth(value); - } -} - -void -LibertyReader::visitDefaultOcvDerateGroup(LibertyAttr *attr) -{ - const char *derate_name = getAttrString(attr); - OcvDerate *derate = library_->findOcvDerate(derate_name); - if (derate) - library_->setDefaultOcvDerate(derate); - else - libWarn(1284, attr, "OCV derate group named %s not found.", derate_name); -} - -void -LibertyReader::visitOcvDerateGroup(LibertyAttr *attr) -{ - ocv_derate_name_ = stringCopy(getAttrString(attr)); -} - -void -LibertyReader::beginOcvDerate(LibertyGroup *group) -{ - const char *name = group->firstName(); - if (name) - ocv_derate_ = library_->makeOcvDerate(name); - else - libWarn(1285, group, "ocv_derate missing name."); -} - -void -LibertyReader::endOcvDerate(LibertyGroup *) -{ - ocv_derate_ = nullptr; -} - -void -LibertyReader::beginOcvDerateFactors(LibertyGroup *group) -{ - if (ocv_derate_) { - rf_type_ = RiseFallBoth::riseFall(); - derate_type_ = EarlyLateAll::all(); - path_type_ = PathType::clk_and_data; - beginTable(group, TableTemplateType::ocv, 1.0); - } -} - -void -LibertyReader::endOcvDerateFactors(LibertyGroup *) -{ - if (ocv_derate_) { - for (auto early_late : derate_type_->range()) { - for (auto rf : rf_type_->range()) { - if (path_type_ == PathType::clk_and_data) { - ocv_derate_->setDerateTable(rf, early_late, PathType::clk, table_); - ocv_derate_->setDerateTable(rf, early_late, PathType::data, table_); + libWarn(1307, leak_group, "leakage_power missing value."); + } +} + +void +LibertyReader::readInternalPowerGroups(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group) +{ + for (LibertyPort *port : ports) { + for (const LibertyGroup *ipwr_group : port_group->findSubgroups("internal_power")) { + LibertyPortSeq related_ports = findLibertyPorts(cell, ipwr_group, "related_pin"); + LibertyPort *related_pg_port = findLibertyPort(cell, ipwr_group, "related_pg_pin"); + std::shared_ptr when; + FuncExpr *when1 = readFuncExpr(cell, ipwr_group, "when"); + if (when1) + when = std::shared_ptr(when1); + InternalPowerModels models; + // rise/fall_power group + for (const RiseFall *rf : RiseFall::range()) { + std::string pwr_attr_name = rf->to_string_long() + "_power"; + const LibertyGroup *pwr_group = ipwr_group->findSubgroup(pwr_attr_name); + if (pwr_group) { + TableModel *model = readTableModel(pwr_group, rf, TableTemplateType::power, + energyScale(), + ScaleFactorType::internal_power); + models[rf->index()] = std::make_shared(model); } - else - ocv_derate_->setDerateTable(rf, early_late, path_type_, table_); - } - } - } - endTable(); -} - -void -LibertyReader::visitRfType(LibertyAttr *attr) -{ - const char *rf_name = getAttrString(attr); - if (stringEq(rf_name, "rise")) - rf_type_ = RiseFallBoth::rise(); - else if (stringEq(rf_name, "fall")) - rf_type_ = RiseFallBoth::fall(); - else if (stringEq(rf_name, "rise_and_fall")) - rf_type_ = RiseFallBoth::riseFall(); - else - libError(1286, attr, "unknown rise/fall."); -} - -void -LibertyReader::visitDerateType(LibertyAttr *attr) -{ - derate_type_ = getAttrEarlyLate(attr); -} - -void -LibertyReader::visitPathType(LibertyAttr *attr) -{ - const char *path_type = getAttrString(attr); - if (stringEq(path_type, "clock")) - path_type_ = PathType::clk; - else if (stringEq(path_type, "data")) - path_type_ = PathType::data; - else if (stringEq(path_type, "clock_and_data")) - path_type_ = PathType::clk_and_data; - else - libWarn(1287, attr, "unknown derate type."); -} - -//////////////////////////////////////////////////////////////// - -void -LibertyReader::beginOcvSigmaCellRise(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); -} - -void -LibertyReader::beginOcvSigmaCellFall(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); -} - -void -LibertyReader::endOcvSigmaCell(LibertyGroup *group) -{ - if (table_) { - if (GateTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - if (sigma_type_ == EarlyLateAll::all()) { - timing_->setDelaySigma(rf_, EarlyLate::min(), table_model); - timing_->setDelaySigma(rf_, EarlyLate::max(), table_model); - } - else - timing_->setDelaySigma(rf_, sigma_type_->asMinMax(), table_model); - } - else - libWarn(1288, group, "unsupported model axis."); - } - endTableModel(); -} - -void -LibertyReader::beginOcvSigmaRiseTransition(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); -} - -void -LibertyReader::beginOcvSigmaFallTransition(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); -} - -void -LibertyReader::endOcvSigmaTransition(LibertyGroup *group) -{ - if (table_) { - if (GateTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - if (sigma_type_ == EarlyLateAll::all()) { - timing_->setSlewSigma(rf_, EarlyLate::min(), table_model); - timing_->setSlewSigma(rf_, EarlyLate::max(), table_model); } - else - timing_->setSlewSigma(rf_, sigma_type_->asMinMax(), table_model); + // power group (rise/fall power are the same) + const LibertyGroup *pwr_group = ipwr_group->findSubgroup("power"); + if (pwr_group) { + TableModel *model = readTableModel(pwr_group, RiseFall::rise(), + TableTemplateType::power, + energyScale(), + ScaleFactorType::internal_power); + auto pwr_model = std::make_shared(model); + for (const RiseFall *rf : RiseFall::range()) + models[rf->index()] = pwr_model; + } + if (related_ports.empty()) + cell->makeInternalPower(port, nullptr, related_pg_port, when, models); + else { + for (LibertyPort *related_port : related_ports) + cell->makeInternalPower(port, related_port, related_pg_port, when, models); + } } - else - libWarn(1289, group, "unsupported model axis."); } - endTableModel(); } -void -LibertyReader::beginOcvSigmaRiseConstraint(LibertyGroup *group) -{ - beginTimingTableModel(group, RiseFall::rise(), ScaleFactorType::unknown); -} +//////////////////////////////////////////////////////////////// -void -LibertyReader::beginOcvSigmaFallConstraint(LibertyGroup *group) +FuncExpr * +LibertyReader::readFuncExpr(LibertyCell *cell, + const LibertyGroup *group, + const char *attr_name) { - beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); + const std::string *attr = group->findAttrString(attr_name); + if (attr) + return parseFunc(attr->c_str(), attr_name, cell, group->line()); + else + return nullptr; } -void -LibertyReader::endOcvSigmaConstraint(LibertyGroup *group) -{ - if (table_) { - if (CheckTableModel::checkAxes(table_)) { - TableModel *table_model = new TableModel(table_, tbl_template_, - scale_factor_type_, rf_); - if (sigma_type_ == EarlyLateAll::all()) { - timing_->setConstraintSigma(rf_, EarlyLate::min(), table_model); - timing_->setConstraintSigma(rf_, EarlyLate::max(), table_model); - } +LibertyPort * +LibertyReader::findLibertyPort(LibertyCell *cell, + const LibertyGroup *group, + const char *port_name_attr) +{ + const LibertySimpleAttr *attr = group->findSimpleAttr(port_name_attr); + if (attr) { + const std::string *port_name = attr->stringValue(); + if (port_name) { + LibertyPort *port = cell->findLibertyPort(port_name->c_str()); + if (port) + return port; else - timing_->setConstraintSigma(rf_, sigma_type_->asMinMax(), table_model); + libWarn(1290, attr, "port %s not found.", port_name->c_str()); } - else - libWarn(1290, group, "unsupported model axis."); } - endTableModel(); -} - -void -LibertyReader::visitSigmaType(LibertyAttr *attr) -{ - sigma_type_ = getAttrEarlyLate(attr); + return nullptr; } -void -LibertyReader::visitCellLeakagePower(LibertyAttr *attr) +StdStringSeq +LibertyReader::findAttributStrings(const LibertyGroup *group, + const char *name_attr) { - if (cell_) { - float value; - bool exists; - getAttrFloat(attr, value, exists); - if (exists) - cell_->setLeakagePower(value * power_scale_); + const LibertySimpleAttr *attr = group->findSimpleAttr(name_attr); + if (attr) { + const std::string *strings = attr->stringValue(); + if (strings) { + return parseTokens(*strings, ' '); + } } + return StdStringSeq(); } -void -LibertyReader::beginPgPin(LibertyGroup *group) +LibertyPortSeq +LibertyReader::findLibertyPorts(LibertyCell *cell, + const LibertyGroup *group, + const char *port_name_attr) { - if (cell_) { - const char *name = group->firstName(); - pg_port_ = builder_.makePort(cell_, name); + LibertyPortSeq ports; + StdStringSeq port_names = findAttributStrings(group, port_name_attr); + for (const std::string &port_name : port_names) { + LibertyPort *port = findPort(cell, port_name.c_str()); + if (port) + ports.push_back(port); + else + libWarn(1306, group, "port %s not found.", port_name.c_str()); } + return ports; } -void -LibertyReader::endPgPin(LibertyGroup *) +//////////////////////////////////////////////////////////////// + +TimingModel * +LibertyReader::makeScalarCheckModel(LibertyCell *cell, + float value, + ScaleFactorType scale_factor_type, + const RiseFall *rf) { - pg_port_ = nullptr; + 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); + TableModelsEarlyLate sigmas{}; + CheckTableModel *check_model = new CheckTableModel(cell, table_model, + std::move(sigmas)); + return check_model; } void -LibertyReader::visitPgType(LibertyAttr *attr) +LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, + int line) { - if (pg_port_) { - const char *type_name = getAttrString(attr); - PwrGndType type = findPwrGndType(type_name); - PortDirection *dir = PortDirection::unknown(); - switch (type) { - case PwrGndType::primary_ground: - case PwrGndType::backup_ground: - case PwrGndType::internal_ground: - dir = PortDirection::ground(); - break; - case PwrGndType::primary_power: - case PwrGndType::backup_power: - case PwrGndType::internal_power: - dir = PortDirection::power(); + LibertyPortSet enable_ports = enable_func->ports(); + for (LibertyPort *enable_port : enable_ports) { + TimingSense enable_sense = enable_func->portTimingSense(enable_port); + switch (enable_sense) { + case TimingSense::positive_unate: + case TimingSense::negative_unate: break; - case PwrGndType::none: - libError(1291, attr, "unknown pg_type."); + case TimingSense::non_unate: + libWarn(1200, line, "latch enable function is non-unate for port %s.", + enable_port->name()); break; - default: + case TimingSense::none: + case TimingSense::unknown: + libWarn(1201, line, "latch enable function is unknown for port %s.", + enable_port->name()); break; } - pg_port_->setPwrGndType(type); - pg_port_->setDirection(dir); } } +//////////////////////////////////////////////////////////////// + void -LibertyReader::visitVoltageName(LibertyAttr *attr) -{ - if (pg_port_) { - const char *voltage_name = getAttrString(attr); - pg_port_->setVoltageName(voltage_name); +LibertyReader::readNormalizedDriverWaveform(const LibertyGroup *library_group) +{ + for (const LibertyGroup *waveform_group : + library_group->findSubgroups("normalized_driver_waveform")) { + const char *template_name = waveform_group->firstName(); + if (template_name) { + TableTemplate *tbl_template = library_->findTableTemplate(template_name, + TableTemplateType::delay); + if (!tbl_template) { + libWarn(1256, waveform_group, "table template %s 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, + "normalized_driver_waveform variable_1 must be input_net_transition"); + continue; + } + if (table->axis2()->variable() != TableAxisVariable::normalized_voltage) { + libWarn(1225, waveform_group, + "normalized_driver_waveform variable_2 must be normalized_voltage"); + continue; + } + std::string driver_waveform_name; + const std::string *name_attr = waveform_group->findAttrString("driver_waveform_name"); + if (name_attr) + driver_waveform_name = *name_attr; + library_->makeDriverWaveform(driver_waveform_name, table); + } + else + libWarn(1227, waveform_group, "normalized_driver_waveform missing template."); } } -// Contents Ignored. +//////////////////////////////////////////////////////////////// + void -LibertyReader::beginCcsn(LibertyGroup *) +LibertyReader::readLevelShifterType(LibertyCell *cell, + const LibertyGroup *cell_group) { - in_ccsn_ = true; + const std::string *level_shifter_type = cell_group->findAttrString("level_shifter_type"); + if (level_shifter_type) { + if (*level_shifter_type == "HL") + cell->setLevelShifterType(LevelShifterType::HL); + else if (*level_shifter_type == "LH") + cell->setLevelShifterType(LevelShifterType::LH); + 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"); + } } void -LibertyReader::endCcsn(LibertyGroup *) +LibertyReader::readSwitchCellType(LibertyCell *cell, + const LibertyGroup *cell_group) { - in_ccsn_ = false; + const std::string *switch_cell_type = cell_group->findAttrString("switch_cell_type"); + if (switch_cell_type) { + if (*switch_cell_type == "coarse_grain") + cell->setSwitchCellType(SwitchCellType::coarse_grain); + 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"); + } } -// Contents Ignored. void -LibertyReader::beginEcsmWaveform(LibertyGroup *) +LibertyReader::readCellOcvDerateGroup(LibertyCell *cell, + const LibertyGroup *cell_group) { - in_ecsm_waveform_ = true; + const std::string *derate_name = cell_group->findAttrString("ocv_derate_group"); + if (derate_name) { + OcvDerate *derate = cell->findOcvDerate(derate_name->c_str()); + if (derate == nullptr) + derate = library_->findOcvDerate(derate_name->c_str()); + if (derate) + cell->setOcvDerate(derate); + else + libWarn(1237, cell_group, "OCV derate group named %s not found.", + derate_name->c_str()); + } } void -LibertyReader::endEcsmWaveform(LibertyGroup *) +LibertyReader::readStatetable(LibertyCell *cell, + const LibertyGroup *cell_group) { - in_ecsm_waveform_ = false; -} + for (const LibertyGroup *statetable_group : cell_group->findSubgroups("statetable")) { + const char *input_ports_arg = statetable_group->firstName(); + const char *internal_ports_arg = statetable_group->params().size() >= 2 + ? statetable_group->secondName() : nullptr; + StdStringSeq input_ports; + if (input_ports_arg) + input_ports = parseTokens(input_ports_arg, ' '); + StdStringSeq internal_ports; + if (internal_ports_arg) + internal_ports = parseTokens(internal_ports_arg, ' '); + + const LibertySimpleAttr *table_attr = statetable_group->findSimpleAttr("table"); + if (table_attr) { + const std::string *table_str = table_attr->stringValue(); + StdStringSeq table_rows = parseTokens(table_str->c_str(), ','); + size_t input_count = input_ports.size(); + size_t internal_count = internal_ports.size(); + StatetableRows table; + for (const std::string &row : table_rows) { + const StdStringSeq row_groups = parseTokens(row, ':'); + if (row_groups.size() != 3) { + libWarn(1300, table_attr, "table row must have 3 groups separated by ':'."); + break; + } + StdStringSeq inputs = parseTokens(row_groups[0], ' '); + if (inputs.size() != input_count) { + libWarn(1301,table_attr,"table row has %zu input values but %zu are required.", + inputs.size(), input_count); + break; + } + StdStringSeq currents = parseTokens(row_groups[1], ' '); + if (currents.size() != internal_count) { + libWarn(1302,table_attr, + "table row has %zu current values but %zu are required.", + currents.size(), internal_count); + break; + } + StdStringSeq nexts = parseTokens(row_groups[2], ' '); + if (nexts.size() != internal_count) { + libWarn(1303, table_attr, "table row has %zu next values but %zu are required.", + nexts.size(), internal_count); + break; + } -//////////////////////////////////////////////////////////////// + StateInputValues input_values = parseStateInputValues(inputs, table_attr); + StateInternalValues current_values=parseStateInternalValues(currents,table_attr); + StateInternalValues next_values = parseStateInternalValues(nexts, table_attr); + table.emplace_back(input_values, current_values, next_values); + } -LibertyFunc::LibertyFunc(const char *expr, - LibertySetFunc set_func, - bool invert, - const char *attr_name, - int line) : - expr_(stringCopy(expr)), - set_func_(set_func), - invert_(invert), - attr_name_(stringCopy(attr_name)), - line_(line) -{ + LibertyPortSeq input_port_ptrs; + for (const std::string &input : input_ports) { + LibertyPort *port = cell->findLibertyPort(input.c_str()); + if (port) + input_port_ptrs.push_back(port); + else + libWarn(1298, statetable_group, "statetable input port %s not found.", + input.c_str()); + } + LibertyPortSeq internal_port_ptrs; + for (const std::string &internal : internal_ports) { + LibertyPort *port = cell->findLibertyPort(internal.c_str()); + if (port == nullptr) + port = makePort(cell, internal.c_str()); + internal_port_ptrs.push_back(port); + } + cell->makeStatetable(input_port_ptrs, internal_port_ptrs, table); + } + } } -LibertyFunc::~LibertyFunc() +void +LibertyReader::readTestCell(LibertyCell *cell, + const LibertyGroup *cell_group) { - stringDelete(expr_); - stringDelete(attr_name_); + 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()); + else { + std::string test_cell_name = std::string(cell->name()) + "/test_cell"; + TestCell *test_cell = new TestCell(cell->libertyLibrary(), + std::move(test_cell_name), + cell->filename()); + cell->setTestCell(test_cell); + readCell(test_cell, test_cell_group); + } + } } //////////////////////////////////////////////////////////////// -PortGroup::PortGroup(LibertyPortSeq *ports, - int line) : - ports_(ports), - line_(line) +LibertyPort * +LibertyReader::makePort(LibertyCell *cell, + const char *port_name) { + std::string sta_name = portLibertyToSta(port_name); + return builder_.makePort(cell, sta_name.c_str()); } -PortGroup::~PortGroup() +LibertyPort * +LibertyReader::makeBusPort(LibertyCell *cell, + const char *bus_name, + int from_index, + int to_index, + BusDcl *bus_dcl) { - deleteContents(timings_); - delete ports_; - deleteContents(internal_power_groups_); + std::string sta_name = portLibertyToSta(bus_name); + return builder_.makeBusPort(cell, bus_name, from_index, to_index, bus_dcl); } -void -PortGroup::addTimingGroup(TimingGroup *timing) +// Also used by LibExprParser::makeFuncExprPort. +LibertyPort * +libertyReaderFindPort(const LibertyCell *cell, + const char *port_name) { - timings_.push_back(timing); + LibertyPort *port = cell->findLibertyPort(port_name); + if (port == nullptr) { + const LibertyLibrary *library = cell->libertyLibrary(); + char brkt_left = library->busBrktLeft(); + char brkt_right = library->busBrktRight(); + const char escape = '\\'; + // Pins at top level with bus names have escaped brackets. + std::string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right, escape); + port = cell->findLibertyPort(escaped_port_name.c_str()); + } + return port; } -void -PortGroup::addInternalPowerGroup(InternalPowerGroup *internal_power) +LibertyPort * +LibertyReader::findPort(LibertyCell *cell, + const char *port_name) { - internal_power_groups_.push_back(internal_power); -} - -//////////////////////////////////////////////////////////////// - -SequentialGroup::SequentialGroup(bool is_register, - bool is_bank, - LibertyPort *out_port, - LibertyPort *out_inv_port, - int size, - int line) : - is_register_(is_register), - is_bank_(is_bank), - out_port_(out_port), - out_inv_port_(out_inv_port), - size_(size), - clk_(nullptr), - data_(nullptr), - preset_(nullptr), - clear_(nullptr), - clr_preset_var1_(LogicValue::unknown), - clr_preset_var2_(LogicValue::unknown), - line_(line) -{ -} - -SequentialGroup::~SequentialGroup() -{ - if (clk_) - stringDelete(clk_); - if (data_) - stringDelete(data_); - if (preset_) - stringDelete(preset_); - if (clear_) - stringDelete(clear_); + return libertyReaderFindPort(cell, port_name); } -void -SequentialGroup::setClock(const char *clk) +float +LibertyReader::defaultCap(LibertyPort *port) { - clk_ = clk; + PortDirection *dir = port->direction(); + float cap = 0.0; + if (dir->isInput()) + cap = library_->defaultInputPinCap(); + else if (dir->isOutput() + || dir->isTristate()) + cap = library_->defaultOutputPinCap(); + else if (dir->isBidirect()) + cap = library_->defaultBidirectPinCap(); + return cap; } -void -SequentialGroup::setData(const char *data) -{ - data_ = data; -} +//////////////////////////////////////////////////////////////// -void -SequentialGroup::setClear(const char *clr) -{ - clear_ = clr; -} +static EnumNameMap state_input_value_name_map = + {{StateInputValue::low, "L"}, + {StateInputValue::high, "H"}, + {StateInputValue::dont_care, "-"}, + {StateInputValue::low_high, "L/H"}, + {StateInputValue::high_low, "H/L"}, + {StateInputValue::rise, "R"}, + {StateInputValue::fall, "F"}, + {StateInputValue::not_rise, "~R"}, + {StateInputValue::not_fall, "~F"} + }; -void -SequentialGroup::setPreset(const char *preset) -{ - preset_ = preset; -} +static EnumNameMap state_internal_value_name_map = + {{StateInternalValue::low, "L"}, + {StateInternalValue::high, "H"}, + {StateInternalValue::unspecified, "-"}, + {StateInternalValue::low_high, "L/H"}, + {StateInternalValue::high_low, "H/L"}, + {StateInternalValue::unknown, "X"}, + {StateInternalValue::hold, "N"} + }; -void -SequentialGroup::setClrPresetVar1(LogicValue var) +StateInputValues +LibertyReader::parseStateInputValues(StdStringSeq &inputs, + const LibertySimpleAttr *attr) { - clr_preset_var1_ = var; + StateInputValues input_values; + for (std::string input : inputs) { + bool exists; + 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()); + value = StateInputValue::dont_care; + } + input_values.push_back(value); + } + return input_values; } -void -SequentialGroup::setClrPresetVar2(LogicValue var) +StateInternalValues +LibertyReader::parseStateInternalValues(StdStringSeq &states, + const LibertySimpleAttr *attr) { - clr_preset_var2_ = var; + StateInternalValues state_values; + for (std::string state : states) { + bool exists; + 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()); + value = StateInternalValue::unknown; + } + state_values.push_back(value); + } + return state_values; } //////////////////////////////////////////////////////////////// -StatetableGroup::StatetableGroup(StdStringSeq &input_ports, - StdStringSeq &internal_ports, - int line) : - input_ports_(input_ports), - internal_ports_(internal_ports), - line_(line) -{ -} - -void -StatetableGroup::addRow(StateInputValues &input_values, - StateInternalValues ¤t_values, - StateInternalValues &next_values) +FloatTable +LibertyReader::makeFloatTable(const LibertyComplexAttr *values_attr, + const LibertyGroup *table_group, + size_t rows, + size_t cols, + float scale) { - table_.emplace_back(input_values, current_values, next_values); + FloatTable table; + table.reserve(rows); + for (const LibertyAttrValue *value : values_attr->values()) { + FloatSeq row; + row.reserve(cols); + if (value->isString()) + row = parseStringFloatList(value->stringValue(), scale, 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()); + if (row.size() != cols) { + libWarn(1259, values_attr, "%s row has %zu columns but axis has %zu.", + table_group->type().c_str(), + row.size(), + cols); + for (size_t c = row.size(); c < cols; c++) + row.push_back(0.0); + } + table.push_back(std::move(row)); + } + if (table.size() != rows) { + if (rows == 0) + libWarn(1260, values_attr, "%s missing axis values.", + table_group->type().c_str()); + else + libWarn(1261, values_attr, "%s has %zu rows but axis has %zu.", + table_group->type().c_str(), + table.size(), + rows); + for (size_t r = table.size(); r < rows; r++) { + FloatSeq row(cols, 0.0); + table.push_back(std::move(row)); + } + } + return table; } //////////////////////////////////////////////////////////////// -RelatedPortGroup::RelatedPortGroup(int line) : - related_port_names_(nullptr), - line_(line) +void +LibertyReader::getAttrInt(const LibertySimpleAttr *attr, + // Return values. + int &value, + bool &exists) { + value = 0; + exists = false; + const LibertyAttrValue &attr_value = attr->value(); + if (attr_value.isFloat()) { + float float_val = attr_value.floatValue(); + value = static_cast(float_val); + exists = true; + } + else + libWarn(1268, attr, "%s attribute is not an integer.",attr->name().c_str()); } -RelatedPortGroup::~RelatedPortGroup() +// Get two floats in a complex attribute. +// attr(float1, float2); +void +LibertyReader::getAttrFloat2(const LibertyComplexAttr *attr, + // Return values. + float &value1, + float &value2, + bool &exists) { - if (related_port_names_) { - deleteContents(related_port_names_); - delete related_port_names_; + exists = false; + const LibertyAttrValueSeq &values = attr->values(); + if (values.size() == 2) { + LibertyAttrValue *value = values[0]; + getAttrFloat(attr, value, value1, exists); + if (!exists) + libWarn(1272, attr, "%s is not a float.", attr->name().c_str()); + + value = values[1]; + getAttrFloat(attr, value, value2, exists); + if (!exists) + libWarn(1273, attr, "%s is not a float.", attr->name().c_str()); } + else + libWarn(1274, attr, "%s requires 2 valules.", attr->name().c_str()); } void -RelatedPortGroup::setRelatedPortNames(StringSeq *names) +LibertyReader::getAttrFloat(const LibertyComplexAttr *attr, + const LibertyAttrValue *attr_value, + // Return values. + float &value, + bool &valid) { - related_port_names_ = names; + if (attr_value->isFloat()) { + valid = true; + value = attr_value->floatValue(); + } + else if (attr_value->isString()) { + const std::string &str = attr_value->stringValue(); + variableValue(str.c_str(), value, valid); + if (!valid) { + char *end; + 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()); + valid = true; + } + } } -void -RelatedPortGroup::setIsOneToOne(bool one) +// Parse string of comma separated floats. +// Note that some brain damaged vendors (that used to "Think") are not +// consistent about including the delimiters. +FloatSeq +LibertyReader::parseStringFloatList(const std::string &float_list, + float scale, + const LibertySimpleAttr *attr) { - is_one_to_one_ = one; -} - -//////////////////////////////////////////////////////////////// - -TimingGroup::TimingGroup(int line) : - RelatedPortGroup(line), - attrs_(make_shared()), - related_output_port_name_(nullptr), - receiver_model_(nullptr) -{ - for (auto rf_index : RiseFall::rangeIndex()) { - cell_[rf_index] = nullptr; - constraint_[rf_index] = nullptr; - transition_[rf_index] = nullptr; - intrinsic_[rf_index] = 0.0F; - intrinsic_exists_[rf_index] = false; - resistance_[rf_index] = 0.0F; - resistance_exists_[rf_index] = false; - output_waveforms_[rf_index] = nullptr; - - for (auto el_index : EarlyLate::rangeIndex()) { - delay_sigma_[rf_index][el_index] = nullptr; - slew_sigma_[rf_index][el_index] = nullptr; - constraint_sigma_[rf_index][el_index] = nullptr; + FloatSeq values; + values.reserve(std::max(10, float_list.size() / 5)); + const char *token = float_list.c_str(); + while (*token != '\0') { + // Some (brain dead) libraries enclose floats in brackets. + if (*token == '{') + token++; + char *end; + float value = strtof(token, &end) * scale; + if (end == token + || !(*end == '\0' + || isspace(*end) + || *end == ',' + || *end == '}')) { + std::string token_end = token; + if (end != token) { + token_end.clear(); + for (const char *t = token; t <= end; t++) + token_end += *t; + } + libWarn(1310, attr, "%s is not a float.", token_end.c_str()); + token += token_end.size(); + } + else { + values.push_back(value); + token = end; } + while (*token == ',' || *token == ' ' || *token == '}') + token++; } + return values; } -TimingGroup::~TimingGroup() +FloatSeq +LibertyReader::parseStringFloatList(const std::string &float_list, + float scale, + const LibertyComplexAttr *attr) { - if (related_output_port_name_) - stringDelete(related_output_port_name_); + FloatSeq values; + values.reserve(std::max(10, float_list.size() / 5)); + const char *token = float_list.c_str(); + while (*token != '\0') { + if (*token == '{') + token++; + char *end; + float value = strtof(token, &end) * scale; + if (end == token + || !(*end == '\0' + || isspace(*end) + || *end == ',' + || *end == '}')) { + std::string token_end = token; + if (end != token) { + token_end.clear(); + for (const char *t = token; t <= end; t++) + token_end += *t; + } + libWarn(1275, attr, "%s is not a float.", token_end.c_str()); + token += token_end.size(); + } + else { + values.push_back(value); + token = end; + } + while (*token == ',' || *token == ' ' || *token == '}') + token++; + } + return values; } -void -TimingGroup::setRelatedOutputPortName(const char *name) +FloatSeq +LibertyReader::readFloatSeq(const LibertyComplexAttr *attr, + float scale) { - related_output_port_name_ = stringCopy(name); + FloatSeq values; + const LibertyAttrValueSeq &attr_values = attr->values(); + if (attr_values.size() == 1) { + LibertyAttrValue *value = attr_values[0]; + if (value->isString()) { + values = parseStringFloatList(value->stringValue(), scale, attr); + } + else if (value->isFloat()) { + values.push_back(value->floatValue() * scale); + } + else + libWarn(1276, attr, "%s is missing values.", attr->name().c_str()); + } + else if (attr_values.size() > 1) { + for (LibertyAttrValue *val : attr_values) { + if (val->isFloat()) + values.push_back(val->floatValue() * scale); + else if (val->isString()) { + FloatSeq parsed = parseStringFloatList(val->stringValue(), scale, attr); + values.insert(values.end(), parsed.begin(), parsed.end()); + } + } + } + else + libWarn(1277, attr, "%s has no values.", attr->name().c_str()); + return values; } -void -TimingGroup::setIntrinsic(const RiseFall *rf, - float value) -{ - int rf_index = rf->index(); - intrinsic_[rf_index] = value; - intrinsic_exists_[rf_index] = true; -} +//////////////////////////////////////////////////////////////// void -TimingGroup::intrinsic(const RiseFall *rf, - // Return values. - float &value, - bool &exists) +LibertyReader::getAttrBool(const LibertySimpleAttr *attr, + // Return values. + bool &value, + bool &exists) { - int rf_index = rf->index(); - value = intrinsic_[rf_index]; - exists = intrinsic_exists_[rf_index]; + exists = false; + const LibertyAttrValue &val = attr->value(); + if (val.isString()) { + const std::string &str = val.stringValue(); + if (stringEqual(str.c_str(), "true")) { + value = true; + exists = true; + } + else if (stringEqual(str.c_str(), "false")) { + value = false; + exists = true; + } + else + libWarn(1288, attr, "%s attribute is not boolean.", attr->name().c_str()); + } + else + libWarn(1289, attr, "%s attribute is not boolean.", attr->name().c_str()); } -void -TimingGroup::setResistance(const RiseFall *rf, - float value) +// Read L/H/X string attribute values as bool. +LogicValue +LibertyReader::getAttrLogicValue(const LibertySimpleAttr *attr) { - int rf_index = rf->index(); - resistance_[rf_index] = value; - resistance_exists_[rf_index] = true; + const std::string *str = attr->stringValue(); + if (str) { + if (*str == "L") + return LogicValue::zero; + else if (*str == "H") + return LogicValue::one; + else if (*str == "X") + return LogicValue::unknown; + else + libWarn(1282, attr, "attribute %s value %s not recognized.", + attr->name().c_str(), str->c_str()); + // fall thru + } + return LogicValue::unknown; } -void -TimingGroup::resistance(const RiseFall *rf, - // Return values. - float &value, - bool &exists) +const EarlyLateAll * +LibertyReader::getAttrEarlyLate(const LibertySimpleAttr *attr) { - int rf_index = rf->index(); - value = resistance_[rf_index]; - exists = resistance_exists_[rf_index]; + const std::string *value = attr->stringValue(); + if (*value == "early") + return EarlyLateAll::early(); + else if (*value == "late") + return EarlyLateAll::late(); + else if (*value == "early_and_late") + return EarlyLateAll::all(); + else { + libWarn(1283, attr, "unknown early/late value."); + return EarlyLateAll::all(); + } } -TableModel * -TimingGroup::cell(const RiseFall *rf) -{ - return cell_[rf->index()]; -} +//////////////////////////////////////////////////////////////// -void -TimingGroup::setCell(const RiseFall *rf, - TableModel *model) +FuncExpr * +LibertyReader::parseFunc(const char *func, + const char *attr_name, + const LibertyCell *cell, + int line) { - cell_[rf->index()] = model; + std::string error_msg; + stringPrint(error_msg, "%s, line %d %s", + filename_, + line, + attr_name); + return parseFuncExpr(func, cell, error_msg.c_str(), report_); } -TableModel * -TimingGroup::constraint(const RiseFall *rf) -{ - return constraint_[rf->index()]; -} +//////////////////////////////////////////////////////////////// void -TimingGroup::setConstraint(const RiseFall *rf, - TableModel *model) -{ - constraint_[rf->index()] = model; -} - -TableModel * -TimingGroup::transition(const RiseFall *rf) +LibertyReader::visitVariable(LibertyVariable *var) { - return transition_[rf->index()]; + const std::string &var_name = var->variable(); + float value; + bool exists; + findKeyValue(var_map_, var_name, value, exists); + var_map_[var_name] = var->value(); } void -TimingGroup::setTransition(const RiseFall *rf, - TableModel *model) +LibertyReader::variableValue(const char *var, + float &value, + bool &exists) { - transition_[rf->index()] = model; + findKeyValue(var_map_, var, value, exists); } -void -TimingGroup::setDelaySigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model) -{ - delay_sigma_[rf->index()][early_late->index()] = model; -} +//////////////////////////////////////////////////////////////// void -TimingGroup::setSlewSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model) +LibertyReader::libWarn(int id, + const LibertyGroup *obj, + const char *fmt, + ...) { - slew_sigma_[rf->index()][early_late->index()] = model; + va_list args; + va_start(args, fmt); + report_->vfileWarn(id, filename_, obj->line(), fmt, args); + va_end(args); } void -TimingGroup::setConstraintSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model) +LibertyReader::libWarn(int id, + const LibertySimpleAttr *obj, + const char *fmt, + ...) { - constraint_sigma_[rf->index()][early_late->index()] = model; + va_list args; + va_start(args, fmt); + report_->vfileWarn(id, filename_, obj->line(), fmt, args); + va_end(args); } void -TimingGroup::setReceiverModel(ReceiverModelPtr receiver_model) -{ - receiver_model_ = receiver_model; -} - -OutputWaveforms * -TimingGroup::outputWaveforms(const RiseFall *rf) +LibertyReader::libWarn(int id, + const LibertyComplexAttr *obj, + const char *fmt, + ...) { - return output_waveforms_[rf->index()]; + va_list args; + va_start(args, fmt); + report_->vfileWarn(id, filename_, obj->line(), fmt, args); + va_end(args); } void -TimingGroup::setOutputWaveforms(const RiseFall *rf, - OutputWaveforms *output_waveforms) -{ - output_waveforms_[rf->index()] = output_waveforms; -} - -//////////////////////////////////////////////////////////////// - -InternalPowerGroup::InternalPowerGroup(int line) : - RelatedPortGroup(line), - when_(), - models_{} +LibertyReader::libWarn(int id, + int line, + const char *fmt, + ...) { + va_list args; + va_start(args, fmt); + report_->vfileWarn(id, filename_, line, fmt, args); + va_end(args); } void -InternalPowerGroup::setWhen(std::shared_ptr when) +LibertyReader::libError(int id, + const LibertyGroup *obj, + const char *fmt, + ...) { - when_ = std::move(when); + va_list args; + va_start(args, fmt); + report_->vfileError(id, filename_, obj->line(), fmt, args); + va_end(args); } void -InternalPowerGroup::setModel(const RiseFall *rf, - std::shared_ptr model) +LibertyReader::libError(int id, + const LibertySimpleAttr *obj, + const char *fmt, + ...) { - models_[rf->index()] = std::move(model); + va_list args; + va_start(args, fmt); + report_->vfileError(id, filename_, obj->line(), fmt, args); + va_end(args); } void -InternalPowerGroup::setRelatedPgPin(std::string related_pg_pin) +LibertyReader::libError(int id, + const LibertyComplexAttr *obj, + const char *fmt, + ...) { - related_pg_pin_ = std::move(related_pg_pin); + va_list args; + va_start(args, fmt); + report_->vfileError(id, filename_, obj->line(), fmt, args); + va_end(args); } //////////////////////////////////////////////////////////////// -LeakagePowerGroup::LeakagePowerGroup(int line) : - when_(nullptr), - power_(0.0), - line_(line) -{ -} - void -LeakagePowerGroup::setRelatedPgPin(std::string pin_name) +LibertyReader::readDefaultOcvDerateGroup(const LibertyGroup *library_group) { - related_pg_pin_ = std::move(pin_name); + const std::string *derate_name = + library_group->findAttrString("default_ocv_derate_group"); + if (derate_name) { + OcvDerate *derate = library_->findOcvDerate(derate_name->c_str()); + if (derate) + library_->setDefaultOcvDerate(derate); + else + libWarn(1284, library_group, "OCV derate group named %s not found.", + derate_name->c_str()); + } } +// Read cell or library level ocv_derate groups. void -LeakagePowerGroup::setWhen(FuncExpr *when) +LibertyReader::readOcvDerateFactors(LibertyCell *cell, + const LibertyGroup *parent_group) { - when_ = when; -} + for (const LibertyGroup *ocv_derate_group : + parent_group->findSubgroups("ocv_derate")) { + const char *name = ocv_derate_group->firstName(); + if (name) { + OcvDerate *ocv_derate = cell + ? cell->makeOcvDerate(name) + : library_->makeOcvDerate(name); + for (const LibertyGroup *factors_group : + ocv_derate_group->findSubgroups("ocv_derate_factors")) { + const RiseFallBoth *rf_type = RiseFallBoth::riseFall(); + const std::string *rf_attr = factors_group->findAttrString("rf_type"); + if (rf_attr) { + if (*rf_attr == "rise") + rf_type = RiseFallBoth::rise(); + else if (*rf_attr == "fall") + rf_type = RiseFallBoth::fall(); + else if (*rf_attr == "rise_and_fall") + rf_type = RiseFallBoth::riseFall(); + else + libError(1286, factors_group, "unknown rise/fall."); + } -void -LeakagePowerGroup::setPower(float power) -{ - power_ = power; + const EarlyLateAll *derate_type = EarlyLateAll::all(); + const std::string *derate_attr = factors_group->findAttrString("derate_type"); + if (derate_attr) { + if (*derate_attr == "early") + derate_type = EarlyLateAll::early(); + else if (*derate_attr == "late") + derate_type = EarlyLateAll::late(); + else if (*derate_attr == "early_and_late") + derate_type = EarlyLateAll::all(); + else { + libWarn(1309, factors_group, "unknown early/late value."); + } + } + + PathType path_type = PathType::clk_and_data; + const std::string *path_attr = factors_group->findAttrString("path_type"); + if (path_attr) { + if (*path_attr == "clock") + path_type = PathType::clk; + else if (*path_attr == "data") + path_type = PathType::data; + else if (*path_attr == "clock_and_data") + path_type = PathType::clk_and_data; + else + libWarn(1287, factors_group, "unknown derate type."); + } + + const char *template_name = factors_group->firstName(); + if (template_name) { + TableTemplate *tbl_template = + library_->findTableTemplate(template_name, TableTemplateType::ocv); + if (tbl_template) { + TablePtr table = readTableModel(factors_group, tbl_template, 1.0F); + if (table) { + for (const EarlyLate *early_late : derate_type->range()) { + for (const RiseFall *rf : rf_type->range()) { + if (path_type == PathType::clk_and_data) { + ocv_derate->setDerateTable(rf, early_late, PathType::clk, table); + ocv_derate->setDerateTable(rf, early_late, PathType::data, table); + } + else + ocv_derate->setDerateTable(rf, early_late, path_type, table); + } + } + } + } + else + libWarn(1308, factors_group, "table template %s not found.", template_name); + } + } + } + else + libWarn(1285, ocv_derate_group, "ocv_derate missing name."); + } } //////////////////////////////////////////////////////////////// @@ -6121,7 +3655,7 @@ PortNameBitIterator::PortNameBitIterator(LibertyCell *cell, void PortNameBitIterator::init(const char *port_name) { - LibertyPort *port = visitor_->findPort(port_name); + LibertyPort *port = visitor_->findPort(cell_, port_name); if (port) { if (port->isBus()) bit_iterator_ = new LibertyPortMemberIterator(port); @@ -6133,13 +3667,13 @@ PortNameBitIterator::init(const char *port_name) // Check for bus range. LibertyLibrary *library = visitor_->library(); bool is_bus, is_range, subscript_wild; - string bus_name; + std::string bus_name; int from, to; parseBusName(port_name, library->busBrktLeft(), library->busBrktRight(), '\\', is_bus, is_range, bus_name, from, to, subscript_wild); if (is_range) { - port = visitor_->findPort(port_name); + port = visitor_->findPort(cell_, port_name); if (port) { if (port->isBus()) { if (port->busIndexInRange(from) @@ -6224,13 +3758,9 @@ PortNameBitIterator::findRangeBusNameNext() ? range_bit_ >= range_to_ : range_bit_ <= range_to_) { LibertyLibrary *library = visitor_->library(); - string bus_bit_name; - stringPrint(bus_bit_name, "%s%c%d%c", - range_bus_name_.c_str(), - library->busBrktLeft(), - range_bit_, - library->busBrktRight()); - range_name_next_ = visitor_->findPort(bus_bit_name.c_str()); + std::string bus_bit_name = range_bus_name_ + library->busBrktLeft() + + std::to_string(range_bit_) + library->busBrktRight(); + range_name_next_ = visitor_->findPort(cell_, bus_bit_name.c_str()); if (range_name_next_) { if (range_from_ > range_to_) range_bit_--; @@ -6257,17 +3787,10 @@ OutputWaveform::OutputWaveform(float slew, { } -OutputWaveform::~OutputWaveform() -{ - delete currents_; -} - Table * -OutputWaveform::stealCurrents() +OutputWaveform::releaseCurrents() { - Table *currents = currents_; - currents_ = nullptr; - return currents; + return currents_.release(); } } // namespace diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index b4a8e6044..91fdf2603 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -47,33 +48,17 @@ namespace sta { class LibertyBuilder; class LibertyReader; -class LibertyFunc; -class PortGroup; -class SequentialGroup; -class StatetableGroup; -class RelatedPortGroup; -class TimingGroup; -class InternalPowerGroup; -class LeakagePowerGroup; class PortNameBitIterator; class TimingArcBuilder; -class LibertyAttr; class OutputWaveform; -using LibraryAttrVisitor = void (LibertyReader::*)(LibertyAttr *attr); -using LibraryGroupVisitor = void (LibertyReader::*)(LibertyGroup *group); -using LibraryAttrMap = std::unordered_map; -using LibraryGroupMap = std::unordered_map; -using PortGroupSeq = std::vector; -using SequentialGroupSeq = std::vector; -using LibertyFuncSeq = std::vector; -using TimingGroupSeq = std::vector; -using InternalPowerGroupSeq = std::vector; -using LeakagePowerGroupSeq = std::vector; -using LibertyPortBoolSetter = void (LibertyPort::*)(bool value); -using OutputWaveformSeq = std::vector; +using LibraryGroupVisitor = void (LibertyReader::*)(const LibertyGroup *group, + LibertyGroup *parent_group); +using LibraryGroupVisitorMap = std::unordered_map; using StdStringSeq = std::vector; -using LibertySetFunc = std::function; +using LibertyPortGroupMap = std::map; +using OutputWaveformSeq = std::vector; class LibertyReader : public LibertyGroupVisitor { @@ -81,428 +66,124 @@ public: LibertyReader(const char *filename, bool infer_latches, Network *network); - virtual ~LibertyReader(); virtual LibertyLibrary *readLibertyFile(const char *filename); LibertyLibrary *library() { return library_; } const LibertyLibrary *library() const { return library_; } - virtual void init(const char *filename, - bool infer_latches, - Network *network); - virtual bool save(LibertyGroup *) { return false; } - virtual bool save(LibertyAttr *) { return false; } - virtual bool save(LibertyVariable *) { return false; } - - virtual void beginLibrary(LibertyGroup *group); - virtual void endLibrary(LibertyGroup *group); - virtual void endLibraryAttrs(LibertyGroup *group); - virtual void visitAttr(LibertyAttr *attr); - virtual void visitTimeUnit(LibertyAttr *attr); - virtual void visitCapacitiveLoadUnit(LibertyAttr *attr); - virtual void visitResistanceUnit(LibertyAttr *attr); - virtual void visitPullingResistanceUnit(LibertyAttr *attr); - virtual void visitVoltageUnit(LibertyAttr *attr); - virtual void visitCurrentUnit(LibertyAttr *attr); - virtual void visitPowerUnit(LibertyAttr *attr); - virtual void visitDistanceUnit(LibertyAttr *attr); - virtual void parseUnits(LibertyAttr *attr, - const char *suffix, - float &scale_var, - Unit *unit_suffix); - virtual void visitDelayModel(LibertyAttr *attr); - virtual void visitVoltageMap(LibertyAttr *attr); - virtual void visitBusStyle(LibertyAttr *attr); - virtual void visitNomTemp(LibertyAttr *attr); - virtual void visitNomVolt(LibertyAttr *attr); - virtual void visitNomProc(LibertyAttr *attr); - virtual void visitDefaultInoutPinCap(LibertyAttr *attr); - virtual void visitDefaultInputPinCap(LibertyAttr *attr); - virtual void visitDefaultOutputPinCap(LibertyAttr *attr); - virtual void visitDefaultMaxTransition(LibertyAttr *attr); - virtual void visitDefaultMaxFanout(LibertyAttr *attr); - virtual void visitDefaultIntrinsicRise(LibertyAttr *attr); - virtual void visitDefaultIntrinsicFall(LibertyAttr *attr); - virtual void visitDefaultIntrinsic(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitDefaultInoutPinRiseRes(LibertyAttr *attr); - virtual void visitDefaultInoutPinFallRes(LibertyAttr *attr); - virtual void visitDefaultInoutPinRes(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitDefaultOutputPinRiseRes(LibertyAttr *attr); - virtual void visitDefaultOutputPinFallRes(LibertyAttr *attr); - virtual void visitDefaultOutputPinRes(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitDefaultFanoutLoad(LibertyAttr *attr); - virtual void visitDefaultWireLoad(LibertyAttr *attr); - virtual void visitDefaultWireLoadMode(LibertyAttr *attr); - virtual void visitDefaultWireLoadSelection(LibertyAttr *attr); - virtual void visitDefaultOperatingConditions(LibertyAttr *attr); - virtual void visitInputThresholdPctFall(LibertyAttr *attr); - virtual void visitInputThresholdPctRise(LibertyAttr *attr); - virtual void visitInputThresholdPct(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitOutputThresholdPctFall(LibertyAttr *attr); - virtual void visitOutputThresholdPctRise(LibertyAttr *attr); - virtual void visitOutputThresholdPct(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitSlewLowerThresholdPctFall(LibertyAttr *attr); - virtual void visitSlewLowerThresholdPctRise(LibertyAttr *attr); - virtual void visitSlewLowerThresholdPct(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitSlewUpperThresholdPctFall(LibertyAttr *attr); - virtual void visitSlewUpperThresholdPctRise(LibertyAttr *attr); - virtual void visitSlewUpperThresholdPct(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitSlewDerateFromLibrary(LibertyAttr *attr); - - virtual void beginTechnology(LibertyGroup *group); - virtual void endTechnology(LibertyGroup *group); - virtual void beginTableTemplateDelay(LibertyGroup *group); - virtual void beginTableTemplateOutputCurrent(LibertyGroup *group); - virtual void beginTableTemplate(LibertyGroup *group, - TableTemplateType type); - virtual void endTableTemplate(LibertyGroup *group); - virtual void visitVariable1(LibertyAttr *attr); - virtual void visitVariable2(LibertyAttr *attr); - virtual void visitVariable3(LibertyAttr *attr); - virtual void visitIndex1(LibertyAttr *attr); - virtual void visitIndex2(LibertyAttr *attr); - virtual void visitIndex3(LibertyAttr *attr); - - virtual void beginType(LibertyGroup *group); - virtual void endType(LibertyGroup *group); - virtual void visitBitFrom(LibertyAttr *attr); - virtual void visitBitTo(LibertyAttr *attr); - - virtual void beginCell(LibertyGroup *group); - virtual void endCell(LibertyGroup *group); - virtual void beginScaledCell(LibertyGroup *group); - virtual void endScaledCell(LibertyGroup *group); - virtual void checkScaledCell(LibertyGroup *group); - virtual void finishPortGroups(); - virtual void checkPort(LibertyPort *port, - int line); - virtual void makeTimingArcs(PortGroup *port_group); - virtual void makeInternalPowers(PortGroup *port_group); - virtual void makeCellSequentials(); - virtual void makeCellSequential(SequentialGroup *seq); - virtual void makeStatetable(); - virtual void makeLeakagePowers(); - virtual void parseCellFuncs(); - virtual void makeLibertyFunc(const char *expr, - LibertySetFunc set_func, - bool invert, - const char *attr_name, - LibertyStmt *stmt); - virtual void makeTimingArcs(LibertyPort *to_port, - TimingGroup *timing); - virtual void makeTimingArcs(const char *from_port_name, - PortNameBitIterator &from_port_iter, - LibertyPort *to_port, - LibertyPort *related_out_port, - TimingGroup *timing); - virtual void makeTimingArcs(LibertyPort *to_port, - LibertyPort *related_out_port, - TimingGroup *timing); - - virtual void visitClockGatingIntegratedCell(LibertyAttr *attr); - virtual void visitArea(LibertyAttr *attr); - virtual void visitDontUse(LibertyAttr *attr); - virtual void visitIsMacro(LibertyAttr *attr); - virtual void visitIsMemory(LibertyAttr *attr); - virtual void visitIsPadCell(LibertyAttr *attr); - virtual void visitIsPad(LibertyAttr *attr); - virtual void visitIsClockCell(LibertyAttr *attr); - virtual void visitIsLevelShifter(LibertyAttr *attr); - virtual void visitLevelShifterType(LibertyAttr *attr); - virtual void visitIsIsolationCell(LibertyAttr *attr); - virtual void visitAlwaysOn(LibertyAttr *attr); - virtual void visitSwitchCellType(LibertyAttr *attr); - virtual void visitInterfaceTiming(LibertyAttr *attr); - virtual void visitScalingFactors(LibertyAttr *attr); - virtual void visitCellLeakagePower(LibertyAttr *attr); - virtual void visitCellFootprint(LibertyAttr *attr); - virtual void visitCellUserFunctionClass(LibertyAttr *attr); - - virtual void beginPin(LibertyGroup *group); - virtual void endPin(LibertyGroup *group); - virtual void beginBus(LibertyGroup *group); - virtual void endBus(LibertyGroup *group); - virtual void beginBundle(LibertyGroup *group); - virtual void endBundle(LibertyGroup *group); - virtual void beginBusOrBundle(LibertyGroup *group); - virtual void endBusOrBundle(); - virtual void endPorts(); - virtual void setPortCapDefault(LibertyPort *port); - virtual void visitMembers(LibertyAttr *attr); - virtual void visitDirection(LibertyAttr *attr); - virtual void visitFunction(LibertyAttr *attr); - virtual void visitThreeState(LibertyAttr *attr); - virtual void visitBusType(LibertyAttr *attr); - virtual void visitCapacitance(LibertyAttr *attr); - virtual void visitRiseCap(LibertyAttr *attr); - virtual void visitFallCap(LibertyAttr *attr); - virtual void visitRiseCapRange(LibertyAttr *attr); - virtual void visitFallCapRange(LibertyAttr *attr); - virtual void visitFanoutLoad(LibertyAttr *attr); - virtual void visitMaxFanout(LibertyAttr *attr); - virtual void visitMinFanout(LibertyAttr *attr); - virtual void visitFanout(LibertyAttr *attr, - const MinMax *min_max); - virtual void visitMaxTransition(LibertyAttr *attr); - virtual void visitMinTransition(LibertyAttr *attr); - virtual void visitMinMaxTransition(LibertyAttr *attr, - const MinMax *min_max); - virtual void visitMaxCapacitance(LibertyAttr *attr); - virtual void visitMinCapacitance(LibertyAttr *attr); - virtual void visitMinMaxCapacitance(LibertyAttr *attr, - const MinMax *min_max); - virtual void visitMinPeriod(LibertyAttr *attr); - virtual void visitMinPulseWidthLow(LibertyAttr *attr); - virtual void visitMinPulseWidthHigh(LibertyAttr *attr); - virtual void visitMinPulseWidth(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitPulseClock(LibertyAttr *attr); - virtual void visitClockGateClockPin(LibertyAttr *attr); - virtual void visitClockGateEnablePin(LibertyAttr *attr); - virtual void visitClockGateOutPin(LibertyAttr *attr); - void visitIsPllFeedbackPin(LibertyAttr *attr); - virtual void visitSignalType(LibertyAttr *attr); - const EarlyLateAll *getAttrEarlyLate(LibertyAttr *attr); - virtual void visitClock(LibertyAttr *attr); - virtual void visitIsolationCellDataPin(LibertyAttr *attr); - virtual void visitIsolationCellEnablePin(LibertyAttr *attr); - virtual void visitLevelShifterDataPin(LibertyAttr *attr); - virtual void visitSwitchPin(LibertyAttr *attr); - void visitPortBoolAttr(LibertyAttr *attr, - LibertyPortBoolSetter setter); - - virtual void beginScalingFactors(LibertyGroup *group); - virtual void endScalingFactors(LibertyGroup *group); - virtual void defineScalingFactorVisitors(); - virtual void visitScaleFactorSuffix(LibertyAttr *attr); - virtual void visitScaleFactorPrefix(LibertyAttr *attr); - virtual void visitScaleFactorHiLow(LibertyAttr *attr); - virtual void visitScaleFactor(LibertyAttr *attr); - - virtual void beginOpCond(LibertyGroup *group); - virtual void endOpCond(LibertyGroup *group); - virtual void visitProc(LibertyAttr *attr); - virtual void visitVolt(LibertyAttr *attr); - virtual void visitTemp(LibertyAttr *attr); - virtual void visitTreeType(LibertyAttr *attr); - - virtual void beginWireload(LibertyGroup *group); - virtual void endWireload(LibertyGroup *group); - virtual void visitResistance(LibertyAttr *attr); - virtual void visitSlope(LibertyAttr *attr); - virtual void visitFanoutLength(LibertyAttr *attr); - - virtual void beginWireloadSelection(LibertyGroup *group); - virtual void endWireloadSelection(LibertyGroup *group); - virtual void visitWireloadFromArea(LibertyAttr *attr); - - virtual void beginMemory(LibertyGroup *group); - virtual void endMemory(LibertyGroup *group); - - virtual void beginFF(LibertyGroup *group); - virtual void endFF(LibertyGroup *group); - virtual void beginFFBank(LibertyGroup *group); - virtual void endFFBank(LibertyGroup *group); - virtual void beginLatch(LibertyGroup *group); - virtual void endLatch(LibertyGroup *group); - virtual void beginLatchBank(LibertyGroup *group); - virtual void endLatchBank(LibertyGroup *group); - virtual void beginSequential(LibertyGroup *group, - bool is_register, - bool is_bank); - virtual void seqPortNames(LibertyGroup *group, - const char *&out_name, - const char *&out_inv_name, - bool &has_size, - int &size); - virtual void checkLatchEnableSense(FuncExpr *enable_func, - int line); - virtual void visitClockedOn(LibertyAttr *attr); - virtual void visitDataIn(LibertyAttr *attr); - virtual void visitClear(LibertyAttr *attr); - virtual void visitPreset(LibertyAttr *attr); - virtual void visitClrPresetVar1(LibertyAttr *attr); - virtual void visitClrPresetVar2(LibertyAttr *attr); - - virtual void beginStatetable(LibertyGroup *group); - virtual void endStatetable(LibertyGroup *group); - virtual void visitTable(LibertyAttr *attr); - - virtual void beginTiming(LibertyGroup *group); - virtual void endTiming(LibertyGroup *group); - virtual void visitRelatedPin(LibertyAttr *attr); - virtual void visitRelatedPin(LibertyAttr *attr, - RelatedPortGroup *group); - virtual void visitRelatedBusPins(LibertyAttr *attr); - virtual void visitRelatedBusPins(LibertyAttr *attr, - RelatedPortGroup *group); - virtual void visitRelatedOutputPin(LibertyAttr *attr); - virtual void visitTimingType(LibertyAttr *attr); - virtual void visitTimingSense(LibertyAttr *attr); - virtual void visitSdfCondStart(LibertyAttr *attr); - virtual void visitSdfCondEnd(LibertyAttr *attr); - virtual void visitMode(LibertyAttr *attr); - virtual void visitIntrinsicRise(LibertyAttr *attr); - virtual void visitIntrinsicFall(LibertyAttr *attr); - virtual void visitIntrinsic(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitRiseResistance(LibertyAttr *attr); - virtual void visitFallResistance(LibertyAttr *attr); - virtual void visitRiseFallResistance(LibertyAttr *attr, - const RiseFall *rf); - virtual void visitValue(LibertyAttr *attr); - virtual void visitValues(LibertyAttr *attr); - virtual void beginCellRise(LibertyGroup *group); - virtual void beginCellFall(LibertyGroup *group); - virtual void endCellRiseFall(LibertyGroup *group); - virtual void beginRiseTransition(LibertyGroup *group); - virtual void endRiseFallTransition(LibertyGroup *group); - virtual void beginFallTransition(LibertyGroup *group); - virtual void beginRiseConstraint(LibertyGroup *group); - virtual void endRiseFallConstraint(LibertyGroup *group); - virtual void beginFallConstraint(LibertyGroup *group); - - virtual void beginRiseTransitionDegredation(LibertyGroup *group); - virtual void beginFallTransitionDegredation(LibertyGroup *group); - virtual void endRiseFallTransitionDegredation(LibertyGroup *group); - - virtual void beginTableModel(LibertyGroup *group, - TableTemplateType type, - const RiseFall *rf, - float scale, - ScaleFactorType scale_factor_type); - virtual void endTableModel(); - virtual void beginTimingTableModel(LibertyGroup *group, - const RiseFall *rf, - ScaleFactorType scale_factor_type); - virtual void beginTable(LibertyGroup *group, - TableTemplateType type, - float scale); - virtual void endTable(); - virtual void makeTable(LibertyAttr *attr, - float scale); - virtual FloatTable makeFloatTable(LibertyAttr *attr, - size_t rows, - size_t cols, - float scale); - - virtual void beginLut(LibertyGroup *group); - virtual void endLut(LibertyGroup *group); - - virtual void beginTestCell(LibertyGroup *group); - virtual void endTestCell(LibertyGroup *group); - - virtual void beginModeDef(LibertyGroup *group); - virtual void endModeDef(LibertyGroup *group); - virtual void beginModeValue(LibertyGroup *group); - virtual void endModeValue(LibertyGroup *group); - virtual void visitWhen(LibertyAttr *attr); - virtual void visitSdfCond(LibertyAttr *attr); - - // Power attributes. - virtual void beginTableTemplatePower(LibertyGroup *group); - virtual void beginLeakagePower(LibertyGroup *group); - virtual void endLeakagePower(LibertyGroup *group); - virtual void beginInternalPower(LibertyGroup *group); - virtual void endInternalPower(LibertyGroup *group); - virtual void beginFallPower(LibertyGroup *group); - virtual void beginRisePower(LibertyGroup *group); - virtual void endRiseFallPower(LibertyGroup *group); - virtual void endPower(LibertyGroup *group); - virtual void visitRelatedGroundPin(LibertyAttr *attr); - virtual void visitRelatedPowerPin(LibertyAttr *attr); - virtual void visitRelatedPgPin(LibertyAttr *attr); - virtual void makeInternalPowers(LibertyPort *port, - InternalPowerGroup *power_group); - virtual void makeInternalPowers(LibertyPort *port, - const char *related_port_name, - PortNameBitIterator &related_port_iter, - LibertyPort *related_pg_pin, - InternalPowerGroup *power_group); - - // AOCV attributes. - virtual void beginTableTemplateOcv(LibertyGroup *group); - virtual void visitOcvArcDepth(LibertyAttr *attr); - virtual void visitDefaultOcvDerateGroup(LibertyAttr *attr); - virtual void visitOcvDerateGroup(LibertyAttr *attr); - virtual void beginOcvDerate(LibertyGroup *group); - virtual void endOcvDerate(LibertyGroup *group); - virtual void beginOcvDerateFactors(LibertyGroup *group); - virtual void endOcvDerateFactors(LibertyGroup *group); - virtual void visitRfType(LibertyAttr *attr); - virtual void visitDerateType(LibertyAttr *attr); - virtual void visitPathType(LibertyAttr *attr); - - // POCV attributes. - virtual void beginOcvSigmaCellRise(LibertyGroup *group); - virtual void beginOcvSigmaCellFall(LibertyGroup *group); - virtual void endOcvSigmaCell(LibertyGroup *group); - virtual void beginOcvSigmaRiseTransition(LibertyGroup *group); - virtual void beginOcvSigmaFallTransition(LibertyGroup *group); - virtual void endOcvSigmaTransition(LibertyGroup *group); - virtual void beginOcvSigmaRiseConstraint(LibertyGroup *group); - virtual void beginOcvSigmaFallConstraint(LibertyGroup *group); - virtual void endOcvSigmaConstraint(LibertyGroup *group); - virtual void visitSigmaType(LibertyAttr *attr); - - // PgPin group. - virtual void beginPgPin(LibertyGroup *group); - virtual void endPgPin(LibertyGroup *group); - virtual void visitPgType(LibertyAttr *attr); - virtual void visitVoltageName(LibertyAttr *attr); - - // ccs receiver capacitance - virtual void beginReceiverCapacitance(LibertyGroup *group); - virtual void endReceiverCapacitance(LibertyGroup *group); - - virtual void visitSegement(LibertyAttr *attr); - - virtual void beginReceiverCapacitance1Rise(LibertyGroup *group); - virtual void endReceiverCapacitanceRiseFall(LibertyGroup *group); - virtual void beginReceiverCapacitance1Fall(LibertyGroup *group); - virtual void beginReceiverCapacitance2Rise(LibertyGroup *group); - virtual void beginReceiverCapacitance2Fall(LibertyGroup *group); - void beginReceiverCapacitance(LibertyGroup *group, - int index, - const RiseFall *rf); - void endReceiverCapacitance(LibertyGroup *group, - int index, - const RiseFall *rf); - // ccs - void beginOutputCurrentRise(LibertyGroup *group); - void beginOutputCurrentFall(LibertyGroup *group); - void beginOutputCurrent(const RiseFall *rf, - LibertyGroup *group); - void endOutputCurrentRiseFall(LibertyGroup *group); - void beginVector(LibertyGroup *group); - void endVector(LibertyGroup *group); - void visitReferenceTime(LibertyAttr *attr); - - void beginNormalizedDriverWaveform(LibertyGroup *group); - void endNormalizedDriverWaveform(LibertyGroup *group); - void visitDriverWaveformName(LibertyAttr *attr); - - void visitDriverWaveformRise(LibertyAttr *attr); - void visitDriverWaveformFall(LibertyAttr *attr); - void visitDriverWaveformRiseFall(LibertyAttr *attr, - const RiseFall *rf); - - void beginCcsn(LibertyGroup *group); - void endCcsn(LibertyGroup *group); - void beginEcsmWaveform(LibertyGroup *group); - void endEcsmWaveform(LibertyGroup *group); + virtual void beginLibrary(const LibertyGroup *group, + LibertyGroup *library_group); + virtual void endLibrary(const LibertyGroup *group, + LibertyGroup *null_group); + virtual void visitAttr(const LibertySimpleAttr *attr); + virtual void visitAttr(const LibertyComplexAttr *attr); + virtual void visitVariable(LibertyVariable *var); + // Extension points for custom attributes (e.g. LibertyExt). + virtual void visitAttr1(const LibertySimpleAttr *) {} + virtual void visitAttr2(const LibertySimpleAttr *) {} + + void endCell(const LibertyGroup *group, + LibertyGroup *library_group); + void endScaledCell(const LibertyGroup *group, + LibertyGroup *library_group); + void checkScaledCell(LibertyCell *scaled_cell, + LibertyCell *owner, + const LibertyGroup *scaled_cell_group, + const char *op_cond_name); + + void setPortCapDefault(LibertyPort *port); + void checkLatchEnableSense(FuncExpr *enable_func, + int line); + FloatTable makeFloatTable(const LibertyComplexAttr *attr, + const LibertyGroup *table_group, + size_t rows, + size_t cols, + float scale); + LibertyPort *findPort(LibertyCell *cell, const char *port_name); - virtual void begin(LibertyGroup *group); - virtual void end(LibertyGroup *group); + StdStringSeq findAttributStrings(const LibertyGroup *group, + const char *name_attr); protected: + virtual void begin(const LibertyGroup *group, + LibertyGroup *library_group); + virtual void end(const LibertyGroup *group, + LibertyGroup *library_group); + + // Library gruops. + void makeLibrary(const LibertyGroup *libary_group); + void readLibraryAttributes(const LibertyGroup *library_group); + void readLibraryUnits(const LibertyGroup *library_group); + void readDelayModel(const LibertyGroup *library_group); + void readBusStyle(const LibertyGroup *library_group); + void readDefaultWireLoad(const LibertyGroup *library_group); + void readDefaultWireLoadMode(const LibertyGroup *library_group); + void readTechnology(const LibertyGroup *library_group); + void readDefaultWireLoadSelection(const LibertyGroup *library_group); + void readUnit(const char *unit_attr_name, + const char *unit_suffix, + float &scale_var, + Unit *unit, + const LibertyGroup *library_group); + void readBusTypes(LibertyCell *cell, + const LibertyGroup *type_group); + void readTableTemplates(const LibertyGroup *library_group); + void readTableTemplates(const LibertyGroup *library_group, + const char *group_name, + TableTemplateType type); + void readThresholds(const LibertyGroup *library_group); + TableAxisPtr makeTableTemplateAxis(const LibertyGroup *template_group, + int axis_index); + void readVoltateMaps(const LibertyGroup *library_group); + void readWireloads(const LibertyGroup *library_group); + void readWireloadSelection(const LibertyGroup *library_group); + void readOperatingConds(const LibertyGroup *library_group); + void readScaleFactors(const LibertyGroup *library_group); + void readScaleFactors(const LibertyGroup *scale_group, + ScaleFactors *scale_factors); + void readOcvDerateFactors(LibertyCell *cell, + const LibertyGroup *library_group); + void readDefaultOcvDerateGroup(const LibertyGroup *library_group); + void readNormalizedDriverWaveform(const LibertyGroup *library_group); + void readSlewDegradations(const LibertyGroup *library_group); + void readLibAttrFloat(const LibertyGroup *library_group, + const char *attr_name, + void (LibertyLibrary::*set_func)(float value), + float scale); + void readLibAttrFloat(const LibertyGroup *library_group, + const char *attr_name, + void (LibertyLibrary::*set_func)(const RiseFall *rf, + float value), + const RiseFall *rf, + float scale); + void readLibAttrFloatWarnZero(const LibertyGroup *library_group, + const char *attr_name, + void (LibertyLibrary::*set_func)(float value), + float scale); + + // Cell groups. + void readCell(LibertyCell *cell, + const LibertyGroup *cell_group); + void readScaledCell(const LibertyGroup *scaled_cell_group); + LibertyPortGroupMap makeCellPorts(LibertyCell *cell, + const LibertyGroup *cell_group); + void makePinPort(LibertyCell *cell, + const LibertyGroup *pin_group, + LibertyPortGroupMap &port_group_map); + void makeBusPort(LibertyCell *cell, + const LibertyGroup *bus_group, + LibertyPortGroupMap &port_group_map); + void makeBusPinPorts(LibertyCell *cell, + const LibertyGroup *bus_group, + LibertyPortGroupMap &port_group_map); + void makeBundlePort(LibertyCell *cell, + const LibertyGroup *bundle_group, + LibertyPortGroupMap &port_group_map); + void makeBundlePinPorts(LibertyCell *cell, + const LibertyGroup *bundle_group, + LibertyPortGroupMap &port_group_map); + void makePgPinPort(LibertyCell *cell, + const LibertyGroup *pg_pin_group); LibertyPort *makePort(LibertyCell *cell, const char *port_name); LibertyPort *makeBusPort(LibertyCell *cell, @@ -511,78 +192,270 @@ protected: int to_index, BusDcl *bus_dcl); - TimingModel *makeScalarCheckModel(float value, + void readPortAttributes(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readPortAttrString(const char *attr_name, + void (LibertyPort::*set_func)(const char *value), + const LibertyPortSeq &ports, + const LibertyGroup *group); + void readPortAttrFloat(const char *attr_name, + void (LibertyPort::*set_func)(float value), + const LibertyPortSeq &ports, + const LibertyGroup *group, + float scale); + void readPortAttrBool(const char *attr_name, + void (LibertyPort::*set_func)(bool value), + const LibertyPortSeq &ports, + const LibertyGroup *group); + void readDriverWaveform(const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readPortAttrFloatMinMax(const char *attr_name, + void (LibertyPort::*set_func)(float value, + const MinMax *min_max), + const LibertyPortSeq &ports, + const LibertyGroup *group, + const MinMax *min_max, + float scale); + void readPulseClock(const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readSignalType(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readMinPulseWidth(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readModeDefs(LibertyCell *cell, + const LibertyGroup *cell_group); + void makeTimingArcs(LibertyCell *cell, + 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); + 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); + ReceiverModelPtr readReceiverCapacitance(const LibertyGroup *timing_group, + const RiseFall *rf); + void readReceiverCapacitance(const LibertyGroup *timing_group, + const char *cap_group_name, + int index, + const RiseFall *rf, + ReceiverModelPtr &receiver_model); + OutputWaveforms *readOutputWaveforms(const LibertyGroup *timing_group, + const RiseFall *rf); + OutputWaveforms *makeOutputWaveforms(const LibertyGroup *current_group, + OutputWaveformSeq &output_currents, + const RiseFall *rf); + + TableModel *readTableModel(const LibertyGroup *table_group, + const RiseFall *rf, + TableTemplateType template_type, + float scale, + ScaleFactorType scale_factor_type); + TablePtr readTableModel(const LibertyGroup *table_group, + const TableTemplate *tbl_template, + float scale); + void makeTimingModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs); + void makeLinearModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs); + void makeTableModels(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs); + + TableAxisPtr makeTableAxis(const LibertyGroup *table_group, + const char *index_attr_name, + TableAxisPtr template_axis); + void readGroupAttrFloat(const char *attr_name, + const LibertyGroup *group, + const std::function &set_func, + float scale = 1.0F); + void readTimingArcAttrs(LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs); + void readTimingSense(const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs); + void readTimingType(const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs); + void readTimingWhen(const LibertyCell *cell, + const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs); + void readTimingMode(const LibertyGroup *timing_group, + TimingArcAttrsPtr timing_attrs); + void makePortFuncs(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + + void makeSequentials(LibertyCell *cell, + const LibertyGroup *cell_group); + void makeSequentials(LibertyCell *cell, + const LibertyGroup *cell_group, + bool is_register, + const char *seq_group_name, + const char *clk_attr_name, + const char *data_attr_name); + FuncExpr *makeSeqFunc(LibertyCell *cell, + const LibertyGroup *seq_group, + const char *attr_name, + int size); + void makeSeqPorts(LibertyCell *cell, + const LibertyGroup *seq_group, + // Return values. + LibertyPort *&out_port, + LibertyPort *&out_port_inv, + size_t &size); + void seqPortNames(const LibertyGroup *group, + const char *&out_name, + const char *&out_inv_name, + bool &has_size, + size_t &size); + TimingModel *makeScalarCheckModel(LibertyCell *cell, + float value, ScaleFactorType scale_factor_type, const RiseFall *rf); - void makeMinPulseWidthArcs(LibertyPort *port, - int line); - void setEnergyScale(); + void readPortDir(const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readCapacitance(const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void makeTimingArcs(LibertyCell *cell, + const std::string &from_port_name, + LibertyPort *to_port, + LibertyPort *related_out_port, + bool one_to_one, + TimingArcAttrsPtr timing_attrs, + int timing_line); + void makeTimingArcs(LibertyCell *cell, + LibertyPort *to_port, + LibertyPort *related_out_port, + TimingArcAttrsPtr timing_attrs, + int timing_line); + + void readInternalPowerGroups(LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *port_group); + void readLeagageGrouops(LibertyCell *cell, + const LibertyGroup *port_group); + + void readCellAttributes(LibertyCell *cell, + const LibertyGroup *cell_group); + void readScaleFactors(LibertyCell *cell, + const LibertyGroup *cell_group); + void readCellAttrString(const char *attr_name, + void (LibertyCell::*set_func)(const char *value), + LibertyCell *cell, + const LibertyGroup *group); + void readCellAttrFloat(const char *attr_name, + void (LibertyCell::*set_func)(float value), + LibertyCell *cell, + const LibertyGroup *group, + float scale); + void readCellAttrBool(const char *attr_name, + void (LibertyCell::*set_func)(bool value), + LibertyCell *cell, + const LibertyGroup *group); + void readLevelShifterType(LibertyCell *cell, + const LibertyGroup *cell_group); + void readSwitchCellType(LibertyCell *cell, + const LibertyGroup *cell_group); + void readCellOcvDerateGroup(LibertyCell *cell, + const LibertyGroup *cell_group); + void readStatetable(LibertyCell *cell, + const LibertyGroup *cell_group); + void readTestCell(LibertyCell *cell, + const LibertyGroup *cell_group); + + FuncExpr *readFuncExpr(LibertyCell *cell, + const LibertyGroup *group, + const char *attr_name); + LibertyPort *findLibertyPort(LibertyCell *cell, + const LibertyGroup *group, + const char *port_name_attr); + LibertyPortSeq findLibertyPorts(LibertyCell *cell, + const LibertyGroup *group, + const char *port_name_attr); + + float energyScale(); void defineVisitors(); + void defineGroupVisitor(const char *type, LibraryGroupVisitor begin_visitor, LibraryGroupVisitor end_visitor); - void defineAttrVisitor(const char *attr_name, - LibraryAttrVisitor visitor); - void parseNames(const char *name_str); - void clearAxisValues(); - void makeTableAxis(int index, - LibertyAttr *attr); - - StringSeq *parseNameList(const char *name_list); - StdStringSeq parseTokenList(const char *token_str, - const char separator); - LibertyPort *findPort(const char *port_name); + float defaultCap(LibertyPort *port); - virtual void visitVariable(LibertyVariable *var); void visitPorts(std::function func); StateInputValues parseStateInputValues(StdStringSeq &inputs, - LibertyAttr *attr); + const LibertySimpleAttr *attr); StateInternalValues parseStateInternalValues(StdStringSeq &states, - LibertyAttr *attr); + const LibertySimpleAttr *attr); - const char *getAttrString(LibertyAttr *attr); - void getAttrInt(LibertyAttr *attr, + void getAttrInt(const LibertySimpleAttr *attr, // Return values. int &value, bool &exists); - void getAttrFloat(LibertyAttr *attr, - // Return values. - float &value, - bool &valid); - void getAttrFloat(LibertyAttr *attr, - LibertyAttrValue *attr_value, - // Return values. - float &value, - bool &valid); - void getAttrFloat2(LibertyAttr *attr, + void getAttrFloat2(const LibertyComplexAttr *attr, // Return values. float &value1, float &value2, bool &exists); - FloatSeq parseStringFloatList(const std::string &float_list, - float scale, - LibertyAttr *attr); - LogicValue getAttrLogicValue(LibertyAttr *attr); - void getAttrBool(LibertyAttr *attr, + void getAttrFloat(const LibertyComplexAttr *attr, + const LibertyAttrValue *attr_value, + // Return values. + float &value, + bool &valid); + LogicValue getAttrLogicValue(const LibertySimpleAttr *attr); + void getAttrBool(const LibertySimpleAttr *attr, // Return values. bool &value, bool &exists); - void visitVariable(int index, - LibertyAttr *attr); - void visitIndex(int index, - LibertyAttr *attr); + const EarlyLateAll *getAttrEarlyLate(const LibertySimpleAttr *attr); + + FloatSeq parseStringFloatList(const std::string &float_list, + float scale, + const LibertySimpleAttr *attr); + FloatSeq parseStringFloatList(const std::string &float_list, + float scale, + const LibertyComplexAttr *attr); TableAxisPtr makeAxis(int index, - LibertyGroup *group); - FloatSeq readFloatSeq(LibertyAttr *attr, + const LibertyGroup *group); + FloatSeq readFloatSeq(const LibertyComplexAttr *attr, float scale); void variableValue(const char *var, float &value, bool &exists); FuncExpr *parseFunc(const char *func, const char *attr_name, + const LibertyCell *cell, int line); void libWarn(int id, - LibertyStmt *stmt, + const LibertyGroup *obj, + const char *fmt, + ...) + __attribute__((format (printf, 4, 5))); + void libWarn(int id, + const LibertySimpleAttr *obj, + const char *fmt, + ...) + __attribute__((format (printf, 4, 5))); + void libWarn(int id, + const LibertyComplexAttr *obj, const char *fmt, ...) __attribute__((format (printf, 4, 5))); @@ -592,7 +465,15 @@ protected: ...) __attribute__((format (printf, 4, 5))); void libError(int id, - LibertyStmt *stmt, + const LibertyGroup *obj, + const char *fmt, ...) + __attribute__((format (printf, 4, 5))); + void libError(int id, + const LibertySimpleAttr *obj, + const char *fmt, ...) + __attribute__((format (printf, 4, 5))); + void libError(int id, + const LibertyComplexAttr *obj, const char *fmt, ...) __attribute__((format (printf, 4, 5))); @@ -602,64 +483,12 @@ protected: Debug *debug_; Network *network_; LibertyBuilder builder_; - LibertyVariableMap *var_map_; + LibertyVariableMap var_map_; LibertyLibrary *library_; - LibraryGroupMap group_begin_map_; - LibraryGroupMap group_end_map_; - LibraryAttrMap attr_visitor_map_; - Wireload *wireload_; - WireloadSelection *wireload_selection_; - const char *default_wireload_; - const char *default_wireload_selection_; - ScaleFactors *scale_factors_; - ScaleFactors *save_scale_factors_; - bool have_input_threshold_[RiseFall::index_count]; - bool have_output_threshold_[RiseFall::index_count]; - bool have_slew_lower_threshold_[RiseFall::index_count]; - bool have_slew_upper_threshold_[RiseFall::index_count]; - TableTemplate *tbl_template_; - LibertyCell *cell_; - LibertyCell *scaled_cell_owner_; - const char *ocv_derate_name_; - PortGroupSeq cell_port_groups_; - OperatingConditions *op_cond_; - LibertyPortSeq *ports_; - PortGroup *port_group_; - LibertyPortSeq *saved_ports_; - PortGroup *saved_port_group_; - StringSeq bus_names_; - bool in_bus_; - bool in_bundle_; - bool in_ccsn_; - bool in_ecsm_waveform_; - TableAxisVariable axis_var_[3]; - FloatSeq axis_values_[3]; - int type_bit_from_; - bool type_bit_from_exists_; - int type_bit_to_; - bool type_bit_to_exists_; - SequentialGroup *sequential_; - SequentialGroupSeq cell_sequentials_; - StatetableGroup *statetable_; - TimingGroup *timing_; - InternalPowerGroup *internal_power_; - LeakagePowerGroup *leakage_power_; - LeakagePowerGroupSeq leakage_powers_; - const RiseFall *rf_; - int index_; - OcvDerate *ocv_derate_; - const RiseFallBoth *rf_type_; - const EarlyLateAll *derate_type_; - const EarlyLateAll *sigma_type_; - PathType path_type_; - LibertyPort *pg_port_; - ScaleFactorType scale_factor_type_; - TableAxisPtr axis_[3]; - TablePtr table_; - float table_model_scale_; - ModeDef *mode_def_; - ModeValueDef *mode_value_; - LibertyFuncSeq cell_funcs_; + bool first_cell_; + LibraryGroupVisitorMap group_begin_map_; + LibraryGroupVisitorMap group_end_map_; + float time_scale_; float cap_scale_; float res_scale_; @@ -668,263 +497,11 @@ protected: float power_scale_; float energy_scale_; float distance_scale_; - const char *default_operating_condition_; - ReceiverModelPtr receiver_model_; - OutputWaveformSeq output_currents_; - OutputWaveforms *output_waveforms_; - float reference_time_; - bool reference_time_exists_; - std::string driver_waveform_name_; - - TestCell *test_cell_; - // Saved state while parsing test_cell. - LibertyCell *save_cell_; - PortGroupSeq save_cell_port_groups_; - StatetableGroup *save_statetable_; - SequentialGroupSeq save_cell_sequentials_; - LibertyFuncSeq save_cell_funcs_; static constexpr char escape_ = '\\'; private: friend class PortNameBitIterator; - friend class TimingGroup; -}; - -// Reference to a function that will be parsed at the end of the cell -// definition when all of the ports are defined. -class LibertyFunc -{ -public: - LibertyFunc(const char *expr, - LibertySetFunc set_func, - bool invert, - const char *attr_name, - int line); - ~LibertyFunc(); - const char *expr() const { return expr_; } - LibertySetFunc setFunc() const { return set_func_; } - bool invert() const { return invert_; } - const char *attrName() const { return attr_name_; } - int line() const { return line_; } - -protected: - const char *expr_; - LibertySetFunc set_func_; - bool invert_; - const char *attr_name_; - int line_; -}; - -// Port attributes that refer to other ports cannot be parsed -// until all of the ports are defined. This class saves them -// so they can be parsed at the end of the cell. -class PortGroup -{ -public: - PortGroup(LibertyPortSeq *ports, - int line); - ~PortGroup(); - LibertyPortSeq *ports() const { return ports_; } - TimingGroupSeq &timingGroups() { return timings_; } - void addTimingGroup(TimingGroup *timing); - InternalPowerGroupSeq &internalPowerGroups() { return internal_power_groups_; } - void addInternalPowerGroup(InternalPowerGroup *internal_power); - ReceiverModel *receiverModel() const { return receiver_model_; } - void setReceiverModel(ReceiverModelPtr receiver_model); - int line() const { return line_; } - -private: - LibertyPortSeq *ports_; - TimingGroupSeq timings_; - InternalPowerGroupSeq internal_power_groups_; - ReceiverModel *receiver_model_; - int line_; -}; - -// Liberty group with related_pins group attribute. -class RelatedPortGroup -{ -public: - RelatedPortGroup(int line); - virtual ~RelatedPortGroup(); - int line() const { return line_; } - StringSeq *relatedPortNames() const { return related_port_names_; } - void setRelatedPortNames(StringSeq *names); - bool isOneToOne() const { return is_one_to_one_; } - void setIsOneToOne(bool one); - -protected: - StringSeq *related_port_names_; - bool is_one_to_one_; - int line_; -}; - -class SequentialGroup -{ -public: - SequentialGroup(bool is_register, - bool is_bank, - LibertyPort *out_port, - LibertyPort *out_inv_port, - int size, - int line); - ~SequentialGroup(); - LibertyPort *outPort() const { return out_port_; } - LibertyPort *outInvPort() const { return out_inv_port_; } - int size() const { return size_; } - bool isRegister() const { return is_register_; } - bool isBank() const { return is_bank_; } - const char *clock() const { return clk_; } - void setClock(const char *clk); - const char *data() const { return data_; } - void setData(const char *data); - const char *clear() const { return clear_; } - void setClear(const char *clr); - const char *preset() const { return preset_; } - void setPreset(const char *preset); - LogicValue clrPresetVar1() const { return clr_preset_var1_; } - void setClrPresetVar1(LogicValue var); - LogicValue clrPresetVar2() const { return clr_preset_var2_; } - void setClrPresetVar2(LogicValue var); - int line() const { return line_; } - -protected: - bool is_register_; - bool is_bank_; - LibertyPort *out_port_; - LibertyPort *out_inv_port_; - int size_; - const char *clk_; - const char *data_; - const char *preset_; - const char *clear_; - LogicValue clr_preset_var1_; - LogicValue clr_preset_var2_; - int line_; -}; - -class StatetableGroup -{ -public: - StatetableGroup(StdStringSeq &input_ports, - StdStringSeq &internal_ports, - int line); - const StdStringSeq &inputPorts() const { return input_ports_; } - const StdStringSeq &internalPorts() const { return internal_ports_; } - void addRow(StateInputValues &input_values, - StateInternalValues ¤t_values, - StateInternalValues &next_values); - StatetableRows &table() { return table_; } - int line() const { return line_; } - -private: - StdStringSeq input_ports_; - StdStringSeq internal_ports_; - StatetableRows table_; - int line_; -}; - -class TimingGroup : public RelatedPortGroup -{ -public: - TimingGroup(int line); - virtual ~TimingGroup(); - TimingArcAttrsPtr attrs() { return attrs_; } - const char *relatedOutputPortName()const {return related_output_port_name_;} - void setRelatedOutputPortName(const char *name); - void intrinsic(const RiseFall *rf, - // Return values. - float &value, - bool &exists); - void setIntrinsic(const RiseFall *rf, - float value); - void resistance(const RiseFall *rf, - // Return values. - float &value, - bool &exists); - void setResistance(const RiseFall *rf, - float value); - TableModel *cell(const RiseFall *rf); - void setCell(const RiseFall *rf, - TableModel *model); - TableModel *constraint(const RiseFall *rf); - void setConstraint(const RiseFall *rf, - TableModel *model); - TableModel *transition(const RiseFall *rf); - void setTransition(const RiseFall *rf, - TableModel *model); - void makeTimingModels(LibertyCell *cell, - LibertyReader *visitor); - void setDelaySigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model); - void setSlewSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model); - void setConstraintSigma(const RiseFall *rf, - const EarlyLate *early_late, - TableModel *model); - void setReceiverModel(ReceiverModelPtr receiver_model); - OutputWaveforms *outputWaveforms(const RiseFall *rf); - void setOutputWaveforms(const RiseFall *rf, - OutputWaveforms *output_current); - -protected: - void makeLinearModels(LibertyCell *cell); - void makeTableModels(LibertyCell *cell, - LibertyReader *reader); - - TimingArcAttrsPtr attrs_; - const char *related_output_port_name_; - float intrinsic_[RiseFall::index_count]; - bool intrinsic_exists_[RiseFall::index_count]; - float resistance_[RiseFall::index_count]; - bool resistance_exists_[RiseFall::index_count]; - TableModel *cell_[RiseFall::index_count]; - TableModel *constraint_[RiseFall::index_count]; - TableModel *constraint_sigma_[RiseFall::index_count][EarlyLate::index_count]; - TableModel *transition_[RiseFall::index_count]; - TableModel *delay_sigma_[RiseFall::index_count][EarlyLate::index_count]; - TableModel *slew_sigma_[RiseFall::index_count][EarlyLate::index_count]; - OutputWaveforms *output_waveforms_[RiseFall::index_count]; - ReceiverModelPtr receiver_model_; -}; - -class InternalPowerGroup : public RelatedPortGroup -{ -public: - InternalPowerGroup(int line); - const std::string &relatedPgPin() const { return related_pg_pin_; } - void setRelatedPgPin(std::string related_pg_pin); - const std::shared_ptr &when() const { return when_; } - void setWhen(std::shared_ptr when); - void setModel(const RiseFall *rf, - std::shared_ptr model); - InternalPowerModels &models() { return models_; } - -private: - std::string related_pg_pin_; - std::shared_ptr when_; - InternalPowerModels models_; -}; - -class LeakagePowerGroup -{ -public: - LeakagePowerGroup(int line); - const std::string &relatedPgPin() const { return related_pg_pin_; } - void setRelatedPgPin(std::string pin_name); - FuncExpr *when() const { return when_; } - void setWhen(FuncExpr *when); - float power() const { return power_; } - void setPower(float power); - -protected: - std::string related_pg_pin_; - FuncExpr *when_; - float power_; - int line_; }; // Named port iterator. Port name can be: @@ -968,17 +545,16 @@ public: float axis_value2, Table *currents, float reference_time); - ~OutputWaveform(); float slew() const { return slew_; } float cap() const { return cap_; } - Table *currents() const { return currents_; } - Table *stealCurrents(); + Table *releaseCurrents(); + float referenceTime() { return reference_time_; } private: float slew_; float cap_; - Table *currents_; + std::unique_ptr
currents_; float reference_time_; }; diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 27512dc52..977c9b082 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -45,9 +45,7 @@ size_t findValueIndex(float value, const FloatSeq *values); static void -sigmaModelsMvOwner(TableModel *models[EarlyLate::index_count], - std::array, - EarlyLate::index_count> &out); +sigmaModelsDelete(TableModelsEarlyLate &models); static string reportPvt(const LibertyCell *cell, const Pvt *pvt, @@ -63,40 +61,50 @@ TimingModel::TimingModel(LibertyCell *cell) : GateTableModel::GateTableModel(LibertyCell *cell, TableModel *delay_model, - TableModel *delay_sigma_models[EarlyLate::index_count], + TableModelsEarlyLate delay_sigma_models, TableModel *slew_model, - TableModel *slew_sigma_models[EarlyLate::index_count], + TableModelsEarlyLate slew_sigma_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)), receiver_model_(receiver_model), output_waveforms_(output_waveforms) { - sigmaModelsMvOwner(delay_sigma_models, delay_sigma_models_); - sigmaModelsMvOwner(slew_sigma_models, slew_sigma_models_); } -GateTableModel::~GateTableModel() = default; +GateTableModel::GateTableModel(LibertyCell *cell, + TableModel *delay_model, + TableModel *slew_model) : + GateTimingModel(cell), + delay_model_(delay_model), + delay_sigma_models_{}, + slew_model_(slew_model), + slew_sigma_models_{}, + receiver_model_(nullptr), + output_waveforms_(nullptr) +{ +} + +GateTableModel::~GateTableModel() +{ + sigmaModelsDelete(slew_sigma_models_); + sigmaModelsDelete(delay_sigma_models_); +} static void -sigmaModelsMvOwner(TableModel *models[EarlyLate::index_count], - std::array, - EarlyLate::index_count> &out) -{ - TableModel *early_model = models ? models[EarlyLate::earlyIndex()] : nullptr; - TableModel *late_model = models ? models[EarlyLate::lateIndex()] : nullptr; - if (early_model) { - out[EarlyLate::earlyIndex()].reset(early_model); - if (late_model && late_model != early_model) { - out[EarlyLate::lateIndex()].reset(late_model); - } else if (late_model == early_model) { - out[EarlyLate::lateIndex()] = - std::make_unique(*out[EarlyLate::earlyIndex()]); - } - } else if (late_model) { - out[EarlyLate::lateIndex()].reset(late_model); +sigmaModelsDelete(TableModelsEarlyLate &models) +{ + 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; } } @@ -122,19 +130,19 @@ GateTableModel::gateDelay(const Pvt *pvt, 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()].get(), + 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()].get(), + 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()].get(), + 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()].get(), + sigma_late = findValue(pvt, slew_sigma_models_[EarlyLate::lateIndex()], in_slew, load_cap, 0.0); // Clip negative slews to zero. if (slew < 0.0) @@ -166,22 +174,22 @@ GateTableModel::reportGateDelay(const Pvt *pvt, load_cap, 0.0, digits); if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) result += reportTableLookup("Delay sigma(early)", pvt, - delay_sigma_models_[EarlyLate::earlyIndex()].get(), + 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()].get(), + 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()].get(), + 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()].get(), + 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 (drvr_slew < 0.0) @@ -285,13 +293,13 @@ GateTableModel::driveResistance(const Pvt *pvt) const const TableModel * GateTableModel::delaySigmaModel(const EarlyLate *el) const { - return delay_sigma_models_[el->index()].get(); + return delay_sigma_models_[el->index()]; } const TableModel * GateTableModel::slewSigmaModel(const EarlyLate *el) const { - return slew_sigma_models_[el->index()].get(); + return slew_sigma_models_[el->index()]; } void @@ -354,7 +362,7 @@ GateTableModel::axisValue(const TableAxis *axis, } bool -GateTableModel::checkAxes(const TablePtr &table) +GateTableModel::checkAxes(const TableModel *table) { const TableAxis *axis1 = table->axis1(); const TableAxis *axis2 = table->axis2(); @@ -395,7 +403,7 @@ ReceiverModel::setCapacitanceModel(TableModel table_model, } bool -ReceiverModel::checkAxes(TablePtr table) +ReceiverModel::checkAxes(const TableModel *table) { const TableAxis *axis1 = table->axis1(); const TableAxis *axis2 = table->axis2(); @@ -415,14 +423,25 @@ ReceiverModel::checkAxes(TablePtr table) CheckTableModel::CheckTableModel(LibertyCell *cell, TableModel *model, - TableModel *sigma_models[EarlyLate::index_count]) : + TableModelsEarlyLate sigma_models) : + CheckTimingModel(cell), + model_(model), + sigma_models_(std::move(sigma_models)) +{ +} + +CheckTableModel::CheckTableModel(LibertyCell *cell, + TableModel *model) : CheckTimingModel(cell), - model_(model) + model_(model), + sigma_models_{} { - sigmaModelsMvOwner(sigma_models, sigma_models_); } -CheckTableModel::~CheckTableModel() = default; +CheckTableModel::~CheckTableModel() +{ + sigmaModelsDelete(sigma_models_); +} void CheckTableModel::setIsScaled(bool is_scaled) @@ -434,7 +453,7 @@ CheckTableModel::setIsScaled(bool is_scaled) const TableModel * CheckTableModel::sigmaModel(const EarlyLate *el) const { - return sigma_models_[el->index()].get(); + return sigma_models_[el->index()]; } ArcDelay @@ -449,10 +468,10 @@ CheckTableModel::checkDelay(const Pvt *pvt, 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()].get(), + 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()].get(), + sigma_late = findValue(pvt, sigma_models_[EarlyLate::lateIndex()], from_slew, to_slew, related_out_cap); return makeDelay(mean, sigma_early, sigma_late); } @@ -491,12 +510,12 @@ CheckTableModel::reportCheckDelay(const Pvt *pvt, related_out_cap, digits); if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) result += reportTableDelay("Check sigma early", pvt, - sigma_models_[EarlyLate::earlyIndex()].get(), + 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()].get(), + sigma_models_[EarlyLate::lateIndex()], from_slew, from_slew_annotation, to_slew, related_out_cap, digits); return result; @@ -587,7 +606,7 @@ CheckTableModel::axisValue(const TableAxis *axis, } bool -CheckTableModel::checkAxes(const TablePtr table) +CheckTableModel::checkAxes(const TableModel *table) { const TableAxis *axis1 = table->axis1(); const TableAxis *axis2 = table->axis2(); diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 347c56fef..1f1ea2c10 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -204,6 +204,16 @@ TimingArcSet::TimingArcSet(const TimingRole *role, { } +std::string +TimingArcSet::to_string() +{ + std::string str = from_->name(); + str += " -> "; + str += to_->name(); + str += " " + role()->to_string(); + return str; +} + TimingArcSet::~TimingArcSet() { deleteContents(arcs_); @@ -622,7 +632,7 @@ TimingArc::equiv(const TimingArc *arc1, } void -TimingArc::setIndex(unsigned index) +TimingArc::setIndex(size_t index) { index_ = index; } diff --git a/power/Power.cc b/power/Power.cc index db3ef0538..4319de04d 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -292,7 +292,7 @@ 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 diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 7e3f89b80..1c605a265 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -79,7 +79,7 @@ MakeTimingModel::MakeTimingModel(const char *lib_name, scene_(scene), cell_(nullptr), min_max_(MinMax::max()), - lib_builder_(new LibertyBuilder), + lib_builder_(new LibertyBuilder(debug_, report_)), tbl_template_index_(1), sdc_(scene->sdc()), sdc_backup_(nullptr), @@ -611,7 +611,7 @@ MakeTimingModel::makeScalarCheckModel(float value, 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, nullptr); + CheckTableModel *check_model = new CheckTableModel(cell_, table_model); return check_model; } @@ -628,9 +628,7 @@ MakeTimingModel::makeGateModelScalar(Delay delay, ScaleFactorType::cell, rf); TableModel *slew_model = new TableModel(slew_table, tbl_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(cell_, delay_model, nullptr, - slew_model, nullptr, - nullptr, nullptr); + GateTableModel *gate_model = new GateTableModel(cell_, delay_model, slew_model); return gate_model; } @@ -643,9 +641,7 @@ 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, - nullptr, nullptr, - nullptr, nullptr); + GateTableModel *gate_model = new GateTableModel(cell_, delay_model, nullptr); return gate_model; } @@ -721,10 +717,8 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, ScaleFactorType::cell, rf); TableModel *slew_model = new TableModel(slew_table, model_template, ScaleFactorType::cell, rf); - GateTableModel *gate_model = new GateTableModel(cell_, - delay_model, nullptr, - slew_model, nullptr, - nullptr, nullptr); + GateTableModel *gate_model = new GateTableModel(cell_, delay_model, + slew_model); return gate_model; } } diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 2a7af0246..7e72e6fb5 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -1178,7 +1178,7 @@ PathEndLatchCheck::sourceClkOffset(const StaState *sta) const const TimingRole * PathEndLatchCheck::checkRole(const StaState *sta) const { - if (clk_path_->clkInfo(sta)->isPulseClk()) + if (clk_path_ && clk_path_->clkInfo(sta)->isPulseClk()) // Pulse latches use register cycle accounting. return TimingRole::setup(); else diff --git a/test/get_is_memory.tcl b/test/get_is_memory.tcl index 23080a991..61d9d4618 100644 --- a/test/get_is_memory.tcl +++ b/test/get_is_memory.tcl @@ -1,4 +1,4 @@ -# Tests whether the is_memory attribute works for cells and libcells +# Tests whether the is_memory attribute works for instances and cells read_liberty gf180mcu_sram.lib.gz read_liberty asap7_small.lib.gz read_verilog get_is_memory.v diff --git a/test/gf180mcu_sram.lib.gz b/test/gf180mcu_sram.lib.gz index b4ab4b9fd42edea2bb0ab58aa96dee052d029a6f..84279e4699285d224a3d74decd2db47e3a0f682c 100644 GIT binary patch delta 19 acmaEEcGZkazMF%g-hX-~1M@~1Z)pHSYz656 delta 37 scmca=_S{THzMF$X_>O5h19N(sp@l(ia%p^VQDUxMPG%CrMlNq@0O!;T&;S4c diff --git a/test/liberty_arcs_one2one_1.ok b/test/liberty_arcs_one2one_1.ok index dc99d32b1..eac1a9001 100644 --- a/test/liberty_arcs_one2one_1.ok +++ b/test/liberty_arcs_one2one_1.ok @@ -1,3 +1,4 @@ +Warning 1195: liberty_arcs_one2one_1.lib line 45, port Y function size does not match port size. Warning 1216: liberty_arcs_one2one_1.lib line 48, timing port A and related port Y are different sizes. report_edges -from partial_wide_inv_cell/A[0] A[0] -> Y[0] combinational diff --git a/test/liberty_arcs_one2one_2.ok b/test/liberty_arcs_one2one_2.ok index 323145d37..0dbc0a3a2 100644 --- a/test/liberty_arcs_one2one_2.ok +++ b/test/liberty_arcs_one2one_2.ok @@ -1,3 +1,4 @@ +Warning 1195: liberty_arcs_one2one_2.lib line 45, port Y function size does not match port size. Warning 1216: liberty_arcs_one2one_2.lib line 48, timing port A and related port Y are different sizes. report_edges -to partial_wide_inv_cell/Y[0] A[0] -> Y[0] combinational diff --git a/util/TokenParser.cc b/util/TokenParser.cc index e048cecfc..d0a448592 100644 --- a/util/TokenParser.cc +++ b/util/TokenParser.cc @@ -87,4 +87,27 @@ TokenParser::next() return token_; } +//////////////////////////////////////////////////////////////// + +// Parse space separated tokens. +StdStringSeq +parseTokens(const std::string &s, + const char delimiter) +{ + StdStringSeq tokens; + size_t i = 0; + while (i < s.size()) { + while (i < s.size() && std::isspace(s[i])) + ++i; + size_t start = i; + while (i < s.size() && s[i] != delimiter) + ++i; + if (start < i) { + tokens.emplace_back(s, start, i - start); + ++i; + } + } + return tokens; +} + } // namespace From d31372ef9ac381db8e0105c91e461230e68e8d77 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 27 Feb 2026 17:54:32 -0800 Subject: [PATCH 033/181] regression sta_dir Signed-off-by: James Cherry --- test/regression | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/regression b/test/regression index a64ac7040..2d6aa6ce0 100755 --- a/test/regression +++ b/test/regression @@ -31,7 +31,7 @@ exec tclsh $0 ${1+"$@"} # Directory containing tests. set test_dir [file dirname [file normalize [info script]]] -set sta_dir [file normalize [file join $test_dir ".."]] +set sta_dir [file dirname $test_dir] source [file join $test_dir regression_vars.tcl] source [file join $test_dir regression.tcl] From a6425a3364b790a04f2cf8f85a1fc07fb6dab9f2 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 28 Feb 2026 14:51:09 -0800 Subject: [PATCH 034/181] FindMessages criticalError Signed-off-by: James Cherry --- dcalc/ArnoldiDelayCalc.cc | 3 ++- etc/FindMessages.tcl | 2 +- power/Power.cc | 2 +- search/Levelize.cc | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index ac6705d81..162c4d3ea 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -610,7 +610,8 @@ delay_work_get_residues(delay_work *D,int term_index) // calculate_poles_res // -void arnoldi1::calculate_poles_res(delay_work *D,double rdrive) +void arnoldi1::calculate_poles_res(delay_work *D, + double rdrive) { if (n > D->nmax) delay_work_alloc(D,n); double *p = D->poles; diff --git a/etc/FindMessages.tcl b/etc/FindMessages.tcl index bf60754bf..c5fad936c 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|libWarn|libError| warn)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")} +set warn_regexp_c {(?:(?:->critical|->warn|->fileWarn|->error|->fileError|criticalError|libWarn|libError)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")} set files_tcl {} foreach subdir $subdirs { diff --git a/power/Power.cc b/power/Power.cc index 4319de04d..2faaef231 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -842,7 +842,7 @@ Power::evalBddDuty(DdNode *bdd, else if (bdd == Cudd_ReadLogicZero(bdd_.cuddMgr())) return 0.0; else - criticalError(1100, "unknown cudd constant"); + criticalError(2400, "unknown cudd constant"); } else { float duty0 = evalBddDuty(Cudd_E(bdd), inst); diff --git a/search/Levelize.cc b/search/Levelize.cc index 02ef86e5d..df6c983d5 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -678,7 +678,7 @@ Levelize::setLevelIncr(Vertex *vertex, } max_level_ = max(level, max_level_); if (level >= Graph::vertex_level_max) - criticalError(617, "maximum logic level exceeded"); + criticalError(618, "maximum logic level exceeded"); } void From cd21c43693fd1a83b5b58015a2ddc808b60588ea Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 28 Feb 2026 14:57:00 -0800 Subject: [PATCH 035/181] rm LibertyPort::clkTreeDelays, clockTreePathDelays Signed-off-by: James Cherry --- include/sta/Liberty.hh | 5 ----- liberty/Liberty.cc | 27 --------------------------- 2 files changed, 32 deletions(-) diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 02769b848..3f096a812 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -892,10 +892,6 @@ public: float clkTreeDelay(float in_slew, const RiseFall *from_rf, const MinMax *min_max) const; - // deprecated 2024-06-22 - RiseFallMinMax clkTreeDelays() const __attribute__ ((deprecated)); - // deprecated 2024-02-27 - RiseFallMinMax clockTreePathDelays() const __attribute__ ((deprecated)); static bool equiv(const LibertyPort *port1, const LibertyPort *port2); @@ -916,7 +912,6 @@ protected: void setMinPort(LibertyPort *min); void addScaledPort(OperatingConditions *op_cond, LibertyPort *scaled_port); - RiseFallMinMax clkTreeDelays1() const; void setMemberFlag(bool value, const std::function &setter); void setMemberFloat(float value, diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 5df311432..7510fdcba 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -2736,33 +2736,6 @@ LibertyPort::setDriverWaveform(DriverWaveform *driver_waveform, //////////////////////////////////////////////////////////////// -RiseFallMinMax -LibertyPort::clockTreePathDelays() const -{ - return clkTreeDelays1(); -} - -RiseFallMinMax -LibertyPort::clkTreeDelays() const -{ - return clkTreeDelays1(); -} - -RiseFallMinMax -LibertyPort::clkTreeDelays1() const -{ - RiseFallMinMax delays; - for (const RiseFall *from_rf : RiseFall::range()) { - for (const RiseFall *to_rf : RiseFall::range()) { - for (const MinMax *min_max : MinMax::range()) { - float delay = clkTreeDelay(0.0, from_rf, to_rf, min_max); - delays.setValue(from_rf, min_max, delay); - } - } - } - return delays; -} - float LibertyPort::clkTreeDelay(float in_slew, const RiseFall *rf, From e76f54a068ddcd5821ea554b2ff06ae6a3fc9e2e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 28 Feb 2026 15:45:34 -0800 Subject: [PATCH 036/181] rm TokenParser Signed-off-by: James Cherry --- CMakeLists.txt | 1 - include/sta/StringUtil.hh | 13 ++--- include/sta/TokenParser.hh | 62 -------------------- liberty/LibertyReader.cc | 1 - spice/WriteSpice.cc | 16 +++--- spice/WriteSpice.hh | 6 +- util/StringUtil.cc | 29 ++++++++-- util/TokenParser.cc | 113 ------------------------------------- 8 files changed, 41 insertions(+), 200 deletions(-) delete mode 100644 include/sta/TokenParser.hh delete mode 100644 util/TokenParser.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 60932b82b..973cfb0da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,7 +237,6 @@ set(STA_SOURCE util/StringSeq.cc util/StringSet.cc util/StringUtil.cc - util/TokenParser.cc util/Transition.cc verilog/VerilogReader.cc diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index c222d53b9..cdb9b1b7f 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -33,6 +33,8 @@ namespace sta { +using StdStringSeq = std::vector; + inline bool stringEq(const char *str1, const char *str2) @@ -201,12 +203,9 @@ deleteTmpStrings(); void trimRight(std::string &str); -using StringVector = std::vector; - -void -split(const std::string &text, - const std::string &delims, - // Return values. - StringVector &tokens); +// Spit text into delimiter separated tokens and skip whitepace. +StdStringSeq +parseTokens(const std::string &s, + const char delimiter); } // namespace diff --git a/include/sta/TokenParser.hh b/include/sta/TokenParser.hh deleted file mode 100644 index 27db37f9d..000000000 --- a/include/sta/TokenParser.hh +++ /dev/null @@ -1,62 +0,0 @@ -// 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 -#include - -namespace sta { - -using StdStringSeq = std::vector; - -// Iterate over the tokens in str separated by character sep. -// Similar in functionality to strtok, but does not leave the string -// side-effected. This is preferable to using strtok because it leaves -// string terminators where the separators were. -// Using STL string functions to parse tokens is messy and extremely slow -// on the RogueWave/Solaris implementation, apparently because of mutexes -// on temporary strings. -class TokenParser -{ -public: - TokenParser(const char *str, - const char *delimiters); - bool hasNext(); - char *next(); - -private: - const char *delimiters_; - char *token_; - char *token_end_; - char token_delimiter_; - bool first_; -}; - -// Parse delimiter separated tokens and skipp spaces. -StdStringSeq -parseTokens(const std::string &s, - const char delimiter); - -} // namespace diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 974ab7ec5..c070e3dde 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -33,7 +33,6 @@ #include "EnumNameMap.hh" #include "Report.hh" #include "Debug.hh" -#include "TokenParser.hh" #include "Units.hh" #include "Transition.hh" #include "FuncExpr.hh" diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index cce1989b4..8d55d8c70 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -213,8 +213,7 @@ WriteSpice::writeSubckts(StdStringSet &cell_names) string line; while (getline(lib_subckts_stream, line)) { // .subckt [args..] - StringVector tokens; - split(line, " \t", tokens); + StdStringSeq tokens = parseTokens(line, ' '); if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { const char *cell_name = tokens[1].c_str(); @@ -261,11 +260,11 @@ WriteSpice::writeSubckts(StdStringSet &cell_names) void WriteSpice::recordSpicePortNames(const char *cell_name, - StringVector &tokens) + StdStringSeq &tokens) { LibertyCell *cell = network_->findLibertyCell(cell_name); if (cell) { - StringVector &spice_port_names = cell_spice_port_names_[cell_name]; + StdStringSeq &spice_port_names = cell_spice_port_names_[cell_name]; for (size_t i = 2; i < tokens.size(); i++) { const char *port_name = tokens[i].c_str(); LibertyPort *port = cell->findLibertyPort(port_name); @@ -290,8 +289,7 @@ WriteSpice::findCellSubckts(StdStringSet &cell_names) string line; while (getline(lib_subckts_stream, line)) { // .subckt [args..] - StringVector tokens; - split(line, " \t", tokens); + StdStringSeq tokens = parseTokens(line, ' '); if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { const char *cell_name = tokens[1].c_str(); @@ -304,7 +302,7 @@ WriteSpice::findCellSubckts(StdStringSet &cell_names) else { // Process previous statement. if (tolower(stmt[0]) == 'x') { - split(stmt, " \t", tokens); + StdStringSeq tokens = parseTokens(line, ' '); string &subckt_cell = tokens[tokens.size() - 1]; cell_names.insert(subckt_cell); } @@ -329,7 +327,7 @@ WriteSpice::writeSubcktInst(const Instance *inst) const char *inst_name = network_->pathName(inst); LibertyCell *cell = network_->libertyCell(inst); const char *cell_name = cell->name(); - StringVector &spice_port_names = cell_spice_port_names_[cell_name]; + StdStringSeq &spice_port_names = cell_spice_port_names_[cell_name]; streamPrint(spice_stream_, "x%s", inst_name); for (string subckt_port_name : spice_port_names) { const char *subckt_port_cname = subckt_port_name.c_str(); @@ -357,7 +355,7 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, { LibertyCell *cell = network_->libertyCell(inst); const char *cell_name = cell->name(); - StringVector &spice_port_names = cell_spice_port_names_[cell_name]; + StdStringSeq &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()); diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index 3e55e2942..aee55dfef 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -40,9 +40,9 @@ namespace sta { using ParasiticNodeMap = std::map; -using CellSpicePortNames = std::map; -using LibertyPortLogicValues = std::map; using StdStringSeq = std::vector; +using CellSpicePortNames = std::map; +using LibertyPortLogicValues = std::map; // Utilities for writing a spice deck. class WriteSpice : public StaState @@ -69,7 +69,7 @@ protected: void writeSubckts(StdStringSet &cell_names); void findCellSubckts(StdStringSet &cell_names); void recordSpicePortNames(const char *cell_name, - StringVector &tokens); + StdStringSeq &tokens); void writeSubcktInst(const Instance *inst); void writeSubcktInstVoltSrcs(const Instance *inst, LibertyPortLogicValues &port_values, diff --git a/util/StringUtil.cc b/util/StringUtil.cc index a0b8c1547..6a8680e4c 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -269,12 +269,11 @@ trimRight(string &str) str.erase(str.find_last_not_of(" ") + 1); } -void +StdStringSeq split(const string &text, - const string &delims, - // Return values. - StringVector &tokens) + const string &delims) { + StdStringSeq tokens; auto start = text.find_first_not_of(delims); auto end = text.find_first_of(delims, start); while (end != string::npos) { @@ -284,6 +283,28 @@ split(const string &text, } if (start != string::npos) tokens.push_back(text.substr(start)); + return tokens; +} + +// Parse space separated tokens. +StdStringSeq +parseTokens(const std::string &s, + const char delimiter) +{ + StdStringSeq tokens; + size_t i = 0; + while (i < s.size()) { + while (i < s.size() && std::isspace(s[i])) + ++i; + size_t start = i; + while (i < s.size() && s[i] != delimiter) + ++i; + if (start < i) { + tokens.emplace_back(s, start, i - start); + ++i; + } + } + return tokens; } } // namespace diff --git a/util/TokenParser.cc b/util/TokenParser.cc deleted file mode 100644 index d0a448592..000000000 --- a/util/TokenParser.cc +++ /dev/null @@ -1,113 +0,0 @@ -// 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 "TokenParser.hh" - -#include -#include - -namespace sta { - -TokenParser::TokenParser(const char *str, - const char *delimiters) : - delimiters_(delimiters), - token_(const_cast(str)), - token_delimiter_('\0'), - first_(true) -{ - // Skip leading spaces. - while (*token_ != '\0' && isspace(*token_)) - token_++; - token_end_ = strpbrk(token_, delimiters_); - if (token_end_) { - // Save the delimiter. - token_delimiter_ = *token_end_; - // Replace the separator with a terminator. - *token_end_ = '\0'; - } -} - -bool -TokenParser::hasNext() -{ - if (!first_) { - // Replace the previous separator. - if (token_end_) { - *token_end_ = token_delimiter_; - token_ = token_end_ + 1; - // Skip spaces. - while (*token_ != '\0' && isspace(*token_)) - token_++; - // Skip delimiters. - while (*token_ != '\0' && strchr(delimiters_,*token_) != nullptr) - token_++; - if (*token_ == '\0') - token_ = nullptr; - else { - token_end_ = strpbrk(token_, delimiters_); - if (token_end_) { - // Save the delimiter. - token_delimiter_ = *token_end_; - // Replace the separator with a terminator. - *token_end_ = '\0'; - } - } - } - else - token_ = nullptr; - } - return token_ != nullptr; -} - -char * -TokenParser::next() -{ - first_ = false; - return token_; -} - -//////////////////////////////////////////////////////////////// - -// Parse space separated tokens. -StdStringSeq -parseTokens(const std::string &s, - const char delimiter) -{ - StdStringSeq tokens; - size_t i = 0; - while (i < s.size()) { - while (i < s.size() && std::isspace(s[i])) - ++i; - size_t start = i; - while (i < s.size() && s[i] != delimiter) - ++i; - if (start < i) { - tokens.emplace_back(s, start, i - start); - ++i; - } - } - return tokens; -} - -} // namespace From 0f8d7cffd3f8190c269b83c20708e1423ac50429 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 28 Feb 2026 15:53:23 -0800 Subject: [PATCH 037/181] mv StdStringSeq defs to StringUtil.hh Signed-off-by: James Cherry --- include/sta/PathGroup.hh | 2 +- include/sta/Search.hh | 2 +- include/sta/Sta.hh | 2 +- include/sta/StringSeq.hh | 1 - include/sta/StringSet.hh | 1 - liberty/LibertyReaderPvt.hh | 2 +- spice/WriteSpice.hh | 2 +- spice/Xyce.hh | 2 +- verilog/VerilogReaderPvt.hh | 2 +- 9 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index a3bd6ee3b..836804f7e 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -32,6 +32,7 @@ #include "SdcClass.hh" #include "StaState.hh" #include "SearchClass.hh" +#include "StringUtil.hh" namespace sta { @@ -42,7 +43,6 @@ using PathGroupIterator = PathEndSeq::iterator; using PathGroupClkMap = std::map; using PathGroupNamedMap = std::map; using PathGroupSeq = std::vector; -using StdStringSeq = std::vector; // A collection of PathEnds grouped and sorted for reporting. class PathGroup diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 09bc16879..4c98845c1 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -40,6 +40,7 @@ #include "SearchPred.hh" #include "VertexVisitor.hh" #include "Path.hh" +#include "StringUtil.hh" namespace sta { @@ -70,7 +71,6 @@ using VertexSlackMapSeq = std::vector; using WorstSlacksSeq = std::vector; using DelayDblSeq = std::vector; using ExceptionPathSeq = std::vector; -using StdStringSeq = std::vector; class Search : public StaState { diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 15df9a856..ddb2f7d72 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -30,6 +30,7 @@ #include #include "StringSeq.hh" +#include "StringUtil.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" #include "SdcClass.hh" @@ -80,7 +81,6 @@ using SceneNameMap = std::map; using SlowDrvrIterator = Iterator; using CheckError = StringSeq; using CheckErrorSeq = std::vector; -using StdStringSeq = std::vector; enum class CmdNamespace { sta, sdc }; using ParasiticsNameMap = std::map; // Path::slack/arrival/required function. diff --git a/include/sta/StringSeq.hh b/include/sta/StringSeq.hh index ca7c304f5..9821b7b20 100644 --- a/include/sta/StringSeq.hh +++ b/include/sta/StringSeq.hh @@ -31,7 +31,6 @@ namespace sta { using StringSeq = std::vector; -using StdStringSeq = std::vector; void deleteContents(StringSeq *strings); diff --git a/include/sta/StringSet.hh b/include/sta/StringSet.hh index 36fa7e67f..fc3d0d2be 100644 --- a/include/sta/StringSet.hh +++ b/include/sta/StringSet.hh @@ -31,7 +31,6 @@ namespace sta { using StringSet = std::set; using StdStringSet = std::set; -using StdStringSeq = std::vector; void deleteContents(StringSet *strings); diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 91fdf2603..c17cdeb82 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -31,6 +31,7 @@ #include #include "StringSeq.hh" +#include "StringUtil.hh" #include "MinMax.hh" #include "NetworkClass.hh" #include "Transition.hh" @@ -55,7 +56,6 @@ class OutputWaveform; using LibraryGroupVisitor = void (LibertyReader::*)(const LibertyGroup *group, LibertyGroup *parent_group); using LibraryGroupVisitorMap = std::unordered_map; -using StdStringSeq = std::vector; using LibertyPortGroupMap = std::map; using OutputWaveformSeq = std::vector; diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index aee55dfef..81bd15b73 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -31,6 +31,7 @@ #include "StaState.hh" #include "StringSet.hh" +#include "StringUtil.hh" #include "Liberty.hh" #include "GraphClass.hh" #include "Parasitics.hh" @@ -40,7 +41,6 @@ namespace sta { using ParasiticNodeMap = std::map; -using StdStringSeq = std::vector; using CellSpicePortNames = std::map; using LibertyPortLogicValues = std::map; diff --git a/spice/Xyce.hh b/spice/Xyce.hh index 1c27cb4ef..ce8e5e6c5 100644 --- a/spice/Xyce.hh +++ b/spice/Xyce.hh @@ -27,11 +27,11 @@ #include #include +#include "StringUtil.hh" #include "TableModel.hh" namespace sta { -using StdStringSeq = std::vector; using WaveformSeq = std::vector
; void diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index 60498c7db..6bbe8288c 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -29,12 +29,12 @@ #include #include "StringSeq.hh" +#include "StringUtil.hh" namespace sta { using VerilogDclMap = std::map; using VerilogConstantValue = std::vector; -using StdStringSeq = std::vector; class VerilogStmt { From 94b8fd8f374e377c10d74b265c9f90d7526ccd60 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 1 Mar 2026 17:33:20 -0800 Subject: [PATCH 038/181] dcalc threshold adjustment Signed-off-by: James Cherry --- dcalc/LumpedCapDelayCalc.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 948e277f2..c3f6a5a05 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -170,9 +170,10 @@ LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library, for (const auto [load_pin, load_idx] : load_pin_index_map) { ArcDelay wire_delay = 0.0; + Slew load_slew = drvr_slew; thresholdAdjust(load_pin, drvr_library, rf, wire_delay, drvr_slew); dcalc_result.setWireDelay(load_idx, wire_delay); - dcalc_result.setLoadSlew(load_idx, drvr_slew); + dcalc_result.setLoadSlew(load_idx, load_slew); } return dcalc_result; } From 741bf4d561679a6ef36619cc514df29be89b8156 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 2 Mar 2026 12:13:13 -0800 Subject: [PATCH 039/181] rm using std:: Signed-off-by: James Cherry --- app/Main.cc | 3 +- dcalc/ArcDcalcWaveforms.cc | 6 +- dcalc/ArnoldiDelayCalc.cc | 46 +++--- dcalc/ArnoldiReduce.cc | 4 +- dcalc/CcsCeffDelayCalc.cc | 30 ++-- dcalc/DelayCalcBase.cc | 11 +- dcalc/DmpCeff.cc | 38 ++--- dcalc/FindRoot.cc | 8 +- dcalc/GraphDelayCalc.cc | 18 +-- dcalc/LumpedCapDelayCalc.cc | 7 +- dcalc/ParallelDelayCalc.cc | 6 +- dcalc/PrimaDelayCalc.cc | 15 +- dcalc/UnitDelayCalc.cc | 8 +- graph/Graph.cc | 12 +- liberty/EquivCells.cc | 2 - liberty/FuncExpr.cc | 14 +- liberty/Liberty.cc | 6 +- liberty/LibertyParser.cc | 8 +- liberty/LibertyWriter.cc | 6 +- liberty/LinearModel.cc | 10 +- liberty/TableModel.cc | 66 ++++---- liberty/TimingArc.cc | 11 +- liberty/Units.cc | 7 +- network/ConcreteLibrary.cc | 30 ++-- network/ConcreteNetwork.cc | 26 ++-- network/Network.cc | 8 +- network/ParseBus.cc | 14 +- network/SdcNetwork.cc | 89 ++++++----- network/VerilogNamespace.cc | 60 ++++---- parasitics/ConcreteParasitics.cc | 4 +- parasitics/ReduceParasitics.cc | 5 +- parasitics/SpefReader.cc | 4 +- power/Power.cc | 13 +- power/SaifReader.cc | 16 +- power/VcdParse.cc | 52 +++---- power/VcdReader.cc | 77 +++++----- sdc/ExceptionPath.cc | 10 +- sdc/Sdc.cc | 36 +++-- sdc/WriteSdc.cc | 5 +- sdf/SdfReader.cc | 100 ++++++------ sdf/SdfWriter.cc | 50 +++--- search/CheckTiming.cc | 10 +- search/ClkSkew.cc | 10 +- search/Crpr.cc | 20 +-- search/Genclks.cc | 6 +- search/Levelize.cc | 15 +- search/MakeTimingModel.cc | 22 ++- search/Property.cc | 30 ++-- search/ReportPath.cc | 163 ++++++++++---------- search/Search.cc | 7 +- search/Sta.cc | 46 +++--- search/WorstSlack.cc | 6 +- spice/WritePathSpice.cc | 23 ++- spice/WriteSpice.cc | 76 +++++---- spice/Xyce.cc | 34 ++--- util/Fuzzy.cc | 11 +- util/PatternMatch.cc | 8 +- util/Report.cc | 8 +- util/StringUtil.cc | 28 ++-- util/Transition.cc | 6 +- verilog/VerilogReader.cc | 255 +++++++++++++++---------------- verilog/VerilogWriter.cc | 27 ++-- 62 files changed, 794 insertions(+), 958 deletions(-) diff --git a/app/Main.cc b/app/Main.cc index baa1c0ea6..aa28aed84 100644 --- a/app/Main.cc +++ b/app/Main.cc @@ -39,7 +39,6 @@ namespace sta { extern const char *tcl_inits[]; } -using std::string; using sta::stringEq; using sta::findCmdLineFlag; using sta::Sta; @@ -129,7 +128,7 @@ staTclAppInit(int argc, if (!findCmdLineFlag(argc, argv, "-no_init")) { const char *home = getenv("HOME"); if (home) { - string init_path = home; + std::string init_path = home; init_path += "/"; init_path += init_filename; if (std::filesystem::is_regular_file(init_path.c_str())) diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index b927bf1a4..c718e8b93 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -35,8 +35,6 @@ namespace sta { -using std::make_shared; - Waveform ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, const Scene *scene, @@ -68,8 +66,8 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, FloatSeq time_values; for (float time : in_waveform.axis1()->values()) time_values.push_back(time + dcalc_arg.inputDelay()); - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - std::move(time_values)); + TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, + std::move(time_values)); // Scale the waveform from 0:vdd. FloatSeq *scaled_values = new FloatSeq; for (float value : *in_waveform.values()) { diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 162c4d3ea..bb1d17029 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -63,10 +63,6 @@ namespace sta { // ra_get_r // ra_get_s -using std::string; -using std::abs; -using std::vector; - struct delay_work; struct delay_c; @@ -151,15 +147,15 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc const LoadPinIndexMap &load_pin_index_map, const Scene *scene, const MinMax *min_max) override; - string reportGateDelay(const Pin *drvr_pin, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *parasitic, - const LoadPinIndexMap &load_pin_index_map, - const Scene *scene, - const MinMax *min_max, - int digits) override; + std::string reportGateDelay(const Pin *drvr_pin, + const TimingArc *arc, + const Slew &in_slew, + float load_cap, + const Parasitic *parasitic, + const LoadPinIndexMap &load_pin_index_map, + const Scene *scene, + const MinMax *min_max, + int digits) override; void finishDrvrPin() override; void delay_work_set_thresholds(delay_work *D, double lo, @@ -240,7 +236,7 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc int pin_n_; ArnoldiReduce *reduce_; delay_work *delay_work_; - vector unsaved_parasitics_; + std::vector unsaved_parasitics_; bool pocv_enabled_; }; @@ -469,7 +465,7 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, return dcalc_result; } -string +std::string ArnoldiDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -690,7 +686,7 @@ tridiagEV(int n,double *din,double *ein,double *d,double **v) e[0] = 0.0; for (h=n-1;h>=1;h--) { iter = 0; - while (abs(e[h])>1e-18) { // 1e-6ps + while (std::abs(e[h])>1e-18) { // 1e-6ps m=0; if (m != h) { if (iter++ == 20) @@ -820,14 +816,14 @@ solve_t_bracketed(double s,int order,double *p,double *rr, if (0.0= 0.0) - || (abs(2.0*f) > abs(dxold*df))) { + || (std::abs(2.0*f) > std::abs(dxold*df))) { dxold = dx; dx = 0.5*(xh-xl); if (flast*f >0.0) { @@ -851,7 +847,7 @@ solve_t_bracketed(double s,int order,double *p,double *rr, return rts; } } - if (abs(dx) < xacc) { + if (std::abs(dx) < xacc) { return rts; } get_dv(rts,s,order,p,rr,&f,&df); f -= val; @@ -860,7 +856,7 @@ solve_t_bracketed(double s,int order,double *p,double *rr, else xh = rts; } - if (abs(f)<1e-6) // 1uV + if (std::abs(f)<1e-6) // 1uV return rts; return 0.5*(xl+xh); } @@ -1266,28 +1262,28 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, f = (ptlo-pthi)/p - tlohi; df = dlo-dhi; s = s - f/df; - if (abs(f)<.001e-12) return; // .001ps + if (std::abs(f)<.001e-12) return; // .001ps ra_solve_for_pt(p*s,vlo,&ptlo,&dlo); ra_solve_for_pt(p*s,vhi,&pthi,&dhi); f = (ptlo-pthi)/p - tlohi; df = dlo-dhi; s = s - f/df; - if (abs(f)<.001e-12) return; // .001ps + if (std::abs(f)<.001e-12) return; // .001ps ra_solve_for_pt(p*s,vlo,&ptlo,&dlo); ra_solve_for_pt(p*s,vhi,&pthi,&dhi); f = (ptlo-pthi)/p - tlohi; df = dlo-dhi; s = s - f/df; - if (abs(f)<.001e-12) return; // .001ps + if (std::abs(f)<.001e-12) return; // .001ps ra_solve_for_pt(p*s,vlo,&ptlo,&dlo); ra_solve_for_pt(p*s,vhi,&pthi,&dhi); f = (ptlo-pthi)/p - tlohi; df = dlo-dhi; s = s - f/df; - if (abs(f)<.001e-12) return; // .001ps + if (std::abs(f)<.001e-12) return; // .001ps ra_solve_for_pt(p*s,vlo,&ptlo,&dlo); ra_solve_for_pt(p*s,vhi,&pthi,&dhi); @@ -1295,7 +1291,7 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, df = dlo-dhi; s = s - f/df; - if (abs(f)>.5e-12) // .5ps + if (std::abs(f)>.5e-12) // .5ps debugPrint(debug_, "arnoldi", 1, "ra_solve_for_s p %g tlohi %s err %s", p, units_->timeUnit()->asString(tlohi), diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc index f05f8abcd..ac8095e28 100644 --- a/dcalc/ArnoldiReduce.cc +++ b/dcalc/ArnoldiReduce.cc @@ -38,8 +38,6 @@ namespace sta { -using std::string; - rcmodel::rcmodel() : pinV(nullptr) { @@ -621,7 +619,7 @@ ArnoldiReduce::makeRcmodelFromTs() report_->reportLine(" d[%d] %s", h, units_->timeUnit()->asString(d[h])); - string line = stdstrPrint("U[%d]",h); + std::string line = stdstrPrint("U[%d]",h); for (i=0;ireportLineString(line); diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 14b3e639e..3f460d084 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -24,6 +24,8 @@ #include "CcsCeffDelayCalc.hh" +#include + #include "Debug.hh" #include "Units.hh" #include "Liberty.hh" @@ -38,17 +40,11 @@ namespace sta { -using std::string; - // Implementaion based on: // "Gate Delay Estimation with Library Compatible Current Source Models // and Effective Capacitance", D. Garyfallou et al, // IEEE Transactions on Very Large Scale Integration (VLSI) Systems, March 2021 -using std::abs; -using std::exp; -using std::make_shared; - ArcDelayCalc * makeCcsCeffDelayCalc(StaState *sta) { @@ -144,7 +140,7 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, findCsmWaveform(); ref_time_ = output_waveforms_->referenceTime(in_slew_); gate_delay = region_times_[region_vth_idx_] - ref_time_; - drvr_slew = abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); + 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)", delayAsString(gate_delay, this), @@ -184,12 +180,12 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, } findCsmWaveform(); gate_delay = region_times_[region_vth_idx_] - ref_time_; - drvr_slew = abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); + 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", delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); - if (abs(delayAsFloat(drvr_slew) - prev_drvr_slew) < .01 * prev_drvr_slew) + if (std::abs(delayAsFloat(drvr_slew) - prev_drvr_slew) < .01 * prev_drvr_slew) break; prev_drvr_slew = delayAsFloat(drvr_slew); } @@ -529,8 +525,8 @@ CcsCeffDelayCalc::drvrWaveform() drvr_volts->push_back(v); } } - TableAxisPtr drvr_time_axis = 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; @@ -561,8 +557,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 = 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; @@ -606,8 +602,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 = 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; @@ -663,7 +659,7 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin, //////////////////////////////////////////////////////////////// -string +std::string CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -680,7 +676,7 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, scene, min_max); } - string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, + 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); diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index ecaa16d4f..5d2019879 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -38,9 +38,6 @@ namespace sta { -using std::string; -using std::log; - DelayCalcBase::DelayCalcBase(StaState *sta) : ArcDelayCalc(sta) { @@ -101,9 +98,9 @@ DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin, vh = load_library->slewUpperThreshold(rf); slew_derate = load_library->slewDerateFromLibrary(); } - wire_delay = -elmore * log(1.0 - vth); - load_slew = drvr_slew + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate; - load_slew = drvr_slew + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate; + wire_delay = -elmore * std::log(1.0 - vth); + load_slew = drvr_slew + elmore * std::log((1.0 - vl) / (1.0 - vh)) / slew_derate; + load_slew = drvr_slew + elmore * std::log((1.0 - vl) / (1.0 - vh)) / slew_derate; } void @@ -173,7 +170,7 @@ DelayCalcBase::checkDelay(const Pin *check_pin, return delay_zero; } -string +std::string DelayCalcBase::reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index d759fc40b..dd9b6721d 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -32,8 +32,9 @@ #include "DmpCeff.hh" -#include // abs, min -#include // sqrt, log +#include +#include +#include #include "Report.hh" #include "Debug.hh" @@ -50,15 +51,6 @@ namespace sta { -using std::string; -using std::abs; -using std::min; -using std::max; -using std::sqrt; -using std::log; -using std::isnan; -using std::function; - // Tolerance (as a scale of value) for driver parameters (Ceff, delta t, t0). static const double driver_param_tol = .01; // Waveform threshold crossing time tolerance (1.0 = 100%). @@ -107,7 +99,7 @@ newtonRaphson(const int max_iter, const int n, const double x_tol, // eval(state) is called to fill fvec and fjac. - function eval, + std::function eval, // Temporaries supplied by caller. double *fvec, double **fjac, @@ -337,7 +329,7 @@ DmpAlg::findDriverParams(double ceff) gateDelays(ceff, t_vth, t_vl, slew); // Scale slew to 0-100% double dt = slew / (vh_ - vl_); - double t0 = t_vth + log(1.0 - vth_) * rd_ * ceff - vth_ * dt; + 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, @@ -461,7 +453,7 @@ DmpAlg::showFvec() void DmpAlg::showJacobian() { - string line = " "; + std::string line = " "; for (int j = 0; j < nr_order_; j++) line += stdstrPrint("%12s", dmp_param_index_strings[j]); report_->reportLineString(line); @@ -894,7 +886,7 @@ DmpPi::init(const LibertyLibrary *drvr_library, k0_ = 1.0 / (rd_ * c2_); double a = rpi_ * rd_ * c1_ * c2_; double b = rd_ * (c1_ + c2_) + rpi_ * c1_; - double sqrt_ = sqrt(b * b - 4 * a); + double sqrt_ = std::sqrt(b * b - 4 * a); p1_ = (b + sqrt_) / (2 * a); p2_ = (b - sqrt_) / (2 * a); @@ -1282,7 +1274,7 @@ newtonRaphson(const int max_iter, double x[], const int size, const double x_tol, - function eval, + std::function eval, // Temporaries supplied by caller. double *fvec, double **fjac, @@ -1300,7 +1292,7 @@ newtonRaphson(const int max_iter, bool all_under_x_tol = true; for (int i = 0; i < size; i++) { - if (abs(p[i]) > abs(x[i]) * x_tol) + if (std::abs(p[i]) > std::abs(x[i]) * x_tol) all_under_x_tol = false; x[i] += p[i]; } @@ -1334,7 +1326,7 @@ luDecomp(double **a, for (int i = 0; i < size; i++) { double big = 0.0; for (int j = 0; j < size; j++) { - double temp = abs(a[i][j]); + double temp = std::abs(a[i][j]); if (temp > big) big = temp; } @@ -1363,7 +1355,7 @@ luDecomp(double **a, for (int k = 0; k < j; k++) sum -= a[i][k] * a[k][j]; a[i][j] = sum; - double dum = scale[i] * abs(sum); + double dum = scale[i] * std::abs(sum); if (dum >= big) { big = dum; imax = i; @@ -1507,7 +1499,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, float in_slew1 = delayAsFloat(in_slew); float c2, rpi, c1; parasitics_->piModel(parasitic, c2, rpi, c1); - if (isnan(c2) || isnan(c1) || isnan(rpi)) + 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), table_model, rf, in_slew1, c2, rpi, c1); @@ -1583,7 +1575,7 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, dmp_alg_->name()); } -string +std::string DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -1598,7 +1590,7 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, parasitic, load_pin_index_map, scene, min_max); GateTableModel *model = arc->gateTableModel(scene, min_max); float c_eff = 0.0; - string result; + std::string result; const LibertyCell *drvr_cell = arc->to()->libertyCell(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); const Units *units = drvr_library->units(); @@ -1653,7 +1645,7 @@ gateModelRd(const LibertyCell *cell, gate_model->gateDelay(pvt, in_slew, cap1, pocv_enabled, d1, s1); gate_model->gateDelay(pvt, in_slew, cap2, pocv_enabled, d2, s2); double vth = cell->libertyLibrary()->outputThreshold(rf); - float rd = -log(vth) * abs(delayAsFloat(d1) - delayAsFloat(d2)) / (cap2 - cap1); + float rd = -std::log(vth) * std::abs(delayAsFloat(d1) - delayAsFloat(d2)) / (cap2 - cap1); return rd; } diff --git a/dcalc/FindRoot.cc b/dcalc/FindRoot.cc index b74d7d16b..2b0e96653 100644 --- a/dcalc/FindRoot.cc +++ b/dcalc/FindRoot.cc @@ -28,8 +28,6 @@ namespace sta { -using std::abs; - double findRoot(FindRootFunc func, double x1, @@ -76,7 +74,7 @@ findRoot(FindRootFunc func, // Swap x1/x2 so func(x1) < 0. std::swap(x1, x2); double root = (x1 + x2) * 0.5; - double dx_prev = abs(x2 - x1); + double dx_prev = std::abs(x2 - x1); double dx = dx_prev; double y, dy; func(root, y, dy); @@ -84,7 +82,7 @@ findRoot(FindRootFunc func, // Newton/raphson out of range. if ((((root - x2) * dy - y) * ((root - x1) * dy - y) > 0.0) // Not decreasing fast enough. - || (abs(2.0 * y) > abs(dx_prev * dy))) { + || (std::abs(2.0 * y) > std::abs(dx_prev * dy))) { // Bisect x1/x2 interval. dx_prev = dx; dx = (x2 - x1) * 0.5; @@ -95,7 +93,7 @@ findRoot(FindRootFunc func, dx = y / dy; root -= dx; } - if (abs(dx) <= x_tol * abs(root)) { + if (std::abs(dx) <= x_tol * std::abs(root)) { // Converged. fail = false; return root; diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index e94b3c8b6..5fdf9587d 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -24,6 +24,8 @@ #include "GraphDelayCalc.hh" +#include +#include #include #include "ContainerHelpers.hh" @@ -53,10 +55,6 @@ namespace sta { -using std::string; -using std::abs; -using std::array; - static const Slew default_slew = 0.0; static bool @@ -941,7 +939,7 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, initSlew(drvr_vertex); initWireDelays(drvr_vertex); bool delay_changed = false; - array delay_exists = {false, false}; + std::array delay_exists = {false, false}; VertexInEdgeIterator edge_iter(drvr_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); @@ -983,7 +981,7 @@ GraphDelayCalc::findLatchEdgeDelays(Edge *edge) Instance *drvr_inst = network_->instance(drvr_pin); debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s", sdc_network_->pathName(drvr_inst)); - array delay_exists = {false, false}; + std::array delay_exists = {false, false}; LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); bool delay_changed = findDriverEdgeDelays(drvr_vertex, nullptr, edge, arc_delay_calc_, load_pin_index_map, @@ -999,7 +997,7 @@ GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map, // Return value. - array &delay_exists) + std::array &delay_exists) { Vertex *from_vertex = edge->from(graph_); const TimingArcSet *arc_set = edge->timingArcSet(); @@ -1231,7 +1229,7 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge, float gate_delay1 = delayAsFloat(gate_delay); float prev_gate_delay1 = delayAsFloat(prev_gate_delay); if (prev_gate_delay1 == 0.0 - || (abs(gate_delay1 - prev_gate_delay1) / prev_gate_delay1 + || (std::abs(gate_delay1 - prev_gate_delay1) / prev_gate_delay1 > incremental_delay_tolerance_)) delay_changed = true; graph_->setArcDelay(edge, arc, ap_index, gate_delay); @@ -1660,7 +1658,7 @@ GraphDelayCalc::checkEdgeClkSlew(const Vertex *from_vertex, //////////////////////////////////////////////////////////////// -string +std::string GraphDelayCalc::reportDelayCalc(const Edge *edge, const TimingArc *arc, const Scene *scene, @@ -1673,7 +1671,7 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, const TimingRole *role = arc->role(); const Instance *inst = network_->instance(to_pin); const TimingArcSet *arc_set = edge->timingArcSet(); - string result; + std::string result; const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); const RiseFall *to_rf = arc->toEdge()->asRiseFall(); if (from_rf && to_rf) { diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index c3f6a5a05..c9f2985c0 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -40,9 +40,6 @@ namespace sta { -using std::string; -using std::isnan; - ArcDelayCalc * makeLumpedCapDelayCalc(StaState *sta) { @@ -146,7 +143,7 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, Slew drvr_slew; float in_slew1 = delayAsFloat(in_slew); // NaNs cause seg faults during table lookup. - if (isnan(load_cap) || isnan(delayAsFloat(in_slew))) + if (std::isnan(load_cap) || std::isnan(delayAsFloat(in_slew))) report_->error(1350, "gate delay input variable is NaN"); model->gateDelay(pinPvt(drvr_pin, scene, min_max), in_slew1, load_cap, variables_->pocvEnabled(), @@ -178,7 +175,7 @@ LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library, return dcalc_result; } -string +std::string LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin, const TimingArc *arc, const Slew &in_slew, diff --git a/dcalc/ParallelDelayCalc.cc b/dcalc/ParallelDelayCalc.cc index f18c785d1..d4ead20fa 100644 --- a/dcalc/ParallelDelayCalc.cc +++ b/dcalc/ParallelDelayCalc.cc @@ -34,8 +34,6 @@ namespace sta { -using std::vector; - ParallelDelayCalc::ParallelDelayCalc(StaState *sta): DelayCalcBase(sta) { @@ -71,8 +69,8 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, ArcDcalcResultSeq dcalc_results(drvr_count); Slew slew_sum = 0.0; ArcDelay load_delay_sum = 0.0; - vector intrinsic_delays(dcalc_args.size()); - vector load_delays(dcalc_args.size()); + std::vector intrinsic_delays(dcalc_args.size()); + std::vector load_delays(dcalc_args.size()); for (size_t drvr_idx = 0; drvr_idx < drvr_count; drvr_idx++) { ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx]; diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 835d02c6c..85175777a 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -44,9 +44,6 @@ namespace sta { -using std::string; -using std::abs; -using std::make_shared; using Eigen::SparseLU; using Eigen::HouseholderQR; using Eigen::ColPivHouseholderQR; @@ -726,7 +723,7 @@ PrimaDelayCalc::dcalcResults() 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 = abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]); + Slew 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, @@ -743,7 +740,7 @@ PrimaDelayCalc::dcalcResults() 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 = abs(wire_times[threshold_vh] - wire_times[threshold_vl]); + Slew 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), @@ -908,7 +905,7 @@ PrimaDelayCalc::recordWaveformStep(double time) //////////////////////////////////////////////////////////////// -string +std::string PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -959,8 +956,8 @@ Waveform PrimaDelayCalc::watchWaveform(const Pin *pin) { FloatSeq &voltages = watch_pin_values_[pin]; - TableAxisPtr time_axis = 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; } @@ -1003,7 +1000,7 @@ void PrimaDelayCalc::reportMatrix(MatrixSd &matrix) { for (Eigen::Index i = 0; i < matrix.rows(); i++) { - string line = "| "; + std::string line = "| "; for (Eigen::Index j = 0; j < matrix.cols(); j++) { std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j)); line += entry; diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc index c32197fb7..cafcf426b 100644 --- a/dcalc/UnitDelayCalc.cc +++ b/dcalc/UnitDelayCalc.cc @@ -28,8 +28,6 @@ namespace sta { -using std::string; - ArcDelayCalc * makeUnitDelayCalc(StaState *sta) { @@ -142,7 +140,7 @@ UnitDelayCalc::unitDelayResult(const LoadPinIndexMap &load_pin_index_map) return dcalc_result; } -string +std::string UnitDelayCalc::reportGateDelay(const Pin *, const TimingArc *, const Slew &, @@ -153,7 +151,7 @@ UnitDelayCalc::reportGateDelay(const Pin *, const MinMax *, int) { - string result("Delay = 1.0\n"); + std::string result("Delay = 1.0\n"); result += "Slew = 0.0\n"; return result; } @@ -170,7 +168,7 @@ UnitDelayCalc::checkDelay(const Pin *, return units_->timeUnit()->scale(); } -string +std::string UnitDelayCalc::reportCheckDelay(const Pin *, const TimingArc *, const Slew &, diff --git a/graph/Graph.cc b/graph/Graph.cc index 9e0b189dc..f56a02a7c 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -39,8 +39,6 @@ namespace sta { -using std::string; - //////////////////////////////////////////////////////////////// // // Graph @@ -985,12 +983,12 @@ Vertex::setObjectIdx(ObjectIdx idx) object_idx_ = idx; } -string +std::string Vertex::to_string(const StaState *sta) const { const Network *network = sta->sdcNetwork(); if (network->direction(pin_)->isBidirect()) { - string str = network->pathName(pin_); + std::string str = network->pathName(pin_); str += ' '; str += is_bidirect_drvr_ ? "driver" : "load"; return str; @@ -1002,7 +1000,7 @@ Vertex::to_string(const StaState *sta) const const char * Vertex::name(const Network *network) const { - string name = to_string(network); + std::string name = to_string(network); return makeTmpString(name); } @@ -1229,11 +1227,11 @@ Edge::setObjectIdx(ObjectIdx idx) object_idx_ = idx; } -string +std::string Edge::to_string(const StaState *sta) const { const Graph *graph = sta->graph(); - string str = from(graph)->to_string(sta); + std::string str = from(graph)->to_string(sta); str += " -> "; str += to(graph)->to_string(sta); str += " "; diff --git a/liberty/EquivCells.cc b/liberty/EquivCells.cc index f081435b7..af81a35e2 100644 --- a/liberty/EquivCells.cc +++ b/liberty/EquivCells.cc @@ -38,8 +38,6 @@ namespace sta { -using std::max; - static unsigned hashCell(const LibertyCell *cell); static unsigned diff --git a/liberty/FuncExpr.cc b/liberty/FuncExpr.cc index 38f3faea0..0d422d1a8 100644 --- a/liberty/FuncExpr.cc +++ b/liberty/FuncExpr.cc @@ -30,8 +30,6 @@ namespace sta { -using std::string; - FuncExpr * FuncExpr::makePort(LibertyPort *port) { @@ -199,20 +197,20 @@ FuncExpr::portTimingSense(const LibertyPort *port) const return TimingSense::unknown; } -string +std::string FuncExpr::to_string() const { return to_string(false); } -string +std::string FuncExpr::to_string(bool with_parens) const { switch (op_) { case Op::port: return port_->name(); case Op::not_: { - string result = "!"; + std::string result = "!"; result += left_ ? left_->to_string(true) : "?"; return result; } @@ -231,12 +229,12 @@ FuncExpr::to_string(bool with_parens) const } } -string +std::string FuncExpr::to_string(bool with_parens, char op) const { - string right = right_->to_string(true); - string result; + std::string right = right_->to_string(true); + std::string result; if (with_parens) result += '('; result += left_ ? left_->to_string(true) : "?"; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 7510fdcba..130af8027 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -50,8 +50,6 @@ namespace sta { -using std::string; - void initLiberty() { @@ -2704,13 +2702,13 @@ LibertyPort::setReceiverModel(ReceiverModelPtr receiver_model) receiver_model_ = receiver_model; } -string +std::string portLibertyToSta(const char *port_name) { constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; size_t name_length = strlen(port_name); - string sta_name; + std::string sta_name; for (size_t i = 0; i < name_length; i++) { char ch = port_name[i]; if (ch == bus_brkt_left diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index 4660aa421..d85911b75 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -37,8 +37,6 @@ namespace sta { -using std::string; - void parseLibertyFile(const char *filename, LibertyGroupVisitor *library_visitor, @@ -65,7 +63,7 @@ LibertyParser::LibertyParser(const char *filename, } void -LibertyParser::setFilename(const string &filename) +LibertyParser::setFilename(const std::string &filename) { filename_ = filename; } @@ -253,7 +251,7 @@ LibertyScanner::includeBegin() static const std::regex include_regexp("include_file *\\( *([^)]+) *\\) *;?"); std::cmatch matches; if (std::regex_match(yytext, matches, include_regexp)) { - string filename = matches[1].str(); + std::string filename = matches[1].str(); gzstream::igzstream *stream = new gzstream::igzstream(filename.c_str()); if (stream->is_open()) { yypush_buffer_state(yy_create_buffer(stream, 16384)); @@ -348,7 +346,7 @@ LibertyGroup::deleteSubgroup(const LibertyGroup *subgroup) void LibertyGroup::addDefine(LibertyDefine *define) { - const string &define_name = define->name(); + const std::string &define_name = define->name(); LibertyDefine *prev_define = findKey(define_map_, define_name); if (prev_define) { define_map_.erase(define_name); diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 6c4b757f4..2622b1baf 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -25,7 +25,7 @@ #include "LibertyWriter.hh" #include -#include +#include #include "Units.hh" #include "FuncExpr.hh" @@ -39,8 +39,6 @@ namespace sta { -using std::abs; - class LibertyWriter { public: @@ -271,7 +269,7 @@ LibertyWriter::writeBusDcls() 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", abs(dcl->from() - dcl->to() + 1)); + 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"); diff --git a/liberty/LinearModel.cc b/liberty/LinearModel.cc index 1001e74b0..552533e67 100644 --- a/liberty/LinearModel.cc +++ b/liberty/LinearModel.cc @@ -29,8 +29,6 @@ namespace sta { -using std::string; - GateLinearModel::GateLinearModel(LibertyCell *cell, float intrinsic, float resistance) : @@ -53,7 +51,7 @@ GateLinearModel::gateDelay(const Pvt *, drvr_slew = 0.0; } -string +std::string GateLinearModel::reportGateDelay(const Pvt *, float, float load_cap, @@ -65,7 +63,7 @@ GateLinearModel::reportGateDelay(const Pvt *, const Unit *time_unit = units->timeUnit(); const Unit *res_unit = units->resistanceUnit(); const Unit *cap_unit = units->capacitanceUnit(); - string result = "Delay = "; + std::string result = "Delay = "; result += time_unit->asString(intrinsic_, digits); result += " + "; result += res_unit->asString(resistance_, digits); @@ -105,7 +103,7 @@ CheckLinearModel::checkDelay(const Pvt *, return intrinsic_; } -string +std::string CheckLinearModel::reportCheckDelay(const Pvt *, float, const char *, @@ -117,7 +115,7 @@ CheckLinearModel::reportCheckDelay(const Pvt *, const LibertyLibrary *library = cell_->libertyLibrary(); const Units *units = library->units(); const Unit *time_unit = units->timeUnit(); - string result = "Check = "; + std::string result = "Check = "; result += time_unit->asString(intrinsic_, digits); return result; } diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 977c9b082..b7677e162 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -35,23 +35,17 @@ namespace sta { -using std::string; -using std::min; -using std::max; -using std::abs; -using std::make_shared; - size_t findValueIndex(float value, const FloatSeq *values); static void sigmaModelsDelete(TableModelsEarlyLate &models); -static string +static std::string reportPvt(const LibertyCell *cell, const Pvt *pvt, int digits); static void -appendSpaces(string &result, +appendSpaces(std::string &result, int count); TimingModel::TimingModel(LibertyCell *cell) : @@ -162,14 +156,14 @@ GateTableModel::gateDelay(const Pvt *pvt, gateDelay(pvt, in_slew, load_cap, pocv_enabled, gate_delay, drvr_slew); } -string +std::string GateTableModel::reportGateDelay(const Pvt *pvt, float in_slew, float load_cap, bool pocv_enabled, int digits) const { - string result = reportPvt(cell_, pvt, digits); + std::string result = reportPvt(cell_, pvt, digits); result += reportTableLookup("Delay", pvt, delay_model_.get(), in_slew, load_cap, 0.0, digits); if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) @@ -197,7 +191,7 @@ GateTableModel::reportGateDelay(const Pvt *pvt, return result; } -string +std::string GateTableModel::reportTableLookup(const char *result_name, const Pvt *pvt, const TableModel *model, @@ -496,7 +490,7 @@ CheckTableModel::findValue(const Pvt *pvt, return 0.0; } -string +std::string CheckTableModel::reportCheckDelay(const Pvt *pvt, float from_slew, const char *from_slew_annotation, @@ -505,7 +499,7 @@ CheckTableModel::reportCheckDelay(const Pvt *pvt, bool pocv_enabled, int digits) const { - string result = reportTableDelay("Check", pvt, model_.get(), + 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()]) @@ -521,7 +515,7 @@ CheckTableModel::reportCheckDelay(const Pvt *pvt, return result; } -string +std::string CheckTableModel::reportTableDelay(const char *result_name, const Pvt *pvt, const TableModel *model, @@ -535,7 +529,7 @@ CheckTableModel::reportTableDelay(const char *result_name, float axis_value1, axis_value2, axis_value3; findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, axis_value3); - string result = reportPvt(cell_, pvt, digits); + std::string result = reportPvt(cell_, pvt, digits); result += model_->reportValue(result_name, cell_, pvt, axis_value1, from_slew_annotation, axis_value2, axis_value3, @@ -735,7 +729,7 @@ TableModel::scaleFactor(const LibertyCell *cell, rf_index_, cell, pvt); } -string +std::string TableModel::reportValue(const char *result_name, const LibertyCell *cell, const Pvt *pvt, @@ -746,7 +740,7 @@ TableModel::reportValue(const char *result_name, const Unit *table_unit, int digits) const { - string result = table_->reportValue("Table value", cell, pvt, value1, + std::string result = table_->reportValue("Table value", cell, pvt, value1, comment1, value2, value3, table_unit, digits); result += reportPvtScaleFactor(cell, pvt, digits); @@ -758,7 +752,7 @@ TableModel::reportValue(const char *result_name, return result; } -static string +static std::string reportPvt(const LibertyCell *cell, const Pvt *pvt, int digits) @@ -767,7 +761,7 @@ reportPvt(const LibertyCell *cell, if (pvt == nullptr) pvt = library->defaultOperatingConditions(); if (pvt) { - string result; + std::string result; stringPrint(result, "P = %.*f V = %.*f T = %.*f\n", digits, pvt->process(), digits, pvt->voltage(), @@ -777,7 +771,7 @@ reportPvt(const LibertyCell *cell, return ""; } -string +std::string TableModel::reportPvtScaleFactor(const LibertyCell *cell, const Pvt *pvt, int digits) const @@ -785,7 +779,7 @@ TableModel::reportPvtScaleFactor(const LibertyCell *cell, if (pvt == nullptr) pvt = cell->libertyLibrary()->defaultOperatingConditions(); if (pvt) { - string result; + std::string result; stringPrint(result, "PVT scale factor = %.*f\n", digits, scaleFactor(cell, pvt)); @@ -1166,7 +1160,7 @@ Table::reportValueOrder0(const char *result_name, const Unit *table_unit, int digits) const { - string result = result_name; + std::string result = result_name; result += " constant = "; result += table_unit->asString(value_, digits); if (comment1) @@ -1187,7 +1181,7 @@ Table::reportValueOrder1(const char *result_name, { const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); - string result = "Table is indexed by\n "; + std::string result = "Table is indexed by\n "; result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); @@ -1228,7 +1222,7 @@ Table::reportValueOrder2(const char *result_name, const Units *units = cell->libertyLibrary()->units(); const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); - string result = "------- "; + std::string result = "------- "; result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); @@ -1289,7 +1283,7 @@ Table::reportValueOrder3(const char *result_name, const Unit *unit1 = axis1_->unit(units); const Unit *unit2 = axis2_->unit(units); const Unit *unit3 = axis3_->unit(units); - string result = " --------- "; + std::string result = " --------- "; result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); @@ -1391,7 +1385,7 @@ Table::report(const Units *units, const Unit *unit1 = axis1_->unit(units); report->reportLine("%s", tableVariableString(axis1_->variable())); report->reportLine("------------------------------"); - string line; + std::string line; for (size_t index1 = 0; index1 < axis1_->size(); index1++) { line += unit1->asString(axis1_->axisValue(index1), digits); line += " "; @@ -1410,7 +1404,7 @@ Table::report(const Units *units, const Unit *unit2 = axis2_->unit(units); report->reportLine("%s", tableVariableString(axis2_->variable())); report->reportLine(" ------------------------------"); - string line = " "; + std::string line = " "; for (size_t index2 = 0; index2 < axis2_->size(); index2++) { line += unit2->asString(axis2_->axisValue(index2), digits); line += " "; @@ -1436,7 +1430,7 @@ Table::report(const Units *units, unit1->asString(axis1_->axisValue(axis_index1), digits)); report->reportLine("%s", tableVariableString(axis3_->variable())); report->reportLine(" ------------------------------"); - string line = " "; + std::string line = " "; for (size_t axis_index3 = 0; axis_index3 < axis3_->size(); axis_index3++) { line += unit3->asString(axis3_->axisValue(axis_index3), digits); line += " "; @@ -1455,7 +1449,7 @@ Table::report(const Units *units, } static void -appendSpaces(string &result, +appendSpaces(std::string &result, int count) { while (count--) @@ -1755,7 +1749,7 @@ OutputWaveforms::findVoltages(size_t wave_index, // Make voltage -> current table. FloatSeq axis_volts = volts; TableAxisPtr volt_axis = - make_shared(TableAxisVariable::input_voltage, std::move(axis_volts)); + 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; @@ -1774,7 +1768,7 @@ OutputWaveforms::currentWaveform(float slew, times->push_back(time); currents->push_back(current); } - TableAxisPtr time_axis = 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); } @@ -1951,7 +1945,7 @@ OutputWaveforms::voltageWaveform(float slew, times.push_back(time); volts.push_back(volt); } - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, + TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, std::move(times)); return Table(std::move(volts), time_axis); } @@ -2076,7 +2070,7 @@ OutputWaveforms::voltageCurrentWaveform(float slew, currents->push_back(current); } TableAxisPtr volt_axis = - make_shared(TableAxisVariable::input_voltage, std::move(*volts)); + std::make_shared(TableAxisVariable::input_voltage, std::move(*volts)); delete volts; return Table(currents, volt_axis); } @@ -2095,12 +2089,12 @@ OutputWaveforms::finalResistance() const FloatSeq &voltages = voltage_currents->axis1()->values(); FloatSeq *currents = voltage_currents->values(); size_t idx_last1 = voltages.size() - 2; - return (vdd_ - voltages[idx_last1]) / abs((*currents)[idx_last1]); + return (vdd_ - voltages[idx_last1]) / std::abs((*currents)[idx_last1]); } //////////////////////////////////////////////////////////////// -DriverWaveform::DriverWaveform(const string &name, +DriverWaveform::DriverWaveform(const std::string &name, TablePtr waveforms) : name_(name), waveforms_(waveforms) @@ -2118,7 +2112,7 @@ DriverWaveform::waveform(float slew) time_values->push_back(time); volt_values->push_back(volt); } - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, + TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, std::move(*time_values)); delete time_values; Table waveform(volt_values, time_axis); diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 1f1ea2c10..7bc324a62 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -35,9 +35,6 @@ namespace sta { -using std::string; -using std::make_shared; - static bool timingArcsEquiv(const TimingArcSet *set1, const TimingArcSet *set2); @@ -511,7 +508,7 @@ TimingArcSet::wireArcIndex(const RiseFall *rf) void TimingArcSet::init() { - wire_timing_arc_attrs_ = make_shared(TimingSense::positive_unate); + wire_timing_arc_attrs_ = std::make_shared(TimingSense::positive_unate); wire_timing_arc_set_ = new TimingArcSet(TimingRole::wire(), wire_timing_arc_attrs_); new TimingArc(wire_timing_arc_set_, Transition::rise(), Transition::rise(), nullptr); @@ -549,18 +546,18 @@ TimingArc::~TimingArc() delete scaled_models_; } -string +std::string TimingArc::to_string() const { if (set_->role()->isWire()) { - string str = "wire "; + std::string str = "wire "; str += from_rf_->to_string(); str += " -> "; str += to_rf_->to_string(); return str; } else { - string str = set_->from()->name(); + std::string str = set_->from()->name(); str += " "; str += from_rf_->to_string(); str += " -> "; diff --git a/liberty/Units.cc b/liberty/Units.cc index 97e333099..9cbb46319 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -32,9 +32,6 @@ namespace sta { -using std::abs; - - Unit::Unit(const char *suffix) : scale_(1.0), suffix_(suffix), @@ -175,12 +172,12 @@ Unit::asString(float value, int digits) const { // Special case INF because it blows up otherwise. - if (abs(value) >= INF * .1) + if (std::abs(value) >= INF * .1) return (value > 0.0) ? "INF" : "-INF"; else { float scaled_value = value / scale_; // prevent "-0.00" on slowaris - if (abs(scaled_value) < 1E-6) + if (std::abs(scaled_value) < 1E-6) scaled_value = 0.0; return stringPrintTmp("%.*f", digits, scaled_value); } diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index 0ecba436c..a1fb2b501 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -24,6 +24,7 @@ #include "ConcreteLibrary.hh" +#include #include #include @@ -35,13 +36,6 @@ namespace sta { -using std::string; -using std::map; -using std::min; -using std::max; -using std::abs; -using std::swap; - static constexpr char escape_ = '\\'; ConcreteLibrary::ConcreteLibrary(const char *name, @@ -228,7 +222,7 @@ ConcreteCell::makeBusPortBit(ConcretePort *bus_port, const char *bus_name, int bit_index) { - string bit_name; + std::string bit_name; stringPrint(bit_name, "%s%c%d%c", bus_name, library_->busBrktLeft(), @@ -272,14 +266,14 @@ ConcreteCell::setIsLeaf(bool is_leaf) } void -ConcreteCell::setAttribute(const string &key, - const string &value) +ConcreteCell::setAttribute(const std::string &key, + const std::string &value) { attribute_map_[key] = value; } -string -ConcreteCell::getAttribute(const string &key) const +std::string +ConcreteCell::getAttribute(const std::string &key) const { const auto &itr = attribute_map_.find(key); if (itr != attribute_map_.end()) @@ -350,8 +344,8 @@ void BusPort::addBusBit(ConcretePort *port, int index) { - from_ = min(from_, index); - to_ = max(to_, index); + from_ = std::min(from_, index); + to_ = std::max(to_, index); members_.push_back(port); } @@ -362,7 +356,7 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, { const char bus_brkts_left[2]{bus_brkt_left, '\0'}; const char bus_brkts_right[2]{bus_brkt_right, '\0'}; - map bus_map; + std::map bus_map; // Find ungrouped bus ports. // Remove bus bit ports from the ports_ vector during the scan by // keeping an index to the next insertion index and skipping over @@ -372,7 +366,7 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, for (ConcretePort *port : ports) { const char *port_name = port->name(); bool is_bus; - string bus_name; + std::string bus_name; int index; parseBusName(port_name, bus_brkts_left, bus_brkts_right, escape_, is_bus, bus_name, index); @@ -402,7 +396,7 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, (*members)[member_index] = bus_bit; } if (msb_first) - swap(from, to); + std::swap(from, to); ConcretePort *port = makeBusPort(bus_name.c_str(), from, to, members); port->setDirection(bus_port.direction()); } @@ -505,7 +499,7 @@ int ConcretePort::size() const { if (is_bus_) - return abs(to_index_ - from_index_) + 1; + return std::abs(to_index_ - from_index_) + 1; else if (is_bundle_) return static_cast(member_ports_->size()); else diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index 57c419b97..22fd122db 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -35,8 +35,6 @@ namespace sta { -using std::string; - static void makeChildNetwork(Instance *proto, Instance *parent, @@ -581,8 +579,8 @@ ConcreteNetwork::setIsLeaf(Cell *cell, void ConcreteNetwork::setAttribute(Cell *cell, - const string &key, - const string &value) + const std::string &key, + const std::string &value) { ConcreteCell *ccell = reinterpret_cast(cell); ccell->setAttribute(key, value); @@ -628,9 +626,9 @@ ConcreteNetwork::filename(const Cell *cell) return ccell->filename(); } -string +std::string ConcreteNetwork::getAttribute(const Cell *cell, - const string &key) const + const std::string &key) const { const ConcreteCell *ccell = reinterpret_cast(cell); return ccell->getAttribute(key); @@ -967,9 +965,9 @@ ConcreteNetwork::id(const Instance *instance) const return inst->id(); } -string +std::string ConcreteNetwork::getAttribute(const Instance *inst, - const string &key) const + const std::string &key) const { const ConcreteInstance *cinst = reinterpret_cast(inst); return cinst->getAttribute(key); @@ -1389,8 +1387,8 @@ ConcreteNetwork::connect(Instance *inst, void ConcreteNetwork::setAttribute(Instance *inst, - const string &key, - const string &value) + const std::string &key, + const std::string &value) { ConcreteInstance *cinst = reinterpret_cast(inst); cinst->setAttribute(key, value); @@ -1718,14 +1716,14 @@ ConcreteInstance::childIterator() const } void -ConcreteInstance::setAttribute(const string &key, - const string &value) +ConcreteInstance::setAttribute(const std::string &key, + const std::string &value) { attribute_map_[key] = value; } -string -ConcreteInstance::getAttribute(const string &key) const +std::string +ConcreteInstance::getAttribute(const std::string &key) const { const auto &itr = attribute_map_.find(key); if (itr != attribute_map_.end()) diff --git a/network/Network.cc b/network/Network.cc index 3b6bad62e..78e13b1e6 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -37,8 +37,6 @@ namespace sta { -using std::string; - Network::Network() : default_liberty_(nullptr), divider_('/'), @@ -76,7 +74,7 @@ Network::findPortsMatching(const Cell *cell, { PortSeq matches; bool is_bus, is_range, subscript_wild; - string bus_name; + std::string bus_name; int from, to; parseBusName(pattern->pattern(), '[', ']', '\\', is_bus, is_range, bus_name, from, to, subscript_wild); @@ -1043,12 +1041,12 @@ Network::findInstPinsHierMatching(const Instance *instance, // Return value. PinSeq &matches) const { - string inst_name = name(instance); + std::string inst_name = name(instance); InstancePinIterator *pin_iter = pinIterator(instance); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); const char *port_name = name(port(pin)); - string pin_name = inst_name + divider_ + port_name; + std::string pin_name = inst_name + divider_ + port_name; if (pattern->match(pin_name.c_str())) matches.push_back(pin); } diff --git a/network/ParseBus.cc b/network/ParseBus.cc index 17108412a..c3dae9e57 100644 --- a/network/ParseBus.cc +++ b/network/ParseBus.cc @@ -32,8 +32,6 @@ namespace sta { -using std::string; - bool isBusName(const char *name, const char brkt_left, @@ -60,7 +58,7 @@ parseBusName(const char *name, const char escape, // Return values. bool &is_bus, - string &bus_name, + std::string &bus_name, int &index) { const char brkts_left[2] = {brkt_left, '\0'}; @@ -76,7 +74,7 @@ parseBusName(const char *name, char escape, // Return values. bool &is_bus, - string &bus_name, + std::string &bus_name, int &index) { is_bus = false; @@ -110,7 +108,7 @@ parseBusName(const char *name, // Return values. bool &is_bus, bool &is_range, - string &bus_name, + std::string &bus_name, int &from, int &to, bool &subscript_wild) @@ -129,7 +127,7 @@ parseBusName(const char *name, // Return values. bool &is_bus, bool &is_range, - string &bus_name, + std::string &bus_name, int &from, int &to, bool &subscript_wild) @@ -173,13 +171,13 @@ parseBusName(const char *name, } } -string +std::string escapeChars(const char *token, const char ch1, const char ch2, const char escape) { - string escaped; + std::string escaped; for (const char *s = token; *s; s++) { char ch = *s; if (ch == escape) { diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index 29a0037e9..0495f9438 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -30,13 +30,10 @@ namespace sta { -using std::string; -using std::to_string; - -static string +static std::string escapeDividers(const char *token, const Network *network); -static string +static std::string escapeBrackets(const char *token, const Network *network); @@ -137,9 +134,9 @@ NetworkNameAdapter::id(const Cell *cell) const return network_->id(cell); } -string +std::string NetworkNameAdapter::getAttribute(const Cell *cell, - const string &key) const + const std::string &key) const { return network_->getAttribute(cell, key); } @@ -355,9 +352,9 @@ NetworkNameAdapter::cell(const Instance *instance) const return network_->cell(instance); } -string +std::string NetworkNameAdapter::getAttribute(const Instance *inst, - const string &key) const + const std::string &key) const { return network_->getAttribute(inst, key); } @@ -675,16 +672,16 @@ SdcNetwork::findPort(const Cell *cell, if (port == nullptr) { // Look for matches after escaping brackets. bool is_bus; - string bus_name; + std::string bus_name; int index; parseBusName(name, '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { - string escaped1 = escapeBrackets(name, this); + std::string escaped1 = escapeBrackets(name, this); port = network_->findPort(cell, escaped1.c_str()); if (port == nullptr) { // Try escaping base foo\[0\][1] - string escaped2; - string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); + std::string escaped2; + std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index); @@ -693,7 +690,7 @@ SdcNetwork::findPort(const Cell *cell, } else { // Try escaping brackets foo\[0\].bar - string escaped = escapeBrackets(name, this); + std::string escaped = escapeBrackets(name, this); port = network_->findPort(cell, escaped.c_str()); } } @@ -708,19 +705,19 @@ SdcNetwork::findPortsMatching(const Cell *cell, if (matches.empty()) { // Look for matches after escaping brackets. bool is_bus; - string bus_name; + std::string bus_name; int index; parseBusName(pattern->pattern(), '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { - string escaped1 = escapeBrackets(pattern->pattern(), this); + std::string escaped1 = escapeBrackets(pattern->pattern(), this); PatternMatch escaped_pattern1(escaped1.c_str(), pattern); matches = network_->findPortsMatching(cell, &escaped_pattern1); if (matches.empty()) { // Try escaping base foo\[0\][1] - string escaped_name = escapeBrackets(bus_name.c_str(), this); + std::string escaped_name = escapeBrackets(bus_name.c_str(), this); escaped_name += '['; - escaped_name += to_string(index); + escaped_name += std::to_string(index); escaped_name += ']'; PatternMatch escaped_pattern2(escaped_name.c_str(), pattern); matches = network_->findPortsMatching(cell, &escaped_pattern2); @@ -728,7 +725,7 @@ SdcNetwork::findPortsMatching(const Cell *cell, } else { // Try escaping brackets foo\[0\].bar - string escaped = escapeBrackets(pattern->pattern(), this); + std::string escaped = escapeBrackets(pattern->pattern(), this); PatternMatch escaped_pattern(escaped.c_str(), pattern); matches = network_->findPortsMatching(cell, &escaped_pattern); } @@ -796,7 +793,7 @@ SdcNetwork::findInstance(const char *path_name) const parent = network_->topInstance(); Instance *child = findChild(parent, child_name); if (child == nullptr) { - string escaped_name = escapeDividers(child_name, this); + std::string escaped_name = escapeDividers(child_name, this); child = findChild(parent, escaped_name.c_str()); } return child; @@ -808,10 +805,10 @@ SdcNetwork::findInstanceRelative(const Instance *inst, { Instance *inst1 = network_->findInstanceRelative(inst, path_name); if (inst1 == nullptr) { - string path_name1 = escapeBrackets(path_name, this); + std::string path_name1 = escapeBrackets(path_name, this); inst1 = network_->findInstanceRelative(inst, path_name1.c_str()); if (inst1 == nullptr) { - string path_name2 = escapeDividers(path_name1.c_str(), network_); + std::string path_name2 = escapeDividers(path_name1.c_str(), network_); inst1 = network_->findInstanceRelative(inst, path_name2.c_str()); } } @@ -848,7 +845,7 @@ SdcNetwork::findChild(const Instance *parent, { Instance *child = network_->findChild(parent, name); if (child == nullptr) { - string escaped = escapeBrackets(name, this); + std::string escaped = escapeBrackets(name, this); child = network_->findChild(parent, escaped.c_str()); } return child; @@ -873,8 +870,8 @@ SdcNetwork::findNet(const Instance *instance, { Net *net = network_->findNet(instance, net_name); if (net == nullptr) { - string net_name1 = escapeBrackets(net_name, this); - string net_name2 = escapeDividers(net_name1.c_str(), network_); + std::string net_name1 = escapeBrackets(net_name, this); + std::string net_name2 = escapeDividers(net_name1.c_str(), network_); net = network_->findNet(instance, net_name2.c_str()); } return net; @@ -886,15 +883,15 @@ SdcNetwork::findNetRelative(const Instance *inst, { Net *net = network_->findNetRelative(inst, path_name); if (net == nullptr) { - string path_name1 = escapeDividers(path_name, network_); + std::string path_name1 = escapeDividers(path_name, network_); net = network_->findNetRelative(inst, path_name1.c_str()); if (net == nullptr) { - string path_name2 = escapeBrackets(path_name, network_); + std::string path_name2 = escapeBrackets(path_name, network_); net = network_->findNetRelative(inst, path_name2.c_str()); if (net == nullptr) { - string path_name3 = escapeDividers(path_name2.c_str(), network_); + std::string path_name3 = escapeDividers(path_name2.c_str(), network_); net = network_->findNetRelative(inst, path_name3.c_str()); } } @@ -926,12 +923,12 @@ SdcNetwork::findInstNetsMatching(const Instance *instance, network_->findInstNetsMatching(instance, pattern, matches); if (matches.empty()) { // Look for matches after escaping path dividers. - string escaped_pattern = escapeDividers(pattern->pattern(), this); + std::string escaped_pattern = escapeDividers(pattern->pattern(), this); const PatternMatch escaped_dividers(escaped_pattern.c_str(), pattern); network_->findInstNetsMatching(instance, &escaped_dividers, matches); if (matches.empty()) { // Look for matches after escaping brackets. - string escaped_pattern2 = escapeBrackets(pattern->pattern(),this); + std::string escaped_pattern2 = escapeBrackets(pattern->pattern(),this); const PatternMatch escaped_brkts(escaped_pattern2.c_str(), pattern); network_->findInstNetsMatching(instance, &escaped_brkts, matches); } @@ -959,24 +956,24 @@ SdcNetwork::findPin(const Instance *instance, if (pin == nullptr) { // Look for match after escaping brackets. bool is_bus; - string bus_name; + std::string bus_name; int index; parseBusName(port_name, '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { - string escaped1 = escapeBrackets(port_name, this); + std::string escaped1 = escapeBrackets(port_name, this); pin = network_->findPin(instance, escaped1.c_str()); if (pin == nullptr) { // Try escaping base foo\[0\][1] - string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); - string escaped2; + std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); + std::string escaped2; stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index); pin = network_->findPin(instance, escaped2.c_str()); } } else { // Try escaping port brackets foo\[0\].bar - string escaped = escapeBrackets(port_name, this); + std::string escaped = escapeBrackets(port_name, this); pin = network_->findPin(instance, escaped.c_str()); } } @@ -1028,7 +1025,7 @@ SdcNetwork::visitPinTail(const Instance *instance, if (network_->hasMembers(port)) { bool bus_matches = tail->match(port_name); if (!bus_matches) { - string escaped_name = escapeDividers(port_name, network_); + std::string escaped_name = escapeDividers(port_name, network_); bus_matches = tail->match(escaped_name); } PortMemberIterator *member_iter = network_->memberIterator(port); @@ -1044,7 +1041,7 @@ SdcNetwork::visitPinTail(const Instance *instance, const char *member_name = network_->name(member_port); bool member_matches = tail->match(member_name); if (!member_matches) { - string escaped_name = escapeDividers(member_name, network_); + std::string escaped_name = escapeDividers(member_name, network_); member_matches = tail->match(escaped_name); } if (member_matches) { @@ -1059,7 +1056,7 @@ SdcNetwork::visitPinTail(const Instance *instance, else { bool port_matches = tail->match(port_name); if (!port_matches) { - string escaped_name = escapeDividers(port_name, network_); + std::string escaped_name = escapeDividers(port_name, network_); port_matches = tail->match(escaped_name); } if (port_matches) { @@ -1081,7 +1078,7 @@ SdcNetwork::makeInstance(LibertyCell *cell, const char *name, Instance *parent) { - string escaped_name = escapeDividers(name, this); + std::string escaped_name = escapeDividers(name, this); return network_edit_->makeInstance(cell, escaped_name.c_str(), parent); } @@ -1089,7 +1086,7 @@ Net * SdcNetwork::makeNet(const char *name, Instance *parent) { - string escaped_name = escapeDividers(name, this); + std::string escaped_name = escapeDividers(name, this); return network_edit_->makeNet(escaped_name.c_str(), parent); } @@ -1188,7 +1185,7 @@ SdcNetwork::parsePath(const char *path, else *p++ = ch; if (p - inst_path + 1 > inst_path_length) - report_->critical(1500, "inst path string lenth estimate busted"); + report_->critical(1500, "inst path std::string lenth estimate busted"); } *p = '\0'; stringDelete(inst_path); @@ -1235,7 +1232,7 @@ SdcNetwork::visitMatches(const Instance *parent, network_->findChildrenMatching(parent, &matcher, matches); if (has_brkts && matches.empty()) { // Look for matches after escaping brackets. - string escaped_brkts = escapeBrackets(inst_path, this); + std::string escaped_brkts = escapeBrackets(inst_path, this); const PatternMatch escaped_pattern(escaped_brkts, pattern); network_->findChildrenMatching(parent, &escaped_pattern, matches); } @@ -1257,7 +1254,7 @@ SdcNetwork::visitMatches(const Instance *parent, *p++ = ch; } if (p - inst_path + 1 > inst_path_length) - report_->critical(1501, "inst path string lenth estimate exceeded"); + report_->critical(1501, "inst path std::string lenth estimate exceeded"); } *p = '\0'; if (!found_match) { @@ -1265,7 +1262,7 @@ SdcNetwork::visitMatches(const Instance *parent, found_match |= visit_tail(parent, &tail_pattern); if (!found_match && has_brkts) { // Look for matches after escaping brackets. - string escaped_path = escapeBrackets(inst_path, this); + std::string escaped_path = escapeBrackets(inst_path, this); const PatternMatch escaped_tail(escaped_path, pattern); found_match |= visit_tail(parent, &escaped_tail); } @@ -1276,7 +1273,7 @@ SdcNetwork::visitMatches(const Instance *parent, //////////////////////////////////////////////////////////////// -static string +static std::string escapeDividers(const char *token, const Network *network) { @@ -1284,7 +1281,7 @@ escapeDividers(const char *token, network->pathEscape()); } -static string +static std::string escapeBrackets(const char *token, const Network *network) { diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index 5a1dc8e0e..5362d115b 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -31,39 +31,37 @@ namespace sta { -using std::string; - constexpr char verilog_escape = '\\'; -static string +static std::string staToVerilog(const char *sta_name); -static string +static std::string staToVerilog2(const char *sta_name); -static string -verilogToSta(const string *verilog_name); +static std::string +verilogToSta(const std::string *verilog_name); -string +std::string cellVerilogName(const char *sta_name) { return staToVerilog(sta_name); } -string +std::string instanceVerilogName(const char *sta_name) { return staToVerilog(sta_name); } -string +std::string netVerilogName(const char *sta_name) { bool is_bus; - string bus_name; + std::string bus_name; int index; parseBusName(sta_name, '[', ']', verilog_escape, is_bus, bus_name, index); if (is_bus) { - string bus_vname = staToVerilog(bus_name.c_str()); - string vname; + std::string bus_vname = staToVerilog(bus_name.c_str()); + std::string vname; stringPrint(vname, "%s[%d]", bus_vname.c_str(), index); return vname; } @@ -71,19 +69,19 @@ netVerilogName(const char *sta_name) return staToVerilog2(sta_name); } -string +std::string portVerilogName(const char *sta_name) { return staToVerilog2(sta_name); } -static string +static std::string staToVerilog(const char *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. - string escaped_name = "\\"; + std::string escaped_name = "\\"; bool escaped = false; for (const char *s = sta_name; *s ; s++) { char ch = s[0]; @@ -110,17 +108,17 @@ staToVerilog(const char *sta_name) return escaped_name; } else - return string(sta_name); + return std::string(sta_name); } -static string +static std::string staToVerilog2(const char *sta_name) { constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; // Leave room for leading escape and trailing space if the name // needs to be escaped. - string escaped_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++) { @@ -150,37 +148,37 @@ staToVerilog2(const char *sta_name) return escaped_name; } else - return string(sta_name); + return std::string(sta_name); } //////////////////////////////////////////////////////////////// -string -moduleVerilogToSta(const string *module_name) +std::string +moduleVerilogToSta(const std::string *module_name) { return verilogToSta(module_name); } -string -instanceVerilogToSta(const string *inst_name) +std::string +instanceVerilogToSta(const std::string *inst_name) { return verilogToSta(inst_name); } -string -netVerilogToSta(const string *net_name) +std::string +netVerilogToSta(const std::string *net_name) { return verilogToSta(net_name); } -string -portVerilogToSta(const string *port_name) +std::string +portVerilogToSta(const std::string *port_name) { return verilogToSta(port_name); } -static string -verilogToSta(const string *verilog_name) +static std::string +verilogToSta(const std::string *verilog_name) { if (verilog_name->front() == '\\') { constexpr char divider = '/'; @@ -190,7 +188,7 @@ verilogToSta(const string *verilog_name) size_t verilog_name_length = verilog_name->size(); if (isspace(verilog_name->back())) verilog_name_length--; - string sta_name; + std::string sta_name; // Ignore leading '\'. for (size_t i = 1; i < verilog_name_length; i++) { char ch = verilog_name->at(i); @@ -205,7 +203,7 @@ verilogToSta(const string *verilog_name) return sta_name; } else - return string(*verilog_name); + return std::string(*verilog_name); } } // namespace diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index dd8666643..f6a156ff0 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -45,8 +45,6 @@ namespace sta { -using std::max; - ConcreteParasitic::~ConcreteParasitic() { } @@ -620,7 +618,7 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, node = new ConcreteParasiticNode(net, id, network->highestNetAbove(net1) != net_); sub_nodes_[net_id] = node; if (net == net_) - max_node_id_ = max((int) max_node_id_, id); + max_node_id_ = std::max((int) max_node_id_, id); } else node = id_node->second; diff --git a/parasitics/ReduceParasitics.cc b/parasitics/ReduceParasitics.cc index 1650f8e3e..311ef35c9 100644 --- a/parasitics/ReduceParasitics.cc +++ b/parasitics/ReduceParasitics.cc @@ -24,6 +24,7 @@ #include "ReduceParasitics.hh" +#include #include #include @@ -38,8 +39,6 @@ namespace sta { -using std::max; - typedef std::map ParasiticNodeValueMap; typedef std::map ResistorCurrentMap; typedef std::set ParasiticResistorSet; @@ -174,7 +173,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin, + pinCapacitance(node); y1 = dwn_cap; y2 = y3 = 0.0; - max_resistance = max(max_resistance, src_resistance); + max_resistance = std::max(max_resistance, src_resistance); visit(node); ParasiticResistorSeq &resistors = resistor_map_[node]; diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index b27efe6c4..7d0751f80 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -43,8 +43,6 @@ namespace sta { -using std::string; - bool readSpefFile(const std::string &filename, Instance *instance, @@ -616,7 +614,7 @@ SpefTriple::value(int index) const //////////////////////////////////////////////////////////////// SpefScanner::SpefScanner(std::istream *stream, - const string &filename, + const std::string &filename, SpefReader *reader, Report *report) : yyFlexLexer(stream), diff --git a/power/Power.cc b/power/Power.cc index 2faaef231..f0b590fa5 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -72,13 +72,6 @@ namespace sta { -using std::abs; -using std::max; -using std::min; -using std::isnormal; -using std::vector; -using std::map; - static bool isPositiveUnate(const LibertyCell *cell, const LibertyPort *from, @@ -734,7 +727,7 @@ percentChange(float value, return 1.0; } else - return abs(value - prev) / prev; + return std::abs(value - prev) / prev; } // Return true if the activity changed. @@ -1837,7 +1830,7 @@ Power::clockMinPeriod(const Sdc *sdc) if (!clks.empty()) { float min_period = INF; for (const Clock *clk : clks) - min_period = min(min_period, clk->period()); + min_period = std::min(min_period, clk->period()); return min_period; } else @@ -1963,7 +1956,7 @@ PwrActivity::check() // Densities can get very small from multiplying probabilities // through deep chains of logic. Clip them to prevent floating // point anomalies. - if (abs(density_) < min_density) + if (std::abs(density_) < min_density) density_ = 0.0; } diff --git a/power/SaifReader.cc b/power/SaifReader.cc index 3a758ba2d..bd62602aa 100644 --- a/power/SaifReader.cc +++ b/power/SaifReader.cc @@ -26,6 +26,7 @@ #include #include +#include #include "Error.hh" #include "Debug.hh" @@ -42,9 +43,6 @@ namespace sta { -using std::string; -using std::min; - bool readSaif(const char *filename, const char *scope, @@ -129,9 +127,9 @@ SaifReader::instancePush(const char *instance_name) // Check for a match to the annotation scope. saif_scope_.push_back(instance_name); - string saif_scope; + std::string saif_scope; bool first = true; - for (string &inst : saif_scope_) { + for (std::string &inst : saif_scope_) { if (!first) saif_scope += sdc_network_->pathDivider(); saif_scope += inst; @@ -167,7 +165,7 @@ SaifReader::setNetDurations(const char *net_name, if (in_scope_level_ > 0) { Instance *parent = path_.empty() ? sdc_network_->topInstance() : path_.back(); if (parent) { - string unescaped_name = unescaped(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 @@ -194,10 +192,10 @@ SaifReader::setNetDurations(const char *net_name, stringDelete(net_name); } -string +std::string SaifReader::unescaped(const char *token) { - string unescaped; + std::string unescaped; for (const char *t = token; *t; t++) { char ch = *t; if (ch != escape_) @@ -211,7 +209,7 @@ SaifReader::unescaped(const char *token) //////////////////////////////////////////////////////////////// SaifScanner::SaifScanner(std::istream *stream, - const string &filename, + const std::string &filename, SaifReader *reader, Report *report) : yyFlexLexer(stream), diff --git a/power/VcdParse.cc b/power/VcdParse.cc index 93d6625cf..83e171cf2 100644 --- a/power/VcdParse.cc +++ b/power/VcdParse.cc @@ -35,10 +35,6 @@ namespace sta { -using std::vector; -using std::string; -using std::isspace; - // Very imprecise syntax definition // https://en.wikipedia.org/wiki/Value_change_dump#Structure.2FSyntax // Much better syntax definition @@ -125,7 +121,7 @@ VcdParse::VcdParse(Report *report, void VcdParse::parseTimescale() { - vector tokens = readStmtTokens(); + std::vector tokens = readStmtTokens(); if (tokens.size() == 1) { size_t last; double time_scale = std::stod(tokens[0], &last); @@ -140,7 +136,7 @@ VcdParse::parseTimescale() } void -VcdParse::setTimeUnit(const string &time_unit, +VcdParse::setTimeUnit(const std::string &time_unit, double time_scale) { double time_unit_scale = 1.0; @@ -177,19 +173,19 @@ static EnumNameMap vcd_var_type_map = void VcdParse::parseVar() { - vector tokens = readStmtTokens(); + std::vector tokens = readStmtTokens(); if (tokens.size() == 4 || tokens.size() == 5) { - string type_name = tokens[0]; + 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()); else { - size_t width = stoi(tokens[1]); - string &id = tokens[2]; - string name = tokens[3]; + size_t width = std::stoi(tokens[1]); + std::string &id = tokens[2]; + std::string name = tokens[3]; // iverilog separates bus base name from bit range. if (tokens.size() == 5) { // Preserve space after esacaped name. @@ -208,8 +204,8 @@ VcdParse::parseVar() void VcdParse::parseScope() { - vector tokens = readStmtTokens(); - string &scope = tokens[1]; + std::vector tokens = readStmtTokens(); + std::string &scope = tokens[1]; scope_.push_back(scope); } @@ -223,11 +219,11 @@ VcdParse::parseUpscope() void VcdParse::parseVarValues() { - string token = getToken(); + std::string token = getToken(); while (!token.empty()) { char char0 = toupper(token[0]); if (char0 == '#' && token.size() > 1) { - VcdTime time = stoll(token.substr(1)); + VcdTime time = std::stoll(token.substr(1)); prev_time_ = time_; time_ = time; if (time_ > prev_time_) @@ -238,15 +234,15 @@ VcdParse::parseVarValues() || char0 == 'X' || char0 == 'U' || char0 == 'Z') { - string id = token.substr(1); + std::string id = token.substr(1); if (!reader_->varIdValid(id)) report_->fileError(805, filename_, file_line_, "unknown variable %s", id.c_str()); reader_->varAppendValue(id, time_, char0); } else if (char0 == 'B') { - string bus_value = token.substr(1); - string id = getToken(); + 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()); @@ -261,12 +257,12 @@ VcdParse::parseVarValues() reader_->setTimeMax(time_); } -string +std::string VcdParse::readStmtString() { stmt_line_ = file_line_; - string line; - string token = getToken(); + std::string line; + std::string token = getToken(); while (!token.empty() && token != "$end") { if (!line.empty()) line += " "; @@ -276,12 +272,12 @@ VcdParse::readStmtString() return line; } -vector +std::vector VcdParse::readStmtTokens() { stmt_line_ = file_line_; - vector tokens; - string token = getToken(); + std::vector tokens; + std::string token = getToken(); while (!token.empty() && token != "$end") { tokens.push_back(token); token = getToken(); @@ -289,18 +285,18 @@ VcdParse::readStmtTokens() return tokens; } -string +std::string VcdParse::getToken() { - string token; + std::string token; int ch = gzgetc(stream_); // skip whitespace - while (ch != EOF && isspace(ch)) { + while (ch != EOF && std::isspace(ch)) { if (ch == '\n') file_line_++; ch = gzgetc(stream_); } - while (ch != EOF && !isspace(ch)) { + while (ch != EOF && !std::isspace(ch)) { token.push_back(ch); ch = gzgetc(stream_); } diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 30b0078e0..5e624273a 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -24,8 +24,10 @@ #include "VcdReader.hh" +#include #include #include +#include #include "VcdParse.hh" #include "Debug.hh" @@ -41,13 +43,6 @@ namespace sta { -using std::string; -using std::abs; -using std::min; -using std::to_string; -using std::vector; -using std::unordered_map; - // Transition count and high time for duty cycle for a group of pins // for one bit of vcd ID. class VcdCount @@ -117,9 +112,9 @@ VcdCount::highTime(VcdTime time_max) const //////////////////////////////////////////////////////////////// // VcdCount[bit] -typedef vector VcdCounts; +using VcdCounts = std::vector; // ID -> VcdCount[bit] -typedef unordered_map VcdIdCountsMap; +using VcdIdCountsMap = std::unordered_map; class VcdCountReader : public VcdReader { @@ -134,31 +129,31 @@ class VcdCountReader : public VcdReader double timeScale() const { return time_scale_; } // VcdParse callbacks. - void setDate(const string &) override {} - void setComment(const string &) override {} - void setVersion(const string &) override {} - void setTimeUnit(const string &time_unit, + void setDate(const std::string &) override {} + void setComment(const std::string &) override {} + void setVersion(const std::string &) override {} + void setTimeUnit(const std::string &time_unit, double time_unit_scale, double time_scale) override; void setTimeMin(VcdTime time) override; void setTimeMax(VcdTime time) override; void varMinDeltaTime(VcdTime) override {} - bool varIdValid(const string &id) override; + bool varIdValid(const std::string &id) override; void makeVar(const VcdScope &scope, - const string &name, + const std::string &name, VcdVarType type, size_t width, - const string &id) override; - void varAppendValue(const string &id, + const std::string &id) override; + void varAppendValue(const std::string &id, VcdTime time, char value) override; - void varAppendBusValue(const string &id, + void varAppendBusValue(const std::string &id, VcdTime time, - const string &bus_value) override; + const std::string &bus_value) override; private: - void addVarPin(const string &pin_name, - const string &id, + void addVarPin(const std::string &pin_name, + const std::string &id, size_t width, size_t bit_idx); @@ -189,7 +184,7 @@ VcdCountReader::VcdCountReader(const std::string &scope, } void -VcdCountReader::setTimeUnit(const string &, +VcdCountReader::setTimeUnit(const std::string &, double time_unit_scale, double time_scale) { @@ -209,23 +204,23 @@ VcdCountReader::setTimeMax(VcdTime time) } bool -VcdCountReader::varIdValid(const string &) +VcdCountReader::varIdValid(const std::string &) { return true; } void VcdCountReader::makeVar(const VcdScope &scope, - const string &name, + const std::string &name, VcdVarType type, size_t width, - const string &id) + const std::string &id) { if (type == VcdVarType::wire || type == VcdVarType::reg) { - string path_name; + std::string path_name; bool first = true; - for (const string &context : scope) { + for (const std::string &context : scope) { if (!first) path_name += '/'; path_name += context; @@ -238,25 +233,25 @@ VcdCountReader::makeVar(const VcdScope &scope, path_name += '/'; path_name += name; // Strip the scope from the name. - string var_scoped = path_name.substr(scope_length + 1); + std::string var_scoped = path_name.substr(scope_length + 1); if (width == 1) { - 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; - string bus_name; + std::string bus_name; int from, to; parseBusName(var_scoped.c_str(), '[', ']', '\\', is_bus, is_range, bus_name, from, to, subscript_wild); if (is_bus) { - 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++) { - string pin_name = sta_bus_name; + std::string pin_name = sta_bus_name; pin_name += '['; - pin_name += to_string(bus_bit); + pin_name += std::to_string(bus_bit); pin_name += ']'; addVarPin(pin_name, id, width, bit_idx); bit_idx++; @@ -264,9 +259,9 @@ VcdCountReader::makeVar(const VcdScope &scope, } else { for (int bus_bit = to; bus_bit >= from; bus_bit--) { - string pin_name = sta_bus_name; + std::string pin_name = sta_bus_name; pin_name += '['; - pin_name += to_string(bus_bit); + pin_name += std::to_string(bus_bit); pin_name += ']'; addVarPin(pin_name, id, width, bit_idx); bit_idx++; @@ -281,8 +276,8 @@ VcdCountReader::makeVar(const VcdScope &scope, } void -VcdCountReader::addVarPin(const string &pin_name, - const string &id, +VcdCountReader::addVarPin(const std::string &pin_name, + const std::string &id, size_t width, size_t bit_idx) { @@ -303,7 +298,7 @@ VcdCountReader::addVarPin(const string &pin_name, } void -VcdCountReader::varAppendValue(const string &id, +VcdCountReader::varAppendValue(const std::string &id, VcdTime time, char value) { @@ -329,9 +324,9 @@ VcdCountReader::varAppendValue(const string &id, } void -VcdCountReader::varAppendBusValue(const string &id, +VcdCountReader::varAppendBusValue(const std::string &id, VcdTime time, - const string &bus_value) + const std::string &bus_value) { const auto &itr = vcd_count_map_.find(id); if (itr != vcd_count_map_.end()) { @@ -476,7 +471,7 @@ ReadVcdActivities::checkClkPeriod(const Pin *pin, sdc_network_->pathName(pin)); else { double clk_period = clk->period(); - if (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(), diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 70d131c98..25ba0d784 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -38,8 +38,6 @@ namespace sta { -using std::string; - static bool thrusIntersectPts(ExceptionThruSeq *thrus1, ExceptionThruSeq *thrus2, @@ -326,7 +324,7 @@ ExceptionPath::intersectsPts(ExceptionPath *exception, const char * ExceptionPath::fromThruToString(const Network *network) const { - string str; + std::string str; if (min_max_ != MinMaxAll::all()) { str += " -"; str += min_max_->to_string(); @@ -1186,7 +1184,7 @@ ExceptionFromTo::deletePinBefore(const Pin *pin, const char * ExceptionFromTo::asString(const Network *network) const { - string str; + std::string str; str += " "; str += cmdKeyword(); str += " {"; @@ -1360,7 +1358,7 @@ ExceptionTo::clone(const Network *network) const char * ExceptionTo::asString(const Network *network) const { - string str; + std::string str; if (hasObjects()) str += ExceptionFromTo::asString(network); @@ -1679,7 +1677,7 @@ ExceptionThru::~ExceptionThru() const char * ExceptionThru::asString(const Network *network) const { - string str; + std::string str; bool first = true; int obj_count = 0; if (pins_) { diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 85ee1950a..001d75671 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -62,8 +62,6 @@ namespace sta { -using std::swap; - bool ClockPairLess::operator()(const ClockPair &pair1, const ClockPair &pair2) const @@ -693,10 +691,10 @@ void Sdc::swapDeratingFactors(Sdc *sdc1, Sdc *sdc2) { - swap(sdc1->derating_factors_, sdc2->derating_factors_); - swap(sdc1->net_derating_factors_, sdc2->net_derating_factors_); - swap(sdc1->inst_derating_factors_, sdc2->inst_derating_factors_); - swap(sdc1->cell_derating_factors_, sdc2->cell_derating_factors_); + std::swap(sdc1->derating_factors_, sdc2->derating_factors_); + std::swap(sdc1->net_derating_factors_, sdc2->net_derating_factors_); + std::swap(sdc1->inst_derating_factors_, sdc2->inst_derating_factors_); + std::swap(sdc1->cell_derating_factors_, sdc2->cell_derating_factors_); } void @@ -1818,7 +1816,7 @@ void Sdc::swapClockInsertions(Sdc *sdc1, Sdc *sdc2) { - swap(sdc1->clk_insertions_, sdc2->clk_insertions_); + std::swap(sdc1->clk_insertions_, sdc2->clk_insertions_); } void @@ -2825,17 +2823,17 @@ void Sdc::swapPortDelays(Sdc *sdc1, Sdc *sdc2) { - swap(sdc1->input_delays_, sdc2->input_delays_); - swap(sdc1->input_delay_pin_map_, sdc2->input_delay_pin_map_); - swap(sdc1->input_delay_ref_pin_map_, sdc2->input_delay_ref_pin_map_); - swap(sdc1->input_delay_leaf_pin_map_, sdc2->input_delay_leaf_pin_map_); - swap(sdc1->input_delay_internal_pin_map_, sdc2->input_delay_internal_pin_map_); - swap(sdc1->input_delay_index_, sdc2->input_delay_index_); + std::swap(sdc1->input_delays_, sdc2->input_delays_); + std::swap(sdc1->input_delay_pin_map_, sdc2->input_delay_pin_map_); + std::swap(sdc1->input_delay_ref_pin_map_, sdc2->input_delay_ref_pin_map_); + std::swap(sdc1->input_delay_leaf_pin_map_, sdc2->input_delay_leaf_pin_map_); + std::swap(sdc1->input_delay_internal_pin_map_, sdc2->input_delay_internal_pin_map_); + std::swap(sdc1->input_delay_index_, sdc2->input_delay_index_); - swap(sdc1->output_delays_, sdc2->output_delays_); - swap(sdc1->output_delay_pin_map_, sdc2->output_delay_pin_map_); - swap(sdc1->output_delay_ref_pin_map_, sdc2->output_delay_ref_pin_map_); - swap(sdc1->output_delay_leaf_pin_map_, sdc2->output_delay_leaf_pin_map_); + std::swap(sdc1->output_delays_, sdc2->output_delays_); + std::swap(sdc1->output_delay_pin_map_, sdc2->output_delay_pin_map_); + std::swap(sdc1->output_delay_ref_pin_map_, sdc2->output_delay_ref_pin_map_); + std::swap(sdc1->output_delay_leaf_pin_map_, sdc2->output_delay_leaf_pin_map_); } //////////////////////////////////////////////////////////////// @@ -3374,8 +3372,8 @@ void Sdc::swapPortExtCaps(Sdc *sdc1, Sdc *sdc2) { - swap(sdc1->port_ext_cap_map_, sdc2->port_ext_cap_map_); - swap(sdc1->net_wire_cap_map_, sdc2->net_wire_cap_map_); + std::swap(sdc1->port_ext_cap_map_, sdc2->port_ext_cap_map_); + std::swap(sdc1->net_wire_cap_map_, sdc2->net_wire_cap_map_); } //////////////////////////////////////////////////////////////// diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index e317a8a4d..9ca258264 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include "ContainerHelpers.hh" #include "Zlib.hh" @@ -63,8 +64,6 @@ namespace sta { -using std::string; - typedef std::set ClockSenseSet; typedef std::vector ClockSenseSeq; @@ -1167,7 +1166,7 @@ WriteSdc::writeDisabledEdgeSense(Edge *edge) const { gzprintf(stream_, "set_disable_timing "); const char *sense = to_string(edge->sense()); - string filter; + std::string filter; stringPrint(filter, "sense == %s", sense); writeGetTimingArcs(edge, filter.c_str()); gzprintf(stream_, "\n"); diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 9e9451306..59beb9f9d 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -26,6 +26,7 @@ #include #include +#include #include "ContainerHelpers.hh" #include "Zlib.hh" @@ -45,9 +46,6 @@ namespace sta { -using std::string; -using std::to_string; - class SdfTriple { public: @@ -69,14 +67,14 @@ class SdfPortSpec const std::string *port, const std::string *cond); ~SdfPortSpec(); - const string *port() const { return port_; } + const std::string *port() const { return port_; } const Transition *transition() const { return tr_; } - const string *cond() const { return cond_; } + const std::string *cond() const { return cond_; } private: const Transition *tr_; - const string *port_; - const string *cond_; // timing checks only + const std::string *port_; + const std::string *cond_; // timing checks only }; bool @@ -162,7 +160,7 @@ SdfReader::setDivider(char divider) void SdfReader::setTimescale(float multiplier, - const string *units) + const std::string *units) { if (multiplier == 1.0 || multiplier == 10.0 @@ -182,8 +180,8 @@ SdfReader::setTimescale(float multiplier, } void -SdfReader::interconnect(const string *from_pin_name, - const string *to_pin_name, +SdfReader::interconnect(const std::string *from_pin_name, + const std::string *to_pin_name, SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. @@ -225,7 +223,7 @@ SdfReader::interconnect(const string *from_pin_name, } void -SdfReader::port(const string *to_pin_name, +SdfReader::port(const std::string *to_pin_name, SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. @@ -296,13 +294,13 @@ SdfReader::setEdgeDelays(Edge *edge, } void -SdfReader::setCell(const string *cell_name) +SdfReader::setCell(const std::string *cell_name) { cell_name_ = cell_name; } void -SdfReader::setInstance(const string *instance_name) +SdfReader::setInstance(const std::string *instance_name) { if (instance_name) { if (*instance_name == "*") { @@ -344,13 +342,13 @@ SdfReader::cellFinish() void SdfReader::iopath(SdfPortSpec *from_edge, - const string *to_port_name, + const std::string *to_port_name, SdfTripleSeq *triples, - const string *cond, + const std::string *cond, bool condelse) { if (instance_) { - const string *from_port_name = from_edge->port(); + const std::string *from_port_name = from_edge->port(); Cell *cell = network_->cell(instance_); Port *from_port = findPort(cell, from_port_name); Port *to_port = findPort(cell, to_port_name); @@ -420,7 +418,7 @@ SdfReader::iopath(SdfPortSpec *from_edge, Port * SdfReader::findPort(const Cell *cell, - const string *port_name) + const std::string *port_name) { Port *port = network_->findPort(cell, port_name->c_str()); if (port == nullptr) @@ -437,8 +435,8 @@ SdfReader::timingCheck(const TimingRole *role, SdfTriple *triple) { if (instance_) { - const string *data_port_name = data_edge->port(); - const string *clk_port_name = clk_edge->port(); + const std::string *data_port_name = data_edge->port(); + const std::string *clk_port_name = clk_edge->port(); Cell *cell = network_->cell(instance_); Port *data_port = findPort(cell, data_port_name); Port *clk_port = findPort(cell, clk_port_name); @@ -515,8 +513,8 @@ SdfReader::annotateCheckEdges(Pin *data_pin, bool match_generic) { bool matched = false; - const string *cond_start = data_edge->cond(); - const string *cond_end = clk_edge->cond(); + const std::string *cond_start = data_edge->cond(); + const std::string *cond_end = clk_edge->cond(); // Timing check graph edges from clk to data. Vertex *to_vertex = graph_->pinLoadVertex(data_pin); // Fanin < fanout, so search for driver from load. @@ -557,7 +555,7 @@ SdfReader::timingCheckWidth(SdfPortSpec *edge, // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_) && instance_) { - const string *port_name = edge->port(); + const std::string *port_name = edge->port(); Cell *cell = network_->cell(instance_); Port *port = findPort(cell, port_name); if (port) { @@ -604,8 +602,8 @@ SdfReader::timingCheckSetupHold1(SdfPortSpec *data_edge, const TimingRole *setup_role, const TimingRole *hold_role) { - const string *data_port_name = data_edge->port(); - const string *clk_port_name = clk_edge->port(); + const std::string *data_port_name = data_edge->port(); + const std::string *clk_port_name = clk_edge->port(); Cell *cell = network_->cell(instance_); Port *data_port = findPort(cell, data_port_name); Port *clk_port = findPort(cell, clk_port_name); @@ -626,7 +624,7 @@ SdfReader::timingCheckPeriod(SdfPortSpec *edge, // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_) && instance_) { - const string *port_name = edge->port(); + const std::string *port_name = edge->port(); Cell *cell = network_->cell(instance_); Port *port = findPort(cell, port_name); if (port) { @@ -683,7 +681,7 @@ SdfReader::device(SdfTripleSeq *triples) } void -SdfReader::device(const string *to_port_name, +SdfReader::device(const std::string *to_port_name, SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. @@ -799,7 +797,7 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, } bool -SdfReader::condMatch(const string *sdf_cond, +SdfReader::condMatch(const std::string *sdf_cond, const std::string &lib_cond) { // If the sdf is not conditional it matches any library condition. @@ -828,24 +826,24 @@ SdfReader::condMatch(const string *sdf_cond, SdfPortSpec * SdfReader::makePortSpec(const Transition *tr, - const string *port, - const string *cond) + const std::string *port, + const std::string *cond) { return new SdfPortSpec(tr, port, cond); } SdfPortSpec * -SdfReader::makeCondPortSpec(const string *cond_port) +SdfReader::makeCondPortSpec(const std::string *cond_port) { // Search from end to find port name because condition may contain spaces. - string cond_port1(*cond_port); + std::string cond_port1(*cond_port); trimRight(cond_port1); auto port_idx = cond_port1.find_last_of(" "); if (port_idx != cond_port1.npos) { - string *port1 = new string(cond_port1.substr(port_idx + 1)); + std::string *port1 = new std::string(cond_port1.substr(port_idx + 1)); auto cond_end = cond_port1.find_last_not_of(" ", port_idx); if (cond_end != cond_port1.npos) { - string *cond1 = new string(cond_port1.substr(0, cond_end + 1)); + std::string *cond1 = new std::string(cond_port1.substr(0, cond_end + 1)); SdfPortSpec *port_spec = new SdfPortSpec(Transition::riseFall(), port1, cond1); @@ -913,13 +911,13 @@ SdfReader::setInIncremental(bool incr) in_incremental_ = incr; } -string * -SdfReader::unescaped(const string *token) +std::string * +SdfReader::unescaped(const std::string *token) { char path_escape = network_->pathEscape(); char path_divider = network_->pathDivider(); size_t token_length = token->size(); - string *unescaped = new string; + std::string *unescaped = new std::string; for (size_t i = 0; i < token_length; i++) { char ch = (*token)[i]; if (ch == escape_) { @@ -955,11 +953,11 @@ SdfReader::unescaped(const string *token) return unescaped; } -string * -SdfReader::makePath(const string *head, - const string *tail) +std::string * +SdfReader::makePath(const std::string *head, + const std::string *tail) { - string *path = new string(*head); + std::string *path = new std::string(*head); *path += network_->pathDivider(); *path += *tail; delete head; @@ -967,13 +965,13 @@ SdfReader::makePath(const string *head, return path; } -string * -SdfReader::makeBusName(string *base_name, +std::string * +SdfReader::makeBusName(std::string *base_name, int index) { - string *bus_name = unescaped(base_name); + std::string *bus_name = unescaped(base_name); *bus_name += '['; - *bus_name += to_string(index); + *bus_name += std::to_string(index); *bus_name += ']'; delete base_name; return bus_name; @@ -1006,10 +1004,10 @@ SdfReader::sdfError(int id, } Pin * -SdfReader::findPin(const string *name) +SdfReader::findPin(const std::string *name) { if (path_) { - string path_name(path_); + std::string path_name(path_); path_name += divider_; path_name += *name; Pin *pin = network_->findPin(path_name.c_str()); @@ -1020,9 +1018,9 @@ SdfReader::findPin(const string *name) } Instance * -SdfReader::findInstance(const string *name) +SdfReader::findInstance(const std::string *name) { - string inst_name; + std::string inst_name; if (path_) { inst_name = path_; inst_name += divider_; @@ -1039,8 +1037,8 @@ SdfReader::findInstance(const string *name) //////////////////////////////////////////////////////////////// SdfPortSpec::SdfPortSpec(const Transition *tr, - const string *port, - const string *cond) : + const std::string *port, + const std::string *cond) : tr_(tr), port_(port), cond_(cond) @@ -1084,7 +1082,7 @@ SdfTriple::hasValue() const //////////////////////////////////////////////////////////////// SdfScanner::SdfScanner(std::istream *stream, - const string &filename, + const std::string &filename, SdfReader *reader, Report *report) : yyFlexLexer(stream), diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index f00950170..f42d54a94 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -45,8 +45,6 @@ namespace sta { -using std::string; - class SdfWriter : public StaState { public: @@ -108,10 +106,10 @@ class SdfWriter : public StaState void writeSdfTriple(float min, float max); void writeSdfDelay(double delay); - string sdfPortName(const Pin *pin); - string sdfPathName(const Pin *pin); - string sdfPathName(const Instance *inst); - string sdfName(const Instance *inst); + std::string sdfPortName(const Pin *pin); + std::string sdfPathName(const Pin *pin); + std::string sdfPathName(const Instance *inst); + std::string sdfName(const Instance *inst); private: char sdf_divider_; @@ -315,8 +313,8 @@ SdfWriter::writeInterconnectFromPin(Pin *drvr_pin) Edge *edge = edge_iter.next(); if (edge->isWire()) { Pin *load_pin = edge->to(graph_)->pin(); - string drvr_pin_name = sdfPathName(drvr_pin); - string load_pin_name = sdfPathName(load_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()); @@ -347,7 +345,7 @@ SdfWriter::writeInstHeader(const Instance *inst) { gzprintf(stream_, " (CELL\n"); gzprintf(stream_, " (CELLTYPE \"%s\")\n", network_->cellName(inst)); - string inst_name = sdfPathName(inst); + std::string inst_name = sdfPathName(inst); gzprintf(stream_, " (INSTANCE %s)\n", inst_name.c_str()); } @@ -392,8 +390,8 @@ SdfWriter::writeIopaths(const Instance *inst, gzprintf(stream_, " (COND %s\n", sdf_cond.c_str()); gzprintf(stream_, " "); } - string from_pin_name = sdfPortName(from_pin); - string to_pin_name = sdfPortName(to_pin); + 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()); @@ -661,7 +659,7 @@ SdfWriter::writeCheck(Edge *edge, if (!sdf_cond_start.empty()) gzprintf(stream_, "(COND %s ", sdf_cond_start.c_str()); - string to_pin_name = sdfPortName(to_pin); + std::string to_pin_name = sdfPortName(to_pin); if (use_data_edge) { gzprintf(stream_, "(%s %s)", sdfEdge(arc->toEdge()), @@ -678,7 +676,7 @@ SdfWriter::writeCheck(Edge *edge, if (!sdf_cond_end.empty()) gzprintf(stream_, "(COND %s ", sdf_cond_end.c_str()); - string from_pin_name = sdfPortName(from_pin); + std::string from_pin_name = sdfPortName(from_pin); if (use_clk_edge) gzprintf(stream_, "(%s %s)", sdfEdge(arc->fromEdge()), @@ -704,7 +702,7 @@ SdfWriter::writeWidthCheck(const Pin *pin, float min_width, float max_width) { - string pin_name = sdfPortName(pin); + std::string pin_name = sdfPortName(pin); gzprintf(stream_, " (WIDTH (%s %s) ", sdfEdge(hi_low->asTransition()), pin_name.c_str()); @@ -716,7 +714,7 @@ void SdfWriter::writePeriodCheck(const Pin *pin, float min_period) { - string pin_name = sdfPortName(pin); + std::string pin_name = sdfPortName(pin); gzprintf(stream_, " (PERIOD %s ", pin_name.c_str()); writeSdfTriple(min_period, min_period); gzprintf(stream_, ")\n"); @@ -734,16 +732,16 @@ SdfWriter::sdfEdge(const Transition *tr) //////////////////////////////////////////////////////////////// -string +std::string SdfWriter::sdfPathName(const Pin *pin) { Instance *inst = network_->instance(pin); if (network_->isTopInstance(inst)) return sdfPortName(pin); else { - string inst_path = sdfPathName(inst); - string port_name = sdfPortName(pin); - string sdf_name = inst_path; + std::string inst_path = sdfPathName(inst); + std::string port_name = sdfPortName(pin); + std::string sdf_name = inst_path; sdf_name += sdf_divider_; sdf_name += port_name; return sdf_name; @@ -751,15 +749,15 @@ SdfWriter::sdfPathName(const Pin *pin) } // Based on Network::pathName. -string +std::string SdfWriter::sdfPathName(const Instance *instance) { InstanceSeq inst_path; network_->path(instance, inst_path); - string path_name; + std::string path_name; while (!inst_path.empty()) { const Instance *inst = inst_path.back(); - string inst_name = sdfName(inst); + std::string inst_name = sdfName(inst); path_name += inst_name; inst_path.pop_back(); if (!inst_path.empty()) @@ -769,11 +767,11 @@ SdfWriter::sdfPathName(const Instance *instance) } // Escape for non-alpha numeric characters. -string +std::string SdfWriter::sdfName(const Instance *inst) { const char *name = network_->name(inst); - string sdf_name; + std::string sdf_name; const char *p = name; while (*p) { char ch = *p; @@ -789,12 +787,12 @@ SdfWriter::sdfName(const Instance *inst) return sdf_name; } -string +std::string SdfWriter::sdfPortName(const Pin *pin) { const char *name = network_->portName(pin); size_t name_length = strlen(name); - string sdf_name; + std::string sdf_name; constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc index 9c692fba1..f05de24e8 100644 --- a/search/CheckTiming.cc +++ b/search/CheckTiming.cc @@ -46,8 +46,6 @@ namespace sta { -using std::string; - CheckTiming::CheckTiming(StaState *sta) : StaState(sta), mode_(nullptr), @@ -202,7 +200,7 @@ CheckTiming::checkLoops() loop_count++; } if (loop_count > 0) { - string error_msg; + std::string error_msg; errorMsgSubst("Warning: There %is %d combinational loop%s in the design.", loop_count, error_msg); CheckError *error = new CheckError; @@ -362,7 +360,7 @@ CheckTiming::pushPinErrors(const char *msg, { if (!pins.empty()) { CheckError *error = new CheckError; - string error_msg; + std::string error_msg; errorMsgSubst(msg, pins.size(), error_msg); // Copy the error strings because the error deletes them when it // is deleted. @@ -384,7 +382,7 @@ CheckTiming::pushClkErrors(const char *msg, { if (!clks.empty()) { CheckError *error = new CheckError; - string error_msg; + std::string error_msg; errorMsgSubst(msg, clks.size(), error_msg); // Copy the error strings because the error deletes them when it // is deleted. @@ -404,7 +402,7 @@ CheckTiming::pushClkErrors(const char *msg, void CheckTiming::errorMsgSubst(const char *msg, int obj_count, - string &error_msg) + std::string &error_msg) { for (const char *s = msg; *s; s++) { char ch = *s; diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 9a34fe5ea..09cdf2046 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -49,8 +49,6 @@ namespace sta { -using std::abs; - ClkSkews::ClkSkews(StaState *sta) : StaState(sta), include_internal_latency_(true), @@ -148,7 +146,7 @@ ClkSkews::findWorstClkSkew(const SceneSeq &scenes, float worst_skew = 0.0; for (const auto& [clk, clk_skews] : skews_) { float skew = clk_skews[setup_hold->index()].skew(); - if (abs(skew) > abs(worst_skew)) + if (std::abs(skew) > std::abs(worst_skew)) worst_skew = skew; } return worst_skew; @@ -210,8 +208,8 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, ClkSkew &partial_skew_val = partial_skew[setup_hold_idx]; float partial_skew1 = partial_skew_val.skew(); float final_skew1 = final_skew.skew(); - if (abs(partial_skew1) > abs(final_skew1) - || (fuzzyEqual(abs(partial_skew1), abs(final_skew1)) + if (std::abs(partial_skew1) > std::abs(final_skew1) + || (fuzzyEqual(std::abs(partial_skew1), std::abs(final_skew1)) // Break ties based on source/target path names. && ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, this))) final_skew = partial_skew_val; @@ -325,7 +323,7 @@ ClkSkews::findClkSkew(Vertex *src_vertex, delayAsString(probe.crpr(this), this), time_unit->asString(probe.skew())); if (clk_skew.srcPath() == nullptr - || abs(probe.skew()) > abs(clk_skew.skew())) + || std::abs(probe.skew()) > std::abs(clk_skew.skew())) clk_skew = probe; } } diff --git a/search/Crpr.cc b/search/Crpr.cc index c22265413..e782aef03 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -24,8 +24,8 @@ #include "Crpr.hh" +#include #include // abs -#include #include "Debug.hh" #include "Network.hh" @@ -44,9 +44,6 @@ namespace sta { -using std::min; -using std::abs; - CheckCrpr::CheckCrpr(StaState *sta) : StaState(sta) { @@ -60,11 +57,10 @@ CheckCrpr::maxCrpr(const ClkInfo *clk_info) const Path *crpr_clk_path = clk_info->crprClkPath(this); if (crpr_clk_path) { Arrival other_arrival = otherMinMaxArrival(crpr_clk_path); - float crpr_diff = abs(delayAsFloat(crpr_clk_path->arrival(), - EarlyLate::late(), - this) - - delayAsFloat(other_arrival, EarlyLate::early(), - this)); + float crpr_diff = std::abs(delayAsFloat(crpr_clk_path->arrival(), + EarlyLate::late(), this) + - delayAsFloat(other_arrival, EarlyLate::early(), + this)); return crpr_diff; } return 0.0F; @@ -284,7 +280,7 @@ CheckCrpr::findCrpr1(const Path *src_clk_path, 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 = abs(delayAsFloat(src_arrival) - src_clk_time + float crpr_mean = std::abs(delayAsFloat(src_arrival) - src_clk_time - (delayAsFloat(tgt_arrival) - tgt_clk_time)); // Remove the sigma from both source and target path arrivals. float crpr_sigma2 = delaySigma2(src_arrival, src_el) @@ -300,7 +296,7 @@ CheckCrpr::findCrpr1(const Path *src_clk_path, delayAsString(src_delta, this)); debugPrint(debug_, "crpr", 2, " tgt delta %s", delayAsString(tgt_delta, this)); - float common_delay = min(src_delta, tgt_delta); + float common_delay = std::min(src_delta, tgt_delta); debugPrint(debug_, "crpr", 2, " %s delta %s", network_->pathName(src_clk_path->pin(this)), delayAsString(common_delay, this)); @@ -312,7 +308,7 @@ float CheckCrpr::crprArrivalDiff(const Path *path) { Arrival other_arrival = otherMinMaxArrival(path); - float crpr_diff = abs(delayAsFloat(path->arrival()) + float crpr_diff = std::abs(delayAsFloat(path->arrival()) - delayAsFloat(other_arrival)); return crpr_diff; } diff --git a/search/Genclks.cc b/search/Genclks.cc index 66b8cd143..bbb266213 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -24,6 +24,8 @@ #include "Genclks.hh" +#include + #include "ContainerHelpers.hh" #include "Stats.hh" #include "Debug.hh" @@ -47,8 +49,6 @@ namespace sta { -using std::max; - class GenclkInfo { public: @@ -151,7 +151,7 @@ Genclks::clkPinMaxLevel(const Clock *clk) const Level max_level = 0; for (const Pin *pin : clk->leafPins()) { Vertex *vertex = srcPath(pin); - max_level = max(max_level, vertex->level()); + max_level = std::max(max_level, vertex->level()); } return max_level; } diff --git a/search/Levelize.cc b/search/Levelize.cc index df6c983d5..d04891bfa 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -25,6 +25,7 @@ #include "Levelize.hh" #include +#include #include #include "ContainerHelpers.hh" @@ -43,8 +44,6 @@ namespace sta { -using std::max; - Levelize::Levelize(StaState *sta) : StaState(sta), levelized_(false), @@ -500,16 +499,16 @@ Levelize::assignLevels(VertexSeq &topo_sorted) Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); if (searchThru(edge)) - setLevel(to_vertex, 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, max(to_vertex->level(), - vertex->level() + level_space_)); + setLevel(to_vertex, std::max(to_vertex->level(), + vertex->level() + level_space_)); } } } @@ -541,7 +540,7 @@ Levelize::setLevel(Vertex *vertex, vertex->to_string(this).c_str(), level); vertex->setLevel(level); - max_level_ = max(level, max_level_); + max_level_ = std::max(level, max_level_); if (level >= Graph::vertex_level_max) report_->critical(616, "maximum logic level exceeded"); } @@ -676,7 +675,7 @@ Levelize::setLevelIncr(Vertex *vertex, observer_->levelChangedBefore(vertex); vertex->setLevel(level); } - max_level_ = max(level, max_level_); + max_level_ = std::max(level, max_level_); if (level >= Graph::vertex_level_max) criticalError(618, "maximum logic level exceeded"); } diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 1c605a265..d709f4779 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -26,6 +26,7 @@ #include "MakeTimingModelPvt.hh" #include +#include #include #include "Debug.hh" @@ -51,11 +52,6 @@ namespace sta { -using std::string; -using std::min; -using std::max; -using std::make_shared; - LibertyLibrary * makeTimingModel(const char *lib_name, const char *cell_name, @@ -305,7 +301,7 @@ MakeEndTimingArcs::visit(PathEnd *path_end) margins.value(input_rf_, min_max, max_margin, max_exists); // Always max margin, even for min/hold checks. margins.setValue(input_rf_, min_max, - max_exists ? max(max_margin, delay1) : delay1); + max_exists ? std::max(max_margin, delay1) : delay1); } } @@ -606,7 +602,7 @@ MakeTimingModel::makeScalarCheckModel(float value, ScaleFactorType scale_factor_type, const RiseFall *rf) { - TablePtr table = make_shared
(value); + TablePtr table = std::make_shared
(value); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *table_model = new TableModel(table, tbl_template, @@ -620,8 +616,8 @@ MakeTimingModel::makeGateModelScalar(Delay delay, Slew slew, const RiseFall *rf) { - TablePtr delay_table = make_shared
(delayAsFloat(delay)); - TablePtr slew_table = make_shared
(delayAsFloat(slew)); + TablePtr delay_table = std::make_shared
(delayAsFloat(delay)); + TablePtr slew_table = std::make_shared
(delayAsFloat(slew)); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *delay_model = new TableModel(delay_table, tbl_template, @@ -636,7 +632,7 @@ TimingModel * MakeTimingModel::makeGateModelScalar(Delay delay, const RiseFall *rf) { - TablePtr delay_table = make_shared
(delayAsFloat(delay)); + TablePtr delay_table = std::make_shared
(delayAsFloat(delay)); TableTemplate *tbl_template = library_->findTableTemplate("scalar", TableTemplateType::delay); TableModel *delay_model = new TableModel(delay_table, tbl_template, @@ -708,8 +704,8 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, std::make_shared(TableAxisVariable::total_output_net_capacitance, std::move(axis_values)); - TablePtr delay_table = make_shared
(load_values, load_axis); - TablePtr slew_table = 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); @@ -738,7 +734,7 @@ MakeTimingModel::ensureTableTemplate(const TableTemplate *drvr_template, { TableTemplate *model_template = findKey(template_map_, drvr_template); if (model_template == nullptr) { - string template_name = "template_"; + std::string template_name = "template_"; template_name += std::to_string(tbl_template_index_++); model_template = library_->makeTableTemplate(template_name, diff --git a/search/Property.cc b/search/Property.cc index 07eddc0ac..ba12610a8 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -24,6 +24,9 @@ #include "Property.hh" +#include +#include + #include "StringUtil.hh" #include "MinMax.hh" #include "Transition.hh" @@ -43,22 +46,19 @@ namespace sta { -using std::string; -using std::max; - class PropertyUnknown : public Exception { public: PropertyUnknown(const char *type, const char *property); PropertyUnknown(const char *type, - const string property); + const std::string property); virtual ~PropertyUnknown() {} virtual const char *what() const noexcept; private: const char *type_; - const string property_; + const std::string property_; }; PropertyUnknown::PropertyUnknown(const char *type, @@ -70,7 +70,7 @@ PropertyUnknown::PropertyUnknown(const char *type, } PropertyUnknown::PropertyUnknown(const char *type, - const string property) : + const std::string property) : Exception(), type_(type), property_(property) @@ -556,7 +556,7 @@ PropertyValue::operator=(PropertyValue &&value) noexcept return *this; } -string +std::string PropertyValue::to_string(const Network *network) const { switch (type_) { @@ -683,9 +683,9 @@ Properties::getProperty(const Cell *cell, return PropertyValue(network->name(cell)); else if (property == "full_name") { Library *lib = network->library(cell); - string lib_name = network->name(lib); - string cell_name = network->name(cell); - string full_name = lib_name + network->pathDivider() + cell_name; + std::string lib_name = network->name(lib); + std::string cell_name = network->name(cell); + std::string full_name = lib_name + network->pathDivider() + cell_name; return PropertyValue(full_name); } else if (property == "library") @@ -714,9 +714,9 @@ Properties::getProperty(const LibertyCell *cell, else if (property == "full_name") { Network *network = sta_->cmdNetwork(); LibertyLibrary *lib = cell->libertyLibrary(); - string lib_name = lib->name(); - string cell_name = cell->name(); - string full_name = lib_name + network->pathDivider() + cell_name; + std::string lib_name = lib->name(); + std::string cell_name = cell->name(); + std::string full_name = lib_name + network->pathDivider() + cell_name; return PropertyValue(full_name); } else if (property == "filename") @@ -1099,7 +1099,7 @@ Properties::getProperty(Edge *edge, const std::string &property) { if (property == "full_name") { - string full_name = edge->to_string(sta_); + std::string full_name = edge->to_string(sta_); return PropertyValue(full_name); } if (property == "delay_min_fall") @@ -1159,7 +1159,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(); - string name; + std::string name; stringPrint(name, "%s %s -> %s", cell_name, from, to); return PropertyValue(name); } diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 9122a8057..ad9b9f11a 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -23,6 +23,7 @@ // This notice may not be removed or altered from any source distribution. #include // reverse +#include #include "ReportPath.hh" @@ -63,8 +64,6 @@ namespace sta { -using std::string; - static void hierPinsAbove(const Net *net, const Network *network, @@ -373,7 +372,7 @@ ReportPath::reportPathEndHeader() const void ReportPath::reportPathEndFooter() const { - string header; + std::string header; switch (format_) { case ReportPathFormat::full: case ReportPathFormat::full_clock: @@ -474,7 +473,7 @@ ReportPath::reportFull(const PathEndCheck *end) const reportSlack(end); } -string +std::string ReportPath::checkRoleString(const PathEnd *end) const { return stdstrPrint("library %s time", @@ -486,7 +485,7 @@ ReportPath::reportEndpoint(const PathEndCheck *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); const char *inst_name = cmd_network_->pathName(inst); - string clk_name = tgtClkName(end); + std::string clk_name = tgtClkName(end); const char *rise_fall = asRisingFalling(end->targetClkEndTrans(this)); const TimingRole *check_role = end->checkRole(this); const TimingRole *check_generic_role = check_role->genericRole(); @@ -593,7 +592,7 @@ ReportPath::reportEndpoint(const PathEndLatchCheck *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); const char *inst_name = cmd_network_->pathName(inst); - string clk_name = tgtClkName(end); + 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()); reportEndpoint(inst_name, reason); @@ -625,7 +624,7 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, if (borrow_limit_exists) reportLineTotal("user max time borrow", max_borrow, early_late); else { - string tgt_clk_name = tgtClkName(end); + std::string tgt_clk_name = tgtClkName(end); Arrival tgt_clk_width = end->targetClkWidth(this); const Path *tgt_clk_path = end->targetClkPath(); if (tgt_clk_path->clkInfo(search_)->isPropagated()) { @@ -691,7 +690,7 @@ ReportPath::reportEndpoint(const PathEndPathDelay *end) const else { Instance *inst = network_->instance(end->vertex(this)->pin()); const char *inst_name = cmd_network_->pathName(inst); - string clk_name = tgtClkName(end); + 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()); reportEndpoint(inst_name, reason); @@ -723,7 +722,7 @@ ReportPath::reportFull(const PathEndPathDelay *end) const if (min_max == MinMax::max()) margin = -margin; - string delay_msg = min_max->to_string() + "_delay"; + std::string delay_msg = min_max->to_string() + "_delay"; float delay = path_delay->delay(); reportLine(delay_msg.c_str(), delay, delay, early_late); if (!path_delay->ignoreClkLatency()) { @@ -821,7 +820,7 @@ ReportPath::reportEndpointOutputDelay(const PathEndClkConstrained *end) const if (network_->isTopLevelPort(pin)) { // Pin direction is "output" even for bidirects. if (tgt_clk) { - string clk_name = tgtClkName(end); + std::string clk_name = tgtClkName(end); auto reason = stdstrPrint("output port clocked by %s", clk_name.c_str()); reportEndpoint(pin_name, reason); } @@ -830,7 +829,7 @@ ReportPath::reportEndpointOutputDelay(const PathEndClkConstrained *end) const } else { if (tgt_clk) { - string clk_name = tgtClkName(end); + std::string clk_name = tgtClkName(end); auto reason = stdstrPrint("internal path endpoint clocked by %s", clk_name.c_str()); @@ -875,7 +874,7 @@ ReportPath::reportEndpoint(const PathEndGatedClock *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); const char *inst_name = cmd_network_->pathName(inst); - string clk_name = tgtClkName(end); + 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(); @@ -955,7 +954,7 @@ ReportPath::reportEndpoint(const PathEndDataCheck *end) const void ReportPath::reportEndHeader() const { - string line; + std::string line; // Line one. reportDescription("", line); line += ' '; @@ -981,8 +980,8 @@ ReportPath::reportEndHeader() const void ReportPath::reportEndLine(const PathEnd *end) const { - string line; - string endpoint = pathEndpoint(end); + std::string line; + std::string endpoint = pathEndpoint(end); reportDescription(endpoint.c_str(), line); const EarlyLate *early_late = end->pathEarlyLate(this); reportSpaceFieldDelay(end->requiredTimeOffset(this), early_late, line); @@ -996,7 +995,7 @@ ReportPath::reportEndLine(const PathEnd *end) const void ReportPath::reportSummaryHeader() const { - string line; + std::string line; reportDescription("Startpoint", line); line += ' '; reportDescription("Endpoint", line); @@ -1010,7 +1009,7 @@ ReportPath::reportSummaryHeader() const void ReportPath::reportSummaryLine(const PathEnd *end) const { - string line; + std::string line; PathExpanded expanded(end->path(), this); const EarlyLate *early_late = end->pathEarlyLate(this); auto startpoint = pathStartpoint(end, expanded); @@ -1025,7 +1024,7 @@ ReportPath::reportSummaryLine(const PathEnd *end) const report_->reportLineString(line); } -string +std::string ReportPath::pathStartpoint(const PathEnd *end, const PathExpanded &expanded) const { @@ -1043,7 +1042,7 @@ ReportPath::pathStartpoint(const PathEnd *end, } } -string +std::string ReportPath::pathEndpoint(const PathEnd *end) const { Pin *pin = end->vertex(this)->pin(); @@ -1078,7 +1077,7 @@ void ReportPath::reportJson(const PathEnd *end, bool last) const { - string result; + std::string result; result += "{\n"; stringAppend(result, " \"type\": \"%s\",\n", end->typeName()); stringAppend(result, " \"path_group\": \"%s\",\n", @@ -1149,7 +1148,7 @@ ReportPath::reportJson(const PathEnd *end, void ReportPath::reportJson(const Path *path) const { - string result; + std::string result; result += "{\n"; reportJson(path, "path", 0, false, result); result += "}\n"; @@ -1161,7 +1160,7 @@ ReportPath::reportJson(const Path *path, const char *path_name, int indent, bool trailing_comma, - string &result) const + std::string &result) const { PathExpanded expanded(path, this); reportJson(expanded, path_name, indent, trailing_comma, result); @@ -1172,7 +1171,7 @@ ReportPath::reportJson(const PathExpanded &expanded, const char *path_name, int indent, bool trailing_comma, - string &result) const + std::string &result) const { stringAppend(result, "%*s\"%s\": [\n", indent, "", path_name); for (size_t i = expanded.startIndex(); i < expanded.size(); i++) { @@ -1256,7 +1255,7 @@ ReportPath::reportJson(const PathExpanded &expanded, void ReportPath::reportSlackOnlyHeader() const { - string line; + std::string line; reportDescription("Group", line); line += ' '; reportField("Slack", field_total_, line); @@ -1268,7 +1267,7 @@ ReportPath::reportSlackOnlyHeader() const void ReportPath::reportSlackOnly(const PathEnd *end) const { - string line; + std::string line; const EarlyLate *early_late = end->pathEarlyLate(this); reportDescription(end->pathGroup()->name(), line); if (end->isUnconstrained()) @@ -1318,7 +1317,7 @@ ReportPath::reportMpwChecks(const MinPulseWidthCheckSeq &checks, void ReportPath::reportMpwHeaderShort() const { - string line; + std::string line; reportDescription("", line); line += ' '; reportField("Required", field_total_, line); @@ -1342,7 +1341,7 @@ ReportPath::reportMpwHeaderShort() const void ReportPath::reportShort(const MinPulseWidthCheck &check) const { - string line; + 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); @@ -1356,7 +1355,7 @@ ReportPath::reportShort(const MinPulseWidthCheck &check) const void ReportPath::reportVerbose(const MinPulseWidthCheck &check) const { - string line; + std::string line; const char *pin_name = cmd_network_->pathName(check.pin(this)); line += "Pin: "; line += pin_name; @@ -1462,7 +1461,7 @@ ReportPath::reportChecks(const MinPeriodCheckSeq &checks, void ReportPath::reportPeriodHeaderShort() const { - string line; + std::string line; reportDescription("", line); line += ' '; reportField("", field_total_, line); @@ -1488,7 +1487,7 @@ ReportPath::reportPeriodHeaderShort() const void ReportPath::reportShort(const MinPeriodCheck &check) const { - string line; + std::string line; const char *pin_name = cmd_network_->pathName(check.pin()); reportDescription(pin_name, line); reportSpaceFieldDelay(check.period(), EarlyLate::early(), line); @@ -1500,7 +1499,7 @@ ReportPath::reportShort(const MinPeriodCheck &check) const void ReportPath::reportVerbose(const MinPeriodCheck &check) const { - string line; + std::string line; const char *pin_name = cmd_network_->pathName(check.pin()); line += "Pin: "; line += pin_name; @@ -1536,7 +1535,7 @@ ReportPath::reportChecks(const MaxSkewCheckSeq &checks, void ReportPath::reportMaxSkewHeaderShort() const { - string line; + std::string line; reportDescription("", line); line += ' '; reportField("Required", field_total_, line); @@ -1562,7 +1561,7 @@ ReportPath::reportMaxSkewHeaderShort() const void ReportPath::reportShort(const MaxSkewCheck &check) const { - string line; + std::string line; Pin *clk_pin = check.clkPin(this); const char *clk_pin_name = network_->pathName(clk_pin); TimingArc *check_arc = check.checkArc(); @@ -1581,7 +1580,7 @@ ReportPath::reportShort(const MaxSkewCheck &check) const void ReportPath::reportVerbose(const MaxSkewCheck &check) const { - string line; + std::string line; const char *clk_pin_name = cmd_network_->pathName(check.clkPin(this)); line += "Constrained Pin: "; line += clk_pin_name; @@ -1617,7 +1616,7 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, const EarlyLate *early_late = clk_path->minMax(this); const RiseFall *clk_rf = clk_edge->transition(); const RiseFall *clk_end_rf = clk_path->transition(this); - string clk_name = clkName(clk, clk_end_rf != clk_rf); + 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; @@ -1656,7 +1655,7 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, void ReportPath::reportLimitShortHeader(const ReportField *field) const { - string line; + std::string line; reportDescription("Pin", line); line += ' '; reportField("Limit", field, line); @@ -1676,7 +1675,7 @@ ReportPath::reportLimitShort(const ReportField *field, float limit, float slack) const { - string line; + std::string line; const char *pin_name = cmd_network_->pathName(pin); reportDescription(pin_name, line); line += ' '; @@ -1701,7 +1700,7 @@ ReportPath::reportLimitVerbose(const ReportField *field, const Scene *scene, const MinMax *min_max) const { - string line; + std::string line; line += "Pin "; line += cmd_network_->pathName(pin); line += ' '; @@ -1781,7 +1780,7 @@ ReportPath::reportStartpoint(const PathEnd *end, const Path *clk_path = expanded.clkPath(); bool clk_inverted = clk_path && clk_rf != clk_path->transition(this); - string clk_name = clkName(clk, clk_inverted); + 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()); reportStartpoint(inst_name, reason); @@ -1829,7 +1828,7 @@ ReportPath::pathFromClkPin(const Path *path, void ReportPath::reportStartpoint(const char *start, - const string reason) const + const std::string reason) const { reportStartEndPoint(start, reason, "Startpoint"); } @@ -1878,17 +1877,17 @@ ReportPath::reportUnclockedEndpoint(const PathEnd *end, void ReportPath::reportEndpoint(const char *end, - const string reason) const + const std::string reason) const { reportStartEndPoint(end, reason, "Endpoint"); } void ReportPath::reportStartEndPoint(const char *pt, - string reason, + std::string reason, const char *key) const { - string line; + std::string line; // Account for punctuation in the line. int line_len = strlen(key) + 2 + strlen(pt) + 2 + reason.size() + 1; if (!no_split_ @@ -1921,7 +1920,7 @@ ReportPath::reportStartEndPoint(const char *pt, void ReportPath::reportGroup(const PathEnd *end) const { - string line; + std::string line; line = "Path Group: "; PathGroup *group = end->pathGroup(); line += group ? group->name() : "(none)"; @@ -1946,13 +1945,13 @@ ReportPath::reportGroup(const PathEnd *end) const //////////////////////////////////////////////////////////////// -string +std::string ReportPath::checkRoleReason(const PathEnd *end) const { return stdstrPrint("%s time", end->checkRole(this)->to_string().c_str()); } -string +std::string ReportPath::tgtClkName(const PathEnd *end) const { const ClockEdge *tgt_clk_edge = end->targetClkEdge(this); @@ -1962,11 +1961,11 @@ ReportPath::tgtClkName(const PathEnd *end) const return clkName(tgt_clk, clk_end_rf != clk_rf); } -string +std::string ReportPath::clkName(const Clock *clk, bool inverted) const { - string name = clk->name(); + std::string name = clk->name(); if (inverted) name += '\''; return name; @@ -2088,7 +2087,7 @@ ReportPath::reportSrcClkAndPath(const Path *path, } } } - string clk_name = clkName(clk, clk_rf != clk_end_rf); + std::string clk_name = clkName(clk, clk_rf != clk_end_rf); bool clk_used_as_data = pathFromClkPin(expanded); bool is_prop = isPropagated(path); @@ -2185,7 +2184,7 @@ ReportPath::reportTgtClk(const PathEnd *end, Clock *clk = clk_edge->clock(); const RiseFall *clk_rf = clk_edge->transition(); const RiseFall *clk_end_rf = end->targetClkEndTrans(this); - string clk_name = clkName(clk, clk_end_rf != clk_rf); + std::string clk_name = clkName(clk, clk_end_rf != clk_rf); float clk_time = prev_time + end->targetClkTime(this) + end->targetClkMcpAdjustment(this) @@ -2439,7 +2438,7 @@ ReportPath::reportPathLine(const Path *path, { Vertex *vertex = path->vertex(this); Pin *pin = vertex->pin(); - const string what = descriptionField(vertex); + const std::string what = descriptionField(vertex); const RiseFall *rf = path->transition(this); bool is_driver = network_->isDriver(pin); const EarlyLate *early_late = path->minMax(this); @@ -2449,7 +2448,7 @@ ReportPath::reportPathLine(const Path *path, Slew slew = graph_->slew(vertex, rf, slew_index); float cap = field_blank_; Instance *inst = network_->instance(pin); - string src_attr = ""; + std::string src_attr = ""; if (inst) src_attr = network_->getAttribute(inst, "src"); // Don't show capacitance field for input pins. @@ -2462,7 +2461,7 @@ ReportPath::reportPathLine(const Path *path, void ReportPath::reportRequired(const PathEnd *end, - string margin_msg) const + std::string margin_msg) const { Required req_time = end->requiredTimeOffset(this); const EarlyLate *early_late = end->clkEarlyLate(this); @@ -2503,7 +2502,7 @@ ReportPath::reportSlack(Slack slack) const void ReportPath::reportSpaceSlack(const PathEnd *end, - string &result) const + std::string &result) const { Slack slack = end->slack(this); reportSpaceSlack(slack, result); @@ -2511,7 +2510,7 @@ ReportPath::reportSpaceSlack(const PathEnd *end, void ReportPath::reportSpaceSlack(Slack slack, - string &result) const + std::string &result) const { const EarlyLate *early_late = EarlyLate::early(); reportSpaceFieldDelay(slack, early_late, result); @@ -2718,7 +2717,7 @@ ReportPath::reportPath6(const Path *path, bool is_clk_start = path1->vertex(this) == clk_start; bool is_clk = path1->isClock(search_); Instance *inst = network_->instance(pin); - string src_attr = ""; + std::string src_attr = ""; if (inst) src_attr = network_->getAttribute(inst, "src"); // Always show the search start point (register clk pin). @@ -2804,13 +2803,13 @@ ReportPath::reportPath6(const Path *path, cap = graph_delay_calc_->loadCap(pin, rf, scene, min_max); if (field_fanout_->enabled()) fanout = drvrFanout(vertex, scene, min_max); - const string what = descriptionField(vertex); + const std::string what = descriptionField(vertex); reportLine(what.c_str(), cap, slew, fanout, incr, time, false, min_max, rf, src_attr, line_case); if (report_net_) { - const string what2 = descriptionNet(pin); + const std::string what2 = descriptionNet(pin); reportLine(what2.c_str(), field_blank_, field_blank_, field_blank_, field_blank_, field_blank_, false, min_max, nullptr, src_attr, ""); @@ -2823,7 +2822,7 @@ ReportPath::reportPath6(const Path *path, || (i == 0) || (i == path_last_index) || is_clk_start) { - const string what = descriptionField(vertex); + const std::string what = descriptionField(vertex); reportLine(what.c_str(), field_blank_, slew, field_blank_, incr, time, false, min_max, rf, src_attr, line_case); @@ -2843,7 +2842,7 @@ ReportPath::reportHierPinsThru(const Path *path) const const Edge *prev_edge = path->prevEdge(this); if (prev_edge && prev_edge->isWire()) { for (const Pin *hpin : hierPinsThruEdge(prev_edge, network_, graph_)) { - const string what = descriptionField(hpin); + const std::string what = descriptionField(hpin); reportLine(what.c_str(), field_blank_, field_blank_, field_blank_, field_blank_, field_blank_, false, path->minMax(this), nullptr, "", ""); @@ -2874,13 +2873,13 @@ ReportPath::nextArcAnnotated(const Path *next_path, return graph_->arcDelayAnnotated(edge, arc, ap_index); } -string +std::string ReportPath::descriptionField(const Vertex *vertex) const { return descriptionField(vertex->pin()); } -string +std::string ReportPath::descriptionField(const Pin *pin) const { const char *pin_name = cmd_network_->pathName(pin); @@ -2906,7 +2905,7 @@ ReportPath::descriptionField(const Pin *pin) const return stdstrPrint("%s (%s)", pin_name, name2); } -string +std::string ReportPath::descriptionNet(const Pin *pin) const { if (network_->isTopLevelPort(pin)) { @@ -3028,7 +3027,7 @@ ReportPath::pathInputDelayRefPath(const Path *path, void ReportPath::reportPathHeader() const { - string line; + std::string line; bool first_field = true; for (const ReportField *field : fields_) { if (field->enabled()) { @@ -3125,10 +3124,10 @@ ReportPath::reportLine(const char *what, bool total_with_minus, const EarlyLate *early_late, const RiseFall *rf, - string src_attr, + std::string src_attr, const char *line_case) const { - string line; + std::string line; size_t field_index = 0; bool first_field = true; for (const ReportField *field : fields_) { @@ -3180,7 +3179,7 @@ ReportPath::reportLine(const char *what, field_index++; } // Trim trailing spaces and report the line. - string line_stdstr = line; + std::string line_stdstr = line; trimRight(line_stdstr); report_->reportLineString(line_stdstr.c_str()); } @@ -3211,7 +3210,7 @@ ReportPath::reportLineTotal1(const char *what, bool incr_with_minus, const EarlyLate *early_late) const { - string line; + std::string line; reportDescription(what, line); line += ' '; if (incr_with_minus) @@ -3231,7 +3230,7 @@ ReportPath::reportDashLineTotal() const void ReportPath::reportDescription(const char *what, - string &line) const + std::string &line) const { reportDescription(what, false, false, line); } @@ -3240,7 +3239,7 @@ void ReportPath::reportDescription(const char *what, bool first_field, bool last_field, - string &line) const + std::string &line) const { line += what; int length = strlen(what); @@ -3260,7 +3259,7 @@ ReportPath::reportDescription(const char *what, void ReportPath::reportFieldTime(float value, ReportField *field, - string &line) const + std::string &line) const { if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); @@ -3275,7 +3274,7 @@ ReportPath::reportFieldTime(float value, void ReportPath::reportSpaceFieldTime(float value, - string &line) const + std::string &line) const { line += ' '; reportFieldTime(value, field_total_, line); @@ -3284,7 +3283,7 @@ ReportPath::reportSpaceFieldTime(float value, void ReportPath::reportSpaceFieldDelay(Delay value, const EarlyLate *early_late, - string &line) const + std::string &line) const { line += ' '; reportTotalDelay(value, early_late, line); @@ -3293,7 +3292,7 @@ ReportPath::reportSpaceFieldDelay(Delay value, void ReportPath::reportTotalDelay(Delay value, const EarlyLate *early_late, - string &line) const + std::string &line) const { const char *str = delayAsString(value, early_late, this, digits_); if (stringEq(str, minus_zero_)) @@ -3307,7 +3306,7 @@ void ReportPath::reportFieldDelayMinus(Delay value, const EarlyLate *early_late, const ReportField *field, - string &line) const + std::string &line) const { if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); @@ -3327,7 +3326,7 @@ void ReportPath::reportFieldDelay(Delay value, const EarlyLate *early_late, const ReportField *field, - string &line) const + std::string &line) const { if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, line); @@ -3345,7 +3344,7 @@ ReportPath::reportFieldDelay(Delay value, void ReportPath::reportField(float value, const ReportField *field, - string &line) const + std::string &line) const { if (value == field_blank_) reportFieldBlank(field, line); @@ -3357,7 +3356,7 @@ ReportPath::reportField(float value, } else { // fanout - string value_str; + std::string value_str; stringPrint(value_str, "%.0f", value); reportField(value_str.c_str(), field, line); } @@ -3367,7 +3366,7 @@ ReportPath::reportField(float value, void ReportPath::reportField(const char *value, const ReportField *field, - string &line) const + std::string &line) const { if (field->leftJustify()) line += value; @@ -3379,7 +3378,7 @@ ReportPath::reportField(const char *value, void ReportPath::reportFieldBlank(const ReportField *field, - string &line) const + std::string &line) const { line += field->blank(); } @@ -3387,7 +3386,7 @@ ReportPath::reportFieldBlank(const ReportField *field, void ReportPath::reportDashLine() const { - string line; + std::string line; for (const ReportField *field : fields_) { if (field->enabled()) { for (int i = 0; i < field->width(); i++) @@ -3401,7 +3400,7 @@ ReportPath::reportDashLine() const void ReportPath::reportDashLine(int line_width) const { - string line; + std::string line; for (int i = 0; i < line_width; i++) line += '-'; report_->reportLineString(line); diff --git a/search/Search.cc b/search/Search.cc index 248477d4f..25683114a 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -24,8 +24,7 @@ #include "Search.hh" -#include -#include // abs +#include #include "ContainerHelpers.hh" #include "Mutex.hh" @@ -70,10 +69,6 @@ namespace sta { -using std::min; -using std::max; -using std::abs; - //////////////////////////////////////////////////////////////// EvalPred::EvalPred(const StaState *sta) : diff --git a/search/Sta.cc b/search/Sta.cc index baa4ab074..5c096867c 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -24,7 +24,9 @@ #include "Sta.hh" +#include #include +#include #include "Machine.hh" #include "ContainerHelpers.hh" @@ -88,10 +90,6 @@ namespace sta { -using std::string; -using std::min; -using std::max; - static bool libertyPortCapsEqual(const LibertyPort *port1, const LibertyPort *port2); @@ -576,7 +574,7 @@ Sta::cmdSdc() const } void -Sta::setCmdMode(const string &mode_name) +Sta::setCmdMode(const std::string &mode_name) { if (!mode_name.empty()) { if (!mode_name_map_.contains(mode_name)) { @@ -3203,8 +3201,8 @@ class EndpointPathEndVisitor : public PathEndVisitor { public: EndpointPathEndVisitor(const std::string &path_group_name, - const MinMax *min_max, - const StaState *sta); + const MinMax *min_max, + const StaState *sta); PathEndVisitor *copy() const; void visit(PathEnd *path_end); Slack slack() const { return slack_; } @@ -3217,8 +3215,8 @@ class EndpointPathEndVisitor : public PathEndVisitor }; EndpointPathEndVisitor::EndpointPathEndVisitor(const std::string &path_group_name, - const MinMax *min_max, - const StaState *sta) : + const MinMax *min_max, + const StaState *sta) : path_group_name_(path_group_name), min_max_(min_max), slack_(MinMax::min()->initValue()), @@ -3249,8 +3247,8 @@ EndpointPathEndVisitor::visit(PathEnd *path_end) Slack Sta::endpointSlack(const Pin *pin, - const std::string &path_group_name, - const MinMax *min_max) + const std::string &path_group_name, + const MinMax *min_max) { ensureGraph(); Vertex *vertex = graph_->pinLoadVertex(pin); @@ -3458,7 +3456,7 @@ MinPeriodEndVisitor::visit(PathEnd *path_end) || pathIsFromInputPort(path_end)))) { Slack slack = path_end->slack(sta_); float period = clk_->period() - delayAsFloat(slack); - min_period_ = max(min_period_, period); + min_period_ = std::max(min_period_, period); } } @@ -3551,12 +3549,12 @@ Sta::worstSlack(const Scene *scene, //////////////////////////////////////////////////////////////// -string +std::string Sta::reportDelayCalc(Edge *edge, - TimingArc *arc, + TimingArc *arc, const Scene *scene, - const MinMax *min_max, - int digits) + const MinMax *min_max, + int digits) { findDelays(); return graph_delay_calc_->reportDelayCalc(edge, arc, scene, min_max, digits); @@ -4097,13 +4095,13 @@ Sta::setResistance(const Net *net, bool Sta::readSpef(const std::string &name, const std::string &filename, - Instance *instance, + Instance *instance, Scene *scene, // -scene deprecated 11/20/2025 - const MinMaxAll *min_max, - bool pin_cap_included, - bool keep_coupling_caps, - float coupling_cap_factor, - bool reduce) + const MinMaxAll *min_max, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + bool reduce) { ensureLibLinked(); Parasitics *parasitics = nullptr; @@ -4137,7 +4135,7 @@ Sta::readSpef(const std::string &name, } bool success = readSpefFile(filename.c_str(), instance, - pin_cap_included, keep_coupling_caps, + pin_cap_included, keep_coupling_caps, coupling_cap_factor, reduce, scene, min_max, parasitics, this); delaysInvalid(); @@ -4151,7 +4149,7 @@ Sta::findParasitics(const std::string &name) } void -Sta::reportParasiticAnnotation(const string &spef_name, +Sta::reportParasiticAnnotation(const std::string &spef_name, bool report_unannotated) { ensureLibLinked(); diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index cad69282e..9eae159bc 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -24,6 +24,8 @@ #include "WorstSlack.hh" +#include + #include "ContainerHelpers.hh" #include "Debug.hh" #include "Report.hh" @@ -34,8 +36,6 @@ namespace sta { -using std::min; - WorstSlacks::WorstSlacks(StaState *sta) : worst_slacks_(sta->scenePathCount(), sta), sta_(sta) @@ -195,7 +195,7 @@ WorstSlack::sortQueue(PathAPIndex path_ap_index) sort(vertices, slack_less); int vertex_count = vertices.size(); - int threshold_index = min(min_queue_size_, vertex_count - 1); + 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", diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index 694ba7b38..1368cd512 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -24,8 +24,8 @@ #include "WritePathSpice.hh" -#include #include +#include #include "Debug.hh" #include "Error.hh" @@ -50,11 +50,6 @@ namespace sta { -using std::string; -using std::ofstream; -using std::ifstream; -using std::max; - typedef int Stage; //////////////////////////////////////////////////////////////// @@ -112,7 +107,7 @@ class WritePathSpice : public WriteSpice // Stage stageFirst(); Stage stageLast(); - string stageName(Stage stage); + std::string stageName(Stage stage); int stageGateInputPathIndex(Stage stage); int stageDrvrPathIndex(Stage stage); int stageLoadPathIndex(Stage stage); @@ -219,7 +214,7 @@ void WritePathSpice::writeHeader() { const Path *start_path = path_expanded_.startPath(); - string title = stdstrPrint("Path from %s %s to %s %s", + std::string title = stdstrPrint("Path from %s %s to %s %s", network_->pathName(start_path->pin(this)), start_path->transition(this)->to_string().c_str(), network_->pathName(path_->pin(this)), @@ -291,7 +286,7 @@ WritePathSpice::writeStageInstances() streamPrint(spice_stream_, "*****************\n\n"); for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { - string stage_name = stageName(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", @@ -450,7 +445,7 @@ WritePathSpice::writeMeasureSlewStmt(Stage stage, { const Pin *pin = path->pin(this); const RiseFall *rf = path->transition(this); - string prefix = stageName(stage); + std::string prefix = stageName(stage); writeMeasureSlewStmt(pin, rf, prefix); } @@ -480,7 +475,7 @@ WritePathSpice::writeInputStage(Stage stage) // External driver not handled. const char *drvr_pin_name = stageDrvrPinName(stage); const char *load_pin_name = stageLoadPinName(stage); - string prefix = stageName(stage); + std::string prefix = stageName(stage); streamPrint(spice_stream_, ".subckt %s %s %s\n", prefix.c_str(), drvr_pin_name, @@ -499,7 +494,7 @@ WritePathSpice::writeGateStage(Stage stage) const char *drvr_pin_name = stageDrvrPinName(stage); const Pin *load_pin = stageLoadPin(stage); const char *load_pin_name = stageLoadPinName(stage); - string subckt_name = "stage" + std::to_string(stage); + std::string subckt_name = "stage" + std::to_string(stage); const Instance *inst = stageInstance(stage); LibertyPort *input_port = stageGateInputPort(stage); @@ -614,10 +609,10 @@ WritePathSpice::stageLast() return (path_expanded_.size() + 1) / 2; } -string +std::string WritePathSpice::stageName(Stage stage) { - string name; + std::string name; stringPrint(name, "stage%d", stage); return name; } diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 8d55d8c70..9b1bf2c39 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -26,6 +26,8 @@ #include // swap #include +#include +#include #include "cudd.h" @@ -50,12 +52,6 @@ namespace sta { -using std::string; -using std::ifstream; -using std::ofstream; -using std::swap; -using std::set; - Net * pinNet(const Pin *pin, const Network *network); @@ -68,7 +64,7 @@ class SubcktEndsMissing : public Exception const char *what() const noexcept; protected: - string what_; + std::string what_; }; SubcktEndsMissing::SubcktEndsMissing(const char *cell_name, @@ -137,7 +133,7 @@ WriteSpice::initPowerGnd() } void -WriteSpice::writeHeader(string &title, +WriteSpice::writeHeader(std::string &title, float max_time, float time_step) { @@ -159,21 +155,21 @@ WriteSpice::writePrintStmt(StdStringSeq &node_names) { streamPrint(spice_stream_, ".print tran"); if (ckt_sim_ == CircuitSim::xyce) { - string csv_filename = replaceFileExt(spice_filename_, "csv"); + std::string csv_filename = replaceFileExt(spice_filename_, "csv"); streamPrint(spice_stream_, " format=csv file=%s", csv_filename.c_str()); writeGnuplotFile(node_names); } - for (string &name : node_names) + for (std::string &name : node_names) streamPrint(spice_stream_, " v(%s)", name.c_str()); streamPrint(spice_stream_, "\n\n"); } -string -WriteSpice::replaceFileExt(string filename, +std::string +WriteSpice::replaceFileExt(std::string filename, const char *ext) { size_t dot = filename.rfind('.'); - string ext_filename = filename.substr(0, dot + 1); + std::string ext_filename = filename.substr(0, dot + 1); ext_filename += ext; return ext_filename; } @@ -184,7 +180,7 @@ WriteSpice::writeGnuplotFile(StdStringSeq &node_nanes) { std::string gnuplot_filename = replaceFileExt(spice_filename_, "gnuplot"); std::string csv_filename = replaceFileExt(spice_filename_, "csv"); - ofstream gnuplot_stream; + std::ofstream gnuplot_stream; gnuplot_stream.open(gnuplot_filename); if (gnuplot_stream.is_open()) { streamPrint(gnuplot_stream, "set datafile separator ','\n"); @@ -206,12 +202,12 @@ void WriteSpice::writeSubckts(StdStringSet &cell_names) { findCellSubckts(cell_names); - ifstream lib_subckts_stream(lib_subckt_filename_); + std::ifstream lib_subckts_stream(lib_subckt_filename_); if (lib_subckts_stream.is_open()) { - ofstream subckts_stream(subckt_filename_); + std::ofstream subckts_stream(subckt_filename_); if (subckts_stream.is_open()) { - string line; - while (getline(lib_subckts_stream, line)) { + std::string line; + while (std::getline(lib_subckts_stream, line)) { // .subckt [args..] StdStringSeq tokens = parseTokens(line, ' '); if (tokens.size() >= 2 @@ -220,7 +216,7 @@ WriteSpice::writeSubckts(StdStringSet &cell_names) if (cell_names.contains(cell_name)) { subckts_stream << line << "\n"; bool found_ends = false; - while (getline(lib_subckts_stream, line)) { + while (std::getline(lib_subckts_stream, line)) { subckts_stream << line << "\n"; if (stringBeginEqual(line.c_str(), ".ends")) { subckts_stream << "\n"; @@ -239,13 +235,13 @@ WriteSpice::writeSubckts(StdStringSet &cell_names) lib_subckts_stream.close(); if (!cell_names.empty()) { - string missing_cells; - for (const string &cell_name : cell_names) { - missing_cells += "\n"; - missing_cells += cell_name; + std::string missing_cells; + for (const std::string &cell_name : cell_names) { + missing_cells += "\n"; + missing_cells += cell_name; } - report_->error(1605, "The subkct file %s is missing definitions for %s", - lib_subckt_filename_, + report_->error(1605, "The subkct file %s is missing definitions for %s", + lib_subckt_filename_, missing_cells.c_str()); } } @@ -284,10 +280,10 @@ WriteSpice::recordSpicePortNames(const char *cell_name, void WriteSpice::findCellSubckts(StdStringSet &cell_names) { - ifstream lib_subckts_stream(lib_subckt_filename_); + std::ifstream lib_subckts_stream(lib_subckt_filename_); if (lib_subckts_stream.is_open()) { - string line; - while (getline(lib_subckts_stream, line)) { + std::string line; + while (std::getline(lib_subckts_stream, line)) { // .subckt [args..] StdStringSeq tokens = parseTokens(line, ' '); if (tokens.size() >= 2 @@ -295,15 +291,15 @@ WriteSpice::findCellSubckts(StdStringSet &cell_names) const char *cell_name = tokens[1].c_str(); if (cell_names.contains(cell_name)) { // Scan the subckt definition for subckt calls. - string stmt; - while (getline(lib_subckts_stream, line)) { + std::string stmt; + while (std::getline(lib_subckts_stream, line)) { if (line[0] == '+') stmt += line.substr(1); else { // Process previous statement. if (tolower(stmt[0]) == 'x') { StdStringSeq tokens = parseTokens(line, ' '); - string &subckt_cell = tokens[tokens.size() - 1]; + std::string &subckt_cell = tokens[tokens.size() - 1]; cell_names.insert(subckt_cell); } stmt = line; @@ -329,7 +325,7 @@ WriteSpice::writeSubcktInst(const Instance *inst) const char *cell_name = cell->name(); StdStringSeq &spice_port_names = cell_spice_port_names_[cell_name]; streamPrint(spice_stream_, "x%s", inst_name); - for (string subckt_port_name : spice_port_names) { + 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); LibertyPort *pg_port = cell->findLibertyPort(subckt_port_cname); @@ -359,7 +355,7 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, const char *inst_name = network_->pathName(inst); debugPrint(debug_, "write_spice", 2, "subckt %s", cell->name()); - for (string subckt_port_sname : spice_port_names) { + 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; @@ -412,7 +408,7 @@ WriteSpice::writeVoltageSource(const char *inst_name, const char *port_name, float voltage) { - string node_name = inst_name; + std::string node_name = inst_name; node_name += '/'; node_name += port_name; writeVoltageSource(node_name.c_str(), voltage); @@ -536,7 +532,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, const Parasitic *parasitic, const NetSet &coupling_nets) { - set reachable_pins; + std::set reachable_pins; // Sort resistors for consistent regression results. ParasiticResistorSeq resistors = parasitics_->resistors(parasitic); sort(resistors, [this] (const ParasiticResistor *r1, @@ -614,8 +610,8 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, const Net *net1 = node1 ? parasitics_->net(node1, network_) : nullptr; const Net *net2 = node2 ? parasitics_->net(node2, network_) : nullptr; if (net2 == net) { - swap(net1, net2); - swap(node1, node2); + std::swap(net1, net2); + std::swap(node1, node2); } if (net2 && coupling_nets.contains(net2)) // Write half the capacitance because the coupled net will do the same. @@ -1036,7 +1032,7 @@ WriteSpice::writeMeasureDelayStmt(const Pin *from_pin, const RiseFall *from_rf, const Pin *to_pin, const RiseFall *to_rf, - string prefix) + std::string prefix) { const char *from_pin_name = network_->pathName(from_pin); float from_threshold = power_voltage_ * default_library_->inputThreshold(from_rf); @@ -1062,7 +1058,7 @@ WriteSpice::writeMeasureDelayStmt(const Pin *from_pin, void WriteSpice::writeMeasureSlewStmt(const Pin *pin, const RiseFall *rf, - string prefix) + std::string prefix) { const char *pin_name = network_->pathName(pin); const char *spice_rf = spiceTrans(rf); @@ -1108,7 +1104,7 @@ WriteSpice::spiceTrans(const RiseFall *rf) // fprintf for c++ streams. // Yes, I hate formatted output to ostream THAT much. void -streamPrint(ofstream &stream, +streamPrint(std::ofstream &stream, const char *fmt, ...) { diff --git a/spice/Xyce.cc b/spice/Xyce.cc index f198df705..a4a0f3b69 100644 --- a/spice/Xyce.cc +++ b/spice/Xyce.cc @@ -26,55 +26,49 @@ #include "Xyce.hh" #include -#include #include +#include +#include #include "Error.hh" namespace sta { -using std::string; -using std::ifstream; -using std::getline; -using std::stringstream; -using std::vector; -using std::make_shared; - void readXyceCsv(const char *csv_filename, // Return values. StdStringSeq &titles, WaveformSeq &waveforms) { - ifstream file(csv_filename); + std::ifstream file(csv_filename); if (file.is_open()) { - string line; + std::string line; // Read the header line. - getline(file, line); - stringstream ss(line); - string field; + std::getline(file, line); + std::stringstream ss(line); + std::string field; size_t col = 0; - while (getline(ss, field, ',')) { + while (std::getline(ss, field, ',')) { // Skip TIME title. if (col > 0) titles.push_back(field); col++; } - vector values(titles.size() + 1); - while (getline(file, line)) { - stringstream ss(line); + std::vector values(titles.size() + 1); + while (std::getline(file, line)) { + std::stringstream ss(line); size_t col = 0; - while (getline(ss, field, ',')) { + while (std::getline(ss, field, ',')) { float value = std::stof(field); values[col].push_back(value); col++; } } file.close(); - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - std::move(values[0])); + TableAxisPtr time_axis = std::make_shared(TableAxisVariable::time, + std::move(values[0])); for (size_t var = 1; var < values.size(); var++) waveforms.emplace_back(new FloatSeq(values[var]), time_axis); } diff --git a/util/Fuzzy.cc b/util/Fuzzy.cc index f71583669..bd055f1d6 100644 --- a/util/Fuzzy.cc +++ b/util/Fuzzy.cc @@ -31,9 +31,6 @@ namespace sta { -using std::max; -using std::abs; - constexpr static float float_equal_tolerance = 1E-15F; bool @@ -43,18 +40,18 @@ fuzzyEqual(float v1, if (v1 == v2) return true; else if (v1 == 0.0) - return abs(v2) < float_equal_tolerance; + return std::abs(v2) < float_equal_tolerance; else if (v2 == 0.0) - return abs(v1) < float_equal_tolerance; + return std::abs(v1) < float_equal_tolerance; else - return abs(v1 - v2) < 1E-6F * max(abs(v1), abs(v2)); + return std::abs(v1 - v2) < 1E-6F * std::max(std::abs(v1), std::abs(v2)); } bool fuzzyZero(float v) { return v == 0.0 - || abs(v) < float_equal_tolerance; + || std::abs(v) < float_equal_tolerance; } bool diff --git a/util/PatternMatch.cc b/util/PatternMatch.cc index 8b1e49e89..7e8142588 100644 --- a/util/PatternMatch.cc +++ b/util/PatternMatch.cc @@ -28,8 +28,6 @@ namespace sta { -using std::string; - PatternMatch::PatternMatch(const char *pattern, bool is_regexp, bool nocase, @@ -65,7 +63,7 @@ PatternMatch::PatternMatch(const char *pattern, compileRegexp(); } -PatternMatch::PatternMatch(const string &pattern, +PatternMatch::PatternMatch(const std::string &pattern, const PatternMatch *inherit_from) : pattern_(pattern.c_str()), is_regexp_(inherit_from->is_regexp_), @@ -83,7 +81,7 @@ PatternMatch::compileRegexp() int flags = TCL_REG_ADVANCED; if (nocase_) flags |= TCL_REG_NOCASE; - string anchored_pattern; + std::string anchored_pattern; anchored_pattern += '^'; anchored_pattern += pattern_; anchored_pattern += '$'; @@ -112,7 +110,7 @@ PatternMatch::hasWildcards() const } bool -PatternMatch::match(const string &str) const +PatternMatch::match(const std::string &str) const { return match(str.c_str()); } diff --git a/util/Report.cc b/util/Report.cc index 5f6b885bf..6772484ea 100644 --- a/util/Report.cc +++ b/util/Report.cc @@ -33,8 +33,6 @@ namespace sta { -using std::min; - Report *Report::default_ = nullptr; Report::Report() : @@ -78,11 +76,11 @@ Report::printString(const char *buffer, redirectStringPrint(buffer, length); else { if (redirect_stream_) - ret = min(ret, fwrite(buffer, sizeof(char), length, redirect_stream_)); + ret = std::min(ret, fwrite(buffer, sizeof(char), length, redirect_stream_)); else - ret = min(ret, printConsole(buffer, length)); + ret = std::min(ret, printConsole(buffer, length)); if (log_stream_) - ret = min(ret, fwrite(buffer, sizeof(char), length, log_stream_)); + ret = std::min(ret, fwrite(buffer, sizeof(char), length, log_stream_)); } return ret; } diff --git a/util/StringUtil.cc b/util/StringUtil.cc index 6a8680e4c..991d25dba 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -24,21 +24,17 @@ #include "StringUtil.hh" -#include +#include +#include #include #include #include // exit -#include -#include #include "Machine.hh" #include "Mutex.hh" namespace sta { -using std::max; -using std::string; - static void stringPrintTmp(const char *fmt, va_list args, @@ -76,7 +72,7 @@ isDigits(const char *str) // print for c++ strings. void -stringPrint(string &str, +stringPrint(std::string &str, const char *fmt, ...) { @@ -90,7 +86,7 @@ stringPrint(string &str, } void -stringAppend(string &str, +stringAppend(std::string &str, const char *fmt, ...) { @@ -103,7 +99,7 @@ stringAppend(string &str, str += tmp; } -string +std::string stdstrPrint(const char *fmt, ...) { @@ -223,7 +219,7 @@ makeTmpString(size_t length) if (tmp_length < length) { // String isn't long enough. Make a new one. delete [] tmp_str; - tmp_length = max(tmp_string_initial_length, length); + tmp_length = std::max(tmp_string_initial_length, length); tmp_str = new char[tmp_length]; tmp_strings[tmp_string_next] = tmp_str; tmp_string_lengths[tmp_string_next] = tmp_length; @@ -233,7 +229,7 @@ makeTmpString(size_t length) } char * -makeTmpString(string &str) +makeTmpString(std::string &str) { char *tmp = makeTmpString(str.length() + 1); strcpy(tmp, str.c_str()); @@ -264,24 +260,24 @@ isTmpString(const char *str) //////////////////////////////////////////////////////////////// void -trimRight(string &str) +trimRight(std::string &str) { str.erase(str.find_last_not_of(" ") + 1); } StdStringSeq -split(const string &text, - const string &delims) +split(const std::string &text, + const std::string &delims) { StdStringSeq tokens; auto start = text.find_first_not_of(delims); auto end = text.find_first_of(delims, start); - while (end != string::npos) { + while (end != std::string::npos) { tokens.push_back(text.substr(start, end - start)); start = text.find_first_not_of(delims, end); end = text.find_first_of(delims, start); } - if (start != string::npos) + if (start != std::string::npos) tokens.push_back(text.substr(start)); return tokens; } diff --git a/util/Transition.cc b/util/Transition.cc index a77a898b5..0e09243c2 100644 --- a/util/Transition.cc +++ b/util/Transition.cc @@ -24,12 +24,12 @@ #include "Transition.hh" +#include + #include "ContainerHelpers.hh" namespace sta { -using std::max; - const RiseFall RiseFall::rise_("rise", "^", 0); const RiseFall RiseFall::fall_("fall", "v", 1); const std::array RiseFall::range_{&rise_, &fall_}; @@ -195,7 +195,7 @@ Transition::Transition(const char *name, { transition_map_[name_] = this; transition_map_[init_final_] = this; - max_index_ = max(sdf_triple_index, max_index_); + max_index_ = std::max(sdf_triple_index, max_index_); } bool diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index e7ac21d33..e101d0f91 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -25,6 +25,7 @@ #include "VerilogReader.hh" #include +#include #include "ContainerHelpers.hh" #include "Zlib.hh" @@ -42,12 +43,10 @@ namespace sta { -using std::string; - using VerilogConstant10 = unsigned long long; -static string -verilogBusBitName(const string &bus_name, +static std::string +verilogBusBitName(const std::string &bus_name, int index); static int hierarchyLevel(Net *net, @@ -227,13 +226,13 @@ VerilogReader::module(Cell *cell) } void -VerilogReader::makeModule(const string *module_vname, +VerilogReader::makeModule(const std::string *module_vname, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, int line) { - const 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]; @@ -258,7 +257,7 @@ VerilogReader::makeModule(const string *module_vname, } void -VerilogReader::makeModule(const string *module_name, +VerilogReader::makeModule(const std::string *module_name, VerilogStmtSeq *port_dcls, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, @@ -288,7 +287,7 @@ VerilogReader::makeCellPorts(Cell *cell, { StdStringSet port_names; for (VerilogNet *mod_port : *ports) { - const string &port_name = mod_port->name(); + const std::string &port_name = mod_port->name(); if (!port_names.contains(port_name)) { port_names.insert(port_name); if (mod_port->isNamed()) { @@ -310,7 +309,7 @@ VerilogReader::makeCellPorts(Cell *cell, Port * VerilogReader::makeCellPort(Cell *cell, VerilogModule *module, - const string &port_name) + const std::string &port_name) { VerilogDcl *dcl = module->declaration(port_name.c_str()); if (dcl) { @@ -341,7 +340,7 @@ VerilogReader::makeNamedPortRefCellPorts(Cell *cell, PortSeq *member_ports = new PortSeq; VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module,this); while (net_name_iter->hasNext()) { - const string &net_name = net_name_iter->next(); + const std::string &net_name = net_name_iter->next(); port_names.insert(net_name); Port *port = makeCellPort(cell, module, net_name); member_ports->push_back(port); @@ -354,7 +353,7 @@ VerilogReader::makeNamedPortRefCellPorts(Cell *cell, // Make sure each declaration appears in the module port list. void VerilogReader::checkModuleDcls(VerilogModule *module, - std::set &port_names) + std::set &port_names) { for (auto const & [port_name, dcl] : *module->declarationMap()) { PortDirection *dir = dcl->direction(); @@ -444,10 +443,10 @@ VerilogReader::makeDclBus(PortDirection *dir, } VerilogDclArg * -VerilogReader::makeDclArg(const string *net_vname) +VerilogReader::makeDclArg(const std::string *net_vname) { dcl_arg_count_++; - const string net_name = netVerilogToSta(net_vname); + const std::string net_name = netVerilogToSta(net_vname); VerilogDclArg *dcl =new VerilogDclArg(net_name); delete net_vname; return dcl; @@ -461,14 +460,14 @@ VerilogReader::makeDclArg(VerilogAssign *assign) } VerilogNetPartSelect * -VerilogReader::makeNetPartSelect(const string *net_vname, +VerilogReader::makeNetPartSelect(const std::string *net_vname, int from_index, int to_index) { net_part_select_count_++; if (report_stmt_stats_) net_bus_names_ += net_vname->size() + 1; - const string net_name = netVerilogToSta(net_vname); + const std::string net_name = netVerilogToSta(net_vname); VerilogNetPartSelect *select = new VerilogNetPartSelect(net_name, from_index, to_index); @@ -477,7 +476,7 @@ VerilogReader::makeNetPartSelect(const string *net_vname, } VerilogNetConstant * -VerilogReader::makeNetConstant(const string *constant, +VerilogReader::makeNetConstant(const std::string *constant, int line) { net_constant_count_++; @@ -485,25 +484,25 @@ VerilogReader::makeNetConstant(const string *constant, } VerilogNetScalar * -VerilogReader::makeNetScalar(const string *net_vname) +VerilogReader::makeNetScalar(const std::string *net_vname) { net_scalar_count_++; if (report_stmt_stats_) net_scalar_names_ += net_vname->size() + 1; - const 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; } VerilogNetBitSelect * -VerilogReader::makeNetBitSelect(const string *net_vname, +VerilogReader::makeNetBitSelect(const std::string *net_vname, int index) { net_bit_select_count_++; if (report_stmt_stats_) net_bus_names_ += net_vname->size() + 1; - const 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; @@ -519,14 +518,14 @@ VerilogReader::makeAssign(VerilogNet *lhs, } VerilogInst * -VerilogReader::makeModuleInst(const string *module_vname, - const string *inst_vname, +VerilogReader::makeModuleInst(const std::string *module_vname, + const std::string *inst_vname, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, const int line) { - const string module_name = moduleVerilogToSta(module_vname); - const 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) @@ -541,7 +540,7 @@ VerilogReader::makeModuleInst(const string *module_vname, VerilogNetPortRefScalarNet *vpin = dynamic_cast(vnet); const char *port_name = vpin->name().c_str(); - const string &net_name = vpin->netName(); + const std::string &net_name = vpin->netName(); Port *port = network_->findPort(cell, port_name); LibertyPort *lport = network_->libertyPort(port); if (lport->isBus()) { @@ -607,20 +606,20 @@ VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalarNet(const string *port_vname) +VerilogReader::makeNetNamedPortRefScalarNet(const std::string *port_vname) { net_port_ref_scalar_net_count_++; if (report_stmt_stats_) port_names_ += port_vname->size() + 1; - const 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; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalarNet(const string *port_vname, - const string *net_vname) +VerilogReader::makeNetNamedPortRefScalarNet(const std::string *port_vname, + const std::string *net_vname) { net_port_ref_scalar_net_count_++; if (report_stmt_stats_) { @@ -628,8 +627,8 @@ VerilogReader::makeNetNamedPortRefScalarNet(const string *port_vname, net_scalar_names_ += net_vname->size() + 1; port_names_ += port_vname->size() + 1; } - const string port_name = portVerilogToSta(port_vname); - const string net_name = netVerilogToSta(net_vname); + 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; @@ -638,18 +637,18 @@ VerilogReader::makeNetNamedPortRefScalarNet(const string *port_vname, } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefBitSelect(const string *port_vname, - const string *bus_vname, +VerilogReader::makeNetNamedPortRefBitSelect(const std::string *port_vname, + const std::string *bus_vname, int index) { net_port_ref_scalar_net_count_++; - const string bus_name = portVerilogToSta(bus_vname); - const string net_name = verilogBusBitName(bus_name, index); + 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 string port_name = portVerilogToSta(port_vname); + const std::string port_name = portVerilogToSta(port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name.c_str(), net_name.c_str()); delete port_vname; @@ -658,25 +657,25 @@ VerilogReader::makeNetNamedPortRefBitSelect(const string *port_vname, } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalar(const string *port_vname, +VerilogReader::makeNetNamedPortRefScalar(const std::string *port_vname, VerilogNet *net) { net_port_ref_scalar_count_++; if (report_stmt_stats_) port_names_ += port_vname->size() + 1; - const 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; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefBit(const string *port_vname, +VerilogReader::makeNetNamedPortRefBit(const std::string *port_vname, int index, VerilogNet *net) { net_port_ref_bit_count_++; - const string port_name = portVerilogToSta(port_vname); + const std::string port_name = portVerilogToSta(port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefBit(port_name.c_str(), index, net); delete port_vname; @@ -684,13 +683,13 @@ VerilogReader::makeNetNamedPortRefBit(const string *port_vname, } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefPart(const string *port_vname, +VerilogReader::makeNetNamedPortRefPart(const std::string *port_vname, int from_index, int to_index, VerilogNet *net) { net_port_ref_part_count_++; - const string port_name = portVerilogToSta(port_vname); + const std::string port_name = portVerilogToSta(port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefPart(port_name, from_index, to_index, net); @@ -776,11 +775,11 @@ VerilogReader::warn(int id, //////////////////////////////////////////////////////////////// -VerilogModule::VerilogModule(const string &name, +VerilogModule::VerilogModule(const std::string &name, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, - const string &filename, + const std::string &filename, int line, VerilogReader *reader) : VerilogStmt(line), @@ -822,7 +821,7 @@ VerilogModule::parseDcl(VerilogDcl *dcl, { for (VerilogDclArg *arg : *dcl->args()) { if (arg->isNamed()) { - const string &net_name = arg->netName(); + const std::string &net_name = arg->netName(); VerilogDcl *existing_dcl = dcl_map_[net_name.c_str()]; if (existing_dcl) { PortDirection *existing_dir = existing_dcl->direction(); @@ -845,7 +844,7 @@ VerilogModule::parseDcl(VerilogDcl *dcl, // input/output/inout dcls. dcl_map_[net_name.c_str()] = dcl; else if (!dcl->direction()->isInternal()) { - string net_vname = netVerilogName(net_name.c_str()); + 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(), @@ -865,14 +864,14 @@ VerilogModule::checkInstanceName(VerilogInst *inst, StdStringSet &inst_names, VerilogReader *reader) { - string inst_name = inst->instanceName(); + std::string inst_name = inst->instanceName(); if (inst_names.contains(inst_name)) { int i = 1; - string replacement_name; + std::string replacement_name; do { replacement_name = stdstrPrint("%s_%d", inst_name.c_str(), i++); } while (inst_names.contains(replacement_name)); - string inst_vname = instanceVerilogName(inst_name.c_str()); + 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(), @@ -884,7 +883,7 @@ VerilogModule::checkInstanceName(VerilogInst *inst, } VerilogDcl * -VerilogModule::declaration(const string &net_name) +VerilogModule::declaration(const std::string &net_name) { return findKey(dcl_map_, net_name.c_str()); } @@ -896,7 +895,7 @@ VerilogStmt::VerilogStmt(int line) : { } -VerilogInst::VerilogInst(const string &inst_name, +VerilogInst::VerilogInst(const std::string &inst_name, VerilogAttrStmtSeq *attr_stmts, const int line) : VerilogStmt(line), @@ -912,13 +911,13 @@ VerilogInst::~VerilogInst() } void -VerilogInst::setInstanceName(const string &inst_name) +VerilogInst::setInstanceName(const std::string &inst_name) { inst_name_ = inst_name; } -VerilogModuleInst::VerilogModuleInst(const string &module_name, - const string &inst_name, +VerilogModuleInst::VerilogModuleInst(const std::string &module_name, + const std::string &inst_name, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, int line) : @@ -953,7 +952,7 @@ VerilogModuleInst::namedPins() } VerilogLibertyInst::VerilogLibertyInst(LibertyCell *cell, - const string &inst_name, + const std::string &inst_name, const StdStringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, const int line) : @@ -1000,7 +999,7 @@ VerilogDcl::appendArg(VerilogDclArg *arg) args_->push_back(arg); } -const string & +const std::string & VerilogDcl::portName() { return (*args_)[0]->netName(); @@ -1036,7 +1035,7 @@ VerilogDclBus::size() const return abs(to_index_ - from_index_) + 1; } -VerilogDclArg::VerilogDclArg(const string &net_name) : +VerilogDclArg::VerilogDclArg(const std::string &net_name) : net_name_(net_name), assign_(nullptr) { @@ -1052,7 +1051,7 @@ VerilogDclArg::~VerilogDclArg() delete assign_; } -const string & +const std::string & VerilogDclArg::netName() { if (assign_) @@ -1082,29 +1081,29 @@ class VerilogNullNetNameIterator : public VerilogNetNameIterator { public: virtual bool hasNext() { return false; } - virtual const string &next(); + virtual const std::string &next(); }; -const string & +const std::string & VerilogNullNetNameIterator::next() { - static const string null; + static const std::string null; return null; } class VerilogOneNetNameIterator : public VerilogNetNameIterator { public: - VerilogOneNetNameIterator(const string &name); + VerilogOneNetNameIterator(const std::string &name); virtual bool hasNext(); - virtual const string &next(); + virtual const std::string &next(); protected: - string name_; + std::string name_; bool has_next_; }; -VerilogOneNetNameIterator::VerilogOneNetNameIterator(const string &name) : +VerilogOneNetNameIterator::VerilogOneNetNameIterator(const std::string &name) : name_(name), has_next_(true) { @@ -1116,7 +1115,7 @@ VerilogOneNetNameIterator::hasNext() return has_next_; } -const string & +const std::string & VerilogOneNetNameIterator::next() { has_next_ = false; @@ -1126,21 +1125,21 @@ VerilogOneNetNameIterator::next() class VerilogBusNetNameIterator : public VerilogNetNameIterator { public: - VerilogBusNetNameIterator(const string bus_name, + VerilogBusNetNameIterator(const std::string bus_name, int from_index, int to_index); virtual bool hasNext(); - virtual const string &next(); + virtual const std::string &next(); protected: - const string bus_name_; + const std::string bus_name_; int from_index_; int to_index_; int index_; - string bit_name_; + std::string bit_name_; }; -VerilogBusNetNameIterator::VerilogBusNetNameIterator(const string bus_name, +VerilogBusNetNameIterator::VerilogBusNetNameIterator(const std::string bus_name, int from_index, int to_index) : bus_name_(bus_name), @@ -1159,7 +1158,7 @@ VerilogBusNetNameIterator::hasNext() && index_ >= to_index_); } -const string & +const std::string & VerilogBusNetNameIterator::next() { bit_name_ = verilogBusBitName(bus_name_, index_); @@ -1170,8 +1169,8 @@ VerilogBusNetNameIterator::next() return bit_name_; } -static string -verilogBusBitName(const string &bus_name, +static std::string +verilogBusBitName(const std::string &bus_name, int index) { return stdstrPrint("%s[%d]", bus_name.c_str(), index); @@ -1181,22 +1180,22 @@ class VerilogConstantNetNameIterator : public VerilogNetNameIterator { public: VerilogConstantNetNameIterator(VerilogConstantValue *value, - const string &zero, - const string &one); + const std::string &zero, + const std::string &one); virtual bool hasNext(); - virtual const string &next(); + virtual const std::string &next(); private: VerilogConstantValue *value_; - const string &zero_; - const string &one_; + const std::string &zero_; + const std::string &one_; int bit_index_; }; VerilogConstantNetNameIterator:: VerilogConstantNetNameIterator(VerilogConstantValue *value, - const string &zero, - const string &one) : + const std::string &zero, + const std::string &one) : value_(value), zero_(zero), one_(one), @@ -1210,7 +1209,7 @@ VerilogConstantNetNameIterator::hasNext() return bit_index_ >= 0; } -const string & +const std::string & VerilogConstantNetNameIterator::next() { return (*value_)[bit_index_--] ? one_ : zero_; @@ -1224,7 +1223,7 @@ class VerilogNetConcatNameIterator : public VerilogNetNameIterator VerilogReader *reader); virtual ~VerilogNetConcatNameIterator(); virtual bool hasNext(); - virtual const string &next(); + virtual const std::string &next(); private: VerilogModule *module_; @@ -1262,7 +1261,7 @@ VerilogNetConcatNameIterator::hasNext() || net_iter_ != nets_->end(); } -const string & +const std::string & VerilogNetConcatNameIterator::next() { if (net_name_iter_ && net_name_iter_->hasNext()) @@ -1276,15 +1275,15 @@ VerilogNetConcatNameIterator::next() return net_name_iter_->next(); } } - static const string null; + static const std::string null; return null; } //////////////////////////////////////////////////////////////// -const string VerilogNetUnnamed::null_; +const std::string VerilogNetUnnamed::null_; -VerilogNetNamed::VerilogNetNamed(const string &name) : +VerilogNetNamed::VerilogNetNamed(const std::string &name) : VerilogNet(), name_(name) { @@ -1294,7 +1293,7 @@ VerilogNetNamed::~VerilogNetNamed() { } -VerilogNetScalar::VerilogNetScalar(const string &name) : +VerilogNetScalar::VerilogNetScalar(const std::string &name) : VerilogNetNamed(name) { } @@ -1318,7 +1317,7 @@ VerilogNetScalar::size(VerilogModule *module) } static VerilogNetNameIterator * -verilogNetScalarNameIterator(const string &name, +verilogNetScalarNameIterator(const std::string &name, VerilogModule *module) { if (!name.empty()) { @@ -1339,7 +1338,7 @@ VerilogNetScalar::nameIterator(VerilogModule *module, return verilogNetScalarNameIterator(name_.c_str(), module); } -VerilogNetBitSelect::VerilogNetBitSelect(const string &name, +VerilogNetBitSelect::VerilogNetBitSelect(const std::string &name, int index) : VerilogNetNamed(verilogBusBitName(name, index)), index_(index) @@ -1359,7 +1358,7 @@ VerilogNetBitSelect::nameIterator(VerilogModule *, return new VerilogOneNetNameIterator(name_); } -VerilogNetPartSelect::VerilogNetPartSelect(const string &name, +VerilogNetPartSelect::VerilogNetPartSelect(const std::string &name, int from_index, int to_index): VerilogNetNamed(name), @@ -1384,7 +1383,7 @@ VerilogNetPartSelect::nameIterator(VerilogModule *, return new VerilogBusNetNameIterator(name_.c_str(), from_index_, to_index_); } -VerilogNetConstant::VerilogNetConstant(const string *constant, +VerilogNetConstant::VerilogNetConstant(const std::string *constant, VerilogReader *reader, int line) { @@ -1392,13 +1391,13 @@ VerilogNetConstant::VerilogNetConstant(const string *constant, } void -VerilogNetConstant::parseConstant(const string *constant, +VerilogNetConstant::parseConstant(const std::string *constant, VerilogReader *reader, int line) { // Find constant size. size_t csize_end = constant->find('\''); - string csize = constant->substr(0, csize_end); + std::string csize = constant->substr(0, csize_end); // Read the constant size. size_t size = std::stol(csize); @@ -1434,7 +1433,7 @@ VerilogNetConstant::parseConstant(const string *constant, } void -VerilogNetConstant::parseConstant(const string *constant, +VerilogNetConstant::parseConstant(const std::string *constant, size_t base_idx, int base, int digit_bit_count) @@ -1463,13 +1462,13 @@ VerilogNetConstant::parseConstant(const string *constant, } void -VerilogNetConstant::parseConstant10(const string *constant, +VerilogNetConstant::parseConstant10(const std::string *constant, size_t base_idx, VerilogReader *reader, int line) { // Copy the constant skipping underscores. - string tmp; + std::string tmp; for (size_t i = base_idx + 1; i < constant->size(); i++) { char ch = constant->at(i); if (ch != '_') @@ -1478,7 +1477,7 @@ VerilogNetConstant::parseConstant10(const string *constant, size_t size = value_->size(); size_t length = tmp.size(); - const string &constant10_max = reader->constant10Max(); + const std::string &constant10_max = reader->constant10Max(); size_t max_length = constant10_max.size(); if (length > max_length || (length == max_length @@ -1546,18 +1545,18 @@ VerilogNetConcat::nameIterator(VerilogModule *module, return new VerilogNetConcatNameIterator(nets_, module, reader); } -VerilogNetPortRef::VerilogNetPortRef(const string &name) : +VerilogNetPortRef::VerilogNetPortRef(const std::string &name) : VerilogNetScalar(name) { } -VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(const string &name) : +VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(const std::string &name) : VerilogNetPortRef(name) { } -VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(const string &name, - const string &net_name) : +VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(const std::string &name, + const std::string &net_name) : VerilogNetPortRef(name), net_name_(net_name) { @@ -1584,7 +1583,7 @@ VerilogNetPortRefScalarNet::nameIterator(VerilogModule *module, return verilogNetScalarNameIterator(net_name_, module); } -VerilogNetPortRefScalar::VerilogNetPortRefScalar(const string &name, +VerilogNetPortRefScalar::VerilogNetPortRefScalar(const std::string &name, VerilogNet *net) : VerilogNetPortRef(name), net_(net) @@ -1615,7 +1614,7 @@ VerilogNetPortRefScalar::nameIterator(VerilogModule *module, return new VerilogNullNetNameIterator(); } -VerilogNetPortRefBit::VerilogNetPortRefBit(const string &name, +VerilogNetPortRefBit::VerilogNetPortRefBit(const std::string &name, int index, VerilogNet *net) : VerilogNetPortRefScalar(name, net), @@ -1623,7 +1622,7 @@ VerilogNetPortRefBit::VerilogNetPortRefBit(const string &name, { } -VerilogNetPortRefPart::VerilogNetPortRefPart(const string &name, +VerilogNetPortRefPart::VerilogNetPortRefPart(const std::string &name, int from_index, int to_index, VerilogNet *net) : @@ -1632,26 +1631,26 @@ VerilogNetPortRefPart::VerilogNetPortRefPart(const string &name, { } -const string & +const std::string & VerilogNetPortRefPart::name() const { return name_; } -VerilogAttrEntry::VerilogAttrEntry(const string &key, - const string &value) : +VerilogAttrEntry::VerilogAttrEntry(const std::string &key, + const std::string &value) : key_(key), value_(value) { } -string +std::string VerilogAttrEntry::key() { return key_; } -string +std::string VerilogAttrEntry::value() { return value_; @@ -1687,8 +1686,8 @@ using BindingMap = std::map; class VerilogBindingTbl { public: - VerilogBindingTbl(const string &zero_net_name_, - const string &one_net_name_); + VerilogBindingTbl(const std::string &zero_net_name_, + const std::string &one_net_name_); Net *ensureNetBinding(const char *net_name, Instance *inst, NetworkReader *network); @@ -1698,8 +1697,8 @@ class VerilogBindingTbl Net *net); private: - const string &zero_net_name_; - const string &one_net_name_; + const std::string &zero_net_name_; + const std::string &one_net_name_; BindingMap map_; }; @@ -1719,7 +1718,7 @@ VerilogReader::linkNetwork(const char *top_cell_name, VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module, this); while (net_name_iter->hasNext()) { - const string &net_name = net_name_iter->next(); + 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_); // Guard against repeated port name. @@ -1795,10 +1794,10 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, VerilogBindingTbl *parent_bindings, bool make_black_boxes) { - const string &module_name = mod_inst->moduleName(); + const std::string &module_name = mod_inst->moduleName(); Cell *cell = network_->findAnyCell(module_name.c_str()); if (cell == nullptr) { - string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); + std::string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); if (make_black_boxes) { cell = makeBlackBox(mod_inst, parent_module); linkWarn(198, parent_module->filename(), mod_inst->line(), @@ -1861,7 +1860,7 @@ VerilogReader::makeNamedInstPins(Cell *cell, VerilogBindingTbl *parent_bindings, bool is_leaf) { - string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); + std::string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); for (auto mpin : *mod_inst->pins()) { VerilogNetPortRef *vpin = dynamic_cast(mpin); const char *port_name = vpin->name().c_str(); @@ -1921,7 +1920,7 @@ VerilogReader::makeOrderedInstPins(Cell *cell, VerilogNet *net = *pin_iter++; Port *port = port_iter->next(); if (network_->size(port) != net->size(parent_module)) { - string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); + 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(), @@ -1959,7 +1958,7 @@ VerilogReader::makeInstPin(Instance *inst, VerilogBindingTbl *parent_bindings, bool is_leaf) { - string net_name; + std::string net_name; if (net_name_iter->hasNext()) net_name = net_name_iter->next(); makeInstPin(inst, port, net_name, bindings, parent, parent_bindings, @@ -1969,7 +1968,7 @@ VerilogReader::makeInstPin(Instance *inst, void VerilogReader::makeInstPin(Instance *inst, Port *port, - const string &net_name, + const std::string &net_name, VerilogBindingTbl *bindings, Instance *parent, VerilogBindingTbl *parent_bindings, @@ -2013,7 +2012,7 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, LibertyCellPortBitIterator port_iter(lib_cell); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); - const string &net_name = net_names[port->pinIndex()]; + const std::string &net_name = net_names[port->pinIndex()]; // net_name may be the name of a single bit bus. if (!net_name.empty()) { Net *net = nullptr; @@ -2023,7 +2022,7 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, if (dcl && dcl->isBus()) { VerilogDclBus *dcl_bus = dynamic_cast(dcl); // Bus is only 1 bit wide. - string bus_name = verilogBusBitName(net_name, dcl_bus->fromIndex()); + std::string bus_name = verilogBusBitName(net_name, dcl_bus->fromIndex()); net = parent_bindings->ensureNetBinding(bus_name.c_str(), parent, network_); } else @@ -2042,7 +2041,7 @@ Cell * VerilogReader::makeBlackBox(VerilogModuleInst *mod_inst, VerilogModule *parent_module) { - const string &module_name = mod_inst->moduleName(); + const std::string &module_name = mod_inst->moduleName(); Cell *cell = network_->makeCell(library_, module_name.c_str(), true, parent_module->filename()); if (mod_inst->namedPins()) @@ -2109,8 +2108,8 @@ VerilogReader::mergeAssignNet(VerilogAssign *assign, VerilogNetNameIterator *lhs_iter = lhs->nameIterator(module, this); VerilogNetNameIterator *rhs_iter = rhs->nameIterator(module, this); while (lhs_iter->hasNext() && rhs_iter->hasNext()) { - const string &lhs_name = lhs_iter->next(); - const string &rhs_name = rhs_iter->next(); + const std::string &lhs_name = lhs_iter->next(); + const std::string &rhs_name = rhs_iter->next(); Net *lhs_net = bindings->ensureNetBinding(lhs_name.c_str(), inst, network_); Net *rhs_net = bindings->ensureNetBinding(rhs_name.c_str(), inst, network_); // Merge lower level net into higher level net so that deleting @@ -2148,8 +2147,8 @@ hierarchyLevel(Net *net, //////////////////////////////////////////////////////////////// -VerilogBindingTbl::VerilogBindingTbl(const string &zero_net_name, - const string &one_net_name) : +VerilogBindingTbl::VerilogBindingTbl(const std::string &zero_net_name, + const std::string &one_net_name) : zero_net_name_(zero_net_name), one_net_name_(one_net_name) { diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index 3ba02ba99..a38e44b90 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -24,9 +24,10 @@ #include "VerilogWriter.hh" -#include #include +#include #include +#include #include "Error.hh" #include "Liberty.hh" @@ -38,10 +39,6 @@ namespace sta { -using std::min; -using std::max; -using std::string; - class VerilogWriter { public: @@ -284,8 +281,8 @@ VerilogWriter::writeWireDcls(const Instance *inst) int index; parseBusName(net_name, '[', ']', escape, is_bus, bus_name, index); BusIndexRange &range = bus_ranges[bus_name]; - range.first = max(range.first, index); - range.second = min(range.second, index); + range.first = std::max(range.first, index); + range.second = std::min(range.second, index); } else { std::string net_vname = netVerilogName(net_name); @@ -337,8 +334,8 @@ VerilogWriter::writeChild(const Instance *child) Cell *child_cell = network_->cell(child); if (!remove_cells_.contains(child_cell)) { const char *child_name = network_->name(child); - string child_vname = instanceVerilogName(child_name); - string child_cell_vname = cellVerilogName(network_->name(child_cell)); + 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()); @@ -369,10 +366,10 @@ VerilogWriter::writeInstPin(const Instance *inst, Net *net = network_->net(pin); if (net) { const char *net_name = network_->name(net); - string net_vname = netVerilogName(net_name); + std::string net_vname = netVerilogName(net_name); if (!first_port) fprintf(stream_, ",\n "); - string port_vname = portVerilogName(network_->name(port)); + std::string port_vname = portVerilogName(network_->name(port)); fprintf(stream_, ".%s(%s)", port_vname.c_str(), net_vname.c_str()); @@ -422,13 +419,13 @@ VerilogWriter::writeInstBusPinBit(const Instance *inst, { Pin *pin = network_->findPin(inst, port); Net *net = pin ? network_->net(pin) : nullptr; - string net_name; + std::string net_name; if (net) net_name = network_->name(net); else // There is no verilog syntax to "skip" a bit in the concatentation. stringPrint(net_name, "_NC%d", unconnected_net_index_++); - string net_vname = netVerilogName(net_name.c_str()); + std::string net_vname = netVerilogName(net_name.c_str()); if (!first_member) fprintf(stream_, ",\n "); fprintf(stream_, "%s", net_vname.c_str()); @@ -455,8 +452,8 @@ VerilogWriter::writeAssigns(const Instance *inst) || (include_pwr_gnd_ && network_->direction(port)->isPowerGround())) && !stringEqual(network_->name(port), network_->name(net))) { // Port name is different from net name. - string port_vname = netVerilogName(network_->name(port)); - string net_vname = netVerilogName(network_->name(net)); + 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()); From e054499e4592cd07c0b5fed2e4d7c413d0b4906d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 2 Mar 2026 13:22:13 -0800 Subject: [PATCH 040/181] report_arrival requires not needed Signed-off-by: James Cherry --- include/sta/Sta.hh | 2 ++ search/Sta.cc | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index ddb2f7d72..ed1c6c47c 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1517,10 +1517,12 @@ protected: void reportDelaysWrtClks(const Pin *pin, const Scene *scene, int digits, + bool find_required, PathDelayFunc get_path_delay); void reportDelaysWrtClks(Vertex *vertex, const Scene *scene, int digits, + bool find_required, PathDelayFunc get_path_delay); void reportDelaysWrtClks(Vertex *vertex, const ClockEdge *clk_edge, diff --git a/search/Sta.cc b/search/Sta.cc index 5c096867c..fa1c3bb4b 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -3270,7 +3270,8 @@ Sta::reportArrivalWrtClks(const Pin *pin, const Scene *scene, int digits) { - reportDelaysWrtClks(pin, scene, digits, + searchPreamble(); + reportDelaysWrtClks(pin, scene, digits, false, [] (const Path *path) { return path->arrival(); }); @@ -3281,7 +3282,7 @@ Sta::reportRequiredWrtClks(const Pin *pin, const Scene *scene, int digits) { - reportDelaysWrtClks(pin, scene, digits, + reportDelaysWrtClks(pin, scene, digits, true, [] (const Path *path) { return path->required(); }); @@ -3292,7 +3293,7 @@ Sta::reportSlackWrtClks(const Pin *pin, const Scene *scene, int digits) { - reportDelaysWrtClks(pin, scene, digits, + reportDelaysWrtClks(pin, scene, digits, true, [this] (const Path *path) { return path->slack(this); }); @@ -3302,24 +3303,29 @@ void Sta::reportDelaysWrtClks(const Pin *pin, const Scene *scene, int digits, + bool find_required, PathDelayFunc get_path_delay) { ensureGraph(); Vertex *vertex, *bidir_vertex; graph_->pinVertices(pin, vertex, bidir_vertex); if (vertex) - reportDelaysWrtClks(vertex, scene, digits, get_path_delay); + reportDelaysWrtClks(vertex, scene, digits, find_required, get_path_delay); if (bidir_vertex) - reportDelaysWrtClks(vertex, scene, digits, get_path_delay); + reportDelaysWrtClks(vertex, scene, digits, find_required, get_path_delay); } void Sta::reportDelaysWrtClks(Vertex *vertex, const Scene *scene, int digits, + bool find_required, PathDelayFunc get_path_delay) { - findRequired(vertex); + if (find_required) + findRequired(vertex); + else + search_->findArrivals(vertex->level()); const Sdc *sdc = scene->sdc(); reportDelaysWrtClks(vertex, nullptr, scene, digits, get_path_delay); const ClockEdge *default_clk_edge = sdc->defaultArrivalClock()->edge(RiseFall::rise()); From f89fcbfa115e74add71487218ff3b76e30023288 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 2 Mar 2026 16:26:14 -0800 Subject: [PATCH 041/181] tcl sta::is_ideal_clock Signed-off-by: James Cherry --- include/sta/Sta.hh | 8 ++++---- search/Search.i | 9 +++++++++ search/Sta.cc | 13 +++++++++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index ed1c6c47c..718f97aef 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1297,13 +1297,13 @@ public: void clkPinsInvalid(const Mode *mode); // The following functions assume ensureClkNetwork() has been called. bool isClock(const Pin *pin, - const Mode *mode) const; + const Mode *mode); bool isClock(const Net *net, - const Mode *mode) const; + const Mode *mode); bool isIdealClock(const Pin *pin, - const Mode *mode) const; + const Mode *mode); bool isPropagatedClock(const Pin *pin, - const Mode *mode) const; + const Mode *mode); const PinSet *pins(const Clock *clk, const Mode *mode); diff --git a/search/Search.i b/search/Search.i index 8978d5301..67d848761 100644 --- a/search/Search.i +++ b/search/Search.i @@ -335,6 +335,15 @@ slow_drivers(int count) return Sta::sta()->slowDrivers(count); } +bool +is_ideal_clock(const Pin *pin) +{ + Sta *sta = Sta::sta(); + const Mode *mode = sta->cmdMode(); + sta->ensureClkNetwork(); + return sta->isIdealClock(pin, mode); +} + //////////////////////////////////////////////////////////////// PathEndSeq diff --git a/search/Sta.cc b/search/Sta.cc index fa1c3bb4b..2d91a4f46 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -6054,28 +6054,31 @@ Sta::ensureClkNetwork(const Mode *mode) bool Sta::isClock(const Pin *pin, - const Mode *mode) const + const Mode *mode) { + ensureClkNetwork(mode); return mode->clkNetwork()->isClock(pin); } bool Sta::isClock(const Net *net, - const Mode *mode) const + const Mode *mode) { + ensureClkNetwork(mode); return mode->clkNetwork()->isClock(net); } bool Sta::isIdealClock(const Pin *pin, - const Mode *mode) const + const Mode *mode) { + ensureClkNetwork(mode); return mode->clkNetwork()->isIdealClock(pin); } bool Sta::isPropagatedClock(const Pin *pin, - const Mode *mode) const + const Mode *mode) { return mode->clkNetwork()->isPropagatedClock(pin); } @@ -6084,12 +6087,14 @@ const PinSet * Sta::pins(const Clock *clk, const Mode *mode) { + ensureClkNetwork(mode); return mode->clkNetwork()->pins(clk); } void Sta::clkPinsInvalid(const Mode *mode) { + ensureClkNetwork(mode); mode->clkNetwork()->clkPinsInvalid(); } From 6f4cdcfea8efa9bb277b6d09909a5fba8bbf5a49 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 2 Mar 2026 16:41:35 -0800 Subject: [PATCH 042/181] tcl sta::is_ideal_clock Signed-off-by: James Cherry --- search/Search.i | 1 - 1 file changed, 1 deletion(-) diff --git a/search/Search.i b/search/Search.i index 67d848761..550f1b003 100644 --- a/search/Search.i +++ b/search/Search.i @@ -340,7 +340,6 @@ is_ideal_clock(const Pin *pin) { Sta *sta = Sta::sta(); const Mode *mode = sta->cmdMode(); - sta->ensureClkNetwork(); return sta->isIdealClock(pin, mode); } From 73e1a392c5998e3e15b5942f5c1b8b22ec7abb08 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 2 Mar 2026 16:47:40 -0800 Subject: [PATCH 043/181] rm unused Sta::report functions Signed-off-by: James Cherry --- include/sta/Sta.hh | 10 ---------- search/Search.i | 20 -------------------- search/Sta.cc | 20 -------------------- 3 files changed, 50 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 718f97aef..5bc48f70a 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -988,16 +988,6 @@ public: void setReportPathDigits(int digits); void setReportPathNoSplit(bool no_split); void setReportPathSigmas(bool report_sigmas); - // Header above reportPathEnd results. - void reportPathEndHeader(); - // Footer below reportPathEnd results. - void reportPathEndFooter(); - // Format report_path_endpoint only: - // Previous path end is used to detect path group changes - // so headers are reported by group. - void reportPathEnd(PathEnd *end, - PathEnd *prev_end, - bool last); void reportPathEnd(PathEnd *end); void reportPathEnds(PathEndSeq *ends); ReportPath *reportPath() { return report_path_; } diff --git a/search/Search.i b/search/Search.i index 550f1b003..8ffff0ff0 100644 --- a/search/Search.i +++ b/search/Search.i @@ -382,32 +382,12 @@ find_path_ends(ExceptionFrom *from, //////////////////////////////////////////////////////////////// -void -report_path_end_header() -{ - Sta::sta()->reportPathEndHeader(); -} - -void -report_path_end_footer() -{ - Sta::sta()->reportPathEndFooter(); -} - void report_path_end(PathEnd *end) { Sta::sta()->reportPathEnd(end); } -void -report_path_end2(PathEnd *end, - PathEnd *prev_end, - bool last) -{ - Sta::sta()->reportPathEnd(end, prev_end, last); -} - void set_report_path_format(ReportPathFormat format) { diff --git a/search/Sta.cc b/search/Sta.cc index 2d91a4f46..7899a1109 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2772,32 +2772,12 @@ Sta::setReportPathSigmas(bool report_sigmas) report_path_->setReportSigmas(report_sigmas); } -void -Sta::reportPathEndHeader() -{ - report_path_->reportPathEndHeader(); -} - -void -Sta::reportPathEndFooter() -{ - report_path_->reportPathEndFooter(); -} - void Sta::reportPathEnd(PathEnd *end) { report_path_->reportPathEnd(end); } -void -Sta::reportPathEnd(PathEnd *end, - PathEnd *prev_end, - bool last) -{ - report_path_->reportPathEnd(end, prev_end, last); -} - void Sta::reportPathEnds(PathEndSeq *ends) { From eb0446d4e20679fcacd85d941069fbe83b4d7d87 Mon Sep 17 00:00:00 2001 From: Deepashree Sengupta Date: Tue, 3 Mar 2026 00:48:15 +0000 Subject: [PATCH 044/181] Write verilog escape (#394) * Fir for write_verilog issue 3826 Signed-off-by: dsengupta0628 * staToVerilog2 remove escaped_name+=ch Signed-off-by: dsengupta0628 * updated regression to remove \ from module name Signed-off-by: dsengupta0628 * Using helpers.tcl function to redirect results Signed-off-by: dsengupta0628 * add std::string and remove trailing space, update regression name Signed-off-by: dsengupta0628 * update regression to reflect correct output verilog name Signed-off-by: dsengupta0628 --------- Signed-off-by: dsengupta0628 --- network/VerilogNamespace.cc | 10 ++-------- test/regression_vars.tcl | 1 + test/verilog_write_escape.ok | 18 ++++++++++++++++++ test/verilog_write_escape.tcl | 10 ++++++++++ test/verilog_write_escape.v | 13 +++++++++++++ verilog/VerilogWriter.cc | 3 ++- 6 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 test/verilog_write_escape.ok create mode 100644 test/verilog_write_escape.tcl create mode 100644 test/verilog_write_escape.v diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index 5362d115b..1d0231d92 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -86,15 +86,12 @@ staToVerilog(const char *sta_name) for (const char *s = sta_name; *s ; s++) { char ch = s[0]; if (ch == verilog_escape) { + escaped = true; char next_ch = s[1]; if (next_ch == verilog_escape) { - escaped_name += ch; escaped_name += next_ch; s++; } - else - // Skip escape. - escaped = true; } else { if ((!(isalnum(ch) || ch == '_'))) @@ -124,15 +121,12 @@ staToVerilog2(const char *sta_name) for (const char *s = sta_name; *s ; s++) { char ch = s[0]; if (ch == verilog_escape) { + escaped = true; char next_ch = s[1]; if (next_ch == verilog_escape) { - escaped_name += ch; escaped_name += next_ch; s++; } - else - // Skip escape. - escaped = true; } else { bool is_brkt = (ch == bus_brkt_left || ch == bus_brkt_right); diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index c1ed29eac..ab1f6c62d 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -165,6 +165,7 @@ record_public_tests { suppress_msg verilog_attribute verilog_specify + verilog_write_escape } define_test_group fast [group_tests all] diff --git a/test/verilog_write_escape.ok b/test/verilog_write_escape.ok new file mode 100644 index 000000000..4906a851f --- /dev/null +++ b/test/verilog_write_escape.ok @@ -0,0 +1,18 @@ +module multi_sink (clk); + input clk; + + wire \alu_adder_result_ex[0] ; + + hier_block \h1\x (.childclk(clk), + .\Y[2:1] ({\alu_adder_result_ex[0] , + \alu_adder_result_ex[0] })); +endmodule +module hier_block (childclk, + \Y[2:1] ); + input childclk; + output [1:0] \Y[2:1] ; + + + BUFx2_ASAP7_75t_R \abuf_$100 (.A(childclk)); + BUFx2_ASAP7_75t_R \ff0/name (.A(childclk)); +endmodule diff --git a/test/verilog_write_escape.tcl b/test/verilog_write_escape.tcl new file mode 100644 index 000000000..29e785909 --- /dev/null +++ b/test/verilog_write_escape.tcl @@ -0,0 +1,10 @@ +# Check if "h1\x" and \Y[2:1] are correctly processed from input to output of Verilog +source helpers.tcl +read_liberty gf180mcu_sram.lib.gz +read_liberty asap7_small.lib.gz +read_verilog verilog_write_escape.v +link_design multi_sink +set verilog_file [make_result_file "verilog_write_escape.v"] +write_verilog $verilog_file +report_file $verilog_file +read_verilog $verilog_file diff --git a/test/verilog_write_escape.v b/test/verilog_write_escape.v new file mode 100644 index 000000000..e0c43c3e0 --- /dev/null +++ b/test/verilog_write_escape.v @@ -0,0 +1,13 @@ +module multi_sink (clk); + input clk; + wire \alu_adder_result_ex[0] ; + \hier_block \h1\x (.childclk(clk), .\Y[2:1] ({ \alu_adder_result_ex[0] , \alu_adder_result_ex[0] }) ); +endmodule // multi_sink + +module hier_block (childclk, \Y[2:1] ); + input childclk; + output [1:0] \Y[2:1] ; + wire [1:0] \Y[2:1] ; + BUFx2_ASAP7_75t_R \abuf_$100 (.A(childclk)); + BUFx2_ASAP7_75t_R \ff0/name (.A(childclk)); +endmodule // hier_block1 diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index a38e44b90..3d6e274c2 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -386,7 +386,8 @@ VerilogWriter::writeInstBusPin(const Instance *inst, if (!first_port) fprintf(stream_, ",\n "); - fprintf(stream_, ".%s({", network_->name(port)); + std::string port_vname = portVerilogName(network_->name(port)); + fprintf(stream_, ".%s({", port_vname.c_str()); first_port = false; bool first_member = true; From 62cd210a8aff98cb8bf795e7b70c610faf5deebb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 3 Mar 2026 07:59:09 -0800 Subject: [PATCH 045/181] back edge->to is a root resolves #395 Signed-off-by: James Cherry --- search/Levelize.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/Levelize.cc b/search/Levelize.cc index d04891bfa..5a0127b00 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -323,7 +323,7 @@ Levelize::findCycleBackEdges() stack.emplace(vertex, new VertexOutEdgeIterator(vertex, graph_)); EdgeSet back_edges = findBackEdges(path, stack); for (Edge *back_edge : back_edges) - roots_.insert(back_edge->from(graph_)); + roots_.insert(back_edge->to(graph_)); back_edge_count += back_edges.size(); } } From 8ed837d74b6c341f574284024de1343127014692 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 5 Mar 2026 13:33:58 -0800 Subject: [PATCH 046/181] relevelize latch EN->Q Signed-off-by: James Cherry --- Dockerfile.ubuntu22.04 | 2 ++ include/sta/TimingRole.hh | 1 + liberty/TimingRole.cc | 6 ++++++ search/Levelize.cc | 29 ++++++++++++++++++++++++----- search/Sta.cc | 28 ++++++++++++++++------------ 5 files changed, 49 insertions(+), 17 deletions(-) diff --git a/Dockerfile.ubuntu22.04 b/Dockerfile.ubuntu22.04 index 9a803c8f3..e636fc210 100644 --- a/Dockerfile.ubuntu22.04 +++ b/Dockerfile.ubuntu22.04 @@ -6,9 +6,11 @@ LABEL maintainer="James Cherry " ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ apt-get install -y \ + git \ wget \ cmake \ gcc \ + gdb \ tcl-dev \ tcl-tclreadline \ libeigen3-dev \ diff --git a/include/sta/TimingRole.hh b/include/sta/TimingRole.hh index 3621f63c6..d77a84541 100644 --- a/include/sta/TimingRole.hh +++ b/include/sta/TimingRole.hh @@ -78,6 +78,7 @@ public: [[nodiscard]] bool isNonSeqTimingCheck() const { return is_non_seq_check_; } [[nodiscard]] bool isDataCheck() const; [[nodiscard]] bool isLatchDtoQ() const; + [[nodiscard]] bool isLatchEnToQ() const; const TimingRole *genericRole() const; const TimingRole *sdfRole() const; // Timing check data path min/max. diff --git a/liberty/TimingRole.cc b/liberty/TimingRole.cc index 1839e52f4..e772d1bcc 100644 --- a/liberty/TimingRole.cc +++ b/liberty/TimingRole.cc @@ -163,6 +163,12 @@ TimingRole::isLatchDtoQ() const return this == &latch_d_q_; } +bool +TimingRole::isLatchEnToQ() const +{ + return this == &latch_en_q_; +} + bool TimingRole::isTimingCheckBetween() const { diff --git a/search/Levelize.cc b/search/Levelize.cc index 5a0127b00..a53f8f7e1 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -526,8 +526,16 @@ Levelize::ensureLatchLevels() for (Edge *edge : latch_d_to_q_edges_) { Vertex *from = edge->from(graph_); Vertex *to = edge->to(graph_); - if (from->level() == to->level()) - setLevel(from, from->level() + level_space_); + 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()); + setLevel(from, adjusted_level); + } } latch_d_to_q_edges_.clear(); } @@ -536,7 +544,7 @@ void Levelize::setLevel(Vertex *vertex, Level level) { - debugPrint(debug_, "levelize", 2, "set level %s %d", + debugPrint(debug_, "levelize", 3, "set level %s %d", vertex->to_string(this).c_str(), level); vertex->setLevel(level); @@ -602,7 +610,7 @@ void Levelize::relevelize() { for (Vertex *vertex : relevelize_from_) { - debugPrint(debug_, "levelize", 1, "relevelize from %s", + debugPrint(debug_, "levelize", 2, "relevelize from %s", vertex->to_string(this).c_str()); if (isRoot(vertex)) roots_.insert(vertex); @@ -641,9 +649,20 @@ Levelize::visit(Vertex *vertex, visit(to_vertex, edge, level+level_space, level_space, path_vertices, path); } - if (edge->role() == TimingRole::latchDtoQ()) + + const TimingRole *role = edge->role(); + if (role->isLatchDtoQ()) latch_d_to_q_edges_.insert(edge); + if (role->isLatchEnToQ()) { + VertexInEdgeIterator edge_iter2(to_vertex, graph_); + while (edge_iter2.hasNext()) { + Edge *edge2 = edge_iter2.next(); + if (edge2->role()->isLatchDtoQ()) + latch_d_to_q_edges_.insert(edge2); + } + } } + // Levelize bidirect driver as if it was a fanout of the bidirect load. if (graph_delay_calc_->bidirectDrvrSlewFromLoad(from_pin) && !vertex->isBidirectDriver()) { diff --git a/search/Sta.cc b/search/Sta.cc index 7899a1109..527e8ee1d 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4671,12 +4671,14 @@ Sta::connectLoadPinAfter(Vertex *vertex) VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - graph_delay_calc_->delayInvalid(from_vertex); - search_->requiredInvalid(from_vertex); - for (Mode *mode : modes_) - mode->sdc()->clkHpinDisablesChanged(from_vertex->pin()); - levelize_->relevelizeFrom(from_vertex); + if (!edge->role()->isTimingCheck()) { + Vertex *from_vertex = edge->from(graph_); + graph_delay_calc_->delayInvalid(from_vertex); + search_->requiredInvalid(from_vertex); + levelize_->relevelizeFrom(from_vertex); + for (Mode *mode : modes_) + mode->sdc()->clkHpinDisablesChanged(from_vertex->pin()); + } } Pin *pin = vertex->pin(); for (Mode *mode : modes_) { @@ -4754,12 +4756,14 @@ Sta::deleteEdge(Edge *edge) edge->from(graph_)->name(sdc_network_), edge->to(graph_)->name(sdc_network_)); Vertex *to = edge->to(graph_); - search_->deleteEdgeBefore(edge); - graph_delay_calc_->delayInvalid(to); - levelize_->relevelizeFrom(to); - levelize_->deleteEdgeBefore(edge); - for (Mode *mode : modes_) - mode->sdc()->clkHpinDisablesChanged(edge->from(graph_)->pin()); + if (!edge->role()->isTimingCheck()) { + search_->deleteEdgeBefore(edge); + graph_delay_calc_->delayInvalid(to); + levelize_->relevelizeFrom(to); + levelize_->deleteEdgeBefore(edge); + for (Mode *mode : modes_) + mode->sdc()->clkHpinDisablesChanged(edge->from(graph_)->pin()); + } graph_->deleteEdge(edge); } From f1e5587fef8115ccdb32a57d0196d22be348e11c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 5 Mar 2026 18:41:25 -0700 Subject: [PATCH 047/181] rapidus liberty latch D->Q/EN->Q matching Signed-off-by: James Cherry --- include/sta/TimingArc.hh | 1 + liberty/Liberty.cc | 88 +++++++++++++++++++++++----------------- liberty/Liberty.i | 10 +++++ liberty/Liberty.tcl | 10 ++++- liberty/LibertyReader.cc | 47 ++++++++++++--------- liberty/TimingArc.cc | 3 +- 6 files changed, 98 insertions(+), 61 deletions(-) diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 63c04d49e..257e446dc 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -176,6 +176,7 @@ public: // other conditional timing arcs between the same pins. bool isCondDefault() const { return is_cond_default_; } void setIsCondDefault(bool is_default); + const FuncExpr *when() const { return attrs_->cond(); } // SDF IOPATHs match sdfCond. // sdfCond (IOPATH) reuses sdfCondStart (timing check) variable. const std::string &sdfCond() const { return attrs_->sdfCondStart(); } diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 130af8027..181b0e42c 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1679,45 +1679,58 @@ LibertyCell::makeLatchEnables(Report *report, { if (hasSequentials() || hasInferedRegTimingArcs()) { - for (auto en_to_q : timing_arc_sets_) { - if (en_to_q->role() == TimingRole::latchEnToQ()) { - LibertyPort *en = en_to_q->from(); - LibertyPort *q = en_to_q->to(); - for (TimingArcSet *d_to_q : timingArcSetsTo(q)) { - if (d_to_q->role() == TimingRole::latchDtoQ() - && condMatch(en_to_q, d_to_q)) { - LibertyPort *d = d_to_q->from(); - const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); - if (en_rf) { - TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, - report); - LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, - en_to_q, - setup_check, - debug); - FuncExpr *en_func = latch_enable->enableFunc(); - if (en_func) { - 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.", - library_->name(), - name(), - en->name(), - q->name(), - 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.", - library_->name(), - name(), - en->name(), - q->name(), - en_rf == RiseFall::rise()?"rising":"falling"); - } + for (TimingArcSet *d_to_q : timing_arc_sets_) { + if (d_to_q->role() == TimingRole::latchDtoQ()) { + LibertyPort *d = d_to_q->from(); + LibertyPort *q = d_to_q->to(); + TimingArcSet *en_to_q = nullptr; + TimingArcSet *en_to_q_when = nullptr; + // Prefer en_to_q with matching when. + for (TimingArcSet *arc_to_q : timingArcSetsTo(q)) { + if (arc_to_q->role() == TimingRole::latchEnToQ()) { + if (condMatch(arc_to_q, d_to_q)) + en_to_q_when = arc_to_q; + else + en_to_q = arc_to_q; + } + } + if (en_to_q_when) + en_to_q = en_to_q_when; + if (en_to_q) { + LibertyPort *en = en_to_q->from(); + const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); + if (en_rf) { + TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, d_to_q, report); + LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, + en_to_q, setup_check, debug); + FuncExpr *en_func = latch_enable->enableFunc(); + if (en_func) { + 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.", + library_->name(), + name(), + en->name(), + q->name(), + 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.", + library_->name(), + name(), + en->name(), + q->name(), + en_rf == RiseFall::rise()?"rising":"falling"); } } } + else + report->warn(1121, "cell %s/%s no latch enable found for %s -> %s.", + library_->name(), + name(), + d->name(), + q->name()); } } } @@ -1810,8 +1823,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d, Debug *debug) { FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf); - latch_enables_.emplace_back(d, en, en_rf, en_func, q, d_to_q, en_to_q, - setup_check); + latch_enables_.emplace_back(d, en, en_rf, en_func, q, d_to_q, en_to_q, setup_check); size_t idx = latch_enables_.size() - 1; latch_d_to_q_map_[d_to_q] = idx; latch_check_map_[setup_check] = idx; diff --git a/liberty/Liberty.i b/liberty/Liberty.i index 2257b2eb7..d2856e686 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -379,6 +379,16 @@ full_name() to); } +const std::string +when() +{ + const FuncExpr *when = self->when(); + if (when) + return when->to_string(); + else + return ""; +} + TimingArcSeq & timing_arcs() { return self->arcs(); } diff --git a/liberty/Liberty.tcl b/liberty/Liberty.tcl index fd75a8bf1..6d93601d7 100644 --- a/liberty/Liberty.tcl +++ b/liberty/Liberty.tcl @@ -126,7 +126,15 @@ proc report_timing_arcs { cell } { puts "" puts "Timing arcs" foreach timing_arc $timing_arcs { - puts [$timing_arc to_string] + puts " [$timing_arc to_string]" + puts " [$timing_arc role]" + set when [$timing_arc when] + if { $when != "" } { + puts " when $when" + } + foreach arc [$timing_arc timing_arcs] { + puts " [$arc from_edge] -> [$arc to_edge]" + } } } } diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index c070e3dde..6851b5016 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -2059,7 +2059,7 @@ LibertyReader::makeTimingModels(LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { - switch (cell->libertyLibrary()->delayModelType()) { + switch (cell->libertyLibrary()->delayModelType()) { case DelayModelType::cmos_linear: makeLinearModels(cell, timing_group, timing_attrs); break; @@ -2115,6 +2115,7 @@ LibertyReader::makeTableModels(LibertyCell *cell, const LibertyGroup *timing_group, TimingArcAttrsPtr timing_attrs) { + bool found_model = false; for (const RiseFall *rf : RiseFall::range()) { std::string delay_attr_name = "cell_" + rf->to_string_long(); TableModel *delay = readGateTableModel(timing_group, delay_attr_name.c_str(), rf, @@ -2160,30 +2161,36 @@ LibertyReader::makeTableModels(LibertyCell *cell, if (delay == nullptr) libWarn(1211, timing_group, "missing cell_%s.", rf->name()); } + found_model = true; } - - std::string constraint_attr_name = rf->to_string_long() + "_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_long() - + "_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))); + else { + std::string constraint_attr_name = rf->to_string_long() + "_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_long() + + "_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; + } } } + if (!found_model) + libWarn(1311, timing_group, "no table models found in timing group."); } + bool LibertyReader::isGateTimingType(TimingType timing_type) { diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 7bc324a62..ba5f086b6 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -207,7 +207,6 @@ TimingArcSet::to_string() std::string str = from_->name(); str += " -> "; str += to_->name(); - str += " " + role()->to_string(); return str; } @@ -333,7 +332,7 @@ TimingArcSet::isRisingFallingEdge() const if (from_rf1 == from_rf2) return from_rf1; } - if (arcs_.size() == 1) + if (arc_count == 1) return arcs_[0]->fromEdge()->asRiseFall(); else return nullptr; From d8c0e9285be75e4b93f4b08b0afccc01224b2f07 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 6 Mar 2026 12:02:05 -0700 Subject: [PATCH 048/181] RiseFall use shortName instead of to_string Signed-off-by: James Cherry --- dcalc/CcsCeffDelayCalc.cc | 2 +- dcalc/GraphDelayCalc.cc | 2 +- dcalc/PrimaDelayCalc.cc | 4 ++-- graph/Graph.i | 2 +- include/sta/Transition.hh | 5 ++--- liberty/LibertyReader.cc | 30 +++++++++++++++--------------- parasitics/ReduceParasitics.cc | 2 +- sdc/Clock.cc | 2 +- search/ClkSkew.cc | 8 ++++---- search/Genclks.cc | 2 +- search/Path.cc | 2 +- search/PathEnum.cc | 2 +- search/PathGroup.cc | 4 ++-- search/Search.cc | 10 +++++----- search/Tag.cc | 3 +-- spice/WritePathSpice.cc | 8 ++++---- tcl/StaTclTypes.i | 4 ++-- util/Transition.cc | 18 ++++++++++++++++++ 18 files changed, 63 insertions(+), 47 deletions(-) diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 3f460d084..ae71f6489 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -118,7 +118,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, ref_time_ = output_waveforms_->referenceTime(in_slew_); debugPrint(debug_, "ccs_dcalc", 1, "%s %s", drvr_cell->name(), - drvr_rf_->to_string().c_str()); + drvr_rf_->shortName()); ArcDelay gate_delay; Slew drvr_slew; gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew); diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 5fdf9587d..06962c785 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -604,7 +604,7 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, { debugPrint(debug_, "delay_calc", 2, " driver cell %s %s", drvr_cell->name(), - rf->to_string().c_str()); + rf->shortName()); for (TimingArcSet *arc_set : drvr_cell->timingArcSets(from_port, to_port)) { for (TimingArc *arc : arc_set->arcs()) { if (arc->toEdge()->asRiseFall() == rf) { diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 85175777a..118233cef 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -231,7 +231,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, output_waveforms_[drvr_idx] = output_waveforms; debugPrint(debug_, "ccs_dcalc", 1, "%s %s", dcalc_arg.drvrCell()->name(), - drvr_rf_->to_string().c_str()); + drvr_rf_->shortName()); LibertyCell *drvr_cell = dcalc_arg.drvrCell(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); bool vdd_exists; @@ -744,7 +744,7 @@ PrimaDelayCalc::dcalcResults() debugPrint(debug_, "ccs_dcalc", 2, "load %s %s delay %s slew %s", network_->pathName(load_pin), - drvr_rf_->to_string().c_str(), + drvr_rf_->shortName(), delayAsString(wire_delay, this), delayAsString(load_slew, this)); diff --git a/graph/Graph.i b/graph/Graph.i index b6f3c3c53..289ca3ba9 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -299,7 +299,7 @@ latch_d_to_q_en() if (enable_port) return stringPrintTmp("%s %s", enable_port->name(), - enable_rf->to_string().c_str()); + enable_rf->shortName()); } return ""; } diff --git a/include/sta/Transition.hh b/include/sta/Transition.hh index 3f3e03c6d..ba6714530 100644 --- a/include/sta/Transition.hh +++ b/include/sta/Transition.hh @@ -48,8 +48,7 @@ public: static const RiseFall *fall() { return &fall_; } static int riseIndex() { return rise_.sdf_triple_index_; } static int fallIndex() { return fall_.sdf_triple_index_; } - const std::string &to_string_long() const { return name_; } - const std::string &to_string() const { return short_name_; } + const std::string &to_string(bool use_short = false) const; const char *name() const { return name_.c_str(); } const char *shortName() const { return short_name_.c_str(); } int index() const { return sdf_triple_index_; } @@ -94,7 +93,7 @@ public: static const RiseFallBoth *rise() { return &rise_; } static const RiseFallBoth *fall() { return &fall_; } static const RiseFallBoth *riseFall() { return &rise_fall_; } - const std::string &to_string() const { return short_name_; } + const std::string &to_string(bool use_short = false) const; const char *name() const { return name_.c_str(); } const char *shortName() const { return short_name_.c_str(); } int index() const { return sdf_triple_index_; } diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 6851b5016..7d77fa3ca 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -524,7 +524,7 @@ void LibertyReader::readThresholds(const LibertyGroup *library_group) { for (const RiseFall *rf : RiseFall::range()) { - std::string suffix = rf->to_string_long(); + std::string suffix = rf->to_string(); readLibAttrFloat(library_group, ("input_threshold_pct_" + suffix).c_str(), &LibertyLibrary::setInputThreshold, rf, 0.01F); if (library_->inputThreshold(rf) == 0.0) @@ -907,7 +907,7 @@ void LibertyReader::readSlewDegradations(const LibertyGroup *library_group) { for (const RiseFall *rf : RiseFall::range()) { - const std::string group_name = rf->to_string_long() + "_transition_degradation"; + const std::string group_name = rf->to_string() + "_transition_degradation"; const LibertyGroup *degradation_group = library_group->findSubgroup(group_name.c_str()); if (degradation_group) { @@ -1480,7 +1480,7 @@ LibertyReader::readCapacitance(const LibertyPortSeq &ports, for (LibertyPort *port : ports) { // rise/fall_capacitance for (const RiseFall *rf : RiseFall::range()) { - std::string attr_name = rf->to_string_long() + "_capacitance"; + std::string attr_name = rf->to_string() + "_capacitance"; float cap; bool exists; port_group->findAttrFloat(attr_name, cap, exists); @@ -1490,7 +1490,7 @@ LibertyReader::readCapacitance(const LibertyPortSeq &ports, } // rise/fall_capacitance_range(min_cap, max_cap); - attr_name = rf->to_string_long() + "_capacitance_range"; + attr_name = rf->to_string() + "_capacitance_range"; const LibertyComplexAttrSeq &range_attrs = port_group->findComplexAttrs(attr_name); if (!range_attrs.empty()) { const LibertyComplexAttr *attr = range_attrs[0]; @@ -2081,7 +2081,7 @@ LibertyReader::makeLinearModels(LibertyCell *cell, { LibertyLibrary *library = cell->libertyLibrary(); for (const RiseFall *rf : RiseFall::range()) { - std::string intr_attr_name = "intrinsic_" + rf->to_string_long(); + std::string intr_attr_name = "intrinsic_" + rf->to_string(); float intr = 0.0; bool intr_exists; timing_group->findAttrFloat(intr_attr_name, intr, intr_exists); @@ -2094,7 +2094,7 @@ LibertyReader::makeLinearModels(LibertyCell *cell, if (timingTypeIsCheck(timing_attrs->timingType())) model = new CheckLinearModel(cell, intr); else { - std::string res_attr_name = rf->to_string_long() + "_resistance"; + std::string res_attr_name = rf->to_string() + "_resistance"; float res = 0.0; bool res_exists; timing_group->findAttrFloat(res_attr_name, res, res_exists); @@ -2117,18 +2117,18 @@ LibertyReader::makeTableModels(LibertyCell *cell, { bool found_model = false; for (const RiseFall *rf : RiseFall::range()) { - std::string delay_attr_name = "cell_" + rf->to_string_long(); + 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_long() + "_transition"; + 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_long(); + std::string delay_sigma_attr_name = "ocv_sigma_cell_" + rf->to_string(); TableModelsEarlyLate delay_sigmas = readEarlyLateTableModels(timing_group, delay_sigma_attr_name.c_str(), @@ -2136,7 +2136,7 @@ LibertyReader::makeTableModels(LibertyCell *cell, time_scale_, ScaleFactorType::unknown); - std::string slew_sigma_attr_name = "ocv_sigma_" + rf->to_string_long() + std::string slew_sigma_attr_name = "ocv_sigma_" + rf->to_string() + "_transition"; TableModelsEarlyLate slew_sigmas = readEarlyLateTableModels(timing_group, @@ -2164,7 +2164,7 @@ LibertyReader::makeTableModels(LibertyCell *cell, found_model = true; } else { - std::string constraint_attr_name = rf->to_string_long() + "_constraint"; + std::string constraint_attr_name = rf->to_string() + "_constraint"; ScaleFactorType scale_factor_type = timingTypeScaleFactorType(timing_attrs->timingType()); TableModel *constraint = readCheckTableModel(timing_group, @@ -2172,7 +2172,7 @@ LibertyReader::makeTableModels(LibertyCell *cell, rf, TableTemplateType::delay, time_scale_, scale_factor_type); if (constraint) { - std::string constraint_sigma_attr_name = "ocv_sigma_" + rf->to_string_long() + std::string constraint_sigma_attr_name = "ocv_sigma_" + rf->to_string() + "_constraint"; TableModelsEarlyLate constraint_sigmas = readEarlyLateTableModels(timing_group, @@ -2298,7 +2298,7 @@ LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, ReceiverModelPtr &receiver_model) { std::string cap_group_name1 = cap_group_name; - cap_group_name1 += "_" + rf->to_string_long(); + cap_group_name1 += "_" + rf->to_string(); const LibertyGroup *cap_group = timing_group->findSubgroup(cap_group_name1); if (cap_group) { const LibertySimpleAttr *segment_attr = cap_group->findSimpleAttr("segment"); @@ -2328,7 +2328,7 @@ OutputWaveforms * LibertyReader::readOutputWaveforms(const LibertyGroup *timing_group, const RiseFall *rf) { - const std::string current_group_name = "output_current_" + rf->to_string_long(); + const std::string current_group_name = "output_current_" + rf->to_string(); const LibertyGroup *current_group = timing_group->findSubgroup(current_group_name); if (current_group) { OutputWaveformSeq output_currents; @@ -2680,7 +2680,7 @@ LibertyReader::readInternalPowerGroups(LibertyCell *cell, InternalPowerModels models; // rise/fall_power group for (const RiseFall *rf : RiseFall::range()) { - std::string pwr_attr_name = rf->to_string_long() + "_power"; + std::string pwr_attr_name = rf->to_string() + "_power"; const LibertyGroup *pwr_group = ipwr_group->findSubgroup(pwr_attr_name); if (pwr_group) { TableModel *model = readTableModel(pwr_group, rf, TableTemplateType::power, diff --git a/parasitics/ReduceParasitics.cc b/parasitics/ReduceParasitics.cc index 311ef35c9..f44b1a9fc 100644 --- a/parasitics/ReduceParasitics.cc +++ b/parasitics/ReduceParasitics.cc @@ -311,7 +311,7 @@ reduceToPiElmore(const Parasitic *parasitic_network, if (drvr_node) { debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver %s %s %s", sta->network()->pathName(drvr_pin), - rf->to_string().c_str(), + rf->shortName(), min_max->to_string().c_str()); ReduceToPiElmore reducer(sta); return reducer.makePiElmore(parasitic_network, drvr_pin, drvr_node, diff --git a/sdc/Clock.cc b/sdc/Clock.cc index 3960f670a..1b39e5855 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -531,7 +531,7 @@ ClockEdge::ClockEdge(Clock *clock, const RiseFall *rf) : clock_(clock), rf_(rf), - name_(stringPrint("%s %s", clock_->name(), rf_->to_string().c_str())), + name_(stringPrint("%s %s", clock_->name(), rf_->shortName())), time_(0.0), index_(clock_->index() * RiseFall::index_count + rf_->index()) { diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 09cdf2046..9deb4c911 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -106,7 +106,7 @@ ClkSkews::reportClkSkew(ClkSkew &clk_skew, report_->reportLine("%7s source latency %s %s", time_unit->asString(src_latency, digits), sdc_network_->pathName(src_path->pin(this)), - src_path->transition(this)->to_string().c_str()); + 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)); @@ -116,7 +116,7 @@ ClkSkews::reportClkSkew(ClkSkew &clk_skew, report_->reportLine("%7s target latency %s %s", time_unit->asString(-tgt_latency, digits), sdc_network_->pathName(tgt_path->pin(this)), - tgt_path->transition(this)->to_string().c_str()); + 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)); @@ -315,10 +315,10 @@ ClkSkews::findClkSkew(Vertex *src_vertex, debugPrint(debug_, "clk_skew", 2, "%s %s %s -> %s %s %s crpr = %s skew = %s", network_->pathName(src_path->pin(this)), - src_path->transition(this)->to_string().c_str(), + src_path->transition(this)->shortName(), time_unit->asString(probe.srcLatency(this)), network_->pathName(tgt_path->pin(this)), - tgt_path->transition(this)->to_string().c_str(), + tgt_path->transition(this)->shortName(), time_unit->asString(probe.tgtLatency(this)), delayAsString(probe.crpr(this), this), time_unit->asString(probe.skew())); diff --git a/search/Genclks.cc b/search/Genclks.cc index bbb266213..225dfcf07 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -878,7 +878,7 @@ Genclks::recordSrcPaths(Clock *gclk) debugPrint(debug_, "genclk", 2, " %s insertion %s %s %s", network_->pathName(gclk_pin), early_late->to_string().c_str(), - rf->to_string().c_str(), + rf->shortName(), delayAsString(path->arrival(), this)); src_path = *path; } diff --git a/search/Path.cc b/search/Path.cc index 40d96bafe..5514d559d 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -201,7 +201,7 @@ Path::to_string(const StaState *sta) const else return stringPrintTmp("%s %s %s/%s %d", vertex(sta)->to_string(sta).c_str(), - transition(sta)->to_string().c_str(), + transition(sta)->shortName(), scene(sta)->name().c_str(), minMax(sta)->to_string().c_str(), tagIndex(sta)); diff --git a/search/PathEnum.cc b/search/PathEnum.cc index e53b08890..8cc8c9c92 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -426,7 +426,7 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, 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->to_string().c_str(), + to_rf->shortName(), delayAsString(search_->deratedDelay(from_vertex, arc, edge, false, from_path->minMax(this), from_path->dcalcAnalysisPtIndex(this), diff --git a/search/PathGroup.cc b/search/PathGroup.cc index ca842db5f..b1ef7a0f8 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -805,7 +805,7 @@ MakePathEndsAll::vertexEnd(Vertex *) debugPrint(debug, "path_group", 2, "insert %s %s %s %d", path_end->vertex(sta_)->to_string(sta_).c_str(), path_end->typeName(), - path_end->transition(sta_)->to_string().c_str(), + path_end->transition(sta_)->shortName(), path_end->path()->tag(sta_)->index()); // Give the group a copy of the path end because // it may delete it during pruning. @@ -820,7 +820,7 @@ MakePathEndsAll::vertexEnd(Vertex *) debugPrint(debug, "path_group", 3, "prune %s %s %s %d", path_end->vertex(sta_)->to_string(sta_).c_str(), path_end->typeName(), - path_end->transition(sta_)->to_string().c_str(), + path_end->transition(sta_)->shortName(), path_end->path()->tag(sta_)->index()); } // Clear ends for next vertex. diff --git a/search/Search.cc b/search/Search.cc index 25683114a..329561451 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1365,8 +1365,8 @@ ArrivalVisitor::visitFromToPath(const Pin * /* from_pin */, debugPrint(debug_, "search", 3, " %s", from_vertex->to_string(this).c_str()); debugPrint(debug_, "search", 3, " %s -> %s %s", - from_rf->to_string().c_str(), - to_rf->to_string().c_str(), + 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()); @@ -2919,7 +2919,7 @@ Search::reportArrivals(Vertex *vertex, prev_str += "NULL"; } report_->reportLine(" %s %s %s / %s %s%s", - rf->to_string().c_str(), + rf->shortName(), path->minMax(this)->to_string().c_str(), delayAsString(path->arrival(), this), req, @@ -3656,8 +3656,8 @@ RequiredVisitor::visitFromToPath(const Pin *, // Don't propagate required times through latch D->Q edges. if (edge->role() != TimingRole::latchDtoQ()) { debugPrint(debug_, "search", 3, " %s -> %s %s", - from_rf->to_string().c_str(), - to_rf->to_string().c_str(), + from_rf->shortName(), + to_rf->shortName(), min_max->to_string().c_str()); debugPrint(debug_, "search", 3, " from tag %2u: %s", from_tag->index(), diff --git a/search/Tag.cc b/search/Tag.cc index 5e368d809..ece8b14c7 100644 --- a/search/Tag.cc +++ b/search/Tag.cc @@ -104,8 +104,7 @@ Tag::to_string(bool report_index, if (report_rf_min_max) { const RiseFall *rf = transition(); const MinMax *min_max = minMax(); - result += rf->to_string(); - result += " "; + result += rf->shortName(); result += min_max->to_string(); result += " "; } diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index 1368cd512..bb033724e 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -215,10 +215,10 @@ 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)->to_string().c_str(), - network_->pathName(path_->pin(this)), - path_->transition(this)->to_string().c_str()); + 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); diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index a7a2d6ebb..270aad83b 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -497,7 +497,7 @@ using namespace sta; const RiseFall *rf = $1; const char *str = ""; if (rf) - str = rf->to_string().c_str(); + str = rf->shortName(); Tcl_SetResult(interp, const_cast(str), TCL_STATIC); } @@ -517,7 +517,7 @@ using namespace sta; RiseFallBoth *tr = $1; const char *str = ""; if (tr) - str = tr->asString(); + str = tr->shortName(); Tcl_SetResult(interp, const_cast(str), TCL_STATIC); } diff --git a/util/Transition.cc b/util/Transition.cc index 0e09243c2..41b286438 100644 --- a/util/Transition.cc +++ b/util/Transition.cc @@ -44,6 +44,15 @@ RiseFall::RiseFall(const char *name, { } +const std::string & +RiseFall::to_string(bool use_short) const +{ + if (use_short) + return short_name_; + else + return name_; +} + const RiseFall * RiseFall::opposite() const { @@ -134,6 +143,15 @@ RiseFallBoth::RiseFallBoth(const char *name, { } +const std::string & +RiseFallBoth::to_string(bool use_short) const +{ + if (use_short) + return short_name_; + else + return name_; +} + const RiseFallBoth * RiseFallBoth::find(const char *tr_str) { From 12c811a492e2835a3e6680240ad69c59d33b67c1 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 6 Mar 2026 18:10:20 -0700 Subject: [PATCH 049/181] latch en=1 tags Signed-off-by: James Cherry --- search/Latches.cc | 147 ++++++++++++++++++++++++++++------------------ search/Search.cc | 2 +- 2 files changed, 90 insertions(+), 59 deletions(-) diff --git a/search/Latches.cc b/search/Latches.cc index 3c113c51a..9dcd688aa 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -323,6 +323,10 @@ Latches::latchOutArrival(const Path *data_path, ArcDelay &arc_delay, Arrival &q_arrival) { + q_tag = nullptr; + arc_delay = 0.0; + q_arrival = 0.0; + Scene *scene = data_path->scene(this); Sdc *sdc = scene->sdc(); const Mode *mode = scene->mode(); @@ -337,83 +341,110 @@ Latches::latchOutArrival(const Path *data_path, // Latch enable may be missing if library is malformed. switch (state) { case LatchEnableState::closed: - // Latch is disabled by constant enable. + // Latch is always closed because enable is constant. break; case LatchEnableState::open: { + // Latch is always open because enable is constant. ExceptionPath *excpt = exceptionTo(data_path, nullptr); 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_tag = data_path->tag(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) + // Note that + // level(crprClkPath(data)) < level(D) + // The danger is that + // level(crprClkPath(data)) == level(Q) + // or some other downstream vertex. + // This can lead to data races when finding arrivals at the same level + // use multiple threads. + // Kill the crprClklPath to be safe. + const ClkInfo *data_clk_info = data_path->clkInfo(this); + const ClkInfo *q_clk_info = + search_->findClkInfo(scene, + data_clk_info->clkEdge(), + data_clk_info->clkSrc(), + data_clk_info->isPropagated(), + data_clk_info->genClkSrc(), + data_clk_info->isGenClkSrcPath(), + data_clk_info->pulseClkSense(), + data_clk_info->insertion(), + data_clk_info->latency(), + data_clk_info->uncertainties(), + min_max, nullptr); + q_tag = search_->findTag(scene, d_q_arc->toEdge()->asRiseFall(), + min_max, q_clk_info, false, + nullptr, false, data_path->tag(this)->states(), + false, nullptr); } - } break; + } case LatchEnableState::enabled: { const MinMax *tgt_min_max = data_path->tgtClkMinMax(this); VertexPathIterator enable_iter(enable_vertex, scene, tgt_min_max, enable_rf, this); while (enable_iter.hasNext()) { Path *enable_path = enable_iter.next(); - const ClkInfo *en_clk_info = enable_path->clkInfo(this); - const ClockEdge *en_clk_edge = en_clk_info->clkEdge(); - if (enable_path->isClock(this)) { - ExceptionPath *excpt = exceptionTo(data_path, en_clk_edge); - // D->Q is disabled when if there is a path delay -to D or EN clk. - if (!(excpt && (excpt->isFalse() - || excpt->isPathDelay()))) { - Path *disable_path = latchEnableOtherPath(enable_path); - Delay borrow, time_given_to_startpoint; - Arrival adjusted_data_arrival; - Required required; - latchRequired(data_path, enable_path, disable_path, - required, borrow, adjusted_data_arrival, - time_given_to_startpoint); - if (delayGreater(borrow, 0.0, this)) { - // Latch is transparent when data arrives. - arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, - false, min_max, dcalc_ap, sdc); - q_arrival = adjusted_data_arrival + arc_delay; - // 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; - const ClkInfo *q_clk_info = - search_->findClkInfo(en_clk_info->scene(), - en_clk_edge, - en_clk_info->clkSrc(), - en_clk_info->isPropagated(), - en_clk_info->genClkSrc(), - en_clk_info->isGenClkSrcPath(), - en_clk_info->pulseClkSense(), - en_clk_info->insertion(), - en_clk_info->latency(), - en_clk_info->uncertainties(), - min_max, crpr_clk_path); - const RiseFall *q_rf = d_q_arc->toEdge()->asRiseFall(); - ExceptionStateSet *states = nullptr; - // Latch data pin is a valid exception -from pin. - if (sdc->exceptionFromStates(data_path->pin(this), - data_path->transition(this), - nullptr, nullptr, // clk below - MinMax::max(), states) - // -from enable non-filter exceptions apply. - && sdc->exceptionFromStates(enable_vertex->pin(), - enable_rf, - en_clk_edge->clock(), - en_clk_edge->transition(), - MinMax::max(), false, states)) - q_tag = search_->findTag(enable_path->tag(this)->scene(), - q_rf, MinMax::max(), q_clk_info, false, - nullptr, false, states, true, nullptr); - } - return; - } - } + const ClkInfo *en_clk_info = enable_path->clkInfo(this); + const ClockEdge *en_clk_edge = en_clk_info->clkEdge(); + if (enable_path->isClock(this)) { + ExceptionPath *excpt = exceptionTo(data_path, en_clk_edge); + // D->Q is disabled when if there is a path delay -to D or EN clk. + if (!(excpt && (excpt->isFalse() + || excpt->isPathDelay()))) { + Path *disable_path = latchEnableOtherPath(enable_path); + Delay borrow, time_given_to_startpoint; + Arrival adjusted_data_arrival; + Required required; + latchRequired(data_path, enable_path, disable_path, + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); + if (delayGreater(borrow, 0.0, this)) { + // Latch is transparent when data arrives. + arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, + false, min_max, dcalc_ap, sdc); + q_arrival = adjusted_data_arrival + arc_delay; + // 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; + const ClkInfo *q_clk_info = + search_->findClkInfo(scene, + en_clk_edge, + en_clk_info->clkSrc(), + en_clk_info->isPropagated(), + en_clk_info->genClkSrc(), + en_clk_info->isGenClkSrcPath(), + en_clk_info->pulseClkSense(), + en_clk_info->insertion(), + en_clk_info->latency(), + en_clk_info->uncertainties(), + min_max, crpr_clk_path); + ExceptionStateSet *states = nullptr; + // Latch data pin is a valid exception -from pin. + if (sdc->exceptionFromStates(data_path->pin(this), + data_path->transition(this), + nullptr, nullptr, // clk below + MinMax::max(), states) + // -from enable non-filter exceptions apply. + && sdc->exceptionFromStates(enable_vertex->pin(), + enable_rf, + en_clk_edge->clock(), + en_clk_edge->transition(), + MinMax::max(), false, states)) + q_tag = search_->findTag(scene, d_q_arc->toEdge()->asRiseFall(), + MinMax::max(), q_clk_info, false, + nullptr, false, states, true, nullptr); + } + return; + } + } } // No enable path found. - } break; } + } } ExceptionPath * diff --git a/search/Search.cc b/search/Search.cc index 329561451..aca08dfba 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -3654,7 +3654,7 @@ RequiredVisitor::visitFromToPath(const Pin *, const MinMax *min_max) { // Don't propagate required times through latch D->Q edges. - if (edge->role() != TimingRole::latchDtoQ()) { + if (!edge->role()->isLatchDtoQ()) { debugPrint(debug_, "search", 3, " %s -> %s %s", from_rf->shortName(), to_rf->shortName(), From ad1cb85580c70ada13a4c02bfe87af4d08317288 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 7 Mar 2026 09:24:58 -0700 Subject: [PATCH 050/181] doc Signed-off-by: James Cherry --- doc/ChangeLog.txt | 2 +- doc/OpenSTA.fodt | 381 +++++++++++++++++++++++----------------------- doc/OpenSTA.pdf | Bin 1402478 -> 1424985 bytes 3 files changed, 192 insertions(+), 191 deletions(-) diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index ee123999f..13bdcc38b 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -3,7 +3,7 @@ OpenSTA Timing Analyzer Release Notes This file summarizes user visible changes for each release. -2025/02/24 +2026/02/24 ---------- The define_scene -library argument now takes a the library name or a diff --git a/doc/OpenSTA.fodt b/doc/OpenSTA.fodt index 344e45a6a..6195f2afe 100644 --- a/doc/OpenSTA.fodt +++ b/doc/OpenSTA.fodt @@ -1,11 +1,11 @@ - Parallax STA documentationJames Cherry5142025-03-17T12:59:52.4638705382010-07-31T21:07:002026-02-25T07:28:47.891834000P123DT1H12M13SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse + Parallax STA documentationJames Cherry5172025-03-17T12:59:52.4638705382010-07-31T21:07:002026-03-07T09:24:29.415873000P123DT1H20M36SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse - 348148 - 534 + 109 + 2667 19290 17736 true @@ -13,12 +13,12 @@ view2 - 8324 - 356965 - 534 - 348148 - 19823 - 365882 + 3041 + 4163 + 2667 + 109 + 21955 + 17844 0 1 false @@ -89,7 +89,7 @@ false true false - 26480554 + 26653592 0 false @@ -198,7 +198,7 @@ - + @@ -5282,6 +5282,7 @@ + @@ -6352,7 +6353,7 @@ - + @@ -6479,7 +6480,7 @@ Example Command Scripts1 Timing Analysis using SDF2 Timing Analysis with Multiple Process Corners2 - Timing Analysis with Multiple Modes3 + Timing Analysis with Multiple Corners and Modes3 Power Analysis3 TCL Interpreter5 Debugging Timing6 @@ -6490,31 +6491,31 @@ 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 + 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. @@ -6526,14 +6527,14 @@ 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 + 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. + 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. @@ -6559,13 +6560,13 @@ 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 + Commands - all_clocks + all_clocks @@ -6578,7 +6579,7 @@ - all_inputs + all_inputs [-no_clocks] @@ -6600,7 +6601,7 @@ - all_outputs + all_outputs @@ -6613,7 +6614,7 @@ - all_registers + all_registers [-clock clock_names][-cells | -data_pins | -clock_pins | -async_pins | ‑output_pins][-level_sensitive][-edge_triggered] @@ -6691,7 +6692,7 @@ - check_setup + check_setup [-verbose][-unconstrained_endpoints][-multiple_clock][-no_clock][-no_input_delay][-loops][-generated_clocks][> filename][>> filename] @@ -6760,7 +6761,7 @@ - connect_pin + connect_pin netport|pin @@ -6859,7 +6860,7 @@ - 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 @@ -6975,7 +6976,7 @@ - create_voltage_area + create_voltage_area [-name name][-coordinate coordinates][-guard_band_x guard_x][-guard_band_y guard_y]cells @@ -6988,7 +6989,7 @@ - current_design + current_design [design] @@ -7001,7 +7002,7 @@ - current_instance + current_instance [instance] @@ -7022,7 +7023,7 @@ - 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 @@ -7060,7 +7061,7 @@ - delete_clock + delete_clock [-all] clocks @@ -7081,7 +7082,7 @@ - delete_from_list + delete_from_list list objects @@ -7111,7 +7112,7 @@ - delete_generated_clock + delete_generated_clock [-all] clocks @@ -7132,7 +7133,7 @@ - delete_instance + delete_instance instance @@ -7153,7 +7154,7 @@ - delete_net + delete_net net @@ -7174,7 +7175,7 @@ - disconnect_pin + disconnect_pin netport | pin | -all @@ -7219,7 +7220,7 @@ - elapsed_run_time + elapsed_run_time @@ -7233,7 +7234,7 @@ - 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] @@ -7448,7 +7449,7 @@ - get_cells + get_cells [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -7525,7 +7526,7 @@ - get_clocks + get_clocks [-regexp][-nocase][-filter expr][-quiet]patterns @@ -7579,7 +7580,7 @@ - get_fanin + get_fanin -to sink_list[-flat][-only_cells][-startpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] @@ -7665,7 +7666,7 @@ - get_fanout + get_fanout -from source_list[-flat][-only_cells][-endpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] @@ -7750,7 +7751,7 @@ - get_full_name + get_full_name object @@ -7841,7 +7842,7 @@ - get_lib_pins + get_lib_pins [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns @@ -7911,7 +7912,7 @@ - get_libs + get_libs [-filter expr][-regexp][-nocase][-quiet]patterns @@ -7965,7 +7966,7 @@ - get_nets + get_nets [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -8042,7 +8043,7 @@ - get_name + get_name object @@ -8064,7 +8065,7 @@ - get_pins + get_pins [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -8135,7 +8136,7 @@ - get_ports + get_ports [-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -8197,7 +8198,7 @@ - get_property + get_property [-object_type object_type]objectproperty @@ -8258,7 +8259,7 @@ - get_scenes + get_scenes [-mode mode_name]scene_name @@ -8287,7 +8288,7 @@ - get_timing_edges + get_timing_edges [-from from_pins][-to to_pins][-of_objects objects][-filter expr][patterns] @@ -8333,7 +8334,7 @@ - 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] @@ -8450,7 +8451,7 @@ - include + include [-echo|-e][-verbose|-v]filename[> log_filename][>> log_filename] @@ -8504,7 +8505,7 @@ - link_design + link_design [-no_black_boxes][cell_name] @@ -8535,7 +8536,7 @@ - make_instance + make_instance inst_pathlib_cell @@ -8565,7 +8566,7 @@ - make_net + make_net net_name_list @@ -8586,7 +8587,7 @@ - read_liberty + read_liberty [-corner corner][-min][-max][-infer_latches]filename @@ -8635,7 +8636,7 @@ - read_saif + read_saif [-scope scope]filename @@ -8665,7 +8666,7 @@ - read_sdc + read_sdc [-mode mode_name][-echo]filename @@ -8705,7 +8706,7 @@ - read_sdf + read_sdf [-scene scene][-unescaped_dividers]filename @@ -8746,7 +8747,7 @@ - read_spef + read_spef [-name name][-keep_capacitive_coupling][-coupling_reduction_factor factor][-reduce][-path path]filename @@ -8793,7 +8794,7 @@ - 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 @@ -8807,7 +8808,7 @@ - read_vcd + read_vcd [-scope scope][-mode mode_name]filename @@ -8844,7 +8845,7 @@ - read_verilog + read_verilog filename @@ -8859,7 +8860,7 @@ - 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. + 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. @@ -8868,7 +8869,7 @@ - replace_cell + replace_cell instance_listreplacement_cell @@ -8898,7 +8899,7 @@ - replace_activity_annotation + replace_activity_annotation [-report_unannotated][-report_annotated] @@ -8927,7 +8928,7 @@ - report_annotated_check + report_annotated_check [-setup][-hold][-recovery][-removal][-nochange][-width][-period][-max_skew][-max_line lines][-report_annotated][-report_unannotated][-constant_arcs] @@ -9037,7 +9038,7 @@ - report_annotated_delay + report_annotated_delay [-cell][-net][-from_in_ports][-to_out_ports][-max_lines lines][-report_annotated][-report_unannotated][-constant_arcs] @@ -9115,7 +9116,7 @@ - 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] @@ -9420,7 +9421,7 @@ - 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] @@ -9578,7 +9579,7 @@ - report_clock_latency + report_clock_latency [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] @@ -9623,7 +9624,7 @@ - report_clock_min_period + report_clock_min_period [-clocks clocks][-scenes scenes][-include_port_paths] @@ -9653,7 +9654,7 @@ - report_clock_properties + report_clock_properties [clock_names] @@ -9674,7 +9675,7 @@ - report_clock_skew + report_clock_skew [-setup|-hold][-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] @@ -9735,7 +9736,7 @@ - report_dcalc + report_dcalc [-from from_pin][-to to_pin][-scene scene][-min][-max][-digits digits][> filename][>> filename] @@ -9797,7 +9798,7 @@ - report_disabled_edges + report_disabled_edges @@ -9811,7 +9812,7 @@ - report_edges + report_edges [-from from_pin][-to to_pin] @@ -9840,7 +9841,7 @@ - report_instance + report_instance instance_path[> filename][>> filename] @@ -9861,7 +9862,7 @@ - report_lib_cell + report_lib_cell cell_name[> filename][>> filename] @@ -9883,7 +9884,7 @@ - report_net + report_net [-digits digits]net_path[> filename][>> filename] @@ -9912,7 +9913,7 @@ - report_parasitic_annotation + report_parasitic_annotation [-report_unannotated][> filename][>> filename] @@ -9933,7 +9934,7 @@ - report_power + report_power [-instances instances][-highest_power_instances count][-digits digits][> filename][>> filename] @@ -9972,7 +9973,7 @@ - report_slews + report_slews [-scenes scenes]pin @@ -10001,7 +10002,7 @@ - report_tns + report_tns [-min][-max][-digits digits] @@ -10038,7 +10039,7 @@ - report_units + report_units @@ -10052,7 +10053,7 @@ - report_wns + report_wns [-min][-max][-digits digits] @@ -10089,7 +10090,7 @@ - report_worst_slack + report_worst_slack [-min][-max][-digits digits] @@ -10127,7 +10128,7 @@ - set_assigned_check + 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 @@ -10245,7 +10246,7 @@ - set_assigned_delay + set_assigned_delay -cell|-net[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins]delay @@ -10338,7 +10339,7 @@ - set_assigned_transition + set_assigned_transition [-rise][-fall][-scene scene][-min][-max]slewpin_list @@ -10408,7 +10409,7 @@ - set_case_analysis + set_case_analysis 0|1|zero|one|rise|rising|fall|fallingport_or_pin_list @@ -10430,7 +10431,7 @@ - set_clock_gating_check + set_clock_gating_check [-setup setup_time][-hold hold_time][-rise][-fall][-high][-low][objects] @@ -10504,7 +10505,7 @@ - set_clock_groups + set_clock_groups [-name name][-logically_exclusive][-physically_exclusive][-asynchronous][-allow_paths]-group clocks @@ -10565,7 +10566,7 @@ - set_clock_latency + set_clock_latency [-source][-clock clock][-rise][-fall][-min][-max]delayobjects @@ -10642,7 +10643,7 @@ - set_clock_transition + set_clock_transition [-rise][-fall][-min][-max]transitionclocks @@ -10704,7 +10705,7 @@ - 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] @@ -10786,7 +10787,7 @@ - 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] @@ -10859,7 +10860,7 @@ - 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 @@ -10920,7 +10921,7 @@ - set_disable_inferred_clock_gating + set_disable_inferred_clock_gating objects @@ -10941,7 +10942,7 @@ - set_disable_timing + set_disable_timing [-from from_port][-to to_port]objects @@ -10986,7 +10987,7 @@ - set_drive + set_drive [-rise][-fall][-max][-min]resistanceports @@ -11047,7 +11048,7 @@ - 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 @@ -11149,7 +11150,7 @@ - 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] @@ -11230,7 +11231,7 @@ - set_fanout_load + set_fanout_load fanoutport_list @@ -11243,7 +11244,7 @@ - set_hierarchy_separator + set_hierarchy_separator separator @@ -11264,7 +11265,7 @@ - set_ideal_latency + set_ideal_latency [-rise] [-fall] [-min] [-max] delay objects @@ -11277,7 +11278,7 @@ - set_ideal_network + set_ideal_network [-no_propagation] objects @@ -11290,7 +11291,7 @@ - set_ideal_transition + set_ideal_transition [-rise] [-fall] [-min] [-max] transition_time objects @@ -11303,7 +11304,7 @@ - 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 @@ -11420,7 +11421,7 @@ - set_input_transition + set_input_transition [-rise][-fall][-max][-min]transitionport_list @@ -11481,7 +11482,7 @@ - set_level_shifter_strategy + set_level_shifter_strategy [-rule rule_type] @@ -11494,7 +11495,7 @@ - set_level_shifter_threshold + set_level_shifter_threshold [-voltage voltage] @@ -11507,7 +11508,7 @@ - set_load + set_load [-rise][-fall][-max][-min][-subtract_pin_load][-pin_load][-wire_load]capacitanceobjects @@ -11596,7 +11597,7 @@ - set_logic_dc + set_logic_dc port_list @@ -11617,7 +11618,7 @@ - set_logic_one + set_logic_one port_list @@ -11639,7 +11640,7 @@ - set_logic_zero + set_logic_zero port_list @@ -11660,7 +11661,7 @@ - set_max_area + set_max_area area @@ -11681,7 +11682,7 @@ - set_max_capacitance + set_max_capacitance capacitanceobjects @@ -11710,7 +11711,7 @@ - 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 @@ -11797,7 +11798,7 @@ - set_max_dynamic_power + set_max_dynamic_power power [unit] @@ -11810,7 +11811,7 @@ - set_max_fanout + set_max_fanout fanoutobjects @@ -11839,7 +11840,7 @@ - set_max_leakage_power + set_max_leakage_power power [unit] @@ -11852,7 +11853,7 @@ - set_max_time_borrow + set_max_time_borrow delayobjects @@ -11882,7 +11883,7 @@ - set_max_transition + set_max_transition [-data_path][-clock_path][-rise][-fall]transitionobjects @@ -11945,7 +11946,7 @@ - set_min_capacitance + set_min_capacitance capacitanceobjects @@ -11975,7 +11976,7 @@ - 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 @@ -12062,7 +12063,7 @@ - set_min_pulse_width + set_min_pulse_width [-high][-low]min_widthobjects @@ -12107,7 +12108,7 @@ - set_mode + set_mode mode_name @@ -12120,7 +12121,7 @@ - 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 @@ -12222,7 +12223,7 @@ - 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] @@ -12308,7 +12309,7 @@ - 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 @@ -12402,7 +12403,7 @@ - set_port_fanout_number + set_port_fanout_number [-min][-max]fanoutports @@ -12447,7 +12448,7 @@ - set_power_activity + set_power_activity [-global][-input][-input_ports ports][-pins pins][-activity activity | -density density][-duty duty][-clock clock] @@ -12527,7 +12528,7 @@ - set_propagated_clock + set_propagated_clock objects @@ -12548,7 +12549,7 @@ - set_pvt + set_pvt [-min][-max][-process process][-voltage voltage] @@ -12611,7 +12612,7 @@ - set_sense + set_sense [-type clock|data][-positive][-negative][-pulse pulse_type][-stop_propagation][-clock clocks]pins @@ -12689,7 +12690,7 @@ - set_timing_derate + set_timing_derate [-rise][-fall][-early][-late][-clock][-data][-net_delay][-cell_delay][-cell_check]derate[objects] @@ -12791,7 +12792,7 @@ - set_resistance + set_resistance [-max][-min]resistancenets @@ -12837,7 +12838,7 @@ - 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] @@ -12902,7 +12903,7 @@ - set_wire_load_min_block_size + set_wire_load_min_block_size size @@ -12915,7 +12916,7 @@ - set_wire_load_mode + set_wire_load_mode top|enclosed|segmented @@ -12952,7 +12953,7 @@ - set_wire_load_model + set_wire_load_model -name model_name[-library library][-max][-min][objects] @@ -13005,7 +13006,7 @@ - set_wire_load_selection_group + set_wire_load_selection_group [-library library][-max][-min]group_name[objects] @@ -13059,7 +13060,7 @@ - suppress_msg + suppress_msg msg_ids @@ -13080,7 +13081,7 @@ - unset_case_analysis + unset_case_analysis port_or_pin_list @@ -13101,7 +13102,7 @@ - unset_clock_latency + unset_clock_latency [-source]objects @@ -13130,7 +13131,7 @@ - unset_clock_transition + unset_clock_transition clocks @@ -13152,7 +13153,7 @@ - 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] @@ -13229,7 +13230,7 @@ - unset_data_check + unset_data_check [-from|-rise_from|-fall_from from_object][-to|-rise_to|-fall_to to_object][-setup][-hold][-clock clock] @@ -13282,7 +13283,7 @@ - unset_disable_inferred_clock_gating + unset_disable_inferred_clock_gating objects @@ -13303,7 +13304,7 @@ - unset_disable_timing + unset_disable_timing [-from from_port][-to to_port]objects @@ -13340,7 +13341,7 @@ - unset_input_delay + unset_input_delay [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list @@ -13410,7 +13411,7 @@ - unset_output_delay + unset_output_delay [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list @@ -13479,7 +13480,7 @@ - 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] @@ -13550,7 +13551,7 @@ - unset_power_activity + unset_power_activity [-global][-input][-input_ports ports][-pins pins] @@ -13603,7 +13604,7 @@ - unset_propagated_clock + unset_propagated_clock objects @@ -13624,7 +13625,7 @@ - unset_timing_derate + unset_timing_derate @@ -13638,7 +13639,7 @@ - unsuppress_msg + unsuppress_msg msg_ids @@ -13659,7 +13660,7 @@ - user_run_time + user_run_time @@ -13672,7 +13673,7 @@ - with_output_to_variable + with_output_to_variable var { commands } @@ -13701,7 +13702,7 @@ - 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] @@ -13775,7 +13776,7 @@ - write_sdc + write_sdc [-digits digits][-gzip][-no_timestamp]filename @@ -13820,7 +13821,7 @@ - write_sdf + write_sdf [-scene scene][-divider /|.][-include_typ][-digits digits][-gzip][-no_timestamp][-no_version]filename @@ -13898,7 +13899,7 @@ - write_timing_model + write_timing_model [-library_name lib_name][-cell_name cell_name] @@ -13951,7 +13952,7 @@ - write_verilog + write_verilog [-include_pwr_gnd][-remove_cells lib_cells]filename @@ -13983,7 +13984,7 @@ 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 + 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. @@ -14046,13 +14047,13 @@ 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 + Variables - hierarchy_separator + hierarchy_separator Any character. @@ -14065,7 +14066,7 @@ - sta_continue_on_error + sta_continue_on_error 0|1 @@ -14078,7 +14079,7 @@ - sta_crpr_mode + sta_crpr_mode same_pin|same_transition @@ -14091,7 +14092,7 @@ - sta_cond_default_arcs_enabled + sta_cond_default_arcs_enabled 0|1 @@ -14104,7 +14105,7 @@ - sta_crpr_enabled + sta_crpr_enabled 0|1 @@ -14117,7 +14118,7 @@ - sta_dynamic_loop_breaking + sta_dynamic_loop_breaking 0|1 @@ -14130,7 +14131,7 @@ - sta_gated_clock_checks_enabled + sta_gated_clock_checks_enabled 0|1 @@ -14143,7 +14144,7 @@ - sta_input_port_default_clock + sta_input_port_default_clock 0|1 @@ -14156,7 +14157,7 @@ - sta_internal_bidirect_instance_paths_enabled + sta_internal_bidirect_instance_paths_enabled 0|1 @@ -14169,7 +14170,7 @@ - sta_pocv_enabled + sta_pocv_enabled 0|1 @@ -14182,7 +14183,7 @@ - sta_propagate_all_clocks + sta_propagate_all_clocks 0|1 @@ -14197,7 +14198,7 @@ - sta_propagate_gated_clock_enable + sta_propagate_gated_clock_enable 0|1 @@ -14210,7 +14211,7 @@ - sta_recovery_removal_checks_enabled + sta_recovery_removal_checks_enabled 0|1 @@ -14223,7 +14224,7 @@ - sta_report_default_digits + sta_report_default_digits integer @@ -14237,7 +14238,7 @@ - sta_preset_clear_arcs_enabled + sta_preset_clear_arcs_enabled 0|1 @@ -14287,7 +14288,7 @@ define_scene13 delete_clock13 delete_from_list13 - delete_generated_clock13 + delete_generated_clock14 delete_instance14 delete_net14 disconnect_pin14 @@ -14421,7 +14422,7 @@ suppress_msg76 TCL Interpreter5 Timing Analysis using SDF2 - Timing Analysis with Multiple Modes3 + Timing Analysis with Multiple Corners and Modes3 Timing Analysis with Multiple Process Corners2 unset_case_analysis76 unset_clock_latency76 @@ -14449,7 +14450,7 @@ - Version 2.6.0, Sep 23, 2024Copyright (c) 2024, 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 c084d80861e41deab524afab6145837c92811e4d..b450630c9554130765517b9634485e150dda314a 100644 GIT binary patch delta 365887 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

QG}eDmOSn**UT5_&~IAjlv9ehBbh~ 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 SM1z$FkG}P-?|$ENkN!XKlx=PR From bbe31b03de992abe45ea3d75517c130a6553cc6c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 7 Mar 2026 10:58:09 -0700 Subject: [PATCH 051/181] comment Signed-off-by: James Cherry --- search/Sta.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/search/Sta.cc b/search/Sta.cc index 527e8ee1d..0cf5b206d 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2214,8 +2214,8 @@ Sta::checkTiming(const Mode *mode, bool generated_clks) { if (unconstrained_endpoints) { - // Only arrivals to find unconstrained_endpoints. - searchPreamble(); + // Only need non-clock arrivals to find unconstrained_endpoints. + searchPreamble(); search_->findAllArrivals(); } else { From 60f6e5963fc5a3fc7ed87f3c6927540fa3807415 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 7 Mar 2026 12:21:31 -0700 Subject: [PATCH 052/181] rm PwrActivityOrigin::defaulted Signed-off-by: James Cherry --- include/sta/PowerClass.hh | 1 - power/Power.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/include/sta/PowerClass.hh b/include/sta/PowerClass.hh index 5b1d36beb..bedb018b6 100644 --- a/include/sta/PowerClass.hh +++ b/include/sta/PowerClass.hh @@ -41,7 +41,6 @@ enum class PwrActivityOrigin propagated, clock, constant, - defaulted, unknown }; diff --git a/power/Power.cc b/power/Power.cc index f0b590fa5..38d1dea17 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -86,7 +86,6 @@ static EnumNameMap pwr_activity_origin_map = {PwrActivityOrigin::propagated, "propagated"}, {PwrActivityOrigin::clock, "clock"}, {PwrActivityOrigin::constant, "constant"}, - {PwrActivityOrigin::defaulted, "defaulted"}, {PwrActivityOrigin::unknown, "unknown"}}; Power::Power(StaState *sta) : From b5b0c66d12b4a8994740dbd61649712dc3259ce3 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 7 Mar 2026 12:37:42 -0700 Subject: [PATCH 053/181] power activity doc resolves #192 Signed-off-by: James Cherry --- doc/OpenSTA.fodt | 3044 +++++++++++++++++++++++----------------------- doc/OpenSTA.pdf | Bin 1424985 -> 1403976 bytes 2 files changed, 1533 insertions(+), 1511 deletions(-) diff --git a/doc/OpenSTA.fodt b/doc/OpenSTA.fodt index 6195f2afe..5deadce7e 100644 --- a/doc/OpenSTA.fodt +++ b/doc/OpenSTA.fodt @@ -1,11 +1,11 @@ - Parallax STA documentationJames Cherry5172025-03-17T12:59:52.4638705382010-07-31T21:07:002026-03-07T09:24:29.415873000P123DT1H20M36SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse + Parallax STA documentationJames Cherry5182025-03-17T12:59:52.4638705382010-07-31T21:07:002026-03-07T12:37:15.449188000P123DT1H22M3SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse - 109 - 2667 + 748993 + 0 19290 17736 true @@ -13,12 +13,12 @@ view2 - 3041 - 4163 - 2667 - 109 - 21955 - 17844 + 6375 + 755996 + 0 + 748993 + 19288 + 766727 0 1 false @@ -89,7 +89,7 @@ false true false - 26653592 + 26836501 0 false @@ -198,7 +198,7 @@ - + @@ -4399,13 +4399,15 @@ - + + - + + @@ -4414,873 +4416,889 @@ - + + - - + + - + - + + + + + + + + + + + + + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -5576,786 +5594,789 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -6491,31 +6512,31 @@ 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 + 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. @@ -6527,14 +6548,14 @@ 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 + 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. + 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. @@ -6560,13 +6581,13 @@ 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 + Commands - all_clocks + all_clocks @@ -6579,7 +6600,7 @@ - all_inputs + all_inputs [-no_clocks] @@ -6601,7 +6622,7 @@ - all_outputs + all_outputs @@ -6614,7 +6635,7 @@ - all_registers + all_registers [-clock clock_names][-cells | -data_pins | -clock_pins | -async_pins | ‑output_pins][-level_sensitive][-edge_triggered] @@ -6692,7 +6713,7 @@ - check_setup + check_setup [-verbose][-unconstrained_endpoints][-multiple_clock][-no_clock][-no_input_delay][-loops][-generated_clocks][> filename][>> filename] @@ -6761,7 +6782,7 @@ - connect_pin + connect_pin netport|pin @@ -6860,7 +6881,7 @@ - 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 @@ -6976,7 +6997,7 @@ - create_voltage_area + create_voltage_area [-name name][-coordinate coordinates][-guard_band_x guard_x][-guard_band_y guard_y]cells @@ -6989,7 +7010,7 @@ - current_design + current_design [design] @@ -7002,7 +7023,7 @@ - current_instance + current_instance [instance] @@ -7023,7 +7044,7 @@ - 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 @@ -7061,7 +7082,7 @@ - delete_clock + delete_clock [-all] clocks @@ -7082,7 +7103,7 @@ - delete_from_list + delete_from_list list objects @@ -7112,7 +7133,7 @@ - delete_generated_clock + delete_generated_clock [-all] clocks @@ -7133,7 +7154,7 @@ - delete_instance + delete_instance instance @@ -7154,7 +7175,7 @@ - delete_net + delete_net net @@ -7175,7 +7196,7 @@ - disconnect_pin + disconnect_pin netport | pin | -all @@ -7220,7 +7241,7 @@ - elapsed_run_time + elapsed_run_time @@ -7234,7 +7255,7 @@ - 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] @@ -7449,7 +7470,7 @@ - get_cells + get_cells [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -7526,7 +7547,7 @@ - get_clocks + get_clocks [-regexp][-nocase][-filter expr][-quiet]patterns @@ -7580,7 +7601,7 @@ - get_fanin + get_fanin -to sink_list[-flat][-only_cells][-startpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] @@ -7666,7 +7687,7 @@ - get_fanout + get_fanout -from source_list[-flat][-only_cells][-endpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] @@ -7751,7 +7772,7 @@ - get_full_name + get_full_name object @@ -7842,7 +7863,7 @@ - get_lib_pins + get_lib_pins [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns @@ -7912,7 +7933,7 @@ - get_libs + get_libs [-filter expr][-regexp][-nocase][-quiet]patterns @@ -7966,7 +7987,7 @@ - get_nets + get_nets [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -8043,7 +8064,7 @@ - get_name + get_name object @@ -8065,7 +8086,7 @@ - get_pins + get_pins [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -8136,7 +8157,7 @@ - get_ports + get_ports [-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -8198,7 +8219,7 @@ - get_property + get_property [-object_type object_type]objectproperty @@ -8230,65 +8251,66 @@ The properties for different objects types are shown below. - cell (SDC lib_cell) + cell (SDC lib_cell) base_namefilenamefull_namelibraryname clock - full_nameis_generatedis_propagatedis_virtualnameperiodsources + 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 + 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) + path (PathEnd) endpointendpoint_clockendpoint_clock_pinslackstartpointstartpoint_clockpoints pin - activity (activity in transitions per second, duty cycle, origin)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 + 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 + 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] @@ -8296,53 +8318,53 @@ - -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. - 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. @@ -8350,7 +8372,7 @@ - -weight weight + -weight weight Not supported. @@ -8358,7 +8380,7 @@ - -critical_range range + -critical_range range Not supported. @@ -8393,7 +8415,7 @@ -through through_list - Group paths through a list of instances, pins or nets. + Group paths through a list of instances, pins or nets. @@ -8401,7 +8423,7 @@ -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. @@ -8409,7 +8431,7 @@ -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. @@ -8417,7 +8439,7 @@ -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. @@ -8425,7 +8447,7 @@ -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. @@ -8433,15 +8455,15 @@ -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. @@ -8451,84 +8473,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. @@ -8536,7 +8558,7 @@ - make_instance + make_instance inst_pathlib_cell @@ -8566,7 +8588,7 @@ - make_net + make_net net_name_list @@ -8587,18 +8609,18 @@ - 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. @@ -8622,12 +8644,12 @@ 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. @@ -8636,48 +8658,48 @@ - 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. @@ -8697,8 +8719,8 @@ - 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. @@ -8706,15 +8728,15 @@ - read_sdf + read_sdf - [-scene scene][-unescaped_dividers]filename + [-scene scene][-unescaped_dividers]filename - scene + scene Scene delays to annotate. @@ -8737,7 +8759,7 @@ - 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. @@ -8747,18 +8769,18 @@ - 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. @@ -8766,12 +8788,12 @@ 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. @@ -8779,10 +8801,10 @@ - ‑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. @@ -8794,58 +8816,58 @@ - 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 @@ -8860,8 +8882,8 @@ - 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. @@ -8869,7 +8891,7 @@ - replace_cell + replace_cell instance_listreplacement_cell @@ -8893,45 +8915,45 @@ - 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] @@ -9001,26 +9023,26 @@ - -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. @@ -9032,16 +9054,16 @@ - 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] @@ -9062,7 +9084,7 @@ - -from_in_ports + -from_in_ports Report annotated delays from input ports. @@ -9070,7 +9092,7 @@ - -to_out_ports + -to_out_ports Report annotated delays to output ports. @@ -9078,26 +9100,26 @@ - -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. @@ -9109,335 +9131,335 @@ - 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 - 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][-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 scens. The default value is all scenes. - -violators + -violators Report all violated timing and design rule constraints. @@ -9445,7 +9467,7 @@ - -verbose + -verbose Use a verbose output format. @@ -9453,18 +9475,18 @@ - -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. @@ -9558,10 +9580,10 @@ - -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. @@ -9569,7 +9591,7 @@ -no_split_lines - Do not split long lines into multiple lines. + Do not split long lines into multiple lines. @@ -9579,60 +9601,60 @@ - 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. @@ -9647,14 +9669,14 @@ - 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] @@ -9675,10 +9697,10 @@ - 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] @@ -9699,55 +9721,55 @@ - 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. - 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. @@ -9755,40 +9777,40 @@ to_pin - Report delay calculations for timing arcs to instance output pin to_pin. + Report delay calculations for timing arcs to instance output pin to_pin. - scene + scene - Report paths for process scene. The -scene keyword is required if more than one process corner is defined. + Report paths for process scene. The -scene keyword is required if more than one process corner is defined. - -min + -min - Report delay calculation for min delays. + Report delay calculation for min delays. - -max + -max - Report delay calculation for max delays. + Report delay calculation for max delays. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default is sta_report_default_digits. + The number of digits after the decimal point to report. The default is sta_report_default_digits. @@ -9798,7 +9820,7 @@ - report_disabled_edges + report_disabled_edges @@ -9806,42 +9828,42 @@ 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] - -from from_pin + -from from_pin - Report edges/timing arcs from pin from_pin. + Report edges/timing arcs from pin from_pin. - -to to_pin + -to to_pin - Report edges/timing arcs to pin to_pin. + Report edges/timing arcs to pin to_pin. - 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] @@ -9862,7 +9884,7 @@ - report_lib_cell + report_lib_cell cell_name[> filename][>> filename] @@ -9884,7 +9906,7 @@ - report_net + report_net [-digits digits]net_path[> filename][>> filename] @@ -9892,10 +9914,10 @@ - -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. @@ -9907,190 +9929,190 @@ - 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 + -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. + 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. - -highest_power_instances count + -highest_power_instances count - Report the power for the count highest power instances. + Report the power for the count highest power instances. - -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. - 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]pin - scenes + scenes - Report slews for process for scenes process corners.. + Report slews for process for scenes process corners.. - pin + 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 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] @@ -10114,10 +10136,10 @@ - -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. @@ -10128,10 +10150,10 @@ - set_assigned_check + 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|-hold|-recovery|-removal[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins][-clock rise|fall][-cond sdf_cond][-worst]margin @@ -10139,7 +10161,7 @@ -setup - Annotate setup timing checks. + Annotate setup timing checks. @@ -10147,7 +10169,7 @@ -hold - Annotate hold timing checks. + Annotate hold timing checks. @@ -10155,7 +10177,7 @@ -recovery - Annotate recovery timing checks. + Annotate recovery timing checks. @@ -10163,7 +10185,7 @@ -removal - Annotate removal timing checks. + Annotate removal timing checks. @@ -10184,10 +10206,10 @@ - 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. @@ -10224,10 +10246,10 @@ - -clock rise|fall + -clock rise|fall - The timing check clock pin transition. + The timing check clock pin transition. @@ -10235,7 +10257,7 @@ margin - The timing check margin. + The timing check margin. @@ -10246,10 +10268,10 @@ - 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 @@ -10286,10 +10308,10 @@ - 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. @@ -10339,10 +10361,10 @@ - set_assigned_transition + set_assigned_transition - [-rise][-fall][-scene scene][-min][-max]slewpin_list + [-rise][-fall][-scene scene][-min][-max]slewpin_list @@ -10364,10 +10386,10 @@ - scene + scene - Annotate delays for scene. + Annotate delays for scene. @@ -10409,7 +10431,7 @@ - set_case_analysis + set_case_analysis 0|1|zero|one|rise|rising|fall|fallingport_or_pin_list @@ -10425,13 +10447,13 @@ 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] @@ -10439,7 +10461,7 @@ - -setup setup_time + -setup setup_time Clock enable setup margin. @@ -10447,7 +10469,7 @@ - -hold hold_time + -hold hold_time Clock enable hold margin. @@ -10505,7 +10527,7 @@ - set_clock_groups + set_clock_groups [-name name][-logically_exclusive][-physically_exclusive][-asynchronous][-allow_paths]-group clocks @@ -10513,7 +10535,7 @@ - -name name + -name name The clock group name. @@ -10566,7 +10588,7 @@ - set_clock_latency + set_clock_latency [-source][-clock clock][-rise][-fall][-min][-max]delayobjects @@ -10574,7 +10596,7 @@ - -source + -source The latency is at the clock source. @@ -10582,7 +10604,7 @@ - -clock clock + -clock clock If multiple clocks are defined at a pin this use this option to specify the latency for a specific clock. @@ -10643,7 +10665,7 @@ - set_clock_transition + set_clock_transition [-rise][-fall][-min][-max]transitionclocks @@ -10654,7 +10676,7 @@ -rise - Set the transition time for the rising edge of the clock. + Set the transition time for the rising edge of the clock. @@ -10662,7 +10684,7 @@ -fall - Set the transition time for the falling edge of the clock. + Set the transition time for the falling edge of the clock. @@ -10671,7 +10693,7 @@ -min - Set the min transition time. + Set the min transition time. @@ -10679,7 +10701,7 @@ -max - Set the min transition time. + Set the min transition time. @@ -10705,7 +10727,7 @@ - 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] @@ -10713,18 +10735,18 @@ - -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. @@ -10732,7 +10754,7 @@ -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. @@ -10740,7 +10762,7 @@ -fall - Inter-clock target clock rise edge, alternative to ‑fall_to. + Inter-clock target clock rise edge, alternative to ‑fall_to. @@ -10748,7 +10770,7 @@ -setup - uncertainty is for setup checks. + uncertainty is for setup checks. @@ -10756,12 +10778,12 @@ -hold - uncertainty is for hold checks. + uncertainty is for hold checks. - uncertainty + uncertainty Clock uncertainty. @@ -10776,81 +10798,81 @@ - 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 An example of the set_units command is shown below. @@ -10860,15 +10882,15 @@ - 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. @@ -10876,7 +10898,7 @@ - -to to_pin + -to to_pin A pin that the setup/hold check is applied to. @@ -10900,7 +10922,7 @@ - -clock clock + -clock clock The setup/hold check clock. @@ -10921,7 +10943,7 @@ - set_disable_inferred_clock_gating + set_disable_inferred_clock_gating objects @@ -10936,13 +10958,13 @@ - 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 @@ -10950,18 +10972,18 @@ - -from from_port + -from from_port - + - -to to_port + -to to_port - + @@ -10969,7 +10991,7 @@ 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. @@ -10987,10 +11009,10 @@ - set_drive + set_drive - [-rise][-fall][-max][-min]resistanceports + [-rise][-fall][-max][-min]resistanceports @@ -10998,7 +11020,7 @@ -rise - Set the drive rise resistance. + Set the drive rise resistance. @@ -11006,7 +11028,7 @@ -fall - Set the drive fall resistance. + Set the drive fall resistance. @@ -11014,7 +11036,7 @@ -max - Set the maximum resistance. + Set the maximum resistance. @@ -11022,7 +11044,7 @@ -min - Set the minimum resistance. + Set the minimum resistance. @@ -11035,7 +11057,7 @@ - ports + ports A list of ports. @@ -11048,26 +11070,26 @@ - 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. @@ -11075,7 +11097,7 @@ -rise - Set the driving cell for a rising edge. + Set the driving cell for a rising edge. @@ -11083,7 +11105,7 @@ -fall - Set the driving cell for a falling edge. + Set the driving cell for a falling edge. @@ -11091,7 +11113,7 @@ -max - Set the driving cell for max delays. + Set the driving cell for max delays. @@ -11099,12 +11121,12 @@ -min - Set the driving cell for min delays. + Set the driving cell for min delays. - -pin pin + -pin pin The output port of the driving cell. @@ -11112,15 +11134,15 @@ - -from_pin from_pin + -from_pin from_pin - Use timing arcs from from_pin to the output pin. + Use timing arcs from from_pin to the output pin. - -input_transition_rise trans_rise + -input_transition_rise trans_rise The transition time for a rising input at from_pin. @@ -11128,7 +11150,7 @@ - -input_transition_fall trans_fall + -input_transition_fall trans_fall The transition time for a falling input at from_pin. @@ -11136,7 +11158,7 @@ - ports + ports A list of ports. @@ -11150,7 +11172,7 @@ - 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] @@ -11161,7 +11183,7 @@ -setup - Apply to setup checks. + Apply to setup checks. @@ -11169,7 +11191,7 @@ -hold - Apply to hold checks. + Apply to hold checks. @@ -11177,7 +11199,7 @@ -rise - Apply to rising path edges. + Apply to rising path edges. @@ -11185,7 +11207,7 @@ -fall - Apply to falling path edges. + Apply to falling path edges. @@ -11198,7 +11220,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11206,7 +11228,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11214,7 +11236,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11231,7 +11253,7 @@ - set_fanout_load + set_fanout_load fanoutport_list @@ -11244,7 +11266,7 @@ - set_hierarchy_separator + set_hierarchy_separator separator @@ -11265,7 +11287,7 @@ - set_ideal_latency + set_ideal_latency [-rise] [-fall] [-min] [-max] delay objects @@ -11278,7 +11300,7 @@ - set_ideal_network + set_ideal_network [-no_propagation] objects @@ -11291,7 +11313,7 @@ - set_ideal_transition + set_ideal_transition [-rise] [-fall] [-min] [-max] transition_time objects @@ -11304,7 +11326,7 @@ - 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 @@ -11315,7 +11337,7 @@ -rise - Set the arrival time for the rising edge of the input. + Set the arrival time for the rising edge of the input. @@ -11323,7 +11345,7 @@ -fall - Set the arrival time for the falling edge of the input. + Set the arrival time for the falling edge of the input. @@ -11332,7 +11354,7 @@ -max - Set the maximum arrival time. + Set the maximum arrival time. @@ -11340,12 +11362,12 @@ -min - Set the minimum arrival time. + Set the minimum arrival time. - -clock clock + -clock clock The arrival time is from clock. @@ -11361,7 +11383,7 @@ - -reference_pin ref_pin + -reference_pin ref_pin The arrival time is with respect to the clock that arrives at ref_pin. @@ -11372,7 +11394,7 @@ -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. @@ -11380,7 +11402,7 @@ -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. @@ -11408,20 +11430,20 @@ - 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 @@ -11432,7 +11454,7 @@ -rise - Set the rising edge transition. + Set the rising edge transition. @@ -11440,7 +11462,7 @@ -fall - Set the falling edge transition. + Set the falling edge transition. @@ -11448,7 +11470,7 @@ -max - Set the minimum transition time. + Set the minimum transition time. @@ -11456,7 +11478,7 @@ -min - Set the maximum transition time. + Set the maximum transition time. @@ -11482,7 +11504,7 @@ - set_level_shifter_strategy + set_level_shifter_strategy [-rule rule_type] @@ -11495,7 +11517,7 @@ - set_level_shifter_threshold + set_level_shifter_threshold [-voltage voltage] @@ -11508,7 +11530,7 @@ - set_load + set_load [-rise][-fall][-max][-min][-subtract_pin_load][-pin_load][-wire_load]capacitanceobjects @@ -11519,7 +11541,7 @@ -rise - Set the external port rising capacitance (ports only). + Set the external port rising capacitance (ports only). @@ -11527,7 +11549,7 @@ -fall - Set the external port falling capacitance (ports only). + Set the external port falling capacitance (ports only). @@ -11536,7 +11558,7 @@ -max - Set the max capacitance. + Set the max capacitance. @@ -11544,7 +11566,7 @@ -min - Set the min capacitance. + Set the min capacitance. @@ -11552,7 +11574,7 @@ -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. @@ -11588,16 +11610,16 @@ - 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 @@ -11618,7 +11640,7 @@ - set_logic_one + set_logic_one port_list @@ -11633,14 +11655,14 @@ - 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 @@ -11655,13 +11677,13 @@ - 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 @@ -11682,7 +11704,7 @@ - set_max_capacitance + set_max_capacitance capacitanceobjects @@ -11711,10 +11733,10 @@ - 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 @@ -11722,7 +11744,7 @@ -rise - Set max delay for rising paths. + Set max delay for rising paths. @@ -11730,13 +11752,13 @@ -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. @@ -11744,7 +11766,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11752,7 +11774,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11768,10 +11790,10 @@ - -probe + -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). @@ -11798,7 +11820,7 @@ - set_max_dynamic_power + set_max_dynamic_power power [unit] @@ -11811,7 +11833,7 @@ - set_max_fanout + set_max_fanout fanoutobjects @@ -11840,7 +11862,7 @@ - set_max_leakage_power + set_max_leakage_power power [unit] @@ -11853,7 +11875,7 @@ - set_max_time_borrow + set_max_time_borrow delayobjects @@ -11877,13 +11899,13 @@ - 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 @@ -11891,34 +11913,34 @@ - -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. @@ -11926,7 +11948,7 @@ transition - The maximum slew/transition time. + The maximum slew/transition time. @@ -11938,7 +11960,7 @@ - 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. @@ -11946,7 +11968,7 @@ - set_min_capacitance + set_min_capacitance capacitanceobjects @@ -11957,7 +11979,7 @@ capacitance - Minimum capacitance. + Minimum capacitance. @@ -11976,10 +11998,10 @@ - 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 @@ -11987,7 +12009,7 @@ -rise - Set min delay for rising paths. + Set min delay for rising paths. @@ -11995,12 +12017,12 @@ -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. @@ -12008,7 +12030,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12016,7 +12038,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12032,10 +12054,10 @@ - -probe + -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). @@ -12051,7 +12073,7 @@ delay - The minimum delay. + The minimum delay. @@ -12063,7 +12085,7 @@ - set_min_pulse_width + set_min_pulse_width [-high][-low]min_widthobjects @@ -12108,20 +12130,20 @@ - 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 @@ -12132,7 +12154,7 @@ -setup - Set cycle count for setup checks. + Set cycle count for setup checks. @@ -12140,7 +12162,7 @@ -hold - Set cycle count for hold checks. + Set cycle count for hold checks. @@ -12148,7 +12170,7 @@ -rise - Set cycle count for rising path edges. + Set cycle count for rising path edges. @@ -12157,7 +12179,7 @@ -fall - Set cycle count for falling path edges. + Set cycle count for falling path edges. @@ -12178,7 +12200,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -12186,7 +12208,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12194,7 +12216,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12223,15 +12245,15 @@ - 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. @@ -12239,7 +12261,7 @@ - -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. @@ -12247,7 +12269,7 @@ - ‑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. @@ -12255,7 +12277,7 @@ - -library lib + -library lib The name of the library that contains condition. @@ -12271,7 +12293,7 @@ - -min min_condition + -min min_condition The operating condition to use for min paths and hold checks. @@ -12280,7 +12302,7 @@ - -max max_condition + -max max_condition The operating condition to use for max paths and setup checks. @@ -12288,7 +12310,7 @@ - -min_library min_lib + -min_library min_lib The name of the library that contains min_condition. @@ -12296,7 +12318,7 @@ - -max_library max_lib + -max_library max_lib The name of the library that contains max_condition. @@ -12309,7 +12331,7 @@ - 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 @@ -12320,7 +12342,7 @@ -rise - Set the output delay for the rising edge of the input. + Set the output delay for the rising edge of the input. @@ -12328,7 +12350,7 @@ -fall - Set the output delay for the falling edge of the input. + Set the output delay for the falling edge of the input. @@ -12336,7 +12358,7 @@ -max - Set the maximum output delay. + Set the maximum output delay. @@ -12344,15 +12366,15 @@ -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. @@ -12360,15 +12382,15 @@ -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. @@ -12376,7 +12398,7 @@ -add_delay - Add this output delay to any existing output delays. + Add this output delay to any existing output delays. @@ -12384,7 +12406,7 @@ delay - The external delay to the check clocked by clock. + The external delay to the check clocked by clock. @@ -12396,17 +12418,17 @@ - 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 @@ -12414,7 +12436,7 @@ -min - Set the min fanout. + Set the min fanout. @@ -12422,7 +12444,7 @@ -max - Set the max fanout. + Set the max fanout. @@ -12442,21 +12464,21 @@ - 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. @@ -12472,7 +12494,7 @@ - -input_ports input_ports + -input_ports input_ports Set the input port activity/duty. @@ -12480,7 +12502,7 @@ - -pins pins + -pins pins Set the pin activity/duty. @@ -12489,46 +12511,46 @@ - -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 @@ -12549,11 +12571,11 @@ - set_pvt + set_pvt - [-min][-max][-process process][-voltage voltage] - [-temperature temperature]instances + [-min][-max][-process process][-voltage voltage] + [-temperature temperature]instances @@ -12561,7 +12583,7 @@ -min - Set the PVT values for max delays. + Set the PVT values for max delays. @@ -12569,12 +12591,12 @@ -max - Set the PVT values for min delays. + Set the PVT values for min delays. - -process process + -process process A process value (float). @@ -12582,7 +12604,7 @@ - -voltage voltage + -voltage voltage A voltage value (float). @@ -12591,7 +12613,7 @@ - -temperature temperature + -temperature temperature A temperature value (float). @@ -12612,7 +12634,7 @@ - set_sense + set_sense [-type clock|data][-positive][-negative][-pulse pulse_type][-stop_propagation][-clock clocks]pins @@ -12620,18 +12642,18 @@ - -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). @@ -12639,7 +12661,7 @@ -positive - The clock sense is positive unate. + The clock sense is positive unate. @@ -12647,15 +12669,15 @@ -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. @@ -12690,7 +12712,7 @@ - set_timing_derate + set_timing_derate [-rise][-fall][-early][-late][-clock][-data][-net_delay][-cell_delay][-cell_check]derate[objects] @@ -12698,18 +12720,18 @@ - -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. @@ -12773,7 +12795,7 @@ derate - The derating factor to apply to delays. + The derating factor to apply to delays. @@ -12792,10 +12814,10 @@ - set_resistance + set_resistance - [-max][-min]resistancenets + [-max][-min]resistancenets @@ -12825,7 +12847,7 @@ - nets + nets A list of nets. @@ -12838,72 +12860,72 @@ - 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 @@ -12916,7 +12938,7 @@ - set_wire_load_mode + set_wire_load_mode top|enclosed|segmented @@ -12953,7 +12975,7 @@ - set_wire_load_model + set_wire_load_model -name model_name[-library library][-max][-min][objects] @@ -12961,7 +12983,7 @@ - -name model_name + -name model_name The name of a wire load model. @@ -12969,7 +12991,7 @@ - -library library + -library library Library to look for model_name. @@ -13006,7 +13028,7 @@ - set_wire_load_selection_group + set_wire_load_selection_group [-library library][-max][-min]group_name[objects] @@ -13060,28 +13082,28 @@ - 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 @@ -13102,7 +13124,7 @@ - unset_clock_latency + unset_clock_latency [-source]objects @@ -13131,7 +13153,7 @@ - unset_clock_transition + unset_clock_transition clocks @@ -13153,7 +13175,7 @@ - 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] @@ -13161,7 +13183,7 @@ - -from from_clock + -from from_clock @@ -13169,7 +13191,7 @@ - -to to_clock + -to to_clock @@ -13209,7 +13231,7 @@ - uncertainty + uncertainty Clock uncertainty. @@ -13230,15 +13252,15 @@ - 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. @@ -13246,7 +13268,7 @@ - -to to_object + -to to_object A pin that the setup/hold check is applied to. @@ -13270,7 +13292,7 @@ - clock + clock The setup/hold check clock. @@ -13283,7 +13305,7 @@ - unset_disable_inferred_clock_gating + unset_disable_inferred_clock_gating objects @@ -13298,13 +13320,13 @@ - 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 @@ -13315,7 +13337,7 @@ from_port - + @@ -13323,7 +13345,7 @@ to_port - + @@ -13341,7 +13363,7 @@ - unset_input_delay + unset_input_delay [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list @@ -13352,7 +13374,7 @@ -rise - Unset the arrival time for the rising edge of the input. + Unset the arrival time for the rising edge of the input. @@ -13360,7 +13382,7 @@ -fall - Unset the arrival time for the falling edge of the input. + Unset the arrival time for the falling edge of the input. @@ -13368,7 +13390,7 @@ -max - Unset the minimum arrival time. + Unset the minimum arrival time. @@ -13376,7 +13398,7 @@ -min - Unset the maximum arrival time. + Unset the maximum arrival time. @@ -13384,7 +13406,7 @@ clock - Unset the arrival time from clock. + Unset the arrival time from clock. @@ -13392,7 +13414,7 @@ -clock_fall - Unset the arrival time from the falling edge of clock + Unset the arrival time from the falling edge of clock @@ -13411,7 +13433,7 @@ - unset_output_delay + unset_output_delay [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list @@ -13480,10 +13502,10 @@ - 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] @@ -13491,7 +13513,7 @@ -setup - Unset path exceptions for setup checks. + Unset path exceptions for setup checks. @@ -13499,7 +13521,7 @@ -hold - Unset path exceptions for hold checks. + Unset path exceptions for hold checks. @@ -13507,7 +13529,7 @@ -rise - Unset path exceptions for rising path edges. + Unset path exceptions for rising path edges. @@ -13516,12 +13538,12 @@ -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. @@ -13529,7 +13551,7 @@ - -through through + -through through A list of instances, pins or nets. @@ -13537,7 +13559,7 @@ - -to to + -to to A list of clocks, instances, ports or pins. @@ -13545,66 +13567,66 @@ 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 @@ -13625,42 +13647,42 @@ - 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 @@ -13673,7 +13695,7 @@ - with_output_to_variable + with_output_to_variable var { commands } @@ -13696,21 +13718,21 @@ - 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. @@ -13718,15 +13740,15 @@ - 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. @@ -13734,7 +13756,7 @@ - model_file + model_file Transistor model definitions .included by spice_file. @@ -13742,7 +13764,7 @@ - power + power Voltage supply name in voltage_map of the default liberty library. @@ -13750,7 +13772,7 @@ - ground + ground Ground supply name in voltage_map of the default liberty library. @@ -13758,25 +13780,25 @@ - -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 @@ -13795,7 +13817,7 @@ -gzip - Compress the SDC with gzip. + Compress the SDC with gzip. @@ -13821,18 +13843,18 @@ - 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. @@ -13854,7 +13876,7 @@ - -digits digits + -digits digits The number of digits after the decimal point to report. The default is 4. @@ -13865,7 +13887,7 @@ -gzip - Compress the SDF using gzip. + Compress the SDF using gzip. @@ -13889,78 +13911,78 @@ 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. @@ -13968,24 +13990,24 @@ - -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. @@ -13994,7 +14016,7 @@ property - Return objects with property value equal to 1. + Return objects with property value equal to 1. @@ -14002,61 +14024,61 @@ 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. @@ -14066,33 +14088,33 @@ - sta_continue_on_error + sta_continue_on_error 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 - 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 @@ -14105,7 +14127,7 @@ - sta_crpr_enabled + sta_crpr_enabled 0|1 @@ -14118,7 +14140,7 @@ - sta_dynamic_loop_breaking + sta_dynamic_loop_breaking 0|1 @@ -14131,20 +14153,20 @@ - sta_gated_clock_checks_enabled + sta_gated_clock_checks_enabled 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 @@ -14157,7 +14179,7 @@ - sta_internal_bidirect_instance_paths_enabled + sta_internal_bidirect_instance_paths_enabled 0|1 @@ -14170,7 +14192,7 @@ - sta_pocv_enabled + sta_pocv_enabled 0|1 @@ -14183,14 +14205,14 @@ - sta_propagate_all_clocks + sta_propagate_all_clocks 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. @@ -14198,33 +14220,33 @@ - sta_propagate_gated_clock_enable + sta_propagate_gated_clock_enable 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 - 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 @@ -14238,7 +14260,7 @@ - sta_preset_clear_arcs_enabled + sta_preset_clear_arcs_enabled 0|1 @@ -14271,186 +14293,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 b450630c9554130765517b9634485e150dda314a..77ebd937428ddf6b36c2e23426bf24752da895ab 100644 GIT binary patch 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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%0AHw)BGH7 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 WI_*7hIAr&ExW>6Pz+M?Y2TxZMNDL2M@c% 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℘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

hrMP6FC6~ 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

-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%0Ajs** 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≈h%s1<-h+OcNl3F2JdJV0pxd+5tSC$|qOe9Wh4b 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 i-+cJ5zx?6PfBBz3|M8zc+-A6oeE2_;@=+52 From ef924bb0b5194a498e0d8c77e54e321a517f6e2c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 7 Mar 2026 16:41:25 -0700 Subject: [PATCH 054/181] fmt Signed-off-by: James Cherry --- network/Network.i | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/network/Network.i b/network/Network.i index 349e94057..4334f6483 100644 --- a/network/Network.i +++ b/network/Network.i @@ -638,13 +638,16 @@ InstancePinIterator * pin_iterator() { return Sta::sta()->ensureLinked()->pinIterator(self); } InstanceNetIterator * net_iterator() { return Sta::sta()->ensureLinked()->netIterator(self); } + Pin * find_pin(const char *name) { return Sta::sta()->ensureLinked()->findPin(self, name); } + std::string -get_attribute(const char *key) { +get_attribute(const char *key) +{ return Sta::sta()->ensureLinked()->getAttribute(self, key); } From 3b61817f908a366796c883c6fc3c206d525e9994 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 7 Mar 2026 17:19:23 -0700 Subject: [PATCH 055/181] sdc:isConstrained for clk latency pin Signed-off-by: James Cherry --- doc/OpenSTA.fodt | 1952 ++++++++++++++++++++++---------------------- include/sta/Sdc.hh | 3 +- sdc/Sdc.cc | 7 +- sdc/Sdc.i | 8 + 4 files changed, 994 insertions(+), 976 deletions(-) diff --git a/doc/OpenSTA.fodt b/doc/OpenSTA.fodt index 5deadce7e..fb9ea3b64 100644 --- a/doc/OpenSTA.fodt +++ b/doc/OpenSTA.fodt @@ -1,11 +1,11 @@ - Parallax STA documentationJames Cherry5182025-03-17T12:59:52.4638705382010-07-31T21:07:002026-03-07T12:37:15.449188000P123DT1H22M3SLibreOffice/25.8.1.1$MacOSX_AARCH64 LibreOffice_project/54047653041915e595ad4e45cccea684809c77b5PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse + 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 - 748993 - 0 + 1488348 + 1956 19290 17736 true @@ -13,12 +13,12 @@ view2 - 6375 - 755996 - 0 - 748993 - 19288 - 766727 + 17619 + 1497110 + 1956 + 1488348 + 21244 + 1506082 0 1 false @@ -89,7 +89,7 @@ false true false - 26836501 + 26953533 0 false @@ -198,7 +198,7 @@ - + @@ -4874,431 +4874,434 @@ - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -5978,405 +5981,408 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + @@ -6512,31 +6518,31 @@ 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 + 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. @@ -6548,14 +6554,14 @@ 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 + 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. + 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. @@ -6581,13 +6587,13 @@ 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 + Commands - all_clocks + all_clocks @@ -6600,7 +6606,7 @@ - all_inputs + all_inputs [-no_clocks] @@ -6622,7 +6628,7 @@ - all_outputs + all_outputs @@ -6635,7 +6641,7 @@ - all_registers + all_registers [-clock clock_names][-cells | -data_pins | -clock_pins | -async_pins | ‑output_pins][-level_sensitive][-edge_triggered] @@ -6713,7 +6719,7 @@ - check_setup + check_setup [-verbose][-unconstrained_endpoints][-multiple_clock][-no_clock][-no_input_delay][-loops][-generated_clocks][> filename][>> filename] @@ -6782,7 +6788,7 @@ - connect_pin + connect_pin netport|pin @@ -6881,7 +6887,7 @@ - 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 @@ -6997,7 +7003,7 @@ - create_voltage_area + create_voltage_area [-name name][-coordinate coordinates][-guard_band_x guard_x][-guard_band_y guard_y]cells @@ -7010,7 +7016,7 @@ - current_design + current_design [design] @@ -7023,7 +7029,7 @@ - current_instance + current_instance [instance] @@ -7044,7 +7050,7 @@ - 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 @@ -7082,7 +7088,7 @@ - delete_clock + delete_clock [-all] clocks @@ -7103,7 +7109,7 @@ - delete_from_list + delete_from_list list objects @@ -7133,7 +7139,7 @@ - delete_generated_clock + delete_generated_clock [-all] clocks @@ -7154,7 +7160,7 @@ - delete_instance + delete_instance instance @@ -7175,7 +7181,7 @@ - delete_net + delete_net net @@ -7196,7 +7202,7 @@ - disconnect_pin + disconnect_pin netport | pin | -all @@ -7241,7 +7247,7 @@ - elapsed_run_time + elapsed_run_time @@ -7255,7 +7261,7 @@ - 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] @@ -7470,7 +7476,7 @@ - get_cells + get_cells [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -7547,7 +7553,7 @@ - get_clocks + get_clocks [-regexp][-nocase][-filter expr][-quiet]patterns @@ -7601,7 +7607,7 @@ - get_fanin + get_fanin -to sink_list[-flat][-only_cells][-startpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] @@ -7687,7 +7693,7 @@ - get_fanout + get_fanout -from source_list[-flat][-only_cells][-endpoints_only][-levels level_count][-pin_levels pin_count][-trace_arcs timing|enabled|all] @@ -7772,7 +7778,7 @@ - get_full_name + get_full_name object @@ -7863,7 +7869,7 @@ - get_lib_pins + get_lib_pins [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns @@ -7933,7 +7939,7 @@ - get_libs + get_libs [-filter expr][-regexp][-nocase][-quiet]patterns @@ -7987,7 +7993,7 @@ - get_nets + get_nets [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -8064,7 +8070,7 @@ - get_name + get_name object @@ -8086,7 +8092,7 @@ - get_pins + get_pins [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -8157,7 +8163,7 @@ - get_ports + get_ports [-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] @@ -8219,7 +8225,7 @@ - get_property + get_property [-object_type object_type]objectproperty @@ -8281,7 +8287,7 @@ - get_scenes + get_scenes [-mode mode_name]scene_name @@ -8310,7 +8316,7 @@ - get_timing_edges + get_timing_edges [-from from_pins][-to to_pins][-of_objects objects][-filter expr][patterns] @@ -8356,7 +8362,7 @@ - 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] @@ -8473,7 +8479,7 @@ - include + include [-echo|-e][-verbose|-v]filename[> log_filename][>> log_filename] @@ -8527,7 +8533,7 @@ - link_design + link_design [-no_black_boxes][cell_name] @@ -8558,7 +8564,7 @@ - make_instance + make_instance inst_pathlib_cell @@ -8588,7 +8594,7 @@ - make_net + make_net net_name_list @@ -8609,7 +8615,7 @@ - read_liberty + read_liberty [-corner corner][-min][-max][-infer_latches]filename @@ -8658,7 +8664,7 @@ - read_saif + read_saif [-scope scope]filename @@ -8688,7 +8694,7 @@ - read_sdc + read_sdc [-mode mode_name][-echo]filename @@ -8728,7 +8734,7 @@ - read_sdf + read_sdf [-scene scene][-unescaped_dividers]filename @@ -8769,7 +8775,7 @@ - read_spef + read_spef [-name name][-keep_capacitive_coupling][-coupling_reduction_factor factor][-reduce][-path path]filename @@ -8816,7 +8822,7 @@ - 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 @@ -8830,7 +8836,7 @@ - read_vcd + read_vcd [-scope scope][-mode mode_name]filename @@ -8867,7 +8873,7 @@ - read_verilog + read_verilog filename @@ -8882,7 +8888,7 @@ - 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. + 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. @@ -8891,7 +8897,7 @@ - replace_cell + replace_cell instance_listreplacement_cell @@ -8921,7 +8927,7 @@ - replace_activity_annotation + replace_activity_annotation [-report_unannotated][-report_annotated] @@ -8950,7 +8956,7 @@ - report_annotated_check + report_annotated_check [-setup][-hold][-recovery][-removal][-nochange][-width][-period][-max_skew][-max_line lines][-report_annotated][-report_unannotated][-constant_arcs] @@ -9060,7 +9066,7 @@ - report_annotated_delay + report_annotated_delay [-cell][-net][-from_in_ports][-to_out_ports][-max_lines lines][-report_annotated][-report_unannotated][-constant_arcs] @@ -9138,7 +9144,7 @@ - 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] @@ -9443,7 +9449,7 @@ - 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] @@ -9601,7 +9607,7 @@ - report_clock_latency + report_clock_latency [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] @@ -9646,7 +9652,7 @@ - report_clock_min_period + report_clock_min_period [-clocks clocks][-scenes scenes][-include_port_paths] @@ -9676,7 +9682,7 @@ - report_clock_properties + report_clock_properties [clock_names] @@ -9697,7 +9703,7 @@ - report_clock_skew + report_clock_skew [-setup|-hold][-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] @@ -9758,7 +9764,7 @@ - report_dcalc + report_dcalc [-from from_pin][-to to_pin][-scene scene][-min][-max][-digits digits][> filename][>> filename] @@ -9820,7 +9826,7 @@ - report_disabled_edges + report_disabled_edges @@ -9834,7 +9840,7 @@ - report_edges + report_edges [-from from_pin][-to to_pin] @@ -9863,7 +9869,7 @@ - report_instance + report_instance instance_path[> filename][>> filename] @@ -9884,7 +9890,7 @@ - report_lib_cell + report_lib_cell cell_name[> filename][>> filename] @@ -9906,7 +9912,7 @@ - report_net + report_net [-digits digits]net_path[> filename][>> filename] @@ -9935,7 +9941,7 @@ - report_parasitic_annotation + report_parasitic_annotation [-report_unannotated][> filename][>> filename] @@ -9956,7 +9962,7 @@ - report_power + report_power [-instances instances][-highest_power_instances count][-digits digits][> filename][>> filename] @@ -9995,7 +10001,7 @@ - report_slews + report_slews [-scenes scenes]pin @@ -10024,7 +10030,7 @@ - report_tns + report_tns [-min][-max][-digits digits] @@ -10061,7 +10067,7 @@ - report_units + report_units @@ -10075,7 +10081,7 @@ - report_wns + report_wns [-min][-max][-digits digits] @@ -10112,7 +10118,7 @@ - report_worst_slack + report_worst_slack [-min][-max][-digits digits] @@ -10150,7 +10156,7 @@ - set_assigned_check + 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 @@ -10268,7 +10274,7 @@ - set_assigned_delay + set_assigned_delay -cell|-net[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins]delay @@ -10361,7 +10367,7 @@ - set_assigned_transition + set_assigned_transition [-rise][-fall][-scene scene][-min][-max]slewpin_list @@ -10431,7 +10437,7 @@ - set_case_analysis + set_case_analysis 0|1|zero|one|rise|rising|fall|fallingport_or_pin_list @@ -10453,7 +10459,7 @@ - set_clock_gating_check + set_clock_gating_check [-setup setup_time][-hold hold_time][-rise][-fall][-high][-low][objects] @@ -10527,7 +10533,7 @@ - set_clock_groups + set_clock_groups [-name name][-logically_exclusive][-physically_exclusive][-asynchronous][-allow_paths]-group clocks @@ -10588,7 +10594,7 @@ - set_clock_latency + set_clock_latency [-source][-clock clock][-rise][-fall][-min][-max]delayobjects @@ -10659,13 +10665,13 @@ - The set_clock_latency command describes expected delays of the clock tree when analyzing 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. + 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 @@ -10676,24 +10682,24 @@ -rise - Set the transition time for the rising edge of the clock. + 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 falling edge of the clock. - -min - Set the min transition time. + Set the min transition time. @@ -10701,7 +10707,7 @@ -max - Set the min transition time. + Set the min transition time. @@ -10727,7 +10733,7 @@ - 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] @@ -10735,18 +10741,18 @@ - -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. @@ -10754,7 +10760,7 @@ -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. @@ -10762,7 +10768,7 @@ -fall - Inter-clock target clock rise edge, alternative to ‑fall_to. + Inter-clock target clock rise edge, alternative to ‑fall_to. @@ -10770,7 +10776,7 @@ -setup - uncertainty is for setup checks. + uncertainty is for setup checks. @@ -10778,12 +10784,12 @@ -hold - uncertainty is for hold checks. + uncertainty is for hold checks. - uncertainty + uncertainty Clock uncertainty. @@ -10798,99 +10804,99 @@ - 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 + set_cmd_units -time ns -capacitance pF -current mA -voltage V -resistance kOhm -distance um - 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. @@ -10898,7 +10904,7 @@ - -to to_pin + -to to_pin A pin that the setup/hold check is applied to. @@ -10922,7 +10928,7 @@ - -clock clock + -clock clock The setup/hold check clock. @@ -10943,7 +10949,7 @@ - set_disable_inferred_clock_gating + set_disable_inferred_clock_gating objects @@ -10962,9 +10968,10 @@ + - set_disable_timing + set_disable_timing [-from from_port][-to to_port]objects @@ -10972,18 +10979,18 @@ - -from from_port + -from from_port - + - -to to_port + -to to_port - + @@ -10991,11 +10998,11 @@ 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. + 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. 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. @@ -11009,18 +11016,19 @@ - set_drive + set_drive - [-rise][-fall][-max][-min]resistanceports + [-rise][-fall][-max][-min]resistanceports + -rise - Set the drive rise resistance. + Set the drive rise resistance. @@ -11028,7 +11036,7 @@ -fall - Set the drive fall resistance. + Set the drive fall resistance. @@ -11036,7 +11044,7 @@ -max - Set the maximum resistance. + Set the maximum resistance. @@ -11044,7 +11052,7 @@ -min - Set the minimum resistance. + Set the minimum resistance. @@ -11057,39 +11065,39 @@ - ports + ports A list of ports. - The set_drive command describes the resistance of an input port external driver. + The set_drive command describes the resistance of an input port external driver. - 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. @@ -11097,7 +11105,7 @@ -rise - Set the driving cell for a rising edge. + Set the driving cell for a rising edge. @@ -11105,7 +11113,7 @@ -fall - Set the driving cell for a falling edge. + Set the driving cell for a falling edge. @@ -11113,7 +11121,7 @@ -max - Set the driving cell for max delays. + Set the driving cell for max delays. @@ -11121,12 +11129,12 @@ -min - Set the driving cell for min delays. + Set the driving cell for min delays. - -pin pin + -pin pin The output port of the driving cell. @@ -11134,15 +11142,16 @@ - -from_pin from_pin + -from_pin from_pin - Use timing arcs from from_pin to the output pin. + Use timing arcs from from_pin to the output pin. + - -input_transition_rise trans_rise + -input_transition_rise trans_rise The transition time for a rising input at from_pin. @@ -11150,7 +11159,7 @@ - -input_transition_fall trans_fall + -input_transition_fall trans_fall The transition time for a falling input at from_pin. @@ -11158,7 +11167,7 @@ - ports + ports A list of ports. @@ -11169,10 +11178,9 @@ - - 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] @@ -11183,7 +11191,7 @@ -setup - Apply to setup checks. + Apply to setup checks. @@ -11191,7 +11199,7 @@ -hold - Apply to hold checks. + Apply to hold checks. @@ -11199,7 +11207,7 @@ -rise - Apply to rising path edges. + Apply to rising path edges. @@ -11207,7 +11215,7 @@ -fall - Apply to falling path edges. + Apply to falling path edges. @@ -11220,7 +11228,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11228,7 +11236,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11236,7 +11244,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11244,16 +11252,15 @@ 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. - - set_fanout_load + set_fanout_load fanoutport_list @@ -11266,7 +11273,7 @@ - set_hierarchy_separator + set_hierarchy_separator separator @@ -11287,7 +11294,7 @@ - set_ideal_latency + set_ideal_latency [-rise] [-fall] [-min] [-max] delay objects @@ -11300,7 +11307,7 @@ - set_ideal_network + set_ideal_network [-no_propagation] objects @@ -11313,7 +11320,7 @@ - set_ideal_transition + set_ideal_transition [-rise] [-fall] [-min] [-max] transition_time objects @@ -11324,9 +11331,10 @@ + - 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 @@ -11337,7 +11345,7 @@ -rise - Set the arrival time for the rising edge of the input. + Set the arrival time for the rising edge of the input. @@ -11345,16 +11353,15 @@ -fall - Set the arrival time for the falling edge of the input. + Set the arrival time for the falling edge of the input. - -max - Set the maximum arrival time. + Set the maximum arrival time. @@ -11362,12 +11369,12 @@ -min - Set the minimum arrival time. + Set the minimum arrival time. - -clock clock + -clock clock The arrival time is from clock. @@ -11383,7 +11390,7 @@ - -reference_pin ref_pin + -reference_pin ref_pin The arrival time is with respect to the clock that arrives at ref_pin. @@ -11394,7 +11401,7 @@ -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. @@ -11402,7 +11409,7 @@ -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. @@ -11430,20 +11437,19 @@ - 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 @@ -11454,7 +11460,7 @@ -rise - Set the rising edge transition. + Set the rising edge transition. @@ -11462,7 +11468,7 @@ -fall - Set the falling edge transition. + Set the falling edge transition. @@ -11470,7 +11476,7 @@ -max - Set the minimum transition time. + Set the minimum transition time. @@ -11478,7 +11484,7 @@ -min - Set the maximum transition time. + Set the maximum transition time. @@ -11504,7 +11510,7 @@ - set_level_shifter_strategy + set_level_shifter_strategy [-rule rule_type] @@ -11515,9 +11521,10 @@ + - set_level_shifter_threshold + set_level_shifter_threshold [-voltage voltage] @@ -11530,7 +11537,7 @@ - set_load + set_load [-rise][-fall][-max][-min][-subtract_pin_load][-pin_load][-wire_load]capacitanceobjects @@ -11541,7 +11548,7 @@ -rise - Set the external port rising capacitance (ports only). + Set the external port rising capacitance (ports only). @@ -11549,16 +11556,15 @@ -fall - Set the external port falling capacitance (ports only). + Set the external port falling capacitance (ports only). - -max - Set the max capacitance. + Set the max capacitance. @@ -11566,7 +11572,7 @@ -min - Set the min capacitance. + Set the min capacitance. @@ -11574,7 +11580,7 @@ -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. @@ -11610,16 +11616,16 @@ - 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 @@ -11640,7 +11646,7 @@ - set_logic_one + set_logic_one port_list @@ -11655,14 +11661,13 @@ - 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 @@ -11677,13 +11682,13 @@ - 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 @@ -11704,7 +11709,7 @@ - set_max_capacitance + set_max_capacitance capacitanceobjects @@ -11718,6 +11723,7 @@ + objects @@ -11733,10 +11739,10 @@ - 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 @@ -11744,7 +11750,7 @@ -rise - Set max delay for rising paths. + Set max delay for rising paths. @@ -11752,13 +11758,12 @@ -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. @@ -11766,7 +11771,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11774,7 +11779,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11793,7 +11798,7 @@ -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). @@ -11814,13 +11819,13 @@ The set_max_delay command constrains the maximum delay through combinational logic paths. See set_false_path for a description of allowed from_list, through_list and to_list objects. If the to_list ends at a timing check the setup/hold time is included in the path delay. - When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. + When the -ignore_clock_latency option is used clock latency at the source and destination of the path delay is ignored. The constraint is reported in the default path group (**default**) rather than the clock path group when the path ends at a timing check. - set_max_dynamic_power + set_max_dynamic_power power [unit] @@ -11833,7 +11838,7 @@ - set_max_fanout + set_max_fanout fanoutobjects @@ -11862,7 +11867,7 @@ - set_max_leakage_power + set_max_leakage_power power [unit] @@ -11875,13 +11880,12 @@ - set_max_time_borrow + set_max_time_borrow delayobjects - delay @@ -11905,7 +11909,7 @@ - set_max_transition + set_max_transition [-data_path][-clock_path][-rise][-fall]transitionobjects @@ -11913,34 +11917,35 @@ - -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. @@ -11948,7 +11953,7 @@ transition - The maximum slew/transition time. + The maximum slew/transition time. @@ -11960,7 +11965,7 @@ - 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. @@ -11968,7 +11973,7 @@ - set_min_capacitance + set_min_capacitance capacitanceobjects @@ -11979,7 +11984,7 @@ capacitance - Minimum capacitance. + Minimum capacitance. @@ -11995,13 +12000,12 @@ - - 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 @@ -12009,20 +12013,21 @@ -rise - Set min delay for rising paths. + Set min delay for rising paths. + -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. @@ -12030,7 +12035,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12038,7 +12043,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12057,7 +12062,7 @@ -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). @@ -12073,7 +12078,7 @@ delay - The minimum delay. + The minimum delay. @@ -12082,10 +12087,9 @@ - - set_min_pulse_width + set_min_pulse_width [-high][-low]min_widthobjects @@ -12130,20 +12134,21 @@ - set_mode + set_mode 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 @@ -12154,7 +12159,7 @@ -setup - Set cycle count for setup checks. + Set cycle count for setup checks. @@ -12162,7 +12167,7 @@ -hold - Set cycle count for hold checks. + Set cycle count for hold checks. @@ -12170,16 +12175,15 @@ -rise - Set cycle count for rising path edges. + Set cycle count for rising path edges. - -fall - Set cycle count for falling path edges. + Set cycle count for falling path edges. @@ -12200,7 +12204,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -12208,7 +12212,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12216,7 +12220,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12243,17 +12247,18 @@ + - 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. @@ -12261,7 +12266,7 @@ - -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. @@ -12269,7 +12274,7 @@ - ‑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. @@ -12277,7 +12282,7 @@ - -library lib + -library lib The name of the library that contains condition. @@ -12293,16 +12298,15 @@ - -min min_condition + -min min_condition 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. @@ -12310,7 +12314,7 @@ - -min_library min_lib + -min_library min_lib The name of the library that contains min_condition. @@ -12318,7 +12322,7 @@ - -max_library max_lib + -max_library max_lib The name of the library that contains max_condition. @@ -12331,18 +12335,19 @@ - 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 - Set the output delay for the rising edge of the input. + Set the output delay for the rising edge of the input. @@ -12350,7 +12355,7 @@ -fall - Set the output delay for the falling edge of the input. + Set the output delay for the falling edge of the input. @@ -12358,7 +12363,7 @@ -max - Set the maximum output delay. + Set the maximum output delay. @@ -12366,15 +12371,15 @@ -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. @@ -12382,15 +12387,15 @@ -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. @@ -12398,7 +12403,7 @@ -add_delay - Add this output delay to any existing output delays. + Add this output delay to any existing output delays. @@ -12406,7 +12411,7 @@ delay - The external delay to the check clocked by clock. + The external delay to the check clocked by clock. @@ -12418,17 +12423,17 @@ - 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 @@ -12436,7 +12441,7 @@ -min - Set the min fanout. + Set the min fanout. @@ -12444,7 +12449,7 @@ -max - Set the max fanout. + Set the max fanout. @@ -12464,21 +12469,21 @@ - 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. @@ -12494,7 +12499,7 @@ - -input_ports input_ports + -input_ports input_ports Set the input port activity/duty. @@ -12502,55 +12507,54 @@ - -pins pins + -pins pins 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 @@ -12569,13 +12573,14 @@ + - set_pvt + set_pvt - [-min][-max][-process process][-voltage voltage] - [-temperature temperature]instances + [-min][-max][-process process][-voltage voltage] + [-temperature temperature]instances @@ -12583,7 +12588,7 @@ -min - Set the PVT values for max delays. + Set the PVT values for max delays. @@ -12591,12 +12596,12 @@ -max - Set the PVT values for min delays. + Set the PVT values for min delays. - -process process + -process process A process value (float). @@ -12604,16 +12609,15 @@ - -voltage voltage + -voltage voltage A voltage value (float). - - -temperature temperature + -temperature temperature A temperature value (float). @@ -12634,7 +12638,7 @@ - set_sense + set_sense [-type clock|data][-positive][-negative][-pulse pulse_type][-stop_propagation][-clock clocks]pins @@ -12642,18 +12646,18 @@ - -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). @@ -12661,7 +12665,7 @@ -positive - The clock sense is positive unate. + The clock sense is positive unate. @@ -12669,15 +12673,16 @@ -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. @@ -12709,10 +12714,9 @@ - - set_timing_derate + set_timing_derate [-rise][-fall][-early][-late][-clock][-data][-net_delay][-cell_delay][-cell_check]derate[objects] @@ -12720,18 +12724,18 @@ - -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. @@ -12774,6 +12778,7 @@ Derate net (interconnect) delays. + -cell_delay @@ -12795,7 +12800,7 @@ derate - The derating factor to apply to delays. + The derating factor to apply to delays. @@ -12814,13 +12819,12 @@ - set_resistance + set_resistance - [-max][-min]resistancenets + [-max][-min]resistancenets - -min @@ -12847,7 +12851,7 @@ - nets + nets A list of nets. @@ -12860,72 +12864,73 @@ - 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 @@ -12938,7 +12943,7 @@ - set_wire_load_mode + set_wire_load_mode top|enclosed|segmented @@ -12975,15 +12980,16 @@ - set_wire_load_model + set_wire_load_model -name model_name[-library library][-max][-min][objects] + - -name model_name + -name model_name The name of a wire load model. @@ -12991,7 +12997,7 @@ - -library library + -library library Library to look for model_name. @@ -13028,7 +13034,7 @@ - set_wire_load_selection_group + set_wire_load_selection_group [-library library][-max][-min]group_name[objects] @@ -13042,7 +13048,6 @@ Library to look for group_name. - -max @@ -13082,28 +13087,28 @@ - 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 @@ -13118,13 +13123,13 @@ - The unset_case_analysis command removes the constant values defined by the set_case_analysis command. + The unset_case_analysis command removes the constant values defined by the set_case_analysis command. - unset_clock_latency + unset_clock_latency [-source]objects @@ -13153,7 +13158,7 @@ - unset_clock_transition + unset_clock_transition clocks @@ -13172,10 +13177,9 @@ - - 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] @@ -13183,7 +13187,7 @@ - -from from_clock + -from from_clock @@ -13191,7 +13195,7 @@ - -to to_clock + -to to_clock @@ -13231,7 +13235,7 @@ - uncertainty + uncertainty Clock uncertainty. @@ -13246,21 +13250,21 @@ - The unset_clock_uncertainty command removes clock uncertainty defined with the set_clock_uncertainty command. + The unset_clock_uncertainty command removes clock uncertainty defined with the set_clock_uncertainty command. - 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. @@ -13268,7 +13272,7 @@ - -to to_object + -to to_object A pin that the setup/hold check is applied to. @@ -13292,20 +13296,20 @@ - clock + clock The setup/hold check clock. - The unset_clock_transition command removes a setup or hold check defined by the set_data_check command. + The unset_clock_transition command removes a setup or hold check defined by the set_data_check command. - unset_disable_inferred_clock_gating + unset_disable_inferred_clock_gating objects @@ -13326,7 +13330,7 @@ - unset_disable_timing + unset_disable_timing [-from from_port][-to to_port]objects @@ -13337,7 +13341,7 @@ from_port - + @@ -13345,7 +13349,7 @@ to_port - + @@ -13361,9 +13365,10 @@ + - unset_input_delay + unset_input_delay [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list @@ -13374,7 +13379,7 @@ -rise - Unset the arrival time for the rising edge of the input. + Unset the arrival time for the rising edge of the input. @@ -13382,7 +13387,7 @@ -fall - Unset the arrival time for the falling edge of the input. + Unset the arrival time for the falling edge of the input. @@ -13390,7 +13395,7 @@ -max - Unset the minimum arrival time. + Unset the minimum arrival time. @@ -13398,7 +13403,7 @@ -min - Unset the maximum arrival time. + Unset the maximum arrival time. @@ -13406,7 +13411,7 @@ clock - Unset the arrival time from clock. + Unset the arrival time from clock. @@ -13414,10 +13419,9 @@ -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 @@ -13433,7 +13437,7 @@ - unset_output_delay + unset_output_delay [-rise][-fall][-max][-min][-clock clock][-clock_fall]port_pin_list @@ -13479,6 +13483,7 @@ The arrival time is from this clock. + -clock_fall @@ -13502,10 +13507,10 @@ - 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] @@ -13513,7 +13518,7 @@ -setup - Unset path exceptions for setup checks. + Unset path exceptions for setup checks. @@ -13521,7 +13526,7 @@ -hold - Unset path exceptions for hold checks. + Unset path exceptions for hold checks. @@ -13529,21 +13534,20 @@ -rise - Unset path exceptions for rising path edges. + Unset path exceptions for rising path edges. - -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. @@ -13551,7 +13555,7 @@ - -through through + -through through A list of instances, pins or nets. @@ -13559,7 +13563,7 @@ - -to to + -to to A list of clocks, instances, ports or pins. @@ -13567,66 +13571,67 @@ 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 @@ -13647,42 +13652,41 @@ - 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 @@ -13695,7 +13699,7 @@ - with_output_to_variable + with_output_to_variable var { commands } @@ -13722,17 +13726,18 @@ + - 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. @@ -13740,15 +13745,15 @@ - 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. @@ -13756,7 +13761,7 @@ - model_file + model_file Transistor model definitions .included by spice_file. @@ -13764,7 +13769,7 @@ - power + power Voltage supply name in voltage_map of the default liberty library. @@ -13772,7 +13777,7 @@ - ground + ground Ground supply name in voltage_map of the default liberty library. @@ -13780,30 +13785,31 @@ - -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. + 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. - write_sdc + write_sdc [-digits digits][-gzip][-no_timestamp]filename + digits @@ -13817,7 +13823,7 @@ -gzip - Compress the SDC with gzip. + Compress the SDC with gzip. @@ -13843,7 +13849,7 @@ - write_sdf + write_sdf [-scene scene][-divider /|.][-include_typ][-digits digits][-gzip][-no_timestamp][-no_version]filename @@ -13854,7 +13860,7 @@ scene - Write delays for scene. + Write delays for scene. @@ -13865,7 +13871,6 @@ Divider to use between hierarchy levels in pin and instance names. - -include_typ @@ -13876,7 +13881,7 @@ - -digits digits + -digits digits The number of digits after the decimal point to report. The default is 4. @@ -13887,7 +13892,7 @@ -gzip - Compress the SDF using gzip. + Compress the SDF using gzip. @@ -13911,78 +13916,79 @@ 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 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. + 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. 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. @@ -13990,24 +13996,24 @@ - -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. @@ -14016,7 +14022,7 @@ property - Return objects with property value equal to 1. + Return objects with property value equal to 1. @@ -14024,61 +14030,61 @@ 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. @@ -14088,33 +14094,34 @@ - sta_continue_on_error + sta_continue_on_error 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 - 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 @@ -14127,7 +14134,7 @@ - sta_crpr_enabled + sta_crpr_enabled 0|1 @@ -14140,7 +14147,7 @@ - sta_dynamic_loop_breaking + sta_dynamic_loop_breaking 0|1 @@ -14153,20 +14160,20 @@ - sta_gated_clock_checks_enabled + sta_gated_clock_checks_enabled 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 @@ -14179,7 +14186,7 @@ - sta_internal_bidirect_instance_paths_enabled + sta_internal_bidirect_instance_paths_enabled 0|1 @@ -14192,7 +14199,7 @@ - sta_pocv_enabled + sta_pocv_enabled 0|1 @@ -14205,14 +14212,14 @@ - sta_propagate_all_clocks + sta_propagate_all_clocks 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. @@ -14220,7 +14227,7 @@ - sta_propagate_gated_clock_enable + sta_propagate_gated_clock_enable 0|1 @@ -14233,7 +14240,7 @@ - sta_recovery_removal_checks_enabled + sta_recovery_removal_checks_enabled 0|1 @@ -14246,7 +14253,7 @@ - sta_report_default_digits + sta_report_default_digits integer @@ -14257,10 +14264,9 @@ - - sta_preset_clear_arcs_enabled + sta_preset_clear_arcs_enabled 0|1 @@ -14293,186 +14299,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/include/sta/Sdc.hh b/include/sta/Sdc.hh index 31b363bdb..dfd9aced5 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -149,7 +149,7 @@ using ExceptionPathPtHash = std::map; using ClockLatencies = std::set; using EdgeClockLatencyMap = std::map; using PinClockUncertaintyMap = std::map; -using InterClockUncertaintySet = std::set; +using InterClockUncertaintySet=std::set; using ClockGatingCheckMap = std::map; using InstanceClockGatingCheckMap = std::map; using PinClockGatingCheckMap = std::map; @@ -1306,6 +1306,7 @@ protected: bool clk_hpin_disables_valid_; PinSet propagated_clk_pins_; ClockLatencies clk_latencies_; + PinSet clk_latency_pins_; EdgeClockLatencyMap edge_clk_latency_map_; ClockInsertions clk_insertions_; PinClockUncertaintyMap pin_clk_uncertainty_map_; diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 001d75671..12bcd471c 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -101,6 +101,7 @@ Sdc::Sdc(Mode *mode, clk_hpin_disables_(network_), propagated_clk_pins_(network_), clk_latencies_(network_), + clk_latency_pins_(network_), edge_clk_latency_map_(network_), clk_insertions_(network_), clk_sense_map_(network_), @@ -169,6 +170,7 @@ Sdc::clear() clock_pin_map_.clear(); clock_leaf_pin_map_.clear(); clk_latencies_.clear(); + clk_latency_pins_.clear(); edge_clk_latency_map_.clear(); clk_insertions_.clear(); @@ -1508,6 +1510,8 @@ Sdc::setClockLatency(Clock *clk, } } latency->setDelay(rf, min_max, delay); + if (pin) + clk_latency_pins_.insert(pin); // set_clock_latency removes set_propagated_clock on the same object. if (clk && pin == nullptr) @@ -1580,8 +1584,7 @@ Sdc::deleteClockLatenciesReferencing(Clock *clk) bool Sdc::hasClockLatency(const Pin *pin) const { - ClockLatency probe(nullptr, pin); - return clk_latencies_.contains(&probe); + return clk_latency_pins_.contains(pin); } void diff --git a/sdc/Sdc.i b/sdc/Sdc.i index e6b95667d..2bca70b01 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -1654,6 +1654,14 @@ set_propagate_all_clocks(bool prop) Sta::sta()->setPropagateAllClocks(prop); } +bool +pin_is_constrained(const Pin *pin) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + return sdc->isConstrained(pin); +} + %} // inline //////////////////////////////////////////////////////////////// From 274637ce4638a839859c9a3bbae635aa074f4b3e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 10:21:55 -0700 Subject: [PATCH 056/181] liberty attributes after cells resolves #400 Signed-off-by: James Cherry --- liberty/LibertyReader.cc | 84 ++++++++++++++++++------------------- liberty/LibertyReaderPvt.hh | 29 ++++++------- 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 7d77fa3ca..872b135bd 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -78,8 +78,7 @@ LibertyReader::LibertyReader(const char *filename, debug_(network->debug()), network_(network), builder_(debug_, report_), - library_(nullptr), - first_cell_(true) + library_(nullptr) { defineVisitors(); } @@ -148,13 +147,13 @@ LibertyReader::beginLibrary(const LibertyGroup *library_group, } void -LibertyReader::endLibrary(const LibertyGroup *group, +LibertyReader::endLibrary(const LibertyGroup *library_group, LibertyGroup *) { - // If a library hasno cells endCell is not called. - if (first_cell_) - readLibraryAttributes(group); - delete group; + // If a library has no cells endCell is not called. + readLibraryAttributes(library_group); + checkThresholds(library_group); + delete library_group; } //////////////////////////////////////////////////////////////// @@ -167,12 +166,7 @@ LibertyReader::endCell(const LibertyGroup *cell_group, // Normally they are all defined by the first cell, but there // are libraries that define table templates and bus tyupes // between cells. - if (first_cell_) - readLibraryAttributes(library_group); - else { - readTableTemplates(library_group); - readBusTypes(nullptr, library_group); - } + readLibraryAttributes(library_group); const char *name = cell_group->firstName(); if (name) { @@ -182,8 +176,10 @@ LibertyReader::endCell(const LibertyGroup *cell_group, } else libWarn(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. library_group->clear(); - first_cell_ = false; } void @@ -527,21 +523,25 @@ LibertyReader::readThresholds(const LibertyGroup *library_group) std::string suffix = rf->to_string(); readLibAttrFloat(library_group, ("input_threshold_pct_" + suffix).c_str(), &LibertyLibrary::setInputThreshold, rf, 0.01F); - if (library_->inputThreshold(rf) == 0.0) - libWarn(1145, library_group, "input_threshold_pct_%s not found.", rf->name()); - readLibAttrFloat(library_group, ("output_threshold_pct_" + suffix).c_str(), &LibertyLibrary::setOutputThreshold, rf, 0.01F); - if (library_->outputThreshold(rf) == 0.0) - libWarn(1146, library_group, "output_threshold_pct_%s not found.", rf->name()); - readLibAttrFloat(library_group, ("slew_lower_threshold_pct_" + suffix).c_str(), &LibertyLibrary::setSlewLowerThreshold, rf, 0.01F); - if (library_->slewLowerThreshold(rf) == 0.0) - libWarn(1147, library_group, "slew_lower_threshold_pct_%s not found.", rf->name()); - readLibAttrFloat(library_group, ("slew_upper_threshold_pct_" + suffix).c_str(), &LibertyLibrary::setSlewUpperThreshold, rf, 0.01F); + } +} + +void +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()); + if (library_->outputThreshold(rf) == 0.0) + libWarn(1146, library_group, "output_threshold_pct_%s not found.", rf->name()); + if (library_->slewLowerThreshold(rf) == 0.0) + libWarn(1147, library_group, "slew_lower_threshold_pct_%s not found.", rf->name()); if (library_->slewUpperThreshold(rf) == 0.0) libWarn(1148, library_group, "slew_upper_threshold_pct_%s not found.", rf->name()); } @@ -3458,37 +3458,37 @@ LibertyReader::variableValue(const char *var, void LibertyReader::libWarn(int id, - const LibertyGroup *obj, + const LibertyGroup *group, const char *fmt, - ...) + ...) const { va_list args; va_start(args, fmt); - report_->vfileWarn(id, filename_, obj->line(), fmt, args); + report_->vfileWarn(id, filename_, group->line(), fmt, args); va_end(args); } void LibertyReader::libWarn(int id, - const LibertySimpleAttr *obj, + const LibertySimpleAttr *attr, const char *fmt, - ...) + ...) const { va_list args; va_start(args, fmt); - report_->vfileWarn(id, filename_, obj->line(), fmt, args); + report_->vfileWarn(id, filename_, attr->line(), fmt, args); va_end(args); } void LibertyReader::libWarn(int id, - const LibertyComplexAttr *obj, + const LibertyComplexAttr *attr, const char *fmt, - ...) + ...) const { va_list args; va_start(args, fmt); - report_->vfileWarn(id, filename_, obj->line(), fmt, args); + report_->vfileWarn(id, filename_, attr->line(), fmt, args); va_end(args); } @@ -3496,7 +3496,7 @@ void LibertyReader::libWarn(int id, int line, const char *fmt, - ...) + ...) const { va_list args; va_start(args, fmt); @@ -3506,37 +3506,37 @@ LibertyReader::libWarn(int id, void LibertyReader::libError(int id, - const LibertyGroup *obj, + const LibertyGroup *group, const char *fmt, - ...) + ...) const { va_list args; va_start(args, fmt); - report_->vfileError(id, filename_, obj->line(), fmt, args); + report_->vfileError(id, filename_, group->line(), fmt, args); va_end(args); } void LibertyReader::libError(int id, - const LibertySimpleAttr *obj, + const LibertySimpleAttr *attr, const char *fmt, - ...) + ...) const { va_list args; va_start(args, fmt); - report_->vfileError(id, filename_, obj->line(), fmt, args); + report_->vfileError(id, filename_, attr->line(), fmt, args); va_end(args); } void LibertyReader::libError(int id, - const LibertyComplexAttr *obj, + const LibertyComplexAttr *attr, const char *fmt, - ...) + ...) const { va_list args; va_start(args, fmt); - report_->vfileError(id, filename_, obj->line(), fmt, args); + report_->vfileError(id, filename_, attr->line(), fmt, args); va_end(args); } diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index c17cdeb82..18c5bc5fe 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -24,6 +24,7 @@ #pragma once +#include #include #include #include @@ -132,6 +133,7 @@ protected: const char *group_name, TableTemplateType type); void readThresholds(const LibertyGroup *library_group); + void checkThresholds(const LibertyGroup *library_group) const; TableAxisPtr makeTableTemplateAxis(const LibertyGroup *template_group, int axis_index); void readVoltateMaps(const LibertyGroup *library_group); @@ -445,36 +447,36 @@ protected: const LibertyCell *cell, int line); void libWarn(int id, - const LibertyGroup *obj, + const LibertyGroup *group, const char *fmt, - ...) + ...) const __attribute__((format (printf, 4, 5))); void libWarn(int id, - const LibertySimpleAttr *obj, + const LibertySimpleAttr *attr, const char *fmt, - ...) + ...) const __attribute__((format (printf, 4, 5))); void libWarn(int id, - const LibertyComplexAttr *obj, + const LibertyComplexAttr *attr, const char *fmt, - ...) + ...) const __attribute__((format (printf, 4, 5))); void libWarn(int id, int line, const char *fmt, - ...) + ...) const __attribute__((format (printf, 4, 5))); void libError(int id, - const LibertyGroup *obj, - const char *fmt, ...) + const LibertyGroup *group, + const char *fmt, ...) const __attribute__((format (printf, 4, 5))); void libError(int id, - const LibertySimpleAttr *obj, - const char *fmt, ...) + const LibertySimpleAttr *attr, + const char *fmt, ...) const __attribute__((format (printf, 4, 5))); void libError(int id, - const LibertyComplexAttr *obj, - const char *fmt, ...) + const LibertyComplexAttr *attr, + const char *fmt, ...) const __attribute__((format (printf, 4, 5))); const char *filename_; @@ -485,7 +487,6 @@ protected: LibertyBuilder builder_; LibertyVariableMap var_map_; LibertyLibrary *library_; - bool first_cell_; LibraryGroupVisitorMap group_begin_map_; LibraryGroupVisitorMap group_end_map_; From a419f0a721ec170328e39acb8764ac3cd85e7c9c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 13:36:54 -0700 Subject: [PATCH 057/181] LibertyPortPair calls Signed-off-by: James Cherry --- liberty/Liberty.cc | 16 ++++++---------- sdc/DisabledPorts.cc | 12 ++++-------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 181b0e42c..23774ac11 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1367,17 +1367,14 @@ LibertyCell::makeTimingArcPortMaps() LibertyPort *from = arc_set->from(); LibertyPort *to = arc_set->to(); if (from && to) { - LibertyPortPair from_to_pair(from, to); - TimingArcSetSeq &sets = port_timing_arc_set_map_[from_to_pair]; + TimingArcSetSeq &sets = port_timing_arc_set_map_[{from, to}]; sets.push_back(arc_set); } - LibertyPortPair from_pair(from, nullptr); - TimingArcSetSeq &from_sets = port_timing_arc_set_map_[from_pair]; + TimingArcSetSeq &from_sets = port_timing_arc_set_map_[{from, nullptr}]; from_sets.push_back(arc_set); - LibertyPortPair to_pair(nullptr, to); - TimingArcSetSeq &to_sets = port_timing_arc_set_map_[to_pair]; + TimingArcSetSeq &to_sets = port_timing_arc_set_map_[{nullptr, to}]; to_sets.push_back(arc_set); const TimingRole *role = arc_set->role(); @@ -1411,8 +1408,7 @@ LibertyCell::timingArcSets(const LibertyPort *from, const LibertyPort *to) const { static const TimingArcSetSeq null_set; - const LibertyPortPair port_pair(from, to); - auto itr = port_timing_arc_set_map_.find(port_pair); + auto itr = port_timing_arc_set_map_.find({from, to}); return (itr == port_timing_arc_set_map_.end()) ? null_set : itr->second; } @@ -1437,8 +1433,8 @@ LibertyCell::timingArcSetCount() const bool LibertyCell::hasTimingArcs(LibertyPort *port) const { - return port_timing_arc_set_map_.contains(LibertyPortPair(port, nullptr)) - || port_timing_arc_set_map_.contains(LibertyPortPair(nullptr, port)); + return port_timing_arc_set_map_.contains({port, nullptr}) + || port_timing_arc_set_map_.contains({nullptr, port}); } void diff --git a/sdc/DisabledPorts.cc b/sdc/DisabledPorts.cc index 99c99350f..0e736b9d8 100644 --- a/sdc/DisabledPorts.cc +++ b/sdc/DisabledPorts.cc @@ -96,18 +96,15 @@ DisabledPorts::setDisabledFromTo(LibertyPort *from, { if (from_to_ == nullptr) from_to_ = new LibertyPortPairSet; - LibertyPortPair pair(from, to); - from_to_->insert(pair); + from_to_->insert({from, to}); } void DisabledPorts::removeDisabledFromTo(LibertyPort *from, LibertyPort *to) { - if (from_to_) { - LibertyPortPair from_to(from, to); - from_to_->erase(from_to); - } + if (from_to_) + from_to_->erase({from, to}); } bool @@ -115,12 +112,11 @@ DisabledPorts::isDisabled(LibertyPort *from, LibertyPort *to, const TimingRole *role) { - LibertyPortPair from_to(from, to); // set_disable_timing instance does not disable timing checks. return (all_ && !role->isTimingCheck()) || (from_ && from_->contains(from)) || (to_ && to_->contains(to)) - || (from_to_ && from_to_->contains(from_to)); + || (from_to_ && from_to_->contains({from, to})); } //////////////////////////////////////////////////////////////// From fe0e3d0673a7c3af1de7d9e2cdd09619e04e37a6 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 13:50:22 -0700 Subject: [PATCH 058/181] const Signed-off-by: James Cherry --- include/sta/Sta.hh | 2 +- search/Sta.cc | 2 +- spice/WritePathSpice.cc | 66 ++++++++++++++++++++--------------------- spice/WritePathSpice.hh | 2 +- spice/WriteSpice.i | 2 +- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 5bc48f70a..cb23b74ef 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1383,7 +1383,7 @@ public: LibertyLibrarySeq *map_libs); LibertyCellSeq *equivCells(LibertyCell *cell); - void writePathSpice(Path *path, + void writePathSpice(const Path *path, const char *spice_filename, const char *subckt_filename, const char *lib_subckt_filename, diff --git a/search/Sta.cc b/search/Sta.cc index 0cf5b206d..574a445f5 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -6012,7 +6012,7 @@ Sta::activity(const Pin *pin, //////////////////////////////////////////////////////////////// void -Sta::writePathSpice(Path *path, +Sta::writePathSpice(const Path *path, const char *spice_filename, const char *subckt_filename, const char *lib_subckt_filename, diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index bb033724e..28b702f91 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -57,7 +57,7 @@ typedef int Stage; class WritePathSpice : public WriteSpice { public: - WritePathSpice(Path *path, + WritePathSpice(const Path *path, const char *spice_filename, const char *subckt_filename, const char *lib_subckt_filename, @@ -116,24 +116,24 @@ class WritePathSpice : public WriteSpice const Path *stageLoadPath(Stage stage); const TimingArc *stageGateArc(Stage stage); const TimingArc *stageWireArc(Stage stage); - Edge *stageGateEdge(Stage stage); - Edge *stageWireEdge(Stage stage); - Pin *stageGateInputPin(Stage stage); - Pin *stageDrvrPin(Stage stage); - LibertyPort *stageGateInputPort(Stage stage); - LibertyPort *stageDrvrPort(Stage stage); - Pin *stageLoadPin(Stage stage); + const Edge *stageGateEdge(Stage stage); + const Edge *stageWireEdge(Stage stage); + const Pin *stageGateInputPin(Stage stage); + const Pin *stageDrvrPin(Stage stage); + const LibertyPort *stageGateInputPort(Stage stage); + const LibertyPort *stageDrvrPort(Stage stage); + const Pin *stageLoadPin(Stage stage); const char *stageGateInputPinName(Stage stage); const char *stageDrvrPinName(Stage stage); const char *stageLoadPinName(Stage stage); - LibertyCell *stageLibertyCell(Stage stage); - Instance *stageInstance(Stage stage); + const LibertyCell *stageLibertyCell(Stage stage); + const Instance *stageInstance(Stage stage); float findSlew(const Path *path); float findSlew(const Path *path, const RiseFall *rf, const TimingArc *next_arc); - Path *path_; + const Path *path_; PathExpanded path_expanded_; // Input clock waveform cycles. int clk_cycle_count_; @@ -152,7 +152,7 @@ class WritePathSpice : public WriteSpice //////////////////////////////////////////////////////////////// void -writePathSpice(Path *path, +writePathSpice(const Path *path, const char *spice_filename, const char *subckt_filename, const char *lib_subckt_filename, @@ -168,7 +168,7 @@ writePathSpice(Path *path, writer.writeSpice(); } -WritePathSpice::WritePathSpice(Path *path, +WritePathSpice::WritePathSpice(const Path *path, const char *spice_filename, const char *subckt_filename, const char *lib_subckt_filename, @@ -497,8 +497,8 @@ WritePathSpice::writeGateStage(Stage stage) std::string subckt_name = "stage" + std::to_string(stage); const Instance *inst = stageInstance(stage); - LibertyPort *input_port = stageGateInputPort(stage); - LibertyPort *drvr_port = stageDrvrPort(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(), @@ -515,7 +515,7 @@ WritePathSpice::writeGateStage(Stage stage) const Path *drvr_path = stageDrvrPath(stage); const RiseFall *drvr_rf = drvr_path->transition(this); - Edge *gate_edge = stageGateEdge(stage); + const Edge *gate_edge = stageGateEdge(stage); LibertyPortLogicValues port_values; bool is_clked; @@ -579,7 +579,7 @@ WritePathSpice::findPathCellNames() path_cell_names.insert(cell->name()); } // Include side receivers. - Pin *drvr_pin = stageDrvrPin(stage); + const Pin *drvr_pin = stageDrvrPin(stage); auto pin_iter = network_->connectedPinIterator(drvr_pin); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); @@ -673,49 +673,49 @@ WritePathSpice::stageWireArc(Stage stage) return path_expanded_.path(path_index)->prevArc(this); } -Edge * +const Edge * WritePathSpice::stageGateEdge(Stage stage) { const Path *path = stageDrvrPath(stage); return path->prevEdge(this); } -Edge * +const Edge * WritePathSpice::stageWireEdge(Stage stage) { const Path *path = stageLoadPath(stage); return path->prevEdge(this); } -Pin * +const Pin * WritePathSpice::stageGateInputPin(Stage stage) { const Path *path = stageGateInputPath(stage); return path->pin(this); } -LibertyPort * +const LibertyPort * WritePathSpice::stageGateInputPort(Stage stage) { - Pin *pin = stageGateInputPin(stage); + const Pin *pin = stageGateInputPin(stage); return network_->libertyPort(pin); } -Pin * +const Pin * WritePathSpice::stageDrvrPin(Stage stage) { const Path *path = stageDrvrPath(stage); return path->pin(this); } -LibertyPort * +const LibertyPort * WritePathSpice::stageDrvrPort(Stage stage) { - Pin *pin = stageDrvrPin(stage); + const Pin *pin = stageDrvrPin(stage); return network_->libertyPort(pin); } -Pin * +const Pin * WritePathSpice::stageLoadPin(Stage stage) { const Path *path = stageLoadPath(stage); @@ -725,35 +725,35 @@ WritePathSpice::stageLoadPin(Stage stage) const char * WritePathSpice::stageGateInputPinName(Stage stage) { - Pin *pin = stageGateInputPin(stage); + const Pin *pin = stageGateInputPin(stage); return network_->pathName(pin); } const char * WritePathSpice::stageDrvrPinName(Stage stage) { - Pin *pin = stageDrvrPin(stage); + const Pin *pin = stageDrvrPin(stage); return network_->pathName(pin); } const char * WritePathSpice::stageLoadPinName(Stage stage) { - Pin *pin = stageLoadPin(stage); + const Pin *pin = stageLoadPin(stage); return network_->pathName(pin); } -Instance * +const Instance * WritePathSpice::stageInstance(Stage stage) { - Pin *pin = stageDrvrPin(stage); + const Pin *pin = stageDrvrPin(stage); return network_->instance(pin); } -LibertyCell * +const LibertyCell * WritePathSpice::stageLibertyCell(Stage stage) { - Pin *pin = stageDrvrPin(stage); + const Pin *pin = stageDrvrPin(stage); return network_->libertyPort(pin)->libertyCell(); } diff --git a/spice/WritePathSpice.hh b/spice/WritePathSpice.hh index a6d014d10..647ef542c 100644 --- a/spice/WritePathSpice.hh +++ b/spice/WritePathSpice.hh @@ -35,7 +35,7 @@ class StaState; // Write a spice deck for path. // Throws FileNotReadable, FileNotWritable, SubcktEndsMissing void -writePathSpice(Path *path, +writePathSpice(const Path *path, // Spice file written for path. const char *spice_filename, // Subckts used by path included in spice file. diff --git a/spice/WriteSpice.i b/spice/WriteSpice.i index e7adadbe2..30bf14e4f 100644 --- a/spice/WriteSpice.i +++ b/spice/WriteSpice.i @@ -33,7 +33,7 @@ %inline %{ void -write_path_spice_cmd(Path *path, +write_path_spice_cmd(const Path *path, const char *spice_filename, const char *subckt_filename, const char *lib_subckt_filename, From c7ba405a1469f23629b0f8a89da0b3a0d281ce14 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 13:59:22 -0700 Subject: [PATCH 059/181] comment Signed-off-by: James Cherry --- dcalc/DmpCeff.cc | 2 +- spice/WriteSpice.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index dd9b6721d..85b9b93ab 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -156,7 +156,7 @@ class DmpAlg : public StaState // Return values. double &vo, double &dol_dt); - // Load responce to driver waveform. + // Load response to driver waveform. void Vl(double t, // Return values. double &vl, diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 9b1bf2c39..ba4774444 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -825,7 +825,7 @@ WriteSpice::railToRailSlew(float slew, //////////////////////////////////////////////////////////////// -// Find the logic values for expression inputs to enable paths from input_port. +// Find the logic values for expression inputs to sensitize the path from input_port. void WriteSpice::gatePortValues(const Pin *input_pin, const Pin *drvr_pin, From c2c40f76b120c5b7e2ccab0728da9d0c86f822b3 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 14:03:53 -0700 Subject: [PATCH 060/181] DmpAlg Signed-off-by: James Cherry --- dcalc/DmpCeff.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 85b9b93ab..c32362f21 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -125,7 +125,7 @@ class DmpAlg : public StaState { public: DmpAlg(int nr_order, StaState *sta); - virtual ~DmpAlg(); + ~DmpAlg() override = default; virtual const char *name() = 0; // Set driver model and pi model parameters for delay calculation. virtual void init(const LibertyLibrary *library, @@ -288,8 +288,6 @@ DmpAlg::DmpAlg(int nr_order, fjac_[i] = fjac_storage_ + i * max_nr_order_; } -DmpAlg::~DmpAlg() = default; - void DmpAlg::init(const LibertyLibrary *drvr_library, const LibertyCell *drvr_cell, @@ -1645,7 +1643,8 @@ gateModelRd(const LibertyCell *cell, gate_model->gateDelay(pvt, in_slew, cap1, pocv_enabled, d1, s1); gate_model->gateDelay(pvt, in_slew, cap2, pocv_enabled, 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(delayAsFloat(d1) - delayAsFloat(d2)) + / (cap2 - cap1); return rd; } From 48511e09b1ec1ea366dd1c5683b210a7982459e3 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 14:07:40 -0700 Subject: [PATCH 061/181] override Signed-off-by: James Cherry --- include/sta/Bfs.hh | 44 +++++++++++++++++----------------- include/sta/ConcreteLibrary.hh | 4 ++-- include/sta/Graph.hh | 16 ++++++------- include/sta/Iterator.hh | 12 +++++----- include/sta/Liberty.hh | 16 ++++++------- include/sta/Path.hh | 4 ++-- liberty/LibertyReaderPvt.hh | 4 ++-- network/ConcreteNetwork.cc | 40 +++++++++++++++---------------- network/Network.cc | 8 +++---- search/PathEnum.hh | 4 ++-- verilog/VerilogReader.cc | 20 ++++++++-------- 11 files changed, 86 insertions(+), 86 deletions(-) diff --git a/include/sta/Bfs.hh b/include/sta/Bfs.hh index 9b6396248..5fa335c0e 100644 --- a/include/sta/Bfs.hh +++ b/include/sta/Bfs.hh @@ -77,9 +77,9 @@ public: void remove(Vertex *vertex); void reportEntries() const; - virtual bool hasNext(); + bool hasNext() override; bool hasNext(Level to_level); - virtual Vertex *next(); + Vertex *next() override; // Apply visitor to all vertices in the queue in level order. // Returns the number of vertices that are visited. @@ -131,19 +131,19 @@ public: SearchPred *search_pred, StaState *sta); virtual ~BfsFwdIterator(); - virtual void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred); - virtual void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, - const Mode *mode); + void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred) override; + void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + const Mode *mode) override; using BfsIterator::enqueueAdjacentVertices; protected: - virtual bool levelLessOrEqual(Level level1, - Level level2) const; - virtual bool levelLess(Level level1, - Level level2) const; - virtual void incrLevel(Level &level) const; + bool levelLessOrEqual(Level level1, + Level level2) const override; + bool levelLess(Level level1, + Level level2) const override; + void incrLevel(Level &level) const override; }; class BfsBkwdIterator : public BfsIterator @@ -153,19 +153,19 @@ public: SearchPred *search_pred, StaState *sta); virtual ~BfsBkwdIterator(); - virtual void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred); - virtual void enqueueAdjacentVertices(Vertex *vertex, - SearchPred *search_pred, - const Mode *mode); + void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred) override; + void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + const Mode *mode) override; using BfsIterator::enqueueAdjacentVertices; protected: - virtual bool levelLessOrEqual(Level level1, - Level level2) const; - virtual bool levelLess(Level level1, - Level level2) const; - virtual void incrLevel(Level &level) const; + bool levelLessOrEqual(Level level1, + Level level2) const override; + bool levelLess(Level level1, + Level level2) const override; + void incrLevel(Level &level) const override; }; } // namespace diff --git a/include/sta/ConcreteLibrary.hh b/include/sta/ConcreteLibrary.hh index 48c4db06b..69e60dcea 100644 --- a/include/sta/ConcreteLibrary.hh +++ b/include/sta/ConcreteLibrary.hh @@ -264,8 +264,8 @@ class ConcreteCellPortBitIterator : public Iterator { public: ConcreteCellPortBitIterator(const ConcreteCell *cell); - virtual bool hasNext(); - virtual ConcretePort *next(); + bool hasNext() override; + ConcretePort *next() override; private: void findNext(); diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 286258542..7d9557b40 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -427,8 +427,8 @@ class VertexIterator : public Iterator { public: VertexIterator(Graph *graph); - virtual bool hasNext() { return vertex_ || bidir_vertex_; } - virtual Vertex *next(); + bool hasNext() override { return vertex_ || bidir_vertex_; } + Vertex *next() override; private: bool findNextPin(); @@ -450,8 +450,8 @@ public: const Graph *graph); VertexInEdgeIterator(VertexId vertex_id, const Graph *graph); - bool hasNext() { return (next_ != nullptr); } - Edge *next(); + bool hasNext() override { return (next_ != nullptr); } + Edge *next() override; private: Edge *next_; @@ -463,8 +463,8 @@ class VertexOutEdgeIterator : public VertexEdgeIterator public: VertexOutEdgeIterator(Vertex *vertex, const Graph *graph); - bool hasNext() { return (next_ != nullptr); } - Edge *next(); + bool hasNext() override { return (next_ != nullptr); } + Edge *next() override; private: Edge *next_; @@ -478,8 +478,8 @@ public: EdgesThruHierPinIterator(const Pin *hpin, Network *network, Graph *graph); - virtual bool hasNext(); - virtual Edge *next(); + bool hasNext() override; + Edge *next() override; private: EdgeSet edges_; diff --git a/include/sta/Iterator.hh b/include/sta/Iterator.hh index eade8d31c..06416b236 100644 --- a/include/sta/Iterator.hh +++ b/include/sta/Iterator.hh @@ -56,8 +56,8 @@ public: { } - bool hasNext() { return seq_ && itr_ != seq_->end(); } - OBJ_TYPE next() { return *itr_++; } + bool hasNext() override { return seq_ && itr_ != seq_->end(); } + OBJ_TYPE next() override { return *itr_++; } protected: const VECTOR_TYPE *seq_; @@ -80,8 +80,8 @@ public: { } - bool hasNext() { return map_ && itr_ != map_->end(); } - OBJ_TYPE next() { + bool hasNext() override { return map_ && itr_ != map_->end(); } + OBJ_TYPE next() override { OBJ_TYPE next = itr_->second; itr_++; return next; @@ -108,8 +108,8 @@ public: { } - bool hasNext() { return set_ && itr_ != set_->end(); } - OBJ_TYPE next() { return *itr_++; } + bool hasNext() override { return set_ && itr_ != set_->end(); } + OBJ_TYPE next() override { return *itr_++; } protected: const SET_TYPE *set_; diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 3f096a812..981b2a26e 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -458,8 +458,8 @@ class LibertyCellIterator : public Iterator { public: LibertyCellIterator(const LibertyLibrary *library); - bool hasNext(); - LibertyCell *next(); + bool hasNext() override; + LibertyCell *next() override; private: ConcreteLibraryCellIterator iter_; @@ -715,8 +715,8 @@ class LibertyCellPortIterator : public Iterator { public: LibertyCellPortIterator(const LibertyCell *cell); - bool hasNext(); - LibertyPort *next(); + bool hasNext() override; + LibertyPort *next() override; private: ConcreteCellPortIterator iter_; @@ -727,8 +727,8 @@ class LibertyCellPortBitIterator : public Iterator public: LibertyCellPortBitIterator(const LibertyCell *cell); virtual ~LibertyCellPortBitIterator(); - bool hasNext(); - LibertyPort *next(); + bool hasNext() override; + LibertyPort *next() override; private: ConcreteCellPortBitIterator *iter_; @@ -980,8 +980,8 @@ class LibertyPortMemberIterator : public Iterator public: LibertyPortMemberIterator(const LibertyPort *port); virtual ~LibertyPortMemberIterator(); - virtual bool hasNext(); - virtual LibertyPort *next(); + bool hasNext() override; + LibertyPort *next() override; private: ConcretePortMemberIterator *iter_; diff --git a/include/sta/Path.hh b/include/sta/Path.hh index bf938249e..c7fcd081c 100644 --- a/include/sta/Path.hh +++ b/include/sta/Path.hh @@ -204,8 +204,8 @@ public: const MinMax *min_max, const StaState *sta); virtual ~VertexPathIterator(); - virtual bool hasNext(); - virtual Path *next(); + bool hasNext() override; + Path *next() override; private: void findNext(); diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 18c5bc5fe..bbfabaf8d 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -517,8 +517,8 @@ public: LibertyReader *visitor, int line); ~PortNameBitIterator(); - virtual bool hasNext(); - virtual LibertyPort *next(); + bool hasNext() override; + LibertyPort *next() override; unsigned size() const { return size_; } protected: diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index 22fd122db..a6a0f65d2 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -62,8 +62,8 @@ class ConcreteInstanceChildIterator : public InstanceChildIterator { public: ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map); - bool hasNext(); - Instance *next(); + bool hasNext() override; + Instance *next() override; private: ConcreteInstanceChildMap *map_; @@ -96,8 +96,8 @@ class ConcreteInstanceNetIterator : public InstanceNetIterator { public: ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets); - bool hasNext(); - Net *next(); + bool hasNext() override; + Net *next() override; private: void findNext(); @@ -152,8 +152,8 @@ class ConcreteInstancePinIterator : public InstancePinIterator public: ConcreteInstancePinIterator(const ConcreteInstance *inst, int pin_count); - bool hasNext(); - Pin *next(); + bool hasNext() override; + Pin *next() override; private: void findNext(); @@ -206,8 +206,8 @@ class ConcreteNetPinIterator : public NetPinIterator { public: ConcreteNetPinIterator(const ConcreteNet *net); - bool hasNext(); - Pin *next(); + bool hasNext() override; + Pin *next() override; private: ConcretePin *next_; @@ -238,8 +238,8 @@ class ConcreteNetTermIterator : public NetTermIterator { public: ConcreteNetTermIterator(const ConcreteNet *net); - bool hasNext(); - Term *next(); + bool hasNext() override; + Term *next() override; private: ConcreteTerm *next_; @@ -322,8 +322,8 @@ class ConcreteLibraryIterator1 : public Iterator { public: ConcreteLibraryIterator1(const ConcreteLibrarySeq &libs); - virtual bool hasNext(); - virtual Library *next(); + bool hasNext() override; + Library *next() override; private: const ConcreteLibrarySeq &libs_; @@ -361,8 +361,8 @@ class ConcreteLibertyLibraryIterator : public Iterator public: ConcreteLibertyLibraryIterator(const ConcreteNetwork *network); virtual ~ConcreteLibertyLibraryIterator(); - virtual bool hasNext(); - virtual LibertyLibrary *next(); + bool hasNext() override; + LibertyLibrary *next() override; private: void findNext(); @@ -719,8 +719,8 @@ class ConcreteCellPortIterator1 : public CellPortIterator public: ConcreteCellPortIterator1(const ConcreteCell *cell); ~ConcreteCellPortIterator1(); - virtual bool hasNext() { return iter_->hasNext(); } - virtual Port *next(); + bool hasNext() override { return iter_->hasNext(); } + Port *next() override; private: ConcreteCellPortIterator *iter_; @@ -756,8 +756,8 @@ class ConcreteCellPortBitIterator1 : public CellPortIterator public: ConcreteCellPortBitIterator1(const ConcreteCell *cell); ~ConcreteCellPortBitIterator1(); - virtual bool hasNext() { return iter_->hasNext(); } - virtual Port *next(); + bool hasNext() override { return iter_->hasNext(); } + Port *next() override; private: ConcreteCellPortBitIterator *iter_; @@ -903,8 +903,8 @@ class ConcretePortMemberIterator1 : public PortMemberIterator public: ConcretePortMemberIterator1(const ConcretePort *port); ~ConcretePortMemberIterator1(); - virtual bool hasNext(); - virtual Port *next(); + bool hasNext() override; + Port *next() override; private: ConcretePortMemberIterator *iter_; diff --git a/network/Network.cc b/network/Network.cc index 78e13b1e6..ec77fa7a3 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -1221,8 +1221,8 @@ class LeafInstanceIterator1 : public LeafInstanceIterator public: LeafInstanceIterator1(const Instance *inst, const Network *network); - bool hasNext() { return next_; } - Instance *next(); + bool hasNext() override { return next_; } + Instance *next() override; private: void nextInst(); @@ -1366,8 +1366,8 @@ class ConnectedPinIterator1 : public ConnectedPinIterator public: ConnectedPinIterator1(PinSet *pins); virtual ~ConnectedPinIterator1(); - virtual bool hasNext(); - virtual const Pin *next(); + bool hasNext() override; + const Pin *next() override; protected: PinSet *pins_; diff --git a/search/PathEnum.hh b/search/PathEnum.hh index f2f68cd81..72b2ddb8d 100644 --- a/search/PathEnum.hh +++ b/search/PathEnum.hh @@ -68,8 +68,8 @@ public: // Insert path ends that are enumerated in slack/arrival order. void insert(PathEnd *path_end); virtual ~PathEnum(); - virtual bool hasNext(); - virtual PathEnd *next(); + bool hasNext() override; + PathEnd *next() override; private: void makeDiversions(PathEnd *path_end, diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index e101d0f91..72a49a4b0 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -1080,8 +1080,8 @@ VerilogAssign::~VerilogAssign() class VerilogNullNetNameIterator : public VerilogNetNameIterator { public: - virtual bool hasNext() { return false; } - virtual const std::string &next(); + bool hasNext() override { return false; } + const std::string &next() override; }; const std::string & @@ -1095,8 +1095,8 @@ class VerilogOneNetNameIterator : public VerilogNetNameIterator { public: VerilogOneNetNameIterator(const std::string &name); - virtual bool hasNext(); - virtual const std::string &next(); + bool hasNext() override; + const std::string &next() override; protected: std::string name_; @@ -1128,8 +1128,8 @@ class VerilogBusNetNameIterator : public VerilogNetNameIterator VerilogBusNetNameIterator(const std::string bus_name, int from_index, int to_index); - virtual bool hasNext(); - virtual const std::string &next(); + bool hasNext() override; + const std::string &next() override; protected: const std::string bus_name_; @@ -1182,8 +1182,8 @@ class VerilogConstantNetNameIterator : public VerilogNetNameIterator VerilogConstantNetNameIterator(VerilogConstantValue *value, const std::string &zero, const std::string &one); - virtual bool hasNext(); - virtual const std::string &next(); + bool hasNext() override; + const std::string &next() override; private: VerilogConstantValue *value_; @@ -1222,8 +1222,8 @@ class VerilogNetConcatNameIterator : public VerilogNetNameIterator VerilogModule *module, VerilogReader *reader); virtual ~VerilogNetConcatNameIterator(); - virtual bool hasNext(); - virtual const std::string &next(); + bool hasNext() override; + const std::string &next() override; private: VerilogModule *module_; From c7d48679c99bc812b36c22498703a17ca09484c1 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 14:27:49 -0700 Subject: [PATCH 062/181] include/sta/WriteSdc.hh -> sdc/WriteSdc.hh Signed-off-by: James Cherry --- {include/sta => sdc}/WriteSdc.hh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {include/sta => sdc}/WriteSdc.hh (100%) diff --git a/include/sta/WriteSdc.hh b/sdc/WriteSdc.hh similarity index 100% rename from include/sta/WriteSdc.hh rename to sdc/WriteSdc.hh From bf8a61fe080e3599a38d6960ba800f780483acf7 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 14:27:56 -0700 Subject: [PATCH 063/181] PinPair Signed-off-by: James Cherry --- sdc/ExceptionPath.cc | 6 ++---- sdc/Sdc.cc | 21 +++++++-------------- search/Sta.cc | 2 +- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 25ba0d784..8849d9c57 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -2427,8 +2427,7 @@ void InsertPinPairsThru::visit(const Pin *drvr, const Pin *load) { - PinPair pair(drvr, load); - pairs_->insert(pair); + pairs_->insert({drvr, load}); } static void @@ -2475,8 +2474,7 @@ void DeletePinPairsThru::visit(const Pin *drvr, const Pin *load) { - PinPair pair(drvr, load); - pairs_->erase(pair); + pairs_->erase({drvr, load}); } static void diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 12bcd471c..7517020d1 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -1346,8 +1346,7 @@ bool FindClkHpinDisables::drvrLoadExists(const Pin *drvr, const Pin *load) { - PinPair probe(drvr, load); - return drvr_loads_.contains(probe); + return drvr_loads_.contains({drvr, load}); } void @@ -3506,24 +3505,21 @@ void Sdc::disableWire(const Pin *from, const Pin *to) { - PinPair pair(from, to); - disabled_wire_edges_.insert(pair); + disabled_wire_edges_.insert({from, to}); } void Sdc::removeDisableWire(Pin *from, Pin *to) { - PinPair probe(from, to); - disabled_wire_edges_.erase(probe); + disabled_wire_edges_.erase({from, to}); } bool Sdc::isDisabledWire(const Pin *from, const Pin *to) const { - PinPair pair(from, to); - return disabled_wire_edges_.contains(pair); + return disabled_wire_edges_.contains({from, to}); } void @@ -3583,8 +3579,7 @@ void DisableEdgesThruHierPin::visit(const Pin *drvr, const Pin *load) { - PinPair pair(drvr, load); - pairs_->insert(pair); + pairs_->insert({drvr, load}); } void @@ -3625,8 +3620,7 @@ void RemoveDisableEdgesThruHierPin::visit(const Pin *drvr, const Pin *load) { - PinPair pair(drvr, load); - pairs_->erase(pair); + pairs_->erase({drvr, load}); } void @@ -3659,8 +3653,7 @@ Sdc::isDisabled(const Instance *inst, { if (role == TimingRole::wire()) { // Hierarchical thru pin disables. - PinPair pair(from_pin, to_pin); - return disabled_wire_edges_.contains(pair); + return disabled_wire_edges_.contains({from_pin, to_pin}); } else { LibertyCell *cell = network_->libertyCell(inst); diff --git a/search/Sta.cc b/search/Sta.cc index 574a445f5..98fdbbf3e 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -52,7 +52,7 @@ #include "Sdc.hh" #include "Mode.hh" #include "Variables.hh" -#include "WriteSdc.hh" +#include "sdc/WriteSdc.hh" #include "ExceptionPath.hh" #include "Parasitics.hh" #include "parasitics/SpefReader.hh" From d7905a38861ea0583395c0908244ef75eb4f20c4 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 14:41:22 -0700 Subject: [PATCH 064/181] delay_calcs use std::string Signed-off-by: James Cherry --- dcalc/DelayCalc.cc | 28 +++++++++++++--------------- dcalc/DelayCalc.i | 2 +- include/sta/DelayCalc.hh | 12 +++++++----- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index bde9248c9..1e27b20eb 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -25,6 +25,7 @@ #include "DelayCalc.hh" #include +#include #include "ContainerHelpers.hh" #include "StringUtil.hh" @@ -37,9 +38,9 @@ namespace sta { -typedef std::map DelayCalcMap; +typedef std::map DelayCalcMap; -static DelayCalcMap *delay_calcs = nullptr; +static DelayCalcMap delay_calcs; void registerDelayCalcs() @@ -54,26 +55,23 @@ registerDelayCalcs() } void -registerDelayCalc(const char *name, +registerDelayCalc(const std::string &name, MakeArcDelayCalc maker) { - if (delay_calcs == nullptr) - delay_calcs = new DelayCalcMap; - (*delay_calcs)[name] = maker; + delay_calcs[name] = maker; } void deleteDelayCalcs() { - delete delay_calcs; - delay_calcs = nullptr; + delay_calcs.clear(); } ArcDelayCalc * -makeDelayCalc(const char *name, +makeDelayCalc(const std::string &name, StaState *sta) { - MakeArcDelayCalc maker = findKey(delay_calcs, name); + MakeArcDelayCalc maker = findKey(&delay_calcs, name); if (maker) return maker(sta); else @@ -81,16 +79,16 @@ makeDelayCalc(const char *name, } bool -isDelayCalcName(const char *name) +isDelayCalcName(const std::string &name) { - return delay_calcs->contains(name); + return delay_calcs.contains(name); } -StringSeq +StdStringSeq delayCalcNames() { - StringSeq names; - for (const auto [name, make_dcalc] : *delay_calcs) + StdStringSeq names; + for (const auto &[name, make_dcalc] : delay_calcs) names.push_back(name); return names; } diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index a0bbaea66..62e4c24f0 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -36,7 +36,7 @@ %inline %{ -StringSeq +StdStringSeq delay_calc_names() { return sta::delayCalcNames(); diff --git a/include/sta/DelayCalc.hh b/include/sta/DelayCalc.hh index dbadf0b6c..091b87997 100644 --- a/include/sta/DelayCalc.hh +++ b/include/sta/DelayCalc.hh @@ -24,7 +24,9 @@ #pragma once -#include "StringSeq.hh" +#include + +#include "StringUtil.hh" namespace sta { @@ -38,18 +40,18 @@ void registerDelayCalcs(); // Register a delay calculator for the set_delay_calc command. void -registerDelayCalc(const char *name, +registerDelayCalc(const std::string &name, MakeArcDelayCalc maker); bool -isDelayCalcName(const char *name); -StringSeq +isDelayCalcName(const std::string &name); +StdStringSeq delayCalcNames(); void deleteDelayCalcs(); // Make a registered delay calculator by name. ArcDelayCalc * -makeDelayCalc(const char *name, +makeDelayCalc(const std::string &name, StaState *sta); } // namespace From ab9951235171998362bb1d085b94a1405263aab4 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 14:48:48 -0700 Subject: [PATCH 065/181] spef reader use std::string Signed-off-by: James Cherry --- parasitics/SpefParse.yy | 13 ++++++++----- parasitics/SpefReader.cc | 11 +++-------- parasitics/SpefReaderPvt.hh | 6 +++--- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/parasitics/SpefParse.yy b/parasitics/SpefParse.yy index 39ad246f2..260a212cf 100755 --- a/parasitics/SpefParse.yy +++ b/parasitics/SpefParse.yy @@ -27,7 +27,7 @@ #include "Report.hh" #include "StringUtil.hh" -#include "StringSeq.hh" +#include "StringUtil.hh" #include "parasitics/SpefReaderPvt.hh" #include "parasitics/SpefScanner.hh" @@ -62,7 +62,7 @@ sta::SpefParse::error(const location_type &loc, char *string; int integer; float number; - sta::StringSeq *string_seq; + sta::StdStringSeq *std_string_seq; sta::PortDirection *port_dir; sta::SpefRspfPi *pi; sta::SpefTriple *triple; @@ -105,7 +105,7 @@ sta::SpefParse::error(const location_type &loc, %type hchar suffix_bus_delim prefix_bus_delim -%type qstrings +%type qstrings %type direction %type par_value total_cap @@ -220,11 +220,14 @@ design_flow: qstrings: QSTRING - { $$ = new sta::StringSeq; + { $$ = new sta::StdStringSeq; $$->push_back($1); + sta::stringDelete($1); } | qstrings QSTRING - { $$->push_back($2); } + { $$->push_back($2); + sta::stringDelete($2); + } ; hierarchy_div_def: diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 7d0751f80..16f7a6dc6 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -92,7 +92,6 @@ SpefReader::SpefReader(const std::string &filename, cap_scale_(1.0), res_scale_(1.0), induct_scale_(1.0), - design_flow_(nullptr), parasitics_(parasitics), parasitic_(nullptr) { @@ -101,11 +100,6 @@ SpefReader::SpefReader(const std::string &filename, SpefReader::~SpefReader() { - if (design_flow_) { - deleteContents(design_flow_); - delete design_flow_; - design_flow_ = nullptr; - } } bool @@ -297,9 +291,10 @@ SpefReader::portDirection(char *spef_dir) } void -SpefReader::setDesignFlow(StringSeq *flow) +SpefReader::setDesignFlow(StdStringSeq *flow) { - design_flow_ = flow; + design_flow_ = std::move(*flow); + delete flow; } Pin * diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index f96480c4e..de0b80435 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -27,7 +27,7 @@ #include #include "Zlib.hh" -#include "StringSeq.hh" +#include "StringUtil.hh" #include "NetworkClass.hh" #include "ParasiticsClass.hh" #include "StaState.hh" @@ -82,7 +82,7 @@ public: void makeNameMapEntry(const char *index, const char *name); const char *nameMapLookup(const char *index); - void setDesignFlow(StringSeq *flow_keys); + void setDesignFlow(StdStringSeq *flow_keys); Pin *findPin(char *name); Net *findNet(const char *name); void rspfBegin(Net *net, @@ -139,7 +139,7 @@ private: float res_scale_; float induct_scale_; SpefNameMap name_map_; - StringSeq *design_flow_; + StdStringSeq design_flow_; Parasitics *parasitics_; Parasitic *parasitic_; }; From 8bd938d840bcaa3012307a28342358ece5a9f276 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 15:02:14 -0700 Subject: [PATCH 066/181] CheckError use std::string Signed-off-by: James Cherry --- include/sta/Sta.hh | 2 +- liberty/LibertyReaderPvt.hh | 1 - search/CheckTiming.cc | 28 +++++++++------------------- search/CheckTiming.hh | 4 ++-- tcl/StaTclTypes.i | 7 +++---- 5 files changed, 15 insertions(+), 27 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index cb23b74ef..483b3a00e 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -79,7 +79,7 @@ class GraphLoop; using ModeNameMap = std::map; using SceneNameMap = std::map; using SlowDrvrIterator = Iterator; -using CheckError = StringSeq; +using CheckError = StdStringSeq; using CheckErrorSeq = std::vector; enum class CmdNamespace { sta, sdc }; using ParasiticsNameMap = std::map; diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index bbfabaf8d..ec8facff0 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -31,7 +31,6 @@ #include #include -#include "StringSeq.hh" #include "StringUtil.hh" #include "MinMax.hh" #include "NetworkClass.hh" diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc index f05de24e8..965a8d150 100644 --- a/search/CheckTiming.cc +++ b/search/CheckTiming.cc @@ -24,7 +24,6 @@ #include "CheckTiming.hh" -#include "ContainerHelpers.hh" #include "Error.hh" #include "TimingRole.hh" #include "Network.hh" @@ -64,7 +63,6 @@ void CheckTiming::deleteErrors() { for (CheckError *error : errors_) { - deleteContents(error); delete error; } } @@ -204,25 +202,23 @@ CheckTiming::checkLoops() errorMsgSubst("Warning: There %is %d combinational loop%s in the design.", loop_count, error_msg); CheckError *error = new CheckError; - error->push_back(stringCopy(error_msg.c_str())); + error->push_back(error_msg); for (GraphLoop *loop : loops) { if (loop->isCombinational()) { Edge *last_edge = nullptr; for (Edge *edge : *loop->edges()) { Pin *pin = edge->from(graph_)->pin(); - const char *pin_name = stringCopy(sdc_network_->pathName(pin)); - error->push_back(pin_name); + error->push_back(sdc_network_->pathName(pin)); last_edge = edge; } if (last_edge) { - error->push_back(stringCopy("| loop cut point")); + error->push_back("| loop cut point"); const Pin *pin = last_edge->to(graph_)->pin(); - const char *pin_name = stringCopy(sdc_network_->pathName(pin)); - error->push_back(pin_name); + error->push_back(sdc_network_->pathName(pin)); // Separator between loops. - error->push_back(stringCopy("--------------------------------")); + error->push_back("--------------------------------"); } } } @@ -362,15 +358,12 @@ CheckTiming::pushPinErrors(const char *msg, CheckError *error = new CheckError; std::string error_msg; errorMsgSubst(msg, pins.size(), error_msg); - // Copy the error strings because the error deletes them when it - // is deleted. - error->push_back(stringCopy(error_msg.c_str())); + error->push_back(error_msg); // Sort the error pins so the output is independent of the order // the the errors are discovered. PinSeq pins1 = sortByPathName(&pins, network_); for (const Pin *pin : pins1) { - const char *pin_name = stringCopy(sdc_network_->pathName(pin)); - error->push_back(pin_name); + error->push_back(sdc_network_->pathName(pin)); } errors_.push_back(error); } @@ -384,15 +377,12 @@ CheckTiming::pushClkErrors(const char *msg, CheckError *error = new CheckError; std::string error_msg; errorMsgSubst(msg, clks.size(), error_msg); - // Copy the error strings because the error deletes them when it - // is deleted. - error->push_back(stringCopy(error_msg.c_str())); + error->push_back(error_msg); // Sort the error clks so the output is independent of the order // the the errors are discovered. ClockSeq clks1 = sortByName(&clks); for (const Clock *clk : clks1) { - const char *clk_name = stringCopy(clk->name()); - error->push_back(clk_name); + error->push_back(clk->name()); } errors_.push_back(error); } diff --git a/search/CheckTiming.hh b/search/CheckTiming.hh index 26edc150b..f2c821c74 100644 --- a/search/CheckTiming.hh +++ b/search/CheckTiming.hh @@ -26,7 +26,7 @@ #include -#include "StringSeq.hh" +#include "StringUtil.hh" #include "NetworkClass.hh" #include "GraphClass.hh" #include "SdcClass.hh" @@ -36,7 +36,7 @@ namespace sta { class ClkNetwork; -using CheckError = StringSeq; +using CheckError = StdStringSeq; using CheckErrorSeq = std::vector; class CheckTiming : public StaState diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 270aad83b..b1c7b288c 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -1137,10 +1137,9 @@ using namespace sta; CheckErrorSeq *check_errors = $1; for (CheckError *error : *check_errors) { Tcl_Obj *string_list = Tcl_NewListObj(0, nullptr); - for (const char *str : *error) { - size_t str_len = strlen(str); - Tcl_Obj *obj = Tcl_NewStringObj(const_cast(str), - static_cast(str_len)); + for (const std::string &str : *error) { + Tcl_Obj *obj = Tcl_NewStringObj(str.c_str(), + static_cast(str.size())); Tcl_ListObjAppendElement(interp, string_list, obj); } Tcl_ListObjAppendElement(interp, error_list, string_list); From 0c36caa1827252865d8af4d839530cb5e54a736f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 15:16:00 -0700 Subject: [PATCH 067/181] rm StdString Signed-off-by: James Cherry --- CMakeLists.txt | 1 - graph/Graph.i | 4 ++-- include/sta/Scene.hh | 1 - include/sta/Sta.hh | 5 ++--- include/sta/StringSeq.hh | 38 ----------------------------------- include/sta/TclTypeHelpers.hh | 6 ------ sdc/Sdc.i | 4 ++-- search/ReportPath.cc | 6 +++--- search/ReportPath.hh | 4 ++-- search/Search.i | 4 ++-- search/Sta.cc | 8 ++++---- tcl/StaTclTypes.i | 3 +-- tcl/TclTypeHelpers.cc | 20 ------------------ util/StringSeq.cc | 36 --------------------------------- verilog/VerilogReaderPvt.hh | 1 - 15 files changed, 18 insertions(+), 123 deletions(-) delete mode 100644 include/sta/StringSeq.hh delete mode 100644 util/StringSeq.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 973cfb0da..4e548733f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,7 +234,6 @@ set(STA_SOURCE util/RiseFallMinMaxDelay.cc util/RiseFallValues.cc util/Stats.cc - util/StringSeq.cc util/StringSet.cc util/StringUtil.cc util/Transition.cc diff --git a/graph/Graph.i b/graph/Graph.i index 289ca3ba9..4eab5270b 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -229,12 +229,12 @@ arc_delays(TimingArc *arc) return delays; } -StringSeq +StdStringSeq arc_delay_strings(TimingArc *arc, int digits) { Sta *sta = Sta::sta(); - StringSeq delays; + StdStringSeq 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), diff --git a/include/sta/Scene.hh b/include/sta/Scene.hh index 9b2e61a0b..289863772 100644 --- a/include/sta/Scene.hh +++ b/include/sta/Scene.hh @@ -28,7 +28,6 @@ #include #include -#include "StringSeq.hh" #include "GraphClass.hh" #include "SearchClass.hh" diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 483b3a00e..f8fe4a950 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -29,7 +29,6 @@ #include #include -#include "StringSeq.hh" #include "StringUtil.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" @@ -126,7 +125,7 @@ public: void setThreadCount(int thread_count); // define_corners compatibility. - void makeScenes(StringSeq *scene_names); + void makeScenes(StdStringSeq *scene_names); void makeScene(const std::string &name, const std::string &mode_name, const StdStringSeq &liberty_min_files, @@ -976,7 +975,7 @@ public: bool clk_gating_setup, bool clk_gating_hold); void setReportPathFormat(ReportPathFormat format); - void setReportPathFieldOrder(StringSeq *field_names); + void setReportPathFieldOrder(StdStringSeq *field_names); void setReportPathFields(bool report_input_pin, bool report_hier_pins, bool report_net, diff --git a/include/sta/StringSeq.hh b/include/sta/StringSeq.hh deleted file mode 100644 index 9821b7b20..000000000 --- a/include/sta/StringSeq.hh +++ /dev/null @@ -1,38 +0,0 @@ -// 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 - -#include "StringUtil.hh" - -namespace sta { - -using StringSeq = std::vector; - -void -deleteContents(StringSeq *strings); - -} // namespace diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index e521c4a93..33d837d34 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -24,7 +24,6 @@ #include "ArcDelayCalc.hh" #include "StringSet.hh" -#include "StringSeq.hh" #include @@ -37,11 +36,6 @@ namespace sta { StringSet * tclListSetConstChar(Tcl_Obj *const source, Tcl_Interp *interp); - -StringSeq * -tclListSeqConstChar(Tcl_Obj *const source, - Tcl_Interp *interp); - StdStringSeq tclListSeqStdString(Tcl_Obj *const source, Tcl_Interp *interp); diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 2bca70b01..328abb32e 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -1580,12 +1580,12 @@ filter_timing_arcs(const char *property, //////////////////////////////////////////////////////////////// -StringSeq +StdStringSeq group_path_names() { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); - StringSeq pg_names; + StdStringSeq pg_names; for (auto const& [name, group] : sdc->groupPaths()) pg_names.push_back(name); return pg_names; diff --git a/search/ReportPath.cc b/search/ReportPath.cc index ad9b9f11a..4029d09da 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -211,15 +211,15 @@ ReportPath::findField(const char *name) const } void -ReportPath::setReportFieldOrder(StringSeq *field_names) +ReportPath::setReportFieldOrder(StdStringSeq *field_names) { // Disable all fields. for (ReportField *field : fields_) field->setEnabled(false); ReportFieldSeq next_fields; - for (const char *field_name : *field_names) { - ReportField *field = findField(field_name); + for (const std::string &field_name : *field_names) { + ReportField *field = findField(field_name.c_str()); if (field) { next_fields.push_back(field); field->setEnabled(true); diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 39c8b1a55..8c61e356c 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -27,7 +27,7 @@ #include #include -#include "StringSeq.hh" +#include "StringUtil.hh" #include "SearchClass.hh" #include "PathEnd.hh" #include "CheckMinPulseWidths.hh" @@ -49,7 +49,7 @@ public: virtual ~ReportPath(); ReportPathFormat pathFormat() const { return format_; } void setPathFormat(ReportPathFormat format); - void setReportFieldOrder(StringSeq *field_names); + void setReportFieldOrder(StdStringSeq *field_names); void setReportFields(bool report_input_pin, bool report_hier_pins, bool report_net, diff --git a/search/Search.i b/search/Search.i index 8ffff0ff0..f6ec25b46 100644 --- a/search/Search.i +++ b/search/Search.i @@ -395,7 +395,7 @@ set_report_path_format(ReportPathFormat format) } void -set_report_path_field_order(StringSeq *field_names) +set_report_path_field_order(StdStringSeq *field_names) { Sta::sta()->setReportPathFieldOrder(field_names); delete field_names; @@ -753,7 +753,7 @@ define_scene_cmd(const char *name, } void -define_scenes_cmd(StringSeq *scene_names) +define_scenes_cmd(StdStringSeq *scene_names) { Sta *sta = Sta::sta(); sta->makeScenes(scene_names); diff --git a/search/Sta.cc b/search/Sta.cc index 98fdbbf3e..9bde46bc3 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2450,7 +2450,7 @@ void Sta::makeDefaultScene() { const char *name = "default"; - StringSeq scene_names; + StdStringSeq scene_names; scene_names.push_back(name); Parasitics *parasitics = makeConcreteParasitics(name, ""); @@ -2468,7 +2468,7 @@ Sta::makeDefaultScene() // define_corners (before read_liberty). void -Sta::makeScenes(StringSeq *scene_names) +Sta::makeScenes(StdStringSeq *scene_names) { if (scene_names->size() > scene_count_max) report_->error(1553, "maximum scene count exceeded"); @@ -2478,7 +2478,7 @@ Sta::makeScenes(StringSeq *scene_names) mode->clear(); deleteScenes(); - for (const char *name : *scene_names) + for (const std::string &name : *scene_names) makeScene(name, mode, parasitics); cmd_scene_ = scenes_[0]; @@ -2729,7 +2729,7 @@ Sta::setReportPathFormat(ReportPathFormat format) } void -Sta::setReportPathFieldOrder(StringSeq *field_names) +Sta::setReportPathFieldOrder(StdStringSeq *field_names) { report_path_->setReportFieldOrder(field_names); } diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index b1c7b288c..7d47632d5 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -28,7 +28,6 @@ #include "Machine.hh" #include "StringUtil.hh" #include "StringSet.hh" -#include "StringSeq.hh" #include "PatternMatch.hh" #include "Network.hh" #include "Liberty.hh" @@ -306,7 +305,7 @@ using namespace sta; } %typemap(in) StdStringSeq* { - $1 = tclListSeqStdString($input, interp); + $1 = tclListSeqStdStringPtr($input, interp); } %typemap(out) StringSeq* { diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index c5cc219dd..adf21f623 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -50,26 +50,6 @@ tclListSetConstChar(Tcl_Obj *const source, return nullptr; } -StringSeq * -tclListSeqConstChar(Tcl_Obj *const source, - Tcl_Interp *interp) -{ - Tcl_Size argc; - Tcl_Obj **argv; - - if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { - StringSeq *seq = new StringSeq; - for (int i = 0; i < argc; i++) { - int length; - const char *str = Tcl_GetStringFromObj(argv[i], &length); - seq->push_back(str); - } - return seq; - } - else - return nullptr; -} - StdStringSeq tclListSeqStdString(Tcl_Obj *const source, Tcl_Interp *interp) diff --git a/util/StringSeq.cc b/util/StringSeq.cc deleted file mode 100644 index f5615d6c9..000000000 --- a/util/StringSeq.cc +++ /dev/null @@ -1,36 +0,0 @@ -// 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 "StringSeq.hh" - -namespace sta { - -void -deleteContents(StringSeq *strings) -{ - for (const char *string : *strings) - stringDelete(string); -} - -} // namespace diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index 6bbe8288c..9f1fdffe3 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -28,7 +28,6 @@ #include #include -#include "StringSeq.hh" #include "StringUtil.hh" namespace sta { From a1797918da0b478178e5611adc8495ee72b24993 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 15:19:27 -0700 Subject: [PATCH 068/181] Use & for StdStringSeq args Signed-off-by: James Cherry --- include/sta/Sta.hh | 4 ++-- search/ReportPath.cc | 4 ++-- search/ReportPath.hh | 2 +- search/Search.i | 6 ++---- search/Sta.cc | 8 ++++---- tcl/StaTclTypes.i | 6 +++++- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index f8fe4a950..b8212de80 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -125,7 +125,7 @@ public: void setThreadCount(int thread_count); // define_corners compatibility. - void makeScenes(StdStringSeq *scene_names); + void makeScenes(const StdStringSeq &scene_names); void makeScene(const std::string &name, const std::string &mode_name, const StdStringSeq &liberty_min_files, @@ -975,7 +975,7 @@ public: bool clk_gating_setup, bool clk_gating_hold); void setReportPathFormat(ReportPathFormat format); - void setReportPathFieldOrder(StdStringSeq *field_names); + void setReportPathFieldOrder(const StdStringSeq &field_names); void setReportPathFields(bool report_input_pin, bool report_hier_pins, bool report_net, diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 4029d09da..8c3d7b325 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -211,14 +211,14 @@ ReportPath::findField(const char *name) const } void -ReportPath::setReportFieldOrder(StdStringSeq *field_names) +ReportPath::setReportFieldOrder(const StdStringSeq &field_names) { // Disable all fields. for (ReportField *field : fields_) field->setEnabled(false); ReportFieldSeq next_fields; - for (const std::string &field_name : *field_names) { + for (const std::string &field_name : field_names) { ReportField *field = findField(field_name.c_str()); if (field) { next_fields.push_back(field); diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 8c61e356c..2d5da89cf 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -49,7 +49,7 @@ public: virtual ~ReportPath(); ReportPathFormat pathFormat() const { return format_; } void setPathFormat(ReportPathFormat format); - void setReportFieldOrder(StdStringSeq *field_names); + void setReportFieldOrder(const StdStringSeq &field_names); void setReportFields(bool report_input_pin, bool report_hier_pins, bool report_net, diff --git a/search/Search.i b/search/Search.i index f6ec25b46..b2310c881 100644 --- a/search/Search.i +++ b/search/Search.i @@ -395,10 +395,9 @@ set_report_path_format(ReportPathFormat format) } void -set_report_path_field_order(StdStringSeq *field_names) +set_report_path_field_order(const StdStringSeq &field_names) { Sta::sta()->setReportPathFieldOrder(field_names); - delete field_names; } void @@ -753,11 +752,10 @@ define_scene_cmd(const char *name, } void -define_scenes_cmd(StdStringSeq *scene_names) +define_scenes_cmd(const StdStringSeq &scene_names) { Sta *sta = Sta::sta(); sta->makeScenes(scene_names); - delete scene_names; } Scene * diff --git a/search/Sta.cc b/search/Sta.cc index 9bde46bc3..5353178e9 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2468,9 +2468,9 @@ Sta::makeDefaultScene() // define_corners (before read_liberty). void -Sta::makeScenes(StdStringSeq *scene_names) +Sta::makeScenes(const StdStringSeq &scene_names) { - if (scene_names->size() > scene_count_max) + if (scene_names.size() > scene_count_max) report_->error(1553, "maximum scene count exceeded"); Parasitics *parasitics = findParasitics("default"); Mode *mode = modes_[0]; @@ -2478,7 +2478,7 @@ Sta::makeScenes(StdStringSeq *scene_names) mode->clear(); deleteScenes(); - for (const std::string &name : *scene_names) + for (const std::string &name : scene_names) makeScene(name, mode, parasitics); cmd_scene_ = scenes_[0]; @@ -2729,7 +2729,7 @@ Sta::setReportPathFormat(ReportPathFormat format) } void -Sta::setReportPathFieldOrder(StdStringSeq *field_names) +Sta::setReportPathFieldOrder(const StdStringSeq &field_names) { report_path_->setReportFieldOrder(field_names); } diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 7d47632d5..2bcb3fa05 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -52,7 +52,6 @@ namespace sta { typedef MinMaxAll MinMaxAllNull; -typedef std::vector StdStringSeq; #if TCL_MAJOR_VERSION < 9 typedef int Tcl_Size; @@ -304,6 +303,11 @@ using namespace sta; $1 = tclListSeqStdString($input, interp); } +%typemap(in) const StdStringSeq & (StdStringSeq seq) { + seq = tclListSeqStdString($input, interp); + $1 = &seq; +} + %typemap(in) StdStringSeq* { $1 = tclListSeqStdStringPtr($input, interp); } From 7e7ab963292c39a04f51f6eda6954860130c5594 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 15:25:02 -0700 Subject: [PATCH 069/181] rm StdString Signed-off-by: James Cherry --- tcl/StaTclTypes.i | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 2bcb3fa05..7de25386a 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -291,10 +291,6 @@ using namespace sta; Tcl_SetResult(interp, nullptr, TCL_STATIC); } -%typemap(in) StringSeq* { - $1 = tclListSeqConstChar($input, interp); -} - %typemap(in) StdStringSet* { $1 = tclListSetStdString($input, interp); } @@ -312,26 +308,6 @@ using namespace sta; $1 = tclListSeqStdStringPtr($input, interp); } -%typemap(out) StringSeq* { - StringSeq *strs = $1; - Tcl_Obj *list = Tcl_NewListObj(0, nullptr); - for (const char *str : *strs) { - Tcl_Obj *obj = Tcl_NewStringObj(str, strlen(str)); - Tcl_ListObjAppendElement(interp, list, obj); - } - Tcl_SetObjResult(interp, list); -} - -%typemap(out) StringSeq { - StringSeq &strs = $1; - Tcl_Obj *list = Tcl_NewListObj(0, nullptr); - for (const char *str : strs) { - Tcl_Obj *obj = Tcl_NewStringObj(str, strlen(str)); - Tcl_ListObjAppendElement(interp, list, obj); - } - Tcl_SetObjResult(interp, list); -} - %typemap(in) StdStringSet* { $1 = tclListSetStdString($input, interp); } From 4f540792a098f704dd45bd7dacdd4b77ac826b9d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 15:39:41 -0700 Subject: [PATCH 070/181] rm StringSet Signed-off-by: James Cherry --- CMakeLists.txt | 1 - include/sta/Sdc.hh | 1 - include/sta/SearchClass.hh | 1 - include/sta/StringSet.hh | 38 ----------------------------------- include/sta/StringUtil.hh | 2 ++ include/sta/TclTypeHelpers.hh | 9 +++------ include/sta/VerilogReader.hh | 2 +- liberty/Liberty.cc | 1 - spice/WritePathSpice.hh | 1 - spice/WriteSpice.hh | 1 - tcl/StaTclTypes.i | 5 ----- tcl/TclTypeHelpers.cc | 20 ------------------ util/StringSet.cc | 36 --------------------------------- 13 files changed, 6 insertions(+), 112 deletions(-) delete mode 100644 include/sta/StringSet.hh delete mode 100644 util/StringSet.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e548733f..99a94864f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,7 +234,6 @@ set(STA_SOURCE util/RiseFallMinMaxDelay.cc util/RiseFallValues.cc util/Stats.cc - util/StringSet.cc util/StringUtil.cc util/Transition.cc diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index dfd9aced5..add84e7f6 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -30,7 +30,6 @@ #include #include "StringUtil.hh" -#include "StringSet.hh" #include "MinMax.hh" #include "StaState.hh" #include "NetworkClass.hh" diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index 656839664..c8f456276 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -29,7 +29,6 @@ #include #include "VectorMap.hh" -#include "StringSet.hh" #include "MinMaxValues.hh" #include "Delay.hh" #include "NetworkClass.hh" diff --git a/include/sta/StringSet.hh b/include/sta/StringSet.hh deleted file mode 100644 index fc3d0d2be..000000000 --- a/include/sta/StringSet.hh +++ /dev/null @@ -1,38 +0,0 @@ -// 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 - -#include "StringUtil.hh" - -namespace sta { - -using StringSet = std::set; -using StdStringSet = std::set; - -void -deleteContents(StringSet *strings); - -} // namespace diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index cdb9b1b7f..9b67ca4cb 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -28,12 +28,14 @@ #include #include #include +#include #include "Machine.hh" // __attribute__ namespace sta { using StdStringSeq = std::vector; +using StdStringSet = std::set; inline bool stringEq(const char *str1, diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index 33d837d34..657aa0185 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -22,20 +22,17 @@ // // This notice may not be removed or altered from any source distribution. -#include "ArcDelayCalc.hh" -#include "StringSet.hh" - #include +#include "ArcDelayCalc.hh" +#include "StringUtil.hh" + namespace sta { #if TCL_MAJOR_VERSION < 9 typedef int Tcl_Size; #endif -StringSet * -tclListSetConstChar(Tcl_Obj *const source, - Tcl_Interp *interp); StdStringSeq tclListSeqStdString(Tcl_Obj *const source, Tcl_Interp *interp); diff --git a/include/sta/VerilogReader.hh b/include/sta/VerilogReader.hh index 2b244d0c2..cf1dab959 100644 --- a/include/sta/VerilogReader.hh +++ b/include/sta/VerilogReader.hh @@ -28,7 +28,7 @@ #include #include -#include "StringSet.hh" +#include "StringUtil.hh" #include "NetworkClass.hh" namespace sta { diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 23774ac11..825502f68 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -31,7 +31,6 @@ #include "Debug.hh" #include "Error.hh" #include "StringUtil.hh" -#include "StringSet.hh" #include "PatternMatch.hh" #include "Units.hh" #include "Transition.hh" diff --git a/spice/WritePathSpice.hh b/spice/WritePathSpice.hh index 647ef542c..c8823aeb6 100644 --- a/spice/WritePathSpice.hh +++ b/spice/WritePathSpice.hh @@ -24,7 +24,6 @@ #pragma once -#include "StringSet.hh" #include "CircuitSim.hh" namespace sta { diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index 81bd15b73..eb10fcfe2 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -30,7 +30,6 @@ #include #include "StaState.hh" -#include "StringSet.hh" #include "StringUtil.hh" #include "Liberty.hh" #include "GraphClass.hh" diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 7de25386a..2c63941e1 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -27,7 +27,6 @@ #include "Machine.hh" #include "StringUtil.hh" -#include "StringSet.hh" #include "PatternMatch.hh" #include "Network.hh" #include "Liberty.hh" @@ -1227,10 +1226,6 @@ using namespace sta; Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); } -%typemap(in) StringSet* { - $1 = tclListSetConstChar($input, interp); -} - %typemap(out) Mode* { const Mode *mode = $1; if (mode) diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index adf21f623..36e606fa6 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -30,26 +30,6 @@ namespace sta { -StringSet * -tclListSetConstChar(Tcl_Obj *const source, - Tcl_Interp *interp) -{ - Tcl_Size argc; - Tcl_Obj **argv; - - if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { - StringSet *set = new StringSet; - for (int i = 0; i < argc; i++) { - int length; - const char *str = Tcl_GetStringFromObj(argv[i], &length); - set->insert(str); - } - return set; - } - else - return nullptr; -} - StdStringSeq tclListSeqStdString(Tcl_Obj *const source, Tcl_Interp *interp) diff --git a/util/StringSet.cc b/util/StringSet.cc deleted file mode 100644 index 798635472..000000000 --- a/util/StringSet.cc +++ /dev/null @@ -1,36 +0,0 @@ -// 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 "StringSet.hh" - -namespace sta { - -void -deleteContents(StringSet *strings) -{ - for (const char *string : *strings) - stringDelete(string); -} - -} // namespace From 859982bdc75a200faceda1fb39635c3b3478d462 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 15:51:50 -0700 Subject: [PATCH 071/181] StdStringSeq -> StringSeq Signed-off-by: James Cherry --- dcalc/DelayCalc.cc | 4 ++-- dcalc/DelayCalc.i | 2 +- doc/ApiChanges.txt | 2 +- doc/ChangeLog.txt | 2 +- graph/Graph.i | 4 ++-- include/sta/DelayCalc.hh | 2 +- include/sta/Mode.hh | 2 +- include/sta/PathGroup.hh | 6 +++--- include/sta/Search.hh | 2 +- include/sta/Sta.hh | 18 +++++++++--------- include/sta/StringUtil.hh | 4 ++-- include/sta/TclTypeHelpers.hh | 4 ++-- liberty/LibertyReader.cc | 28 ++++++++++++++-------------- liberty/LibertyReaderPvt.hh | 6 +++--- parasitics/SpefParse.yy | 4 ++-- parasitics/SpefReader.cc | 2 +- parasitics/SpefReaderPvt.hh | 4 ++-- sdc/Sdc.i | 4 ++-- search/CheckTiming.hh | 2 +- search/Mode.cc | 2 +- search/PathGroup.cc | 10 +++++----- search/ReportPath.cc | 2 +- search/ReportPath.hh | 2 +- search/Search.cc | 2 +- search/Search.i | 12 ++++++------ search/Sta.cc | 24 ++++++++++++------------ spice/WritePathSpice.cc | 2 +- spice/WriteSpice.cc | 18 +++++++++--------- spice/WriteSpice.hh | 8 ++++---- spice/Xyce.cc | 2 +- spice/Xyce.hh | 2 +- tcl/StaTclTypes.i | 12 ++++++------ tcl/TclTypeHelpers.cc | 8 ++++---- util/StringUtil.cc | 8 ++++---- verilog/VerilogReader.cc | 6 +++--- verilog/VerilogReaderPvt.hh | 6 +++--- 36 files changed, 114 insertions(+), 114 deletions(-) diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index 1e27b20eb..98a3e7b17 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -84,10 +84,10 @@ isDelayCalcName(const std::string &name) return delay_calcs.contains(name); } -StdStringSeq +StringSeq delayCalcNames() { - StdStringSeq names; + StringSeq names; for (const auto &[name, make_dcalc] : delay_calcs) names.push_back(name); return names; diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index 62e4c24f0..a0bbaea66 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -36,7 +36,7 @@ %inline %{ -StdStringSeq +StringSeq delay_calc_names() { return sta::delayCalcNames(); diff --git a/doc/ApiChanges.txt b/doc/ApiChanges.txt index 34136d76f..23c9e11f1 100644 --- a/doc/ApiChanges.txt +++ b/doc/ApiChanges.txt @@ -40,7 +40,7 @@ StaState::clk_network__ moved to Mode StaState::parasitics_ moved to Scene Sta::findPathEnds group_paths arg has been changed from PathGroupNameSet* -to StdStringSeq&. +to StringSeq&. Sta::isClock has been removed. Use mode->clkNetwork()->isClock instead. diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index 13bdcc38b..8d695f63d 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -201,7 +201,7 @@ to remove paths through identical pins and rise/fall edges. Instances now have pins for verilog netlist power/ground connections, Sta::findPathEnds group_paths arg has been changed from PathGroupNameSet* -to StdStringSeq&. +to StringSeq&. Release 2.6.1 2025/03/30 ------------------------- diff --git a/graph/Graph.i b/graph/Graph.i index 4eab5270b..289ca3ba9 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -229,12 +229,12 @@ arc_delays(TimingArc *arc) return delays; } -StdStringSeq +StringSeq arc_delay_strings(TimingArc *arc, int digits) { Sta *sta = Sta::sta(); - StdStringSeq delays; + 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), diff --git a/include/sta/DelayCalc.hh b/include/sta/DelayCalc.hh index 091b87997..cea48177e 100644 --- a/include/sta/DelayCalc.hh +++ b/include/sta/DelayCalc.hh @@ -44,7 +44,7 @@ registerDelayCalc(const std::string &name, MakeArcDelayCalc maker); bool isDelayCalcName(const std::string &name); -StdStringSeq +StringSeq delayCalcNames(); void deleteDelayCalcs(); diff --git a/include/sta/Mode.hh b/include/sta/Mode.hh index 0c0827e11..b47adc004 100644 --- a/include/sta/Mode.hh +++ b/include/sta/Mode.hh @@ -71,7 +71,7 @@ public: bool unique_edges, float min_slack, float max_slack, - StdStringSeq &group_names, + StringSeq &group_names, bool setup, bool hold, bool recovery, diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index 836804f7e..df0c5ca7a 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -120,7 +120,7 @@ public: bool unique_edges, float slack_min, float slack_max, - StdStringSeq &group_names, + StringSeq &group_names, bool setup, bool hold, bool recovery, @@ -144,7 +144,7 @@ public: PathGroup *findPathGroup(const Clock *clock, const MinMax *min_max) const; PathGroupSeq pathGroups(const PathEnd *path_end) const; - static StdStringSeq pathGroupNames(const PathEnd *path_end, + static StringSeq pathGroupNames(const PathEnd *path_end, const StaState *sta); static const char *asyncPathGroupName() { return async_group_name_; } static const char *pathDelayGroupName() { return path_delay_group_name_; } @@ -194,7 +194,7 @@ protected: StdStringSet &group_names) const; static GroupPath *groupPathTo(const PathEnd *path_end, const StaState *sta); - StdStringSeq pathGroupNames(); + StringSeq pathGroupNames(); const Mode *mode_; int group_path_count_; diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 4c98845c1..bc5165021 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -107,7 +107,7 @@ public: float slack_min, float slack_max, bool sort_by_slack, - StdStringSeq &group_names, + StringSeq &group_names, bool setup, bool hold, bool recovery, diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index b8212de80..fee94bd19 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -78,7 +78,7 @@ class GraphLoop; using ModeNameMap = std::map; using SceneNameMap = std::map; using SlowDrvrIterator = Iterator; -using CheckError = StdStringSeq; +using CheckError = StringSeq; using CheckErrorSeq = std::vector; enum class CmdNamespace { sta, sdc }; using ParasiticsNameMap = std::map; @@ -125,11 +125,11 @@ public: void setThreadCount(int thread_count); // define_corners compatibility. - void makeScenes(const StdStringSeq &scene_names); + void makeScenes(const StringSeq &scene_names); void makeScene(const std::string &name, const std::string &mode_name, - const StdStringSeq &liberty_min_files, - const StdStringSeq &liberty_max_files, + const StringSeq &liberty_min_files, + const StringSeq &liberty_max_files, const std::string &spef_min_file, const std::string &spef_max_file); Scene *findScene(const std::string &name) const; @@ -652,7 +652,7 @@ public: const Sdc *sdc) __attribute__ ((deprecated)); bool isPathGroupName(const char *group_name, const Sdc *sdc) const; - StdStringSeq pathGroupNames(const Sdc *sdc) const; + StringSeq pathGroupNames(const Sdc *sdc) const; void resetPath(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, @@ -965,7 +965,7 @@ public: bool sort_by_slack, // Path groups to report. // Empty list reports all groups. - StdStringSeq &group_names, + StringSeq &group_names, // Predicates to filter the type of path // ends returned. bool setup, @@ -975,7 +975,7 @@ public: bool clk_gating_setup, bool clk_gating_hold); void setReportPathFormat(ReportPathFormat format); - void setReportPathFieldOrder(const StdStringSeq &field_names); + void setReportPathFieldOrder(const StringSeq &field_names); void setReportPathFields(bool report_input_pin, bool report_hier_pins, bool report_net, @@ -1582,8 +1582,8 @@ protected: void setThreadCount1(int thread_count); void updateLibertyScenes(); void updateSceneLiberty(Scene *scene, - const StdStringSeq &liberty_min_files, - const StdStringSeq &liberty_max_files); + const StringSeq &liberty_min_files, + const StringSeq &liberty_max_files); Scene *makeScene(const std::string &name, Mode *mode, diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index 9b67ca4cb..d237e9c36 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -34,7 +34,7 @@ namespace sta { -using StdStringSeq = std::vector; +using StringSeq = std::vector; using StdStringSet = std::set; inline bool @@ -206,7 +206,7 @@ void trimRight(std::string &str); // Spit text into delimiter separated tokens and skip whitepace. -StdStringSeq +StringSeq parseTokens(const std::string &s, const char delimiter); diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index 657aa0185..08aa291b7 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -33,10 +33,10 @@ namespace sta { typedef int Tcl_Size; #endif -StdStringSeq +StringSeq tclListSeqStdString(Tcl_Obj *const source, Tcl_Interp *interp); -StdStringSeq * +StringSeq * tclListSeqStdStringPtr(Tcl_Obj *const source, Tcl_Interp *interp); StdStringSet * diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 872b135bd..5d99d1f16 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -1894,8 +1894,8 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, LibertyPort *related_output_port = findLibertyPort(cell, timing_group, "related_output_pin"); - StdStringSeq related_port_names = findAttributStrings(timing_group, "related_pin"); - StdStringSeq related_bus_names=findAttributStrings(timing_group,"related_bus_pins"); + StringSeq related_port_names = findAttributStrings(timing_group, "related_pin"); + StringSeq related_bus_names=findAttributStrings(timing_group,"related_bus_pins"); TimingType timing_type = timing_attrs->timingType(); for (LibertyPort *to_port : ports) { @@ -2743,7 +2743,7 @@ LibertyReader::findLibertyPort(LibertyCell *cell, return nullptr; } -StdStringSeq +StringSeq LibertyReader::findAttributStrings(const LibertyGroup *group, const char *name_attr) { @@ -2754,7 +2754,7 @@ LibertyReader::findAttributStrings(const LibertyGroup *group, return parseTokens(*strings, ' '); } } - return StdStringSeq(); + return StringSeq(); } LibertyPortSeq @@ -2763,7 +2763,7 @@ LibertyReader::findLibertyPorts(LibertyCell *cell, const char *port_name_attr) { LibertyPortSeq ports; - StdStringSeq port_names = findAttributStrings(group, port_name_attr); + StringSeq port_names = findAttributStrings(group, port_name_attr); for (const std::string &port_name : port_names) { LibertyPort *port = findPort(cell, port_name.c_str()); if (port) @@ -2915,40 +2915,40 @@ LibertyReader::readStatetable(LibertyCell *cell, const char *input_ports_arg = statetable_group->firstName(); const char *internal_ports_arg = statetable_group->params().size() >= 2 ? statetable_group->secondName() : nullptr; - StdStringSeq input_ports; + StringSeq input_ports; if (input_ports_arg) input_ports = parseTokens(input_ports_arg, ' '); - StdStringSeq internal_ports; + StringSeq internal_ports; if (internal_ports_arg) internal_ports = parseTokens(internal_ports_arg, ' '); const LibertySimpleAttr *table_attr = statetable_group->findSimpleAttr("table"); if (table_attr) { const std::string *table_str = table_attr->stringValue(); - StdStringSeq table_rows = parseTokens(table_str->c_str(), ','); + StringSeq table_rows = parseTokens(table_str->c_str(), ','); size_t input_count = input_ports.size(); size_t internal_count = internal_ports.size(); StatetableRows table; for (const std::string &row : table_rows) { - const StdStringSeq row_groups = parseTokens(row, ':'); + const StringSeq row_groups = parseTokens(row, ':'); if (row_groups.size() != 3) { libWarn(1300, table_attr, "table row must have 3 groups separated by ':'."); break; } - StdStringSeq inputs = parseTokens(row_groups[0], ' '); + 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.", inputs.size(), input_count); break; } - StdStringSeq currents = parseTokens(row_groups[1], ' '); + 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.", currents.size(), internal_count); break; } - StdStringSeq nexts = parseTokens(row_groups[2], ' '); + 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.", nexts.size(), internal_count); @@ -3087,7 +3087,7 @@ static EnumNameMap state_internal_value_name_map = }; StateInputValues -LibertyReader::parseStateInputValues(StdStringSeq &inputs, +LibertyReader::parseStateInputValues(StringSeq &inputs, const LibertySimpleAttr *attr) { StateInputValues input_values; @@ -3106,7 +3106,7 @@ LibertyReader::parseStateInputValues(StdStringSeq &inputs, } StateInternalValues -LibertyReader::parseStateInternalValues(StdStringSeq &states, +LibertyReader::parseStateInternalValues(StringSeq &states, const LibertySimpleAttr *attr) { StateInternalValues state_values; diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index ec8facff0..9cfca58b4 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -101,7 +101,7 @@ public: LibertyPort *findPort(LibertyCell *cell, const char *port_name); - StdStringSeq findAttributStrings(const LibertyGroup *group, + StringSeq findAttributStrings(const LibertyGroup *group, const char *name_attr); protected: @@ -402,9 +402,9 @@ protected: float defaultCap(LibertyPort *port); void visitPorts(std::function func); - StateInputValues parseStateInputValues(StdStringSeq &inputs, + StateInputValues parseStateInputValues(StringSeq &inputs, const LibertySimpleAttr *attr); - StateInternalValues parseStateInternalValues(StdStringSeq &states, + StateInternalValues parseStateInternalValues(StringSeq &states, const LibertySimpleAttr *attr); void getAttrInt(const LibertySimpleAttr *attr, diff --git a/parasitics/SpefParse.yy b/parasitics/SpefParse.yy index 260a212cf..921db7032 100755 --- a/parasitics/SpefParse.yy +++ b/parasitics/SpefParse.yy @@ -62,7 +62,7 @@ sta::SpefParse::error(const location_type &loc, char *string; int integer; float number; - sta::StdStringSeq *std_string_seq; + sta::StringSeq *std_string_seq; sta::PortDirection *port_dir; sta::SpefRspfPi *pi; sta::SpefTriple *triple; @@ -220,7 +220,7 @@ design_flow: qstrings: QSTRING - { $$ = new sta::StdStringSeq; + { $$ = new sta::StringSeq; $$->push_back($1); sta::stringDelete($1); } diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 16f7a6dc6..77dea5522 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -291,7 +291,7 @@ SpefReader::portDirection(char *spef_dir) } void -SpefReader::setDesignFlow(StdStringSeq *flow) +SpefReader::setDesignFlow(StringSeq *flow) { design_flow_ = std::move(*flow); delete flow; diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index de0b80435..2ddb52ad9 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -82,7 +82,7 @@ public: void makeNameMapEntry(const char *index, const char *name); const char *nameMapLookup(const char *index); - void setDesignFlow(StdStringSeq *flow_keys); + void setDesignFlow(StringSeq *flow_keys); Pin *findPin(char *name); Net *findNet(const char *name); void rspfBegin(Net *net, @@ -139,7 +139,7 @@ private: float res_scale_; float induct_scale_; SpefNameMap name_map_; - StdStringSeq design_flow_; + StringSeq design_flow_; Parasitics *parasitics_; Parasitic *parasitic_; }; diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 328abb32e..2bca70b01 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -1580,12 +1580,12 @@ filter_timing_arcs(const char *property, //////////////////////////////////////////////////////////////// -StdStringSeq +StringSeq group_path_names() { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); - StdStringSeq pg_names; + StringSeq pg_names; for (auto const& [name, group] : sdc->groupPaths()) pg_names.push_back(name); return pg_names; diff --git a/search/CheckTiming.hh b/search/CheckTiming.hh index f2c821c74..75bd63acc 100644 --- a/search/CheckTiming.hh +++ b/search/CheckTiming.hh @@ -36,7 +36,7 @@ namespace sta { class ClkNetwork; -using CheckError = StdStringSeq; +using CheckError = StringSeq; using CheckErrorSeq = std::vector; class CheckTiming : public StaState diff --git a/search/Mode.cc b/search/Mode.cc index 80e5b5fd3..1c44a73f3 100644 --- a/search/Mode.cc +++ b/search/Mode.cc @@ -105,7 +105,7 @@ Mode::makePathGroups(int group_path_count, bool unique_edges, float slack_min, float slack_max, - StdStringSeq &group_names, + StringSeq &group_names, bool setup, bool hold, bool recovery, diff --git a/search/PathGroup.cc b/search/PathGroup.cc index b1ef7a0f8..5f4ce29ac 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -258,7 +258,7 @@ PathGroups::PathGroups(int group_path_count, bool unique_edges, float slack_min, float slack_max, - StdStringSeq &group_names, + StringSeq &group_names, bool setup, bool hold, bool recovery, @@ -479,11 +479,11 @@ PathGroups::pathGroups(const PathEnd *path_end) const } // Mirrors PathGroups::pathGroup. -StdStringSeq +StringSeq PathGroups::pathGroupNames(const PathEnd *path_end, const StaState *sta) { - StdStringSeq group_names; + StringSeq group_names; const char *group_name = nullptr; const Search *search = sta->search(); ExceptionPathSeq group_paths = search->groupPathsTo(path_end); @@ -576,14 +576,14 @@ PathGroups::pushEnds(PathEndSeq &path_ends) } } -StdStringSeq +StringSeq PathGroups::pathGroupNames() { std::set group_names1; const Sdc *sdc = mode_->sdc(); for (const auto& [name, group] : sdc->groupPaths()) group_names1.insert(name); - StdStringSeq group_names2; + StringSeq group_names2; for (const std::string &name : group_names1) group_names2.push_back(name); sort(group_names2); diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 8c3d7b325..33fb36896 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -211,7 +211,7 @@ ReportPath::findField(const char *name) const } void -ReportPath::setReportFieldOrder(const StdStringSeq &field_names) +ReportPath::setReportFieldOrder(const StringSeq &field_names) { // Disable all fields. for (ReportField *field : fields_) diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 2d5da89cf..b934bc9ed 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -49,7 +49,7 @@ public: virtual ~ReportPath(); ReportPathFormat pathFormat() const { return format_; } void setPathFormat(ReportPathFormat format); - void setReportFieldOrder(const StdStringSeq &field_names); + void setReportFieldOrder(const StringSeq &field_names); void setReportFields(bool report_input_pin, bool report_hier_pins, bool report_net, diff --git a/search/Search.cc b/search/Search.cc index aca08dfba..c7cc2a584 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -504,7 +504,7 @@ Search::findPathEnds(ExceptionFrom *from, float slack_min, float slack_max, bool sort_by_slack, - StdStringSeq &group_names, + StringSeq &group_names, bool setup, bool hold, bool recovery, diff --git a/search/Search.i b/search/Search.i index b2310c881..410cfdc29 100644 --- a/search/Search.i +++ b/search/Search.i @@ -242,7 +242,7 @@ endpoint_slack(const Pin *pin, } } -StdStringSeq +StringSeq path_group_names() { Sta *sta = Sta::sta(); @@ -359,7 +359,7 @@ find_path_ends(ExceptionFrom *from, float slack_min, float slack_max, bool sort_by_slack, - StdStringSeq path_groups, + StringSeq path_groups, bool setup, bool hold, bool recovery, @@ -395,7 +395,7 @@ set_report_path_format(ReportPathFormat format) } void -set_report_path_field_order(const StdStringSeq &field_names) +set_report_path_field_order(const StringSeq &field_names) { Sta::sta()->setReportPathFieldOrder(field_names); } @@ -740,8 +740,8 @@ write_timing_model_cmd(const char *lib_name, void define_scene_cmd(const char *name, const char *mode_name, - const StdStringSeq liberty_min_files, - const StdStringSeq liberty_max_files, + const StringSeq liberty_min_files, + const StringSeq liberty_max_files, const char *spef_min_file, const char *spef_max_file) { @@ -752,7 +752,7 @@ define_scene_cmd(const char *name, } void -define_scenes_cmd(const StdStringSeq &scene_names) +define_scenes_cmd(const StringSeq &scene_names) { Sta *sta = Sta::sta(); sta->makeScenes(scene_names); diff --git a/search/Sta.cc b/search/Sta.cc index 5353178e9..f91341757 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2075,10 +2075,10 @@ Sta::isPathGroupName(const char *group_name, || stringEq(group_name, PathGroups::unconstrainedGroupName()); } -StdStringSeq +StringSeq Sta::pathGroupNames(const Sdc *sdc) const { - StdStringSeq names; + StringSeq names; for (const Clock *clk : sdc->clocks()) names.push_back(clk->name()); @@ -2450,7 +2450,7 @@ void Sta::makeDefaultScene() { const char *name = "default"; - StdStringSeq scene_names; + StringSeq scene_names; scene_names.push_back(name); Parasitics *parasitics = makeConcreteParasitics(name, ""); @@ -2468,7 +2468,7 @@ Sta::makeDefaultScene() // define_corners (before read_liberty). void -Sta::makeScenes(const StdStringSeq &scene_names) +Sta::makeScenes(const StringSeq &scene_names) { if (scene_names.size() > scene_count_max) report_->error(1553, "maximum scene count exceeded"); @@ -2490,8 +2490,8 @@ Sta::makeScenes(const StdStringSeq &scene_names) void Sta::makeScene(const std::string &name, const std::string &mode_name, - const StdStringSeq &liberty_min_files, - const StdStringSeq &liberty_max_files, + const StringSeq &liberty_min_files, + const StringSeq &liberty_max_files, const std::string &spef_min_file, const std::string &spef_max_file) { @@ -2598,12 +2598,12 @@ Sta::findScenes(const std::string &name, void Sta::updateSceneLiberty(Scene *scene, - const StdStringSeq &liberty_min_files, - const StdStringSeq &liberty_max_files) + const StringSeq &liberty_min_files, + const StringSeq &liberty_max_files) { StdStringSet warned_files; for (const MinMax *min_max : MinMax::range()) { - const StdStringSeq &liberty_files = min_max == MinMax::min() + const StringSeq &liberty_files = min_max == MinMax::min() ? liberty_min_files : liberty_max_files; for (const std::string &lib_file : liberty_files) { @@ -2678,7 +2678,7 @@ Sta::findPathEnds(ExceptionFrom *from, float slack_min, float slack_max, bool sort_by_slack, - StdStringSeq &group_names, + StringSeq &group_names, bool setup, bool hold, bool recovery, @@ -2729,7 +2729,7 @@ Sta::setReportPathFormat(ReportPathFormat format) } void -Sta::setReportPathFieldOrder(const StdStringSeq &field_names) +Sta::setReportPathFieldOrder(const StringSeq &field_names) { report_path_->setReportFieldOrder(field_names); } @@ -3214,7 +3214,7 @@ void EndpointPathEndVisitor::visit(PathEnd *path_end) { if (path_end->minMax(sta_) == min_max_) { - StdStringSeq group_names = PathGroups::pathGroupNames(path_end, sta_); + 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_); diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index 28b702f91..77a117234 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -227,7 +227,7 @@ WritePathSpice::writeHeader() void WritePathSpice::writePrintStmt() { - StdStringSeq node_names; + StringSeq node_names; for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { node_names.push_back(stageDrvrPinName(stage)); node_names.push_back(stageLoadPinName(stage)); diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index ba4774444..7d8c7ab8b 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -151,7 +151,7 @@ WriteSpice::writeHeader(std::string &title, } void -WriteSpice::writePrintStmt(StdStringSeq &node_names) +WriteSpice::writePrintStmt(StringSeq &node_names) { streamPrint(spice_stream_, ".print tran"); if (ckt_sim_ == CircuitSim::xyce) { @@ -176,7 +176,7 @@ WriteSpice::replaceFileExt(std::string filename, // Write gnuplot command file for use with xyce csv file. void -WriteSpice::writeGnuplotFile(StdStringSeq &node_nanes) +WriteSpice::writeGnuplotFile(StringSeq &node_nanes) { std::string gnuplot_filename = replaceFileExt(spice_filename_, "gnuplot"); std::string csv_filename = replaceFileExt(spice_filename_, "csv"); @@ -209,7 +209,7 @@ WriteSpice::writeSubckts(StdStringSet &cell_names) std::string line; while (std::getline(lib_subckts_stream, line)) { // .subckt [args..] - StdStringSeq tokens = parseTokens(line, ' '); + StringSeq tokens = parseTokens(line, ' '); if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { const char *cell_name = tokens[1].c_str(); @@ -256,11 +256,11 @@ WriteSpice::writeSubckts(StdStringSet &cell_names) void WriteSpice::recordSpicePortNames(const char *cell_name, - StdStringSeq &tokens) + StringSeq &tokens) { LibertyCell *cell = network_->findLibertyCell(cell_name); if (cell) { - StdStringSeq &spice_port_names = cell_spice_port_names_[cell_name]; + StringSeq &spice_port_names = cell_spice_port_names_[cell_name]; for (size_t i = 2; i < tokens.size(); i++) { const char *port_name = tokens[i].c_str(); LibertyPort *port = cell->findLibertyPort(port_name); @@ -285,7 +285,7 @@ WriteSpice::findCellSubckts(StdStringSet &cell_names) std::string line; while (std::getline(lib_subckts_stream, line)) { // .subckt [args..] - StdStringSeq tokens = parseTokens(line, ' '); + StringSeq tokens = parseTokens(line, ' '); if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { const char *cell_name = tokens[1].c_str(); @@ -298,7 +298,7 @@ WriteSpice::findCellSubckts(StdStringSet &cell_names) else { // Process previous statement. if (tolower(stmt[0]) == 'x') { - StdStringSeq tokens = parseTokens(line, ' '); + StringSeq tokens = parseTokens(line, ' '); std::string &subckt_cell = tokens[tokens.size() - 1]; cell_names.insert(subckt_cell); } @@ -323,7 +323,7 @@ WriteSpice::writeSubcktInst(const Instance *inst) const char *inst_name = network_->pathName(inst); LibertyCell *cell = network_->libertyCell(inst); const char *cell_name = cell->name(); - StdStringSeq &spice_port_names = cell_spice_port_names_[cell_name]; + StringSeq &spice_port_names = cell_spice_port_names_[cell_name]; streamPrint(spice_stream_, "x%s", inst_name); for (std::string subckt_port_name : spice_port_names) { const char *subckt_port_cname = subckt_port_name.c_str(); @@ -351,7 +351,7 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, { LibertyCell *cell = network_->libertyCell(inst); const char *cell_name = cell->name(); - StdStringSeq &spice_port_names = cell_spice_port_names_[cell_name]; + 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()); diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index eb10fcfe2..f28513a80 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -40,7 +40,7 @@ namespace sta { using ParasiticNodeMap = std::map; -using CellSpicePortNames = std::map; +using CellSpicePortNames = std::map; using LibertyPortLogicValues = std::map; // Utilities for writing a spice deck. @@ -63,12 +63,12 @@ protected: void writeHeader(std::string &title, float max_time, float time_step); - void writePrintStmt(StdStringSeq &node_names); - void writeGnuplotFile(StdStringSeq &node_nanes); + void writePrintStmt(StringSeq &node_names); + void writeGnuplotFile(StringSeq &node_nanes); void writeSubckts(StdStringSet &cell_names); void findCellSubckts(StdStringSet &cell_names); void recordSpicePortNames(const char *cell_name, - StdStringSeq &tokens); + StringSeq &tokens); void writeSubcktInst(const Instance *inst); void writeSubcktInstVoltSrcs(const Instance *inst, LibertyPortLogicValues &port_values, diff --git a/spice/Xyce.cc b/spice/Xyce.cc index a4a0f3b69..500a677ba 100644 --- a/spice/Xyce.cc +++ b/spice/Xyce.cc @@ -37,7 +37,7 @@ namespace sta { void readXyceCsv(const char *csv_filename, // Return values. - StdStringSeq &titles, + StringSeq &titles, WaveformSeq &waveforms) { std::ifstream file(csv_filename); diff --git a/spice/Xyce.hh b/spice/Xyce.hh index ce8e5e6c5..79db58219 100644 --- a/spice/Xyce.hh +++ b/spice/Xyce.hh @@ -37,7 +37,7 @@ using WaveformSeq = std::vector

;X%1?JIbuMjXw(*19?_j6-a!Gn5{od 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

; void readXyceCsv(const char *csv_filename, // Return values. - StdStringSeq &titles, + StringSeq &titles, WaveformSeq &waveforms); } // namespace diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 2c63941e1..ec6ed2d51 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -294,16 +294,16 @@ using namespace sta; $1 = tclListSetStdString($input, interp); } -%typemap(in) StdStringSeq { +%typemap(in) StringSeq { $1 = tclListSeqStdString($input, interp); } -%typemap(in) const StdStringSeq & (StdStringSeq seq) { +%typemap(in) const StringSeq & (StringSeq seq) { seq = tclListSeqStdString($input, interp); $1 = &seq; } -%typemap(in) StdStringSeq* { +%typemap(in) StringSeq* { $1 = tclListSeqStdStringPtr($input, interp); } @@ -311,12 +311,12 @@ using namespace sta; $1 = tclListSetStdString($input, interp); } -%typemap(in) StdStringSeq { +%typemap(in) StringSeq { $1 = tclListSeqStdString($input, interp); } -%typemap(out) StdStringSeq { - StdStringSeq &strs = $1; +%typemap(out) StringSeq { + StringSeq &strs = $1; Tcl_Obj *list = Tcl_NewListObj(0, nullptr); for (const std::string &str : strs) { Tcl_Obj *obj = Tcl_NewStringObj(str.c_str(), str.size()); diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index 36e606fa6..733b0b41f 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -30,14 +30,14 @@ namespace sta { -StdStringSeq +StringSeq tclListSeqStdString(Tcl_Obj *const source, Tcl_Interp *interp) { Tcl_Size argc; Tcl_Obj **argv; - StdStringSeq seq; + StringSeq seq; if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { for (int i = 0; i < argc; i++) { int length; @@ -48,7 +48,7 @@ tclListSeqStdString(Tcl_Obj *const source, return seq; } -StdStringSeq * +StringSeq * tclListSeqStdStringPtr(Tcl_Obj *const source, Tcl_Interp *interp) { @@ -56,7 +56,7 @@ tclListSeqStdStringPtr(Tcl_Obj *const source, Tcl_Obj **argv; if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { - StdStringSeq *seq = new StdStringSeq; + StringSeq *seq = new StringSeq; for (int i = 0; i < argc; i++) { int length; const char *str = Tcl_GetStringFromObj(argv[i], &length); diff --git a/util/StringUtil.cc b/util/StringUtil.cc index 991d25dba..2b93704ca 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -265,11 +265,11 @@ trimRight(std::string &str) str.erase(str.find_last_not_of(" ") + 1); } -StdStringSeq +StringSeq split(const std::string &text, const std::string &delims) { - StdStringSeq tokens; + StringSeq tokens; auto start = text.find_first_not_of(delims); auto end = text.find_first_of(delims, start); while (end != std::string::npos) { @@ -283,11 +283,11 @@ split(const std::string &text, } // Parse space separated tokens. -StdStringSeq +StringSeq parseTokens(const std::string &s, const char delimiter) { - StdStringSeq tokens; + StringSeq tokens; size_t i = 0; while (i < s.size()) { while (i < s.size() && std::isspace(s[i])) diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 72a49a4b0..334633dab 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -535,7 +535,7 @@ VerilogReader::makeModuleInst(const std::string *module_vname, if (liberty_cell && hasScalarNamedPortRefs(liberty_cell, pins)) { const int port_count = liberty_cell->portBitCount(); - StdStringSeq net_names(port_count); + StringSeq net_names(port_count); for (VerilogNet *vnet : *pins) { VerilogNetPortRefScalarNet *vpin = dynamic_cast(vnet); @@ -953,7 +953,7 @@ VerilogModuleInst::namedPins() VerilogLibertyInst::VerilogLibertyInst(LibertyCell *cell, const std::string &inst_name, - const StdStringSeq &net_names, + const StringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, const int line) : VerilogInst(inst_name, attr_stmts, line), @@ -2008,7 +2008,7 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, network_->setAttribute(inst, entry->key(), entry->value()); } } - const StdStringSeq &net_names = lib_inst->netNames(); + const StringSeq &net_names = lib_inst->netNames(); LibertyCellPortBitIterator port_iter(lib_cell); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index 9f1fdffe3..59c242e6d 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -216,16 +216,16 @@ class VerilogLibertyInst : public VerilogInst public: VerilogLibertyInst(LibertyCell *cell, const std::string &inst_name, - const StdStringSeq &net_names, + const StringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, const int line); virtual bool isLibertyInst() const { return true; } LibertyCell *cell() const { return cell_; } - const StdStringSeq &netNames() const { return net_names_; } + const StringSeq &netNames() const { return net_names_; } private: LibertyCell *cell_; - StdStringSeq net_names_; + StringSeq net_names_; }; // Abstract base class for nets. From 28d94b83faf9bf77826d3c7e85e36f80561a5640 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 15:55:12 -0700 Subject: [PATCH 072/181] StdStringSet -> StringSet Signed-off-by: James Cherry --- include/sta/PathGroup.hh | 4 ++-- include/sta/StringUtil.hh | 2 +- include/sta/TclTypeHelpers.hh | 2 +- include/sta/VerilogReader.hh | 2 +- search/PathGroup.cc | 6 +++--- search/Sta.cc | 2 +- spice/WritePathSpice.cc | 10 +++++----- spice/WriteSpice.cc | 4 ++-- spice/WriteSpice.hh | 4 ++-- tcl/StaTclTypes.i | 4 ++-- tcl/TclTypeHelpers.cc | 4 ++-- verilog/VerilogReader.cc | 8 ++++---- verilog/VerilogReaderPvt.hh | 2 +- 13 files changed, 27 insertions(+), 27 deletions(-) diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index df0c5ca7a..4d6d90dd6 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -184,14 +184,14 @@ protected: bool unique_edges, float slack_min, float slack_max, - StdStringSet &group_names, + StringSet &group_names, bool setup_hold, bool async, bool gated_clk, bool unconstrained, const MinMax *min_max); bool reportGroup(const char *group_name, - StdStringSet &group_names) const; + StringSet &group_names) const; static GroupPath *groupPathTo(const PathEnd *path_end, const StaState *sta); StringSeq pathGroupNames(); diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index d237e9c36..b12ae00d0 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -35,7 +35,7 @@ namespace sta { using StringSeq = std::vector; -using StdStringSet = std::set; +using StringSet = std::set; inline bool stringEq(const char *str1, diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index 08aa291b7..a551c5845 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -39,7 +39,7 @@ tclListSeqStdString(Tcl_Obj *const source, StringSeq * tclListSeqStdStringPtr(Tcl_Obj *const source, Tcl_Interp *interp); -StdStringSet * +StringSet * tclListSetStdString(Tcl_Obj *const source, Tcl_Interp *interp); diff --git a/include/sta/VerilogReader.hh b/include/sta/VerilogReader.hh index cf1dab959..e3aa94fa9 100644 --- a/include/sta/VerilogReader.hh +++ b/include/sta/VerilogReader.hh @@ -173,7 +173,7 @@ protected: void makeNamedPortRefCellPorts(Cell *cell, VerilogModule *module, VerilogNet *mod_port, - StdStringSet &port_names); + StringSet &port_names); void checkModuleDcls(VerilogModule *module, std::set &port_names); void makeModuleInstBody(VerilogModule *module, diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 5f4ce29ac..794d2c6ed 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -276,7 +276,7 @@ PathGroups::PathGroups(int group_path_count, slack_min_(slack_min), slack_max_(slack_max) { - StdStringSet groups; + StringSet groups; for (std::string &group_name : group_names) groups.insert(group_name); @@ -297,7 +297,7 @@ PathGroups::makeGroups(int group_path_count, bool unique_edges, float slack_min, float slack_max, - StdStringSet &group_names, + StringSet &group_names, bool setup_hold, bool async, bool gated_clk, @@ -417,7 +417,7 @@ PathGroups::findPathGroup(const Clock *clock, bool PathGroups::reportGroup(const char *group_name, - StdStringSet &group_names) const + StringSet &group_names) const { return group_names.empty() || group_names.contains(group_name); diff --git a/search/Sta.cc b/search/Sta.cc index f91341757..a6c430715 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2601,7 +2601,7 @@ Sta::updateSceneLiberty(Scene *scene, const StringSeq &liberty_min_files, const StringSeq &liberty_max_files) { - StdStringSet warned_files; + StringSet warned_files; for (const MinMax *min_max : MinMax::range()) { const StringSeq &liberty_files = min_max == MinMax::min() ? liberty_min_files diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index 77a117234..6c7034bea 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -80,8 +80,8 @@ class WritePathSpice : public WriteSpice void writeGateStage(Stage stage); void writeStageParasitics(Stage stage); void writeSubckts(); - StdStringSet findPathCellNames(); - void findPathCellSubckts(StdStringSet &path_cell_names); + StringSet findPathCellNames(); + void findPathCellSubckts(StringSet &path_cell_names); float maxTime(); float pathMaxTime(); void writeMeasureDelayStmt(Stage stage, @@ -562,14 +562,14 @@ WritePathSpice::writeStageParasitics(Stage stage) void WritePathSpice::writeSubckts() { - StdStringSet cell_names = findPathCellNames(); + StringSet cell_names = findPathCellNames(); writeSubckts(cell_names); } -StdStringSet +StringSet WritePathSpice::findPathCellNames() { - StdStringSet path_cell_names; + StringSet path_cell_names; for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { const TimingArc *arc = stageGateArc(stage); if (arc) { diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 7d8c7ab8b..1e5cfb12f 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -199,7 +199,7 @@ WriteSpice::writeGnuplotFile(StringSeq &node_nanes) } void -WriteSpice::writeSubckts(StdStringSet &cell_names) +WriteSpice::writeSubckts(StringSet &cell_names) { findCellSubckts(cell_names); std::ifstream lib_subckts_stream(lib_subckt_filename_); @@ -278,7 +278,7 @@ WriteSpice::recordSpicePortNames(const char *cell_name, // Subckts can call subckts (asap7). void -WriteSpice::findCellSubckts(StdStringSet &cell_names) +WriteSpice::findCellSubckts(StringSet &cell_names) { std::ifstream lib_subckts_stream(lib_subckt_filename_); if (lib_subckts_stream.is_open()) { diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index f28513a80..b728d154a 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -65,8 +65,8 @@ protected: float time_step); void writePrintStmt(StringSeq &node_names); void writeGnuplotFile(StringSeq &node_nanes); - void writeSubckts(StdStringSet &cell_names); - void findCellSubckts(StdStringSet &cell_names); + void writeSubckts(StringSet &cell_names); + void findCellSubckts(StringSet &cell_names); void recordSpicePortNames(const char *cell_name, StringSeq &tokens); void writeSubcktInst(const Instance *inst); diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index ec6ed2d51..f8de82fdc 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -290,7 +290,7 @@ using namespace sta; Tcl_SetResult(interp, nullptr, TCL_STATIC); } -%typemap(in) StdStringSet* { +%typemap(in) StringSet* { $1 = tclListSetStdString($input, interp); } @@ -307,7 +307,7 @@ using namespace sta; $1 = tclListSeqStdStringPtr($input, interp); } -%typemap(in) StdStringSet* { +%typemap(in) StringSet* { $1 = tclListSetStdString($input, interp); } diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index 733b0b41f..481046908 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -68,7 +68,7 @@ tclListSeqStdStringPtr(Tcl_Obj *const source, return nullptr; } -StdStringSet * +StringSet * tclListSetStdString(Tcl_Obj *const source, Tcl_Interp *interp) { @@ -76,7 +76,7 @@ tclListSetStdString(Tcl_Obj *const source, Tcl_Obj **argv; if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { - StdStringSet *set = new StdStringSet; + StringSet *set = new StringSet; for (int i = 0; i < argc; i++) { int length; const char *str = Tcl_GetStringFromObj(argv[i], &length); diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 334633dab..de6099a55 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -285,7 +285,7 @@ VerilogReader::makeCellPorts(Cell *cell, VerilogModule *module, VerilogNetSeq *ports) { - StdStringSet port_names; + StringSet port_names; for (VerilogNet *mod_port : *ports) { const std::string &port_name = mod_port->name(); if (!port_names.contains(port_name)) { @@ -335,7 +335,7 @@ void VerilogReader::makeNamedPortRefCellPorts(Cell *cell, VerilogModule *module, VerilogNet *mod_port, - StdStringSet &port_names) + StringSet &port_names) { PortSeq *member_ports = new PortSeq; VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module,this); @@ -805,7 +805,7 @@ VerilogModule::~VerilogModule() void VerilogModule::parseStmts(VerilogReader *reader) { - StdStringSet inst_names; + StringSet inst_names; for (VerilogStmt *stmt : *stmts_) { if (stmt->isDeclaration()) parseDcl(dynamic_cast(stmt), reader); @@ -861,7 +861,7 @@ VerilogModule::parseDcl(VerilogDcl *dcl, // expansion so errors are only reported once. void VerilogModule::checkInstanceName(VerilogInst *inst, - StdStringSet &inst_names, + StringSet &inst_names, VerilogReader *reader) { std::string inst_name = inst->instanceName(); diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index 59c242e6d..23f243058 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -75,7 +75,7 @@ public: private: void parseStmts(VerilogReader *reader); void checkInstanceName(VerilogInst *inst, - StdStringSet &inst_names, + StringSet &inst_names, VerilogReader *reader); std::string name_; From d99cdd11dee1c6d5158585cab1af68d89c68a51c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 16:10:24 -0700 Subject: [PATCH 073/181] Sta::makeCheckTiming Signed-off-by: James Cherry --- search/Sta.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/search/Sta.cc b/search/Sta.cc index a6c430715..a83835b1b 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -299,6 +299,7 @@ Sta::makeComponents() makeReportPath(); makePower(); makeClkSkews(); + makeCheckTiming(); setCmdNamespace1(CmdNamespace::sdc); setThreadCount1(defaultThreadCount()); @@ -355,8 +356,7 @@ Sta::updateComponentsState() latches_->copyState(this); graph_delay_calc_->copyState(this); report_path_->copyState(this); - if (check_timing_) - check_timing_->copyState(this); + check_timing_->copyState(this); clk_skews_->copyState(this); if (power_) @@ -2224,8 +2224,6 @@ Sta::checkTiming(const Mode *mode, mode->sim()->ensureConstantsPropagated(); mode->clkNetwork()->ensureClkNetwork(); } - if (check_timing_ == nullptr) - makeCheckTiming(); return check_timing_->check(mode, no_input_delay, no_output_delay, reg_multiple_clks, reg_no_clks, unconstrained_endpoints, From 28b9401e31ee8d56678dc19383850976d95c8f36 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 16:15:24 -0700 Subject: [PATCH 074/181] const Signed-off-by: James Cherry --- dcalc/GraphDelayCalc.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 06962c785..8bc0765ef 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -1114,8 +1114,7 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex, const Pin *from_pin = from_vertex->pin(); const RiseFall *from_rf = arc1->fromEdge()->asRiseFall(); const RiseFall *drvr_rf = arc1->toEdge()->asRiseFall(); - Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, scene, min_max); - in_slew = edgeFromSlew(from_vertex, from_rf, edge1, scene, min_max); + const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, scene, min_max); const Pin *drvr_pin1 = drvr_vertex1->pin(); float load_cap; @@ -1694,7 +1693,7 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, related_out_cap, scene, min_max, digits); } else { - const Slew &from_slew = edgeFromSlew(from_vertex, from_rf, edge, scene, min_max); + const Slew from_slew = edgeFromSlew(from_vertex, from_rf, edge, scene, min_max); const Parasitic *to_parasitic; float load_cap; parasiticLoad(to_pin, to_rf, scene, min_max, nullptr, arc_delay_calc_, From 9b2bdf85e10e5f4b807fcd4951f4efc098f6311f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 8 Mar 2026 16:39:48 -0700 Subject: [PATCH 075/181] PathGroup::name std::string Signed-off-by: James Cherry --- include/sta/PathGroup.hh | 2 +- search/ReportPath.cc | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index 4d6d90dd6..58bb84d98 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -66,7 +66,7 @@ public: float min_slack, float max_slack, const StaState *sta); - const char *name() const { return name_.c_str(); } + const std::string &name() const { return name_; } const MinMax *minMax() const { return min_max_;} const PathEndSeq &pathEnds() const { return path_ends_; } void insert(PathEnd *path_end); diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 33fb36896..bf6d83fe7 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -406,7 +406,7 @@ ReportPath::reportEndpointHeader(const PathEnd *end, : "max_delay/setup"; report_->reportLine("%s group %s", setup_hold, - group->name()); + group->name().c_str()); reportBlankLine(); reportEndHeader(); } @@ -1079,11 +1079,17 @@ ReportPath::reportJson(const PathEnd *end, { std::string result; result += "{\n"; - stringAppend(result, " \"type\": \"%s\",\n", end->typeName()); - stringAppend(result, " \"path_group\": \"%s\",\n", - end->pathGroup()->name()); - stringAppend(result, " \"path_type\": \"%s\",\n", - end->minMax(this)->to_string().c_str()); + result += " \"type\": \""; + result += end->typeName(); + result += "\",\n"; + + result += " \"path_group\": \""; + result += end->pathGroup()->name(); + result += "\",\n"; + + result += " \"path_type\": \""; + result += end->minMax(this)->to_string(); + result += "\",\n"; PathExpanded expanded(end->path(), this); const Pin *startpoint = expanded.startPath()->vertex(this)->pin(); @@ -1269,7 +1275,7 @@ ReportPath::reportSlackOnly(const PathEnd *end) const { std::string line; const EarlyLate *early_late = end->pathEarlyLate(this); - reportDescription(end->pathGroup()->name(), line); + reportDescription(end->pathGroup()->name().c_str(), line); if (end->isUnconstrained()) reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); else From f1b33edd98f4f89c418e839d8bc5ee097b82d385 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 9 Mar 2026 10:15:53 -0700 Subject: [PATCH 076/181] PathGroup use BoundedHeap Signed-off-by: James Cherry --- include/sta/BoundedHeap.hh | 15 ++--- include/sta/PathEnd.hh | 12 +++- include/sta/PathGroup.hh | 24 +++---- include/sta/Search.hh | 4 +- search/PathEnd.cc | 38 ++++++----- search/PathEnum.cc | 2 +- search/PathGroup.cc | 125 +++++++++++-------------------------- search/Search.cc | 4 +- 8 files changed, 91 insertions(+), 133 deletions(-) diff --git a/include/sta/BoundedHeap.hh b/include/sta/BoundedHeap.hh index 45bfdeed3..af934e0c5 100644 --- a/include/sta/BoundedHeap.hh +++ b/include/sta/BoundedHeap.hh @@ -60,7 +60,6 @@ public: comp_(comp), min_heap_comp_(comp) { - heap_.reserve(max_size); } // Copy constructor @@ -107,7 +106,12 @@ public: setMaxSize(size_t max_size) { max_size_ = max_size; - heap_.reserve(max_size); + } + + void + reserve(size_t size) + { + heap_.reserve(size); } // Insert an element into the heap. @@ -172,8 +176,6 @@ public: { // Convert heap to sorted vector (best to worst) std::sort_heap(heap_.begin(), heap_.end(), min_heap_comp_); - // Reverse to get best first (according to user's comparison) - std::reverse(heap_.begin(), heap_.end()); std::vector result = std::move(heap_); heap_.clear(); return result; @@ -181,11 +183,10 @@ public: // Extract all elements sorted from best to worst (const version). // Creates a copy since we can't modify the heap. - std::vector extract() const + std::vector contents() const { std::vector temp_heap = heap_; std::sort_heap(temp_heap.begin(), temp_heap.end(), min_heap_comp_); - std::reverse(temp_heap.begin(), temp_heap.end()); return temp_heap; } @@ -245,7 +246,7 @@ private: Compare comp_; explicit MinHeapCompare(const Compare& c) : comp_(c) {} bool operator()(const T& a, const T& b) const { - return comp_(b, a); // Inverted: worst is at root + return comp_(a, b); // comp = less puts largest at root (worst) } }; diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index 4f974f3fa..ee8deb39a 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -153,9 +153,13 @@ public: static bool less(const PathEnd *path_end1, const PathEnd *path_end2, + // Compare slack (if constrained), or arrival when false. + bool cmp_slack, const StaState *sta); static int cmp(const PathEnd *path_end1, const PathEnd *path_end2, + // Compare slack (if constrained), or arrival when false. + bool cmp_slack, const StaState *sta); static int cmpSlack(const PathEnd *path_end1, const PathEnd *path_end2, @@ -611,11 +615,13 @@ protected: class PathEndLess { public: - PathEndLess(const StaState *sta); + PathEndLess(bool cmp_slack, + const StaState *sta); bool operator()(const PathEnd *path_end1, const PathEnd *path_end2) const; protected: + bool cmp_slack_; const StaState *sta_; }; @@ -623,11 +629,13 @@ protected: class PathEndSlackLess { public: - PathEndSlackLess(const StaState *sta); + PathEndSlackLess(bool cmp_slack, + const StaState *sta); bool operator()(const PathEnd *path_end1, const PathEnd *path_end2) const; protected: + bool cmp_slack_; const StaState *sta_; }; diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index 58bb84d98..03bcb0c5d 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -29,10 +29,12 @@ #include #include +#include "BoundedHeap.hh" #include "SdcClass.hh" #include "StaState.hh" #include "SearchClass.hh" #include "StringUtil.hh" +#include "PathEnd.hh" namespace sta { @@ -48,7 +50,6 @@ using PathGroupSeq = std::vector; class PathGroup { public: - ~PathGroup(); // Path group that compares compare slacks. static PathGroup *makePathGroupArrival(const char *name, int group_path_count, @@ -68,7 +69,7 @@ public: const StaState *sta); const std::string &name() const { return name_; } const MinMax *minMax() const { return min_max_;} - const PathEndSeq &pathEnds() const { return path_ends_; } + PathEndSeq pathEnds() const; void insert(PathEnd *path_end); // Push group_path_count into path_ends. void pushEnds(PathEndSeq &path_ends); @@ -76,15 +77,14 @@ public: bool saveable(PathEnd *path_end); bool enumMinSlackUnderMin(PathEnd *path_end); int maxPaths() const { return group_path_count_; } - PathEndSeq &pathEnds() { return path_ends_; } // This does NOT delete the path ends. void clear(); - static size_t group_path_count_max; + static int group_path_count_max; protected: PathGroup(const char *name, - size_t group_path_count, - size_t endpoint_path_count, + int group_path_count, + int endpoint_path_count, bool unique_pins, bool unique_edges, float min_slack, @@ -92,21 +92,17 @@ protected: bool cmp_slack, const MinMax *min_max, const StaState *sta); - void ensureSortedMaxPaths(); - void prune(); - void sort(); std::string name_; - size_t group_path_count_; - size_t endpoint_path_count_; + int group_path_count_; + int endpoint_path_count_; bool unique_pins_; bool unique_edges_; float slack_min_; float slack_max_; - PathEndSeq path_ends_; const MinMax *min_max_; - bool compare_slack_; - float threshold_; + bool cmp_slack_; + BoundedHeap heap_; std::mutex lock_; const StaState *sta_; }; diff --git a/include/sta/Search.hh b/include/sta/Search.hh index bc5165021..ba4f8d0dc 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -100,8 +100,8 @@ public: bool unconstrained, const SceneSeq &scenes, const MinMaxAll *min_max, - size_t group_path_count, - size_t endpoint_path_count, + int group_path_count, + int endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 7e72e6fb5..33b6e1cd5 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -2030,32 +2030,22 @@ PathEndPathDelay::exceptPathCmp(const PathEnd *path_end, //////////////////////////////////////////////////////////////// -PathEndLess::PathEndLess(const StaState *sta) : - sta_(sta) -{ -} - -bool -PathEndLess::operator()(const PathEnd *path_end1, - const PathEnd *path_end2) const -{ - return PathEnd::less(path_end1, path_end2, sta_); -} - bool PathEnd::less(const PathEnd *path_end1, const PathEnd *path_end2, + bool cmp_slack, const StaState *sta) { - return cmp(path_end1, path_end2, sta) < 0; + return cmp(path_end1, path_end2, cmp_slack, sta) < 0; } int PathEnd::cmp(const PathEnd *path_end1, const PathEnd *path_end2, + bool cmp_slack, const StaState *sta) { - int cmp = path_end1->isUnconstrained() + int cmp = !cmp_slack || path_end1->isUnconstrained() ? -cmpArrival(path_end1, path_end2, sta) : cmpSlack(path_end1, path_end2, sta); if (cmp == 0) { @@ -2139,7 +2129,25 @@ PathEnd::cmpNoCrpr(const PathEnd *path_end1, //////////////////////////////////////////////////////////////// -PathEndSlackLess::PathEndSlackLess(const StaState *sta) : +PathEndLess::PathEndLess(bool cmp_slack, + const StaState *sta) : + cmp_slack_(cmp_slack), + sta_(sta) +{ +} + +bool +PathEndLess::operator()(const PathEnd *path_end1, + const PathEnd *path_end2) const +{ + return PathEnd::less(path_end1, path_end2, cmp_slack_, sta_); +} + +//////////////////////////////////////////////////////////////// + +PathEndSlackLess::PathEndSlackLess(bool cmp_slack, + const StaState *sta) : + cmp_slack_(cmp_slack), sta_(sta) { } diff --git a/search/PathEnum.cc b/search/PathEnum.cc index 8cc8c9c92..67465a1bf 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -91,7 +91,7 @@ DiversionGreater::operator()(Diversion *div1, { PathEnd *path_end1 = div1->pathEnd(); PathEnd *path_end2 = div2->pathEnd(); - return PathEnd::cmp(path_end1, path_end2, sta_) > 0; + return PathEnd::cmp(path_end1, path_end2, true, sta_) > 0; } static void diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 794d2c6ed..8f3738c65 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -50,7 +50,7 @@ namespace sta { -size_t PathGroup::group_path_count_max = std::numeric_limits::max(); +int PathGroup::group_path_count_max = std::numeric_limits::max(); PathGroup * PathGroup::makePathGroupSlack(const char *name, @@ -82,8 +82,8 @@ PathGroup::makePathGroupArrival(const char *name, } PathGroup::PathGroup(const char *name, - size_t group_path_count, - size_t endpoint_path_count, + int group_path_count, + int endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, @@ -99,43 +99,36 @@ PathGroup::PathGroup(const char *name, slack_min_(slack_min), slack_max_(slack_max), min_max_(min_max), - compare_slack_(cmp_slack), - threshold_(min_max->initValue()), + cmp_slack_(cmp_slack), + heap_(group_path_count, PathEndLess(cmp_slack, sta)), sta_(sta) { } -PathGroup::~PathGroup() +PathEndSeq +PathGroup::pathEnds() const { - deleteContents(path_ends_); + return heap_.contents(); } bool PathGroup::saveable(PathEnd *path_end) { - float threshold; - { - LockGuard lock(lock_); - threshold = threshold_; - } - if (compare_slack_) { + 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, threshold, sta_) && delayLessEqual(slack, slack_max_, sta_)) { // Now check with crpr. slack = path_end->slack(sta_); - return delayLessEqual(slack, threshold, sta_) - && delayLessEqual(slack, slack_max_, sta_) + return delayLessEqual(slack, slack_max_, sta_) && delayGreaterEqual(slack, slack_min_, sta_); } } else { const Arrival &arrival = path_end->dataArrivalTime(sta_); - return !delayIsInitValue(arrival, min_max_) - && delayGreaterEqual(arrival, threshold, min_max_, sta_); + return !delayIsInitValue(arrival, min_max_); } return false; } @@ -148,7 +141,7 @@ PathGroup::saveable(PathEnd *path_end) bool PathGroup::enumMinSlackUnderMin(PathEnd *path_end) { - if (compare_slack_ + if (cmp_slack_ && endpoint_path_count_ > 1 && slack_min_ > -INF) { const Path *path = path_end->path(); @@ -177,72 +170,28 @@ void PathGroup::insert(PathEnd *path_end) { LockGuard lock(lock_); - path_ends_.push_back(path_end); + heap_.insert(path_end); path_end->setPathGroup(this); - if (group_path_count_ != group_path_count_max - && path_ends_.size() > 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 < group_path_count_ - && path_counts[vertex] < 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 (compare_slack_) - threshold_ = delayAsFloat(last_end->slack(sta_)); - else - threshold_ = delayAsFloat(last_end->dataArrivalTime(sta_)); } void PathGroup::pushEnds(PathEndSeq &path_ends) { - ensureSortedMaxPaths(); - for (PathEnd *path_end : path_ends_) - path_ends.push_back(path_end); -} - -void -PathGroup::ensureSortedMaxPaths() -{ - if (path_ends_.size() > group_path_count_) - prune(); - else - sort(); -} - -void -PathGroup::sort() -{ - sta::sort(path_ends_, PathEndLess(sta_)); + if (!heap_.empty()) { + PathEndSeq ends = heap_.extract(); + 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())); + } } void PathGroup::clear() { LockGuard lock(lock_); - threshold_ = min_max_->initValue(); - path_ends_.clear(); + heap_.clear(); } //////////////////////////////////////////////////////////////// @@ -632,9 +581,8 @@ PathGroups::makePathEnds(ExceptionTo *to, unique_pins_, unique_edges_, scenes, min_max); pushEnds(path_ends); - if (sort_by_slack) { - sort(path_ends, PathEndLess(this)); - } + if (sort_by_slack) + sort(path_ends, PathEndLess(true, this)); if (unconstrained_paths && path_ends.empty()) @@ -663,12 +611,12 @@ class MakePathEnds1 : public PathEndVisitor PathGroups *path_groups_; PathGroupEndMap ends_; - PathEndLess cmp_; + PathEndLess less_; }; MakePathEnds1::MakePathEnds1(PathGroups *path_groups) : path_groups_(path_groups), - cmp_(path_groups) + less_(true, path_groups) { } @@ -693,7 +641,7 @@ MakePathEnds1::visitPathEnd(PathEnd *path_end, // Only keep the path end with the smallest slack/latest arrival. PathEnd *worst_end = findKey(ends_, group); if (worst_end) { - if (cmp_(path_end, worst_end)) { + if (less_(path_end, worst_end)) { ends_[group] = path_end->copy(); delete worst_end; } @@ -741,8 +689,8 @@ class MakePathEndsAll : public PathEndVisitor PathGroups *path_groups_; const StaState *sta_; PathGroupEndsMap ends_; - PathEndSlackLess slack_cmp_; - PathEndNoCrprLess path_no_crpr_cmp_; + PathEndSlackLess less_; + PathEndNoCrprLess path_no_crpr_less_; }; MakePathEndsAll::MakePathEndsAll(int endpoint_path_count, @@ -750,8 +698,8 @@ MakePathEndsAll::MakePathEndsAll(int endpoint_path_count, endpoint_path_count_(endpoint_path_count), path_groups_(path_groups), sta_(path_groups), - slack_cmp_(path_groups), - path_no_crpr_cmp_(path_groups) + less_(true, path_groups), + path_no_crpr_less_(path_groups) { } @@ -792,8 +740,8 @@ MakePathEndsAll::vertexEnd(Vertex *) Debug *debug = sta_->debug(); for (auto [group, ends] : ends_) { if (ends) { - sort(ends, slack_cmp_); - PathEndNoCrprSet unique_ends(path_no_crpr_cmp_); + sort(ends, less_); + PathEndNoCrprSet unique_ends(path_no_crpr_less_); auto end_iter = ends->begin(); int n = 0; while (end_iter != ends->end() @@ -898,11 +846,8 @@ 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()) { - if (group->saveable(end) - || group->enumMinSlackUnderMin(end)) - path_enum.insert(end); - } + for (PathEnd *end : group->pathEnds()) + path_enum.insert(end); group->clear(); // Parallel path enumeratation to find the endpoint_path_count/max path ends. diff --git a/search/Search.cc b/search/Search.cc index c7cc2a584..871b0e0d7 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -497,8 +497,8 @@ Search::findPathEnds(ExceptionFrom *from, bool unconstrained, const SceneSeq &scenes, const MinMaxAll *min_max, - size_t group_path_count, - size_t endpoint_path_count, + int group_path_count, + int endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, From 129d007f7603497c3831c6077df4f413a786c6e9 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 9 Mar 2026 17:43:03 -0700 Subject: [PATCH 077/181] override Signed-off-by: James Cherry --- dcalc/ArnoldiDelayCalc.cc | 2 +- dcalc/DmpCeff.cc | 1 - graph/Graph.cc | 10 +- include/sta/ExceptionPath.hh | 280 +++++++++++++++++------------------ network/Network.cc | 6 +- sdc/Sdc.cc | 18 +-- search/FindRegister.cc | 40 ++--- search/Genclks.cc | 4 +- search/MakeTimingModel.cc | 6 +- search/PathEnum.cc | 6 +- search/PathGroup.cc | 18 +-- search/Search.cc | 14 +- search/Sta.cc | 10 +- util/ReportStd.cc | 2 +- 14 files changed, 208 insertions(+), 209 deletions(-) diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index bb1d17029..957963f36 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -119,7 +119,7 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc { public: ArnoldiDelayCalc(StaState *sta); - virtual ~ArnoldiDelayCalc(); + ~ArnoldiDelayCalc() override; ArcDelayCalc *copy() override; const char *name() const override { return "arnoldi"; } Parasitic *findParasitic(const Pin *drvr_pin, diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index c32362f21..0b59f18be 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -77,7 +77,6 @@ class DmpError : public Exception { public: DmpError(const char *what); - virtual ~DmpError() {} virtual const char *what() const noexcept { return what_; } private: diff --git a/graph/Graph.cc b/graph/Graph.cc index f56a02a7c..6f37c6214 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -107,7 +107,7 @@ class FindNetDrvrLoadCounts : public PinVisitor int &bidirect_count, int &load_count, const Network *network); - virtual void operator()(const Pin *pin); + void operator()(const Pin *pin) override; protected: Pin *drvr_pin_; @@ -345,8 +345,8 @@ class MakeEdgesThruHierPin : public HierPinThruVisitor MakeEdgesThruHierPin(Graph *graph); private: - virtual void visit(const Pin *drvr, - const Pin *load); + void visit(const Pin *drvr, + const Pin *load) override; Graph *graph_; }; @@ -1483,8 +1483,8 @@ class FindEdgesThruHierPinVisitor : public HierPinThruVisitor public: FindEdgesThruHierPinVisitor(EdgeSet &edges, Graph *graph); - virtual void visit(const Pin *drvr, - const Pin *load); + void visit(const Pin *drvr, + const Pin *load) override; protected: EdgeSet &edges_; diff --git a/include/sta/ExceptionPath.hh b/include/sta/ExceptionPath.hh index f5b89f14f..72a28f638 100644 --- a/include/sta/ExceptionPath.hh +++ b/include/sta/ExceptionPath.hh @@ -164,17 +164,17 @@ public: bool own_pts, int priority, const char *comment); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isFalse() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::false_path; } - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) override; + bool isFalse() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::false_path; } + const char *typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; }; // Loop paths are false paths used to disable paths around @@ -184,10 +184,10 @@ class LoopPath : public FalsePath public: LoopPath(ExceptionThruSeq *thrus, bool own_pts); - virtual bool isLoop() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::loop; } - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; + bool isLoop() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::loop; } + const char *typeString() const override; + bool mergeable(ExceptionPath *exception) const override; }; // set_max_delay/set_min_delay @@ -203,21 +203,21 @@ public: float delay, bool own_pts, const char *comment); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isPathDelay() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::path_delay; } - virtual const char *asString(const Network *network) const; - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual float delay() const { return delay_; } - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; - virtual bool ignoreClkLatency() const { return ignore_clk_latency_; } - virtual bool breakPath() const { return break_path_; } + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + 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; + const char *typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + float delay() const override { return delay_; } + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; + bool ignoreClkLatency() const override { return ignore_clk_latency_; } + bool breakPath() const override { return break_path_; } protected: bool ignore_clk_latency_; @@ -237,24 +237,24 @@ public: int path_multiplier, bool own_pts, const char *comment); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isMultiCycle() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::multi_cycle; } - virtual bool matches(const MinMax *min_max, - bool exactly) const; - virtual const char *asString(const Network *network) const; - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual bool useEndClk() const { return use_end_clk_; } - virtual int pathMultiplier(const MinMax *min_max) const; - virtual int pathMultiplier() const { return path_multiplier_; } - virtual int priority(const MinMax *min_max) const; - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) override; + bool isMultiCycle() const override { return true; } + 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; + const char *typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + bool useEndClk() const override { return use_end_clk_; } + int pathMultiplier(const MinMax *min_max) const; // overload, not override + int pathMultiplier() const override { return path_multiplier_; } + int priority(const MinMax *min_max) const override; + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; using ExceptionPath::priority; @@ -271,22 +271,22 @@ public: ExceptionThruSeq *thrus, ExceptionTo *to, bool own_pts); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isFilter() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::filter; } - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual bool resetMatch(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const Network *network); - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) override; + bool isFilter() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::filter; } + const char *typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + bool resetMatch(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const Network *network) override; + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; }; class GroupPath : public ExceptionPath @@ -299,20 +299,20 @@ public: ExceptionTo *to, bool own_pts, const char *comment); - virtual ~GroupPath(); - virtual ExceptionPath *clone(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool own_pts); - virtual bool isGroupPath() const { return true; } - virtual ExceptionPathType type() const { return ExceptionPathType::group_path; } - virtual const char *typeString() const; - virtual bool mergeable(ExceptionPath *exception) const; - virtual bool overrides(ExceptionPath *exception) const; - virtual int typePriority() const; - virtual bool tighterThan(ExceptionPath *exception) const; - virtual const char *name() const { return name_; } - virtual bool isDefault() const { return is_default_; } + ~GroupPath() override; + ExceptionPath *clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) override; + bool isGroupPath() const override { return true; } + ExceptionPathType type() const override { return ExceptionPathType::group_path; } + const char *typeString() const override; + bool mergeable(ExceptionPath *exception) const override; + bool overrides(ExceptionPath *exception) const override; + int typePriority() const override; + bool tighterThan(ExceptionPath *exception) const override; + const char *name() const override { return name_; } + bool isDefault() const override { return is_default_; } protected: const char *name_; @@ -385,39 +385,39 @@ public: bool own_pts, const Network *network); ~ExceptionFromTo(); - virtual PinSet *pins() { return pins_; } + PinSet *pins() override { return pins_; } bool hasPins() const; - ClockSet *clks() { return clks_; } + ClockSet *clks() override { return clks_; } bool hasClocks() const; - InstanceSet *instances() { return insts_; } + InstanceSet *instances() override { return insts_; } bool hasInstances() const; - virtual NetSet *nets() { return nullptr; } - virtual EdgePinsSet *edges() { return nullptr; } + NetSet *nets() override { return nullptr; } + EdgePinsSet *edges() override { return nullptr; } bool hasObjects() const; void deleteObjects(ExceptionFromTo *pt, const Network *network); - virtual PinSet allPins(const Network *network); + PinSet allPins(const Network *network) override; bool equal(ExceptionFromTo *from_to) const; - virtual int compare(ExceptionPt *pt, - const Network *network) const; - virtual void mergeInto(ExceptionPt *pt, - const Network *network); - virtual const char *asString(const Network *network) const; - virtual size_t objectCount() const; + int compare(ExceptionPt *pt, + const Network *network) const override; + void mergeInto(ExceptionPt *pt, + const Network *network) override; + const char *asString(const Network *network) const override; + size_t objectCount() const override; void deleteClock(Clock *clk); - virtual void addPin(const Pin *pin, - const Network *network); - virtual void addClock(Clock *clk); - virtual void addInstance(const Instance *inst, - const Network *network); - virtual void addNet(const Net *, - const Network *) {} - virtual void addEdge(const EdgePins &, - const Network *) {} - virtual void connectPinAfter(PinSet *, - Network *) {} - virtual void deletePinBefore(const Pin *, - Network *); + void addPin(const Pin *pin, + const Network *network) override; + void addClock(Clock *clk) override; + void addInstance(const Instance *inst, + const Network *network) override; + void addNet(const Net *, + const Network *) override {} + void addEdge(const EdgePins &, + const Network *) override {} + void connectPinAfter(PinSet *, + Network *) override {} + void deletePinBefore(const Pin *, + Network *) override; void deleteInstance(const Instance *inst, const Network *network); @@ -443,14 +443,14 @@ public: bool own_pts, const Network *network); ExceptionFrom *clone(const Network *network); - virtual bool isFrom() const { return true; } + bool isFrom() const override { return true; } bool intersectsPts(ExceptionFrom *from, const Network *network) const; - virtual int typePriority() const { return 0; } + int typePriority() const override { return 0; } protected: - virtual const char *cmdKeyword() const; - virtual void findHash(const Network *network); + const char *cmdKeyword() const override; + void findHash(const Network *network) override; }; class ExceptionTo : public ExceptionFromTo @@ -466,12 +466,12 @@ public: bool own_pts, const Network *network); ExceptionTo *clone(const Network *network); - virtual bool isTo() const { return true; } - const char *asString(const Network *network) const; + bool isTo() const override { return true; } + const char *asString(const Network *network) const override; const RiseFallBoth *endTransition() { return end_rf_; } bool intersectsPts(ExceptionTo *to, const Network *network) const; - virtual int typePriority() const { return 1; } + int typePriority() const override { return 1; } bool matches(const Pin *pin, const ClockEdge *clk_edge, const RiseFall *end_rf, @@ -486,8 +486,8 @@ public: const ClockEdge *clk_edge, const RiseFall *end_rf, const Network *network) const; - virtual int compare(ExceptionPt *pt, - const Network *network) const; + int compare(ExceptionPt *pt, + const Network *network) const override; protected: bool matches(const Pin *pin, @@ -495,7 +495,7 @@ protected: const RiseFall *end_rf, bool inst_matches_reg_clk_pin, const Network *network) const; - virtual const char *cmdKeyword() const; + const char *cmdKeyword() const override; // -rise|-fall endpoint transition. const RiseFallBoth *end_rf_; @@ -512,48 +512,48 @@ public: const Network *network); ~ExceptionThru(); ExceptionThru *clone(const Network *network); - virtual const char *asString(const Network *network) const; - virtual bool isThru() const { return true; } - PinSet *pins() { return pins_; } - EdgePinsSet *edges() { return edges_; } - NetSet *nets() { return nets_; } - InstanceSet *instances() { return insts_; } - virtual ClockSet *clks() { return nullptr; } + const char *asString(const Network *network) const override; + bool isThru() const override { return true; } + PinSet *pins() override { return pins_; } + EdgePinsSet *edges() override { return edges_; } + NetSet *nets() override { return nets_; } + InstanceSet *instances() override { return insts_; } + ClockSet *clks() override { return nullptr; } bool hasObjects() const; void deleteObjects(ExceptionThru *pt, const Network *network); - virtual PinSet allPins(const Network *network); + PinSet allPins(const Network *network) override; bool matches(const Pin *from_pin, const Pin *to_pin, const RiseFall *to_rf, const Network *network); bool equal(ExceptionThru *thru) const; - virtual int compare(ExceptionPt *pt, - const Network *network) const; - virtual void mergeInto(ExceptionPt *pt, - const Network *network); + int compare(ExceptionPt *pt, + const Network *network) const override; + void mergeInto(ExceptionPt *pt, + const Network *network) override; bool intersectsPts(ExceptionThru *thru, const Network *network) const; - virtual int typePriority() const { return 2; } - virtual size_t objectCount() const; - virtual void connectPinAfter(PinSet *drvrs, - Network *network); - virtual void deletePinBefore(const Pin *pin, - Network *network); + int typePriority() const override { return 2; } + size_t objectCount() const override; + void connectPinAfter(PinSet *drvrs, + Network *network) override; + void deletePinBefore(const Pin *pin, + Network *network) override; void deleteInstance(const Instance *inst, const Network *network); protected: void findHash(const Network *network); - virtual void addPin(const Pin *pin, - const Network *network); - virtual void addEdge(const EdgePins &edge, - const Network *network); - virtual void addNet(const Net *net, - const Network *network); - virtual void addInstance(const Instance *inst, - const Network *network); - virtual void addClock(Clock *) {} + void addPin(const Pin *pin, + const Network *network) override; + void addEdge(const EdgePins &edge, + const Network *network) override; + void addNet(const Net *net, + const Network *network) override; + void addInstance(const Instance *inst, + const Network *network) override; + void addClock(Clock *) override {} void deletePin(const Pin *pin, const Network *network); void deleteEdge(const EdgePins &edge); diff --git a/network/Network.cc b/network/Network.cc index ec77fa7a3..0e919d6b3 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -1365,7 +1365,7 @@ class ConnectedPinIterator1 : public ConnectedPinIterator { public: ConnectedPinIterator1(PinSet *pins); - virtual ~ConnectedPinIterator1(); + ~ConnectedPinIterator1() override; bool hasNext() override; const Pin *next() override; @@ -1401,7 +1401,7 @@ class FindConnectedPins : public PinVisitor { public: FindConnectedPins(PinSet *pins); - virtual void operator()(const Pin *pin); + void operator()(const Pin *pin) override; protected: PinSet *pins_; @@ -1572,7 +1572,7 @@ class FindDrvrPins : public PinVisitor public: FindDrvrPins(PinSet *pins, const Network *network); - virtual void operator()(const Pin *pin); + void operator()(const Pin *pin) override; protected: PinSet *pins_; diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 7517020d1..77a7b022b 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -1289,7 +1289,7 @@ class FindClkHpinDisables : public HpinDrvrLoadVisitor const Pin *load); protected: - virtual void visit(HpinDrvrLoad *drvr_load); + void visit(HpinDrvrLoad *drvr_load) override; void makeClkHpinDisables(const Pin *clk_src, const Pin *drvr, const Pin *load); @@ -3173,7 +3173,7 @@ class FindNetCaps : public PinVisitor float &fanout, bool &has_net_load, const Sdc *sdc); - virtual void operator()(const Pin *pin); + void operator()(const Pin *pin) override; protected: const RiseFall *rf_; @@ -3560,8 +3560,8 @@ class DisableEdgesThruHierPin : public HierPinThruVisitor Graph *graph); protected: - virtual void visit(const Pin *drvr, - const Pin *load); + void visit(const Pin *drvr, + const Pin *load) override; PinPairSet *pairs_; Graph *graph_; @@ -3601,8 +3601,8 @@ class RemoveDisableEdgesThruHierPin : public HierPinThruVisitor Graph *graph); protected: - virtual void visit(const Pin *drvr, - const Pin *load); + void visit(const Pin *drvr, + const Pin *load) override; PinPairSet *pairs_; Graph *graph_; @@ -5151,9 +5151,9 @@ class ExpandException : public ExpandedExceptionVisitor ExpandException(ExceptionPath *exception, ExceptionPathSet &expansions, Network *network); - virtual void visit(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to); + void visit(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to) override; private: ExceptionPathSet &expansions_; diff --git a/search/FindRegister.cc b/search/FindRegister.cc index a3607f61c..1b2cd4b7a 100644 --- a/search/FindRegister.cc +++ b/search/FindRegister.cc @@ -322,9 +322,9 @@ class FindRegInstances : public FindRegVisitor const Mode *mode); private: - virtual void visitReg(Instance *inst); - virtual void visitSequential(Instance *inst, - const Sequential *seq); + void visitReg(Instance *inst) override; + void visitSequential(Instance *inst, + const Sequential *seq) override; InstanceSet regs_; }; @@ -383,9 +383,9 @@ class FindRegPins : public FindRegVisitor const Mode *mode); protected: - virtual void visitReg(Instance *inst); - virtual void visitSequential(Instance *inst, - const Sequential *seq); + void visitReg(Instance *inst) override; + void visitSequential(Instance *inst, + const Sequential *seq) override; virtual bool matchPin(Pin *pin); void visitExpr(FuncExpr *expr, Instance *inst, @@ -461,9 +461,9 @@ class FindRegDataPins : public FindRegPins FindRegDataPins(const StaState *sta); private: - virtual bool matchPin(Pin *pin); - virtual FuncExpr *seqExpr1(const Sequential *seq); - virtual FuncExpr *seqExpr2(const Sequential *seq); + bool matchPin(Pin *pin) override; + FuncExpr *seqExpr1(const Sequential *seq) override; + FuncExpr *seqExpr2(const Sequential *seq) override; }; FindRegDataPins::FindRegDataPins(const StaState *sta) : @@ -528,9 +528,9 @@ class FindRegClkPins : public FindRegPins FindRegClkPins(const StaState *sta); private: - virtual bool matchPin(Pin *pin); - virtual FuncExpr *seqExpr1(const Sequential *seq); - virtual FuncExpr *seqExpr2(const Sequential *seq); + bool matchPin(Pin *pin) override; + FuncExpr *seqExpr1(const Sequential *seq) override; + FuncExpr *seqExpr2(const Sequential *seq) override; }; FindRegClkPins::FindRegClkPins(const StaState *sta) : @@ -586,9 +586,9 @@ class FindRegAsyncPins : public FindRegPins FindRegAsyncPins(const StaState *sta); private: - virtual bool matchPin(Pin *pin); - virtual FuncExpr *seqExpr1(const Sequential *seq) { return seq->clear(); } - virtual FuncExpr *seqExpr2(const Sequential *seq) { return seq->preset(); } + bool matchPin(Pin *pin) override; + FuncExpr *seqExpr1(const Sequential *seq) override { return seq->clear(); } + FuncExpr *seqExpr2(const Sequential *seq) override { return seq->preset(); } }; FindRegAsyncPins::FindRegAsyncPins(const StaState *sta) : @@ -629,14 +629,14 @@ class FindRegOutputPins : public FindRegPins FindRegOutputPins(const StaState *sta); private: - virtual bool matchPin(Pin *pin); - virtual void visitSequential(Instance *inst, - const Sequential *seq); + bool matchPin(Pin *pin) override; + void visitSequential(Instance *inst, + const Sequential *seq) override; void visitOutput(LibertyPort *port, Instance *inst); // Unused. - virtual FuncExpr *seqExpr1(const Sequential *seq); - virtual FuncExpr *seqExpr2(const Sequential *seq); + FuncExpr *seqExpr1(const Sequential *seq) override; + FuncExpr *seqExpr2(const Sequential *seq) override; }; FindRegOutputPins::FindRegOutputPins(const StaState *sta) : diff --git a/search/Genclks.cc b/search/Genclks.cc index 225dfcf07..fde24a63c 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -707,8 +707,8 @@ class GenclkSrcArrivalVisitor : public ArrivalVisitor BfsFwdIterator *insert_iter, GenclkInfo *genclk_info, const Mode *mode); - virtual VertexVisitor *copy() const; - virtual void visit(Vertex *vertex); + VertexVisitor *copy() const override; + void visit(Vertex *vertex) override; protected: GenclkSrcArrivalVisitor(Clock *gclk, diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index d709f4779..1e3388d75 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -236,9 +236,9 @@ class MakeEndTimingArcs : public PathEndVisitor public: MakeEndTimingArcs(Sta *sta); MakeEndTimingArcs(const MakeEndTimingArcs&) = default; - virtual ~MakeEndTimingArcs() {} - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); + ~MakeEndTimingArcs() override {} + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; void setInputRf(const RiseFall *input_rf); const ClockEdgeDelays &margins() const { return margins_; } diff --git a/search/PathEnum.cc b/search/PathEnum.cc index 67465a1bf..efe385ecc 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -246,11 +246,11 @@ class PathEnumFaninVisitor : public PathVisitor bool unique_pins, bool unique_edges, PathEnum *path_enum); - virtual VertexVisitor *copy() const override; + VertexVisitor *copy() const override; void visitFaninPathsThru(Path *before_div, Vertex *prev_vertex, TimingArc *prev_arc); - virtual bool visitFromToPath(const Pin *from_pin, + bool visitFromToPath(const Pin *from_pin, Vertex *from_vertex, const RiseFall *from_rf, Tag *from_tag, @@ -277,7 +277,7 @@ class PathEnumFaninVisitor : public PathVisitor Edge *edge, const Pin *to_pin, Vertex *to_vertex) override; - virtual void visit(Vertex *) override {} // Not used. + void visit(Vertex *) override {} // Not used. void insertUniqueEdgeDiv(Diversion *div); void reportDiversion(const Edge *edge, const TimingArc *div_arc, diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 8f3738c65..1fb9e4ea1 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -601,9 +601,9 @@ class MakePathEnds1 : public PathEndVisitor public: MakePathEnds1(PathGroups *path_groups); MakePathEnds1(const MakePathEnds1&) = default; - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); - virtual void vertexEnd(Vertex *vertex); + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; + void vertexEnd(Vertex *vertex) override; private: void visitPathEnd(PathEnd *path_end, @@ -676,10 +676,10 @@ class MakePathEndsAll : public PathEndVisitor MakePathEndsAll(int endpoint_path_count, PathGroups *path_groups); MakePathEndsAll(const MakePathEndsAll&) = default; - virtual ~MakePathEndsAll(); - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); - virtual void vertexEnd(Vertex *vertex); + ~MakePathEndsAll() override; + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; + void vertexEnd(Vertex *vertex) override; private: void visitPathEnd(PathEnd *path_end, @@ -906,8 +906,8 @@ class MakeEndpointPathEnds : public VertexVisitor const StaState *sta); MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends); ~MakeEndpointPathEnds(); - virtual VertexVisitor *copy() const; - virtual void visit(Vertex *vertex); + VertexVisitor *copy() const override; + void visit(Vertex *vertex) override; private: VisitPathEnds visit_path_ends_; diff --git a/search/Search.cc b/search/Search.cc index 871b0e0d7..e4b154226 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -733,8 +733,8 @@ class SeedFaninsThruHierPin : public HierPinThruVisitor Search *search); protected: - virtual void visit(const Pin *drvr, - const Pin *load); + void visit(const Pin *drvr, + const Pin *load) override; Graph *graph_; Search *search_; @@ -3444,9 +3444,9 @@ class FindEndRequiredVisitor : public PathEndVisitor FindEndRequiredVisitor(RequiredCmp *required_cmp, const StaState *sta); FindEndRequiredVisitor(const StaState *sta); - virtual ~FindEndRequiredVisitor(); - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); + ~FindEndRequiredVisitor() override; + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; protected: const StaState *sta_; @@ -4092,8 +4092,8 @@ class FindEndSlackVisitor : public PathEndVisitor FindEndSlackVisitor(SlackSeq &slacks, const StaState *sta); FindEndSlackVisitor(const FindEndSlackVisitor &) = default; - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; protected: SlackSeq &slacks_; diff --git a/search/Sta.cc b/search/Sta.cc index a83835b1b..3b11c23fe 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -114,9 +114,9 @@ class StaDelayCalcObserver : public DelayCalcObserver { public: StaDelayCalcObserver(Search *search); - virtual void delayChangedFrom(Vertex *vertex); - virtual void delayChangedTo(Vertex *vertex); - virtual void checkDelayChangedTo(Vertex *vertex); + void delayChangedFrom(Vertex *vertex) override; + void delayChangedTo(Vertex *vertex) override; + void checkDelayChangedTo(Vertex *vertex) override; private: Search *search_; @@ -3390,8 +3390,8 @@ class MinPeriodEndVisitor : public PathEndVisitor bool include_port_paths, StaState *sta); MinPeriodEndVisitor(const MinPeriodEndVisitor &) = default; - virtual PathEndVisitor *copy() const; - virtual void visit(PathEnd *path_end); + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; float minPeriod() const { return min_period_; } private: diff --git a/util/ReportStd.cc b/util/ReportStd.cc index abf088d3d..b94e97232 100644 --- a/util/ReportStd.cc +++ b/util/ReportStd.cc @@ -38,7 +38,7 @@ class ReportStd : public Report ReportStd(); protected: - virtual size_t printConsole(const char *buffer, size_t length); + size_t printConsole(const char *buffer, size_t length) override; virtual size_t printErrorConsole(const char *buffer, size_t length); }; From 0850e97b8872bf4003b6dd3126370c3f8953f70c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 9 Mar 2026 18:12:56 -0700 Subject: [PATCH 078/181] readLibraryAttributes only call when non-empty library Signed-off-by: James Cherry --- liberty/LibertyParser.cc | 18 ++++++++++++++++++ liberty/LibertyParser.hh | 2 ++ liberty/LibertyReader.cc | 6 ++++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index d85911b75..2049bec33 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -324,6 +324,24 @@ LibertyGroup::clear() deleteContents(variables_); } +bool +LibertyGroup::empty() const +{ + 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(); +} + void LibertyGroup::addSubgroup(LibertyGroup *subgroup) { diff --git a/liberty/LibertyParser.hh b/liberty/LibertyParser.hh index 2f533c364..7f426f716 100644 --- a/liberty/LibertyParser.hh +++ b/liberty/LibertyParser.hh @@ -127,6 +127,8 @@ public: int line); ~LibertyGroup(); void clear(); + bool empty() const; + bool oneGroupOnly() const; const std::string &type() const { return type_; } const LibertyAttrValueSeq ¶ms() const { return params_; } // First param as a string. diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 5d99d1f16..9f8a696e9 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -151,7 +151,8 @@ LibertyReader::endLibrary(const LibertyGroup *library_group, LibertyGroup *) { // If a library has no cells endCell is not called. - readLibraryAttributes(library_group); + if (!library_group->empty()) + readLibraryAttributes(library_group); checkThresholds(library_group); delete library_group; } @@ -166,7 +167,8 @@ LibertyReader::endCell(const LibertyGroup *cell_group, // Normally they are all defined by the first cell, but there // are libraries that define table templates and bus tyupes // between cells. - readLibraryAttributes(library_group); + if (!library_group->oneGroupOnly()) + readLibraryAttributes(library_group); const char *name = cell_group->firstName(); if (name) { From 2c1024a6bb1857f3819512f7a5e54b0931c424e1 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 10 Mar 2026 10:26:59 -0700 Subject: [PATCH 079/181] path end leak Signed-off-by: James Cherry --- include/sta/BoundedHeap.hh | 23 ++++++++++++++--------- include/sta/PathGroup.hh | 1 + search/PathGroup.cc | 17 ++++++++++++++--- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/include/sta/BoundedHeap.hh b/include/sta/BoundedHeap.hh index af934e0c5..676c1e394 100644 --- a/include/sta/BoundedHeap.hh +++ b/include/sta/BoundedHeap.hh @@ -27,6 +27,7 @@ #include #include #include +#include namespace sta { @@ -118,13 +119,14 @@ public: // If the heap is not full, the element is added. // If the heap is full and the new element is better than the worst element, // the worst element is replaced. Otherwise, the element is ignored. - // Returns true if the element was inserted, false if it was ignored. - bool + // Returns (inserted, displaced): inserted is true if the element was inserted, + // displaced is set when an element was pushed out (caller must handle ownership). + std::pair> insert(const T& value) { if (heap_.size() < max_size_) { heap_.push_back(value); std::push_heap(heap_.begin(), heap_.end(), min_heap_comp_); - return true; + return {true, std::nullopt}; } else if (!heap_.empty()) { // When keeping N worst (smallest) values: if new value is smaller than worst, @@ -134,23 +136,25 @@ public: if (comp_(value, heap_.front())) { // New value is smaller than worst - find and replace the largest element auto max_it = std::max_element(heap_.begin(), heap_.end(), comp_); + T displaced = std::move(*max_it); *max_it = value; // Rebuild heap since we modified an internal element std::make_heap(heap_.begin(), heap_.end(), min_heap_comp_); - return true; + return {true, std::move(displaced)}; } // Otherwise, new value is >= worst, so we already have worse values - reject it } - return false; + return {false, std::nullopt}; } // Insert an element using move semantics - bool insert(T&& value) + std::pair> + insert(T&& value) { if (heap_.size() < max_size_) { heap_.push_back(std::move(value)); std::push_heap(heap_.begin(), heap_.end(), min_heap_comp_); - return true; + return {true, std::nullopt}; } else if (!heap_.empty()) { // When keeping N worst (smallest) values: if new value is smaller than worst, @@ -160,14 +164,15 @@ public: if (comp_(value, heap_.front())) { // New value is smaller than worst - find and replace the largest element auto max_it = std::max_element(heap_.begin(), heap_.end(), comp_); + T displaced = std::move(*max_it); *max_it = std::move(value); // Rebuild heap since we modified an internal element std::make_heap(heap_.begin(), heap_.end(), min_heap_comp_); - return true; + return {true, std::move(displaced)}; } // Otherwise, new value is >= worst, so we already have worse values - reject it } - return false; + return {false, std::nullopt}; } // Extract all elements sorted from best to worst. diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index 03bcb0c5d..f71b9f383 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -67,6 +67,7 @@ public: float min_slack, float max_slack, const StaState *sta); + ~PathGroup(); const std::string &name() const { return name_; } const MinMax *minMax() const { return min_max_;} PathEndSeq pathEnds() const; diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 1fb9e4ea1..7a5ccd33f 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -105,6 +105,12 @@ PathGroup::PathGroup(const char *name, { } +PathGroup::~PathGroup() +{ + PathEndSeq path_ends = heap_.extract(); + deleteContents(path_ends); +} + PathEndSeq PathGroup::pathEnds() const { @@ -170,15 +176,20 @@ void PathGroup::insert(PathEnd *path_end) { LockGuard lock(lock_); - heap_.insert(path_end); - path_end->setPathGroup(this); + auto [inserted, displaced] = heap_.insert(path_end); + if (inserted) + path_end->setPathGroup(this); + else + delete path_end; + if (displaced) + delete *displaced; } void PathGroup::pushEnds(PathEndSeq &path_ends) { if (!heap_.empty()) { - PathEndSeq ends = heap_.extract(); + 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(), From 2ada1ce715e732beb5e727d0c080c35501d600f5 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 10 Mar 2026 12:33:39 -0700 Subject: [PATCH 080/181] delete_all_memory mem use after delete Signed-off-by: James Cherry --- search/Sta.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/Sta.cc b/search/Sta.cc index 3b11c23fe..b6e2a3b6d 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -497,7 +497,6 @@ Sta::sta() Sta::~Sta() { - delete variables_; // Verilog modules refer to the network in the sta so it has // to deleted before the network. delete verilog_reader_; @@ -526,6 +525,7 @@ Sta::~Sta() delete power_; delete equiv_cells_; delete dispatch_queue_; + delete variables_; deleteContents(parasitics_name_map_); deleteContents(modes_); deleteContents(scenes_); From 6280635c3868da25ef9db0af56555c3928ab2984 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 10 Mar 2026 12:58:53 -0700 Subject: [PATCH 081/181] liberty statetable ref test_cell ports resolves #276 Signed-off-by: James Cherry --- liberty/LibertyReader.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 9f8a696e9..2ef3e2c54 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -986,6 +986,8 @@ LibertyReader::readCell(LibertyCell *cell, // Make ff/latch output ports. makeSequentials(cell, cell_group); + // Test cell ports may be referenced by a statetable. + readTestCell(cell, cell_group); readCellAttributes(cell, cell_group); @@ -1000,8 +1002,6 @@ LibertyReader::readCell(LibertyCell *cell, readInternalPowerGroups(cell, ports, port_group); } - readTestCell(cell, cell_group); - cell->finish(infer_latches_, report_, debug_); } @@ -2966,6 +2966,9 @@ LibertyReader::readStatetable(LibertyCell *cell, LibertyPortSeq input_port_ptrs; for (const std::string &input : input_ports) { LibertyPort *port = cell->findLibertyPort(input.c_str()); + if (port == nullptr + && cell->testCell()) + port = cell->testCell()->findLibertyPort(input.c_str()); if (port) input_port_ptrs.push_back(port); else From fbe9da3fb73f0918b3a1f25bacb4f84c0a7a5aa3 Mon Sep 17 00:00:00 2001 From: Deepashree Sengupta Date: Tue, 10 Mar 2026 17:57:21 -0400 Subject: [PATCH 082/181] Fix for OpenSTA issue 398 and OpenROAD issue 9454 with regression (#401) * Fix for OpenSTA issue 398 and OpenROAD issue 9454 with regression Signed-off-by: dsengupta0628 * Incorporated feedbacks from previous version Signed-off-by: dsengupta0628 * rename tests Signed-off-by: dsengupta0628 * remove unnecessary newline Signed-off-by: dsengupta0628 * Updated to use network_->portBitIterator Signed-off-by: dsengupta0628 --------- Signed-off-by: dsengupta0628 --- test/regression_vars.tcl | 1 + test/verilog_unconnected_hpin.ok | 13 +++++++++++++ test/verilog_unconnected_hpin.tcl | 9 +++++++++ test/verilog_unconnected_hpin.v | 24 ++++++++++++++++++++++++ verilog/VerilogReader.cc | 20 +++++++++++--------- 5 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 test/verilog_unconnected_hpin.ok create mode 100644 test/verilog_unconnected_hpin.tcl create mode 100644 test/verilog_unconnected_hpin.v diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index ab1f6c62d..edc16ee06 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -166,6 +166,7 @@ record_public_tests { verilog_attribute verilog_specify verilog_write_escape + verilog_unconnected_hpin } define_test_group fast [group_tests all] diff --git a/test/verilog_unconnected_hpin.ok b/test/verilog_unconnected_hpin.ok new file mode 100644 index 000000000..3c5cb8cfe --- /dev/null +++ b/test/verilog_unconnected_hpin.ok @@ -0,0 +1,13 @@ +Find b1/out2: b1/out2 +Find b2/out2: b2/out2 +Net b2/out2 + Pin capacitance: 0.00 + Wire capacitance: 0.00 + Total capacitance: 0.00 + Number of drivers: 1 + Number of loads: 0 + Number of pins: 1 + +Driver pins + b2/u3/Y output (BUFx2_ASAP7_75t_R) + diff --git a/test/verilog_unconnected_hpin.tcl b/test/verilog_unconnected_hpin.tcl new file mode 100644 index 000000000..351633984 --- /dev/null +++ b/test/verilog_unconnected_hpin.tcl @@ -0,0 +1,9 @@ +read_liberty asap7_small.lib.gz +read_verilog verilog_unconnected_hpin.v +link_design top +puts "Find b1/out2: [get_full_name [get_pins b1/out2]]" +puts "Find b2/out2: [get_full_name [get_pins b2/out2]]" +# Check if net is connected to "b2/u3/Y" that was the b2/out2 in parent block +set iterm [sta::find_pin "b2/u3/Y"] +set net [get_net -of_object [get_pin $iterm]] +report_net [get_full_name $net] diff --git a/test/verilog_unconnected_hpin.v b/test/verilog_unconnected_hpin.v new file mode 100644 index 000000000..eec7865d1 --- /dev/null +++ b/test/verilog_unconnected_hpin.v @@ -0,0 +1,24 @@ +module top (in, clk1, clk2, out, out2); + input in, clk1, clk2; + output out, out2; + block1 b1 (.in(in), .clk(clk1), .out(b1out), .out2(out2)); + block2 b2 (.in(b1out), .clk(clk2), .out(out)); +endmodule // top + +module block1 (in, clk, out, out2); + input in, clk; + output out, out2; + BUFx2_ASAP7_75t_R u1 (.A(in), .Y(u1out)); + DFFHQx4_ASAP7_75t_R r1 (.D(u1out), .CLK(clk), .Q(r1q)); + BUFx2_ASAP7_75t_R u2 (.A(r1q), .Y(out)); + BUFx2_ASAP7_75t_R u3 (.A(out), .Y(out2)); +endmodule // block1 + +module block2 (in, clk, out, out2); + input in, clk; + output out, out2; + BUFx2_ASAP7_75t_R u1 (.A(in), .Y(u1out)); + DFFHQx4_ASAP7_75t_R r1 (.D(u1out), .CLK(clk), .Q(r1q)); + BUFx2_ASAP7_75t_R u2 (.A(r1q), .Y(out)); + BUFx2_ASAP7_75t_R u3 (.A(out), .Y(out2)); +endmodule // block2 diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index de6099a55..205db906d 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -1824,14 +1824,13 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, } } - if (lib_cell) { - // Make all pins so timing arcs are built. - LibertyCellPortBitIterator port_iter(lib_cell); - while (port_iter.hasNext()) { - LibertyPort *port = port_iter.next(); - network_->makePin(inst, reinterpret_cast(port), nullptr); - } + // Make all pins so timing arcs are built and get_pins finds them. + CellPortBitIterator *port_iter = network_->portBitIterator(cell); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + network_->makePin(inst, port, nullptr); } + delete port_iter; bool is_leaf = network_->isLeaf(cell); VerilogBindingTbl bindings(zero_net_name_, one_net_name_); if (mod_inst->hasPins()) { @@ -1983,8 +1982,11 @@ VerilogReader::makeInstPin(Instance *inst, network_->connect(inst, port, net); } else { - Pin *pin = network_->makePin(inst, port, net); - if (!is_leaf && net) { + // Pin should already exist by prior makePin, then connect to parent + // net if present and create a term for the child-side net. + Pin *pin = network_->findPin(inst, port); + if (net) { + network_->connect(inst, port, net); const char *port_name = network_->name(port); Net *child_net = bindings->ensureNetBinding(port_name, inst, network_); network_->makeTerm(pin, child_net); From 83d08b5b5ce34d330d6276cb96953bfbb78ea5ce Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 10 Mar 2026 13:09:26 -0700 Subject: [PATCH 083/181] verilog reader use override Signed-off-by: James Cherry --- verilog/VerilogReader.cc | 2 +- verilog/VerilogReaderPvt.hh | 88 ++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 205db906d..76f49e0f9 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -1221,7 +1221,7 @@ class VerilogNetConcatNameIterator : public VerilogNetNameIterator VerilogNetConcatNameIterator(VerilogNetSeq *nets, VerilogModule *module, VerilogReader *reader); - virtual ~VerilogNetConcatNameIterator(); + ~VerilogNetConcatNameIterator() override; bool hasNext() override; const std::string &next() override; diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index 23f243058..fd0f57ad1 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -61,7 +61,7 @@ public: const std::string &filename, int line, VerilogReader *reader); - virtual ~VerilogModule(); + ~VerilogModule() override; const std::string &name() { return name_; } const char *filename() { return filename_.c_str(); } VerilogAttrStmtSeq *attrStmts() { return attr_stmts_; } @@ -128,10 +128,10 @@ public: VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, int line); - virtual bool isBus() const { return true; } + bool isBus() const override { return true; } int fromIndex() const { return from_index_; } int toIndex() const { return to_index_; } - virtual int size() const; + int size() const override; private: int from_index_; @@ -161,8 +161,8 @@ public: VerilogAssign(VerilogNet *lhs, VerilogNet *rhs, int line); - virtual ~VerilogAssign(); - virtual bool isAssign() const { return true; } + ~VerilogAssign() override; + bool isAssign() const override { return true; } VerilogNet *lhs() const { return lhs_; } VerilogNet *rhs() const { return rhs_; } @@ -177,8 +177,8 @@ public: VerilogInst(const std::string &inst_name, VerilogAttrStmtSeq *attr_stmts, const int line); - virtual ~VerilogInst(); - virtual bool isInstance() const { return true; } + ~VerilogInst() override; + bool isInstance() const override { return true; } const std::string &instanceName() const { return inst_name_; } VerilogAttrStmtSeq *attrStmts() const { return attr_stmts_; } void setInstanceName(const std::string &inst_name); @@ -196,8 +196,8 @@ public: VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, const int line); - virtual ~VerilogModuleInst(); - virtual bool isModuleInst() const { return true; } + ~VerilogModuleInst() override; + bool isModuleInst() const override { return true; } const std::string &moduleName() const { return module_name_; } VerilogNetSeq *pins() const { return pins_; } bool namedPins(); @@ -219,7 +219,7 @@ public: const StringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, const int line); - virtual bool isLibertyInst() const { return true; } + bool isLibertyInst() const override { return true; } LibertyCell *cell() const { return cell_; } const StringSeq &netNames() const { return net_names_; } @@ -258,7 +258,7 @@ class VerilogNetNamed : public VerilogNet { public: VerilogNetNamed(const std::string &name); - virtual ~VerilogNetNamed(); + ~VerilogNetNamed() override; bool isNamed() const override { return true; } virtual bool isScalar() const = 0; const std::string &name() const override { return name_; } @@ -272,10 +272,10 @@ class VerilogNetScalar : public VerilogNetNamed { public: VerilogNetScalar(const std::string &name); - virtual bool isScalar() const { return true; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + bool isScalar() const override { return true; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; }; class VerilogNetBitSelect : public VerilogNetNamed @@ -284,10 +284,10 @@ public: VerilogNetBitSelect(const std::string &name, int index); int index() { return index_; } - virtual bool isScalar() const { return false; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + bool isScalar() const override { return false; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; private: int index_; }; @@ -298,10 +298,10 @@ public: VerilogNetPartSelect(const std::string &name, int from_index, int to_index); - virtual bool isScalar() const { return false; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + bool isScalar() const override { return false; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; int fromIndex() const { return from_index_; } int toIndex() const { return to_index_; } @@ -316,10 +316,10 @@ public: VerilogNetConstant(const std::string *constant, VerilogReader *reader, int line); - virtual ~VerilogNetConstant(); - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + ~VerilogNetConstant() override; + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; private: void parseConstant(const std::string *constant, @@ -341,10 +341,10 @@ class VerilogNetConcat : public VerilogNetUnnamed { public: VerilogNetConcat(VerilogNetSeq *nets); - virtual ~VerilogNetConcat(); - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); + ~VerilogNetConcat() override; + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; private: VerilogNetSeq *nets_; @@ -355,7 +355,7 @@ class VerilogNetPortRef : public VerilogNetScalar { public: VerilogNetPortRef(const std::string &name); - virtual bool isNamedPortRef() { return true; } + bool isNamedPortRef() override { return true; } virtual bool hasNet() = 0; }; @@ -369,12 +369,12 @@ public: VerilogNetPortRefScalarNet(const std::string &name); VerilogNetPortRefScalarNet(const std::string &name, const std::string &net_name); - virtual bool isScalar() const { return true; } - virtual bool isNamedPortRefScalarNet() const { return true; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); - virtual bool hasNet() { return !net_name_.empty(); } + bool isScalar() const override { return true; } + bool isNamedPortRefScalarNet() const override { return true; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; + bool hasNet() override { return !net_name_.empty(); } const std::string &netName() const { return net_name_; } void setNetName(const std::string &net_name) { net_name_ = net_name; } @@ -387,12 +387,12 @@ class VerilogNetPortRefScalar : public VerilogNetPortRef public: VerilogNetPortRefScalar(const std::string &name, VerilogNet *net); - virtual ~VerilogNetPortRefScalar(); - virtual bool isScalar() const { return true; } - virtual int size(VerilogModule *module); - virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, - VerilogReader *reader); - virtual bool hasNet() { return net_ != nullptr; } + ~VerilogNetPortRefScalar() override; + bool isScalar() const override { return true; } + int size(VerilogModule *module) override; + VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader) override; + bool hasNet() override { return net_ != nullptr; } private: VerilogNet *net_; From 981f44db68707c5e82449852b9a81a6189b35d4f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 10 Mar 2026 13:21:17 -0700 Subject: [PATCH 084/181] update copyright Signed-off-by: James Cherry --- app/StaMain.cc | 2 +- dcalc/ArcDcalcWaveforms.cc | 2 +- dcalc/ArcDcalcWaveforms.hh | 2 +- dcalc/ArcDelayCalc.cc | 2 +- dcalc/Arnoldi.hh | 2 +- dcalc/ArnoldiDelayCalc.cc | 2 +- dcalc/ArnoldiDelayCalc.hh | 2 +- dcalc/ArnoldiReduce.cc | 2 +- dcalc/ArnoldiReduce.hh | 2 +- dcalc/CcsCeffDelayCalc.cc | 2 +- dcalc/CcsCeffDelayCalc.hh | 2 +- dcalc/DelayCalc.cc | 2 +- dcalc/DelayCalc.i | 2 +- dcalc/DelayCalc.tcl | 2 +- dcalc/DelayCalcBase.cc | 2 +- dcalc/DelayCalcBase.hh | 2 +- dcalc/DmpCeff.cc | 2 +- dcalc/DmpCeff.hh | 2 +- dcalc/DmpDelayCalc.cc | 2 +- dcalc/DmpDelayCalc.hh | 2 +- dcalc/FindRoot.cc | 2 +- dcalc/FindRoot.hh | 2 +- dcalc/GraphDelayCalc.cc | 2 +- dcalc/LumpedCapDelayCalc.cc | 2 +- dcalc/LumpedCapDelayCalc.hh | 2 +- dcalc/NetCaps.cc | 2 +- dcalc/NetCaps.hh | 2 +- dcalc/ParallelDelayCalc.cc | 2 +- dcalc/ParallelDelayCalc.hh | 2 +- dcalc/PrimaDelayCalc.cc | 2 +- dcalc/PrimaDelayCalc.hh | 2 +- dcalc/UnitDelayCalc.cc | 2 +- dcalc/UnitDelayCalc.hh | 2 +- graph/DelayFloat.cc | 2 +- graph/DelayNormal1.cc | 2 +- graph/DelayNormal2.cc | 2 +- graph/Graph.cc | 2 +- graph/Graph.i | 2 +- graph/Graph.tcl | 2 +- graph/GraphCmp.cc | 2 +- include/sta/ArcDelayCalc.hh | 2 +- include/sta/Bdd.hh | 2 +- include/sta/Bfs.hh | 2 +- include/sta/BoundedHeap.hh | 2 +- include/sta/CircuitSim.hh | 2 +- include/sta/ClkNetwork.hh | 2 +- include/sta/Clock.hh | 2 +- include/sta/ClockGatingCheck.hh | 2 +- include/sta/ClockGroups.hh | 2 +- include/sta/ClockInsertion.hh | 2 +- include/sta/ClockLatency.hh | 2 +- include/sta/ConcreteLibrary.hh | 2 +- include/sta/ConcreteNetwork.hh | 2 +- include/sta/ContainerHelpers.hh | 2 +- include/sta/CycleAccting.hh | 2 +- include/sta/DataCheck.hh | 2 +- include/sta/Debug.hh | 2 +- include/sta/Delay.hh | 2 +- include/sta/DelayCalc.hh | 2 +- include/sta/DelayFloat.hh | 2 +- include/sta/DelayNormal1.hh | 2 +- include/sta/DelayNormal2.hh | 2 +- include/sta/DeratingFactors.hh | 2 +- include/sta/DisabledPorts.hh | 2 +- include/sta/EnumNameMap.hh | 2 +- include/sta/EquivCells.hh | 2 +- include/sta/Error.hh | 2 +- include/sta/ExceptionPath.hh | 2 +- include/sta/FuncExpr.hh | 2 +- include/sta/Fuzzy.hh | 2 +- include/sta/Graph.hh | 2 +- include/sta/GraphClass.hh | 2 +- include/sta/GraphCmp.hh | 2 +- include/sta/GraphDelayCalc.hh | 2 +- include/sta/Hash.hh | 2 +- include/sta/HpinDrvrLoad.hh | 2 +- include/sta/InputDrive.hh | 2 +- include/sta/InternalPower.hh | 2 +- include/sta/Iterator.hh | 2 +- include/sta/LeakagePower.hh | 2 +- include/sta/Liberty.hh | 2 +- include/sta/LibertyClass.hh | 2 +- include/sta/LibertyWriter.hh | 2 +- include/sta/LinearModel.hh | 2 +- include/sta/Machine.hh | 2 +- include/sta/MakeConcreteNetwork.hh | 2 +- include/sta/MinMax.hh | 2 +- include/sta/MinMaxValues.hh | 2 +- include/sta/Mode.hh | 2 +- include/sta/Mutex.hh | 2 +- include/sta/Network.hh | 2 +- include/sta/NetworkClass.hh | 2 +- include/sta/NetworkCmp.hh | 2 +- include/sta/ObjectId.hh | 2 +- include/sta/ObjectTable.hh | 2 +- include/sta/Parasitics.hh | 2 +- include/sta/ParasiticsClass.hh | 2 +- include/sta/ParseBus.hh | 2 +- include/sta/Path.hh | 2 +- include/sta/PathEnd.hh | 2 +- include/sta/PathExpanded.hh | 2 +- include/sta/PathGroup.hh | 2 +- include/sta/PatternMatch.hh | 2 +- include/sta/PinPair.hh | 2 +- include/sta/PortDelay.hh | 2 +- include/sta/PortDirection.hh | 2 +- include/sta/PortExtCap.hh | 2 +- include/sta/PowerClass.hh | 2 +- include/sta/Property.hh | 2 +- include/sta/Report.hh | 2 +- include/sta/ReportStd.hh | 2 +- include/sta/ReportTcl.hh | 2 +- include/sta/RiseFallMinMax.hh | 2 +- include/sta/RiseFallMinMaxDelay.hh | 2 +- include/sta/RiseFallValues.hh | 2 +- include/sta/Scene.hh | 2 +- include/sta/Sdc.hh | 2 +- include/sta/SdcClass.hh | 2 +- include/sta/SdcCmdComment.hh | 2 +- include/sta/SdcNetwork.hh | 2 +- include/sta/Search.hh | 2 +- include/sta/SearchClass.hh | 2 +- include/sta/SearchPred.hh | 2 +- include/sta/Sequential.hh | 2 +- include/sta/Sta.hh | 2 +- include/sta/StaMain.hh | 2 +- include/sta/StaState.hh | 2 +- include/sta/Stats.hh | 2 +- include/sta/TableModel.hh | 2 +- include/sta/TclTypeHelpers.hh | 2 +- include/sta/TimingArc.hh | 2 +- include/sta/TimingModel.hh | 2 +- include/sta/TimingRole.hh | 2 +- include/sta/Transition.hh | 2 +- include/sta/Units.hh | 2 +- include/sta/Variables.hh | 2 +- include/sta/VectorMap.hh | 2 +- include/sta/VerilogNamespace.hh | 2 +- include/sta/VerilogReader.hh | 2 +- include/sta/VerilogWriter.hh | 2 +- include/sta/VertexId.hh | 2 +- include/sta/VertexVisitor.hh | 2 +- include/sta/VisitPathEnds.hh | 2 +- include/sta/Wireload.hh | 2 +- include/sta/Zlib.hh | 2 +- liberty/EquivCells.cc | 2 +- liberty/FuncExpr.cc | 2 +- liberty/InternalPower.cc | 2 +- liberty/LeakagePower.cc | 2 +- liberty/LibExprParse.yy | 2 +- liberty/LibExprReader.cc | 2 +- liberty/LibExprReader.hh | 2 +- liberty/LibExprReaderPvt.hh | 2 +- liberty/LibExprScanner.hh | 2 +- liberty/Liberty.cc | 2 +- liberty/Liberty.i | 2 +- liberty/Liberty.tcl | 2 +- liberty/LibertyBuilder.cc | 2 +- liberty/LibertyBuilder.hh | 2 +- liberty/LibertyParse.yy | 2 +- liberty/LibertyParser.cc | 2 +- liberty/LibertyParser.hh | 2 +- liberty/LibertyReader.cc | 2 +- liberty/LibertyReader.hh | 2 +- liberty/LibertyReaderPvt.hh | 2 +- liberty/LibertyScanner.hh | 2 +- liberty/LibertyWriter.cc | 2 +- liberty/LinearModel.cc | 2 +- liberty/Sequential.cc | 2 +- liberty/TableModel.cc | 2 +- liberty/TimingArc.cc | 2 +- liberty/TimingModel.cc | 2 +- liberty/TimingRole.cc | 2 +- liberty/Units.cc | 2 +- liberty/Wireload.cc | 2 +- network/ConcreteLibrary.cc | 2 +- network/ConcreteNetwork.cc | 2 +- network/HpinDrvrLoad.cc | 2 +- network/Link.tcl | 2 +- network/Network.cc | 2 +- network/Network.i | 2 +- network/Network.tcl | 2 +- network/NetworkCmp.cc | 2 +- network/NetworkEdit.i | 2 +- network/NetworkEdit.tcl | 2 +- network/ParseBus.cc | 2 +- network/PortDirection.cc | 2 +- network/SdcNetwork.cc | 2 +- network/VerilogNamespace.cc | 2 +- parasitics/ConcreteParasitics.cc | 2 +- parasitics/ConcreteParasitics.hh | 2 +- parasitics/ConcreteParasiticsPvt.hh | 2 +- parasitics/EstimateParasitics.cc | 2 +- parasitics/EstimateParasitics.hh | 2 +- parasitics/Parasitics.cc | 2 +- parasitics/Parasitics.i | 2 +- parasitics/Parasitics.tcl | 2 +- parasitics/ReduceParasitics.cc | 2 +- parasitics/ReduceParasitics.hh | 2 +- parasitics/ReportParasiticAnnotation.cc | 2 +- parasitics/ReportParasiticAnnotation.hh | 2 +- parasitics/SpefNamespace.cc | 2 +- parasitics/SpefNamespace.hh | 2 +- parasitics/SpefParse.yy | 2 +- parasitics/SpefReader.cc | 2 +- parasitics/SpefReader.hh | 2 +- parasitics/SpefReaderPvt.hh | 2 +- parasitics/SpefScanner.hh | 2 +- power/Power.cc | 2 +- power/Power.hh | 2 +- power/Power.i | 2 +- power/Power.tcl | 2 +- power/ReportPower.cc | 2 +- power/ReportPower.hh | 2 +- power/SaifParse.yy | 2 +- power/SaifReader.cc | 2 +- power/SaifReader.hh | 2 +- power/SaifReaderPvt.hh | 2 +- power/SaifScanner.hh | 2 +- power/VcdParse.cc | 2 +- power/VcdParse.hh | 2 +- power/VcdReader.cc | 2 +- power/VcdReader.hh | 2 +- sdc/Clock.cc | 2 +- sdc/ClockGatingCheck.cc | 2 +- sdc/ClockGroups.cc | 2 +- sdc/ClockInsertion.cc | 2 +- sdc/ClockLatency.cc | 2 +- sdc/CycleAccting.cc | 2 +- sdc/DataCheck.cc | 2 +- sdc/DeratingFactors.cc | 2 +- sdc/DisabledPorts.cc | 2 +- sdc/ExceptionPath.cc | 2 +- sdc/InputDrive.cc | 2 +- sdc/PinPair.cc | 2 +- sdc/PortDelay.cc | 2 +- sdc/PortExtCap.cc | 2 +- sdc/Sdc.cc | 2 +- sdc/Sdc.i | 2 +- sdc/Sdc.tcl | 2 +- sdc/SdcCmdComment.cc | 2 +- sdc/Variables.cc | 2 +- sdc/WriteSdc.cc | 2 +- sdc/WriteSdc.hh | 2 +- sdc/WriteSdcPvt.hh | 2 +- sdf/ReportAnnotation.cc | 2 +- sdf/ReportAnnotation.hh | 2 +- sdf/Sdf.i | 2 +- sdf/Sdf.tcl | 2 +- sdf/SdfParse.yy | 2 +- sdf/SdfReader.cc | 2 +- sdf/SdfReader.hh | 2 +- sdf/SdfReaderPvt.hh | 2 +- sdf/SdfScanner.hh | 2 +- sdf/SdfWriter.cc | 2 +- sdf/SdfWriter.hh | 2 +- search/Bdd.cc | 2 +- search/Bfs.cc | 2 +- search/CheckCapacitances.cc | 2 +- search/CheckCapacitances.hh | 2 +- search/CheckFanouts.cc | 2 +- search/CheckFanouts.hh | 2 +- search/CheckMaxSkews.cc | 2 +- search/CheckMaxSkews.hh | 2 +- search/CheckMinPeriods.cc | 2 +- search/CheckMinPeriods.hh | 2 +- search/CheckMinPulseWidths.cc | 2 +- search/CheckMinPulseWidths.hh | 2 +- search/CheckSlews.cc | 2 +- search/CheckSlews.hh | 2 +- search/CheckTiming.cc | 2 +- search/CheckTiming.hh | 2 +- search/ClkDelays.hh | 2 +- search/ClkInfo.cc | 2 +- search/ClkInfo.hh | 2 +- search/ClkLatency.cc | 2 +- search/ClkLatency.hh | 2 +- search/ClkNetwork.cc | 2 +- search/ClkSkew.cc | 2 +- search/ClkSkew.hh | 2 +- search/Crpr.cc | 2 +- search/Crpr.hh | 2 +- search/FindRegister.cc | 2 +- search/FindRegister.hh | 2 +- search/GatedClk.cc | 2 +- search/GatedClk.hh | 2 +- search/Genclks.cc | 2 +- search/Genclks.hh | 2 +- search/Latches.cc | 2 +- search/Latches.hh | 2 +- search/Levelize.cc | 2 +- search/Levelize.hh | 2 +- search/MakeTimingModel.cc | 2 +- search/MakeTimingModel.hh | 2 +- search/MakeTimingModelPvt.hh | 2 +- search/Mode.cc | 2 +- search/Path.cc | 2 +- search/PathEnd.cc | 2 +- search/PathEnum.cc | 2 +- search/PathEnum.hh | 2 +- search/PathExpanded.cc | 2 +- search/PathGroup.cc | 2 +- search/Property.cc | 2 +- search/Property.i | 2 +- search/ReportPath.cc | 2 +- search/ReportPath.hh | 2 +- search/Scene.cc | 2 +- search/Search.cc | 2 +- search/Search.i | 2 +- search/Search.tcl | 2 +- search/SearchPred.cc | 2 +- search/Sim.cc | 2 +- search/Sim.hh | 2 +- search/Sta.cc | 2 +- search/StaState.cc | 2 +- search/Tag.cc | 2 +- search/Tag.hh | 2 +- search/TagGroup.cc | 2 +- search/TagGroup.hh | 2 +- search/VertexVisitor.cc | 2 +- search/VisitPathEnds.cc | 2 +- search/WorstSlack.cc | 2 +- search/WorstSlack.hh | 2 +- spice/WritePathSpice.cc | 2 +- spice/WritePathSpice.hh | 2 +- spice/WriteSpice.cc | 2 +- spice/WriteSpice.hh | 2 +- spice/WriteSpice.i | 2 +- spice/WriteSpice.tcl | 2 +- spice/Xyce.cc | 2 +- spice/Xyce.hh | 2 +- tcl/CmdArgs.tcl | 2 +- tcl/CmdUtil.tcl | 2 +- tcl/Exception.i | 2 +- tcl/Init.tcl | 2 +- tcl/Property.tcl | 2 +- tcl/Splash.tcl | 4 ++-- tcl/Sta.tcl | 2 +- tcl/StaTclTypes.i | 2 +- tcl/TclTypeHelpers.cc | 2 +- tcl/Util.tcl | 2 +- tcl/Variables.tcl | 2 +- util/Debug.cc | 2 +- util/Error.cc | 2 +- util/Fuzzy.cc | 2 +- util/Hash.cc | 2 +- util/Machine.cc | 2 +- util/MinMax.cc | 2 +- util/PatternMatch.cc | 2 +- util/Report.cc | 2 +- util/ReportStd.cc | 2 +- util/ReportTcl.cc | 2 +- util/RiseFallMinMax.cc | 2 +- util/RiseFallMinMaxDelay.cc | 2 +- util/RiseFallValues.cc | 2 +- util/Stats.cc | 2 +- util/Transition.cc | 2 +- util/Util.i | 2 +- verilog/Verilog.i | 2 +- verilog/Verilog.tcl | 2 +- verilog/VerilogParse.yy | 2 +- verilog/VerilogReader.cc | 2 +- verilog/VerilogReaderPvt.hh | 2 +- verilog/VerilogScanner.hh | 2 +- verilog/VerilogWriter.cc | 2 +- 365 files changed, 366 insertions(+), 366 deletions(-) diff --git a/app/StaMain.cc b/app/StaMain.cc index 5be334ea7..a7147ab03 100644 --- a/app/StaMain.cc +++ b/app/StaMain.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index c718e8b93..4c0fe95b5 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/ArcDcalcWaveforms.hh b/dcalc/ArcDcalcWaveforms.hh index 6babfce68..8c9964b9a 100644 --- a/dcalc/ArcDcalcWaveforms.hh +++ b/dcalc/ArcDcalcWaveforms.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index 2f6fcbfd9..a17ab9a9a 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/Arnoldi.hh b/dcalc/Arnoldi.hh index 775124137..51453bd6d 100644 --- a/dcalc/Arnoldi.hh +++ b/dcalc/Arnoldi.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 957963f36..970c91f11 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/ArnoldiDelayCalc.hh b/dcalc/ArnoldiDelayCalc.hh index 1802b08ee..de16171ec 100644 --- a/dcalc/ArnoldiDelayCalc.hh +++ b/dcalc/ArnoldiDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc index ac8095e28..5fee6823e 100644 --- a/dcalc/ArnoldiReduce.cc +++ b/dcalc/ArnoldiReduce.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/ArnoldiReduce.hh b/dcalc/ArnoldiReduce.hh index 96297bb77..aed95e524 100644 --- a/dcalc/ArnoldiReduce.hh +++ b/dcalc/ArnoldiReduce.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index ae71f6489..07d8ff1b5 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 27c5159a8..83a729111 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index 98a3e7b17..c149d6cb7 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index a0bbaea66..7577f0e50 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/DelayCalc.tcl b/dcalc/DelayCalc.tcl index e195fa234..967039aac 100644 --- a/dcalc/DelayCalc.tcl +++ b/dcalc/DelayCalc.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index 5d2019879..bc5b77e0e 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/DelayCalcBase.hh b/dcalc/DelayCalcBase.hh index 6caa4f6c6..6d641c7ee 100644 --- a/dcalc/DelayCalcBase.hh +++ b/dcalc/DelayCalcBase.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 0b59f18be..4619bc979 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index fc3fbb2b9..d1931517a 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 5e8b37b01..e0a350d2c 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/DmpDelayCalc.hh b/dcalc/DmpDelayCalc.hh index aba00ac24..34ecbd7b3 100644 --- a/dcalc/DmpDelayCalc.hh +++ b/dcalc/DmpDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/FindRoot.cc b/dcalc/FindRoot.cc index 2b0e96653..dd35e7ba6 100644 --- a/dcalc/FindRoot.cc +++ b/dcalc/FindRoot.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/FindRoot.hh b/dcalc/FindRoot.hh index 7224671dd..a137b48ae 100644 --- a/dcalc/FindRoot.hh +++ b/dcalc/FindRoot.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 8bc0765ef..e33067d21 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index c9f2985c0..03d238085 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index 5cf829bd8..ac631f3ae 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/NetCaps.cc b/dcalc/NetCaps.cc index 0500b57dc..3ca8cd6aa 100644 --- a/dcalc/NetCaps.cc +++ b/dcalc/NetCaps.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/NetCaps.hh b/dcalc/NetCaps.hh index aeb1d9ff8..cb0db7eac 100644 --- a/dcalc/NetCaps.hh +++ b/dcalc/NetCaps.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/ParallelDelayCalc.cc b/dcalc/ParallelDelayCalc.cc index d4ead20fa..512ea9f5b 100644 --- a/dcalc/ParallelDelayCalc.cc +++ b/dcalc/ParallelDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/ParallelDelayCalc.hh b/dcalc/ParallelDelayCalc.hh index 45f868147..c7d3c921e 100644 --- a/dcalc/ParallelDelayCalc.hh +++ b/dcalc/ParallelDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 118233cef..b8b5c85c2 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index 361b23a43..c295478cb 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc index cafcf426b..61221c715 100644 --- a/dcalc/UnitDelayCalc.cc +++ b/dcalc/UnitDelayCalc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/dcalc/UnitDelayCalc.hh b/dcalc/UnitDelayCalc.hh index 094626cd1..7ae21affb 100644 --- a/dcalc/UnitDelayCalc.hh +++ b/dcalc/UnitDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/graph/DelayFloat.cc b/graph/DelayFloat.cc index 73b877dff..cdb19f9cb 100644 --- a/graph/DelayFloat.cc +++ b/graph/DelayFloat.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc index 2e855586f..f13518e04 100644 --- a/graph/DelayNormal1.cc +++ b/graph/DelayNormal1.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index b6d74df6b..be8936a02 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/graph/Graph.cc b/graph/Graph.cc index 6f37c6214..b397b7b28 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/graph/Graph.i b/graph/Graph.i index 289ca3ba9..5c16e5f8a 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/graph/Graph.tcl b/graph/Graph.tcl index 72c72e429..27ea5f193 100644 --- a/graph/Graph.tcl +++ b/graph/Graph.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/graph/GraphCmp.cc b/graph/GraphCmp.cc index 92aa24d06..3c3a5dfc9 100644 --- a/graph/GraphCmp.cc +++ b/graph/GraphCmp.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index 457de4cf8..d2b990122 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Bdd.hh b/include/sta/Bdd.hh index ee2249759..df565a5a3 100644 --- a/include/sta/Bdd.hh +++ b/include/sta/Bdd.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Bfs.hh b/include/sta/Bfs.hh index 5fa335c0e..162bd9a80 100644 --- a/include/sta/Bfs.hh +++ b/include/sta/Bfs.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/BoundedHeap.hh b/include/sta/BoundedHeap.hh index 676c1e394..ff0ee39d6 100644 --- a/include/sta/BoundedHeap.hh +++ b/include/sta/BoundedHeap.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/CircuitSim.hh b/include/sta/CircuitSim.hh index 201710dbd..3e5756e4a 100644 --- a/include/sta/CircuitSim.hh +++ b/include/sta/CircuitSim.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ClkNetwork.hh b/include/sta/ClkNetwork.hh index 8b4692fda..4bbfacfa4 100644 --- a/include/sta/ClkNetwork.hh +++ b/include/sta/ClkNetwork.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index 25dd92443..2b9efc5e1 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ClockGatingCheck.hh b/include/sta/ClockGatingCheck.hh index e87cdb749..414173841 100644 --- a/include/sta/ClockGatingCheck.hh +++ b/include/sta/ClockGatingCheck.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ClockGroups.hh b/include/sta/ClockGroups.hh index 38e905c11..e09cfb91e 100644 --- a/include/sta/ClockGroups.hh +++ b/include/sta/ClockGroups.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ClockInsertion.hh b/include/sta/ClockInsertion.hh index 43fc48999..1a1aef85f 100644 --- a/include/sta/ClockInsertion.hh +++ b/include/sta/ClockInsertion.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ClockLatency.hh b/include/sta/ClockLatency.hh index 9972bdc40..33479fd70 100644 --- a/include/sta/ClockLatency.hh +++ b/include/sta/ClockLatency.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ConcreteLibrary.hh b/include/sta/ConcreteLibrary.hh index 69e60dcea..a47585dda 100644 --- a/include/sta/ConcreteLibrary.hh +++ b/include/sta/ConcreteLibrary.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ConcreteNetwork.hh b/include/sta/ConcreteNetwork.hh index fe2b76c31..25ab0f915 100644 --- a/include/sta/ConcreteNetwork.hh +++ b/include/sta/ConcreteNetwork.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ContainerHelpers.hh b/include/sta/ContainerHelpers.hh index abc87ffbd..a626bc18c 100644 --- a/include/sta/ContainerHelpers.hh +++ b/include/sta/ContainerHelpers.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/CycleAccting.hh b/include/sta/CycleAccting.hh index 867d620c9..ad18a0cd7 100644 --- a/include/sta/CycleAccting.hh +++ b/include/sta/CycleAccting.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/DataCheck.hh b/include/sta/DataCheck.hh index 23ed758b9..a1bb30ee0 100644 --- a/include/sta/DataCheck.hh +++ b/include/sta/DataCheck.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Debug.hh b/include/sta/Debug.hh index 4c958065d..1c21131ee 100644 --- a/include/sta/Debug.hh +++ b/include/sta/Debug.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index 1711f78ee..860b23da4 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/DelayCalc.hh b/include/sta/DelayCalc.hh index cea48177e..be0da6d32 100644 --- a/include/sta/DelayCalc.hh +++ b/include/sta/DelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/DelayFloat.hh b/include/sta/DelayFloat.hh index a834bb578..81f047b92 100644 --- a/include/sta/DelayFloat.hh +++ b/include/sta/DelayFloat.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/DelayNormal1.hh b/include/sta/DelayNormal1.hh index 87beef239..5787797f8 100644 --- a/include/sta/DelayNormal1.hh +++ b/include/sta/DelayNormal1.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/DelayNormal2.hh b/include/sta/DelayNormal2.hh index f19a64119..7355fd77a 100644 --- a/include/sta/DelayNormal2.hh +++ b/include/sta/DelayNormal2.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/DeratingFactors.hh b/include/sta/DeratingFactors.hh index dcc6b344e..07678695b 100644 --- a/include/sta/DeratingFactors.hh +++ b/include/sta/DeratingFactors.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/DisabledPorts.hh b/include/sta/DisabledPorts.hh index d41661d46..20ea6a18b 100644 --- a/include/sta/DisabledPorts.hh +++ b/include/sta/DisabledPorts.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/EnumNameMap.hh b/include/sta/EnumNameMap.hh index 50ef13e64..68d1fe9aa 100644 --- a/include/sta/EnumNameMap.hh +++ b/include/sta/EnumNameMap.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/EquivCells.hh b/include/sta/EquivCells.hh index 3ceadeb78..1227a5449 100644 --- a/include/sta/EquivCells.hh +++ b/include/sta/EquivCells.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Error.hh b/include/sta/Error.hh index 2230f45f7..d73775f45 100644 --- a/include/sta/Error.hh +++ b/include/sta/Error.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ExceptionPath.hh b/include/sta/ExceptionPath.hh index 72a28f638..b7f7e655a 100644 --- a/include/sta/ExceptionPath.hh +++ b/include/sta/ExceptionPath.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/FuncExpr.hh b/include/sta/FuncExpr.hh index b32cf023d..00ff6dee7 100644 --- a/include/sta/FuncExpr.hh +++ b/include/sta/FuncExpr.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Fuzzy.hh b/include/sta/Fuzzy.hh index 0ae490aa5..5517190b7 100644 --- a/include/sta/Fuzzy.hh +++ b/include/sta/Fuzzy.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 7d9557b40..e96a2c647 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/GraphClass.hh b/include/sta/GraphClass.hh index ebe184c83..c56cea173 100644 --- a/include/sta/GraphClass.hh +++ b/include/sta/GraphClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/GraphCmp.hh b/include/sta/GraphCmp.hh index 7f51b87fa..bf67c9634 100644 --- a/include/sta/GraphCmp.hh +++ b/include/sta/GraphCmp.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index aea61fcb5..a1f5373af 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Hash.hh b/include/sta/Hash.hh index be6d5375b..ebd833a92 100644 --- a/include/sta/Hash.hh +++ b/include/sta/Hash.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/HpinDrvrLoad.hh b/include/sta/HpinDrvrLoad.hh index 995e6e14c..974156fc3 100644 --- a/include/sta/HpinDrvrLoad.hh +++ b/include/sta/HpinDrvrLoad.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/InputDrive.hh b/include/sta/InputDrive.hh index 9a3e31198..6eacc7e7d 100644 --- a/include/sta/InputDrive.hh +++ b/include/sta/InputDrive.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/InternalPower.hh b/include/sta/InternalPower.hh index 0f1836625..e37003ac6 100644 --- a/include/sta/InternalPower.hh +++ b/include/sta/InternalPower.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Iterator.hh b/include/sta/Iterator.hh index 06416b236..83e799ebf 100644 --- a/include/sta/Iterator.hh +++ b/include/sta/Iterator.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/LeakagePower.hh b/include/sta/LeakagePower.hh index f14c8db2f..15f84db21 100644 --- a/include/sta/LeakagePower.hh +++ b/include/sta/LeakagePower.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 981b2a26e..76bc763e0 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/LibertyClass.hh b/include/sta/LibertyClass.hh index 5436f4807..af00c3a4f 100644 --- a/include/sta/LibertyClass.hh +++ b/include/sta/LibertyClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/LibertyWriter.hh b/include/sta/LibertyWriter.hh index 1b142af52..a58ce44ea 100644 --- a/include/sta/LibertyWriter.hh +++ b/include/sta/LibertyWriter.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/LinearModel.hh b/include/sta/LinearModel.hh index a173ad6b0..aa7ba59e6 100644 --- a/include/sta/LinearModel.hh +++ b/include/sta/LinearModel.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Machine.hh b/include/sta/Machine.hh index 68e955778..b02e1e5f8 100644 --- a/include/sta/Machine.hh +++ b/include/sta/Machine.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/MakeConcreteNetwork.hh b/include/sta/MakeConcreteNetwork.hh index 3247a6f23..7f82834a9 100644 --- a/include/sta/MakeConcreteNetwork.hh +++ b/include/sta/MakeConcreteNetwork.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/MinMax.hh b/include/sta/MinMax.hh index 4642c7905..24b911553 100644 --- a/include/sta/MinMax.hh +++ b/include/sta/MinMax.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/MinMaxValues.hh b/include/sta/MinMaxValues.hh index 6a96ee0bd..7814c256c 100644 --- a/include/sta/MinMaxValues.hh +++ b/include/sta/MinMaxValues.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Mode.hh b/include/sta/Mode.hh index b47adc004..ddb824ad0 100644 --- a/include/sta/Mode.hh +++ b/include/sta/Mode.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Mutex.hh b/include/sta/Mutex.hh index 943596ed5..a36685823 100644 --- a/include/sta/Mutex.hh +++ b/include/sta/Mutex.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 5e88b04c3..f1c919acf 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/NetworkClass.hh b/include/sta/NetworkClass.hh index 9e72fef6a..3480d587a 100644 --- a/include/sta/NetworkClass.hh +++ b/include/sta/NetworkClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/NetworkCmp.hh b/include/sta/NetworkCmp.hh index dd7b871f5..ac5ffc5b9 100644 --- a/include/sta/NetworkCmp.hh +++ b/include/sta/NetworkCmp.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ObjectId.hh b/include/sta/ObjectId.hh index b4c04ef74..cd4558586 100644 --- a/include/sta/ObjectId.hh +++ b/include/sta/ObjectId.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ObjectTable.hh b/include/sta/ObjectTable.hh index 515d476a5..4736b5e0f 100644 --- a/include/sta/ObjectTable.hh +++ b/include/sta/ObjectTable.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index bd2a0940d..5d1800a97 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ParasiticsClass.hh b/include/sta/ParasiticsClass.hh index 02a7e2e4d..d75ff05f3 100644 --- a/include/sta/ParasiticsClass.hh +++ b/include/sta/ParasiticsClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ParseBus.hh b/include/sta/ParseBus.hh index a33daf017..d62bd9823 100644 --- a/include/sta/ParseBus.hh +++ b/include/sta/ParseBus.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Path.hh b/include/sta/Path.hh index c7fcd081c..6ca1d8cc2 100644 --- a/include/sta/Path.hh +++ b/include/sta/Path.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index ee8deb39a..899c96ace 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/PathExpanded.hh b/include/sta/PathExpanded.hh index 0269f6856..ebb9d080e 100644 --- a/include/sta/PathExpanded.hh +++ b/include/sta/PathExpanded.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index f71b9f383..418238bfa 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/PatternMatch.hh b/include/sta/PatternMatch.hh index 03c308aa9..429aef04d 100644 --- a/include/sta/PatternMatch.hh +++ b/include/sta/PatternMatch.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/PinPair.hh b/include/sta/PinPair.hh index bdc511352..073212efa 100644 --- a/include/sta/PinPair.hh +++ b/include/sta/PinPair.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/PortDelay.hh b/include/sta/PortDelay.hh index 9f36e396b..78cb246d5 100644 --- a/include/sta/PortDelay.hh +++ b/include/sta/PortDelay.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/PortDirection.hh b/include/sta/PortDirection.hh index b61f48f6c..38ca9b5b1 100644 --- a/include/sta/PortDirection.hh +++ b/include/sta/PortDirection.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/PortExtCap.hh b/include/sta/PortExtCap.hh index 716dcbb90..3e186b691 100644 --- a/include/sta/PortExtCap.hh +++ b/include/sta/PortExtCap.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/PowerClass.hh b/include/sta/PowerClass.hh index bedb018b6..55fb292fb 100644 --- a/include/sta/PowerClass.hh +++ b/include/sta/PowerClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Property.hh b/include/sta/Property.hh index 4bfa86ca3..ae89b599e 100644 --- a/include/sta/Property.hh +++ b/include/sta/Property.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Report.hh b/include/sta/Report.hh index 9e8aed56b..3ca8ffe0f 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ReportStd.hh b/include/sta/ReportStd.hh index a580b6288..c1743de82 100644 --- a/include/sta/ReportStd.hh +++ b/include/sta/ReportStd.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/ReportTcl.hh b/include/sta/ReportTcl.hh index fe3391fc2..127cb23c0 100644 --- a/include/sta/ReportTcl.hh +++ b/include/sta/ReportTcl.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/RiseFallMinMax.hh b/include/sta/RiseFallMinMax.hh index 81511ec51..3f9bca811 100644 --- a/include/sta/RiseFallMinMax.hh +++ b/include/sta/RiseFallMinMax.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/RiseFallMinMaxDelay.hh b/include/sta/RiseFallMinMaxDelay.hh index 5d102fdbe..22b999c91 100644 --- a/include/sta/RiseFallMinMaxDelay.hh +++ b/include/sta/RiseFallMinMaxDelay.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/RiseFallValues.hh b/include/sta/RiseFallValues.hh index a02c979e8..e1198afec 100644 --- a/include/sta/RiseFallValues.hh +++ b/include/sta/RiseFallValues.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Scene.hh b/include/sta/Scene.hh index 289863772..83e576684 100644 --- a/include/sta/Scene.hh +++ b/include/sta/Scene.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index add84e7f6..01e07ca71 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/SdcClass.hh b/include/sta/SdcClass.hh index adf159478..dc0563b53 100644 --- a/include/sta/SdcClass.hh +++ b/include/sta/SdcClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/SdcCmdComment.hh b/include/sta/SdcCmdComment.hh index 0ad099f1c..5e995fb5a 100644 --- a/include/sta/SdcCmdComment.hh +++ b/include/sta/SdcCmdComment.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/SdcNetwork.hh b/include/sta/SdcNetwork.hh index c20627f3b..e462af6d8 100644 --- a/include/sta/SdcNetwork.hh +++ b/include/sta/SdcNetwork.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Search.hh b/include/sta/Search.hh index ba4f8d0dc..5e9c5ccba 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -1,5 +1,5 @@ // opensta, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index c8f456276..4e41e94ef 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/SearchPred.hh b/include/sta/SearchPred.hh index 314c3dca7..0d688369a 100644 --- a/include/sta/SearchPred.hh +++ b/include/sta/SearchPred.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Sequential.hh b/include/sta/Sequential.hh index 67b62f3f1..89b3f5a9a 100644 --- a/include/sta/Sequential.hh +++ b/include/sta/Sequential.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index fee94bd19..7b39c47e8 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/StaMain.hh b/include/sta/StaMain.hh index 0d34ff079..db4291b85 100644 --- a/include/sta/StaMain.hh +++ b/include/sta/StaMain.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/StaState.hh b/include/sta/StaState.hh index 7d6ce5a70..2cfbede41 100644 --- a/include/sta/StaState.hh +++ b/include/sta/StaState.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Stats.hh b/include/sta/Stats.hh index 4d40594b8..6968c8fe7 100644 --- a/include/sta/Stats.hh +++ b/include/sta/Stats.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 1e88faf6e..59e26ba0d 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index a551c5845..95584b7bf 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 257e446dc..fcec3c4c0 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/TimingModel.hh b/include/sta/TimingModel.hh index c9f5ee8a7..db9caf08e 100644 --- a/include/sta/TimingModel.hh +++ b/include/sta/TimingModel.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/TimingRole.hh b/include/sta/TimingRole.hh index d77a84541..23a075f02 100644 --- a/include/sta/TimingRole.hh +++ b/include/sta/TimingRole.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Transition.hh b/include/sta/Transition.hh index ba6714530..4af601d4a 100644 --- a/include/sta/Transition.hh +++ b/include/sta/Transition.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Units.hh b/include/sta/Units.hh index 79bf472ac..3390c1031 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Variables.hh b/include/sta/Variables.hh index 2bd999901..9e5531938 100644 --- a/include/sta/Variables.hh +++ b/include/sta/Variables.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/VectorMap.hh b/include/sta/VectorMap.hh index 33cc50808..b78f76cfb 100644 --- a/include/sta/VectorMap.hh +++ b/include/sta/VectorMap.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/VerilogNamespace.hh b/include/sta/VerilogNamespace.hh index 835bd45e1..8649c2d78 100644 --- a/include/sta/VerilogNamespace.hh +++ b/include/sta/VerilogNamespace.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/VerilogReader.hh b/include/sta/VerilogReader.hh index e3aa94fa9..35311f6d0 100644 --- a/include/sta/VerilogReader.hh +++ b/include/sta/VerilogReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/VerilogWriter.hh b/include/sta/VerilogWriter.hh index a0ba63468..2867c7ea6 100644 --- a/include/sta/VerilogWriter.hh +++ b/include/sta/VerilogWriter.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/VertexId.hh b/include/sta/VertexId.hh index c2dbcfbc3..864139bf0 100644 --- a/include/sta/VertexId.hh +++ b/include/sta/VertexId.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/VertexVisitor.hh b/include/sta/VertexVisitor.hh index b371ef8ec..3b4a61763 100644 --- a/include/sta/VertexVisitor.hh +++ b/include/sta/VertexVisitor.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/VisitPathEnds.hh b/include/sta/VisitPathEnds.hh index e07fb2d09..74ef04965 100644 --- a/include/sta/VisitPathEnds.hh +++ b/include/sta/VisitPathEnds.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Wireload.hh b/include/sta/Wireload.hh index 426211714..01c54a886 100644 --- a/include/sta/Wireload.hh +++ b/include/sta/Wireload.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/include/sta/Zlib.hh b/include/sta/Zlib.hh index c1e143449..e7ca6fe8f 100644 --- a/include/sta/Zlib.hh +++ b/include/sta/Zlib.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/EquivCells.cc b/liberty/EquivCells.cc index af81a35e2..d0b96b303 100644 --- a/liberty/EquivCells.cc +++ b/liberty/EquivCells.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/FuncExpr.cc b/liberty/FuncExpr.cc index 0d422d1a8..b127cd278 100644 --- a/liberty/FuncExpr.cc +++ b/liberty/FuncExpr.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index 2516a36db..5380a359d 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LeakagePower.cc b/liberty/LeakagePower.cc index a5751c15f..150a2cf1a 100644 --- a/liberty/LeakagePower.cc +++ b/liberty/LeakagePower.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibExprParse.yy b/liberty/LibExprParse.yy index fe8f7e763..fc1a4a7de 100644 --- a/liberty/LibExprParse.yy +++ b/liberty/LibExprParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibExprReader.cc b/liberty/LibExprReader.cc index dfb2d679c..c3e1e5f99 100644 --- a/liberty/LibExprReader.cc +++ b/liberty/LibExprReader.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibExprReader.hh b/liberty/LibExprReader.hh index fa36aaa33..3f65600e0 100644 --- a/liberty/LibExprReader.hh +++ b/liberty/LibExprReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibExprReaderPvt.hh b/liberty/LibExprReaderPvt.hh index 06a65ea27..aa7d0646c 100644 --- a/liberty/LibExprReaderPvt.hh +++ b/liberty/LibExprReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibExprScanner.hh b/liberty/LibExprScanner.hh index f3ec9cf16..f901be3d6 100644 --- a/liberty/LibExprScanner.hh +++ b/liberty/LibExprScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 825502f68..de0190093 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/Liberty.i b/liberty/Liberty.i index d2856e686..2e1814eee 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/Liberty.tcl b/liberty/Liberty.tcl index 6d93601d7..698289c33 100644 --- a/liberty/Liberty.tcl +++ b/liberty/Liberty.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index 988c32e9a..72a72b1f6 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh index 065386876..708790920 100644 --- a/liberty/LibertyBuilder.hh +++ b/liberty/LibertyBuilder.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibertyParse.yy b/liberty/LibertyParse.yy index a3d4b7159..35ca71807 100644 --- a/liberty/LibertyParse.yy +++ b/liberty/LibertyParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index 2049bec33..5d16566d1 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibertyParser.hh b/liberty/LibertyParser.hh index 7f426f716..00f2ac33d 100644 --- a/liberty/LibertyParser.hh +++ b/liberty/LibertyParser.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 2ef3e2c54..c075ad076 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibertyReader.hh b/liberty/LibertyReader.hh index 648e8f1fa..ebf4503db 100644 --- a/liberty/LibertyReader.hh +++ b/liberty/LibertyReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 9cfca58b4..474cc7999 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibertyScanner.hh b/liberty/LibertyScanner.hh index c53d3f882..b32369cd8 100644 --- a/liberty/LibertyScanner.hh +++ b/liberty/LibertyScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 2622b1baf..19e087292 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/LinearModel.cc b/liberty/LinearModel.cc index 552533e67..85d158e78 100644 --- a/liberty/LinearModel.cc +++ b/liberty/LinearModel.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/Sequential.cc b/liberty/Sequential.cc index 782a22dce..5106f11cc 100644 --- a/liberty/Sequential.cc +++ b/liberty/Sequential.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index b7677e162..0d5257a88 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index ba5f086b6..5d77e931e 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/TimingModel.cc b/liberty/TimingModel.cc index fe9550050..3acc7b95b 100644 --- a/liberty/TimingModel.cc +++ b/liberty/TimingModel.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/TimingRole.cc b/liberty/TimingRole.cc index e772d1bcc..ace8d7dec 100644 --- a/liberty/TimingRole.cc +++ b/liberty/TimingRole.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/Units.cc b/liberty/Units.cc index 9cbb46319..98cad0aab 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/liberty/Wireload.cc b/liberty/Wireload.cc index e85ee6038..cc88891bf 100644 --- a/liberty/Wireload.cc +++ b/liberty/Wireload.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index a1fb2b501..50bc62b23 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index a6a0f65d2..de4b04648 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/HpinDrvrLoad.cc b/network/HpinDrvrLoad.cc index a6f1bb1f2..2c98c5fa0 100644 --- a/network/HpinDrvrLoad.cc +++ b/network/HpinDrvrLoad.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/Link.tcl b/network/Link.tcl index 4dd5bae49..721960870 100644 --- a/network/Link.tcl +++ b/network/Link.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/network/Network.cc b/network/Network.cc index 0e919d6b3..ccf1c158f 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/Network.i b/network/Network.i index 4334f6483..6495ec8b6 100644 --- a/network/Network.i +++ b/network/Network.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/Network.tcl b/network/Network.tcl index a8cd501c5..8e1c84407 100644 --- a/network/Network.tcl +++ b/network/Network.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/network/NetworkCmp.cc b/network/NetworkCmp.cc index dd82c3448..7dbc9511f 100644 --- a/network/NetworkCmp.cc +++ b/network/NetworkCmp.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/NetworkEdit.i b/network/NetworkEdit.i index 4436c3aeb..d52c2e1df 100644 --- a/network/NetworkEdit.i +++ b/network/NetworkEdit.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/NetworkEdit.tcl b/network/NetworkEdit.tcl index a13285fd6..d135db409 100644 --- a/network/NetworkEdit.tcl +++ b/network/NetworkEdit.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/network/ParseBus.cc b/network/ParseBus.cc index c3dae9e57..83f097b32 100644 --- a/network/ParseBus.cc +++ b/network/ParseBus.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/PortDirection.cc b/network/PortDirection.cc index 4c6946af3..5a6aaae5b 100644 --- a/network/PortDirection.cc +++ b/network/PortDirection.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index 0495f9438..5200367e6 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index 1d0231d92..a018782e1 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index f6a156ff0..db291c536 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index bf5ca5479..f033ae357 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh index 597512dc3..ef363cab2 100644 --- a/parasitics/ConcreteParasiticsPvt.hh +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/EstimateParasitics.cc b/parasitics/EstimateParasitics.cc index 415a7f1cb..32b8b9d3a 100644 --- a/parasitics/EstimateParasitics.cc +++ b/parasitics/EstimateParasitics.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/EstimateParasitics.hh b/parasitics/EstimateParasitics.hh index a6adf42ea..f41cd5fe2 100644 --- a/parasitics/EstimateParasitics.hh +++ b/parasitics/EstimateParasitics.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index d9a04ffed..8aaf5fc04 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/Parasitics.i b/parasitics/Parasitics.i index 548b50147..440945ecf 100644 --- a/parasitics/Parasitics.i +++ b/parasitics/Parasitics.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/Parasitics.tcl b/parasitics/Parasitics.tcl index 2b4ce36ee..e09f496be 100644 --- a/parasitics/Parasitics.tcl +++ b/parasitics/Parasitics.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/parasitics/ReduceParasitics.cc b/parasitics/ReduceParasitics.cc index f44b1a9fc..6dbab37d1 100644 --- a/parasitics/ReduceParasitics.cc +++ b/parasitics/ReduceParasitics.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/ReduceParasitics.hh b/parasitics/ReduceParasitics.hh index 283822f7d..2fd623628 100644 --- a/parasitics/ReduceParasitics.hh +++ b/parasitics/ReduceParasitics.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/ReportParasiticAnnotation.cc b/parasitics/ReportParasiticAnnotation.cc index dc327468a..a67269117 100644 --- a/parasitics/ReportParasiticAnnotation.cc +++ b/parasitics/ReportParasiticAnnotation.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/ReportParasiticAnnotation.hh b/parasitics/ReportParasiticAnnotation.hh index a3c7e6a87..6c9274d4c 100644 --- a/parasitics/ReportParasiticAnnotation.hh +++ b/parasitics/ReportParasiticAnnotation.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/SpefNamespace.cc b/parasitics/SpefNamespace.cc index e9c1ab7d5..2822875cf 100644 --- a/parasitics/SpefNamespace.cc +++ b/parasitics/SpefNamespace.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/SpefNamespace.hh b/parasitics/SpefNamespace.hh index 17b150463..042019f4a 100644 --- a/parasitics/SpefNamespace.hh +++ b/parasitics/SpefNamespace.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/SpefParse.yy b/parasitics/SpefParse.yy index 921db7032..42983f0b2 100755 --- a/parasitics/SpefParse.yy +++ b/parasitics/SpefParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 77dea5522..99c536333 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/SpefReader.hh b/parasitics/SpefReader.hh index a3cae7717..f5a9efcb5 100644 --- a/parasitics/SpefReader.hh +++ b/parasitics/SpefReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index 2ddb52ad9..cf58eb514 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/parasitics/SpefScanner.hh b/parasitics/SpefScanner.hh index eb59d16f3..0ae97b072 100644 --- a/parasitics/SpefScanner.hh +++ b/parasitics/SpefScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/Power.cc b/power/Power.cc index 38d1dea17..eb8d40ccf 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/Power.hh b/power/Power.hh index 1082af53b..49a6dc0ab 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/Power.i b/power/Power.i index 9ff3438ae..bc8186685 100644 --- a/power/Power.i +++ b/power/Power.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/Power.tcl b/power/Power.tcl index f56681d68..1c624e5be 100644 --- a/power/Power.tcl +++ b/power/Power.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/power/ReportPower.cc b/power/ReportPower.cc index 3cd77090b..3f0d0d76c 100644 --- a/power/ReportPower.cc +++ b/power/ReportPower.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/ReportPower.hh b/power/ReportPower.hh index c069a8b2e..966e8acbf 100644 --- a/power/ReportPower.hh +++ b/power/ReportPower.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/SaifParse.yy b/power/SaifParse.yy index 0cae9d637..be4f723fa 100644 --- a/power/SaifParse.yy +++ b/power/SaifParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/SaifReader.cc b/power/SaifReader.cc index bd62602aa..17345229c 100644 --- a/power/SaifReader.cc +++ b/power/SaifReader.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/SaifReader.hh b/power/SaifReader.hh index 7fa8b9884..e91979145 100644 --- a/power/SaifReader.hh +++ b/power/SaifReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/SaifReaderPvt.hh b/power/SaifReaderPvt.hh index cfbb7dd2d..4de66c5dd 100644 --- a/power/SaifReaderPvt.hh +++ b/power/SaifReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/SaifScanner.hh b/power/SaifScanner.hh index 2e7e6dcb8..0fa4a8055 100644 --- a/power/SaifScanner.hh +++ b/power/SaifScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/VcdParse.cc b/power/VcdParse.cc index 83e171cf2..1227e1b21 100644 --- a/power/VcdParse.cc +++ b/power/VcdParse.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/VcdParse.hh b/power/VcdParse.hh index 0fdb23014..6979b0ad7 100644 --- a/power/VcdParse.hh +++ b/power/VcdParse.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 5e624273a..7a1b312ac 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/power/VcdReader.hh b/power/VcdReader.hh index ca1d6d2e2..3a2e63f59 100644 --- a/power/VcdReader.hh +++ b/power/VcdReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/Clock.cc b/sdc/Clock.cc index 1b39e5855..b54f4302d 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/ClockGatingCheck.cc b/sdc/ClockGatingCheck.cc index 1e890baf2..ca706cfec 100644 --- a/sdc/ClockGatingCheck.cc +++ b/sdc/ClockGatingCheck.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/ClockGroups.cc b/sdc/ClockGroups.cc index 7a67fca7c..9729a15c4 100644 --- a/sdc/ClockGroups.cc +++ b/sdc/ClockGroups.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/ClockInsertion.cc b/sdc/ClockInsertion.cc index ee317e4b0..6a0b05451 100644 --- a/sdc/ClockInsertion.cc +++ b/sdc/ClockInsertion.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/ClockLatency.cc b/sdc/ClockLatency.cc index d9267663b..7912cd3f2 100644 --- a/sdc/ClockLatency.cc +++ b/sdc/ClockLatency.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/CycleAccting.cc b/sdc/CycleAccting.cc index 9df530e5b..3c674e051 100644 --- a/sdc/CycleAccting.cc +++ b/sdc/CycleAccting.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/DataCheck.cc b/sdc/DataCheck.cc index 23d3abd27..c3095188d 100644 --- a/sdc/DataCheck.cc +++ b/sdc/DataCheck.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/DeratingFactors.cc b/sdc/DeratingFactors.cc index 3da41c415..f187f20b3 100644 --- a/sdc/DeratingFactors.cc +++ b/sdc/DeratingFactors.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/DisabledPorts.cc b/sdc/DisabledPorts.cc index 0e736b9d8..599f970ba 100644 --- a/sdc/DisabledPorts.cc +++ b/sdc/DisabledPorts.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 8849d9c57..4ca26e4e6 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/InputDrive.cc b/sdc/InputDrive.cc index 2c647562a..26b06e57a 100644 --- a/sdc/InputDrive.cc +++ b/sdc/InputDrive.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/PinPair.cc b/sdc/PinPair.cc index e88801da3..05eac4d12 100644 --- a/sdc/PinPair.cc +++ b/sdc/PinPair.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/PortDelay.cc b/sdc/PortDelay.cc index 073c8e26d..db0c0263a 100644 --- a/sdc/PortDelay.cc +++ b/sdc/PortDelay.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/PortExtCap.cc b/sdc/PortExtCap.cc index 48287fec1..377c818f7 100644 --- a/sdc/PortExtCap.cc +++ b/sdc/PortExtCap.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 77a7b022b..6d035b051 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 2bca70b01..1347ac25f 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index 744657e2b..50f49e959 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/sdc/SdcCmdComment.cc b/sdc/SdcCmdComment.cc index 667cacd09..3033ad630 100644 --- a/sdc/SdcCmdComment.cc +++ b/sdc/SdcCmdComment.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/Variables.cc b/sdc/Variables.cc index c97bbba11..6aae98944 100644 --- a/sdc/Variables.cc +++ b/sdc/Variables.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index 9ca258264..cd58f136d 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/WriteSdc.hh b/sdc/WriteSdc.hh index 02abd6938..3ac822e16 100644 --- a/sdc/WriteSdc.hh +++ b/sdc/WriteSdc.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdc/WriteSdcPvt.hh b/sdc/WriteSdcPvt.hh index 7fd42340d..b4adae560 100644 --- a/sdc/WriteSdcPvt.hh +++ b/sdc/WriteSdcPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc index c4e875b18..0e516e0e3 100644 --- a/sdf/ReportAnnotation.cc +++ b/sdf/ReportAnnotation.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/ReportAnnotation.hh b/sdf/ReportAnnotation.hh index 1973fe742..cc810464b 100644 --- a/sdf/ReportAnnotation.hh +++ b/sdf/ReportAnnotation.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/Sdf.i b/sdf/Sdf.i index 395e0f7e0..66c479249 100644 --- a/sdf/Sdf.i +++ b/sdf/Sdf.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/Sdf.tcl b/sdf/Sdf.tcl index ab6572c21..df0a775c0 100644 --- a/sdf/Sdf.tcl +++ b/sdf/Sdf.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/sdf/SdfParse.yy b/sdf/SdfParse.yy index 8475dafe8..60e8b7ef5 100644 --- a/sdf/SdfParse.yy +++ b/sdf/SdfParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 59beb9f9d..93804d879 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/SdfReader.hh b/sdf/SdfReader.hh index fd17bb84e..f894ebd61 100644 --- a/sdf/SdfReader.hh +++ b/sdf/SdfReader.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/SdfReaderPvt.hh b/sdf/SdfReaderPvt.hh index d5a623ab6..3b140a4e7 100644 --- a/sdf/SdfReaderPvt.hh +++ b/sdf/SdfReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/SdfScanner.hh b/sdf/SdfScanner.hh index e66e8593b..14407236d 100644 --- a/sdf/SdfScanner.hh +++ b/sdf/SdfScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index f42d54a94..6cc3cd1d9 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/sdf/SdfWriter.hh b/sdf/SdfWriter.hh index 3c992366f..c14df631a 100644 --- a/sdf/SdfWriter.hh +++ b/sdf/SdfWriter.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Bdd.cc b/search/Bdd.cc index 89d776555..7d5bf55f6 100644 --- a/search/Bdd.cc +++ b/search/Bdd.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Bfs.cc b/search/Bfs.cc index 3ae832f55..9de9be6c6 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckCapacitances.cc b/search/CheckCapacitances.cc index c084ca8c3..41ccd9c21 100644 --- a/search/CheckCapacitances.cc +++ b/search/CheckCapacitances.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckCapacitances.hh b/search/CheckCapacitances.hh index 4d395cf53..bcd720516 100644 --- a/search/CheckCapacitances.hh +++ b/search/CheckCapacitances.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckFanouts.cc b/search/CheckFanouts.cc index 7b5a03d01..07e2f0af6 100644 --- a/search/CheckFanouts.cc +++ b/search/CheckFanouts.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckFanouts.hh b/search/CheckFanouts.hh index 0cd3d1c3b..1bcb647bf 100644 --- a/search/CheckFanouts.hh +++ b/search/CheckFanouts.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckMaxSkews.cc b/search/CheckMaxSkews.cc index 8e71b8a55..8682f6f15 100644 --- a/search/CheckMaxSkews.cc +++ b/search/CheckMaxSkews.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckMaxSkews.hh b/search/CheckMaxSkews.hh index 0c7a58829..ac1660cc0 100644 --- a/search/CheckMaxSkews.hh +++ b/search/CheckMaxSkews.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckMinPeriods.cc b/search/CheckMinPeriods.cc index 7008d4138..bb9b9282e 100644 --- a/search/CheckMinPeriods.cc +++ b/search/CheckMinPeriods.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckMinPeriods.hh b/search/CheckMinPeriods.hh index a8e0b918e..9a0caa13a 100644 --- a/search/CheckMinPeriods.hh +++ b/search/CheckMinPeriods.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index b13de4e35..0f7ff320e 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckMinPulseWidths.hh b/search/CheckMinPulseWidths.hh index 0acda2737..27e2aff31 100644 --- a/search/CheckMinPulseWidths.hh +++ b/search/CheckMinPulseWidths.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckSlews.cc b/search/CheckSlews.cc index faa3a77c1..760aeee5d 100644 --- a/search/CheckSlews.cc +++ b/search/CheckSlews.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckSlews.hh b/search/CheckSlews.hh index 60a167dba..6921c53de 100644 --- a/search/CheckSlews.hh +++ b/search/CheckSlews.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc index 965a8d150..de4ad8b42 100644 --- a/search/CheckTiming.cc +++ b/search/CheckTiming.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/CheckTiming.hh b/search/CheckTiming.hh index 75bd63acc..9cdd42277 100644 --- a/search/CheckTiming.hh +++ b/search/CheckTiming.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ClkDelays.hh b/search/ClkDelays.hh index 1b394914c..2b9d6a361 100644 --- a/search/ClkDelays.hh +++ b/search/ClkDelays.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ClkInfo.cc b/search/ClkInfo.cc index ac8e1bf99..767a44a17 100644 --- a/search/ClkInfo.cc +++ b/search/ClkInfo.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ClkInfo.hh b/search/ClkInfo.hh index 8b0432bfe..5efe7cabc 100644 --- a/search/ClkInfo.hh +++ b/search/ClkInfo.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc index 09d09bd56..0c1867041 100644 --- a/search/ClkLatency.cc +++ b/search/ClkLatency.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ClkLatency.hh b/search/ClkLatency.hh index c8fbd49b9..a07a4b763 100644 --- a/search/ClkLatency.hh +++ b/search/ClkLatency.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ClkNetwork.cc b/search/ClkNetwork.cc index a27ad6100..5f757b893 100644 --- a/search/ClkNetwork.cc +++ b/search/ClkNetwork.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 9deb4c911..ddbeb397d 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ClkSkew.hh b/search/ClkSkew.hh index cedefa8ac..349589e39 100644 --- a/search/ClkSkew.hh +++ b/search/ClkSkew.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Crpr.cc b/search/Crpr.cc index e782aef03..4d1bc3a0f 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Crpr.hh b/search/Crpr.hh index 379608c1e..5c7e6e4f1 100644 --- a/search/Crpr.hh +++ b/search/Crpr.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/FindRegister.cc b/search/FindRegister.cc index 1b2cd4b7a..8c0f3969b 100644 --- a/search/FindRegister.cc +++ b/search/FindRegister.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/FindRegister.hh b/search/FindRegister.hh index 96cd5062b..65ec3d618 100644 --- a/search/FindRegister.hh +++ b/search/FindRegister.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/GatedClk.cc b/search/GatedClk.cc index e7fa73fc1..fdba28de2 100644 --- a/search/GatedClk.cc +++ b/search/GatedClk.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/GatedClk.hh b/search/GatedClk.hh index 6cec6bc48..aeade0c5f 100644 --- a/search/GatedClk.hh +++ b/search/GatedClk.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Genclks.cc b/search/Genclks.cc index fde24a63c..ca81bb0a5 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Genclks.hh b/search/Genclks.hh index b3da5bbda..195e95385 100644 --- a/search/Genclks.hh +++ b/search/Genclks.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Latches.cc b/search/Latches.cc index 9dcd688aa..8389c9c13 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Latches.hh b/search/Latches.hh index 864904e5d..33980a201 100644 --- a/search/Latches.hh +++ b/search/Latches.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Levelize.cc b/search/Levelize.cc index a53f8f7e1..dff1f199f 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Levelize.hh b/search/Levelize.hh index aab99a00d..3a30d869b 100644 --- a/search/Levelize.hh +++ b/search/Levelize.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 1e3388d75..e1f9cba73 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/MakeTimingModel.hh b/search/MakeTimingModel.hh index bf929526b..c09634bb3 100644 --- a/search/MakeTimingModel.hh +++ b/search/MakeTimingModel.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/MakeTimingModelPvt.hh b/search/MakeTimingModelPvt.hh index 8184c46ef..aeca42671 100644 --- a/search/MakeTimingModelPvt.hh +++ b/search/MakeTimingModelPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Mode.cc b/search/Mode.cc index 1c44a73f3..6bced47a4 100644 --- a/search/Mode.cc +++ b/search/Mode.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Path.cc b/search/Path.cc index 5514d559d..8ec27d205 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 33b6e1cd5..6c6445921 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/PathEnum.cc b/search/PathEnum.cc index efe385ecc..ed57ed286 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/PathEnum.hh b/search/PathEnum.hh index 72b2ddb8d..8fbab624a 100644 --- a/search/PathEnum.hh +++ b/search/PathEnum.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/PathExpanded.cc b/search/PathExpanded.cc index 6f77a595c..1f9713a18 100644 --- a/search/PathExpanded.cc +++ b/search/PathExpanded.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 7a5ccd33f..47086f3b9 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Property.cc b/search/Property.cc index ba12610a8..3a2a973b5 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Property.i b/search/Property.i index 45f02d085..d6d2425b7 100644 --- a/search/Property.i +++ b/search/Property.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ReportPath.cc b/search/ReportPath.cc index bf6d83fe7..b7d3c9d27 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/ReportPath.hh b/search/ReportPath.hh index b934bc9ed..642d43149 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Scene.cc b/search/Scene.cc index 154eab343..0bd27aa3a 100644 --- a/search/Scene.cc +++ b/search/Scene.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Search.cc b/search/Search.cc index e4b154226..92fe7f7de 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Search.i b/search/Search.i index 410cfdc29..90ac5e471 100644 --- a/search/Search.i +++ b/search/Search.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Search.tcl b/search/Search.tcl index a9c1312d0..f7a548c59 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/search/SearchPred.cc b/search/SearchPred.cc index d9e829cd1..803bcf544 100644 --- a/search/SearchPred.cc +++ b/search/SearchPred.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Sim.cc b/search/Sim.cc index 19e47a2de..31e7ca8ea 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Sim.hh b/search/Sim.hh index 841170947..337f55466 100644 --- a/search/Sim.hh +++ b/search/Sim.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Sta.cc b/search/Sta.cc index b6e2a3b6d..d0d643eef 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/StaState.cc b/search/StaState.cc index fca517929..6ba49123c 100644 --- a/search/StaState.cc +++ b/search/StaState.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Tag.cc b/search/Tag.cc index ece8b14c7..a242be9c6 100644 --- a/search/Tag.cc +++ b/search/Tag.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/Tag.hh b/search/Tag.hh index 1b24c1900..b989c644e 100644 --- a/search/Tag.hh +++ b/search/Tag.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/TagGroup.cc b/search/TagGroup.cc index 93e0251e2..67d0d5eb6 100644 --- a/search/TagGroup.cc +++ b/search/TagGroup.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/TagGroup.hh b/search/TagGroup.hh index 2a02f2b97..972b3e73e 100644 --- a/search/TagGroup.hh +++ b/search/TagGroup.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/VertexVisitor.cc b/search/VertexVisitor.cc index 900c78237..fc04c8926 100644 --- a/search/VertexVisitor.cc +++ b/search/VertexVisitor.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/VisitPathEnds.cc b/search/VisitPathEnds.cc index 528590103..0c414c867 100644 --- a/search/VisitPathEnds.cc +++ b/search/VisitPathEnds.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index 9eae159bc..d2e6d5ce4 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/search/WorstSlack.hh b/search/WorstSlack.hh index b23b8a9e0..de3b03720 100644 --- a/search/WorstSlack.hh +++ b/search/WorstSlack.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index 6c7034bea..95fabc422 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/spice/WritePathSpice.hh b/spice/WritePathSpice.hh index c8823aeb6..027cc04dc 100644 --- a/spice/WritePathSpice.hh +++ b/spice/WritePathSpice.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 1e5cfb12f..3e646beac 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index b728d154a..ace2b78b0 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/spice/WriteSpice.i b/spice/WriteSpice.i index 30bf14e4f..a6d5dbec3 100644 --- a/spice/WriteSpice.i +++ b/spice/WriteSpice.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/spice/WriteSpice.tcl b/spice/WriteSpice.tcl index 7bf9674e5..7cbb9be22 100644 --- a/spice/WriteSpice.tcl +++ b/spice/WriteSpice.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/spice/Xyce.cc b/spice/Xyce.cc index 500a677ba..ff3e4b597 100644 --- a/spice/Xyce.cc +++ b/spice/Xyce.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/spice/Xyce.hh b/spice/Xyce.hh index 79db58219..84cf4f343 100644 --- a/spice/Xyce.hh +++ b/spice/Xyce.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/tcl/CmdArgs.tcl b/tcl/CmdArgs.tcl index 094fc6332..fb962df6b 100644 --- a/tcl/CmdArgs.tcl +++ b/tcl/CmdArgs.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/tcl/CmdUtil.tcl b/tcl/CmdUtil.tcl index 3eab60187..74fda26fd 100644 --- a/tcl/CmdUtil.tcl +++ b/tcl/CmdUtil.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/tcl/Exception.i b/tcl/Exception.i index 0c4d788e6..372d843e9 100644 --- a/tcl/Exception.i +++ b/tcl/Exception.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/tcl/Init.tcl b/tcl/Init.tcl index bb5c5fd7e..936321191 100644 --- a/tcl/Init.tcl +++ b/tcl/Init.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/tcl/Property.tcl b/tcl/Property.tcl index ed2910917..87e0d3c3b 100644 --- a/tcl/Property.tcl +++ b/tcl/Property.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/tcl/Splash.tcl b/tcl/Splash.tcl index 60b49931a..701fcf16d 100644 --- a/tcl/Splash.tcl +++ b/tcl/Splash.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 @@ -33,7 +33,7 @@ namespace eval sta { define_cmd_args show_splash {} proc show_splash {} { - report_line "OpenSTA [sta::version] [string range [sta::git_sha1] 0 9] Copyright (c) 2025, Parallax Software, Inc. + report_line "OpenSTA [sta::version] [string range [sta::git_sha1] 0 9] Copyright (c) 2026, Parallax Software, Inc. License GPLv3: GNU GPL version 3 This is free software, and you are free to change and redistribute it diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index 65d9d9508..c1ceaf147 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index f8de82fdc..e29d5a6bf 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index 481046908..8667048be 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/tcl/Util.tcl b/tcl/Util.tcl index dc038e443..15443529b 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/tcl/Variables.tcl b/tcl/Variables.tcl index 336e72865..e37f73969 100644 --- a/tcl/Variables.tcl +++ b/tcl/Variables.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/util/Debug.cc b/util/Debug.cc index ba46f8088..c2429db4d 100644 --- a/util/Debug.cc +++ b/util/Debug.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/Error.cc b/util/Error.cc index 4f0062fed..aa4b85e53 100644 --- a/util/Error.cc +++ b/util/Error.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/Fuzzy.cc b/util/Fuzzy.cc index bd055f1d6..0308bc9a9 100644 --- a/util/Fuzzy.cc +++ b/util/Fuzzy.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/Hash.cc b/util/Hash.cc index 2160ec1c2..4f51bd57d 100644 --- a/util/Hash.cc +++ b/util/Hash.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/Machine.cc b/util/Machine.cc index d387e9948..07e141829 100644 --- a/util/Machine.cc +++ b/util/Machine.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/MinMax.cc b/util/MinMax.cc index 864b09de9..d3720ac31 100644 --- a/util/MinMax.cc +++ b/util/MinMax.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/PatternMatch.cc b/util/PatternMatch.cc index 7e8142588..a8397e375 100644 --- a/util/PatternMatch.cc +++ b/util/PatternMatch.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/Report.cc b/util/Report.cc index 6772484ea..4a6e6bf89 100644 --- a/util/Report.cc +++ b/util/Report.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/ReportStd.cc b/util/ReportStd.cc index b94e97232..bcf73c2df 100644 --- a/util/ReportStd.cc +++ b/util/ReportStd.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/ReportTcl.cc b/util/ReportTcl.cc index a8b9c159f..d3e070481 100644 --- a/util/ReportTcl.cc +++ b/util/ReportTcl.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/RiseFallMinMax.cc b/util/RiseFallMinMax.cc index 4b21a4267..0284e8832 100644 --- a/util/RiseFallMinMax.cc +++ b/util/RiseFallMinMax.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/RiseFallMinMaxDelay.cc b/util/RiseFallMinMaxDelay.cc index c07469971..70cac6090 100644 --- a/util/RiseFallMinMaxDelay.cc +++ b/util/RiseFallMinMaxDelay.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/RiseFallValues.cc b/util/RiseFallValues.cc index fb52568fb..f04989fed 100644 --- a/util/RiseFallValues.cc +++ b/util/RiseFallValues.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/Stats.cc b/util/Stats.cc index ccf1675f8..ac6d98dd5 100644 --- a/util/Stats.cc +++ b/util/Stats.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/Transition.cc b/util/Transition.cc index 41b286438..9dd7fdc7a 100644 --- a/util/Transition.cc +++ b/util/Transition.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/util/Util.i b/util/Util.i index 5bc47a535..32d9e0c12 100644 --- a/util/Util.i +++ b/util/Util.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/verilog/Verilog.i b/verilog/Verilog.i index c1485fea1..3a60be304 100644 --- a/verilog/Verilog.i +++ b/verilog/Verilog.i @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/verilog/Verilog.tcl b/verilog/Verilog.tcl index 73e753dea..216d7c3e1 100644 --- a/verilog/Verilog.tcl +++ b/verilog/Verilog.tcl @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy index 8572f71da..71f056adc 100644 --- a/verilog/VerilogParse.yy +++ b/verilog/VerilogParse.yy @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 76f49e0f9..3d402e795 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index fd0f57ad1..7a445ba44 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/verilog/VerilogScanner.hh b/verilog/VerilogScanner.hh index f2dbec19b..1776062ca 100644 --- a/verilog/VerilogScanner.hh +++ b/verilog/VerilogScanner.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index 3d6e274c2..d8ee7c6f2 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 From 7668e43d2c5966e7beda1c50815aca19b2290f3f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 10 Mar 2026 13:33:06 -0700 Subject: [PATCH 085/181] rm deprecated functions Signed-off-by: James Cherry --- include/sta/Network.hh | 4 ---- network/Network.cc | 7 ------- network/Network.tcl | 16 +--------------- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/include/sta/Network.hh b/include/sta/Network.hh index f1c919acf..64480738d 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -520,10 +520,6 @@ public: virtual Pin *connect(Instance *inst, LibertyPort *port, Net *net) = 0; - // makePin/connectPin replaced by connect. - // deprecated 2018-09-28 - virtual void connectPin(Pin *pin, - Net *net) __attribute__ ((deprecated)); // Disconnect pin from net. virtual void disconnectPin(Pin *pin) = 0; virtual void deletePin(Pin *pin) = 0; diff --git a/network/Network.cc b/network/Network.cc index ccf1c158f..fe9a9c1d5 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -1694,13 +1694,6 @@ NetworkEdit::NetworkEdit() : { } -void -NetworkEdit::connectPin(Pin *pin, - Net *net) -{ - connect(instance(pin), port(pin), net); -} - //////////////////////////////////////////////////////////////// NetworkConstantPinIterator:: diff --git a/network/Network.tcl b/network/Network.tcl index 8e1c84407..376bd289e 100644 --- a/network/Network.tcl +++ b/network/Network.tcl @@ -147,23 +147,9 @@ define_cmd_args "report_net" {[-scene scene] [-digits digits]\ proc_redirect report_net { global sta_report_default_digits - parse_key_args "report_net" args keys {-corner -scene -digits} \ - flags {-connections -verbose -hier_pins} + parse_key_args "report_net" args keys {-corner -scene -digits} flags {} check_argc_eq1 "report_net" $args - if { [info exists flags(-connections)] } { - # deprecated 2024-01-17 - sta_warn 235 "report_net -connections is deprecated." - } - if { [info exists flags(-verbose)] } { - # deprecated 2024-01-17 - sta_warn 236 "report_net -verbose is deprecated." - } - if { [info exists flags(-hier_pins)] } { - # deprecated 2024-01-17 - sta_warn 237 "report_net -hier_pins is deprecated." - } - set scene [parse_scene_or_default keys] set digits $sta_report_default_digits if { [info exists keys(-digits)] } { From 0703d8ef407ff66e0fcf2d6f4460b05d25814d5a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 11 Mar 2026 08:22:26 -0700 Subject: [PATCH 086/181] README Signed-off-by: James Cherry --- .clang-format | 2 +- Dockerfile.centos7 | 2 +- README.md | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.clang-format b/.clang-format index 26ac15c1c..0d0670af5 100644 --- a/.clang-format +++ b/.clang-format @@ -22,7 +22,7 @@ BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom # fails if all initializers fit on one line BreakConstructorInitializers: AfterColon -ColumnLimit: 90 +ColumnLimit: 85 # fails ConstructorInitializerIndentWidth: 2 IncludeBlocks: Preserve diff --git a/Dockerfile.centos7 b/Dockerfile.centos7 index 8857a99ce..cdcd6037e 100644 --- a/Dockerfile.centos7 +++ b/Dockerfile.centos7 @@ -11,7 +11,7 @@ RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \ RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \ && sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo \ && sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo \ - && yum install -y devtoolset-11 wget cmake3 make eigen3-devel tcl swig3 flex zlib-devel valgrind \ + && yum install -y devtoolset-11 git wget cmake3 make eigen3-devel tcl swig3 flex zlib-devel valgrind \ && yum clean -y all RUN ln -sf /usr/bin/cmake3 /usr/bin/cmake diff --git a/README.md b/README.md index 6814119f9..8323cf74c 100644 --- a/README.md +++ b/README.md @@ -184,13 +184,23 @@ files in the build directory. ## Build with Docker -An alternative way to build and run OpenSTA is with +AN alternative way to build and run OpenSTA is with [Docker](https://www.docker.com). After installing Docker, the following command builds a Docker image. ``` cd OpenSTA docker build --file Dockerfile.ubuntu22.04 --tag opensta_ubuntu22.04 . +or +docker build --file Dockerfile.centos7 --tag opensta_centos7 . +``` + +The centos7 build on mac/OsX with ARM processorts requires the platform +to be specified. + +``` +docker build --file Dockerfile.centos7 --platform=linux/amd64 --tag opensta_centos7 . + ``` To run a docker container using the OpenSTA image, use the -v option From 00ee6a1956cd3d1c1654cff64a63dc044bce8f2a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 11 Mar 2026 11:58:58 -0700 Subject: [PATCH 087/181] liberty max_transition warning resolves #403 Signed-off-by: James Cherry --- liberty/LibertyReader.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index c075ad076..32e551b48 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -1520,8 +1520,11 @@ LibertyReader::readCapacitance(const LibertyPortSeq &ports, // min/max_transition attr_name = min_max->to_string() + "_transition"; port_group->findAttrFloat(attr_name, limit, exists); - if (exists) + if (exists) { + if (min_max == MinMax::max() && limit == 0.0) + libWarn(1241, port_group, "max_transition is 0.0."); port->setSlewLimit(limit * time_scale_, min_max); + } } // Default capacitance. From 818596f25aff382d8a085d4464b3b1b9d481e30b Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 12 Mar 2026 15:53:10 -0700 Subject: [PATCH 088/181] LumpedCapDelayCalc::makeResult resolves #403 Signed-off-by: James Cherry --- dcalc/LumpedCapDelayCalc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 03d238085..4c4a1251e 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -168,7 +168,7 @@ LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library, for (const auto [load_pin, load_idx] : load_pin_index_map) { ArcDelay wire_delay = 0.0; Slew load_slew = drvr_slew; - thresholdAdjust(load_pin, drvr_library, rf, wire_delay, drvr_slew); + thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); dcalc_result.setWireDelay(load_idx, wire_delay); dcalc_result.setLoadSlew(load_idx, load_slew); } From 0a5b95a52394300bf3399c1f5894037e4488b443 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 13 Mar 2026 10:19:07 -0700 Subject: [PATCH 089/181] CcsCeffDelayCalc::loadDelaySlew Signed-off-by: James Cherry --- dcalc/CcsCeffDelayCalc.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 07d8ff1b5..951561458 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -340,8 +340,8 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, ArcDelay &wire_delay, Slew &load_slew) { - ArcDelay wire_delay1 = 0.0; - Slew load_slew1 = drvr_slew; + wire_delay = 0.0; + load_slew = drvr_slew; bool elmore_exists = false; float elmore = 0.0; if (parasitic_ @@ -352,15 +352,13 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, (elmore == 0.0 // Elmore delay is small compared to driver slew. || elmore < delayAsFloat(drvr_slew) * 1e-3)) { - wire_delay1 = elmore; - load_slew1 = drvr_slew; + wire_delay = elmore; + load_slew = drvr_slew; } else - loadDelaySlew(load_pin, drvr_slew, elmore, wire_delay1, load_slew1); + loadDelaySlew(load_pin, drvr_slew, elmore, wire_delay, load_slew); thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); - wire_delay = wire_delay1; - load_slew = load_slew1; } void From d6e7b4256c202023e892feb38a06bf8cc554d69e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 13 Mar 2026 14:06:35 -0700 Subject: [PATCH 090/181] 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 | 5437 +++++++++++---------- 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 | 221 +- liberty/LibertyReaderPvt.hh | 34 +- liberty/LibertyWriter.cc | 5 +- liberty/LinearModel.cc | 26 +- liberty/TableModel.cc | 562 ++- liberty/TimingArc.cc | 5 +- power/Power.cc | 15 +- sdc/Variables.cc | 23 +- 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, 6761 insertions(+), 5840 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 99a94864f..055927fdd 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 4c0fe95b5..6c1e7560d 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 a17ab9a9a..54143b289 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 970c91f11..2b44188e1 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -237,7 +237,6 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc 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 951561458..31762fd07 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 83a729111..53a103bb8 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 000000000..7154e3d8e --- /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 967039aac..7d15b5a91 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 bc5b77e0e..25429d68f 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 6d641c7ee..faaa4358f 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 000000000..31f03a525 --- /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 000000000..69346355a --- /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 000000000..82bdfbe41 --- /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 4619bc979..48114895f 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 @@ class DmpAlg : public StaState 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 @@ class DmpCap : public DmpAlg 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 d1931517a..4d3b7fd57 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 e0a350d2c..aece357e3 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -59,8 +59,8 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc 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 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc 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 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc 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 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc 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 e33067d21..10c64e931 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 4c4a1251e..07a68e5b2 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 ac631f3ae..752edea99 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 512ea9f5b..38b71c329 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 b8b5c85c2..90e31ed31 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 23c9e11f1..0ac6caa82 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 8d695f63d..1f1871b98 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 fb9ea3b64..56bfdfc27 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. + Return rising paths through a list of instances, pins or nets. + - -fall_through through_list + -fall_through through_list - Return falling paths through a list of instances, pins or nets. + Return falling paths through a list of instances, pins or nets. - -to to_list + -to to_list - Return paths to a list of clocks, instances, ports or pins. + Return paths to a list of clocks, instances, ports or pins. - -rise_to to_list + -rise_to to_list - Return rising paths to a list of clocks, instances, ports or pins. + Return rising paths to a list of clocks, instances, ports or pins. - -fall_to to_list + -fall_to to_list - Return falling paths to a list of clocks, instances, ports or pins. + Return falling paths to a list of clocks, instances, ports or pins. - -unconstrained + -unconstrained - Report unconstrained paths also. + Report unconstrained paths also. - - -path_delay min + -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 transition design rule checks. + + + + + -max_skew - Report max skew design rule checks. + Report max skew design rule checks. - -min_pulse_width + -min_pulse_width - Report min pulse width design rule checks. + Report min pulse width design rule checks. - -min_period + -min_period - Report min period design rule checks. + Report min period design rule checks. - -min_slew + -min_slew - Report min slew design rule checks. + Report min slew design rule checks. - -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. - -no_split_lines + -no_split_lines - Do not split long lines into multiple 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. + Report delay calculations for timing arcs to instance output pin to_pin. + - scene + scene - Report paths for process scene. The -scene keyword is required if more than one process corner is defined. + Report paths for process scene. The -scene keyword is required if more than one process corner is defined. - - -min + -min - Report delay calculation for min delays. + Report delay calculation for min delays. - -max + -max - Report delay calculation for max delays. + Report delay calculation for max delays. - -digits digits + -digits digits - The number of digits after the decimal point to report. The default is sta_report_default_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_pin + + + Report edges/timing arcs from pin from_pin. + + + + + to_pin + + + Report edges/timing arcs to pin to_pin. - -from from_pin + -report_variation - Report edges/timing arcs from pin from_pin. + - -to to_pin + digits - Report edges/timing arcs to pin to_pin. + 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 + -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. + 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. - -highest_power_instances count + -highest_power_instances count - Report the power for the count highest power instances. + Report the power for the count highest power instances. - -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. - 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 + + + Report slews for process for scenes process corners. + + + + + -report_variation + + + Report SSTA distribution parameters. - scenes + -digits digits - Report slews for process for scenes process corners.. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. - pin + 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 + -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. @@ -10153,117 +10299,117 @@ - - set_assigned_check + 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|-hold|-recovery|-removal[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins][-clock rise|fall][-cond sdf_cond][-worst]margin - -setup + -setup - Annotate setup timing checks. + Annotate setup timing checks. - -hold + -hold - Annotate hold timing checks. + Annotate hold timing checks. - -recovery + -recovery - Annotate recovery timing checks. + Annotate recovery timing checks. - -removal + -removal - Annotate removal timing checks. + Annotate removal timing checks. - -rise + -rise - Annotate rising delays. + Annotate rising delays. - -fall + -fall - Annotate falling delays. + Annotate 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 value of the process corner. + Annotate the minimum value of the process corner. - -max + -max - Annotate the maximum value of the process corner. + Annotate the maximum value of the process corner. - from_pins + from_pins - A list of pins for the clock. + A list of pins for the clock. - to_pins + to_pins - A list of pins for the data. + A list of pins for the data. + - -clock rise|fall + -clock rise|fall - The timing check clock pin transition. + The timing check clock pin transition. - margin + 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 + set_assigned_transition - [-rise][-fall][-scene scene][-min][-max]slewpin_list + [-rise][-fall][-scene scene][-min][-max]slewpin_list - -rise + -rise - Annotate the rising transition. + Annotate the rising transition. - - -fall + -fall - Annotate the falling transition. + Annotate the falling transition. - scene + scene - Annotate delays for scene. + Annotate delays for scene. - -min + -min - Annotate the minimum transition time. + Annotate the minimum transition time. - -max + -max - Annotate the maximum transition time. + Annotate the maximum transition time. - slew + slew - The pin transition time. + The pin transition time. - pin_list + pin_list - A list of pins. + 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][-hold hold_time][-rise][-fall][-high][-low][objects] + - -setup setup_time + -setup setup_time - Clock enable setup margin. + Clock enable setup margin. - -hold hold_time + -hold hold_time - Clock enable hold margin. + Clock enable hold margin. - -rise + -rise - The setup/hold margin is for the rising edge of the clock enable. + The setup/hold margin is for the rising edge of the clock enable. - -fall + -fall - The setup/hold margin is for the falling edge of the clock enable. + The setup/hold margin is for the falling edge of the clock enable. - -high + -high - The gating clock is active high (pin and instance objects only). + The gating clock is active high (pin and instance objects only). - - -low + -low - The gating clock is active low (pin and instance objects only). + The gating clock is active low (pin and instance objects only). - objects + objects - A list of clocks, instances, pins or ports. + 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 + -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. + 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. - -asynchronous + -asynchronous - The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. + The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. - -allow_paths + -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. + Set the transition time for the rising edge of the clock. - - -fall + -fall - Set the transition time for the falling edge of the clock. + Set the transition time for the falling edge of the clock. - -min + -min - Set the min transition time. + Set the min transition time. - -max + -max - Set the min transition time. + Set the min transition time. - transition + transition - Clock transition time (slew). + Clock transition time (slew). - clocks + clocks - A list of 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 + -hold - uncertainty is for hold checks. + uncertainty is for hold checks. - uncertainty + uncertainty - Clock uncertainty. + Clock uncertainty. - objects + 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][-fall][-max][-min]resistanceports - - -rise + -rise - Set the drive rise resistance. + Set the drive rise resistance. - -fall + -fall - Set the drive fall resistance. + Set the drive fall resistance. - -max + -max - Set the maximum resistance. + Set the maximum resistance. - -min + -min - Set the minimum resistance. + Set the minimum resistance. - resistance + resistance - The external drive resistance. + The external drive resistance. - ports + 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. + Set the driving cell for a rising edge. + - -fall + -fall - Set the driving cell for a falling edge. + Set the driving cell for a falling edge. - -max + -max - Set the driving cell for max delays. + Set the driving cell for max delays. - -min + -min - Set the driving cell for min delays. + Set the driving cell for min delays. - -pin pin + -pin pin - The output port of the driving cell. + The output port of the driving cell. - -from_pin from_pin + -from_pin from_pin - Use timing arcs from from_pin to the output pin. + Use timing arcs from from_pin to the output pin. - - -input_transition_rise trans_rise + -input_transition_rise trans_rise - The transition time for a rising input at from_pin. + The transition time for a rising input at from_pin. - -input_transition_fall trans_fall + -input_transition_fall trans_fall - The transition time for a falling input at from_pin. + The transition time for a falling input at from_pin. - ports + 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 scalar. + + + + + + sta_pocv_quartile + + + quartile - Enable parametric on chip variation using statistical timing analysis. The default value is 0. + 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 z1|}U)p^6iG1Lv>^B6p4*CZMAZWrG#R8$s9#)Y=aP@G#dl>`+VjX%G5Ik9F+5=OF>QRufR#}>h)K|vY9)TSa$P4MW>Z_F_>S%XzfF4HfGIfThGw}Z2uN4}zb zcs9K|d3=jZ1sI`c_OyoROdFYE@=3+~#pW9z$a}kWbTM1UZGXLcY9quDB;n6{ln@(< zAy#4cqI!L1piMg7^~K|vtTE149HKu5?cw8X=B3diEx3hw_Z&zB@&f{)3>h}QbS80CYY9oHp?uT zBBPmjlD?@Z(SsX@!#q~z5mKi0Dv(t?=18edE>w0>^Sq|kwjGFD!RGz4&=OgCFYO(` zye(zz1cp*ylKojlzZVO)=8gf1y&fHQ&*f3gM1p>4a@@?#qv0m&t_Evy%Hh%gFYUma z@Ka*GEPf6jU27}CoO8Q)*IB)xb^Q+F83v_0QdMe3DdHR{#wO~AU0n{vn74#KbWcAygu3_vY|@V!?@9;`SW>`^5^GGq+RJQjE)f2Gl(j{#w*65Dn`*`Uhe( z=^k_)gyMsdX^dxKTt z%1xC>OjC527H}sverq^M^51Y+=$B*(qD|xg+dWd>{@MoWDn~h9L@p2h8I-SE z7VgujZGNR_($KrnF~psF^qMJN(RRD7@(o-jSy7+&!=6||4BRj{xsWw*j|9Ogn zmHq$H#TV73Z2tzB|8tAtq?#WUej5xHxWNcRN8kTyi621(LG=EFF5zNns^GHcT0W^P zRIDlCMG;I@h#% z_YM}IbwG?-aH>A?2=sOPim$Hqb0va+$m8Md?Tyntp6TK3*ZThDf@3Khr8TY--5>E> zi^nnOLl~3vWR^s!-*$?T4NA1_i~W9BM>o0+9_8?i=WNy*HRzUQw%736_&Ev#a-sXX zUv4jNKVn?+E7`L!4uKGb&-_=ozyjU>ThjkAzu`InG`@nw(qW>`=T#Kz`4@w@RC~D-XDE9 zhw)K8ofCxbQSg(n>;!ou$@83Qn-4_qZ77_;br}d?VcJDEFae9w{=z}_gJ}Z$=%RY1 zzve5WciE&1EAXhDck~0tflMR&OiZkmRgYp+SbH&dau(1XT zUj1}atAt%&JgX7y^>4BKBrO8nrxnKyO)g@BMoI&e9+(j~SNmFyf0|=940=W@+(PcV zNvsgUOU-_+D@E!hvS&NoDmAhG9Mg>g};z-Mi zeY$D>1;p%2%{F(J^7jFiPs0-V;T5h4#5B0j3hUfQW&vzdS?G2+A%Lk09%IC`{aQRF zv^nW*U6(J(izNOuc)&i7?>QvkhMS(g~<&DmBx04%{_2#`lqza6I{*V#TB{rc%Axj1PlBd2*qw$Xw_YlNlf)3oYT9Vk~fbUuI;>rCEKo3lamGwL>HKN zGLqn1T@l2+{ACe9f$9iHgw3Bn@#=DfocUz3DK!w{8>dW72NO)2GorUfix!T~d4vvu z*99)-@kZXzTuV$zFc#uD->PCj1DBVwxnM(c0vwuV= z7BQlwzZrq3%7}O@`|;u1Ec`O>PEx8zQapS}0-%Yz-tP*bb;)h#WUrFR#HRFkq&gLz z9YpnB#igKgZYKu-QmIe@i;;r9S-!ybC6Z$7>LS0C ztB^?k;nuAlkPrd(Qj1WkdtWbW9G&&O5^g_5CW1i^?{v0KK3~s)a~yV#xjWZqEfLbm z*iQ-$4RMmc6#xUCW+VoTFGI1#8Gdc)Ar5b~BGkMvD}+2z9VTVJ(vm^Y@C`(jgsl6Y ziuAuK@4t#P6EoXCKltYj&Bnp-cdYv7`R`GSjpHAUFXUeZ}x}qx%~7bkquNilFlRa(|&Cv?Dh+ zV{_v3Y&nF*YO2)+wYU8`@6_Y@)2-)i_xxz#EKE*^;Jfqv<#6D0YU**~vn4ddnzbX4 zAj&t^R;^=QWyJm~=5gvW^T@*tM}O*a0M0U9KjU=j^8FwG)A*EYt8eT3=fHwmmbdG( z+w;@y)dc;^_s#2q5+ZOCB)kQ!CThWQe>jrtWGq|V`VG#~{tt%PDBBva@h!ja|Btb6 z3KA^JvQ68zZQHi3O53(=+h(P0+gWMbW~E+s_r%OZOh>%_yRqZ`uXXlbSltKT#$!`| zRWZX$Ifhg)^^bJx=iA({dZ1_Mib2=OB7C1l{hF929{92%Z*UqwC~gCkto+7hXz_RV(@H;?+L99IfwUT8`bg6P~rnyrydo_-{dFmj}2!l1<8AWk!?G0^cKcn#zg8_le*yy{1ZlapFW^2=BY|5GLRnmRH8O}-xed* z3c);`XXDkOXCMT_T2rfrJw_ij+wLDUhlbYkXa>fFlCdVLN3G)lq5D*? zk_c``!|4&^Dh*Wo1-yD%#5E{IUKH{b&>GY%%V`qp8h=3D1wj7!z+5Dvy8Y;29dp2V z(#0%-KY@_k>fG{dfMbmW&g+@)V_X@c&Q^DKBfyxRZvMEo(@M1fW^kXE$ z@8ciy=)mdiUgm-r{CUTSi5=TubFBKgX7G}_gohf-{EcCz^^IZE3xg;nCu_oq>l=f| zmjRO%QN;I}e$`=nnQz zhH6=1q29pQ9gPnG4i?v})gs_>xSOzMaf_V#Fw!AXnEgqZ{k090vC^*6NKK>OBG=dL zoT@_!jpHKmNv2KojWFloH<#fo8rv)IkGQBL$*>>i&{RGY4VlVbK#cq_mQ+6k8<*dL zW~upGt$Ek*ov+-!2x!V*i-r`$%-E7%WdEGl15{g$^RsG#(%ttcy(@QDc=Xmy&J+(# z5Pf9k)i*rTPD^XoQ(RFuCUiB?V*W<)mDAcJx1H5ajKs*l>ngLIy^wuusT)169G-o^ zaw%}J=F(Ueo|WSdeH}tDyRLa=NXtskj&fl)nrh!_PnD@Ac+?nRb}WncQrzvG%zkt})fhdX zh?@mrAF_$I=R)F?0nV9I|fz1^P(g^g9&@Q^#QHv*IW+Su1@e6Hh+aJ-9-DBWC$ zxW;I4nwYlQFbR=F^^EbC&ay!HRxehb3~LF+cwonyISZn0C$v9g(<#aP04*I_+XwTM16Sd{U=MUoywM|K^LN)t;0Sq;e^rf zO1>MHd32fd#<>Zr)%7d~&hKpdi~#QjbQ=rkmHmG; zi*T^~-*7vQe+J?H|FCshOVf&k9pOi7z!=}!7p=>(xiSs~}{lpKS@29u;`T6HVLVq$Z2`@Gz~gq7WJyr1pe?R_2$rtRFlA3co^AM+2^h(*MWVdZ+kSNCDS0pTvpydD?x*pLDra zJf4cU+~VD*7fjGd!yfw#ktTX&1A(n0ZdZ)(p0Px`tp`8orVk(|0J2O28Wq%Ybd)hy zbR_l|niwa&?c~mYE_R+hEUq*fe)z>Z(HDRis=DNnIV_ni%h{b7@53a2K{dZnzjj$* z_^X8wUeASdM4UX$$aOQA3b+(BBe<_gdOkUD9N?7+gyD=BF;j`*4AmYdIE%$eBDSvy z+8m1Qcy1|eerUL-Dsc@blVCClP0rvkEdbdVa&aFrrhkRJwl=c_0v1zWH>(7xC2J7X zxETac`U%P?5va;coLAm;$at910y20IvEN<`CYUAwi=bTZdV>hz-fCLpKt{ZVz5S*7 zfmN4oRE|l`Y}$nf%^*4hqYRJlCk^p#ZxQ=DTT_c2&vrdR=5)SD%g<3qhvGQhpE-#F zZGJL}SI5BP56gu!5i+04MhWWaJje3iDOy57;B??k?zl}_xgdzDV?)C|wdv|#lOR>M zh-E<4`4tT`IF z27{4TRp_UK7w>oW?M*$FLzAAU|ID-^$PFqO$G%eTH>!AMoYWiqSpkQNEY>QJk{Xzp zWNo|Dr_zZuKs{YAF>w^NG(V^+4uvI~_pg>v#g{CMHCE^V7RChG+kYIL9HLE0M{=94 zzgWbtjc*&0Dwf&b@sBzPNuY9WbkxB_tVP=C=6DU%PySS^No){{*P(?unphg8wiI-H zqiMcX6+UD7rG;%AIiq+CTn6&irqRh(YT`rm*R-BbGYydXTqM8CFP1C92+I%w8yYmB(aC+UZG%sH%l`qF+43L+`L2Pq?C2qaM|e88^I-^IEwT`wYJBj zy$M~av>WGfPO&wi9-vH&sZ(1oFp7p;TZQFHou@4}_*&k;JgaXWHRxGM?1w9}E)a4cVT&OBL41tjKlU*3Z$W*1C-1VQp;ON=T zXzwZ`BIzxgH2i3NS45oeR=~h?v&smC5xKTMD~9z(*BnCIIU6mfr2p)BLug8q2IhQ9K>p z2r%umP5z{<3ClF}AWPPSL)Yy@r*aiI!RagaaAVQzGk%{jL8oO8pwAOd{aX_6Rl+1V zM^R%~uG1t$&aAu2DABB!4rt+fktu+s87@%ia^WPPwbm<^UVU-e!yo|*tF+Ha(wP*S z*2UyQd1;Eww+!O-+pw2pRh0$&mbERc*7t+;RaH|gUa259J=6@Yr!w0}@Q8{@x9!45 z29BJg=<&&3??XuTD2K+zQwgbAPchVHi@FkrDtT8yRJmy!lMM|nV6#$Sea)dPgJr?i zU{zvr+U12P85|XAWgJh{cLT~@`@%KsdP>GrY0Vl=Dc0syEV|C_zWzvir*gsaPpxis zW&HWZauLTgtH{T$g|Nuzcz?2RO`p^|lf}DBaQM7-AcLo@JXr&ekozz`cwYX1^+zW^ zCP3JW^M|WC1^u8E2=#q+CCXqrJ$ZpEs@M|go@DnZfrLDY{a>*D0pc?0v!`c%Z<@v7 ze*^3P!s>s*Iwu3?e?OTy|J#!LKTqbLOxyq9$$X_{6NSx=@OAx@Wp5YqaHiX-597PB zAdqhX|F_^V$l`h-SSL}BzUlJ?SF$SYPi)ggrvel$_;U^ItORpWh8bQ&wZ+5ex#!F6 z>+|);q*0@vhsNh}d}uYK6}ISE$70ibj+bAX?oV;@gMmw+;>wSj^80UOLT4*{FTX9@ z3O)P|9(KdlbzBh0tCxG@`c5?qFe#P@<=SSK?QgGbg@w({&!z(0t5#=W*JDvTzaEcz zuB~dllp|#s-?Ygzxv$V`NqM+K2~30HvI!nVSb5yN+Xy0OuLoN(;z5=Lx#U!kNqy{f zB-7NPR`osAbogQqMcZa|YhgHTTQxt>THcS?>?w6{?roS7V+`^E`EsTMuA<;RyQwtW zaK*9p8oyGtl+PU8BbXc*)}5$jKJ16VWH0iiSOXi3GqST_B#h%yGc$r9_xH}bn={e!xg6~M4`LH8`k+?%wNh z)tx+{+1T=Q`q7Lzc6}rLE6zQg;Cb=$!^8m&k-iYGKDJk0C7H~V1`_-+=LumMLz zur7L(n*yBEJYIhvyuZ&rYuKZyu*58SOlRoUwL8ekS7)*z+}X9c8b9_9K0n_Et?0Fl z8ZcF@=#7x;;`5(q`OCNhtYF0qlf z1Y!o|Z!D-mP%d@gLQ|XF)+ZiZZM{9bi>H76IJ+OR!XCziRM6!S* zoX>{>qj*N21Fga^ECa#BosQVwMZ!%WA6^_YHjIMc9QM!;l1Xkf3wZk}%9@y`0w;5* z)issUK~!2EM`0N;8z?j}Yr<3l?!tbpnvmTT5obfo&v?zhaq8prJVMNj8tk{QIEJ2@ z^w{(lu8g+2j3Cq<_%q$fZi;N$35$eiiV2(*Qd|}Q1d}dvscgom0nQ776Ega(Fz*7{ z6_p4IUEyC?b@S;>2X}31N5vj`?E!HN&rbmSVN1~&ReBiNb=R~npwE}P=Xre5UR&2d z!1kx18gZ4$CaU?-3?=YehlwHhHjsxz%&%7Y|2JaQerKtU(Q zWh^Gd;$nz~Jj-2vCyzCnO5H1ryCv5jHK!8%INJ|dA}CX!p>Hj_8Z(=}ONy=&V9!k( z)_7qerkhg}E?P_>!<;SSzZ){y*GQRYmOz<=2&5hw?_vvhNsf$bGU>u}IGR9(=rl>f zUG=gIEQjE+-dja=b41mLV0nP-UkT?;MU;ZR3^Wyj)T78UNtKf+P$AQR6lOR|Byai? z7@@wHF_(mrE3gma!x5Y{0F{^+@4kJ^9YhaTO2F~#poPo>6{uthRIY+jed*5yu@h#v z-{>pA2KiZOc<7?&)QUU3RG0yBOJH(n3CTz{*L7XVd$sIwh2a2>&fOwL1_3~&d%QY! zYAEtN@9hcICVx26)u#8rh=nUCBhR@Aqfdq_eK~Hpj**mEpve{cPJu?A@Q}ra8PXhc zoaM|*!{Q7fss|sy#5vIk8NhPvMh6$5WtM0jg~IysjFma!u7uDj6!)!sz4KHm8Jo+s z{*qt5XZ;(yGbTrfq;1=s0(_1>>QsT61g~2IfN@9xzivpUzfT*2s*f*R{&Y`s1BrG; zsIs0ZZ_Y_3S2T&H6<(*FG6CtVOZtS?>aup8wLtE94N{Ee!^i%<&w|9}7J*|sin1^c z=$xDBiZY3Z^&Ch@jEfW%eWORSTB2!&I|!I3uK|6wkb_h!y8^+;MH*uU6z_L}QAEoE zr(H!i{iE6VUKz|~5YEZH!bi}hZaDsOQ3D;9M9lEWsPuxvV}QA&u+xv}-1ywuvN8-- zD;o4hKZ?i!S+Romyr<1C**pQU2Cpj0gTLOyl_PrfITADzoL?FT29=4=Lr9yrsyQlw zvI>p;4gCpcd!@p*X3;{1$Bp4e{#K_TYthKX0UnP0UZvla1scoX_Iv_mdqOZzgruWs zFs*P8b-REu(_*H>1TAw+ik~qb`DMK6FnY`-Sh_|iLW!A7rX*+P9dawZ64CPbkZrN= z;$m}^B-h~vnze1aTYruA3qu`26+7t-qr$)9}q!Oi3!*Ed_Wq}gyzx>!!=@`trqTVc{uy~jPI(q^{d+HpROJ(y3T zku%WPa5{_?>+ykFF_|j2jb;uO4Q7tn+t3}o^*<91ET~WK@B=GA{u`q7FZS~fqQu1X zzitbR4F7#|_#dY9GyG3tO0Isc3tM6cKk@!TKeaz8-O_+{3r$-yl^m0m9I`o@m0XYJ-!7nhIUZ;2iJ*=vJW5BE-Py&EjB zd+Y_(6}w9GplXVW-#5=93&fZb(ij#Lq=mh{y`4HYdPR#a~K&B_u?76IS=+WE8Hjv6^jg+D~9-7|rc6Iv>oR)}Y?5|{c;;0qZ;{Vm){g%*+2x#rStknf$R`6-{MI~xkILUyMpQrU2m`a~LqHX7;uOyEGP(2EAM3Hs z)pqh3KQ-Ue9R`a`I(7^+;0s5r0f{N(ePZQ(f;k3Y9HL->>>izxY{-5EKC zT%X0i$h9kEnmwO*%+x-NM*p!h4UDj zsuP=b2m3>A(}EOEW1ISe!KfZ3MoC)_P`UHA1lI^~WR127ra|6D2s)}c0;X56LyP7w z{$@$Y4&`&9En|fg-A#&ew?=^$B~NG(&d&jX1vDrcw&Pv4 zG)joSHneq3xp-E)_bT&M6j>WVJ7$IbQx>Dz0&bKu4P0-iS85m;;_b{Xl9X4M6%nvs zNr?$pf4H8>Sk?ZK4OpQHCCK{gSUecJbHtD&kY z@x&N0e*;OQ*RR=RaO{7@@z%>@YY(dJ22u0I^eME08`8?Af=+aHs9cjk(WB^KWISuF-gebL7$yU#j(jVLc;KTG7{6++X52B|T>3pUn6JC5Hkul6M_&_^&7uPjb zKsP%vlWa5eRT>N_A1)RX`kOipKv9p5-et=K5KcB#bC)yA?5?z;q0q)-Ji>7&Fa?AC zA>-6ko>30QK{70*GCOsa2Yg1NZ5XUGR+!GCf1bM4OV7!QScq?4X=B zDD}!M1-*C_yl{NN#NkAuf0tsX1ux#5Uz2+5jU)$j5|^XNeU_Lg*WrChbLV7M4vajm zy?$ykIXgPN&k)u6O>h+!eO36q+e zHd2G;OacblQ%iS*UTPDq7wMu*9fl9;S|B3=Dxp%F8%T360*O`>>zmtO47nHlu`wMX zuLV#9!d9pW{>^jOl(mM6L{F|%_vqf<^1%($-S4<=c)a*HXKw*TH?>U&%FWa|x(&c;2*&zAW_hna2ra!DyoY7cklaIE=+c6F4jG&+!Y)`6eV zCAX;Gp|LDR@;yU_wumX;Ww|?;&E))%8f^=U7n!eZj1Trc=H>OMZ^&qTG<`&gm9Wc5 zrBY3m-?!|ljDI7To1R*$8~jEPx8IVo>ODuLGWF|T_(}N;Ky#YCGP!ZQuhAVKc0zou zck-|-YasU{FCx9{;zCVMn=y6d-M4j{%6}E>8MRH zgr3=2gr7S3wcoyyfr1S1ltcRAIUzVgaKc>|tL`>t4P@iJ@-D4Q@D)p$YccQv`nR#g zxs}(IwdYk-up0t*C+DVazTR$rf1m1CdwHsT={5S9iZ`AmGw7qqj z+s;crFCK5-2De-A+D`JaD{_?@&?eO^@_QuG@CDODT3%GxibhSndx`b>4H zHUTFSV9;k(ww{IiQCFyXb$W_Mf=ls$cOz0M=7WJkJp)*SXO36v|0s^UX*A#ia$-UCR8@*8IG15k>%`=d78I@Qz3XZ7F1B64~Yj5#s$`v_T zwH7U3Db^b+@_+0bruV}%5V{Lacz9t4g6sYV-axAFlmAuS=aZKssJ#Dh142P#M#>8INsPms@r#qLWueLoq!vQpPyzB(=u6y_C`8<0)>ympzg-jL5JZgXj3L zQ=ob9os%*;dVgDS+B#DaA@SS#UuZ@ZKSpU|5eOaJ)v!@LXzFnn;ir}{I z6Ebm7%c)xQMi%vVPSyJ`)9So7S&!?s&0wXznUkZll;?nqcLL3xkj6MH0^udb`*}Q3 zyCH)?@kCZUKJ3H=qsqwk#q9CZ90}2R26F#i1^GTp%xQh8KP0p&RNQglvws^AS70^< zr(*tV3E9Sm`{}@oE1bXp4fX<*id=>64e?|(%H~%;cf}(}nbo$m&PatKQp0F;s|`l8 z;PO-bq$Hb~#9$`Kwi-lYb{9`#ObUqbAjyD)cg%%TqEw2SlhP=PX8@t2YSREf!h_@# z*OqX2AOpTYQ~a(&y*4=(Vo_+{5f;xtuD>+C26HFGJGy1IyJ%ubuhjmLal{@H{d3U~ z$(_>Y)E|VgdyGmGBM1qrfT=a%X2!a#Szw2h1O0h3Vy7}kIj6aP>d}YF2%E^(PJr-Y z=ZMNI&QB+XoI{z&%m}U_G9vju>h7Moh9;!ZN29qjP04R0i3b`|Y5Q_iNg9l4t4vF; zFa9o#HkE%v1+xxA@`81VBep8M2S_m2a(>BXr4^5syo2yN5MW{m#ESe{+@k|e7=yJi zY%WN&0Jn9ssG2pHj3}YUSSF%^57hy+0dNwq2fCI_*tJf1%csx_BLd=McWwOoXv zf<2>Sh;yVgN)vMxA>mbS<4z3+Gc|I$yL^#KECXgN)7p|gM#7AXzXVWrWLEsforOCuP*fLDcl-YFRBKV9L)-Bc$%V6q z@8tGR2$`^0$xtzpeJV8U&^+DsLJ3^J03GZT)Nfoh(i}uZr9{}3Vp1ql5thmM?Tc=9 z4-V_nQk>ZlNiN5pVZCGu5{n(O#=ffVENsP08yTp{?{2RQw-fPdAZ@uq_u_6V@=kRx z))hvmaT)L&PtVi9BpsYLIg%jWgSdW|9v`RjbU5tns{IV}pP2P-AMG6mnXR7V z`J!EHVEnCHcOJ%Qov_jpJ54e^>1}E)Wg84+R`3#P-L9`0jJd9WA#!>sitVEBNF+<; z9UlF-xF!Z2UkYEG;Oid(j{WKIef=SBKjytifnohRge@tbZ2J=qj0X55(SvWDTxvF^ zcA9voFLD|!0_PQc9&nY9Cr$PueNS7?8N?CRAyniT+iV1ZXbL+szit*@Vo8SEHrx1Q z+vU1IXSMSTHyBZL!?zV)4TXLF($iB(lxU1eki4o=-~QVpO~^wuL4_W1?@yO{>6RzT z{$Ld1_cgJ{io+f^BKm=kDbj>$rlQbFP+01L$g~q(?V735VF;rT%TV};OsYYt$^C;~ z;?y)h*>Q)tT%(9eWHmsxB+H8l#NFi2wk}0Nb(O*3WMybOMKC~ByW)-Q|=5PxfwNGdmL)=KrH zjB`-1{GCPvh;#R_J#xt3T788}Qy6X#^9us2Q+R1t1(ITjN@Wwdrq;6GIz^eBkfHl{ zRmd=+yKF%gYO#Jyf|Z2G#mTCbrtZ+7XZGK!ckpp?Ypwqd-2a8w|2q6I{*R!Yk?EhS zi+{}j*FJ-Z<3Fi?+4@m*9RBPxexlu9MAK?6{?Q#(|CJKC)b#g#-|_wR@$32VUAft-v-h=;2(<#zib~XC z%TnuX`O~+n{q<<$d~Y8@UiX@RYbOuycdx$6y~pY~o4cPJ!Gs(RFR5mdP4h16>a zU?wg{3cOGwX(v9P^n1s`d~8r`F_$6;&={KKP!SV})XDI54u22GHD#!gYXSBqcaRXv zU>W>^YDC=y31Nj8|0^y-Z;?exddNxk)BDWBNpTYvlw`)Dz?NOR)7(j_d~*#jT#Aqj z|A8~sd4$BP&A%n#D!UlvD_4YA*btua%&mNQe!f1j;yh+epwyNFWNP9(S%vc$AocZD z*sWlMyEOT~w`9tdSvY99Ce;3{Vd_O{qQELs4Jm9Qjd5*m=Jt4d0}=A);>o+kmCgF~ z?QE5aU|==rIW(lW927f{U#+mN*%&v1<^PIc_097i}9P(Sd${s_`pqWa`uP>-1EvLx#efk*f@t-IO!iCl_T5 zN|fnSdeza##gXrF@r>wpDOXf6d$eHf+Nu7?Q;VRkT5#W~+}xJ!LAo@LIe(;2c*@3X z-7bBT+Acg^e~0+6xXPrf-|0(h-4^#G+~k|5N6ZH0Q|bNfgss5`m7zP@STu5r7jK*c^mIW|bp6g9#E+FbCsd z(&n0xM>8iKVHS`L^aTv6z<3K>cZ*=d#sexFxV;^~bF|xM8i5e2u`F;m#M}2*^zAJ- zPMP1&kRs2q>Sv|(<~dxS6(u7DkF$$NOEC%{Eg*wG98_3S8@-ojyofRSJxK9||2Nyqs1ASZCDE883S`JG zYbCPw2Lr%F+Bul07-S1YMX5vOE?zd#TEqvbEalq51RB*r^NNR&N&m4l0XbIgXjSZp z1cdTQ;3@{Af%6bxyU`Ix>O)O|t5*_vN%a|HHDfh&Nt_l-yfXqT8?@?iU$?Jscv(t5@1=cNz-+dB>0Lm`hl{`s!6Kiw| zMAs2br$Bu*-90+PxH>$Z0i(2gGzpvBrX0L%O&-ol4rmvlrbeTukjqCxQoBfJ^p z&D-_hnRNG&8O4NiBy{EFn5M`beu^4}7TOZ5C}_JpW@qi}vgoK?HY1;5r(w@ox1e%`+( zDwSfYKMezH2HT$fe%mTde*wTMSZrXmJHXeCGYh}&jh-40RNoE={@AbIw)Hy4ZSY?5 z>3&1QIlO<})qcf{KYB3^j&F2vm0mfjQO{@3A6vy~)F?AYrOwuY@PcJBmAi00;y_ni zrYo$fc;SHgUP4AFs@wl|>T2@hxCVp6Qp6Mb+jHa$$6duqahK*#%g9@zQqQ4B!+?{k z&M^rZy&j+vz>JjwbIL8vOj9_YLcbu?$;YBJs$wvVtYXH5%Cv;%SuGSuYy^k>YPK5E zxecc~e?@;cK{u&#U^wzFTMpD^>1@n!o$Q@%ir~t5={p|HPl_sWTYE&xaO~_pqlVQc zR@hx)N2u_gFNt72q96i?zDFx0GqV~6PPtMo?XEk3{SKg0Z~n)uDQmJW%G7|6CBEs< znz4#%n3zNmYl3KMKJ8XZv+iVoSXS~ zfs|~o%EZOARrx}n7}Y%{Fm@$NeE^=BY4^eym@r>iuR$%)%U)XEUf)TAwQ|&+ZQsnr%GqGLkSC2owf3Pi%%Z{ z;+GV3xTkZA!yHEoIFBL7xE&1h2~zIe32?fVm6QTOSQ%H}tc(4L)5e!x<1pe{{$jaT zUJ?mzu9OLRR7MM>kB-k<$@#dI_X(59Qav7S7WTzyvUIj809f2U(-ARX(}*}I1>o3T zUqfO+$ZR7_kj%)~vuCvRVp&VbnWA5g4Xq$904nf%7H)ssQn!Dcde;xJ2mg-Q{EMvo zYi+^C`j0>U@eReu%=v$N?S6*;3A4G!6l!9*ME(fK*moC)$L z`)Xu%vJj>fZ$R68zsHhHrXJP1b$j{5hX!?OYv#Uum=dhe z(D_blj5vN zcdu^JLB&2otC_aeHe_j;^fhd}qbk=KurfzC0{M@hKVKR8b-D{KfJV~UqaiN`)Bys+ z%U*A#B^l;8Q(i5d!Xd*{VXCc~NE3Wn5AnOv)`&E7cy|XYay(su#AMK7g8L)E zI;y@j{;%(EEYArgu*5lmzr21fa^fq~E=qz8sXogmnzt;>!c5epHRP5-gr|lWnjP@> zOr+A0wE!uT;da;&q*Se@CBSfeI~f84$#aFHHMOMYt*w$(l_eT)DzQqnzlGMV-Z#7Z zugEmwO6_+onW^hyS;eVF zFq)xT8tk~x-M-m)hrUnS?)=`k{Mh@%8V$W16r=c>Z28;yCnv4r3XY3VpjqXdEgCx{ z$ClS4ZEIU>o6Qo-!zMW!3a|m^F9Ja70}Dvq^>4<2I_VCA*0aAW!2+W-uz+-d-RZy= z2>UiDEN!ISw0Nx-RHYqWfts#zKhU0^Yr zrNd)|lNQ@YTIlS2RXmm~0mxk#t@pSAXVH&+$exvKX=yo4x>U4 z0M8x~7hi8^D=c6Ey2x$&aS7Vq++C9yUa{W?+->S|QPO!TSPr5YksIi+S+!yW)%aj* zSWSveXdrRRD!;O)!_@r>gRXaQ{4TnLQ~6JDHB&j&E!t8~+|sj$ZkbAWPZ_({SFH-m z=GM(91c5m8AJ_|rGh&YFx;h71Z^{ZL3)pyhd(JBXw^|UAMz8Da)*kO+RWT(L(Pf zc-55!(I6{O*|L*Mt;4tqTi}o_y_mRaY6vES0-5#_vi&aL!j*j_=nmqkr|XJfPvU-0 zZjc!uFZf&*nsddbO%tZHzM0?_0ZUE|x_H@#%fVMCi4V16pRg9E=Fk4Xa|&*>YHlN+e); z_0~}5f~l-$kRJ)jZ21o_3HQfuXtH+&+CD(1F68wb%T~y3nI$OQl%6HHV^kBM+JuxZ z@(KP*q(DD~CO8`{t5SX}!_tQYmF&G}6)#85ATG0ABt-^xlWR?_z%thB5I_%^(DZcT zB}X95Nb+4qK<;(jBq;mMCbCEL*oe(BMu~!m75cl=`cxn&B9CjxnhuLc0!!uNFre7? z=vGj!AU0lf^7aN(fC4M|(En2zmJlUI39nhwC(jn4Kw{UD43Y7s3`?v$0Z+IX=KGIF za3MD;dt83?y$Ug;XkjtFqzuN%-wF`HkHG%m%pvanFoJIrCWkj;p@h1d4WP@4r#jlU zeK?hQ)qDq9R}Pc&b^68#KSQ>HF~hS!e{@g#T9++*A^E2Z#?J3IXQ9OdYxbP{F2^l* zp(NJLIC6-xY%aBnyG5{3Dg!V?mp6YQd0ioGQ-*T%y!T+CxL=m}8D_b}%CmAQC+UBy zF^5l$iMsD^eD-D}SQDqm4ls1!83!Mcw}S7zVa~fFnSl8MfHO{2Rr~w$^yUS zl0GAYBVkRngjvmZVGrBFjCHv=`@XDZ3}gd4y9e|mZxx|h=^J_hUSOw9{v9g+3uynj zZ{T2I{2x?i;o$r~=1fMGe>yh*e-4t8|6NsAHv1~ zeOFU${SijGBU?%=KVc`h^dYrXgKZTqf1wRWz=I={T_?OiWsMX)FobDnl9Fz;KE z&~cZhi=nXsAOp;$yS-9r5YvmQb@fTLGL25u#gpvaaB%bXyg%E2pIrF74S%35RxDm6bb$7GGH4@%7 zZDzy3UUbLH0lb$qD8Y#HZHE~;K2)S^D>+WA$zyqsY}&5gjxgg#{5UoB*2S}Nn7s%H zK78LBc72epvtLp+fsa0`CaB5p;4~%-tUPq%sPzT}N`~}G9fo%~-sp!?xnB_xw&fHvksX^f(`_cL9K8li91LTeDLdxjiU?A}znZgjD&vJp_K5f3H z#POB-TymPU$>&S#Fy@PE?IVa@rUy*SlO4oIrYo(Kk!S}`;YilPrO5!V7GDCv1hf)o z#~Im2|77XSftOlNGlV3e zP8dMNGCTn(|29|P)U2uVk_=D7UV=@fL?bsILL+eGs~iap;n629_pud6Eq}fd6iQwp zR%>K5Xq)X@?-dX5zE6zbvkk8dlIUs@JX5-%-tau*8ZYixZwa&w2$5L}1{rNLpXVX# z2d9WD3k>}ZHLLh_E1y}9j^cP)t5z1Q-hh~E01*Gh@$d^>__kRNnZXqp*P&ELpRWyB|rpsad+WOY)XscSijrhnk+##TZVomBhg?KpwLnVmppcW zEZNaykR8o79>MIFZE44@c5C_MA|cpDUpwy%NlH(^6j~;>@Q)60r?ZF+z+fpW7Y+&YkVeA`{tzK-n0R$WZ(-S0n zk78Y(-f<8$ypRa-1OnEQ@|+FXjdf@S*(m|mJ)=Vd8i;Ydvmn6a(YklDs|&(hi33bv zcn&-e4wzU}j1lZhw>E~=OGRM2j0H(WtM zkn}qMThI-Y+C&%$bq7fMNwIpqmzrfFs}5C;eU?41&GusWggka(!q68LjdE>j_O@}S zJCa6XDt1-l=!E(8@%TtSeE4OZ5e!~0P`Kx}Kz??bFD{#S)mqoCLq%TPhZS%x$z%^R zXGekt7O&MH%{UXR64^%!P5MeswG{NI}agf-JI^~Kap-!7$7_|OG ze|f-QzKyTEf+UJnVkx5w8HahdyzEs9-XJwlwAKw-C>vCyt?LC7?oc|^AMHke=klX! z22ClzW5Pd}>ta(44EPyXn88jVr*Q}U;=V6eLPSoaFWEvM^d)6|sBk&!dhLWNE5q2I zsGXkVo38$S*poY+-GMS(#psYl&XJM}!Quc+R!W*~u&xb0C*DELqulUR ze!T%4Mv?XCz?YOuQ!{!74ip`B`VyT@t%%pC@F?H8s4CBo!83Dbf0vz}x|}cHpbATC zF^lgqdD)A#`%~kS36a;&_W?ii>gs*8H)u=Z(bZPzN-s@2!cfKv$!H_^vC!Q0*bi%> z_>`nDkQ0r-WT$OIb7t9SPtrS;wzf(TrL<@`LLGv3|CN?=Y%;gnmVaWwxgh@8n!91I zg@!aoL=AYp2{J5ZFnPx9S;s$&df&~+KVTjm{h_1DCaOZ&$NLRj>}?v!%=^dCQ<9}o zL`R!oI5EK<$(tnM?mwwJu zB%!4AXj3-sPl?MXcfyLKI+z-{@}$vmAXda~{PB&W9&(yAqGGH8xAGv zafSAWz$P!!3=;;0*$m$KRm|5(^MNVGJOO!n-x6^o@_vLy4>=y-n%b&wz&tkm+keMx z{zX^*Yvb@gxf6`6oc}i+=x6w!!~>ORZP{(GA^6SyBGk77-oJmP?PJiNysrZSU8e2B z3*`l|T5mRApp&k@>fK35mWbq=R|*7av6go0>fG^3v<9B$SFe4JlgHoL`MO)e*>+a< zW34v89qXnukoO{8-^)~Ua_ahcQPt1si7o!RXLDzVuM1le zSTr+e8-kVaH`soIQKW^j0YEwczy;r4mA)EYRkeFaitP~&^{7x{aPNWj_v_xq=hooj z*gwb(y~nAdy0~dTJzU6{LOEJKRbcTLG#kB;E^%ck;-o}OAK;1w<_nlcHM*=LQ}rRr zlOf=fqab!DNu*`Ewy!RBODSxPg$=-YKSNNs9euo=)gdAw$p=@<%c>X~`WWz8F|m)J zSK{s**Du2lq^Hb0OTGghFAe}hz}U!wOdNAH0Wfnv+^I>(UzP=;&RWKfhUi0tpDN)V z4(6Uq4U+h{tf0xtv}CgH)F-58io|pc`E+x?5X-_aa}=U0ucgNA{~u%T7#vu;1&u}% z+qP}nwr$(CZQHhO+Y>tzCzDK!n>p{T`<<%qt-9ymu3h`*v-awzSFdh??c%amkV5L+ z^1u!6;5j2Zgh)1 z&RtgPhSZ!fdPTA`*O_)hY&BibZEjrqxgDtci+n+N`f=aThI5C)9lv;c4Va~t@WY{*@WB;8EHP4}qi*-x%1Xy8C z`&sHsTzyO!4;{<5&qJqy7vm-HPs$d6$%l$WT2cX=`hxIgL=3gVh(6l} z-TtIrVR0F@h{8q2MAA8Mx_UhZd7y5+*K^KF|KTkf0QP)Z!Y#lK+@6Fq5G*!CE{!RG ze1DT{eo-!#Z{#l{PD48Ip(0?D-?09;<`Fib>jS_l`7#Ibg3>jp%85AwrkE0HLnfuv z%_LpOq4?(ruw8)IL2jNb4;0GTjqHfgb48v<1=m^Gwd4v2rj7Lr90IkeIntUyNvQ=Q zW8UL!X@-zwOv>RD>+9)F>5ci;G0hX!AtBNN-MUq@^U^FF1VpZ=!mwN>Jk&?1R)RP_ zTvPm{zMv2pdT~Kl`EYn&Mef% z6QmsM2ZbVk*unS&dMr?!pWD1sUlG2DL*aoTIUcG;Dn_U*M2ypc#QU<=>!yTmHH#oP zeJ7Y6rqoDOj6Tz15}}EPIu?jFXkkV=Eo47O7m-czQo{nQ-`X5Ox2BYZRdi$R0B^?mMYnwS}{=UtAk&#re{T88s1oT z%$7j!g2HPURkJrxxyolsVKvl)#{{zvJ2RbxsCSyE8#O~5>CjZz>k~_clA4A<2+?;v>_IQ4+S;LyJw=VVnghDCn-os*&9&9 zTe|Gx__SCJXXZEvHN%*b$N>c^ZH|{&l@^@H2|mhuTC;uaTr$GRgBK5Z`?8(<^M<9` zN@6jyP8G6bN z{e0`5J8M5+4D6*cW5xFQ;sO(fwQqXi$i>OYu?ey^K6yrPaRbSHFc@wpe0s&zsDuk; zU(*KJK)RW~J81M7P88s1`ukkkcAN-mcl)5igKTkVqL(QHp;=8UN`@2&8n0m2?Zn%NC@;)2p^MB?EWIpT^<9Y=tDNJ@r$$rH}1Y zD#efKbh!#L#ik^?Uh19uC4gOl%3V=X3GNtTm0g!u3q*Bt#}yf}93bIG7uT zoVu7jt$!$x_NfI>w5muf8>RUOvJaUz_hz_UD)}%E$Tl#(_;|a8$hS-K`A>3Jy=vsw zA5@2cLT54_C0s=as3-Dyo5(w-7)B}$rCe_Jbq>73vM(`ZjSCU=EnbcnB)GSs75ih= z=?C!NBCa&biX9_LO>BK5>LgW^GBfhH97+$-6W9sL;@#Z#qq8ekesO%HA^5Y0wC% z6#=`9{95TV4>dIRg9Dw_;61EPE#T3fj&j;h*QFa`0@|d`7b+T2G)PWI;{T_*7+OP`z;Fn zPwI%4{{OQ z8W1CVSzRB;FIR^zai<5jzuRA`^nXxc*hNC-TU(KzulnlZviAAvd9Ob_FK^!+E-k$b zU2I=pAK#5B4Rl`HJL-+*O=ZpO`zkqY^_ki@e08>CDFarw`)x7&0AsBWbx;MS|GC|_ z`S6sbcGSy%1(jWbrRIAGim4B92qOmvji^I8cslIJw38k;k2X^1Y(lbwx^0tznTfw z-fLvLwx7x7Ddc6-8dBi2+g$Ysh|7kuee*=8ilae|-C?P6LO)TgX`><$3GYZe*d^N) z(_)?q$_G}CW+a%v#g@JE63mx~!?22cy;H{{+G1P*+Bo0;A@&*hJzc4BQIv^HHCkxX zxb3VDMP8MB&34Cx7C;&zU(_C_<1&_-K*^WP7>7w%KiGe)X6YYT<3bWoM@&JNz{*3M zTe~P$*;c)@rX;IibLPGJ-huHBx(L(f%NjI7I`$VM%uOyNz64~cT7Z48x)K!37o?lc zDrqod3|xzij$3#hInU5|;_$3XyGaYxW8#z9wGxC5=h zx-dqpAHf~KjqOC-1Afl2U|0`iN~Z^8R)d+|(F8=|mc|xDxwEgcBW)4!(7NoEl9!T5Wivf6*cUTCSeJgPlX+ks4;9+^;bwOhYqxyT}@`(UZdi=!^ zza2$yq`$mS=nb4%2p@3`5>%suM7$YpABo*pMKkeOv(l1!8rXAoq{;l?UMI*h-}<*X zBDUrwcKuOJ>9etlOGB}VO!yXozA=td=9qSrgHh9DVJH;#%}Da(7)&bDB2e6^GC`Wy z10TL(p0YNjPm}t-aZVekns^}TSyvcvV-!y|vSqOpZqT<>R+Rz%{X#oQFt8p%jG`-zL^=y^Dt#!#nQrKvBSzI2h-2=q-l4OMC%P?ow{d1RL z!ezXs@~2xU3i7j#Mcbz7_b45~+4nmE{Cyo~4t=LQ>Vq8$XT2V!#pO|OqMAj%bPg+f zyP(A!eUvSGktPj}7XFj-_#M9dhP!B1hWF{ut{I+DH z>KN@L0;~75PsIv7XFIwt7><4^QI zM^M=Z-JUqd*NRwiuY84-*extGmt9{qk>Q9WRjZnjJ7^q)Z7Pku{Q-o^kTQVms2#;{ zu2e5j&%U_%^A6pt(%~mm(!N$zccGrn##3=b>@H9s$_!7zgz@$*kb@0PsjO^aB?V76 zs;{^QR>ia@VoPx1#7U6`|D!AIEWqzrzlS(ZYq?1FTKwu6-ren9SmrkQnhyr4=NECejgu4Bj#9lku%+1jj(mzKzlyr z($#ToQNs3&tWM+UfJWmj5DvH_n+#YF2K3Or@=y+;dB`ri3tlhJ$%$M%%6Ma^RR?~0 zZj{;o*7g5s>FFV9lZNT z%N(C6f-s=*Q`ClX?2}qdUAVW)nW=Z)&GB-RH-@Ra+*p z-}Yh3_PyK3Q@KvT-d=C5qHtB9R?RRWQ-PNDj#C$=1UH?x3Rt@%q`uAh2Gq)q1WVNI zC%n;~-m^pC*O_jcYd=l1bl{S<-n(Dk|15p}2zcMwzIl8a`?xk(WoNs9G+tdz3&An9 z3DsCz2Luir2DZ{~W~{p!b4n3cbEH?WEC7SM2Eti&iOxCKC*k}o442fYC~inBe^@7o zgghV>82LqcL`K=9z}-@zVq+Nq#nf=N#~G<=lw?R#CcDM1^lT;_yp}>8n=-5(9!#4# z6%q_E;ggoHcIH`IeGMd%T{49~Q5_6^zz&iaVscAi$cYTCiq!DnUGNiHj|fVF?8l}hA+og{aIT;+1tTZXdUQ-+LW>2IC?`=n_GTb)#BW%} z-vUKF{wy?6sEBh~ji(?5d^wiSqBqi~a$cF_^mm|ij?2aqmV7UhRf$Vz8)M#j%# zG~`nr9oeOWc~J1aE}`}(SPx3d+mVPD`tZd9nyvO}IN_d80)*f(0WB>HZE+)+~# z3M91m-w_x(+vB8n*dEdTMpm0tcuc6muE%`lI61d>fjIc5B1DH8%tz1;mzM}xDtgnTX?=j4L{k(XSp*Mxa>e3k@*^3d^q{| zczoHTPHdBW(D3oW&V&Qfy`lzRKRr2(Z*-%KT1(1SH}m53dVP4i6Zz{-4?pd>@g7Y& zdN;c*ekiLf=?X6Fo*AuPGQtTr?Ty&dt>z$r8$Kxq3W7jYZum^Lc3t|4IvW?E?Q}=S z(vasrlN$&X16c!!{l*RpqSh>rz*i`Q>jsHDpa{V+Ug8IftxU;{I-wASp-2t_ZYwtp zJuTm#aPwELL*+rh;v|yZL*^FCq=4UnrKs~JwCQl_O4%-X#&qDp;4YkxECR~0($l%L^-vZ>d+3x96I%~fglb^AI16ZYYg~fTR6^RBb)rMqtf~fX(^Nv< zxN6FU+drzJDu=KM*N77KoS8v+=q0&XZi9sx+$QojgD45tb%RlIrSATTp}qp1;3}pZh2?M~f}$McDqU1s9PUL~(A7)`#nCLd zw7_>*FUj`^Q?{g@^Cg8K&(gHs`AWu~x;fV;B$J_rC0)$Iu7e?UxSaoRMg)z0Jzln_ z^9AnC#g+Nj!~UNG{P$F|GXEcUDh77?e<9Yt%i(`G>?7(rRh#VaK67>Sms$k9m=did z;Gkr!1tEi|KyUJo1P>>VL65C46x-hhRTjiQBTULVo0C(u{l=Y`#fqFpO*xEr)!b&z z-aq$mPqJKX`D|zPP7w)o@}vFFv`vUw_{{qnSN_eH?z= z-FbKKT3_p4=eA)~UD#X*?)NQiuDJFsLN8F3HhuWGuAyPnTYuFD&|G8~qHHn%l2o04 zgsHUY^$;dMKHEP(-bKUv;mL-zm329$RN?@zWeSl6b+RJtn31NYH1=oA;;Ek=5EjSw0VK6h5?E2!FeF)+o&luA(C8ki zUH5T#GK!5gts?=0NYu7FYHtLrtR^h$aUO#(wm<7c!u}OcI~h7vLyG=q7+7_(0}~V* znA2>h045e>dH-_KA1$n4ByW*hz_3J1v8i;*WOx-xeV~dIX}Ox%Fk3@N-1KGIuk%FO z#&H6O+Qd>!6s^q4c?31v3Nr^Kd!5+gC<+*9WxE1uGyCn%O--a88YC?|Q^ffdZ*7V~ zxK%T#RVOp3R6W!X_|_gPA+nRDKr$1Q8@dRYA4;r30Sc@+O&7@H<@H>IMDcwY;|1nE zq7@%lahYL<_0^@=uEgZEL~;U=ogqcEfe&WoT27Fx`U_Pc6SPDVMet}lM&c5NMCVSi zPlDPF0TKGTCV>~XUsDdwkyId{H~Py(aV0N$qG-l3!A!hd785kUgQ>WX*NuWWU}~ z8EKa2Yau>EihPnBiq;&$WPYO}Nj(A})Tjn61tLm(6s!LZL02{5Zv}-F)C$IqUu8x( zsgPV5XPvpAayJErzT6oL{RauNPq`BfD|M|f6OoRO{m z9eZLBhC&r+`aIAKz~+|nC12lNHN9d`wAYGGlk}6kSAV9RmASNtw+|xVENW!_cEgF` zHChzu>RiyUDmJe87K@GPKuuZUR~??-G;PV{dKI)5dtc=dnD`U|_uqbM|NN-_{?r&4{u@lQGyDsfW@q?ctYm6Z zb)vHPkwb4jP=I@=h&k|L5B*_{MS4QdEs^gD2ZflYkJ!)5_z zRDz^d2&$5+aIC^15l|6Dv%<)p7z$@de`T%Z@giakM*Xm)>H?dVORj-}Byg>hMqqgO|R zEy{sK)pQNps?GPVThb;}tV${s!=G;hN_BfA#RtzPuQ2xq;DLT9_!XqEX%Vj@>o+=WV>D9}TYE$s)<+RP+*X`H-@lnFrZS$WbJgrfi62R7$fXU_koBNNG zG`p&@8Hl`2-Vdj@=a<6|w(R|{Cpot#Il3peuI)dYk8egJk8eW_L?L{0H>_bkNR;aXA4D1DBsCLnqEnFAjik+)cN&n%W3l*n0-%VnkY41g`JFuYhl2rDT2oH3#LnSMxeKiaQ&-fj&1l*!_+Vewt~x);K@R&}Y~D|f{k)qk zl*k`|2{xO1O8ohef#QO5V5qpbbvkfZ+chUlpKNU(dCKZQWY8^bLCChK(35`y=7d2< zSxHPN$c?;2Xagb+d9en^yQqSN5K-qA(Fe0!2$002k3SCN#2l(hTr0nMhJ-7=eEK-J z_L@hNt_3h=d==HCm*1{-A`#Raiy0lk9E_cES~rZ^TG4n)Bx(sqjm9Z;f}3Tqo=TIp zpRQc!NxiUo_J^laPKt7AJS}%X>oMF%J6LS>~?u}eLsPB^Y~!i zS+YFF<14ynI4)EExTqI$zV)U+&;&^EK#{t1!(Itp@hSEm$wj)@hW&mgsz#%|e+vvG zIMWH8HCH?~tkF2uri7}-d;MaeN~lN-4%RmquAMkIrkYA14gf5kex|7qg}UTpQ-JyD zlk)K$OzKx}r?;ImA2c?~brVSh=RKujULk1SRgGgFEO0?V`>adux79K23;hl&gh;iuD!b+?tN#e+w!Afh& z|0hXl8_tDtRP5G?@AVliH?;W{MD36vF7tb9LoA)?+{<@;bR* zuUbs|HNB|cv}O#MXPiFQ0i|MnE)|XiI6a?qijUnWyCY}1qy9n4*~0jN#82G4zBISa zDH4RQ*;GKW^QVsfZbyNr{*C@AziK@|K#`2#28R1|QvT}?ApoW5C+Yymd1+hGLEvsO zN>#Jy!m&zO3s59ChVQwOofhJ9dpt*0uYp?(e&*OKQTofD;KDB6si@_+=fl-#P-I8; z8dWn`vv!t&x@cv6#6oY_XMQJsp@aJ!}eFmLi{otauny~4XDd4%H(2{y|g#S z$#z~v$7}3NEmrZ1=cy%Pe=sV?khRLB;18&6MZMpFWj-~Rl3H7WuaM|iM1(_TR`H|? z3o*sbDvOKHR)6K3j_llK*->6c232_rtBWhFfzPyhTD0(fus7_geGubsDK_>K`5GA1 z0byC^02is&F@sEn4%_w@lA`7lWh#f2T0B9w1w?Wq+_kPPc? zJPDe~%THGVjfw>wgr`kw+2iE;wlRpZ#>U;<_JlRFgP~cqXW# zndYyV@t}1vTp#kN+0z${833)II;*+T?>_BbeLt9a55>(`8{-a24iE610|B<*!O!N8 zE5UgDm`bJ85QR6Vu68>|Ww4cipk4YxbAe#Xx^b{)%I^je z;%w)Tnz5t^rrdaCCjdS<1BMEn_=He@!;fUkrKuEEL}nKrOsIw-Hi_9cY3Pq9!dX)P z>^v9D%&(tBY5ws_zzlfYjrjnihAvGdi2#~IC?;(JEy~U~HO=0GMlOPCMxZj1&h}rV6Z`*nJ^wG#iG%)c+|vIa>9nG`Y5ToW=d)V}|F>~_ zko`c1x*Vaz#OGh4oewL7+Z=VpT9>e7H-CLP7AYoo5SHx9$`^t6TBDkQo}1>#E+))-8L*e2A}gvN|nsRbg$7mJYQ z1`*bz=DcJ1&4QFhZBpQZY(L?MDhIAn{we~F5I>|Fe3+57cpvpjhGRy0wu!@6R!X*u{gXX<%vy(9{y8 zA$@MF{gTxU7GIHrJB%y%&?a>-cb9sv3^vNC7o9iARV&S%jm#IKA-S&n9)kJ3S;?qA z=A68UABYtLf#J38xaedztv0xI97bp#P!&q6Cb1gbS^Y>&-M7R{c*Q+IK>9? zVeie$%ZNZ$Qfrl7TxUdW8O7p2Z6_P4%UDM2EzT+**w z|H++ce8!vs4Gwn}5Y@=p4kCJ6-YS97}g!&R7UWMNj0{?A|r zsJ@w$dJ8M7AOvt?Ua)nb%mN?;?7Qp1N7?k`b)j!mA(NO&fip+lYy_#V61LE z{ns;bqdEsr@^duLl)`E!15a?;f6ds251ds@NpWCt4wMAB( zJzZdSz4Ahn+8>>7mm`B84(?4Z2rTf2iLvYZ6j?@F?OOr_v-GoQI`n-I#yBBn+#HiM zCla*Ncsa|U&y&#dcpU8QuF1z1V_&={ z@3x!;Fy`s}8a^m3Q{GfJw)LB=kZ^2FoHW3iuOu|!o}#6iXu+q|jB)00(iFGfii76; zl3n!0j@859nJ=k1(hl}WV_k|^nfor)Ek!*Y%UX*q7Cla>cSg8=nm;!~M<%VYku2F9 z+9Po#&SLZiys(zQfQhqfDm zooHinU^|=wGSBnIcu5wiKUpkG&cHjv#@~5lD*Bco`T*~4j zfMGApb(W{?N9}(Z;LcH}Q&uNj1A{M2ovVRzwWZS`!1{5}u5zV!LHzsc3-W!W(pvS6KRy%HVw_nIes zK;XDTC+vp5X5M1tSrtCW(__375R>*k^k0yI68g9YW?T?-?kdEc^HG=qZvEy+6x#iO zaIb6z(3SBsWHkHeQ0OdZ0g>Bh;~7!c@B3BMwHLHCkmhO=%_7*maMdke_iBW=<_|uzfJX$LcGCW>L;#pY;12XXENBod@AwIZCff? zGMj)0sHbC@6;;-{&evuh=Re-|XKr6#y4>Wx-nVjkdHLSg8)C08+HyeGpFla6!oKR} z;QT&(p6>P|<$5@LJm2zm9UiZ{*8F^Uv3ITql0;C4Y?Pvz+7zD4S?#Q}udYsD(~J^`-sIg zrV=ze)vg65eo~|GOhl-;d8QW#AyOlAXQ3X%5zZnMagUZ~T`R$9ftwJ??w`}LG(28z zvQlHx`+{niOPgsL)5HnS40f)9g<7YKb41Y-8rV)ko)QQT(@7E$mlva<8g{oo8*^kFB+e>nIyhW0q4-c;opMTE6?C##4E97S+;i;;0 zpcGS}z(w3B%L|-iE0)5qQtC2UnYDXkPh3UhwXFh3>%F+)#&H2S}`?q;g`JfXr%{8a=T#}k$jMw=wv+0U>03A9|#BFtSX;J z?pVEGFRaap{XjKE(qX!nqGV3di*T#cXrX*afJrMrgv~e)dIC?ye6@Ko4ceXW;CqHC zJ^mam?#!wty60DTS9TlrV5Z7Nq)syed0Vu!#c@ad|V*2!kRP$*MxI! zEdHUy(0i(qzpIq^Lu049T6|Y6!Oklvze?v86x2(_D~Nf*o)g`COewhwa0@j+DN9Tq z$Bl7{cf5=YZxzy8Fl9;66`^1u{@XB}O@i?JJc2-K{6Ogk?Bv$Yw|l&7 zR2#p>-HFq`Xq2s z2HDo6G@RQE_cp$R>J!NB-XU)#M3@L30hQ&aApaywqR6?FbW=_n6fHx_B8 z<=irAs(Lc*P_CfXUM(s?LFgtA^{n&R%xLGaI?~d?pIl zCRG2!rnK#cCzqK{fV|H`td?OyOFXbsMG|~;6AY9sYA45xDo{B9!_V1y!A)gWB>Yhx z;!=f={#stAnDUqw1YOoKcI-V#Tqfho=#xWsKFy=xrpzy1_gH&i#&{Y1i94mly*Al6 zTr&2$v$M-w)HRFxil2e$;*3I$OS(6m@0C4rWbKmhqJSCIzdUEp?q1B+fopIY?rW z(8f%YU3jp`8{Zi!#;xdABX;JTTpCW%5$kG5q&wMe^-Zwor(Qtq*WF{X1UvX2`z8cG zQ&c5r*fnAT7_<}@S9rUF=l7^3s<$9;?+7`L)eJa!Ac zh4zxGr||dV0(sbxxRG41`8g81;Y3s_#-YWVlT(1z_gSyyWjh(7xm5#^g%S*_Msk)8 z7FG$ZgcbGna|ukVNs9arVl@IQZ%Y}Y#1d{E(Sw%hITUZL%9e+yfXoRY1oVq>3qflU zA8;wEN^MZ(GIz?-XiHY{kGHMN;>1m0>@;ctD~$23A*oYkVib~AVF175wj2+p@B448y zpEi>-_$AiGeT8mCup>v1tRXt0fs-dS83krGTA9B0r1BCLn31RI0dhywn5~>8A}tzG z2)w{PQKU`l0ecs;ZK0UBpm85Z9qT?8W49(h{ z(irWMrbFbPu~e)u?(7&iP>3 zmU*V@^uZL@T?9*e3tEIdBynm;n1$xb&~rUJofUKPG;qXIQm-~|y2qb!AAyV<(CA|- zwcf^4cX9&zX*zsl7g{t=;NnuO7t?AOxp@zaCz>}larJdd?y$l&aQxP$8^SSbL`#<_UM*4pt za2Of>CjzHMV>9wwweOSn*X^->qqeu=$56j^F?o+y^n|`<}cIJ8`qgvwkQ$=r^ z=5{8=VLd&Sg{I1RrBa!!g*epAZq?@1+40cO-BX(BlV{&+69J3`gh~vE`5SCzZQGaJ zQH_yI}?HbO=N3KOSx0}pO1y=sd zt}vFf@Zel~AmM$7wvM-U%xhJ<>g}PAp}VQWDYxWzXKtL`yx6?2{=>Va`zW^`Bx=aU zQy`p5G)>p(E>9Pjp6G1#q*@;fHnzVok3wrD>fkEo)t<0gNxhwhE(xr+IZbRCr|y93 z_FUfcJxNrs&My-b=+I|AW}$SE_pw9Uo8j~T8J-ICZq>5+*56&9C?h7Wm%98Iz~S&@ z{X)p+E&EH9YY?Xd(l^{XgwZya-st3>7*?x~O6GdN(=_6P2gEu1gA;g8(hx--+pDO@ zN@1677q;#`AOe#mqVSBy_Ts>ud9Ym}ri5lF?h&(8opL4s9E z8sZx(n~Qte(Nr5FC!=X=<+PLHDqaKrU26k+xX9M&cNS6ovnlL4TM>K7Dv06#o8OU{&;w}8o)(BMI^lrY<4#?> z)!cNM*#c*{6?V`QQjqdz_lE#qmo4M-OciKm9D0)|Ay{J;dbi$}6;!`Z%iJy@lFIzi1Hb zH??rfSY=ZY(66z5a&QtWSekG zjY2-BIqZux5e;=pO(R!^*KDrNLwDtL%a4Fr2z*Hvw~P0xVxmB1>{$jm{da>;0yWmJw=%@Q|Xf)thSI)%p{YH?9d7 zt|^Fc$T{Ug*vFdo07$h=WT$b@G#`%@tZhHNYN$cjYy*KOu189&TGEfjlgHA*O~te1 zfFJwdY7ob`CHBcU<*Ljl_(5d9_Cifool0_F^q_}xOG>(0fIZ9SGtYkW`OYfeCu#JR z)0{5ZSI4SJw%K0AiyhCmmAFx3Ov~3al{Ul=i(@{%J_?)2)nssi#3gOM4az`Gv1zgR zREyk^AlSA`H=%2ZDKdKng(C~$gyaSFw;#^Nq9vS`+@}nnfBNpIXE1b%g28h`3%a) z8~!Q_p$u}ou}oM z+e~jLUzUlV&qNlI>N0#QCb^XfEIAVUZa1gW1XmAR61-{c<0FW{pr?1}{jV%RX^`0Q zAn21S5IG2wtUB0O4M%fgNym=BD8Udz=VM~b0+-kmmV!Z=3&>J-qv)f;wb1Hv6?CPH zGU*Nyb8I7PZIW{xSOMl?&2)X#FAW*8NkAC4$qlUS`jF1)e4n*PjwIKCwQ8es80~&C zGckZD5VA`op7;ksgt;U&6hMLe>?DHZrntkxBSRCKVP^Y8eUPnS4CmNn{YGxP$!TNI zcp>Oeh(G#G*hGpMams~qHK-)UtkEJOnaLNta;C~%ATzS?oCW4#Mq`igzB>NoG>Dsq zlS4`|)(_gms`uYc{lgK|Nr$##@hyd%iyl7gWs;x*I0drQg`?ml9fQG_8NXNS!`XB( z&Qz*M2_Dvb@&Di*8pWW9#b1#&*H4{me8H$ACl z!vY{nEi;K`=%T!lxs(R?RkO?GO=LX=GGP+N-lUC&*JY~GWgs09CPePc6QY?W-%r~l zg-HebGOM4n3UYQ6iILqx7;uG>#7PY@Ou}D9q^E;(-g2x}&lzMSE*JI0XI|)$kn2a& zWq@8wU*HND4+Z|EIsU)>?jK5qk^b)&f1e>48Cd^s!@%#C|BGQ@i^e}z$a#7M<68Qo zIzCXK1KAR&sEt_vSRsd}wYV|;V})${$9aeyIU)(~FY z_Unh~qna3JBfhd5qW%KaFXfw0ygIW!-(8+**Y*p;rAmpQk;GjHn5@@5kNGLyvjpiA+ydKYCM>6U{+RY{jwL89aiAtrjAFdCjpfwHG^AMcMcUv5}e?m7kL@5U&R0O!g^A=+C zwbV_$e4L&iUl4wLzSwhb;lk;C4c~vHN#9Z?D@jVBLcm+EnueE8pKbIbHLYRw$$A@h zBu_4;k&-L@KsPpSnX*rNry#)j+=O;pK7wZ$6FO$qw^t?QR;?gN0Sm*4<#l6^9|wJI z&DSTvAaWfx(WGxWAXaxv9-xA9jKInJg%Ena=pks4SoD zoYxc5>Lr_|2@}mqwOn3fh6m6ZLH@yd?5c5$&(O2mV@ zO01V$y=7Km28|w-8X#%DUxW`MyHW*qEGQ8MsxEY2yZ>aR(-aVkS?3|?eDFT=)-%Q_ zml^sjg6s_uAy1MXu7Wv?dl!Q*ZaKPG-j?5VHSo@qiTRbBe@hJTFNCxF>GM8dZpVwa zm*a-xoHxIStBv81Kj@&4cAX zx}atkZUqJ1^DQNcA7!G737hNLmU7_di9dLrsi(wZ zE^oyCGc@u|3}gL zN5}pVJvNU2E_(kVN5aJTzsiwZ{U15*Lq6ADqS(v^fuO3N#0({ z_$u0$s^&6zLgVi&NL1ZbeN>-RpXyWD{cwLUw0YZ|?SHS7ojRFyp6Lht;%eYq+{5(zKXYNP zO0zo9ry8Vp9F{k`+6~Q{8KyatD#PSix4L*~+{5s*PZz?%!aiQ;11oAv3Yb4M_$2><#+znR(V`>iqDBULH99V6n(n!w|qt?4;vgTE`ptedQ)c`i& zU}zK*K2r=H4lZWk>395~qE2H(!J65^!R~%f&^f!P2D@lAzDG1q@2=R}WJ|;?N4)N~ z*swtaqefFnWgk}Y4BP;JZUe@WRQj47M?iZ=&GdPpuje+wTOMh%;Z`Xev+!mk2f`9D#5bf2~aFf0g($&l~XfO!rbz!odS;+9A z3e_TI?x$HyFn0y|;uhP#C+>U!q|ce>+vU{_z22Fd|5&>5EjinLjnJLYH0eq60&x?A zY8T_n)83c)K~eDxmzoT7iQd`FB$c?GA$=ng8+c)Nnu*`Tn8gwTaLr@y@qku2|LD!K z_w6vNyE8oDJ|!=BdKHHL*MGWZo98=13LUw;p>EkWt*nw~K2RwfpCMFnP+PdN*Bu5y zcvY{-)^+dMFdqaDkLz$^-<6Al_Rr@$t}by3SpkY=$ZPk^UO1BdLG z1wYleA-uZON<`gi$NNS%lp4g||8Pl`#sawV)(#mxSTmAnOH-|WI04a%cM0_^SC~wn zm{~+YC9U5;9A{B88|yJs=gyIM_WrndbaPmzIo1_mJle#Z&Ob5tEmnv)vJ;%i(e_1x z2cpqxaG?0!A1MtMdDJwHn9qA21tJF;HmEgK){e3H>Y}J}qsCAarm6NL1PIX-2k|1o zfhOD*I}Vp_oj7f@O)4SR=vdA|Q)1pwx^YwkmCcbT(tF;pHcXDlnuMbk!Gcv5-53+{ zkqTG@Ob18E9V1e? z@!pw=1cVrp0UINTsE}iRac)oL~lpzIFk8p0B!V7nX>|K4wN_&en+ z;*@2|WvIC_n;f{Oy=Hp~czG%6WUntq{k?IWG6GFB>5vx0izv3hN~23-8d6LX4DHP6 zOCzYDj9UMaS7bMZtIS`-;%&{_xqvE`Ii7`7eX0X;FkYf|1~f z^6ES`+0(zE6VQlaT+<&-G|8WWIU{Mz)Xllpsvsu#f-8|7bgH=$SuXZA!8X<(NRkle z{zw=g&(jJ~iW2+)U+4LDN;ptIIJWy`?IQLHJnc%_fkxf%1mrrR0Ptzc8 zK=4D1Ms&eRvec!%kUV@mg+Itf?UX^1Fr^l-)9xuzJ;rnfiT(74)QU^sS^iMa@gF%b z9o9YK3d5Q#**n#bv6j7n%8%B)SS~18%ThAMZ|F`Ys8bP0;1lYUSw6uUYrwv#!1XX0 z$!OOzwMv*HIKukWXtSLN?x)MNBQPz_aT~gk)>_gqur)4G*?qKl#{l!5jr&T{SOSbI z5NG+)G(5U`E8YboW{L20KGnfVt%B|$r?h{Ww$~$a62i~F>9uJMHlXKC#(>&Bia8)Oi+)|mrq-PjtI||HZrY>Jqb|4^_jX*q0 z2X39q#oolgb$+U%H=kJ@k&Mp}D@8-Yo8y}(@Asy(?)lxo+?_Vf_Mq80aKQQG#Z*p$ zcpyhNzrZay(@mr>A`z-?u5YttiWUd$qyU&^e8@uT{|h`YvMM`}j>D1Z%{ZLHZw)Zt z=q4Z2dICZVGiL69be!FtLXAJh-)JckrxPX%Qen29AT`$_lC^gfkd%<}mvTD%L;sq( z$vod9Um9b?GWSu4#mrqRCC@5LD$IEZ$L4n?v3^|(F-%C^KBvFYkZ$K%= ziTIg*fN%sZX8+Ms{r}r^rk@(6|5_w4vHjnSV<7m) zp{zC&t#(@ewmX6gPuDMRh7S`hQwwJOEqb_oTYoz?x;&YE{cKi!{W#v~?{*$KruN(x z97(nTcOs9UyMMVN!PXu>U)6A*?lp$tVo&C8?A+q6cy$}&)a0|Scr>oC8-XWrUQG#R zSi8CC?@8zQ_%f6f76M1-OwuPE|4DkYsKt-yy8XmKe1Oouo}W1QCW}DHxRW5C&5MqI zCdBLO+2ecuysd1SG5ou6bus1A@iu2^^L}!D_b?J>&oS`#ChriTBN2G>($Zk!;sd8Prk>P)n`p|NZj`@NoAjcCF!zPPNsp_f(&3a0dS1l z0l5KgPKt+Wr2649esd6D3~IS9K$bvi|KKN0(NW^1vvm7;JwaSeeVc%SPxq*X9=+c# zR=gTcZIO_mGa-^7!%~snXvhj~(wCtp-Th}2n(Sm*K$}GvM{+HYuyDf3bMO2j8WBGRK#3J<3OYwa8TqT4Gzl% zO4U6ivO80~D^3PHQ$xHj&WN0w8C$qcU;t zxL!0(a2+?7GMTZm&k@ytDB<8~Oxhx^e{Ni^(p8gDg&k+MkaAsxUwK%KuLY$4p{07gCfcclWIfSg>o;;mQD56;_g4wB^dd95EIz*)fhiVr63P0uMFl}ZH4QF^0e z(UEX+NFB|adRHpYZ)$G-BjaTRfp>&~+UAH?Hf*A;7ul4*qy-K#Iz);0V)$O7c54vb zy%h92)b>`2emMv8fym!qC*8Ixq$1CBk-AqBaw@66=^VGU;a%eP4WtoyJp6K3df$f1 zq6N?_YiG`CL;Wxv1Pg;3FxDz*G1Jk(ewv+Lo72!U7sN!dx%6Nh9hEu{lu2JMlqo1V zA|RI5Sy3*km3lImifcLqfGDmj&YTuqTi-Bp$bjlXdvdG~Y;zUx8um`TgyPo#m_fS8 zsMJ1V4Aw$5`9lvPF2>$=hqp__`BrmdIi-tHKrRv%Q0Wps)%x766w1GoutYL?0HtKW z&Y9OOS>^e-H}`ZiXI>I5NH}JcL4jV`LnbGwv3mu4KFMbXD?HA#@J%D0pSB7Geg&r( zHfP9+D`^;cjhMno8dL-bN>-xsP*OCma_A!maxISX8pEc)n9lw1lHj%9SnCKm{_m(JM`$Rg7J*jbuB&d8o( zTyXMRYm4otddS7-*!}g;6?h7~QE-9E5UYG7#!CB9%@m|Nv;<7LK=6nEiu zNK3^{`%h|<43}JyY(YYIF>YVN89fMF)8?VWLQFE8aHKq%eEe1Rr>LU49wCy5f+_E$ za~w?E_*y=pd6n+~`d5V%nEsjmujPsq`SP&JexdX-eB15mQCkJV=Idd`5mud3##Adt z!b$8t^2TsVC+^l@NXiyT_D0#GLJ?=h;qtjdFg`>g46HOC>+VPBIW^dIxo}jJF{GW0 z^#nzl{rnbYz*t5{Ho;ymAmoctu;D9%;puTT2}b47cBSYVm<%Yl;AR&Gi+T2(KUc&s z>X3_Q%I`FsCD)Eu06U@^uDq7gDMQ{X!gMG?{s7RRZw6OB%?oU1pk zH0N%F^hmWOdKzt(+ycrXP}g)j#D0Bs<^%KrwrTlm8`WOEMgJAmROb0}Rfj=wM+@H6 zzzB1$Y5vMNkxKOBkEj?4bjE#NCKKcJ$1LBlartu@N&U<9$j1>{I#B_rsEA|A=0-$| z#Y~}S@vRsUZ%V=K=||H`#uOML4c@|Yi!%0WX?QfPbB2#n{H+9?>K}i(PIy}I(@M*r z@zz{x9ZGEl%Bq)URa^H;Qjyz0y9^gNM$=~q zsluB1+ozb#a5M?W~|>cr-L)Pn`)oXc9r#*MPtnW z^lH+F>!`ns`p8_o5j0FUJ5neTPfl#b7MRya*?Fm+l82snd~KfuI*wM7&3#ZmqgH2? zt1h?howq^FT?NmHmrkL94##1vjSsp`!6W6TiP$p^_q;fij?F|Z_rZ(GW6`$IT@8No z`^_jl>XPzAZ!kLs?xwkpgK=}dj}ge@*BOR#GjT{z9*rFHM=CRF{wIU+Z_n_rr417U z3(J2Q3}&W(JxcuR^Z&|WFflXzZ{Qz41|#mU8S!86Jpy|`u?cOWSqp(G;$}c#Mup$jeZFqL&y%#{$1jhU zn^4wD(*k>#ld3C^1Zdtq6Q_+%HVEWUBWsZp1kFvb3UljBQ_4YxEPp_uwX5IuF`|t=Pdquyk2XqEq2>z**&%aSWbh+ zU-)MOnSql?0>PV1Y1@>&SWZ-l&v-RTk{TG-Hf}jNynjCK2+S@*xB)>rt$8Lgr7MjV za)f_-(8E;*5o$Y(rV;bk0{U^aUmH-o-s$1B1{30M`HZ#p#nrQWk=+U%z)uaKz|q3q z(Ro-B4Gtgh@`li#<>z+@zhmvB1d`pspY&o+xyUOcPW0;Tf@nHB9~88*B;;1JlS}+${o$3si%IqF zmA8zXh_rIDH0MsZETm7Hp6^c{iJ`pc#^lH>88W)1?M!mQK-|6-2d(h8TVG@nY=i&m znBWC4KWs-y9Se?iPKWP{EL?xoCQHnXor#9)9f-z)WjIJ&6P|fGIPFzPo6Nw9CZ^t5 zP$Gl|2d03+hrSAqt#nI2eb6o0>QbE%zIQ-11L)O1l@;EYZdM+0)(jgY-%??QkY`=J zyA>f+Zs8``gHHw8i}M%UBn>HpaH5wc@+TlRoESU(MKK{DfEgR(5qBh|IWxt_R-zc& z|O3 z!pkTa7*bKzf6O|)aJCyXRoy-1Q0ctliOQL2j8`;IM>dS;-E5yOJ~&BB8(ATjI3Cyt z%hM;w5oc2UByY9+qlXwt>0^QgVTd)-SXE7?65`)qniwyMS?OlC#--b z&>Vx3i77PKGG3dfq*9Zxu8OBx-DzcWdwKEzHWeaN7E z&*Lo{1AS?THkd-P2toxc1u-oB4-S`8T_U1JS6Wzd%Sr+jbW@Yg0(ZCorNN}55SZTG ziiv+p!G3a34OF57eFa1UutmQXB@-%{GlXfZCg%ilGuSu|H)gmp+}3>OCQk2fEkLt; zn|$>)AWa53@@;>sYKGgkTBtu})@&Z;kftM4lqU?!f_XSQB&SLF?RK!K_b^?}+WshQ zR411|5V*^fGO{{M-hQ#)h}RY249GfB21UD^LE0b>FPj`oX4L7(083E48~~0Dd*_zp z*mCYEDQ8mUFrj7;f3#Fuokr07^ADH8x<)m4+n&mgtcQhE=6TiEw{bGcBjlJA6YuWv zrAie+loh8e=tQEro7j&VoSvh`lc$zm2Sc zY`jdxkUm@+j4GGdNxP{lRb~D4d@XG^>^qlI1^?tm80zWgBfeDgq)_IUZN{-GDpio_QElqfe{z*7EnWO_Kw;^m60%;aI!9RY zKJL_7Yo`7ZY`e@N@fXo{xm(T|*ZKWZ&uuHm8&9|^&DZ<4IC~IV0!4$^UL%!a;4RSp zFu(eEwFwqC6seRWXmqi*A|v7a(dn#M^H&AEkYcXJc#MmP0N3G5J zL0G!=TAn!$qN(%=Xqq?e+ZYs}6r9_bo=$)JL*E7winpv=+3a?x92-Vg*rh|Rj%Yg- zR9?qIHxusQz0*cRlMdrSG0vo7;3k`&Tbs72P2r``+7A0JxfQKGjNVnB<-hiG(8jV`fImw$E6LHqe<2S-+8qksFEAg zD6NNgrI8Z{klh3l>ATSEmQJukky}>N88qz%cdgnn)g zs)tmVHb&OjdNNHkdYtGvDTw|O&4w{$ri@JU$pRSQcVv}T?b7?Lv(N{hiAv<`XKVP4 zbqCF11-#$Aq4qDZ)%t24dR16Yzn~0f#QOJgi(WQLJoBuKB7MNg#aeF?M>}*9VkE>) zz)QXa`@GZie4rY3j}x8@XI`(3G!+2H=Tmf-cQ|YON{m#G+PaS)YG8=SJYe3(MDUEC z#W(mOd8FunGI;;?Z2uTM7AB7WX7K(g#QL8GkCX9#5wz{n+_3v0WcaM8Bb;gjE^vF1 z{}3`n%5D7tWUYR~i{b~b%OMZSowr+jyySE})RTykqrylNwXLu5I&+$f7X|O%Ou2qN zetv&HZaCNZ`K*2_#s?+`p9=vcrvwVAUVok0Jv_7w9TX2Ku7t_M!Z-MwK;`SS4QZ1d*u{f*6w%frhHQXIwnLm*8yTS=F}Jej?x-)z!@>?yr? z9ceFkeVVHB3Isx{ujqxZe+JjAaewPDv$v_!V&O6hhQ;) z-s_vs3>RyrlLPkAHrakVv>C8f!(PJ`4#QBAlrAkRwI+^>VA&uivSlE5=NHGf`z*DG zq-ONVGKZA9Vu80N_EHp_zBXaUIKXs%}-?rK0TF06) zKf{AUDA)VC>W9m(&Xzk3)0Es#xY)nArI4l7{w1|h1w&=Kbs02wjWr43jkfDli_nu3 z`^#&mI$+(p<5?j;HAqtyoViMh73w&GYqV0cBbu#x_K((88tGsvUQSx}fCs)rCc_!1 z@?#ZjTM#T8CazhHrt!^icx|04t16dpOlV`6#f(>6OY`XmLeuvlU31`#(r-hVIB2Pz zmskR>i<`Dq^fzCDD-MFmlyjZQ7A_$dnKOo>izMORp$S~I0uan2y zw*%d93|?Hu;C2VafDY{WM65O7cuuYU%MZYeq1!RQqAumVyjq?h`CH{BdM#kIih7ZL z%d(_51=P0y2z^Xz?-C7kYL@=nzXoYTxB3a8>8bC`A>;cPtWy2Lqs$psOel7 zJoZr)O-gFCue{K_`_{QgrsZgJ3Rog}NIHza>mu(5w_JOGTFW{}eI*|R)Z95JFq0pN z6wICi!x9smG>m3~nC7tV3XMp2&y0C@jo}RvONjxz_H#KL_RNRNFLQ4S9o}cjn&KcSs`; zvmN_%!zbxILD|xGA6PB?BDlcQuDY5}{e@lMA7iB{H3wWvj)-Z=PzkB_SFG055kk|o zR{kvLPniD0-z*~FfK$WVU1(Ac zhS(sa?;sh7;nHA`Nrl+QZtMXceG;5U;I?+FU)}GHP0XNQ$uRE0E(rp+B-T$a7P^hKO-%h}thEwlaggGC z4!hYCO3pC_oyG5ufFh((G0haghsML$L!I!%$h!JF#Ar5j7MLlu z7Y6QQ&y);bG$}kHdE#&A^Qs6Zr z44C-xMb1aXuwAlf8$N#`t1&S>YHsYxdDH3OvwIee_{07~8v4kuL#>qftS+Y0!Vu57 z37cx|kz@+z_6>pstXu#di^Q+Qh|b|BG7@}OO;5H)TS+P!mR@3BXln4DGFJYtjzHMS zjR>(d;Gr{Wn2{*{w9wHV*Hj`wY63l zT?KNGD-h~_YlA?}H>$s!D_SC#2JD&6ui`XYA{PSJE$z7Si#Ye>M!A?3X2vO|z=7mh z!h^V{Su2&3^j?i%=mM;;Vtm8AV!=J9O* zPpwN#%>T&Wzci7No%z3do|#xU{{@cvA6m)sPd&l^3rcaNrEPu4hVruvF+9~a#K9d) zw7~#7UUxIh-++BIJR6uL@iI)>l2iT{M@`pe>^XrqS0*)tE@1G^V>)5_>LWH*6K-v% zpI~e*D<5mXU!IcaN^WkGC4sU{Z|WDbScI&?WCvfen8&YKy^{KUf=okCfaJ+I0^}J6raEj_ari`o96y zZijhT+ke;HC#%{XYLeF8Q?G82zr^TueO|qbnqT;dPj%Ib#HwQL^ zeRM*kb<_10P=dw)+D1fDO|tQ*vMfQ4J3a`t8&NA7O~bCgFgbz>4{1Z@xC>vf5Z?qQ zL=Dd?=3WJnmoi<7s!Cr+^>jQQ+vR3j*wyoKJ2z0#dmYow)nbE9hSbAa0(3$r#mJ-{ zuaA~uPct$Jt&VSo8JK}biyH5#j;JxUhQNJTVX8yKW&Y`CfJS-t&^ux}KiLfyo& z;aj@3#&d9}D2=uztOK^&Hqky>F`ZKIJgP1p$|uc=Swn3sJOF8d1x&3p!DWIqv0V?C zi|HZsj~rx0w3y!#!5t>FXcp&o!cNeFBC#7R!J!QtGGyiClaxSCIjyXWkgpRJf4R5) zIy=@+_I~+(HAj#IN|WWH{FAVg3bVwqBZSMpjc2#b-)Y+zFQMw%#PXX5NJT?Onq&aW zw%vYX-rn)y+QG(Ufng|W5lwh;d2#tKTnZBqp1g7<&k5no0ga3PGT$<>6^hre$JwuL#tK0{)Z&QZcL|7vDh2LOCC6jui+GjKu#Jk|W6Nab zWmPn5J24g{+=Gp^kSiChj#Q^lfA~;h#^a+ZEv&@=YYV7IKKGid3j4x_5TEGW+ziW3 zwsJu_-Pl$hN#%XeY6m`*68ID0N>8J9x^rY%4!=(!Djm9nOHO!vA+P)%+k`iYKVobz zeu;u#t4^_f7M5srN;Ld%iLzg4_|ic@HNRal;!{Io#!hNXLLTu_xYG3J3n}$mjbGnFAO;6##qv*mFJNxc-Mx2mo`sBYKDitxjP4b z2Qe#8U*#_49dEMS61ZO>mNHAo3KGGE*B2T^FJ;zbc74vu zCe9iZ+ab4VsV2o=m3tES=g zOV*cWQD)27+%Y+GWY|r$T`E6L$nPHtL`^3&cuR79(G+rI@D(2iB4)pHnmIcCu5|{v zVa9w};fP8y*>ig~%QjdnfjcP0494PE0H|C%Nhq*CXd#c;kr0RVN3kosp)hpQ$wSwc zHNtO3pw5;tjuL+nHMx!g*{v!*c2>yN z+$7Ee|R&#_di^S%=JbE`(DT`(s zWYzVk-0YI(62O=_wr@+Zir94RqNSKo156|;*@nsc1iM9ClzcHy0ygVs6zuP&-(P^u z714$yo#h!WRF!3SS#(Q+AS9+b(71~irD`EPQgXD_r=Nzr1pK-;Kbu+mW&_0B%XWb&O`hS* z^e#V2p2L?6QW}~Rfh3+MwH_R}JyB@hVwZ_$$-F?%?kTk6HVmPi^cq}-Z|$K~Z-o>R zk7l5GXa;*#Hw1|H)-jpqM$5dVzk46OfcG-qS{zgg5wZ2zEK|6g`vN^2wX;Ab>{QHQuk zt1YgmLBa^Khtkv-B?JCBn8iq3lTF>4GE0y5_APH;qSd5w-I%ToHnfqpTKdW^Z>F&- z{np#M%FXrj`?#HOZu0Zd_|lA!S`=bS0WH-APq7~OSU5Nzsi}~!K(21*{(88*dk7nu zI=+6|+b||=-uSBP!Tg)MUG@AzO|ICDEW4F?{=ITu?Ti|J{(U8eGbDK#-1icsAQaSV zOk3%&@q<5I_&SHJ0O252%EX$?z*?x712;5)CvY0CsQp57Ap0~lE_7};iHzTc@mA647p7$aQsJ1b3y!|Sz`!a>D_@TyZW@Y{E4kZ>1 z1Zn0ZZV_NYZ$lUj5b-R-gs|bJ_6h4p+5!%8W(SR)YXWmN1AZ1scHp;Y)iDE2j^f5we9fe5$ z1^rQ|d5m7oVHRa;@7!pS(#l#HMa#JDvc z+3P~U*hyPfIC}*BO(&r5ogRYOpow{2>1GAc+p19DeTyx& zRHP%r9&zP9Vuf|-b{CMXPnv@k;sUl8N>pR)fMQQW{Mep`aJudN0bDQ|o(;ns%Jxo{I2F-*ciU z3{)R9V7F49eiP6~srEIXP4i&*#m(Q60^XCzk$FYKOf7IvhFviS|FmJ6TheYjj&CGx zyjO%~9iW#KSwKG-oeu{SeAC>0j-9;jGAe|t+o>tUZmiI+S%56naiB1nICbVv&bPRX zqOz#tNBIohMkBtDYkc26A(*ALr&0}$-|7HPq@o`lwF;eqKJH&be|VCca3b+=KPJph z;%O67@A6C0YfL`?(rD|&r)6EFXr;5l`IcMucTYE%UBpQx#f&)y16Aiy>qfH1xmFc3 z&6H2S{R%xk|MZ=>rLZ-e+xG^sK?@4J*pfmwHu!aT=0^2YjG^I62cHCB?Mw(p^ZrQ; zdFEnU-06On1T;|q$NesH`)5ZEAAg7M^J(vmnzGx=!_(vCXSc%sx%qW>#%VcuHT*}} zb)iB<@JJr}GSMk03DKr^H!6OkX+3-ZO~3sLi+;|vQ^cH=_Vl#1(D?Gy1@Wb_ae4>o zTJ3S}t>GK(FCq=4)tOy_$U?;+DFrc}@yR`P7lnnxH&VT{dKZ9TDjB6-2!D_d{VHoa zp(m)x9@n}&H5hc~84A!LSN-3~QQcNm+6;7H5SJY;O!BbPn5K@anU|VsY0FrIKb9wy zuwc()P-O0uu>JERdq$HAQuh?S0La!QPgC{gy)lUsPqs7+bAWd zN}psQu+R@Em~|;k{1^=gk|Sy`meiwhYT%wQF5F>kOwW;|m{$}U=MDg+!=rmeu7;X5r&+hl~?n=MtP z!*mIQECrUXkGAghK zLtHC6Fl~{9RPr@Pb~J1|x`x8KgH;)JFU2=CadAoR!FSw*P@ZC7V_b35lP@zH_jEg6 z0agB@0mQ-Br~-hmKD@3qG625>Pm!#--Rh}v3E!_Y|F`%Zy*&oa??1`uzZLS|(>wcr z%b&2Z{)g$EjrCs|`~Q;Df8|epOaswSH0DM_=7Xb;|go+MCi>z;CL<)w)cEEyS8h0(LDn$@o`PN;OAT& zz|`x)!9aw&CD?q0Pdk2$e6*zX?C|@A&B>>gOmvh?jO+}Z{AjrO1}U9u1mgw}GBS3v zh`Y2)OV6c6&j>fmwIR5S4#KwmC>-wdzSFk-p!~f%PidSA-_lszK2^8#FT?YYLT`0G zsP_(U6?OpgH#8-FFn1Aj?4_L6>_gFJ0^bDs3h^>K##0$zkpf7Y>x#6gS;qxPN# z?D}rzQ%R!B64PO;VnZ=YY&PJ_FC1}Ra!x4!uNrs$?PTMnZAMy#alv>dIr_t_jH%%DY;NETxjv9fOJwC z2`b7{nAHI`vlyd^gLBHcAY4O1w}^t^!v(^QtjSOeD-3|J{kH8HZOUHMHI}=Sv5u+2 zE2&FqTvAjomcSYZN+g73THPKpug2rOP{WOq0~}n1pKV2p!H*G+_U*Ff7FqbL``z;R z;3KiV%^s`Ey>BNIPaRKfg&LKuKuzhieh$XT=_RZkro*+8r0#|1FCY!(0Y1EYHqOwM z<71tRaMsS@?cveN)9vU?6G_rVCy~kr5htOt?;j$MLWP6KK}coNt%2RJBC5!xagi7T zg^!5t6eIrF9?C@xT0|5n{|@GstWJ<{kn3T7(psIfRb&`-E{ct&Ajrio1HF1)ye4LM zhBbG>1p7G@U3rsLdV;QOucJd!eUD)AiQ5oGm!04qDb^>mubD>X(%Uwl)4KqXwx;Q* zIDo-Z24OPca^B)!l8RQ&O-chqm>6kxB@SDC|Lf)=s8{LdA4$itt630pjMN-eok-_d zhecZyZta68!D3eBE=vwfm1qZTG@5U_wU(;(QOma(eDG->5LJ1SM17)uf|tM!)G_i* z=p3@X5G6gU2eiBU9iB8Mig=ym-r8W zaH1w;f4dWO1&IoOA&8k|xLX=rQ@R8t3gbL`FxI=uYQ_qp5^Sh32tz<(>IP6>P3fGz z_f)cnXk~R0_OtvY-ayK7{8KBv`b>!iXwOM1LLR&zt6SWt4wIE|UtyiQ z5-VexiA7)AfJEVF3)(h}u&?&#GW`FvO_9({Iig@!nkhxet(hbqKl#8-LTNS2wmB{0 zj>nXtI+AI8-h5dh0tzj44zX$~HjJ%A_M{&HVNSr%v-lx=jpiO!hj5o$k_@XwYHr9l zhcmr=y+87K?<}28A_?qT7xn{>u|u{OD4o0_VaK}F9+3`Q&2N&LiZT{x<5=X2;j*Vm zaBEegAe6GYrx=@=&~;fb#zIFVMGv4WBEJ<+q#Jj39RaBk%mB+&*wLbS8)Fc(E$wm1 z6>l?FmzM(#g^$m5aTIfCC%s4tzs=CAEvV4~9thQ~D}SCMp`v89srblE;oz!C#L|NV zkSMf1ohQzBVWUgMPMc)lWm1H7gv{u!LgR?nvLXU%<{nYXtrlD@FWZSS9E!B8F%#87 zoG$w&YBY1siav--$_V3#a1Uxe(pUtNU^>%eLDe=}Y{2_!t@l;-3R{%^+L-}do z$oq6&b0@YS;x*VO6Vjq$7l=T^LasPSc`wr>zv+NhIfTD==s>RHAL7!wR?rL0DI?5Nd4V;mL4;;x zoMp~SOk}fIXnaQ_Mar8spGB=b;;6p~$<#S|89ZugbrOvw9X6CLYFT z^WEKJl)nSTZoXz52RqTDe{=xTD@YQd5|*)y5Vq0IB1q zr&eDyGEe+V6ZoP>ZKY$Rg#D$Zhp*~|9$2zFiJLJ)wxlJ$bQOLLZjqhQ6?`vS-szd!W zV(Z`{4&HqXJNrJAp&;SjnL}UUe7%`^5q5e9AbFp9u`#JsF_YD*(PilQui2Rs7<)!P zeH0E^wu@a&Qo#%)fhe%VM!0V(hYLwCNezk~1Q9PR5z1H>4JI1>RUjY+IGp%tz(JE4 z5DS+>KBn2vJwKLaA7r~NrUrHkVJ)2ruo@7K(E2W10G<~dpt>;x$88fF5HJ>w0}-8h z08>n*u~wzFJ3ix$X@$Sm^(-D5r-pL%akNOZ6XYt@B!jLI3VIBkAQX_XjX(i>T-3yb zj!#|tZz!6DWu$}tBvXJq^w!+CdcUW1p`!-Ld+3S46CX4oJ@b4Z4T}(ch*DUL54BLp z66GQ5p5f(@fnmj=!9#skJmT;E9eOpQeWB_>kU`!?mGYPcNv76dkx1zR^FARoQE)U% zlZ9nTz-Kl}e<<^dRo@;QR{a1;HrdZ*&VQRR8Ed(RPJsDvZ*a}`%K=mi?# zayvZ8TJWGFLHgl-oTYdv)_wkAAsL>l!|jSMjNJn}G0qG(te?V5l(|9MJV!ju6xRA!@uQ?! z@{HYRk%BVo|Ms|d7k3RvE~EEXM8w+cZ9^iS^go$$y>KgjKlZvv-?) zY(F@W_*K!)NMZ(n*W$SzC?cYqHxP>JwKhuucBGSdyb#=sO6Jm^GzH6WL{1Rp!zpf6 zb=*$hP2mOSZu+=>O>?iKR+VIxEKb&dyCzxG&1oGh=R4^{2eBYh3(B!!2!x^V40ZDq z116g5W%D07!jf;Vnkyz|V|3)TST!?_TcgV+E} z+j~>SB##GI(`;Zd0=Tapaul&nIyD2v6fY zCbiOlRt38Ku{485!*0V_G~O)Do@0S-!}U6Smf=fA`uJU`qKj<0w&q!U9c67b0Jem1 zfaK>XfQGgCdtzjfqCEVu@`MBqUM>Q~41B?7H!O$^nZ2{e>o`2XVA<;s^|bDTJtGN9drjJMi7 zR~irv?$Y!4oT0Sv5@?x!WzB4vly(M`7_1>$TxKF8Oi-BP_}JT-)sl7lMK&v$R&xGt z{Jq2ZJXFbcU{iw%Ju?TZ=}URG&6UHVUS-?1ZQHhO+qPY`3ahlrwryKo-`;(r&+Xn3=lsja$Uhm8 zkz>sH&iM>(#C$Xj!S7nx8%2dhxi~W&^dJ0fKbn@f&`&tFh9R8B%LQg)21A(FUF1>w ztN00X=v2uR2Ho;E0A@n#`F}DQ|Mm_4HW|$Sjq@fk!AC%(e@xr0A)`I+JQ|D!_ z-q)M`+kVMf?`F+ctuU!2p^gdQS!+P0#pm(;=h^agRoNn-*e2c=509@$yI!5$_Lc93 znqkxG)%(xP!Rx-ISDPitnq6>}meOWVoo$>9Xkq7jJ(`yTLJs{)2ePC&f_q)a&^fO9 z#}Ps1F^T zbrd(N1zy<_a2yzMyrD3vyR!veQPY%`6%z(V#sI)7@Hn?+EH5Zrtbes(Fc&-jNJB5( zOQ`wQ)07H#!pbF}&SeG|Xjo#BbgO1YxN;QrHLU}ZoaQwMQb8nOq)5TJQ0oa%=I^Z# z3*=czTIw$OffO8L#kF=}3U+~C`gaAp{scQ%!EidzkTvE~woDqf)(qSGuP}noyqTjEG10&XU#0bY>S#=TH8RaI!{zea>-lXyW#g0`4)XOZor%fL> z?smN;7^+-%_(14@0Mnf(KaWl9lKwg;L#vlei;q2SonuNedL&%aNXJ ztN)PhwM3R8D;Wp)!=xOQF?$XWZdT+}fr8`(0n6@qE+A) zdb}`xx&&fG@wO`51lTpa77-Un{+(L?^(C6X)xGTOm+xh*kXU*OJKB=?LCRZ79+r{g zI;eEd_Q}oxW6NsbYwph4zkR(W-M8iA>&?y6^9@(BaX(TQ_YS{r1rJT^?T-hBl0WP=`OVdr@W5W;#FO7QF4jC<4u#REbrdg;A08xi}PTYK;Dh>?Ftl#9rHlSq^Nn z8J|73PtH+UHh5S%GUs%(Z`f_7KF#zQ+N34(N;iOUiHj#%W7!hi&>qr>u@;S{thw~m zseL=1r&i-J(e|mdD0KcC|IARFtg^PC^)FM}WV)#@$r}u0!elE0Td0MitvoJW0-8-; zF0*=N6{tn`U{5kxPD1N}imz1WuTSdUCwJJlwLXt!GkzvnGr5WyCxw5UM3V+!WQK>#`p*uvF{4=nb{ zJorT1{N*gn1igNNI{yuKvmVQdDZuqFu8QiPU^a+&piylo$uKINp?0`pCXdf%LaKYE z4tx&ye7h@)KK&wTiH(RIx}6z1pM0Q*82aFx?Uy+feLQu&2@(pJF9JFd5v6d+{->7$ zhB~U}>QlfXlq*{$K*urahz>HeY9%5;7*^3Et9ar}m#lA?Xy~xw%3zYQCB}>1xaX3L zD<)wOX`Td~I7 zgbsm>>7k0?)Yv@KalMGkxfs0FdY&Gvr3Y1%WOe7_Wn3ja7}rv|Kx->E+!fuKv;2w9d!it#dV?m$v(t!$ zBDGIj^A(<0#f@#8LCCBv1cVoPXqrfraD0tO#GSFNyx&WhHR*b-8|oK#t+t+}GQ#gG7J77~Gf|TcU z)Ya+U!KGcJry=qAr~2LIb@zw>^ zbr#_2LU>EIH`n2^Uf}4r|a9Dw8qr?}sCNY)n0W=js7lb;GN zx(v9lFS`J(<=gWN+)XVAvlT(6C-pXy4ZjE?=`MiomI!|k(VbL>pX zfX}+pg|kY`aIpRD)_y5*(dPiS}m;6fPAsltGnyMAKOZ0Q(_1|4?oO13|m?_L~Nd z2!1nS(kYepS>D=yg+1CH#xaPeAILNjh*IjatE7Z*>gO~9g+XKDYW%0!aO`Ganp~Wr zT$AW>+Kni)$2>t)LaheGQ|UL6Ri~wmK%#ySO_!5MVv-8D<6tzV*DMW zQl(xKiDQoI72%jGlsy6LD;*B6=uwKBtQaY+t=fn4#4$$_qHxvJ%>_+PE(=O zS_C7T%_$izOtBTlHD!$rgaH%{ZUO|X|3rXXs6;>?+mM2ZLC})RQ6fjBASCyAQ7Lm6 zVC~A*kPuiTU;=vt8V@f!v**Q+U zo3?R3Bajrs=h~;gS68)2f|E%KufN`rM-o()nnA>paC|tx6n$xpYs#KmN^#f*Pxkj4aCj;5zlDSc% zQ=(COip2_=7VzViKSC@oXWau22fs%#(*&+@5YUR;fZQg{OmZVJr!7RKDQ47e1V9o^ zii0G6=^wbW5IlCvh1ibt8OH4ME<)s5qUCc8@iJ09O0*Jd*igAH9!P*O|w;lp&53OgH z;1DyAQb~lP_VmG9Gke)%k!k|*W|i(j^_+}U)$sWt}l(sML>O-ceyhgB*btM z1=Z*yZxGu1FbVT)^#kxifQM{y(?eA08UzTj)km~x71obe>4zqqFL?smqMNNx&MBt@AeImjdlw0z8XiG=y1o z%s`|v>2Vh;>8Yzrbu*ue6MkLHX%<#P$m(i62eNkq>KhOMLk(gcw)}~h9EcC+;{s1L zUvQz>xZKni*u{k8l=yZ?;^|Wr_wK+vb#Ad%T z0P#FG+?JiAy4aI(*L7G6>Eo#!wd)j%ndCJUhjGv6oLM*k>|y0JKx{dEwWfoLjW zKeT(bH3;PPc<+cpL)aP2f`Pua$8FWlhpsNPh@B+FHO&zlO3^f9Zuwwf8Z`Mk!IuZs2C#jFx?{7bPhxOnP}Q%}B-i&Pq@f zS`}8Z`!ebq-nWS;Y7pl^=mQk^IMIDgwwFz6aH!ZFH#t_WmDoJhR_%=or zQgp4eYvD-2^O3xUdE%dIC?@)U`KtfF+4{N3_&*q8*!&q{ zz!v)_&>G{19DUg91%?)&Iv0!q!|_k5LVd(?Y|OGNt5xF1yRzG3O{NgE2jsBkWO-TT zTUD3mN~)WmjqU}0-d0b?`(?$?WWvhFY(uO~#tGNpt80CC6Y!T$_x8tw%d>-1pbDKW zZ_n59ul}uWuaEoFx3{6H!M2guOINlLe!wrD-p!2-+iC!^)To*ZTRk^A(N%|~&ED77 z%naP$rh~HDQNANF@7;meUT)9vzb&5`u4@0VG}Wg*Z@fSQK6}u${DzjQGtPJ42od85 z+TK$X=eS9Ca6DY|lV4^=0KwaC3u` zygeA61vB?*7Sg45LE5+9;qD$uK4Q4vJ#vV3P3YvUDsq9i4a6&^l0Wk^(Ic)Gi&>xx4jV0xmD<%-9r2Z*ZlBsMfJe=pd`}fJ zcqu?1Zv$CF2qH5#hpy!}!@6X%4Y5(&%f)!-i=d(%~b zJg@jKume(4&A-GvGiHF zs+7~q{PlbWWypf9{oVP&!OfB7ga`fvJk7b;5BAInye}wP0^^4Xk=+((I?oKQq3rm9c`%fK z5-8M?y)nD698QwI*$-?&Iz*E(Sw&UF_S8>mWyLYxg~*03mXtD3@}BznO1CRKi&lJ7 zyo1nVXSey4#$qjPID}F&6_;JGt0a)qWy3vZv}QH^XCQO6l~y^mB!;0bYsS>PISnj~ z+RpD&LZ*@PrWLYf6%ALONZYcD{5zev`@!v*MwS8G!WW@e{pE`3rp2Tp-EuDP@VGj6>U@6pRs&$6_qbC4^q&JPbcKxp>4ORNa>T&1^ zigU}zPAH-)VNgozZ7>e0Bo6nqG3dGzB`jY;pN$B=|W!;sG?9#w!^G zIIH3_OOHOC573?n)`}MtOL^wkG9BlRA?fLHzJM=5LB#c7ut*Wi%R{PR`<;F$0ONx` zwZB15LwOx3>xFznMIuSkR6PTorIicMhBPY56sh(^Ms}SnX5ARYh0o8%c_<|-t8!yW5&mS@5QUKhH4Zq&&K(;+X%@&a8e6g5&J+te z=&7$CXJW7SkTRthqdc2Zfp_69*`7ar*|nAF6RB$_W=R zNYV;RA!Es*F6O8jp{>ia{Nv{h^s`Z1N~SYai~uS4U=v5O&&;f`RB=}TR6Ro(Q(+@4 zG~T4n_?7%=l>Jh=>I$?BE3dKo=GjH(`Mmc^IB6M`iw$g_AFyc$=h6s)xX9g{fOOn_ z_=nvk?vF5=4A7lBu0vu)g_wP;fr<^AStYTOseBHH8m{X%VA%nXXG%ilsStDpy8=`# zO%!SdCA%8SA&beaW2L;aOI$dmtEy>kneW0hWrhX=BZVo+YL9Km-cMQk5_6%U(J-7-d2 z;~ec-a4QE4v_M$IwbIIdN6#?HF{WPa;vpGfU`cYNGMwWzLS=`*(B%7BCD)T1DLoSY z6jsGp^bs8zlU!IdFjbrz<8hh&$E7Rikn%)%7T^gJbHRAd94(iMxfmai@cC2!kQ@p&PZO}1hs!5My6dn91Z(9H>C@+e$!G9|}pcwLkn ze?%~rczXZhcTfoC&HTz#>p*=FfF*0a`JzT$>9H>RUU4&}joz&ENqByA{`?UWQI;~I@Rf)_{T5>OCF2GWH`!qVMJxj9jU^5hxg5%ez?YpS3~j}%ojLW@ zgn0|CuA>#F4J=v7Uc1a$PYl8RinY*>yB|ximpY0pBu)o`Hd=N~U*6ui?3Y$sE)pK- zt$g>iz--Cqg225H0GC>kEW&x0ZT$B`p8qvByy}K3qR%j3Gew!1-(MHzq2MG)VcjBd zMM4MM=DT;0OC+XUa~V`KU))d(X^1e)YzhZX#8UI~hh*vQ?dj(Bd9UQ+?X>oxnGm@U z?4lxMQKPBZ^7qTPd-LPQ8azwZGEkF(?^d|kgv)Vp8Y}rCu;M%JcF-J#zwZkj_u%ASd=0g!zpz}? zTl$CFILBh-VQ}78YVg;tCSPF=i{OM7pH7c#sq>q=pWmlF@H{(r@29sPcG*CEh5gc^ zc5m?ryQQe?BuJVq3khzh*Spt zB;C|`cg>;SpYu|w-799iy`^v_zpZ_g$)Ke};l<5pO)thdwE8Im=Ang0k7aY+!f5Gt zxQK|P&;h=auHWtwvl|DgDAKds9F*e;kPebkLkqLNqOud@qcuFc$RgHZU7iz$Mv}uH z1ljLe%{f4@toAoo?e{!gTa~2V5Jf8v^bJtvBI9VJ?gM;m>x>3&^x>G8D+Zm65?gr^ zj{aDF5(q*yh06*OAvt0Onme{dk*8!E!~;FCnmXC_|I>o4#XD{9^%RU?(vC1Y?j}K(ajS||<3fYk2sQl1% zjrQnu!fHI)RAHkbBfjRzGCS-u%MVA^pW`q3fLF1&00_d^hswq$W}bAqKjxdZ@=3cJ zsj%uS^SG(D*1d%}n6!*qvo#1n1?j~^Rpo`SD?{^f7I(=zyY*5X#`R`a_34(|gi(ovahZDLf@ns18*|Jh*eDF=27@Hy@1Feb-iDLv zKdU)BFf74kljn*&)sVy3##6ML)8|H2G@U4Ge}DhZL!V4JnjNas=kc^oO9-O#OW9Nd z0w|F~maV@E!Y$o7zMcYtqt202jUl{p@EmgYb(}%CS5Nl9xtAxRRu(5e3N%EA69{Lb zas?bsTfGVbIjPZbc+QP;n~`5a*ndPUfix-P%IJ&gmM%43XASb=3WkjKFR^9x$t0=y z2C|3P1c(!L^kLFnLE!b20(;@W>;U3|te5UXS`i-Nq;HCbdOL@6p)6D&1>{J+z5cnW z&fyH*LM+ZRkv^lnsP3Eu$uM_#&>t!>odEmR@TbvvP^#39_?F~~bL6qDDJD?uLE0%a zS@SL;rF=$B@m!4@Q>Z^;zwQJFWDnb^-Yq)9N%(Vbxod#H{aPW)jSpE>Loj5xRLIzK#L&?csq>x- zUNZht;z)3SSZ#x3xN6R282#Xy5F>i5YJjphksTn4nUp2eO@^iY3x%UHt6Z)*6cD;l zQLzI+Hl+km8EQU0Z>z@%nU8Icf%)O{Z2xmm;f!4t6A4zrHliKzAshltl-o5)4G&B= zoMGThf)AzwBNz!Q3=w{io%X0hiDAo&p^XNp&De8Gw?LR$3D*%bAfRaZM@}j`yjbky z4dAXyd+6Bw%CK>~6VAjNa+B&3+g}nmvUQ<@e;$oIYX53{6BX`}<654rPaNJ%o8Sg1 zWV8#G7NZ}qsx%H?%h}qZ+pJWNqS%Sd9!1&^W_SX2r#*83tP~cKpG0@BV6EU%G zI#nyt#qSG-@)`qH5}ez5L&I$x-zfYrk@~!x*YP)jwyHNqKv3K@fZ@?VWX`#qO^l@=TjUT9{sp3zNu#6;K@1kY%&NdPYk|W>Q_3;}PU7|BpQxh5~1*&Fpz54>O zQZ#x-kF||Pv;5*o;P`^3;3@ihMZQis?-!3#J>ty6_wFY&-WHv;c08k1} z$4F@R?rB3o7q^r33iS^Vu>W|(>Kp@q#AgHd`8HkeH~^q#uVA%UJPkMVFeGIaLOB`j zkghN>kjGJ)b@Q{9>-4-RN|;(R`pZL}XSYfw2iyaR`-s+UagsieA+^i!@F#5k)eV+o zw72F9=+aqg`afBtf4iOkZH@lV{tFZ1e^@8|x%rP@`tPjKkcL*&{tvxkx{m&#h8_=B zhXw+0Bti-ut#dtqH<>qhs-$beBx`E!Y5Q5c(PW4tmC3Xqm?V)VFRS>Pi_?@8#jmdR zeE+=ceek+&=*g$)tw|sB7iJw4th^?0NW<>8d)wRL`<_S3h{E!_ADo;X-u2S?`SWC4 zSD+2+y3RAbZ!CP(<>X)cWIJ^gO_hxewB;H~e^j<-t5zc~Ri=<{SmbKMR7`8YFR9k; z4R75`J5_vf5q`?Oe6dXDI*BG4OACcJv_my%pg~t?&Z-bOt65c1n#y@1Y{HBQNt9w? zLNTPGO(+_%u$i+^2UQajDMi=}@V$IolU_hgRw+{l8TwsIx_!gabXB&c z9l>J7`a;aMqp4OO3Z=Q+ko7AT*AKjAV0Tzg;i?pW>#Z06!fqGzV^_D_L55Ef)`u>U z1MtKeTeLmvOETnW2jBYVBrpwR2gFd_AA^hFA3;1W~2Ubj5AVx zz)$>O-bfKquK31%VU4W6-wA+uB{Ba#dt5W^B#mkx`G9#-c!_PX(aFh$9cu*#zP;Q$ zp4$2Yrwx-AN5BY#I`$HeDU#@%Im|8HL9ZXNuHuaS z(F{I4&}_9FrjPJ-(H&rm0JKm6y6+qv{0G4Swq&X-4b4l2ce4m^uI@{@-=9m@9U816 z@8?%Yj8MifSD*0Go&!4XuZNNwCM7#S%FAK$9LW9%0(1c!&v* z;5mJJ5C}f~V~>Yk&NOh#j(2IGF@0#dah11m74so8ouuqfDI0F<4^Uo*sgDyo(ZYML z^7qy_iqMlxJU@bV7|1#_D&i(^qSq-xqcdKOJVQU<b-zF zM(W=LJDBcnZ#&<-ePrvMc)4$A62*V=&j=$fI+-Mj(`9cMj3|dR+NCFmx=K)uSgji( zU>c#I6=08F;3U>|+405x$WZJvafUYLg%OC5xi94M?l(=a1A41pn3kib`^)$6@a!C@ zv5fuYweB7WCeMOcO0T%QImf7X7{cm4vW!I&Yd78S8p~FU6k>af#tr#UzfFMU?GZ&q#nIsv2 z<(QT_Pljktq_*%YXB#%9m)XWN3Mt|)T5ouXTYeoziC`M2zihepPA4MT0(xNMaN4&tsf~<>RBZTs!vX&^hQde?H^({x&aP>=ea9>EBxI z(zYDMoZ_Cg7jHt#^xNuQ=lkvL?f11V<@9poe(&^8rWh|XR6eek`P=N5?@vWFew|g8 z1?kn3_2u&QF<&;))#bL0^X+cAV(Z4<5qA6dv3q3iH}90Z-PF+GXTCM516~~K?iA}Lez_|MtIxpQ2+?7!X$38wF$8oo8Xg?+tK!S}#}k!#x`-E{Jg-wx*sKifixLt#SL7t982{*ZC#rcC)74Xax5w z-L35<<#nq&M8j=Hr$NEb6@I5E>D029^UAiLcL;p`@4f1})R}>PJ(0}4Ij2=Z-^Prh zpM_?qu{!b0*p-`K&-WYEJ;Yyb!xPPvOtYJ#{#^%*nIL%SbcVRMAX~utPFOWLLB*u_ zZaZ+t7y#D|%4rd1Yaf%D^x@RC;kESH)Zw$PtQ|4ilN$q zwH`~{&d#h#1zPDJCbQ+4hUnoX5j$xTW{rFK&C&$-LR;;+^AKMQF8UE0J_^gd2tVj~ zXRWDh@~7P<5P#+6x$0(3qYwa(CG!YQOzF>R*SI!+PUS$kA{1~nh~kjx$*o!4E($yL zDxN|ec;7SVXsw{N9)8GRL=_C~KREl2I{JtbWevA7vANW~GwN(&~ zxFM=`i%;nR#sE$;uje9Z=jGyHg7&Y2nb>9-o{QIX!PXGreS{OKUq^(U93TEVk%R=V z>zqt!G!{HO>IC}Y;LyRyR<2Bqu;fnQYYL;4|+{IL-IykCkSu-*xr36Ict zho&dc8VFc_fuE@YSW}sJQh$e-@^F z;SyY3xZ_bsH8_ZOElMzUZw`02DegW;j(>|~kSJglpLlKn>x47R1+ow0<4wck=kW$B zi05LBD*)jik-LPAkAQ9H6Az0TMN442Q*}NY-*U^$6G?7Ep{MgUIW}Ba zECKt%@ji9fru73{==?nv+im#SOjmA9X#h!pu?X7%M7m%nrz@TKtEd1OEplamsGivE z5{pynt80?U$Sl7!0kJFpD#n zh-w}&A3K{knR}!s9A0Zji;yu`RMWO zK`yA=JvMiS8VpSF&iU1WFW#=U(S0f8P2vQlWTj%bpM1uBkC^~)bY9L{wbMc0qL zOCo8rGg1dKw@FKfx>rh1AboRPZydQ83)=R`%D1ZlG%Zr^f~1|$K4GZ19vkP{ngNVc zO`I!2$d(2gF^H!MK)4P#M;DZ3+mn|!L1L~m=pd&@;{Lsq^#O9`9W{}eB(V(7ChCl+ z>L-D@YNG_?7TQt>nMNY67Eb{mVR<-rWs`^wC!(oG?5n;P`z}>X$%lw*xZ<~rcF4J4 zLvq6jigkt8{$*tQSmc>gX4H*~zKdkEu&>tWlqL8;XO?Y&#s7WmHQaOo=uo`X9XHs{lfN{OzaeLhAwWn>mXOrF% zvNZ>SuLKGRFl=q1ed-`0h4#8(kQ_}_);@pL*DPp_D+4;kRPiXc-{!5YM+wo1M5#R< zzLkJ+_2KJ^WGOFbND{I796$yOdRHG5QYvb2RV-;ZkJe`08D;YY`9n-W;dB($MH^Jk!Hx!FM+RuAZPjAf<$W&iw)-yKR z+h{Y%oav6Ah@RV7)0|eJnBPT{q?)WLLvrF%Z_Op#1G>v`Q^zsBinNdGg(|N|gf=T% zm5)`Azkgb~UHXs6(KB`LCc37dmfJc-_!(kapZqkBLf2=YV&BcVY+iLmdfI{`pvyg6 zaEYWfkxa{Hsu9rEDIfY;Exs?jPuHYUB{#DVYL4BKT8v7qYqa;O(W23i^7_|15JlI# zqHBU$2U3u}xvLk4g6ly`V{Mi;OkC00sEYM`U!AgAo!WIRsK5a{il@ce`_rlA2LPVF zqwkJ(zIL`OR$T?(E^CsGX8J9ckR*cK2&IMfr;k>9?lOEp#AR>w%OxbX4ZZr*>21Bl2@L&x60G4ve96tfRz# z!JRPg&25ou^n&A^dFstNoKab3*cjl)*V9Y16V*YhCf@>?JO3D2`#(so67cy9b!+c* zaWt-!AgW!F-0CK%Zu@=~6Zfxa@+A1azDZD~^*#<-HuVzN9DY%g(H~OJ4oBiHkuJ4S zbn1d@<=717{7unf(+*edJ(9$%FQ&DS#Tgi5_3(0R=!av=+cQ$G2!F+nl=uvoE!!&* z);n#|iHs6VtJ8KNSy=%!)LBnctEClL?PQx-FTe3SRjHdpo+SCklHE`TlaEvQ(>fTW~!-pMQ=JDlF~QPm}P8hQ85>S{}@+ z?fQ8cxc+?!$|f?O@d!YQ78Yse(Jzybd`#O;8uaamzJVf?W%Y+uT=p7xrpEYn3I>j(#Ns8-yxm7wCh4^{{b~J(PX_iI{68Aee|gJ)wN+R+ zSpRwApKB;4Cgy)U=l^9ynVA1!jr@0Ql}im7yB~md&-o8PyT<<{%L!01y^SorWcvhw3cT7E+92 z{6?OumZOWgKEk?@nhXI+3>wKzrX}i&hOx)T$*<|I5j&8NdcF%joTog$qVvn2pB30p zY<2T+qa;1!z(=>dD4H0biQ2dl2^`Jx+c?Y*VnJ9;H_$CGW$K%b;K zlzeEDjL~l<&!&m{sORn`QUxB<0-oe4&)Q=<2!ao^Z!Kd^lJG@l2K=Ot#^AW)U^27A zuWkG|aXSVf^nxj)vjKZ`6|xA#P^pyLCQIwUzG zfnToqT)T<{dGH2AlQ`&t$Hy(dD|- zzQ(U*WY3dori|egoK6Tv5kLYZQs?y2nJdemQ4bnI^|{WMeCAjq3j@jGLCJ1sCP5W5 zQw%p#jF-)4O`+WKJEnP`|Hb2>q<@=WTvUJZL3rp}8bhh#Gl^{H0#;&IEt-Bd{ne8@viWs1!$bc9N&3k1%oV#3Wu+-UI4ksCSNI^ zNgDGZ<>HlMCr_J{^C&*>t^3XVpVhO|Ij3e_AVYYlL?{xYkSLec#j6r; z4^4${DYZoPNlE^zPC_7HlK}4~B0xh%>Kls1ScyT9GuFLOGl_pcmCOx$rlFXvviJ?4 zaIy-BvUv*P^b9$P4ASjn@@Q+pu7XA3oCZ4slDVlLi2wT2h6V=XClD2K9SAZw0J(G%{3g&? zh>%6CbDxw&oe2B3Vq);gYZME6zz<`fTsxY(Zkq6c$W+T3vLJP&zj2^R4nuHh5h~4- zDy0hX!3e!|xoq1=p~~GYjtGwdKb^8*sZJiu+=1j{ni$E7T-609fCz2iON#Za-GK5XLj0Gsa_1Mt{27AGgzoj7=@c!dz*GrU!HKM z8SmWr%|-qF2p5Gl#(!d5x~_IwPr)1a2Tlt8^O2J}a}uXwaDaL=%2~Y_Cg!`;Rj$p< z(v2rONxrS-yDsmSAvI4=&)gpz)WME3yP$e}@dK$c8v6riCrACj(ipwB(o4}l<2%$1 z9$7t!D=JJ32;mh{J4G2uVX}7n+I$l5ZN~EMuF+~FUw=cl5@W`|hJO+8D2hAZkfBp} z7RU0V0J}~vlV{Qq1nQ_cn35mpPJRy&DwEgTp85fp&RbW@VB{@NqoX7*Bf-erqvIQ%J2DKYcqc* zd?kR4;%zD(cJkd|9||7&9~_tQB(AU=oBe#v_?ie7V2AvM;x;)cj211>g!NU<`Fp(@|5t-<|*I$6q)s&QxX{uMk-0r{~KSuUu6iIDh+7b$!NfHggJ%+Va`*7Xd;}K0V!6cIfonALCx};{D#_ z^CRCW+n>LkpT9lFf9UzVd)Y|fj}7MEH&5<(P%bXJI~PUOY6$R*!d`d)H0(;nUa>nZ z+N?cI@Ve?Xv_fAgah}&@ds6rfNND?d`7}vX3Oepg>e$Z+J9k$`Jum60+sAZ-X_ zONcdIxh^l+lg=~NKprz8HF?t}z^$@23b9a(n2zK)m@XjoLdy)yV*^%Nes{*l0Bpe0 zQW}!-S$TsERZs~@w9fANw35atQ&%4$@oQPgB& zSk-28!84RcJK*GZ8G^~Fo;*WKs&(r%iy-c*qHj$D3__;sg_Ar&(vQ2(oPr%l}AIQ!5cOav-n3&cDymhHO{gJt8c;BgV0rY6qj;;u~9w6++SYC~H za+-K36jq{QAW;e%e{}X7w0|PX<9wMNyc@6t5~Q)X7eYR@iU5Twb&NYbc1H*v^dfFF z3$9H*nFHKYmkQpisf`T=VwU@KyBE^mRiegQ?r?EY<|E-t+>#}zO1P$qdIw&4g}}1L zCP)4be<EYn3F#Tjs(q;E0fESJHV1Gr3j=MF$p_^ zAtMl|b&zV_H2M8t{`|mGHC!|KoPL0?tW@q;mD%;{xEi)GO`Cc*SJ&On;>2ei4!sHr zGtC9V@x#aCgUgQWrH?={^=V5+mjWDj@wx{-QL%CI5}T>yJ(JXm9ejC4!?Ed#A7%PlZ;d zr_N6b2Zh4KN2)K2HkG>7VGQ|u+Q^?MbFy0DA$pue0c@v+IY#Fv2l z9VeZ=>`~k-J}Jx}hN-lKEP&V##iovlS5mahDiPf$M$%aZbK1s#%RlKluds?Fb?v5Z(EXw@IWvU?6rfcd}i{1+2l&>B;_W4 z_co)R0?DsFT=CZspy@b;iZI$j8GPne|zm7j8(i=fLJPJ zUVOrjOyUsR8U;s4E(WMX0XpX5pY17Y0wfiV8$N&f6FH0$_C z1PbJ5%>FDeI5ftG#fK4Yxmb6%HfbasY?r;sx5Zk?;!qz|2A;;L-w>FBA@jT62b zIW2DRb@%Xm-^E|*;j#WUp*G^!Hz@_YtMsof-}ruJ%fkOX#cQqUeOCE-@BMuGIce+m zcz1KjVC2%n_x6J`_?k5J>!D=-OQ)Wq^LwgRmI^pkXZtEWFfpY%07+JQY5AJ<^6@Ao z$@9hC&F$;v?blT1J6qnhEibn-l)oYX0VOQJ#UFpvnZ8`e1r@j!96Q_TyluYx)a$9| zW47mGx!-%o^CRUkN^9)qRW%bK^W@pRd9L#P6`{CR6Lp1Kceq@OFIA2BA~c6i%^RAL|6UHjzfPHu^p#{D~w9QJ}wzJZtuJIi2fta7jgP- z$By^*-D9pb$9M+ya(y@4s}?xH%u5CseeWh0Ee^JU^&i{1E(RGDYdeI2$pAA+5Fhi< zGPH^^CSYb~`Cq_Rg{V<%RGyy;>g}2%?0f?~m5P8!yAf=g&s&&=hXN$C<6Nk=~yi!=!bGq-1rfX)QY(1<@nGos<|Y&ee=txlp_IG z%H*ud{wgf77$wP$_H_-8H185XblikuBWJLT0>~$p%s2YM zK#YQJpqU9!AeO4+spgU>`C3jfGHcZEnlho4d)}bEHI-5RP)vaX6(J2dG~19E^FZva z35N!z25Kroe!FWKhd$G%FG;|+fVc^~%fN!ew(9X*YP8AGYT!~F(P{~f^dx)?87UkX zMNLg&psl2BUwaIlEvaFer7I2Q>vTuf=IOzU?eAMjwkKuJtO{o`nfc_>iM8Ph&*XS` zogYFH()K`cq>S+qUJ99|jBPy@Osx_Im)SFg-&u>{>}98=9Y&cySra`#YBK&q@ezyr ziAb%W%7$tEY{3)l{y!GXh>{PoJ+wfgMLyt}ORbpz&m*IMQ+MqI$~)ZPcIdsqsi~=_ z+6_gw2*g7CuDNIlQ?o%K8LB=Z6`BFM((Au9`zyR`6lgfdor1p9T73 z`ln;Zfbk1&ibntpxuO{rEBVCH>r5-3J@GUC(6HAX*9SRt9{;^q(t0A4YTT^NG}frl zkv*IdPc;a-a|cMsR0LgwfL~n6{F%!0ab|9hMm90%mSByOZ)ENe05G}w;sC3L&cZDX=mQIxAnE_p^jQPZ?ZLj9{2_>Xt4F)I8r8RYHd+7?!y zw|~_h(c#@p{j?gmo;0PIY+&ba5k%2%27JHqzUyB}@f9i(i;avIW9LvZ;;0y|rcBGm zm1nd@_{m2Ru?f{XdiqIRopd|+r4u6G2l~&zM}F<}Z64nqILC`04|4$X?J=9){P+y@ zujUQsN?UT!*aU#z!NP8ed-&Em5bd7z155T^p|LIuxAI zK(aCZm!`n3zvUh{x1ghPIYVBbykFb?X~r`!3K73rI8SR#nQN|`2$&^>*0lX z(=QcR&NgJF7_4`1-wG28XPI$HhxRbg$h#gm(oN$GF3Ej%wp~TXTwlR=|5jN~XZS<+&RAeK}o3C4bU|>|hDBHJok~)ry^}?D<~NiEt5 zxd*BgCpl^WqcMP_=+Se{a=RgR)X>G*|6KKI^jl|i7q0?#&r-F@^w;(8BUU92E*&By zL*DB(Zi!m%O{4d}gV~MyWQaKOlX}BEdc!2ufE!t9^Y{!@|9k59^9TQ7g#Qs!7PRbp z(J!n)O4^@>U!2aLxh!V;-t)QM{_&7u=^WAdr-x4otkFY|zLsS{L|6QI+Uv2_3z(E55o+RT6qaNwf?`=ooofo2W3_I;MtfICtD zlH?<$U2JDt`0aB&RMrXtrXy&C25q*-1{bE zaZeQY%_Pjc!gZX!4bsR)(}6a2J?naBr)$&CC(H*oG6IF#9CoM^*qsfUTPJSlF!>q% zKbz>l6?aEpF_yCb+8rW2|FyT3RtFp{3pj>K-ac z&U1#5yO+_(ga%0~1idLtO7WKqUV>nb<)iLAoDxQ#;^bdyI0;_fXgv|A2VR+>Nu9>l zI#tYrCW{1s)(^n&x0Z#Z9mLDms-ljLW}jlRC)m@th;C|8Fzi64DvUYSqF1E4?C#!H!-$XDSnl0QM$XS|)wW>o(7#-;7_+A1;mEny}c%cqw#FP{3ePQ_1B27RxwcMNmtX%hgQ{Oe)3?B-{VJIs=oLG zFYm_vm|Ztl5l!!hMkDQPS1<-#V6Ti_YII|c&W&(aNA=#j+v(QRk)^pGMm%@~h3aw2 zuH69P#rOQvDT-kK_=)&|lk(!iFAfR9EB^M@;GTNoaE-YMbCF$Q!R+(X57CJVvB}SM zo3>;IkkdaQYA&->lS>KEQ9cWRY=)5swzQ2$pqVR~v65{Gb+N0;K96LHPVa!Y4~$W% z=I%5I%}n^)Kv{fuN+^d3-z|SD|8N{{t@fOKYv zKXjwwk! zr(~lX2wvzK7YCx*Z`PQgMleekCg6Aqmvee$6L9i%8VxYhLC}RS+b`;2kj)e8XizFh z!iuC`8SG0BS^}v%(9jxOlb5}^l`j*gQK_rhMUNrzFAk^rXW6IoZitos(Ba|t;^mU} z&HHs-JUqtFgv?=m>Y?K7K-=;LsrOl*Dp4vpj3J`g40FYaMYaF3%B7fo*pSl#jl$8dkfp;OiHGsFGQ<_wB0?(Cz@sBW8xJ0$;x6$W=}?u!E|DO5{hx*3?j?l z47a{in0Y^qLzUcWZ9>?1B21GLF|Zku%I!8)XGK1=?GSm<0yW6EDn8VaT3?VQOEY97 z+%HjGTxmq(YHcjdG0mXvIKhd*GRdaKm5Woj7b+v~a!hU83!hbZ=5pa;7&F`82%h1d zlyG);Yj(NyNs_A+9~eMaEo|bfli>rLJsms=Jn)$8Qm+?79O(S?=h0qTFD*PI)*4@3 z6c>k9)i>hQKLG|l=%jUQ*`?Hl&P9@ubLKJAc!6wX?r!W!TpM zUV~$MXi3E+hx-mHxm5xOU%`vkd@uM90+DBaY~@|Nw8;IR8ks5t?K@1>BVoIbmFywx z8?5p@6Vm}&=UyE2m*fMM0Y~@@aG0{TmNv%RIk$~2cvk2P0?6!j7#RQVVkOmjRa;D9 z=8#PTop7m*LLqQx3Ey37-N_(xU;|#AY{s=LiqK+WCb}7!Y=5>9De<(rt$W(-t2VKa zb}zKCXk#_PoW&qZ-3YtzcB5X7wKL_)))8AiqWSH<#?4IG`I5h!YS2T=Hh>aEu)&$A zPCHTvPcHH*1&YbO4NcQ-h;T9t3MiTu!D{h&2P!H8sq)c^BrT--8!)kgjSm;qs_JeV zFwj?7{8@uZhaaL2R=G7ss=;@3EISU|pyHW>>=_T$hc^6o1dtVY8J1+}l7nO7ghas= z#UJesu4pj|hyc+=VDEKtb(h+C-XkQW)+4M#hCP$vJuL8)m`xC1+mg z6>ZVtL@hLU(Z)P4>&qjo9;ldA4pNYb?e%a^RFzy4OM_f{AMGCHSwfw^$&Qj{w

RyS8?f)M5c2-}5q8>c@G2|PqbqEABeybv&^Qh;0j_uI zmyw(ubATlnPD)dp^$uD=9D$R%QqgD1oH_3y&uA&kf<A+-{%P6mPnB zmvZDHHi2^h#PVdFWy{*qnlNPokbc$0%R*U$4!RveXHI}n3mdw(=>BxCI(cJR+S+vkvFmv1GFx$pN=ZW@kbE#6L=B@#OZmb=MV5BjCFV$a0Jch3$Eto4d+u*r(}@_I?F&$>AC z2J_L}S~~iT=l$>_hG2%yX$;CHEsbF~{*g`k4diFefH=Ss8td8;eHz3$**y%P2Vh;C zeZ_j8^l;|)At>FR*igmeTBI&X}4dhMy z#lza0J%+x{vEEKiMSZTID7TOi z8+w_j7aS0j%MCaP%H0z1k`_loW43;HX^*Twmun1u_z`nu3-77kD11X|(XsNvkAK!anxcSY;YKlN(Y5bf;Sl=7#v!1W*|B9rso|G27*aGd z(r*Ss*(ntdSR0PSaPlkH75G~a7*<(Au@3-^4wnWrIhbpsojEV77cQ$0a4qKfYK}%p zQ#r9{2`;HyJ=BUaX4`~5DMm1 zNe2^hyD)I!Jcw$Yv^{EkFNzp%;G{cuyX@ixT%@ty$l9UfTzF5W%9_zT?R@yc8@ERg zk3M6kcrwP&f3^2CGmO37BbG_&vF;DbkUPPS)(;0b7K42uS6I;TT^^y8spZ^Bjg|CR zDIb?Y^e__e-ehGsp#~T~jxfhz4|P~M=w?K*PRpPeYGNMt{SM_zYW5DDdWfAAON_cu z@w>v7^BA-V4vf4qJdYFJWPgAqC{BU^{PwJS=pEvq1t*2}a=MhG`i+0`KHwaiZJW_W z2m#&)>_U({7`mGB0ihe1cW#5>=~8+r>}c1`^H7}P144nAl!T+PS$WAybMxxWe6L z5p|^8+#x*cIf3Wz>egnNUdztPS~Z;OZNoBCO_fCLIl2g|b}gC<1r3PF+soD^*2o+V zUe#{W=Wk$x#NbL{&H@QzPOmh^>s@Kcdz{~VJ=9_}!ubO4zTU2BUpQHWv>Lq?5R}u2 zBOz753v#C&vIYK;lNRa){39>U04|9Br%P${Moh8_qESS}{FYVlmi)fe$Z-NMoFY(y zy|k$tuZ_|U3-+ha+oPU*aVb0jtDAW^39G;emM$-Vkzg7a1U7JdmJ7c!A@a}QYwiH( zOL{O22->nz7gv=7cO!; z#iX7&)Ugv#@^99gvpw&yAI-Abru(Wj7$Y4v>kikwOtA&BnbS(0(E{G=@+faQ-#UQj z2OI+K152W%*@Q>la<9F$KMd&2zLTzxY#auR3zge1%_qLLRlk~dGbl;{C~lC7z0xud zSm$twUpa+av1j&IE<3&ceEZR{HxZyh~l>Icdo@h}vdM zn9W#+H{+MICN1KVBCyb=SM^JCxs_>kxX?NyNVQ#@)%U?eZ~-Vi7=A{Z;K==z{>=dw zPAoXU5*jDQ5`CO-`o{qUMxh&=WcYqrjo+uBjg(5bNhjs?pd&b3IKiP>r#Fd=p!x<* z`XwnR0gP}W4c|sx;2O9Y={ybwsA)-gCOt7WzwV zp}&+CdK>I${cwO|G1!-;e=F0>{>q;8SeP<^eZOenO}q;B>AHhs8mkb3jVAbA3dK6Y ziJ0`AdDu$_yDMHem0*aS6ibY(fW0ehQ=|JFcG~v;nsPT|C-4Zmv~hqXC{BU^Oe^vO z(xdqHx{_L&&W(R^+Cj7$)6xy9z2F0`1!bOYaB1lV^_^RNpieiDZ-g#cd_Y(u#=|2? z=F!-v8(esUx{6e7m-fkbINf%{@!qu8MvhL6&HYh_v&{bPIWpJo*T#_3XaB#Z=U z?JJg`v$^t?VbnVJ+~&&l^LD*AHynH|pJ2FnH8hc`MX=KQzOs|5YT%h4uFy`W z(}y^V@=)-8SB3F**cp+*X8#ELuCMbK6#f4CZ_pVckGazL?fv?HC;v+enBI%+2E<}w+)%6OT9_qQg2eY)SDE<1J+P0c9UO){VZDXYZIJ? zbO5?_$~EZjZwYbISSd+`VKffH{(?BXd{b?2p5&Kyt> zl(u$nc-yLc<)nuo!(W5fyGB-dFjSiItM(#={eD$yXBnXPFiwOi9@cVThB9%iH%)6< zeID!7>ql6Z=4NMd*I|3*w-SUVZ0iXJ1PIL zh+J_LIO{zJ;s&TDPS@H1el7PL;BVcg5RoNR`T^k4fzyDeUrTZ(-2MrrM2w`VK8NJW zTb5OXlY0RUF(#T{jZ<+ZMX^pxq8J8Q7TWY&L%QExCCHo6N05>ZY@;sp>iGaf3r=l< zUJbPsPo*o|T4Mdi5GOk

bj?(Z@;pS}fE40z-T|MU;Mt!@Gf%k=mBvp_B5U=UTY0 zgFP47iMmRIzwy1uPv&uwl;CG^ynB0u6X~`$vZmH$p3xSf>537pShV zFU|NyE2-5qlt3FU&E||BN0=MH-c^i)p4tNYUd13xN!qx7!-`Dp&) zU3r6k_E(qLq}$ykPFT_?opTD5Pf0Jmd1q8H@}l)5!}9rzGbxG>wCskOBO*yyKGO>d zg(6^8v^sO>10!^|UGY^`^zpe}INsr|Es-F1lqE7&?Wospgrh#d5*m%h5~+xCXyh!C z1JGFMBG=N&skL(9CrMA1KJcI*5zW>7?{_!ckU($BiYrC)pZ&{j4liG6agv<~CO@yQx+WY*JE2k!?WZF^_s!lp|hbw1QgaNO& z+KJb@N+Zb;`ZZ~ntwIkB8C~&%jES(9*DR4L@sFGbkt4O6T{)XT#e$YX@Ro@F@CDaf zeC4!*5(HYt!BB)zIIeB8q6D8Jc)zpqIq8NsaS~493b=Xhz_sNL3Pg8g&t(d$b-l+*&P#XFb8p-_ib^BAy{7a1^V+YxkyWW^Ln~c$ZW%JMiHU>MyI(gR z6ffPBd}?Ike2!d@>;gs37pJS})|EsFaIwd9cr}x-6vJ%b)nGGwxxQ+Y)8qQ8QI;!i zO-wpmc{SI{8oCx5iP0~oZG6LIjp7OuFBE~O%EA6b(SXdYg}ZUGf(AwtkFG)o`R@A& zu{?IT5XLzfC0R(Xy~K>61T1E#=6BJ7%h9(f_5y?fEkW+eG=lZ0Ew|Lh9lCl@7)I~6 z-HA7TJwlsZqF1ie7V9E(>iDL}wv@fiH~2>)-#;E7IP?t{YS?glo4NwQt~*5EK~J95 z3|;J*Q8zl8%{zJL{hQiG>voSeBO?sGutoi?y}o3KIgWbZq|Dg_2Wz1uJ}bY=T;L#GQCI3bmdI8l3RsMWWooJtfk z*4u8<^lKcVL!gldCoR$J=?%v}0&+Rh+*)F0CvT4>Gz5($x@&;)wNOR-At>&bf>r;n zi`zVB;3j?X-tcy^f8(5iC`|;f$DfTM(}SVXRDzJEek1Z$gjY6z3=mp1f^il}4)QlJ z&AvYJmiy4xIo2CCK(>yPDK(0R${Jz4?W3=ZT>)Q|#2OG&6k-3*Gnn02}pngo93u1)-^6;kOJH;@_h zT+5on9?00BtRNGl>{aMcl}7~Syetj<Kci@l0W!seB8sqWNBhcK^8R4Nlw3&Ma63(!8 zftVi&zi|MRKy%}r`BTHN`!hG*Y`S$A9Rkgba~(7&QoNXS#Zi@7XbvZFgH)rHbK>#0 z8X~00*X8v=6MhbyJHVp@rvXn6`ZjFM9D;P+KB1I|A)4xkZhlg|GN!c8A7V_H$Y_ES z6iR4oxd}B~M2VP%Ha*wqcTeAV$NmUX(!6fe)gjQ_coV;h2GKiR`Nvyow~Fq{KTe&{ z-g9IqsB{2PUUKj>WS^fnci{lX(y%v<)rb9n1(P=}cfh_dT~4oect$S zggG9=A&h}NCn!|wyt7v^4}0micZq^G*p0B$9AL+rcmeD3i%aMne;B+e; zG}aQ7)O&y>7*6_Rh1-BOvL7df8Un3!SA1_%P5dwnR=%Y%-WUmncY7qMt1>ivj`wcn zAHCicf}G2s{VJ`o8IiX^8{@At*M8gRjWZUChe`M8q)ZI!s+l%h$6=j!?2oW6UC2gO znB48l@+N+j;-h!EHACL6zfp7m0#VU~2HwO!BR$Y%-$}P2w@U8{6yC%?CA!O5yj_2* z=$%_VZoG+qN_rO_c*E*e>AlC(GgVX+YN;?aV{VN?l@4&Lbbwo>1KcVd;8y7Xw@L@N zRXTtuK2??dF4G(5Jsc3rbhgy7tdJiJr1-5r2du&gH*MYJ?NbwOsYChg%qEB5yC~}J zS-5fDf$}Non>X*=>Qnv3>s$IpINIU^Ek$AI-7KH!nT06L<+J&-@oy~)U?>=`>){T@ z7ddm_0NrSO7~S-@4hKYz{ney6-HH#5Q8-%w(%RI^SMxaOcw(diOi6 z`qJl+!k=ujuS1eexb>GTk*&Wf|6D5d^j}o=(5)~RtZUHC0+rO-2K}mu4pHT{sX_l1 zz20T~%?UFm?bgnn02D(AMF<(g1 zgkMlKS$lmzb(NXG=wWoa$zl9!5-J>l)2%QV@T>63Zly~ef9vlYbM8*5RCeG`S1Ja7 z-P4+GoNJXU`CCh~n$&0RGt3b!X|G!=-FYLq@<-`el+bi5jpRT4hz4HkE4(dS`AXXx zUSGP=S7b0g$_=_XbL4GT;fIp&$lF{#c`Kcs8cuiKr9POQ-`mFZDl1wWK5&n&F~)tW zb$!jwr|7k|0=e^k^^L-ZH#6MR3Grd*X?-*rfon0p^ZxaXLqJEX>kJo>Lg`yD+}oz@ zYSztQC{5eq0c*`JhLc}89N=%=&?K^iVwZ5a12i^VHlWF7bmdC=S}c=H>{Pg%)M5#j z5{;6ka$+&CmlR((diy`$G{}NK@19d%uzYp8C*3nMROm zJI<@DD^bhFGu}3?Owjg@*LQBkleF1epS-7Ugrm+l*<%Tf6Jv?)W|n*{mT5Om?v>Ob z!*?n7&QJf=Mr!)E_8Q4fRPX%4f?^#ZD45D7@{>1k(l5DqFZ2*6X_3Il+F^U#c`vxi znr+{8H9?2BaOW-YTSd1!UiTzV=)c-~QuZ?Hh)l;9H&6F^(7h-xCCbv;ZWF=!-B82> z9E-v}FBDms)-|74rv0!lL*{mc4Ci{IX%kF1(Uur4|{1$ zcZp(GqyAl5$c?NWx`8|x4qaiVyuv zb!esA7k20U-6}S>eX7++FuYx$x$_qG{7$>>c8^|fH`wLDNxwR<@)cF3NG$#Q9#>PP zaC?D6Rbv!x=CNM3NQ{T^&goH}Y|kvOvclwU7QkEbwHr120qr|odCfcTw~FoxGv1`F z!jy0%Oz4YDH%jrsLs(~cwafgThU{xtPBp2at5=Uj(rL$Hp{pE@WgLssuzCxI)!30t zqOm*!z85kQ^wV8Nz4MmVD9lK!zOf-(+IFqnIlDpo#gSpA>~I==a{@>4f%Xc8p}w^b zET4~ctM-q-{Q9Rq|Bme}995b3vreK5@0atP{P(t#e<&D-4m{83W~!6_zC$+*AN@{# z`C6Ez4!}ocpJd(IbR?@!u>?D|{{7)@bC*pop9?OVE=ydp!LjZL$l6_6?8@^A)b%M% ztbdb|Ed8qgYa!uSM_7xTaL6hRNte2d14yIxo$K6r^iEgJM6Y+nQhG3aJvrRxEfjV3 z(h*+)>|7P148%Y3DgolH(E;z7SFunIJ~w*h5kFdNMCLJa47#)PY)WVulOpIKc>esl zooWzVd*JHM^}Td%8<}`hz4AHf_%?!pAee5vLhgrG+n3Xcbd=+w@3`fS^kC?G%C5p( zFtTs6>j}qq0#m7!?o$KqoGenZ-3~+FJ|8&4y<|IM=>A$}`dX;09MSJ`S;$*@Diu$? zGeCy#^X)t};NJG`*GPdraTng^d-w0{MFI7ln_Zy4ax~Ske{VbXhmwpC{wi}sX*QAG zQ}b(R_|Zh9?KQ5l!V-3``p$^~5!R#EyF|kY0b57mQU&J-sBjpGe)(|JrF(idw1JLP zM^@vd@%bd?NinOodEZN$w~g9U3jsTv*hjCI6N^@z6Hm2j?}=WuRr}s2ukL3>7T zExki*x^uFHatmp{e=9IYg$bOYAHC8yLN6_8ODF-8lJMx&ZdDgnfpJFOgFM2$kP(cm zu!Nf~^VW1_-RPZ<4`e`8S#|(xu@EYC;{Z@#IEK)DicA1qpjHmXfWpQ~Pp2eXrN=3Y zZoP~*;z#)-8i-pb;~mLc2jcyRo=>0=ePp07t>Z9lF8}bWmZ^o0fxqP$6Mogk73u1B zA(wNh4u}Re4Ybif?CNEr?sz~ETUk!`G<;vg@{aAuP1_^2n<~oPY;)&5>B@iPhi)`JjBfgi z%IRxivN-@B<+_8D4Bz8}pUu&`T;nansFk3(WxLDgb-|)Yy>H`s{d{B&&*gFYd|Z>y zuV-1`Wk$ZP)k$@`{;CYfY)bEsbdlp5E`gy`HgM%f&vzIt_j0=e2Zc=W{pVqcPO}>{Pb@zwyn8b$dz!2v{=Y-WFEuH>0TEtE~ z8avQkg8fVEi^&OgSj?}7!Pzm;&5Up0j9A;&B0Mk7CzfWioE;!VGE82Z>j$Vin7Zgk13A9|7JzKZp*0Az&IEuV0;6;AsSf` z=%nIM3_7QeJgi&rv;i~9;y#N`HKudFkRIys4Lp64doPOu)lrJD)_FSu%}VQxkrS-l z`+jg%!jyw-mMLH_3~Y6B^=dKXop%l> z0{ihHPPZnJe$%@os4#~0_O9OYUA1sFw|9k>^Lukw{Pl`+y2=4`IHfar{JMmb^LRU`ftsFFk6;QMt zvP1K;fN}c7Nlb-KfFf&+Lk<+Y#kuWEdbBOhRWSHQvpZ4eWoG14P2jG23OH*ID+~7h)T5)k7Kkr<5B3Z5a{%eHKPGNJq!oUJb?rHSl%8|^EtRj{XJ`-70e-v& z_oeARZ~a)b>SP+U1?1o^6ppuaS7r!x>;BOvIy_QaXBgbI6ZWDEhz^$z1YgwTD6g!4 z(9oemSlilOVY)G|%b+?@W2*dZ8{&tOut=s_Qy%45d!r)WA3oZN;{_w#&8Y8?mbM)@6HS^-vNP_|;9OIF_Wlvcr#eAAwj-^EQBk zJ<_E}sK1Rpc=NNibic-PRFwyBXWlx1=LhBtn7Cj*g$l_IgC00GGYk|(_MNn?Z{sjf zePdE!zQnupRW;NeSwD2eQ9P~Zy+7)C?_#_ZG3$BnkN$b@4}M^MiJd#ZqhqH5PrrEP z9H0YYr$nWcQToMmD$L`M(m9^yEjy~^bF)`~V~+m$*Pnm?^T+e^@qDerU2%oSv*o$m zKG7m`z1==}r)9;OYb!1MQMh-=rg<;HIP!C5y;W+Oa=g9C8<-#3 zchWjoK|6RS(w%+hYP!%yQ}6U4h^}RS!`%~1;!cmGdB7I)Tv!SJ&gR490{_UKieg7A z;>nM$ZzIn$ou|Jp0j-I?Tq>|F9owdGd3R`!97&$-nfyZuSQtqBLg-erv}OC@k~!r>NLTkPf~Mjg(Pnx168pIDBg__&{e*PM!_Fpa5A~ zi4RD60A#mde5P-N;uJTKa{~_tr0C5Vfj5y40ok@rl`sZl*xK&N59-&JI5IS*;dKDH ze&btOf&#MJ=kZK-k7Zh2e$8lUoomuNbXpV_U(;gZUU;pbH-u`)7T?9$4;69m6)K`)FTsl8P%9cL$HlYW4%>5zl<`!gj1s9MSK&};7Pc|F zf*w3nI4NTU6-CY{vZgI9cdXtsJv(OzMa?L$jZ`qI^!n6{=M*(#k1;G-T4VI78S=GI zMF&)RLOEK|A}V%hNrZt=0)-aoU_T_JVSNs(TxRl_-XDrmYA{8d7`e(kT9O_*Xra}} z5G}n49MIC)ihL$kKsb7Bh%|XW#1Qa{6a&3q(2RP3F(_L4Mfwy+V^0|-9-h$BUmuw# zQ9KoiV&tbhq+yTQ?f=UALw~6T>dgY8z&ekXK7r!N2^1r=NZ-G4>#~b!Vg;v2D6|Lx z#k@9B0XM`+M(D#t`o_032184qFY%mWZ0tcxWo$e}#V&^Mwqk9h<3bwtyWRPf&%uUJ zp0vn`^^>l`(b7=Po@GPF_;6~44xMI*mL9T!7Q3Y>%h2r9zFFd73?ER*<|9t{0JK}C2T=AB!!igcK}_?f;DigSP^EL4;wx{Wc< zWcOI6{qy@us(Ffvy;fEXhZ1Nb9T(4}@5Q<$ogGvI^=1K)tGoe~ek|L?*%` z%EeRq(GQ7d=v8rzT=M={89D7so6DpW94c+v@6#=woCh(=D$=)aWCc{*jwk0gsDLG0 z74y!`P|5CLkxO%Ffyxd`Sg0sV^k??zYq59ihf1YeJo|K@XKKd{B~V6E6oh^848p)W zjDZHN#|0|A`ag1&d8m}OWGfSGtQtW@7$3&10Ts6w$w@ET3aOXeyfYgAD65ns`0c29 zJIsDdFsSrtGEdGCJ7BI-8w{U)Qp}UHRkV?go7PKHoi)%@EK|gj5n$hdNUWQhTj&?fLklaR3H%*|Mbg${f-8sf5Cw#5C|GpOq3Cbd-49X zWQYgQXB2i6Z7Ie73Ik}Y6p06VPzE^yBT+CYzae6_vD>P1O5GP zKmWsTKjXwV+zI^kg|l>et@Eel^L}d1*?C-E$L!pfuVlitaX$AZuW*#Zl-FOwyep zN1vtfiS%FyDneDLUcr-tbX*UF?nDxGCXT*hTAo3Burjv#0;K z__oU_&lNG@090-((O_*!n-o919?!e&ROM05(4P{)=eF8Lug-;fCLP#ymj&nVFx50j z!lO;oU{wu*Svmw?xeT7An*@D-kH27&A0DL1Z%S$dQM_|?pU_F(71_)e@p@YSPl=ok z;IfKd6U0$cBk$PPQ;(gI8clHaowV<+K_irw77I;^;lU(ABVLxK;kXGvyc1Z!uho&{ zSB@U|TP~IHD`#K)O7jv^Zs5-|r}8{^0D3LL?p;LV<2mOm^&%!xk0?c$5We2hY$3)- zh)mk+t>rFX{IGuIG%atUi7DuvRinZ?=bc7~EN=}hcA75l0pd&;9u{gAzUt^A_a%|M zrD9}N_(;xq0esyHFHJx&O8enQG_G1h(9o;Un`k=gg6RHS3IQ`dtvEE%$TVn|Av6Mw zC35sIG9|NzF?lxvv-hQ_B`S7ti4!F>0#jr&MuOo@fsBvRRG{VNwlstqEMN#%{GzUkM}{cv z?8e(U_opCmhangO%S_qGl)e_K=m-SX%Am^)TD}qwB9tH_&`injU!@>t>;)fi#p*P@ z1Mlob_N`#-(mV1u!qFBV5Jrp{T_njg7`yBah(zanLofzW(UtX_H=)6JWK`@@CNS!$ zsJO!r6pTSwQRZDcKi6es5D9D#(#ymh@W^$@bfqKi58Ok&b^)6iol2WH1L0#^~a2Cfntx4z% zUYn#B$2XfK=Nc={`i)`C-C+m@_3nitumqnL?T30HC*f25`2eRqD5$rzhw7uC9lHe! z-ZCFSJ$k*H+NK9XLu$WzXF@rGdSON^U)`p7sF>ZN1fI6ZHF9zk6$ zjPOu*lU&XYQ6_1rAk~Ya;SWs`I5p3pd9EY$C^Nc95^V?uby1qnsjJyTuRWl|F56si z&vp1V6Jeo?#^c^z-&^+CaB|k=^KrszUAZCta=m@RVddnk+w=Z;PTrh00LvLmWo=@u zUdrJLsAKYob3{#3{Vw|r%Av_G3{JgyaXSCP0*)$q|K3k{>p9|k9vnt*56Z{?LSvJT z^SuCtvAU9uvs`B0iI8ga$D7cn-suwth2FQc39i-_&BG>$(92h0&LD%}A9+C<5yNVM z)+#EVwrqvB@ahj#2llKSbn&ueG+}mZ3Z(JXgHTUDxTk9l!a%A#M9#xEf`O!)D9RnG zF1|^lgI0VmBt+?Bl*qbwH$WU3O%Blin#f)UiG6*(?i|dzuxSK;=E*hHtavJI9vRhh z?Z|!!{?oilj9*E`E|qc~gz|uv^ia_Oevq3#PLm#%ua@*s-^qs-@0WB*5BVFRlUKvP zQ{L(d#scs5T0rh{9v~8r1-hvVkEfLUmIG)l)ut|lNqCcqOoO)aT;ip>XJ&U)w1O-$ zS_NeXH6rngZluE71&l%kDa!T0povZcBTbTxc>oNTJf|rRkm|^Ym>3*B`lN3Rfg!9A zGr9<(+U^z@FNy;O*k4)T1cT8etK}TjZ~{xXqJWbJRBn<6w#o62a#NQ#0AjY?2l9ST3LE2Tm?7ldR?5 z^jMys=hyMXh)s1GC4qCDWQhS?sNr;u9BSNtjnEE^U&7bjo*ln8;N91|nM;x*^pkHm zBS1~Nd=>hKaJ2F7oKFznjX$G}nJgNpfZq1sSC{hiPJE4)xKr=+p=mzxL~?o;pz&o& zPPhA%K2Gfp-$Vx`mQRe6z70;PW>HH)O47 zw~8xm2#v$Y51xsuq4;$sQ}1-1z-bXv?{uEPX*Vlc54IXI-ttu%zJXT!Blp6=f$)zU zCV?btC5kPTqDuq#0nI+}A}a@t8znajE5SHQIYxOW|BQIR|DB$Ba3v*h=se+1MkV_D_T33Iq^$F3W}S{LBi5+bZA{>o-F8JV5I{b~ak?7B45xPs+v1si7v#YNy1`!pg|Q z_oE?e)^uv#KHSokc^0f=RW~Q%KZgqivGyaK_QK}2PCbMHnM!>6^f1-R!i!LDK;I4eJA_)tH)7GBg_Ai3WUW3mUR=aF>(4_EU4$#n|91AhkSv*rU6qb>jX0 zQATK5|6ek$=Zn}O^R9iR`le(2_0Abb_ca*upqvhX;sx~qJamzPdJqme$M@SrhbC>4 z(p7H#gjQ#zP7q4eTY* zZZ@KC3juJ<6w23ygxb-4>UZ~_600OEmk#hT9R!Sz^%s9QckF;5P;T;oe)`ju=?f&5 zlMp$MF`Qdz;53+G9buJBiZCAmx*Pxun9g=b*pc(IjI4mDTf*S96NR0IWAchBTd`9P z<2SLG(^C$x1jC6a%`OL?IPvNLoD`B0jNYH!a7utS(sAXb<7S^`u8qJTdg&qZlQ(eE zFE4o;*9a%l*za-T7N0oVL77091LmF4_<4oDoTNM3`6MTFRGe(L1j9*TLs;ytUyE~G zp6~7`D$BJU&`Q6kqi?u|hJ^_XJu4I{ z##{ma&P#rnE*Nj-lokc}hR&k&=~6mxL7#c2BSoG#*~K?Nz9fu7a_I81)PlJ$^p?v; zOhZ@h;;D3kqp-y6!m4tOVWG+Mu(2hK|NAu^3;fH^tD`mv?NsO^Bbk5y>94=nqlM46 zyl03LO+S{8Gv0W`mg@cE@$pQ@oSr_gpy?OZu;eGpHdwyE5(5tX!NRW@6DM4B)IL3g z|M=-&e^HSpuW&gJ<L^w>{| zzzdY7!}64r;1||XA9pBIR3fHM{Jfx6)s#v^)FudJK3MbKeQkqgO{%_p1g+ICJ2p6# zK6Psfx|(o+DQtzJOp!`3S08XErKCogG>MuNPF?(QtbnT_KQ0wP;vOA&q)m< zkTyJrHp=+oKRp<_nzBU0b9lWo8voD|eb1lbIh=M_LZeadIrVuC^0iP!N1!pU&@8xX zr0WZuP}=K>nXEcG?WEsNU9AF#J~ub3VD|Qj)iqg_IDg&SRf)n!MMYD`*(dVgk`z{a zzY5(y{_@k$zy9_oKoN0W7zqZJ2s*B`;mnP1INQOL7GkC{5Y4%l5A4}sD@n&7oGr!1 z-U1N^QmtN+M_i7x-v&e*ndRNFvrEB+!?E6wz=y)unJ{ZQtR14F0~ZRx5@)Ju7p6cm z?+CVX8f5ZrbxzknU;g%YfBn;c{yQ!kN)8u&!%pz!xlonbqU}n>K8r4*uC?R=>37wJ zoy6eKD5lSx?)KOiL3U}bNl+$E2J#}U4i)Mu$5@=s*0C;k{5-G9m`O_MW*+qJF$t8D zNpHQuAIR_)(wu6b9iXjp_Bw#{N_!oz--vZwpxx|e;D#zuc%)+nZIxR=PFUaMWa2ac zr6TFAG+K`Y54eL^4LH&%^8gwag50taJ;iX=OeyAV;bv5dZBo0;e+A+e=HRHyND z2$YR%P}={vAm&pPfW$UM3m=M_6q4sas3fX5NKwIx8^B z^$L^YAA+$T6*)(tM8zGJAYc@oc|*|R>;N!|hp*+KXxK?GxLd5#7*I_fxo0-dYrg$| z;x1F1OW3{3)J(Xq_eb|GQv}#FHA{tV4l4*Sx#Y#@^8adr9p{3Cp{AD^5Nhnqd*Rn| zu$lKL-L@{?+44kPhcV{>4=tEsbDG1ql z>Br@4i?7atx|{N*mxo3yxufNJ48x?);=$5FAv73F@kaS5wn^WVgBMz?5X=x!WDnhb zil!-L1>szn_YX3~KL#710@VV^wW+L8wN~wjMFHIk7`W8Yf0cDayOa-+;NYA18&KQuOR4 zpfNMVNq>Uo`Z1PyZo<;sda*xNmV=2M{9EHd|6=eAO7tiK914Zr%E5@U)0dcpqf*T4R+v_+m)nLwOBca z%NDDU2O1IM&29xp!$0z}Gb$1z*_e-DBwOSjkMF*ZqK&wh_>${&;cZnp*ut^#e*vpx z^839utAFehP1a5os}<^3DklhmK>X@0+5Fm$Tc6_$jgkvma>h^sos`d?=X2W`^t`{S z*M2kn!W(6yvPk#_MoU0n;`SKj^V8wq`~n&?gf3)0!U-PQ!p&55sNXiYdJ8Y2K1@|B zp9w4V{glV4AqN=$hWz?og6;VWXt}#FDdZN-ev?g$u0)z)nhb6+Ul$kK$X~fL$%t+k8q7h8WdeaAahY zJ0ARK*KdU4RG_44WfH3#D9z#RIAeV6qHr?tmiN)jvN`e}l~tbaqt1854rg>IOSCkO z`p#{!3Y)L}nI&g{9AF8H#?}&j8py)AR!5*Qrx9qSpJsWBx;D~rK@NcEr2#N$16+H9JR6Q)+ahIzNfiuiAtUSzoHo)NU~3WP%y5I?Dl$qutE!gRR5%j5?#9ac}*K(d>>=01Z^_~}+E9U7Iq2p{DTsx6Ab=c-pr`86oM%9 zwrqkbBX$DHPA7_-x+i0mu*SGi7?%9e5(es5W{Na#43Imz<@kBfnJyeu>`=cZ!$V25 zP1atgc7MC+mOq!Sb*rNFYoUtrlEGO{d!6(XnZo5Q&a-&=nmG1UBMqFQ>N>}V;0qu{dTZVV;SKHza#H&V}{G>1#hR~RiBNDscV z#7cAOtool*n|5dqRW3wtcI5(Z#NRpqSD+_(LeaG=y#r$IO6y6l=W;Owe&r$#zq%{= z=P5qMd54GjDARaYe-56$7HaN*C{wsV&`Q$0^Ksr7uWTp1ipHTgrUF++rkBT>$T=Vp zSY}HDhJV%XdM-`iCMI!;gw{>7vOWw<+^@04VdbC|T#bz>xlWo#>qdL7(M|V|lryIi z9pJg*ZEJq)^Cxf|JIl~fer#LUtD&&7;^x@t!O$VK@e{ZwH9s)E49m|ZIa{RoK+6@W zql+M_eL&|&;{$0C!Nm^VqdrP6!ay)uOM6__{a44Hv_uO1=AF^_N0#XKpr1Ko;{Z!g zH1;d#WcN@-N1!q1FQBoXT{+jicTnQcKU@2#HC3~uj^@Q)XZ{%^tfn*n{5XGNK9sL- zxU~l*=BdRRYb5=rUs=-_aX)i%Udk)}v_~eMTRKWrKyOEn(^1x0JC@0MegE~XGj6Zc znr9|!Syf0WSiQi?x;v}-oVv4Y$d{0HNi1lp_CpBhpbhfO5(|Jw`lXKhfDM$52r~SU;!Tu z8w>Pf5oe~9XxQHTQ8ms%Q|9jC(`%F{j0)qIe1kFaf%fqFDiM6Z6@*jt3D_M1CSu2I z#XRx>X@_-n04I+WA4s=u%t|ss;cI3kO7k+&bv^}?mN-$>&K)6Y5XVxE^#s0-l;<@& zbmQ^S=%zpGRg1KrOys1~UfEBvWOB%t-<2#r8b1=GY zFX@?6HZuA%Je%#g>U)5^rK6oWQDx?xSO73qNYtJ%m3+e*18c5OUfFf~`oJm$mf+I% zTe*?M@6&R|(Kpz~a3@8dIVC9v6}o*{Z9EfSRCN~ZJz?F;5hBl=-S91RHOeT{h=(8-|r>ty@CfC@{<28Ah-le#DBE zBXOSj?IK#`y|^>ulV0V^xlgY+51RUO>f53DHT8j(otbMZ?dMe*_U|0QB#>#oq`sCi zHtCS1G%REvXr;es!ifgT_R@x(Z7;lt8T{no^#a}p=vk=qdtY{6JYBtuS=faU|L$m-auZ)I2s>}P_g)_g@ z;Q&Ke04hV|^llBo*Rt370cryUz&Y&{0M-sj8NQpJUs8v5XrvZ^m-e!SiWZcB#_sZ5 z_{!GqmzEAuU%AolGL+5;*RnJer`kPD=#K_q)|s7;T~ZTjDAQoA_>$NZW$sw~0cEl? z1f6YZL!^C;$>9$GD5L8bf@hnY0i{!SQW6~r2>`RstRc=*T!6j-59;4Vsz=8@ABlPMV zOE4j5*MKk5o^I@olFm8vnj*rNv*}qu$V^;OlMD%>xHDBo29I8^Ts4E{ObP|EmNv07 z+Svw{3ujv7D0gW|7DEX%xXz!K)2nMpm6w-3-2%@uld0Sy&Fanz(&e^R;WUhBg}3|A z@3=vyu9wmHs5W(F#~#JsJ}-BWA$oQFrS$k46(uu=Sjlz$C9$w<8sQ+xWrcVfbb-^J za!@c5%y+(Dq~F+?9zJE?Nw>JQ;Sa1QFQo_CibDfZL0n=s%qp29uBj)p(rhxh@J{E=`oeiZ2S89^9JH_TRNKwVAX#)vr`{fgK&lj`udOCkT3bS&-zRop;v2|l7mn0Xa?dPBV`ZH_+geks5 z`8EehDOUSD6M19!^%Amn7(?$x8)Nhn8s|rqkd+u-vkoFYT6Iz8@L8cQeeoV_?GC-t z4$Dw(Zps-w8h)vVVdhUa5X`odpmp0#F?lf5w1S}G)9f~Uzck|W7N($ z7efg4gCR_8KEtU3+7d?w!@?l2;!Fj@9hRUF?9W`&*FtAKAPb6%CkSRR8m;gSa+L+; ziVfqWox>n5^Zc2Ma6HUAx5nOn!?t`QR&uUIg$pz($vgx_X|DL-B$g2b6`7?3E?B~q z;qzpcOCp-s0K3@|{a`pnX1VOJ1cRU`&0B&KQ1@rSLSw=`RsB+!xBM#z3N^(zDGcux zO*lnI#a_k7DbBRj@ckPkF6kR%2)>gA<{{YU^IUkh_y~fki@azJmT9kbdeRI3K#S;x#bwnNKl5Ci%=$uj#2EDPEtR_ znHI@QqMXdmh2|kx4(>G0$QctO2r3dq2^WbdU_V29l(Z#|?2+tqAufZC{5&h)VF?Do z(yeb)#HmIHK(O5PgR63Lv!Rh788FOP8iKvq5)AKpSmtj8xhU32ai%Eu3?bN0^qj3y zVz00$CWFBn5bPJnyKV%zv^RpZ!O$(o%-;xdX>SCn@7!vM($8v~ZR$pl%MMF02=;FT zk*|dSI0Aw>`{LHJNlg?}MVdMOkePt>_^7K+~fZ8<0Po!szcza?l^=IoRL zIJxn`j3xRm2V8Saf(+g5hg~@tW03Q%EkC2`$n$cpsS!PNrq-0vD{U~n?z-P0`pOvx zf!c+mD`!s32yd4puBih()W=4x+v&CpGW<$gXk1%XOf_WWbc1{&J_T39<*KEVU(6+c zE3G}Eg^o*3m4-WDVxwULlk6C-TpcjEI@9t=E!L1xqG2|LmEg*|?X?#+YbRBVAAYp6 z@+@WgqznA~Y`6UM!!UC{0M8CUV%pMw|SB_MLb2rTZyb3j84O)F_ z$u*G%LJ1TA+jHcr7)XnDQhrSjhVRsdPhs;Y!*^Fcu8BDiic<`qI5v4wUPl)xc^-kH zG#3?kihIZ@-uSI_Mym)tIZWQYM1|hhSU$lAnI=JcErRCf554JyjFx=Y>S$)Uv$2r7j?0Il>lgZSkY-;^;C9GrQ_h2WJ9oqO|P zVCfTG=TH}Ej$40~laui)$4qSOl^NEhoXv)vhU^kp@XUt5x=Z<$i|W;hB)PA@`pma0 z?-$PyHH_F=)lEq_T_Mvt{^y^6`YYlIWQ^YHDr`>2P=O_QCla*@i-{i)y6&v3 zP_upSYQFHjQ?j}&$0--nvUcu^6&r-&5++V{gqakoh(}VhqacR|l>30LLZewdHyJyv zH3)?x{jFf)+UI6mISWC>Z)vDE8Es||9geq4SDd*}f^>WL$WV{I0I1yV)92$+_Fr53q=H4$#}B%StpF41lk4qg&AOH-?=^* zzEdHtB?wHKJY@D>%S9Dd92#~D@Y=TYp(F%&iWqY3^8>EC_yO1Uu7)Zqu;|u_=lKCw zx!U6`ABzpq=Lg6)Lcch`5E_BT68+M7iXX5Cft4SSRqWP5=J^5FmLJd>-c?)k{D5oA z52)|tL6;c5+Y&3^7#hCqN*_uxjle!V0ChNvQKQLYOAn}`c1Uv9d3wOLr3ch^ZZ<@J z9dL>saNS`DhQK~OK)x0`hwQ-YL13i^WEDFE&eH>~Ej^$b2_3QNq+OGD<{1LlmLX7I zyVdr6hJbt{)^o0Z+P?ImB-0@569lfi2m;rZAW%hxE!-Swo*-~-2?F(kdOO5cUZI^0nAG4gg_UrpPMxAY4~DLJ3qMD!uNeR)OKM$!}>o*LjWr?k|A^v)dn| zRbaVw!jF=c#-#*mIRZm3oCje)C+wmKTw97j6&1E{)8BcDz*YL!jeWO*uul z|0zV>(KPN7A}1xNRkD_W zFpN#3%A{-)Sy)i=LNUBF)tk{pu1ce1+Zxm-FkE@7{s^gZcY+ZWhH%T3^WFQ`wtIiS zA^Iy)Q{DU59fq(7RF>%XXAGz$`w>{^B1=}WLmtz$(bP9@F_BxcWyMi zjK$9sIE$nrFjXm&l28ObaTs@*y!5FhH%{ZQoAAr6OWc@IliG)m%1f-{VRcc*E%{{V z7zb$0!lhB0=%-`J)kd*Q2f(E?%7*}?SeB}%eB4d)8{$abv>_D_NCJ_p*AGtU4gabqBbPSibZ;^HYDZBWPPsCzwoPD z*xxwSCnt5&a&!~wiGkk_KVo6i`X$}>wXHGXdoLeYm$9#=%Xn=y628`!p*Mcx#k9D# z%DHj&#gwczVc$5xWLnne2UfzcKxec&&xnb%3vaaVq}?C05@Vuh-%IDFGup)x5f-kn z24LSz@1~fR`2K+v0rP6k+RLx*U4gwlL`j;%)(28l-WI{H(z|t&n_Cbx1O{tJ5yzEZ zW%J-x7)HyjkU8)x_txN7m-2n%OoO({xFxp0njLUi^sVqAX40qc0JCj{^6oe%$p+j_ zZitwaKvx&%&#xQqWzV;JJa9{Uy|qtt0A(6a;9coTHEsLwPSu<~aP@g&WUiOsJU-IZ zEHEf6Dc$4IS={JjpFbfQ%~fcBlC4>63d;5nG9G$zutbg3Qnbg^g-5Itq7ehDG*}MGqaQ(GAYQT>2(#023Ba z^;|!&sbfb^i(hSsX3uk|g0}wj@eY2`T|ByR0!2Z~4r6Gv7-RG+&-Ar#iI&0wf>yeV z3^(3xuZ+|-uJ1Gww!^VAZTuy@KUPLgOu{`e9vG#@d9;*1Zz~f;@CYr^C~jm0T2jGj zzeMXSZHN?l&1)kSjL=eA*sVCUpsZ*~715+5+87KirNQ3F#FyUfpi(C!#yE2Y*9END=Inxh6VzPs|i8aLf zY#1xFA(|Dtp$bN5Nh9=0bMNJ z=^LRq2Ux;FrL}}8%~>Lmn_`*v&+o;(SEz`JodjvwKa@ZlsdZ9eV0`y|0g0`p|4WFA}x%DXpx2a19 zZaWOYa3VVMUb+@Xp^CaGWllR~9V~et(kpP%um3o!LE)sOFx=+Eh}OZ@^;GMVbY@#{ z(o!CVGH{%<89!;kS0TI~e~6QIZdde$#sW8T>IB*ZsWLIp+ZL*Tlm2Scps_obUONn7 zaiR>7XA9Oj_&P%`pA?02?G&68rU|stFaG8k0k@VBF!WI#%+?(Ts|s2JR-Clc*^w&d zank1k+<4Ra2q$fSZq=7RDDak^3xlP?iSQ^6!bxekHHJvv2*o+T5DX`xGnY^J#U%&i zlX6?bauXFh3HVX^+DOL*v$Q8??X$jqXrGoDQk^e~8E*ikAICVyVF;8SsBzeot2sGi zLmT0^;uk*AuQ52gpde+3B`8w*t4`@_v3KmxD23kxt@QIOr*(WGs+P| z!}nw46p`ZA9=<=6grl&nxbR7Y~oTC;Qz7I^CT%jP?GAXK&V0br`nP*bmq|N`J z*jr8O!_c8lYCYLyRFG{}N?X}R|hv>R6PVE7pYL22kW@7!#O?yTpY zz7dLZfF&3N3s1p%$7=UjrUM|Dv+p9q??8}Bpt4|l0Y=Re*r>F;nEU(%7AyW9%@BFP&LXIBdw%Ov#oXKJ?<*@wijSjSVxXO zDnv``yNPq0YE)vcVi}duMUu=zP?YA?P9xt4g6#zuRco+>U5>eP286c6aXUE6y<8z- zEito=bKhYJ2EjfZLcSIP-~b5L>mu&`>mu%)kf9)`2pPpmVR*Y7bLY$oZTMb1j~ssl z!9JBmo1DSsE8hoCG2v{Jv|uum$HOV5GKbzv_y9U z;GVt_ic^E(JChS_2?oJF%j2Hx9?P`95G}XYEHZpw8_csj?sD0~F-CfRz&EX1u5{M>bq3r&AO!mR9-d!M~>=cEyByLTE9 zc)~YERJ zYpdwxY#5^_PI=L8bYyW{*e-zWaxZio{7NShxQxrGT4j2I4B%f-eDhoBIev!h1350xu^W zs$@q`*Icy~jtj(qN`E4i?nH>)GEZH07(+wF7^A;imA)3sw0|Y0kh7qbq+%C9crUjy zl5jc1_V+^6iSiVOimUE83#K5YWxEU|;7DnkSEPI1tA_gT2r2DZ|5aAB5!o2Q`35;r z;%Lmj`|F?n^WT5@?RV%OfiHNwi$J?b6VX>(xjau?xl3#RAx;D>WK`i-#yo!YlT!L_ zjI@mY9S&yEuN*AE9$!r9puaj2hh3R27xopjl6IS%i_h~Y?k$gEQjBPx<94p6XcYIh zbAKqoJo@`Iiu*1a#l59bR9QWL_0F4>d zIng2(LV<%{8DIF-&$Cn1kUb8@&@X!Q*n7JpZ&G6g{goP$)$SzV-TEr`Cl<*XkkAx6 zDMn;CH?89gm6H2ga>`JGdGwc~t4;07JC;Yt5q5;p)(=0r4q{FcQ3hxkA;aPG;%m7} z_qX>4y6Sa+As9;fjFLO28uccVliH~$$@K!XvJT%*m^gbw8>#8(m=q&I2|7%zOHMU& zv}BK=2yeioU%~96SlknH!Uh$T$gW17r&!#j|KC7wYli7lEaV%*T5^XaC{Fsb+W`S* ze=S)^Ho!?Y$3FN47A;O5EgfR$q&%28POiLsWSvLLgBXf1jT2FViy@E1WC&#%q?3o7 zqi%c}II;6Ec`*EpHbFzk9gd%yV3FZPYcw^5Zo ze zVGO?!Cw3D3-~^gtot7vw^b?Mgrsd@!L{4XQDhfKVpQImNHS@8a?2++XR=~xEhSJACxWH#{2r_ZDDJX zLHZgjeIs^GS@(Xle4wEu+8!D(#va{GYx!C%)BYMPIbq5zqGAu270ICl+DOOE=^bV3 z!EaJ2Xc3;x0ciR9Y<4AZ@eGvE@fcc-4AIg#-Xmy6J-`wg zC&m)}YM67y4oED8AOo!=6+5kP_JT5!Fi|F*bdddB4aO-u#X2op=glZ)+m2hD2u;N6 zhL$2DoXDjhMqM3d(Sy@wvJOIx?8qYMt|HDo%R!e^htqY)^60k7At6@=w3~*YD_V_?N%@{NDfrO5B#cG6Zoi zj+~Zq)o!BuINFPRA$FHs61BXep&VLC%!T{hm&f3)nf)bEVHy<$J7CZAOdc)IWT={+ ziIzvH*%4dKgfqQ-Xq}eVFcd*s$GUJk!|O9l9=jMOkCtIlWkuWk`2+FH@l53LPys5I zv^ zM`w`-L&GVQw+xf2k!=pNJPN}Ezf!fh`gosS@|dERJa+gNLw=uLB3}#Dc0j3D=_N9J z9}nm0C6D$#jGcxX%R)bRSN zbXwt5yz;-^)5h!iTt0^C<2xMSQobvPGo=5uN|jl>>wi(Dx{E)pf2;Rz+{?`d=!2|Z zE9VoVZ=*<>x7_jH$~NEnwY+DNewDF3@&P~JVbW0ECo`4wUsPS?9I*bkMJfXk&fIF* zU=N`@8N?zAxczI+Fe7){59454DgWV@fBVZ{e}SjH<}8>bERW&}DDEBk?ZJ6Jdbqtc z8w}TN#Bokj&?)?A=`3~J=tH*MWe?7ddH3}$Wb@mA-hI9I1-dHrEqwtjJux>Rfk^@W zkz)x882lrL4%AxsM~*1Kh=IpE3Fpz0a2^tcwqLkr#h_X&lZloiF_b`;6w4@y;mko2-21Nju?Lfp#EURO01xxDK<3N(JL*l797j8SWd`DKiM{3Rv zX=R#WqCDE0fhIMayKJ-Dt{I}wM0s$ck~Vb8Pm&=f#fZ9Wk=CpxriwWs9!-isFrc@A z`qZTdXU8bjv=o`WsyTi5#kutU5RNrMG)vq=DYSO9?`riFK7+N``n6alSq$3cyR{xF zRh5x|Q-z}B*3xULmeR7Ief?Uug#*;~Z>ov<&Otf-kN@%0zy9K5AnKi*$*ee2SwdJ< zMx6IeeasV2JF1)(|u|7BKEG1O;O*W~@x# zI+2uHZlINZDx1F%<{+v~4ZO}@;2a8`5qK$N1 zEOV!1Jvh6nSf{-fXXq#MFzLpyXIiNV^X(8OJwyX0ZbHwwQ`!Q@RlJJ~uLF~*!gq%u zC`|gRnCWYwCGslfp3<(|W(1gYbL^Af8`B!;+1?v7^iiJj(F36ahP*u1giIQrEw5@Q z!YoWg2hL%hoFOoP$+NxNrHYDmFFPTV2g8wT3#0{3lcBcdbHJoqUOcDncG+VH0+XyX zo8jbZp^EmyB)c|d?=t+3FRb_tCD2AXE|$4XvFFqUEKga0pY#(7lcrbU*{88Q6Bi*= z%?KvK7&4-!Ju$38yEXAA=T9j+v}CCYz^z~r!i958&a=wNeUCXTSXz7Z$yV~U&>lyC zC1;Z5*{4=L6Nw{~KpW|}h~`cwd~$+Su};ga8u|$ZOS5e$I`DBMZPSIS8G@y^?gX&d z8Ok$}i^9)n3mjMc!Xf%a2Ip8EUTEeaId4i^Ak8w(JEP(A;&*xG zZD(f)zP6NWVB!8uN}>(HU{cx!j3owixBW0Fls0IkJCT1T=Ehts6T;7=YPgZG)oC=5 zULML(U?Pl@NfESl;`J`kfzKwK7chiL57C{i^JnTK2}iDBa$NDdJWEf8HN^UjP@Dq{ zL1EIJ$Ug@>w*z33a|mG4&$08@!aRlVGwGuoCTIdM)=4iPTBjvb9anVq$tzFJJs816 zx|EEz02A8+@=OngBUdmHo{xFw)@+i7*9i_ZhZdOZFa(21Y3;JkG1J$+W4$Z8_F8oH zsZ`IzzL_-Aaj^{TF;){(K}<+3lYTOtO@x8uCbOKFGJ=UT%NbDt6T8XyOb^akK)6Ka zom*j2C=bRE=^LRq6(+rzS0srx1cOQGFf=kHv&S;+&nAh^m*ys-V($yIheyRSA&?wm zB-c3hOfQc$kxOD>HBE}3twV|~p*4BcP~;9_(sLxh#7*H-BWyTwZGj`F@P!{>T#_@y z3QTqwg2JRfg->4#Rdm2gm$>$tP15jvj^$h$Wh7yvh>t!Ky9zLlA_yntjYGx9NY8(RJK`M$H0}9d=+K zDP8;qxan)5ih3ZK^v>M*_!P%pLDKJye{#}{Hc~SsofISLe01eGC)HHrG)3P~f@zTS zi8W7tJHiMg(xGoW8XNHzB%GU|^xtw5hNI^}YLU@-kZ@YV0d`;@DXazy5}MPG07+gp zfL1aQ#z?^F3ED`6Z(kWdfM`fp0O z;pm4T>8~eoI>`Zcpdd*)bA>M_{Tu+2!Ylzu`UPL&41}(rjMTRB?=+Gt&C|O>F^X~S ziBV?4b5w9lgQPIvO!(lpe~dsP+!Q0~4?nt{qInw6v!(Iu)c^AAQ!3;e-_i~YBz->5 zGg&>BsWWw-)J`e4#Ie_Mt6Y9!U12_wwJeWGF`{zIcK z=b4N^(o#>VsQ%7_C#R6;Aa!85`|Ptvo}717gH^MHPzN^`tQbi8e3B=>PU(P5QtwTY z;rk+V{z{i;d!@@zH9Xy;Uj|J(2Cq|Bw>(>x!%zwel(vPaPd9mT9>@?VJzv7)J#h=| z;AybR%pxQvggc=_xFm%pAN$S~Rbk}WD!r>1`OQWO=R!0xqDScBE(AT~+6cdLn8UAr z70G#91^YW(%VJ;oR==E2Ukj~s0QM_^=Go_WJvm=O8LqvQW{5g&I5YtnKjlQ8Vx5-5 zGZcXWzio=@lO~?LAAJO!_Ck;+IoM!lY2BmS;fW8O(Az=bH>S@-sqa&o{Z_?32o`&dovKSC{zrkidmf_)+2? zXbx#{WG#<{E0BJb|KeE>8h_Rkl%;h86#}-5%NZq4*>_$4COaDIFIl!tziQmgw~u~@ zV!WImBE?O5UzP7!*h&@$t6-s#RrV7ihe;v8mTObud?RvF#9QdZ>mXghw}7cP)2zv8~9*JE52M)Jlos2 zCiN!Kr?}(C2Y&UM6!GV^*qL_FkElyeSn?Ut3BmC$i*Y8_rw;H4?p z8d*InH$OOOf)u69J0ocG=z{O|YH$3R#&#(x@YHroXq*^J$DW2p$IGmXzO&zM39MMcZ8F2w%mGwX_XZ?F$DrX zLqk>nFc17!JxBF)Mm$LUe&Z-C5e}bOJv>DFyQ6R=0&jcYItsHjo3$E_Lc`&>i4eNK z@aYQGLQl!I18B2)G-W{kTBywf!dqe7;Zwar# zHx3?6lZ``ikfllHm&cmO)@fNqHDp9-P+Lb=xH`Obc^r_5CZB)&{iol4{^_qjLm_a# zmccWWOcJz|YMs*x^V`0qHkhw=xjvAI&p~9LXiv#nm{moY4lUEX8oZ&mq8m91ua_+S zEWdngofNJn;qr#B=J~2Al=cv7FrpMPjjW!3mLCjsP}ntb_tT($Eo*E^SaM#}6dC9l5<^A5jqEnrbV*&5$) zDan%W;IXteR!C{NHE&tMFOS$$WFZGlVg(|?Lz)DuhKTA%UoV?`tsHeZH4upw+*&oq z#c>e7upo-wPxSwy?-R|Z==in%LuVQXC`+%a8*K%VovyP8-zrTRyMX^Q(e8EnlD5xRfGp zxja5^$*!p3pqRYUfL|#Tua#Tbl(g1g5gdkyXicoWzNupMhabUBu&<|u@HZ?6(X@{- z=kSCrSM&*^T^>EcI3E6g{EzSd>A(Num+$}Y|M~xZkEPG^ms}?F@qxem!~ga1g|s|) z4p)MacB-(4Hj?2?1+TfRROS6*|Ar`hQI_Ua#C%-RIHS z#S8p-{l++iY_|ggiRjFw;{g$GKS&DOE)U;pWx$v91;Kmv*|4O8b(`h(DhO`ND z0bHzo!%nV5ZYLeg5KP z3k|k+T%2bkY~Na?uP+ZZRH9wE%%&+*;!;6JyYuJidZ{;_oIY=lS1KxU4tu?GYnl?ik4p)hSXO+X;gpRfVF1!| z`YyW+Dx#ns)eHAjAR6C*C0wPH9t>Sg#ju91H6A~YVR?8yHO|ns@E^ZZ!D>rrG#X3v ztLpT%P(@|wYX=$&mk$H$SJj-fpo}CmEMxd>aKR3(i3*&-R&dfV)y96Jt)ml-woYkh zwldLBGQvr7NzhAH;KVIgPcyd8UkzjH_0DZ3z{^sVj zzxFM0QYeN+#a=7(%LC1&L}S(PU}mqNgLqSG0}Z2WEP}QU#YsPkak|e4Ck+kgCF{CA zXhQ^NH${a*^m@0#%(*gKN8u{!Bv0tdI^Z5x*`L1(D+a%EWr$yS1=hwrS|8L{{95jR z*)57i+Q(P~=n_ic_q1v}AX66dAwHGV?jiuEpD4o#Rp*dQiA8c_o%Hh1fr@n+#@f5q zVG*F*x?*Lb9uq>dUrGAj|3Rb5FaP!X|FL}i%U^!}Z>StIYoY4wQ2%Mk>?~|~<8|2| z>~&fBL*LRA6evM3ri+0G0HyHIik>Y{PKihoYNZ9rsk!xL?4>+ax=wg`c_>E)%BkUF zjYZI5i~vQH;3gp_eg}5)H)%5ZT`foDx#b`3CHr%|Iggp^xBf+*kbei6$kLd z9{A*mkNpL_N!9ZMs!;aC)j$t&l&|jXtOUAm7xLW(+Wz%kAxhdJMJ;JZ#Bn~L2j^z( zj-W^`lA^dm_T*ol_La{kvldc*g#k9$Dj8t^s7-~Gm3t?zbu8wN9q2)J> zpkTZMlN;_-DP|6E*o8wq9PHve6Q_zY1EQVhomI6Hr#(xC(*RC*T=`>ZTEHJm*4DNMOA(e zU&ypf!ZJI{owj%!V3ep3ctNOSYkQ7NR7`Ra+MsmW#dMf4KIXQfPI=^3 zmR#3LoF16c%mFWBF+l89Baqf_C&U23?N;>r^ucMI*fPltG-9|(cul>gO{*5DLckEO zWs+g)K?!IGcz*M45f#+_!bjkH$dQX{jHuOR2zWW&RJ7pVczP9FIQwJg8f37ysw&5M zKvTg3f@8+EPb-x7LO`0UkGc#6L0GqA(vhj9BVJ}hfO0c;YDwPKjGSHw7}QwWZ3t|) z5a3gUrobg=N}ytQpv=S^JSavh1XRT^r{kpfTrhF^u0un>^Lu{KjN2EQu(KrvQV%{t zYN$d$Ri=mE4eGzxjvdthBCB&iLaV|P>|jAs8X^%vOp(;N$Vgg*YD+$-odgnb>;MwK zAPh6SV@4{O0)t|-KvM0sIz!O02UT2Fb0SF67_+g>kxPT%gvHTARu=MPRUwSid!~}! zlb^Xaz4(+3c}LS95=ssjf}+IV<%YMlWjc-$@$3yHiPBqHwm$Zfd(4yR7|MhO_MDJM$TeM7r6 z#m~D>isEhu?uh5hS9*TUc%bka! z2Z@GoE(I~;q$$3j2Q68@r=^re;w#Qy3g*x`s;Z&GjEJ$Y97gEshQ`7RzI2nkD3KCzkS&N_ro%A7E%gH2UKE1QmGT4J~DNVp=16Qa@9-f$hL^v zoVa`kpIc$^Qb_7LO6jW#L}Q;#`gp%zZqI}8_3?Pe$zu3$(q*0Bug`;UQD40#PDD^K zNA77ds>NKp9jKi+^=0UpmfbUPcF)i=t35Tpvt{U+RWRXXlaXgugP#-Uy?tT~$C)X> z5D#c@I)$?=oc-ZH#j_=xC*dD4#-o{vf5h4w{Zuq{(SOE&is#7awc9AS0j`Os809eF ziU0fc+n2B3zJL7!?|Ta`H*Po*@HD!l$#9*+j3UB#hYdt(*oEZf&myl8hivkRQbGgBtdOg?#L3ha@l$ETY4 zfQ#ucqYvtR^2`+8)|M&O%tyjZ%J;JTy1pDI&rBtGCcE^$IDoCTb)ct1X2N@ zWC3-J*r_w7%mETT!oe3=vg7J}T;aHj(Et!Puhz%t0eb4=9S8uK6f~uKlC%UfC%%-1 zn@iryLBruZ#L7VvRSeIw+=Vl^xfl%qqJzp|W={Ojs)=I&8ztz7YRWFp+@ThT!yZuy zaadWrqm!*k+Kg7m9X2{yfN|WoT1TN_SzG12wj>VHtQ>TAoEDCAX5r1^Imbd7p}3Ia5HvQrh^(;oN=}D5!4?)Nr7=Wrb^|weZeu!J z+s%m%1g%6$j5d;NPuo~^j%Z#AUAkX0^cnf}eO`;t$m9G0Uzv&x(dBk4K0+Gz``cMf z`W(hGWfD*fdLc^0_LW}y9FBjQYyZTa98OP-DE!_Hjg?|=G$%UHz7o3)=>ur91&@+bkfcAKFWWqA1 zk0@^|nH;05`RhM^{@d5OH(U+P&778z+^l3|bVVR=*P%F3GpuUpeqF03DEAZ~%7_?B z8k7Y2Lzh;vq5bm#Wob+rZMml?b9h@D5mExr#objXWn=o_h)9Vm5mUf0Z@4nuq&1pr zbGYBerczArR0(PPBAi*SW;w+juv$MZhj+B~9UBITvat0ky80%7(P3G1R_%hq)P7T> zUVfBITa40R2z!b$j}w9Bk_gmYKCdK~*(W8)JDRJ`k*jDLbtTb;&|uV-5T#X?2xf1~ zbOIRDSwmEDB|!jsTY>`#GNhCp`_bwBQGTf6GEMJkJ9?cy-fz$O9EoM2Mi%^3tK$qJ zvg7QUzH~yhNC|`3TvanZGv^$rL?v`112P_eTNOKIP)u6l9Ik`}xld(K1dRm6j#X#- zqJz3*A7>2BC1dC%Gu7pd%;sPth3XOIJSuCzA&=o$t&JR2%m=ilX-PO(4>62WG@{wTs7}CbIMcp=~sF=#HTSsxZczB(;ReA`!px` z^wMm}`N`TsyA`u!_MmU(93E|i(9U?t7g~X3nSK7w++mHxl1Fax8I(lZLL<7|5>iUWQ+B^r0@Bt|(O;KAKJ{tlB%dx) zJWvBERUeTPc(esVyd8ATvKFY5%p%Kk;sf0WonQzHlY$mGCoYLea>4AM3X@!r`h<=- zK(H;r?@*B9Q-D)(OWU#_HK&Snz2Dxapu=z!CfR>$<4APd{_=@!bD;1v*ZGO1DfX$x z=$u2Na8Yt-x?!P%i)zEjc{d3VLJAv%rjAStj=IV#K#FU1=R~`)x}jB{*3EfQ+nv(2 zl5MYRI?xH)!d231dE1QAmR}79+q7khS+?XI4mwe)N5%g(Sfv|*j8-@cN88KG8?6+_ zeYqN-o&!m#t%+Cz$(I&S@MWE5h2K=|hBUOSYOb#yoTCg))KAmIl|)O|WeD zpF5<;ne)OjPF_j)I;v=#7)$uIV23_;0#1T&Z#eOpVKb-n9yu5}sqceXjm*o!he+Q) zioQ?c1tSXh`(n{&i!>;LhJh;k)%9AUgSr3? zq?@KlI#8S!tFc?h4h>so>|RluVLOCfi40U>A3{GaI`hk*QY<;3L|w9LEv8TK+Kk8R zfEY`JaW-gJiL@!_W-^*A-?1Z=K-sYz<)0JpYhTX&c(aU$xih*SlWuv}P*(=xEvxni@= z5Szz2EOW_W$=2tnZMC*Bv|W7#N1op-S2A46E>(i{#@(v+xtV{z;&-Upg~4^24Shv5jwf>PWzJ1|Ben z1u-K#NTwg;;}_bd*vao%z86zb2Cr}V?B2eHo~^XB+;~kf6HpyII& z9}U~qdrCQm6B{eX_V6ZzVl2iN%w7KizI5&~3Um30Vx6`Pr6+>Rn_kvc+~!3O+;Y1pyfzsPK-!zn+lyCBc6>?* zYiw&mS|a%9+zXl@-mIG6V2N!{tyIJ5JiW3{XiEnBU_@3p4nJ(JTwCDGvd^dMfGGFL zhR`t4mJp>?mgwA-bOKCbmI6%N5)bF(Oe@MribC{CwNj5{pzUjdScj<+VNzZ;(@l-T zFi9j9D-ta%v}($s<%6oJc4?evmg4&X)lkO`fvIxb@U|wl3ci;(J)W6T~Q- zwRJ)mG!d&;Q2BfuIjOLYLQ_pP&?{Mn`ff4$h+U2w;yGKX7vdFxDY-L3e7OD1EIx2d znx3Hg5{<{=#K%692Bn?S||+R$LDMGR`ns6vOD4;c)0}G%-XiS`@XYCjte2F+V6e zsKX+^bw~U3NnsHsdHx9R5=dChaFAl*0hL&XRXS+|L)!5`h)MN;hIEY5DoQj3D-+#pq!=Y^ph2sN zc2dJ6(TCu^ev};Nuq;Bbcu9=bL0`4o8RD)i!uT1KfVi*Tr3V!_?n`acD@SGg+=z;s zBQXwO2e6m$+a`7uP=xwp9=k7uphEF?dEp&RaZ)HbU<(ZtV+=o?gtxV2iY=1^KuPQ& zXvMF|$5||kP%KU{QuQSWd3akBF^mc0XHbN3r1&v}54HA?BA2QdQE|u??gFaD0Txc+ zP+*bUd(4yff<>n1tF0R6P-UP1>?6}TjWvx!N_iY5Sy5 z3tg;gHZwL|P^;#o*rD2CVM3|-{u5MU1B22@^kJ)$p}DbW$bvLdkYJKU& z_$I&hM&VWN_8E*Lyj;XVhm~V!1}XJux2eFc%19{F9U{4MsLJP@TccGIHm>KW22_{j zO+I>pHn&w1J*Z3J?SPtecr$wP8wBBP?T{Pmp7Oq_uIY3qEnO;wX20Jc7^eX(<<&%k zVzfGG)#ou8^(wglo!QvzkAk_42^ z-`OcNzN?BO9HWWw@TMHeI>MS7^b>6z(;Xf^>06K zzD(O81l~a+9f%i>eiR(>s#HqvR8Gp*$zx726@YV)swsnB#ujhgcUFODLO9Gp=Up|J`bX%M(1>Z<*g=%9|e z{C*{+nv%1VL+ri+&U`+^s42XtX|5C0q$7yYlN&+AC~cW!U%CpdY|wOSU%ICIuP~qF z4k)dG4$_oBTPl?8BE~ADs2_gZXDQpRUWXVlYsrY|ND)tOe_9+c6d*n+ zW*sNRtR*RCP>fas#}cUI*=tehCrgNkrOj87w{IN#A(19zn92WmIkdTdo_ zh6(-VGxthw85Aq$DJcgk*+X69^>(9~cq7US$v{X6cHgyz_ck?=YDu{i%-E|HgS2Z| z8GfX1s;qJoP^B7vkxPs*I8*+^Z@>J< zzkU7o9Y}!2g(+cBfhcAP9V`FnxC&vdXWGNbmEXruDY@bjsqG&}e4OeQNXag{a zY6vjKs6p-~Vw7{0JeaO!xpTn=sm&rv4xui6Cf$n2TTC&F9`HLI>a^pDT`R}qT)(s% zC~1C!8cVx@MA?F-+~K~J^Ln&zdfVo3XNcAG_QOGED9&*Pm)0tru*5jiXvLw2KPktR zL-k!rNWjs%z(h zw?!FVXhFuATD4s-919(&3Ui!&l@M4G zukz|zk_;oz=eewbywbFL7lDmXXeZNG&QZ{YI5QOb*PpCIxAUwA3_%g-Hy^^=nwB^L zfrlb_l0`yK*){*Jt(t>jqz$hAF#v0xa1 zK7D89t>XlqwY=0M8wryP)kzGeA8EsT;e5B@V|T$QnG=0d4ivJ<=?L@-V?L?gLtuHk zN@o=Zd>N%41pe8QC>Fyse2?&jqf` z2~AYJlTPDK2D$w5iylmb7h)nmU{duo#z{15NuoJu0et+$r9;R&KGP5kCO(;Fk9(S?cE zrb~0YXfV|yv7DHpO>y3_55B|W*BP8Xbb>`JUX)4v(wvje&cI9TI{+_!CC>R7%19+i zqwB0Zt<;mC9TGrHE9K2WT@ff=3ZBTC_yGpO{Gx~+UW95;rImX2lv6Ua1FQ0=M0#{90IMlIrhS-1&!H&i(Ic#he}(4%MY7M*FJD!sFYxU_uNU;+0s`1!E%wamSb9;fNt97$Ycjq?WfeRdgJT z7r8qLTcJz3Gsj54FP>?EvAlyOhb;JjJz%_y6I3oGK_x$PZ$S3%l96|`^&Nenyc(q| zi4MqWxJ&gVL=|;>;MY*}z!)yav2g>2u&l|;_$@k@@)n)$@_S%R6M)w3hN9vDLohIk z(yFk?d1fboF;Ro?sjHB9Dha~OchfyvU@S=pmq74=Lu?cnqx0~)=F?ST6vm1q+x*CV zSo}cjNwe%$pluGPDNrnrE+xaKE6Xq%(>$sbiFRaqXe`MPS=8;uV*8eD2h&`*wnR~) ze`B4t4OaB%w0*eEf^d{Z#5iw@w`#JZyT%6TAw0XS z6^HcjQth(iyLHpYJEnx5!~4M?+o^+$h;Dz$s9N|dIyq)ca))~`8vR$SI7{x zvWYkfuF||Du8@DT`DOeTmWzy^o8|69Gd4O_?VCrp9X5v4a-hGf91UZ&*T6qMGLb}d zQ13N%5lJp3=_8AX`m)Mt<3y6?Gc6tGvWrO4eEj7N`w^7LT{?-eZ+G>VP3AO#BW=c-v>fB$GTYJ_qT-Z!pnDIx&`2HC$)dK31*e-8x-A8HP!k!#Bkj z=OpxCB8-z7CRL@$X*S9NCC#S0{2okv&cTHoeO8Bosdn0;j>k_ zJ}U>`w_}?sZKN{#`85~LBSUy|i(%~q?{C>b97B9*qe(Rz`d6MSxkxfdXH7&CII*M=(jW$w1+$lz?=tUP! zvdMS}MeLvm6febC<;5r3TsWnqhnJFNlA-O;i!PioqAhS@{VU6J9D*j(6AWQtQd%PM z7i=VA&O|nkOujz^~3=^Mda|v{c z-PKol4@ef(p%-2Foe|msXV$+yTjbKACd6!Vzz`HB{$4)3t!ar9V3KG}xCAz+xPl3v zL~l(F2qVQrb@)J4fpdzE_JK1)Wjc^FVTm(%G8n!6MJpZ*gVAU4T==~vy__XXkJ=Db zbvsUrxs0_W>E88AMm%HoQ{;hSkjKV-l%|@1^26pc zKXC{W2l@~*_EK>shf^~gotjh|=G&XnDcF|`i)&&O?aPI=7<`UjIt179x|Oa3BnQs# z^Ye4s*EHg9HM1FbP&2`sr1I#>RRZY`|MLCoZ*L#(^ZgzNgylvWnLoe1)z>IoIXx!@ zyaSrj0nez)9Yq(rG*uM?-Zk896i?R%wG_v`uX%V`mR|+D>-be9*OGlRC`RjJ%_uOe z)6mK_Sx2ZOgZ@JNSRMF^4jk3cEk>_?C@)RO+QL+?J;1thYKRUbMFE$exi63`*Knfmtg^`RYM5~LREk#z48dR`N~_A}@V2Ik zPRJ&SIs~ow`7F$fHzm+UIxm)?R5RJ6GZ-I(SUDJYAy{+k_XTT)xZUGa& zO~*+$%1FXXIVF~Xnqz&ER_e(*LXsI2L0hNY&A$3Hn`>Y^G>y}RiRVawi9K4na#Dx3 zK&TA{m*0g+bPa2Wq3ZX5At+4Z*5Ass?ai8&H~}V^a&=9FP9s6!Tr?%nMmjH+VYg-E z7$Tlr<@%XHKN*IJ&pWwtwnYynpsl?8;fGr0U7Owu^NF$Cv5<#-h~ z5rdLwLok@6PMVR4GvLmENz5V3)#pWBInAZjMlb0tg%6-%U=}z|Z@HHAmi)}U6@^c4 zk#{tMLh=D&1nu$xlCwl-RYAeIC0#Ijp$ojos!_nHBN~jaK%fUkpEh#skQHK$eZUYD zjDBIo$#5rtF|msPM!!|TXVY6?6po5kY4vq7+R-A8|blo!s%m4Q6%b&k}!Cjy@gZTKk{P5#H|N7o0%*{&szzXZ%lp{Num>=TBfu;NI4Eum-@f!U^7O{s>sw4({Ud%yul9zLkWn zV>+6azk z2!vv#I&0w^m7!-=Nt_dqKC!;QxfYflM}Kg#iP{VQj*(ngs8*%aVKtcj?sUazy>b@P zw*1XTdlRVgK*<_7DZw&hZ@QvQUOC}Lt+8a;WVdMrQXa6-A2zz?%$yXk0%2wg6u)3m zy>TlN%k>10&(67WDva{}lJ}D3!l?cFhW!-u#r*8`T(sl=GZKaB1Wr(!B8`8wGu++kCtmDo|xN>H)gf8GMQ zqFH}xBbDV<;*W4KTEkTzZ&gjkZ%#?Td6eZ?PX+=UHPa(Xs66Ht2m(#rbow;6a;X2B zM8&4Wz(ju6d`oG8TX_jee&jw2EV4Rh(Y*MN8X|B-B!zSB zWSGS5I<(?;C~kZdT^lK+;uzb<#Av}}K?2o56;;;2e(_e)Wx9?s4ionj_QshWU6^=? z#;kR<@a=a^AJ7&ERl+$IkkvUbp*2+uS4ZqXVG=h8);D-B!OJFN{uyB+&IerpCb1AR z5^z3?GE#ZhN7qGp6=o}s8)xk#zkqiGKsljko-@ke}Dt#CRFlXMJiV8ZDSJ(viS#2^|lu?@f5IPK=9Xg5b;;?o=C z9iM3k1{0rtbK~@}6S7I-iQ%Nd-@bFQMdLj2rpOn`UhsixQyzZURJuZ;++1S(ss#bQ z?ZImOP|4{|DO4(2Q(9e1l5sHlk&<&hx?uD|7YtD~2{@NS8{(B%)H8%nZTkfNgL|x}2fkkwM*U$vs=4^`FyL<1`|@T(2SF44B>m6=P6^jb~O7JPYZEVsnH*@ghBxWKt6F&q9b^9TO#*Dxvi^$VhnI<(*I zU|H+@{?Z#A|5ikS6xIjU3qTW3gL4#93h0Wtx4 zM+G&#gEy1(BRZ&ishrE8d{$^FMlO1aZ+w~4b&@Z``@A^QQb3tYY8&8T5wTX5?Hx^* zNHx>}Q|KDX*upQc!`s?2$-e)lC~RXMnhsj>#B@|~weRPwhUg8z^&fuwW*$upC-IAoUr^#0-Mzz&8phvO2 zJ&n*+jM89>DzR|7jy8l>?zEQQ!(+N(!Wv?GN89&PXxwiIjmK?1ps<8rRCC%{Y%mO1CQcyf#4UIx=4xb$$wr>V14~1FnEe{dxr0O(H?k}=@$02CVu23k0>&8gV z_!l4H*LDv@f_WrgS$ncYuH~qbA!jKhyO#uou8xOMm}dP==;O1*T@;24Yn1kvpTGjO zd62hnwI$AshccO6TVkjzIbaEjK)){G+>jFxnCTz4#E3A?!0)9{2rTcV$WHo~pQ?Ew zK0mMQE<^<5OnVXNUn_CrgpY*4lIxKd*OG)Hu-t|;X6Zwv*9^Q`Lnp;3gygT^zHIOO zVYzXl#|bL2P*6IFy=&`;yi9dkC0#S=WRyrRart#J;=}?C1wRN@TLC_gt_^)=2=uR1 zxbX`}vfYbjU025)D5NhML+Ne=ya)0_6AGPVbT4@hSyYsSwl63mJ=PXNpISK{rX*p00pIQeZL_?jPI3n<|SKzY9NNumU2%7Ly6jHkjz34m+Igpj1AjI}84~F4mCuV)&hfSSpw=x!us!vhe zImJc!f^aj;!}rDN<(@{=?5g?pOiuIeB@HHfdP@=t%Hk?;QAO>Ze9*rKO5scxrEND4 zVVuu&vhL#?le-M!o5!{^(8Fa|p|kLjlhq@(u((vlh_#sUj}AHMc#cd2BzUNMvU=yk z;~Fq$?zJmt;nhM!N+8YHoY3+Uij?9*M52S*VZyL`GidgZBD4wP(m;xB{N2M3o3GIp zD31NRfuaW!f0q9~L^Zh|FocCkX$etURY-8I)CuV;-T4BTxW|I`Fm60%Bw>yi!?%32 zV=mixUh% zVdB?VoGWz#OcE(-i7Kvu%6TX%mdV8!rx>aFeAb;4RkC$nX=Dt;#3!rV`3U^809GS~ zcbWb-S^_+(_7!IW^ibG(jUDY4dB);k$*<@Jp-vnW?E!_$Jx%nRN{W*-POvz`qo}V! zLx-Pqyt+%5%RnnGG4IarQc-veyn@lvikk~2vep^e6AM4XID0E|j^oka5)Y9S?1(R> zb+*>D5fON-TECne(8XipHPs%A(;~EiUx{|yz{@=oBkul{6xjRFjzg^H4_KVy(J$h{ z+nT}ag#0KN3Q;O3yNU>$_@K;Hj@%y%pNIkmkDUqoXq|GxzAM5o9@Fr>6^U*Ddw?ma z5?NH>vf6EhA2xkJTOc%A4c1%Xs0)}z6dEcc4;X^MM3h!L>1Q_!#kDt>q{}pnFK}{1 zt5zy`5~UARo2c-^K_Bn}@m?SBKhJ;fjMpbHz7oEN!I&oRtpT=nRZ~MfFqU_j6ox>~ zu&<#lIR!@Ht(*i#|1Og|XZ@raQixQ8l4wIPF#61gJLjdHkhAikyez-o>GE$gdh-R@ z<|9B+r`Kci1-JL>Iz7LJJ*|&-d@MIys&QE`Nq-4$wtWvf9u$W&!bBpEE#B?FNf> zD}7o6f5ps=nmQh|Ax!1}U!O&H=acy{V8LbZrt&VBtSC(6Ry#U=jm^+AtE!ZfESk@} zCsIv4zc1@)hKf&|aX{e-ZN^TBaP08!m}ZgMz_Nt3mU|*o{Fndo<3IgB|M>dj|Mg%0 z?{KCV7xngr|MKVm<88v^gb7dK-izkaRCtn$79=QSZH>5pP1UR%Aw0V&MhZ)bVzC{U zb|uhZNnNoty+59eu)AFSQI!asC80G_H2Au5b*d=v6)tbv%yd=Og)ySf)R7L|MoE5c z$M2y@p}V|-W>7zoC~!}=d%(y2h9IX;XfqXs&Y&2rss5_o;;fY!!ocZ-A0hca#B2o=$i99h}JW>Qv6zJtr7Kqr_MuCQ%z?H?y~q8=GWD zP!gK~%iSmR-1%J#+DOHg@Su^ZF9O~<2PauaxCzGhTA5}XCXE2H>8@#J~5kdvSi zOb!@=!97ylkc`g0_y@T&s#$bWl0WBaj=K zB6VTnITB!Ex6BXT*V7g_uj~a(-16eV`8N?JM+`w>;+DViwx%VH!z8*kU?R(}eSvdN z8Ydx-l7^Dq6+U2h=JLbl0<{mE87h6s$b*xRvJZIMidH-r2BYZAM#=|gdGx?2WAP@v zf=pshLLQvGp$t(n2eNTn%kPwSvOLG__z_@=iu(=0z?irMdI$9AFvAX)cH(`|#qtmp zTVJ^I?;&|7Auriy$4SWL9T%TJPeRt|@{Sg1I0?bmb@AQ9pu=zyl5T`XvoKa?*y+Wo zbY$s7xx6RJB|=g<^$^uNUP7yg@-*Qgq@@}Nw%IK|Y(^wGEH}nzS*7UJhaXd?Ij8u|=x<1N{gsB9K8JhUZIYb#LuVkfRr1(6d2WQOmkW!LpvciCrYX8m& zG1>?r`d2}ck2`T#x!H|;Z8OAv}j4mmhkK~lZ+FE4!b)m>9LfS~@g|lk- z^T9bp2__|dDEnT^Pbf?Z2IwQ`pjHiM%yePmITB#rMaX&)Lh;s8N`A^x)u)I39Z#Hgf zQG@4|4LMIU%Omla@K+?4;IF(T40NWjS@sDzj}G}IM#2MFvyo7NE#RW6ZNuA|syv~( z4gn@;C5;hUFWrinrUc5EB_Ss}NGl9+V`3D}%0aj8DH4|SjIIbY5*8%iN4iMFh{Opw zJph-)oGdEf6h1YLDQEk#&83@m*!40MEimeB-?%~N+&AL;p;iB6oDlSoJ6ukxhcY4P zkqAM{BaO`(O$#P%ylK5fJiofq3zp>*{GEpAFbok-NeL1vBA>r9wjYs_a00J zwUZjTi7y1LM4pO~AY9?l)QUDzv5g!Qqc!|p<&!JjzNCDT2qqhQp)0{KO#E9c9(*vr z2a|FXIg5%tw*?75EmZm!ii$zrUXY}NKI;y&;yu9*3?!nnDwpspc+LPxtoD{iVgXsL z(1hQbuP85^C|sZ_CparbyFf8wlOMU;1^kvd=QAZ2D5*?cNyee*=W@C*`ZeBm)%vVwZo$^Y@vq zK-V^194SY$p|>LkUEyRX#}{d2ZU0R+uG?Qym~Vec6&U`Ci-#KG@P#o1rwpl~ijp#v ztx;bs@!+%{GEgKJik2Gmx6+qi?S^-{yfCAPx%+}OH(450E@??y9U_=0u9aiQ|kY-)^ ziQa^%ER2T1qhcySz`(c!1{(06V!{J5t+X`sYdv_OAH#Z8Wj91u`v2d>{J_rXGD-m@ zRU?A{3aILu@WbE31(rwRUl=oUE{a}daSU9pG7J;h$I@Ng?-fjZ=E;NKQ=+PzlJ=2x zFcRdQA*^b!JUqPV9jUxYcTgPN@y+k^ns2fc9n>8H&cV>C@uI*^)r^g$$?Ox3(NH;{ zO^o9abjZ=ix4yUu_L*wcRin{sBe>99kT!57!EMmnQq}mJMs;t(SwHwqIE7{L({PMw zN{D@y#c78W#xpHj; zua4Vigt$Dc8ilr%hds0-IVWx(a^WoZbL%ac#$N5UxiX{P>OM|gb-h8hjVRuU@18_C0I-|V*Lm|Y`TFq zLWsJ9%jZVGd!u*QVE|~E*2pK=f`P^5-9*bbwR8qp;+7#UzmA=p`J?Pq(tNV*_XRZ> zSTNCyvxAa>N@7q~ieaz>H8|R#`0awl!!;Urrb7Uyr5j{z1aE`9&j@&L^bTtXnomzK z1Vu{R_gb0g9_0k2Bt90j5>#9T&S7EjxiH z?;%AlmN6*+NU18GVG0f;Io6VK3)!&%$;t;=v89rju9MBTU*eqn)1s5TTrn0WpQ|0JETVXzyYu%suc|4XI)OJ zP(^@{Q3l^_F3^%({W2}j&pAxf`i>5+e9vKB z9`CoH!|ihpkMvD8v@hbP8)DA{)fDQ_aK;t>ia0=W4c*-Bza_pN{_?k!VOYKCzQt)8 zJT7br10?W|c(k?+jT&?F0sHo;@$;cPFv)=dX#O8*(jGU2#_t7NQD|7&M8R_K|C%m( z85KcBB9+{VmK>prJuW^_m5N`OH$kc>gXG*D9>y~c=#8iJLyK z%?%b2;{G5~hhgF}jn)rNhUmehxaQ`GXuza872%u}Wr2dT@_goAn1tok$A_Hnl3}vn z5DX@X?_yy>tDZAp5_d*0x}Rf*uere!PK;$}!c{wzk(+KH#VgyjaisWR?7pe5nQOa9 z@q7tLG0X|9iMoMCO2`3&fr4)n@!sei#w$5XC?Vy5At+LU(&}(dH*qH*CEaubt@vqn zh)jak=^uXk$mT)5w<&WH;l0te4siO8@@;f&wNhm9FLYRZ6v;<>G{bA3T{we zl3_5q_vt{S4WmtwdSEP=B~MXdh-&*6e%O4C0%I}W@O z`)^|DDn9ZHhSWJialz2jYk`DnhsUX7$4}>)M`Ih)=_65laAKp~ZyIO!Oa;5=xz-U6 zsGH&ywMQP_j1)0OP6eH$FM^JdIOtvP*}=9>UgET^qJ*l-ojA8A`hm?-Q^DzZ%II)< zOM;vV&QRYQISQYj6om!c zYIO>e7EPHtrOG6n9OD~6qr;ez-!|W*j8Slr`YPyb9b|>IF*uDWheWT_)(Q>U7#5JF zF%p-_1_@d>o&m_XVOXaIZHW_&otdK7?9-}NwqgbMz<9;V^nTm^r$7Gj-~aO`)EoE5 zvsRgIYf-Dj;2ENgyGNe*7h0at=1*UL{_^X$UqV?n$(1VN;!`3@)dNEEyQX`S5+pyg zJ9cv;hp zV+fiGx=Y`5rT?zt8}MHI6?YF-DKrRbOBH>-gO-vpG%L#X{I)WpeF{25Um3)Y)%o`n z7&dj<8s~gW1?QtL7x806p0D4&Y-;!cMjNVO4yZ)AtWk+y!i1N#trTX~p@5=j9Q2h` z#%6*?RA~ho&Z$z_Xkq^>fCT{g}3TaoD2M{DP#(5qaQ z({EBX5>nG(KGZ59F6)yV5Qk}CIqtG~M0)aBH&d9LZh}S|<-{m!o5@q>N`vUH1a?}2~Evetgf5C0228EmQ{?u@phf$UxNSMBh4 znr9utKz`atC{eyDl@YuuE^6D27NghJKD3OExrS0X_DkBLav(ukDQRV^9lz)bWzjIE zSbOFc*qWX>^vr5=&B;xlU}ltcFmnu$C}{AH7_X7MkwdK>X>Id6Svy^%YGi$xW0S4D z6Ef9^-o&XxzmK{Q(yAe;X`KHv$;COQUP0|)$4MrBII8f`dS+Eug&#IC_X+qw+}%EY zC<@v-8~6sItZe&-{a(eyZ-W^!cp|6tobdb4zkUDnw=Wp+c>8!?uJKdQA8*^07*+3Z zoB=YG43KG5n1B5Ix3BeCWfk6r*dJ3#I_OG3HUx^DGx_X~Y52mHbrQ}%^!#I*QN2SG zw}zM*N0fwOeVZQ0ox?|Qp6g!Tf~Aj)T1@E@n^4V+DJZpJGBYD;xhh@x^)V4yunpi@ zj^cOyr!3iP55`%JwGQoIrma+QvkbyT++(*8Omd`D)PCf-<>fFx}n+pdFppr(;<|R zN=`&Je9K3jvZ#PbwUgvL2n7@2j|?uq2NRzH zF>#(whDq2c4@#m9!C>N(A|}owJ0Y9o!A@Cz4HM3V&_+5hmeuX_Fy!Bbi7;+Dl*&f8npr)`|Sae`{7rq?fu) z7EFhVne$jmT+StVXMRbFR?Ugg zy!rx|d}c~cD>3PT$s5f0;3!Vcx#IZ^xx5PIAIS$tl&gcD(XN{is?$6BT#7l2v^VY3 zscYvz%&~TJh;H)3=4+G@O1e%L{M-nuEYI-?-nf%?)huiz6ZKnOsSTl#Vl3f~l*`+i zDmnuxu`GdBV&XKxdHf!jSuQCVR1G&0j_h&5ZbhNZ=Y$lwHfK-@ZJ_oJa27I6RW5MS zK^G|=sDYHN*m9gy=V1KL#7nw}2U_uS zTi}K?B~V5Z!p|v2!f1v8OL%xwj$|FhrRr6i!(j3GF>_!eG)3xxMKLpyr~r#?6wRDf zp)F8OD5ht&f_V=niELpF!8>>{tsF1}gGm~nwK9!NJRB*&#I?5qEYR2LWIIkgnM>ly zEaajee%xmy+gg)nH(fHLq$mNq5)7lnC!NfEcD#oYp>0S^$)ck7Zo@Mhg z41*-7p!OF&Z{Gt+xHlwjVXWZxT^ll6j&c>h&<1q0<7$3Xu!6dLC zYM4~}u_3kuR*YyFxE=P9dtoB%gUTT|e1jG!p5Z9neg+-cmij? zt~SfvE=Ci0d{xZyVgJ3Ws7l8m&!8urSGvQopDrGw-8!dZQtM*ddz&Jj;9@ic_;o#} zYI*(cZdj5OmC8wh?bet`7t0u}aDs{o0YbVlKDRGUdtbzuIj<=hsH8M?m7!y_>30|3 z`I`MJMCQOgY5GhLJtbcuiwcllw@bUK@xVF!y!kS1iZd(A^qElW8AHd^2h3sdqU_<1 zD|9$#$5Un^mLZDxmE|~bW)@b*Ie2-zRdaIk{HDKTym*PIh1zkv__ZZxiuCYOGDr%% zpt*_;d&Sb9!_S-RS9lTH(J7`_=R`;_c;U>g6U@QzlE#3HOq^(ULViiiC1@pncGyS| zSR;c*Ixnc<9Kgyk6wQQfH0UQ3ESKv&vUs5yKbUd8NEa*~uAQ$<%$#|kVRB|^mIw*f z5JP3y0Yg}rC`b{isIMh~k{d!gFH8LP_y|HLk;XJ!)7nT#;k9t3XH9py7Av zkYUA~pjx@K>=k*#ium}zoNm*>#{%tIX}nUXEg2dwNmWm5!F*PZq2pX3VjYj0>pSMd zvePB5h82lM5UrXJc?abx4OXiLr&l<^#L3l;8ATa{Lu*Rre9&-^ffwvvvHiz>6(F#S zq;2L@O&rj>4wFXlevsm9!W1S;*5IH!rrl~DT%LWR*35Yi+Vmk#onm@y3aoW#Zzx;G zOVO=xFXR+iSquJY_z}pgl(~X4ejkK3zYPz{LI$!Ir!GuzrbZ4VoA+% z{m!5-XhUe2=pja*CM9ocmb_=cB&Xm+)3HXP!9yGAyrLUBW-A9xVhJWCF|6w++B&AA zQ=R@#t z^V!15FC|QtlKj&3QEnu(+q8A$;q8DCN9M}wJ-Q-{!bEgXouw~<0Mity1CyoXu4GXG z6I*@D51X&i7I=N8qIsAu%X3yeG?sP15CkSsY1Ph>YKx4mSP7PKa?ny< ziINS!FXf}NWMMKA#1^^MXE26 zXTL1t+>1qs0jC&l`{1O>jp6BC^9WI8PzH+nVy7ias6B>XK+=OwB0g-N$;wvy%THAW z8GhGXxPp!lRt8tkYk0uKF9+oD{`uUfq}>7*2fp$--}JIs+#;-TGXpg)>hSPD=VoS2aAC zp#ZJs2A2-eAtI%`Wuz;`I8yvLcIdj7rM&JXiwdOJ4!8Vp5Gf%EoP`vBW5Y>N2`L8* zL6PDYUYxUc0#fqf@#5e2GV?D^dgtXYwIkGd?bug*uJ5^P*ow%C1S~$g_X|oXjD_5<)q_l8$jka0i znYABmVR+DH{xgx>t?O<-Y(Dc7Y=$}!6_JMqoe(0IiZh@1AKEZ5=^6Qe=LMR6DkC+Qkz2F}yfmGG~ zIQ?P~9)JPYNNA`>Vi2pX<4{LW#Y+0}gxcG;M4(mUea+KCi%?>!6v+uN+A#tqjB0Qj z&J5jGE2UvVmFmJ#F^_F6x6F}_#uuO5vV?auBg_ekw^gFje2gX58R!&3tP+=!SeBy( z*G}p?BuWdA;n%4hR+)1v;GhK3a!@tQSe1w?uoqm{k}gSK?)UffsmFD_yf2ra!|iL2 zi%(0#P-ZWefGqYezy9_=zQg0*LZ;r~dTMD=-&NasoUpP8n_`C`4hE`x5+2@lexdjb zia?{z^>IR0}7=w0xi>!DNZx169Q{PJdZS`b&N$ zkFl+$>MF~s+xa$-K&OPb1R%K?t24l5j2tRCkngU}Yl@v0EExkk z_CQ87-u+XEEAN|m$)a`o!}iO{!M`gPp;mHHx*`l09&)T*c>=#(MR`KW8_9Cvi#JuB z$8U~lC9>AztMV=Uu<2;pnqHS|Zz4$Bz|J>L_+|^i@~#EG_B*5k+r0Z)gXHo)UFIKt{L|N8fBF0Ox8?o%JdP9{)KM}F zk~dY;ifD2jhoCCU=nSvnW-zvq(V7pT$2^mpzNPu~Z5v6yy=@b>@Msw*gnz^|3U|Pm z5k$j(z~BvLNGuBYNug}Cj~EKWSqpk?zM(=*V#A%ht!b#Z(OwgIXFJNyhT%#~;Z8h% z3wP;~4WkuKXwkOxVgnbWWy@-c33taapUN-_N3etL#9*X2_V|`IUS7k9^Bp>^eSD^7 zd#I5WUx%q;DIc9zCJ0O+`^MWTsdsD|*<5^+{59=l zoCM$Aa3U(MkUCBQLBsuJF`783vR-(2J7UBrCRdag-)s5F3pnwk7$-b*apGYbIH@u% zAD!1Y2}xma{hclZW@>$$Z14C?OHiByrPW?KLRLG16Y=eUlbCl+J{hNgU>f;6oY><8 zd3e`4LYEozlX0B*+z(9KFpufsq@2FbReYSL}rSNn+jrCw`XYj0t5V zp%0uA%us3|>zhEvxgn`AF1aAt{f?Jji4I|9qVVlPrRbDrSusp+&zIIA4uriwV3pfs zj6v}DY6lc}0?lB!cNJCb%}8I3YF|pKHO7S2W1n25p?OlB`qGNiwQhB8(@?#L}=bQIF|iryPvW zvI3%2Jsrm2n-Fca6kCtC<2ftmmh4?cb=1RW^|d2~y6qH4f?G$w#$OR*_$x*QYtNx_ z`GAkH2q5;V@%P!~a-t(aD}K4m2?;It6CR7PA-wzchVt9!ol|(UYn)jPrcYBFarlir zIn;Q)ljEVr=l`sn0?`9$IhkLEnySv?jE|W0!ou#wKON)57lo6O<#O0am9x-xXTnKIis_0ljuZdZ zmG#iAE30r4sZ!b;~ z`NQC3i1M+PjGnx@mL%E|3@1M2L*CZ*j^jAVl#i(5YLgkr1WgH4Fbk}Q(c_2?PO6O{ zr&OeTB5a;PKhf6F16_LlAu%e9$M72{x;XJL4V+XvFV5O%?Yv6%M0QkAysDiSpVr^G zii%UX26+Y@>BItl&2#x{Vt$~@>+dl-6Z-Mg?SPNb2;jG#IRWp4ic(O!?YxBiP(=Vf zkKb}Xp@tYF!u_fZ6(`B403*Z`^UPN4?F5+BzZhcWJfIY3N{&tjS4)!dIOFqaRz7~+ zLufgcon?h3s{I|GvDTIdW!+hp@I9Y%OHQx^L!&6I#sY5xAJ4~`TeT)4ipe=)nywi} zy+QbF^Ok>>NZCVX|9HP$mM5Ei!roXoM7NJS2t{E58;K}R@ou3;zK$LSmU3fErhc~n zCa3A!Uvg}_{UxpS@K@aCpjDJl@DlSM<$iP~gtOde^C@8$PUwk&4L<&cmxs-^@poXz z?)~J4P4gU|z?YFkWhEJw#2Ep^j57>4&d_di3ACH0Tzakkge<d5_#@A(D3g~V z)=#vFFK0z4yYLG(Khq}o`krVWolSH&@sH^3I9}M;X0lxTj}_bO!l@iF7~^ao|M%;+ zFJHfX|N14UWgB7%xcHN93%y1Q$-D7<#5X z`oD0B$j~z@eTFIfL;9@dvo4$v_8EW#KTSLsffYyu=7fcW{~CLNLPo;BV*vqO;vccD z0PzD;?)Z1CBrsgPinj4Qd{7lrX!v)`wAE17h0{}_T@bbZ^!4X2zkd5AM59Z|O385> z0rT>PT1XDYF^N~5n#%uQ(_o*VTuh0{XJavo0xh^>0T+J#N4b%>1OkW66upGCg|_+| zJ=I~aJiMu$xP!Qq#E`7}mI!(;r|zJf-XG781BwpdFs5eWqztW^q6^ZMtE}N2)r8NO zVptJ}6&SaxJ}7tLB#sj_rAfy|Rqp;z?9#TL?6oc>r)E$~kzEvh4>?hdk5Ok7-pSTOQtY zmK4OwQT?u;kZ^B|6N?99oI{+6i;VvpZK3N>)z;yM&6R7IgkWo)xz7UY#pg_1_-!pG z7=pn>lvX9)A?;VutvI!m%nNto!0Fbor)FL4Q@ z_)U>I4em=xXvm^E4Q}~i^EC=4GV*>FOrmSp;6P`@PcQ_9i8~f?8DbS2fk|c+Tzpo+ zB{X^mjdWftL;KWPXM1>4j%=M*iu7Taq-M7fDNJEEMe4ysmBuj3*U;^vuq zvxz@sF7Ifr`UFETnE3RL3uj)%Q`ti>iF+b&6kJ~7g)=`?EGzHa$nkNXk?Mnm@bIp6 zguyWAC*v^jV;CRO@501GG+uRYxzAe`W%i6=j5DV z2nNQ)FfjpwQ>jkKS&1_R{P?X3er2{dyU=yd(mv{NZtO$+_~7Za_z?eu0UQ}!z^phv zBpo!kjV{C{acF|XxqGxjV*0^8IG*?6uNVjsrlSs^4R=x>w>L9nr~{mmPLfrEk7ibl z0M0)kN8S0UBS27!mToH6h=s<*BNQ0l;|vuA&ytSvl2mouyB-DL6qba`lD*OuK?lg? zeSTc?*XFh!QU0v^#pkhHIAcTkLCM(2g24~gH((y2$ZCCa!Djl^S}%Bd`N<=`P-`6& z+BkV*#Tn|J9#H=E{qn#dQvB4PQNACN!rPiEN5w52g`gGxu#6MiJ~b#p zSO5I$m+yand%WKtbD=o(P5o)UzGJN6dVR-N9Ip=*7JU-ZW%vCdgUW4AK_UJbryD03 zTuPF`C9!?>VW1kG86p>4gpU!+ot^~afyXBoT=+=63Owb^b5_mv0owVk8tUN_%PQ&! z6Jv0_Wvch0P8wvk4nTuo3i$^#&!SG6L9{Y$d-`tFDS1a(>D(Vv)Dc(S$`zmNa_P{H zoa+$>B`|eYGsJ9(G2exFeN6Az%EhVC`6H++)LlM9>B3nltj_77s`N6a&Z*jjoSdP& zy}VLmRGWYGXC11!Q%*(6US2j#dz#SkMU6V(A0N-UCh;nalaF6daVf8-IH;NGTf{Q{ zV`bY4_BH_HL2gN*>58S*rT1q>Uw$FL2{Ne^DQ~Fg$|TO5fGoY^I^JjtFtKe{`C)VA+5+XqwwLnCV0gozu& zuAEBIgNdR-ByHi7!*45wQ<8Pc8&l4MNzg$ZSHpOI(_y+W3GI2K zEx^P!r>~ruqAhS<*}Gm7`@q12lW$Hi1cgaF)37q}{nFv0OS~_@#J>^c%IPlJNaw|} z>bip~r}AX$gt%{fuZ2`nbWQK;N4{ZObWp>DGfsLiQPh(no7hgrm7m@Z&~2bxB0oEvvSwoIHW=*Jy7xh)u zE5H8e1Uv>e4{13p-*LLFAR055BK;&!Yk`3WQkcj@rtt8l9LYM0Bh(W?N6U80@ao^N za^t@^rl(zQV4yJWobi{xZ|V>_Vtzbe zaf-*Fz6y`wZB1pJfJed5wwp}DM+OIceqWhO$UB3j12-2CVC5L9UKGKp=P|>0Ow(9a zBpRxEfDz`3(H3xNTlm*;n$K0ye0t007QhFlW82!UdZ;&fzz_^3KHcZa>1rpyB=ORq zl~{0Ft&I03<(d#<_?>C1YT=P`=8UUd7w2Mq;-Jg+xshvPWFej+sUnsl zpqog_uZSrNjwgt(i4N2Yqw0f2w>f{AO6Jb$tEa7On+G)AR~salUSdoZ4@Zwx+0Ccu zhn`uL44k$x^h`^LxN>I7$TRCB0?v4HzBZFJ*)w$Yu_~Ea0W{%+5y$Z(KG!r(kM=nL zyIvFRVEc%VEF0U@y35Zg8Brnjvb3uZZ7RP&_s;klC!oCg_^MFgJf=@jJvx=pW(=I@ z%_1V=-?7H@F59_m zU@YkmSuPY%Rilhk8I-!qk^211ed@+HH~sclXOKQ_#}8<|E}))jPeb+=lUse$jQ_EK zx(bEsC1;EiboTf*F1B#;r*&7a8lYzj)pI< zm*~1+2X4;;F`sC14c~xmiqzY}gj+)5b|Z@lLsX>zzfMFOqG-3}XYMwHTdZG08QENQ zihBnPQ6sRhguj^#Z)?jW=f+16nD6+MN3#{pA-?#13_iwBwg>wcDX|ZYNE2-Nr6CFp!bdqJEY+PyPWVYMD7ije zU3B8nGY|P>iz{afj~OdZZu z3_d0Er|SvQT}tUc@v2r?ejR)`IYb%0B!^@%VEAe$z?mM}@F7tcXWDCcpXG7wuohza zFXv42>ROU?;Py;9iO^so5uXt6rT_A3hb$@#QFSoK`7PIy-;$r%T7IvCnYvka)qF(j zxFKo;7M5tFr>!dJrvD=djHS>TRa~9Og=e=Va5|VT+2?rr$E67GkMz-j`&xD|ubIVn z75r4IJl!4$@5*@M_FuW}^D=9>IbuCFcaLef}>a5Z_=T~$JjhpZROmzlB*g`{o*j#xT@NnVI zwqZ2SY%RZsK%X)p?`Vp1f+1=I7M5^(A7b{lOtIA8kHA|Xw!tZZK)=(`p-TS#g~sx6 z$%V#pQ%9v&3727YvE#3c%Ud`jZR%IW7a{)`7sMyKY@u=EB&bg;G;r9B6ZcgX1)JiG z6$hO-leCN83X$fb74C$zU>3a(cjyIKIff#iaA*ca=tRC}#!EM5SdlguHdUkj!3#<7 zhpN!v!{u7tC$>{>Y3AH6kslt&e(H8W>vfnb)b0iud0SKMvCz1cG_h_DR2_|5BHsWU zvGr)Q!nr2O@Fnvjivhz|g~m7;<5rR}@-z40Y`rBihF#)Fg?S5nq|r>0ma(@r!?<2PvvdxpeuWtTOh=Z z<{_Qz6R8MVar+v#AqEQ!18kKSyOg%Cit2Gb$xSE@XBpnFsKPtiN{*RL7(RoNP{iRI zsZYfDB^IBK*U&iWfJY^JPAp16=zvNr7D}C@AzO=2z6Ck~3#sXR^XW!6KB=$bU^%XT zODvne{8VKfKHsejU0z3$AK41|y%;ErhAQEQcXXjp@?g4>3`4=6sJ?NMK(E18@&>Y~ z+ea&nbA{hE7q0D4Y-aN#_u9c9`@ePWmWUzgfE_6Q{Qg>aTT?}8<5}jSLG7gKBe9oO zS${!GslCjU=HSqAB$8N_bXV6-%R5({VJ3QTSXF2)+i36tqXI~4k@hjLr0ug zopy@lFra-CG6S{}l~m&mG`YyZEObPsdLp>$pM__)oi_q1aS}F`QCBM@u&wXw{aICq zYg%e==9Ba)(M(B_kIdNjkZAH>!doe1{Nb_ysM zQNg2_hO?uDLQ#iW5t-9OR!$B{rNjZv>riG??^k@`ZEbIerGzkFUebCBW#Y=)&RR@Y zC0b7RP{tRo!%Jc`uNAH84^Cyt)+uVtBe+l>G`J>&Vw2_Ji z)KMeZdHY*uf;grRp~md8PW#gmw=@839EOuEx-jt&4VY9_#Wb%b`|Rj8-ANwQ7{qe&Y+KWc9G#OaVyGD94i@4 zU02a@j3C9oUE{_HI6b5Yzr-jENU6>)$2mE-@(!2o@;gS5<<;fo5GUt$zz_^6J}F1u z*7Va8kdlaLYgBPncX3*fM#`D{{I`0fhf{}AC`-ifqgxW4@Pis^pW)N;J*1Q~yxFUP zlxj=B2Xd7W%KIB-v}NQpZmTyo}KO?KmK4sE2+ z<1is|pOMfHtEYrGttc6&WE6E>WjLq!x3}E*S24Os30;4qFdUXvbth*HX(OCi|H|^5 z_&}RECm4bv#Xl-V3( zefjyfFAxS>z>?O`bsO%$_55Xr#SlIBfNrSg`rB(xMmnJs%4DOcTWg;C_+>BmlKyhk zI(A4>9^Q*9_wurrt_V7=n7#FV&4UV}gDTYB4_)?hFPR!yR0McSf4B$2N^|Aqaar@3 zkGE>(>&_`Nohq*NrfQ^1e%O@0jC^;wM`bYVaCEO?9M}wh#mWVLxmA?Bw<%%_ibq__ z0$=%-dqyg6Ys+*vWks+C{kRSAJ16&O!@W{gal=(tC*z_$*5Aw4d8J`NfnQAHhz@Gt z^TF31bjmA43gA~Wf_F|jQ5GmkC;6E>E4#bQ&sUj%^HokTgoTN+gj-kL0~zU4VG{H5 zdKVR2t=vLuyDfn;1pShI)X2fuCn6^dI+vG=PM2H0zZhRId%r)w5qXoN=Wte=7J5q` zIYS>YeG~pBow)6H`I`?nso}T(CN)urC9$7^SnHlX-vhy-sVo}Xa`9HM=xe!oJO)O zENpX-@8ag`p7=gIl*{DB$6NVXQ2Xjl+@{G?E)kl5lwpGcv}P_UScPXd#Yj0szCJrB zK_{8#QKY*}=h*;$=bQtrnPBEYxp{CT)BHqfhp`b03 z6r=Y*(%R#=fJ{Ud{)$Ml67czFC(FL%xjVA#2lD9c`5|U}_Wm6+oN|LVK6v%MgcWR@ zi+lQ*vRP9g9W=G7(w1;jjN>_9=e=_x4+)+7)g{4a`%>*a(I}7x?OLe^DUg+e^)E?P zPeLM;TRFHNNss3)BpIXbRtTo?r=Ypqh0amO9}0Nw2E`^Ps|Icmh8OKh#{+)JUCEDa zEtiKi_<#>!PJ2m#|A69cSW}vhx!S6T))}$6b}w07gW8FckoPA$UY)6W=>zf*6DjsKjDH=_Ho71`C{YbpjT|*S0;P&k6uuS&^aQU|@*Y zN&{G^wppA?qAeXtkUAD+|>rMQQgJo^bZ}ALQy*?S4TDB zDhZ(zSsl>~n4;1F9o13ksiZ_lurdurrINii=sKxJP8=aPvA=WZu-ATvsARVO>vDZ= z{m~3BxBfo;?LK}v&0UB>#`VB}v}`8%VKaJvW(2}_OJzmC7#a}9gE)Oe!K|b%47yQk zKY~_-m4jCO30=ZaGU{#xK^lR|4QL`USdl2;RU#_+C0R9agFr&Gj~$Qs<$Waiv90Cu zzy=@i0nB+!Cn(;AHKlpK?BzVWRBV-exTZP} zHba1dTFHIL&)jQxpL=o-?`VpX3@>D-GZ0s9a}ycDK;sY{=3dfaveICP>Y!(w4s$Q* zF!`Cg4H0%M)z)H&4s$}w31TWd3o$yn>t(lbUHOndz4>(5_){q_so8_yEzMdj+~ZrG>O zV~kHM{BhI+F=Kc~e;4g`D;h0uH41~XnC0%wgH;!VGnNwkybhRepeOu6-trHwt!gnT~np zPT1WOnZSxe15`p^NqFiinV?URPVQ+m(TYR^Q?CjsX((A#jP6&x4^Ao3hIl3S_o|R| zUxr=vP!slmAv6MwCH$Q@r>~`*_yGi_>!J{sf{N`v;4yw_%%i+CrZl`weDcF)TqzCz zC|NW4nL80EYpGh@@{XoB5rL1AJkynA6oFA^wbp6e>mcwU*HM_T1q9m6FF$OqTpQw~ zv;OTk>XAq%8i9{M`e}-Df*}Y3qta?ijC@G^z8`_n!%-j0@@x0xe2Ov$K1%Y$L-;&D zK;bq8%I}(rR(3DB6Zw&Q0qAol9z)!Thg>)#Ufq>s7=WU(%IthjOhgNpM3b?Za;36(1h z&GW<7+Y^dTSQY~lAC=u{fu-$U?ELa0_u|k0nE!+GEwbH<8mueHIR1jlYNgZfR`u{# zUM`YFMMu)wVf=29N8%c6(}4MryY1kT3gsPr{Oz{`!=Hb-$b(aTyb5$sI~jjz4;WQk zy8^AtRsJif5m^ir9F^T^d8P1I&h6(%?zX#MWAh7K68-`|CeHPBB^kz_KYst~&~$ihhcn}#&*XS?z9KCL0*?cBp!jP9Lf#&5dN5tzkm?}ePaJ&9e?A51 z5oX7lMpF1Ir@^xrASGJ-h2J$Fsq8MS2~$e##a|kOGc^Y1nk4*{G?TozmL$XY6O~m# zk23;#_!BaPQQBXAvIlaH@Vh}f6z#R{>U;I4Kk)wujDV&%C)k1E&nKWfIA`dD{Fi7! zt1Q3T|Hv4}Mz>IR|>l+0~(%MA+@@A<=%YbuyucGYdXZtcqLt;M5qU^pYBr^@IIw zRbY=(cpj~1Rz(9Rhz!YT55yn*c9W53VrykhON-2*XI2}>@q1=k`mBbJ<%iAqvbkW` z+=84jT6~t_-$BI2h7@l@u*0Ss|A>J@xO(_^Xm;R##2KpC2EW{9xgTk09kbl{M~o;a zVNk{z^^cP(9wn(FyK^hEmy4^<_b|F_<=_pBSX2rp#$@c42(-8;=KCH#nc^`%;{PZ| z{IhD%(YU{%cDE~~dv;aT3G-xdxm@F`ET_tw4^G}m)x-hC+iF5--rwPew>7aAtBFU+ z;L1vGIfG~?Rr86w0A)jY!VjCP(uUuH;+{BCLk&xetpn?a zH1H zfBnOQ(@Rb;1Vvyh&#Wc3#MHFJ2{}+ad_$nmED?0KC2)4sF9AiByklP_Gn19eZN+uW zk;;j|$@hEEVK_UNf+fV42tKUS&-HwYTt^zeV_UH(FuTXB_lX-;^zPP&OyPq_Rh9dE zieCXw2pI;M@U^V0Z;LMeTxRSKe^(_+m)SYGzVM|oq6i(~VsyVOACs)3qzag`) z*y7bAnW$E=MM0&CZ3Z?a$jzVxqJ%U zrGGq z1+~BMS@|AH%JKOusxuA6N8q*o%W|c+danQV;ZMImI`<;P#@zusFp&83@Q?7ecI6s( zuL>k%+DZK*AsB4crO(WG@OwLyk;+l&Z1;U2!G*FmnRG^@qj`jF5u<5O6~iz|Q`07Z z@Ovz}Fe&$gNmOTeh2K)4El`$o>5=;^0GRmOxy~gF$L&C2;+I^UKIE2MW7E1RtlKl zr{~48?eoGabjPPR1rhV$SLG0PFJg?t#0z408AWavCZXCjK8>Qwl$&@B-IQq)oLBY6 zi<9+jQ4dO%EnA^KV22Oib?ruc1DmEfCIr%`;oe=bMwb zyc#B)6qMrC0YfmDxI~F)_NFBcrxUy&-nfCkyyJEJ=kZu3*A<*%Bs=y5IfjCmuy6)N z7>9`;!#Injyhsk>pI4E>RZK7q4{v)(*e^f?Z!^@wkk$?JT$cb7-`1At=(ajK1W=rG zTNOf>XChHdg%?F`IT+y0aNXsDGh$L)QDm5&2--U8TM+nGV7^EW@Ag3*7z$0}^a8x% zZzNF>;LSz}Z>N|o4$JUUuN1{IZ*TU4enDrQ%Tx?UMXUG=_%1iqW*%5 zyF3E?fM(DWT+0Gq`Btp3OfU+L+*I2MDJ!w6;_~Z6&gaXO;e@p^m@l~D?6m6TgA;zT zbzaE~6!_0k9lSf{bz>Y(Jn2D4aZ#SYhsEt{#zU_nqoilHvJ$rdRvmCyWr7hh3cP8* zAuLRkB^n`^ZbeUkNg`xHD~(YGzVY}SMm8?*%X h)4ROBFK)Xw(5RC@?cRq-eb2Nwi?1mJ*= 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%0D$ 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 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 z6#Jeu-Q6#u)8~bXo1fY-XCi_V{+38A04@*eQ9eb1Y6$|puC2jI)sZc+hVX{!TI|4v zeVU7M6FrGcJW0ygy|G6Z;I>-w*$r3&BGd*$D5X0NUpMW>ldd+T71VtBK%?>vc-kaL zDyO+q4T^8opqa7fCNxJs&+F3R4AA&IXI`b3*-PxmqY6lzc8Cl-<@KNwz$$|=>gH=pI6EJ_*ZeV6(k|MC8JnT3S~kCl}bPy3(U{VzNG+vk5RRRNM#$yV9M z{BJ-0E&E4&;{UJ%mVfR4KmGK-AN+ea|KDAV{7+Y&o`Hpl?!TJGriZ(ilJJt*VpmIQ zWmi#0gHuAcbm9*aSyOzgHlkA!dH{|fK30GPnRY>pM5pyl0R0(Y_`cdOKQfv^pDXY+ zeRp8vMEhYFQB$;@YxV&yG-oW+!9Mg^cUiGxPUR;OP+s0Ix1Oiv4)H~9W#_XjMbipL zh+Vy0)S*IvL4y3k5$xxwIw)KGpx=Z+P1u(GUIQ7!41TnPgF+lY6&^V3-R~Fz8#F;k zqN>g{=UlIwx?7@T-JtkW$J#nArDWpCfpR7^Gi2uh;U}Z$>`~lwIU9?J-2-G_AyF;; zZt^WX1KCR^c74wT=ufTJ;8sRo7tLG;__SGRXdFfM4EKO;Q3cHn|h_j|zwTD*jp zfcR~9EYp4Q1=uZu4xsd42jJZI0Fn~v2OJpM1ke#NQFG(U6f7IXIlA!KiJ7O;a^lVR zrV^eqM;s=GmOY3d-Uh<(jcJ06#EH*Rwd`X4OWU>j9pig4Y3^pl<}I2n4A%h+KWISIjU zw6>~w7iQ_rGD;$Rn+3XI#P#7CC9D?iD1Z}GTd9S)jPQv}m}O*G4?`G={nUQEiLT+l z^W3p|lLOx@FYBw|uXvlGKr$GYV|>MWxedLCUXT%UCUm^dopeqbMVGU>2UL^xSV1Ym zNeA9?8aYv&Z8l2``V(sZ!s4JUHqs@@eI?L{`}-N}CQ{Y^?XABv#hAwQ&t8debWAYR z5$nRXhAQW@O+|Ti2Bp&PbZ*O|sEE@;<6_}pkQeth#Zn_v>1HX0Oa&*5CJK6d8WxfA zxCyqDKC;x<9Tv@?sXbg?PYR{vd%qTu+w3beQorS}DdxW|QZal$lRvcn} z{;SZOWlBOCTPSrkrx=Y*{!wmZG|g)Ym(f!b!h&`7KZXI2(k&pMcgr2=K+?*a(FLIu zNPafkDP*oA+B0!jGmA8=CZ}qmiZ?FI5GzTGiySP%gAk9sg`JpT@+Nt@*uB+aG_@QK zdQ#y$IhY1H4{Y%-v9Prg7mS5VH7p2~RDCgl$XrDqXjoXqV+l0*%(CUaUaRkgQAt|w zg7xs*VNIl}+T_?vGTu==u!i};mv982vQHh*ukkMj2D(7m;#y%k!NtNhYPvNU+mZ43 z(CGm=6NVy+LC1EtPodS>x(Y8?YrBO=a4>aooG!5V}%$y*Na9Ob8hzRhUzM9lC&^0r3cTE8{ zSJ^tiinLa8#14UCY#+ryHwL;^k%ZKh5r%JLOr`Kwh18JQ$!p;h{rdC#97cti1l&Pl z^P>k}6Lirj?z!subq*Z5o>`;2AJ7OKJ9B7>iJbP70L? zCIyur_7w|VT1^NBqkcO6J0T;;QK&OT-394Q0+Nq zW*R3%&ai0CC?m5Uq*Gppjenme;Ozn8j~K7|@D1-VMHux05ROw3UmHbLLj7E%>stq? zRhkOmDHv4{Pz>xprLy1nrQV5%Ig7>;2A@8>-pP!BgT5`KB(hQ7?h=)e1??~6Ts3>a z5Gkue12oY&h~`2Tu3$Nmf+yVi5d>(Ph*h__TljH=*hzUTm4J8l=I<~7yjbnZu7XGEn>j%Vmhe& zgTcY47=%+GM0wP4p6<*+OfV;X`22W%8_a<}>lWNWsil9bBVpc+We-0DrJ^kEzYZz8 zx5cgf3ZZhd+>i5XVWD=+krSM1ajyGA7npw5OtCy;q~@5j1CMY*C1^56gnnbhM@Ajs zpRk813vTB=ryL_QV?RPC;v~b)5-LAJNBFy3SY$OR-VaS}cMw@0Ix~>Hn2nY1_=GVi zY&=LUJrBts$c@CG9#)^h8O=l|7=szKknolb05eo?nSg^k3M|9`xG;?V2U-Bx?$Xh< zIxDRsPPk}o^kZ*X1lJA?xwf8FtBO@;DBC!w>at#DBrqm}pZN}|Xst19+86^EaQuj< z0rl1|v8>{VN!5fG340xxBjlZe6+ivAA1hn%eS<(6Stj7(JeSt{d9RWN96&3bvYSg+3_2^pD>=z&uIfvfC^RF z;97nd6J+KIYcw@WW6r>uOpV zsT|}bjbp*ommnQ9R%^*Cg+;=62%C#H8z*GVoLy}HO)Q1eYgAQxMt;kQ?=>K_YS`!*5)j@hPKh@X(#Qzv|q0IeQ!8_Z9_N& z`H{1O1I8XXqr{(DJzt#1U`S2DM4B8PK&B=+ zht<6BtAB}oMZ;<|Zp#B(9bL2^j0Lk~O97pw#{E`ZSRm`JuL!Ngz`Ls_UU(yy4_uHeCbMw_`_mw#Ap9{R#nyA;zWw)Ss)?E-fQG^>;;}N`N=cm z6?WO8!VD6V@XYDGMUtPvs#@d#a)rq}HqDYBg|M)Nys)r@v9Y|q3PFhk<#I+O%-GT$ z5hbOnnqHQ0@^=Od(*9KC`pcE}+-u3(ed_Z^BZk*v(VksnFvWrlf0A z3rp|M?>lCS%|#!uURNG-@me&cU#H#cJ~8Ll8mKm0Xdjr1wlc z`pfW2%jD#3R4!!qKDkc~G&9X2zaW~!`rQ-YY0V6n?I6im4=V?XQ!D7u1R4Rcz&5^B z>SCQ$Oc@~b2H#25i9n( zlGg#^_l-Za8wuVe5uxl4-k*eGlAt;q7p6u%5744VTl{wTV`Eb)rK{C6m-ZXoRfje< za%xg6`Z_Y^=jy;uQ<rNKh<0P%96dguc?Vk09XIu05_LY-1c$5y8^H#U)XAy+YS zzt#l2nNlyRvV#pZId%7FnPB?X+BU6dQ<4pL&*OYeoJL{M9Cp{O+k=sgn@tazwVsy+ zqmqi=2T@QYg~x=OfzyYhN$yrRqfGZ{cwHW7t~pqJyiYkseqAzJovAxc=PyZaWmlM- z__%xMW6BJd57gL%`#g6nPL{gbxDmacSIuHd(;8B}E|Jvs+1K)8Wl#|f8BEea@@HyP z$dM^C*hwKAB_|_;u&a;{(+ANt*RNm%uA0x*;A+?*{Sl32aHNgW8j`$OzLf8|c!yz4 z{7H@G9ya6p<3rQDTM`xFfs7e>gA`dotWqe$;#>bWRHhnFJdl(iyD|S+I8z-~BGfj4k@jH2-`!u91n% zoGgRD0TOfjy~iq%vy_bhO=~cJ2!?jOJue9`^?S+GoP536fE}% zLTQ@TY1ZjnMas20)Zga&XYx4X$O(p|`3FXb&vEdU>W1|g z{5n{?ugIp!)FD{b@QrK0p^}zzC3NJlsZqOZBEb|@AQ=$mRU42H=m}62c)Mh)fOEhx ztT?S7F%DStBG@1GA;VK(a|EQzEMYG15XB)XzaG*cAzw@rQ3Ki5rP$<`KfAq%Ep1t(E~@=ql|fgH-v(M_(0k zB{tlT+4PUdRap%^^#G|wk16e5SJc=I?SkHKsplbvsiKO;7b>OI+Xt4>A{uX^Tl&?m zV40x>U#6?=H->J+O~>S6nLan`MhVFGlLVDh6#v zHiy<*q&?Xi_D+@*ZwXp76;PK}+LtCz#2BWVc2;~Xe8V1=QR11NUKClx=E7Gbc z^>%k{V#3JT?Q;Pz!fnd3)y)~LES1ugl=Px>MfUo3S&_1NnANt&G7?i=U*>3MXyHwm z<9@{&%UcIP>$QFCXlfm)-uP#@hUbOxxZCnL;J84y`Q9Ui>jA!Lej=}@rWKtGhZrZ) zPzlljSCdk_1o(jnU8=Pc+JSNYbGO))WoXdBe0BbI-`0^)Bq+nqyu2`x9gL}&?_vvv zNs34mLjis&9yFQ<#U>bn9ihQ66@E~FprR!y$vZbX;ikt{#_DQ?DXZ=?Esf{_viWo{ zV>9sAWv`8Vm2=(4iJ{cVFn$KfK8q|=CHT{{M(?}GBrcKcavyhGM3ewvsg(E(rSm<7 zz~Fb*COZH>@FEHf$ItMlED*5oi?GIZizBQCj{T2@`w*HgJncTOuwr9`$d37Pn6k@7 zpKXx#&$4VS0Lf4gYih^}<;tBA()&sSg{dIc0VOI!BZJGooajA>2v?<|Br2Fg7rKSU z#1l)B5Tlh0tfa>WJqj$d!aBYq0!dJ*-di{f?>idS_v;NLZ);>@oov1ooy<2-UNWME zwfaW2g-aJeNh`ipi+pjw)Vuxv|D4vD|6yowo#NYnpAQ2%nL{9H5cFn=jat zspN|$c26^cBrgmNF#$bffOMi;Ro142tAi3tQ{CWCXi*v_rC&_cLoc$5qDt>wePW=N zCW#h$YOY^<usyj1pKWQj(H2&ZY)5pr8hGE&pO{WIaeDaRcw5g;3&`%j zEL>d^kVWBrB8$-u5~vI}w;LJS$5!rx6bDqjyx$+oYm%rYjCHwrlRxbl#?ESa(6lFY z!I~-wevYEtYe*+bMj{yON+=T#qs4gztPYQ-6Cn&1;LcOT2F@sZHQwA}?SJr|f6J*D zEtndeeHl93Yi>Y*7&@6}n7kE9yVmw>xPSCkpdO6mIRSUeY1v^bT>&Q%435xORkyMg zM;*qcWK;$aQ87v~psY~A0}b*JM|HaH!lb;NSln;Dw_84cfO@Ik+q~MlR2q9Ogw%vH z3!-th6<;8~YO9ha+1CqJ2WR2V-G0QOwpCyuPCEoF{aD22IG`AW-3S3nbL3 zhprSVH<z&zY6xQ@SY=qiwNdXr}*|h>3ndTIqPbMYh&~`uq{7J@$d(B=&q7g!ar} zfNLAi?@xmAC>%_`BITQgnim<)67#O%)j*lP6~9>ouPHG59XFZ?@gw`$5UuO1zckLx zJp;LQZV)Ns227Smy!*cI$mHh1C<&jamO{CcKYwm`#L%HltqOxO`~i?YSXy2WYK%oq zK0L8fe5}Bb$8PIaRfNw&^{pL6p{i8+$Lu*sIKx0Euh5NeXM^>DZJce+uV;g~1b}w| zr_)e#nACKYGfQGU#y!jM9dhwXE*X5dHlzqPcdo#+I4B8#^1FHzG4yH3Z3H>jR8(0S z(iUHF4xUdF^ZNl$#z+0ckG39t*H@s6(f*mO4$f*;F1({U@?OIVD|Dp~_5l9k{&2hQ zqN3+GN^yTi`6D5gJyWKSh-LwD);2F#MwQ>m(j`qU1ciCJvWf|&o&ND>FJPAapE;-v zF)8|+=L5%3o^U#?$EW3!!S~(9H83Mpn{~5Gjb4`;Q+>KuTRI)i%Q@QAoD{B!J;TJp zsSKoL-iEP$%J?{=$|ZFcX+_5clE~g)o|$BC)OemhE)amW-7VM&4f8M*^o5}}Lg`<_ z6%hUD!zHX#HVllc2|q-d@h^uVj=cB1tsa}-Y~Fn^^GPwg2ox2&!$*Ev>XnJcH>N|Y zD-5^6>iUY4cD!TDdYMA8ee!1sdV}qXYKo!7g9BRbQ!{p_`8yWi_NyMBkR3D*)xX5e zM$B3%;isutnoyWhNykha8;KNZl;kX(C^#Oa*QZC*2vMBXQOHmcGL0dx8I|88+1xms zj0<@i2IHrDS(1-p!ztrXBIl1es(q)QB|t&Mdz)|vQjG2LIXNj`X8iQB(2G4k0Gj|j zN40k5G1TE`GSJW%i+O2Y)Kt{HheY4Dv4nVfs;*wc(Pez=4sGU%Ii#M-*@fzREU`Yv zQhmgIAv%tf9j`p22K}u{W3^^y>AiTrs?3_>b!;(|er3FsZuo`LUv%aBiS>zF^-Fyl zFY(#6s|71`IqJjO)JfT?d=fgtBn~0tS?`XuJpKdm^X%}YPnUrH31=-3@CS&9YM-hP zix+&TekX*@KF0zS_-!Gf9E3_4z@->g zU=g#il^#6)ngI9|m$7)&0QIs;7lu}dUT$I zadz|M-oncr`O^CU6Vw^h-Gd1kRkHVy_c?tc116LmOskzn-P`nHJ9%{T+~`|(^F5vd zyyd^9&?UQo=`X5Dwf${7evExp+rm28;mQ5JcN-1G$?1))!?C&x6SC=d0q5#JQ}Gti z9XGPf-x_>3GR?qGEv?-NSmv&eV3`L>yw$!3C0S_gEMeTF15xL8Q0+{U{&pwWDSo6;wm}Dz5`*0kr*(b5NNEAEsCDW~S*m)GLtAPnXlqkZX8;F8N}7-zN;) zFoP$LuAHCr;b1XqFqwY#E!)1B>25u|X-g^WUrO*ysxL^e)$$UTnsAu=R4h^0aOgEq9#(YK#)_^5^C^ zfAGvmUM!17OTo@6E%(ww`}thpVbS6r+!@MfB{a}6t+?DegUCzFStENyhv*lwR z4OQWX64ZnXp8z5b25Zy504)Q0!I!TQ$&49cyqpE~`ihYWAH(hSr4fho1n@g=OesxRX0mZuf_uu+de> z3oH|AaNbjYXXxG^GPpI_>#tMgPm)(jG95Fl3bZ210F~FmZ&k*FO`-jfmlr<@S!di5 zN}*32H?UoTU4YU*q|AdrIjL)mz?3>1cXGdiTp0D3ZQh zv{_r?BmQ^`s@u(vj?;2?3hG(0W4NN7@}-Rh(1J}*J!UC;&azx5MF?@EN52@M>Pv;J zs$%(dW*?t=$%Ruuw;E>If|PD`)&ekRi7m-igm!+9l56G+(JG_26dLMKmQv+<*H&%D! z+yNreK#t6k6{0t+G36zekD5|nwX)3K#(K!{eC&lZC@JmBqn6aetO?G#@UwN-|=n=cbp2jl=C}#Acq6{jLi9U?#| zI?#uO3H0<-O~#{AV2BNVAt+B(a6cc!ZcqN|cBGMnpzW$oC^~yvw>5XQN7m9~CC6HU zH)w435q<7-)@Bt991cTN`d3%Y1$RR#@}mjTYH$~5?fI5lvUXFcp6p@HC%5X;s8Y?I z+rh{!;Pk~-ytPN3`GS)qu8hlW z%o6q|w$-jn<(kc!PHi@Fb4B9C86z7ka14;-Fuuz@RSaj=dMFS-FMU&7}OJjafnr2{b8>)@0S#SFzN@+nBlbt*8xc@I0*&W#u+Ma&tC;(LYI z6-Vjo+(Gga1SZf@tme`Ir+PgA>S`?&OAQ-ZhNfOm^iF5B^(>`I>lA-ZhoM2@WPt+v z6!D=w@9JZ%s2&KoeWrXsGFk^TX;n;7O!WLZr*>_#$qWnMZGTjjNe1d40)O(+G3qhL zG1P>CJ|zcwFw#6cA=rq2Bdpz4^|r&p%0XGQ}@A;i#osD!>HasJ38a zrh~&-C~{%s86kblce*un#YBKUI;_pL^XgkCg)={Dj--Jm)R2I z0apD?79T9^C^8gN+)E6Y@dccqTXg>SFwpA_W(8EW5yMz-G}%t>3)p7t zebwb`m>d zC28NiP%Grk7nRv^lZdDwaLD$ z-}vCF$){P$m%oM72r4&JTm%<#i9;r*8pGFFe^wP!inC{*PmjM;t=UQ^7;CtZ>|(Pa z>-eGiHy~t*dqLnHpS<8!K{78WV5_wwVO};B2;^a`4o=qfw&#x&7%EOw@Z6p*1Og1)s6Ixz_55$ zEC7n+^*6r@#SHh2&_eh^a*cn+Tu3DG-4NcBEh7!*%?}~^9u{)GhF>|EtmbjC<^}nfs_6hL;bgq663ew{wJlx@|RBf ze|tjyKeqcn+xuVJ|65Ax9||S^gOd7t7W|JhgZ-}xAvZsm7af?)XQmZ%heE zDXz~^$TmQl3=mRIPF6d6`z5LmE1x;E-!_h7@+c>*5-XsZtX@Ks$ zmh7%mnbBqZFR+92(ggiO=PMXMy$X?TFO4>GXVfR?*msYEp&*8;j2N#TlouSogiysX zDVGmLR%(EiX?bsosuF>xUR|I{;iY>#9q`YYLL_mjZoonIK{}2+TbAJ;kFVP)`vO_Jt7T|G z#-)zJ)p_W+1s2r_K(IC6lVK6yYP)i|@g~KabVEeYlpeAKI-h!1bANIAa3clse*Lbe z;*I;FuJO`3fC_!_nP>74{A?=LOeFyEgwoitydnP4=DO`hw}Xp`js6I8+2vxVC+n+Q zvphe)=^VY$=BYZe$mwde-RwM3!jd{a+kX2CM*6&%5;OSq3^KE+v$CjlRKbjf?iR~m zMV=5pc9=CBSx5LzOd_{X`F>%<1FH&W40BFMc;Uum4e#NBKEBAzKx~0!M_Qd2)|^2@ zgtjipS<-zdVPz2ZB#)d({_PD%T#IOb0H7%LL5yGCGjn#CVXe`h-guOWr2tG&cs2rO ziW76fT_7x;DhbI|a?_%&5E&&xGLDL0Vx6oQWpsEm2!2Ck`ZowHl`#`_^+KT#)3#mj zyrHcXG>67plS6rEd0uAl!sMm~J{0*dxf2@&O(Rckhv_URVt&e87?QjVxLIdFH~UuN z#AdN_quG=~?2^|+#Y3xuvSqRdmj+Zc2VH%F>5hOz0W#An5&aX*jzdw)cDq^u8iHfu zrFLG?u(PER(To^e2dZWEggfzUCe&fl(ECOZ(OS`lfVnx%VV^@Ftsg$hpJ|*X%Pr4< z3~D4o&cNZhEDGyxZ#l+eV|kb9#t;AZTU3pn1^amsz-B-F1xZC7nVf}{_LV!$rvw2%;)f;&)L#BRSUO=Du;!NEpe{z^6V$jAne2vxS%Bp zw?(YJ=u2b(%8dQlm1c}d3DxlFRRb$1=EK13c)kV`LSbkyN|UX6a4fl!njKHaTUyvs zeGY6FTAUI&*^rR*Bgn~`nY&tYv_WkKW*0_Bn5Hrn6(UoCM|%FyLMQd(;?SZ8dQbnO zcEOddN+f$E6J17OA2A^w-klH33HZeNcP1`%KGw#dX`R^+%?#_4jR30oDXewzK(UYb zEGCQ-y`^WawkaQ|DP;&CBfZLr*@)QYYLM^^?oPbK4$%=HQ>$kkwU88O09wY08}C}_HG7mGyC%Psy#qI!xkaDQYLO~Yw8ORW%t zT^ao;hWqMT5JMzQrXhn1)eq`)EiF9F1A?|B%KZzdpa_}#1NC=IU7?**1qG1{s1xS? z!AK*Ue6i+p6h|}sbMWg&U9n4CI4^Lzq+cC2(Gh@um%*@Qp%*+%sGjq($%#1QK z`}vl*F9^A2Xd8ommBht zvLxfj_YX%8^(OwV_MGxL4HXpwRZtmq!xFWo#!e?P6>Gq*<*Ue)lDZlrGF1h6JzvpR zNb?4eamndvZ8Zu=TVNvvi?;Go^LX*HNU*&;`%cnzu&JHA!z5m&n~!7Lvle1#>p-j@?%lE8&c{N25pNg;7}0YeneaUmHhi zt(ADXo6S}s-?xpG@%rW8fYYvl_ z0kDRx`qQ@a*XFv3rQQmqNYCSS;U>P$)HgsCg9$9508HB8QwqHa(%d#nv=rJF_e(i% zdJbw9{SjX?SQmuj3dsDPnLo9k9lgO+KauZ?Op&+_;GrY&;~Q&>Ho60b#kM(gD>mjM zS4cq05!#sYV+b!2yd<;#!SA^_qbW`cCP(yoh>XH|UbJ8S#{r*DJ%3xyi`H`?QH50x z#*F&9^gCRqB9-At@4V=M61w$UqxuHRkz8|5@%r!4EtzRsf9l|I!LEx?>4CZ{^|=sq z+1fC4sX{BMK~jBHTm5EL3+vT6l6#W)c=^($Oo^g6Q(`P>l8pZK`Gj@K%vkA!w0GdTgC_jtp zKE~u0sSZpx2&Y7yVFu->s$$x7IeS^yBE@ofnkY$Y0`vi&f&P^M{VWAe zRDUnsRdpN!p&1Ex>aYF2@tuBd0y7j1ae~;4{+K#5%AKgfc?qhl6l8{S*4Cz!NOINI zBU7gIsNlqslJpwyS@PSoKS4jEjP+Ao)W$>H=X-ZV?9@a#kLPl9EOS-V=(sb*NR2Us zgh?`#=D(516O0Ia8iw?n&=FSz)aa=+y0G?A_yxkti}h@$ijvAx5Oj1pU;5#L%$22z zN{#ConPtk%gO7()8_$zF^z_LO??pOrO&ehP3f0mLQtV9|5QJBX%7zNqY^zilxEQ6~ zLyTr-vj@jRk|eI^s%!d3UU@C}a3G-+YJzoC`i! zl(Wud1B8N`Ly!|?4<)}RCIER2+SCYzzXTjhb|vD$$;4mMC2T5ONpwy+rwe>tq-Z)^ z3k8)00>XxmZq~!2-YH)Q8rgHK4W-2E7EV8a_cs04*-0}&1idJBcFP;|9rBEB^LKg| z#c6_`TLRY0SAB%{6s>l$K}U%!)f#i*i1f=fyJc3iTvqBZyQd7>2+CR#6w(U0Btvo9vYJr??4AhOAZ1NM45ON3 zwc8=le0`w~xNc~5>+gV)&FdBVl&9YHme;umdWSly!v+bdC+Jy6{A{{~f<=pn9Fg3F zg&kUg99R7aB^55hv2-czp396suE;#h(I$@+LfVK_gXO+)&-IvhQMKkl`;t1avD@ld z8S^cIN~D2`nisc>W_4rCVf%Xk?w z35Y_cBqm#w()SgI8SPf|HzrdDPBxx@a%8cx&7H+emIPgUs$vZiw55Szq+3JDd_t_K z_7$?b+D_!R|L%V72=XD-hPjAkad;)yx-B$cKubwyUteeaoNohDk z(yZeg^I^56zsY^vn$%4_QQ7h5OKtSN?SuaGyYnT4vk}a`KSSgWJbY6Ld^ukxUUM&3 zW%aU1{xUc;-$Q2yN74d}GTNp&KY~9vZ@z%Faw_Ea!H668;2WxLog2DWsEd)P2M^tl z28SVb`oMJV=$aaT83(^l&-Ybe05{L#K0@6Sl=C!uQ`G8vt)H0@y09xD5ZRdGRb>^~ z&DrXHgHd*|K0*of+_mg+v96PmqFrlcL9A&2$nnDWIs2aN2K{=#JD_em6vaub*n&@;>`Xc(mrE4|5aGa_P-U@iv2s6E^laO zYV_}9I_tlZ=}e3af05~*>RNhM=KsK^v(fzvn@&&1{J&(=|G^LbJ7@Se3R^~||HP)t zNJ@VyY&&i!Su2BK>f7eU`Sq02Z?}ZY-@ic4is|{vxE*yl`zRv8#XJ}tjJI<-9pk#0 z6S%`j!N4CIl*3lH0?2+7jt#_>7OoV#7(OY7-v;D3EiHnJTazv}26X6JN9q;@yPpX) zF9knoFxARahHKd8`jR2@&58YS1F$)2!WHo2zQZ%S2U zrFu^a{P518^Y=BT8iC)wmVMl%<)V|`k%LMpu?|0-iQ-j4og;D^dMpxoP~Qy(q|5Of zt}gcb#;2499)Vh5ph=~V)lR{Cu= zYUH1SmoVo}(<#>gP*cAFhAWo0Uw2k1mKi)a8##e?&v~jc2wvDaKr!7tjlSU@31gJ`0|SAv$SAwBD-~!Dj$mr8lzl!)^5aamhKAXQ;Gen z^*rrPeSsVAKJz;*?iU-_a&t?oCzF?Sqc58%OM?-P|lY2`1tK3Iuui7 ziyr*a4LiE^RnMQw=8s*9ut369@7W&5wB+=x4aOR1v~V`@YOb{2i{>T0hcD(UFOdu+ zl&lw9eUIt3C%6#qSHS5P(6J!mzOUn#Sg%h|CpI)|KX~w2gu4F7o>jxVPzH+?N4zt- z!Cr4U*+x6aJwUI)Od?1+NY=y9;~_nl@I0p zxOX0m`zrw5G>=x9J&JJLA7u(QJqI@{AR7s*Bw0$#IBzq|>kFnxfW}-2j8r^gtq(Uo zW}bcS#UDjlkg6Sz<;h}~#AfYQ3OR%~t6NtQKjY9WY+|0ouzZd|>riggH#)a`xWYU^ z)E(Bc?bS>9M~qj}SG0A^bx7qua@bGAaZ<=T{NW!#B(v$q<2~uLFvhDPEB8HG;M-x? zzK2`rD+kWB-!G6jM2T;P&Fom7Xw32fw~pH!J^0&z*bp88cXxa&%Q;Z#Ke}s^Roy^= zo_8owu9hT+m>uw8^x@|9CZa+~ zTB8!4I169HdXn8!o;J?w;@hOt|KKhb4Bzn6y84D4%u@5vwEb`?e898xVNo0Bfh2rP z!ZGZ*C%i{|O?izlf%qs*k||kcI|sWG=(HkqQSh*RRJA(^@eKRM^~UfPjt7z21Ff5P znF5a(6o}N5El_rZR1`YfFSsK{7e*;hq)81(gcXq%ofJ1_+D}83EIQzKP4_6fPu_Li z^$z)Eph<*`!M8b|OSd{&xOHeU?ad!KxFxR{Ezkl?-B1oS^35?_C;GL zuRRXBS=DmQVkU!Zx8N*LO`qXNs9DHbXdB4f@Z87~TcvFM&^5~KydElRG&qRf#G2%m zf3gP)c4vxjDUUN8r1+W0(-Ljj!$2{=~P47V|7e_}D%l5_a0I)sLIvi)%Kv&%^X) zRq)G@V?I_Ef$-$CVXqeHK7CZ3d!v^ z8H&0{f>Fr#fvJRcJGAR3x zC|@X&asBtfMLixzlS4(a4mjmcxed6v8m-x3ced?@5qlXhBbg@hbo5lJyR$0bUW$G6lfp=Ae&dNZvSd zXqjqh+2b1_1}`H*_1?Z@X_eSuEQhG6*x>BO2%z$c8bF*}k|6ZKyZW17(OO2H!!rvn z^wMkvHL^t3(i7874A#l9ZC+5Ul|&^GH``)5OM*S zh?n;rh?bEoA(X;XL5OJ^2Gm#c13V=0rw==J!aS7jne_#4^1}*a{1h9| zMgtN^s0%fyN^+G@De)&-9WK+ z1>cbDap8O;W2$tV7Ykt|XUbWT1U{RmmDaTJ`g>J6Ta|HpQ$U}<3}MDVv2|qn_pt@r z1hJbN!N^Ue$&|>1HMNo?oK_fN1_Yd@9VE%T?F6NrC`6N{@GuD+R9;oW`ZiG*T-hU} zj0QuP9t0V1N6ZWufwm=fm*l-Q!Kv#y#&0g|SekaW;uA6cSVo8N!fs;UYu`=)@*!|E zjax3!wJ<3jRoOO>moII0@k2yXTcxa>=(^H|c;Y1)Ei&IN?o2i1ApnyoSTQ>QOYj6u z+cA*o5b25*UKJadO0y0Xh%>Hb?ZD?M%bo(!W4P`(K$eYI7(3!m{{+keZ&m+Aq*9T) z@9AZ;-_NGH3`S*YONq4C;&>dV(%>0Re=j2%M-&cA0H2sAY;6bT0c_T)J&e~A{K4Sl z=E!sT-Qo6W!?g>kQ(Z$dQz{DL1zi5%uMcgAkg6vF7y~?!0EgR>TDN=ruQ@Tu zGtUzr#x?AS<_qJH2!lK(R*?;hfV%~N*7EPUTb6!H^`_HYxpL2gr$$yZv5Zx`{25kZ zd9aNzDc3t!T_qFRpgxON>E6s%Vxr&j*O0k$LFJOleb>J?cAwf(bX7SZ7_qrVWim8; z`Wv9fueCL@JY}B<&LZAGPAH#E)@u{r-k!r(%;>({JuJE;OV0NB!3sy8O2N!;;Pi=>6V^?sk}JTt4Nvs@Q!KYQ81{ysXghNxGX;?6*r%8J zb$hg{RfENTN=?i#N?~OYr-yp$`6R!IMu7iL?g#fZs1OR1*(SU4g{3K`wt1?^PK-{b zsrORr)M>}2h-u2|wQ$QJ?T(L$#OUV#$RLQwk|%zIM7B-nAh;T*jM`ci38J1?|4oSu zRp6#sR4LsgOllq?P)FxB`g@)&lYB=hRHTfD$4ZEsj?%1}CnSn~X=0vMK0+eJ6_G3Akb5lH&d=Duof~bB zt9k5GC#_rl_U?)9bWeok;>vjWdQ-VpLPM_;6w{kpZ(~myUKYX@<|nMqhT+A>#+P%^ zP8LJ$5s?#0@~T2iCQSJ_{`k|OX$etC66?a%WA1T=v}C4%UEih(My@gww;l!gq?2sa zAvp$nI&mpD2ZPMfT-Ao--f7u}DGLaVBq_Z0G#s%H^VXCs?nGq&um)6{JhM?QGc~?I zVaW!)12G7fAk#^PTnonIA_5QaRn~qZDOo)VvEtJ87`pYM%-m5Xu=Rlsp%dn~;#hRW zIzk-@Rb)J05{*a>ChoyL(ANfsvn>&@{1LnKs1WcHJO zfxJb{@g&|lZ)|)>jJDO%0UZw?fRw#-P9wqy;g_)ar6sE2C#I5C)@!i9a`+hvRebkwObZ z%Rl=CRhaSu>V@8{+r1o`r9{S~JPfKwdQf;wUb|~hHd_u0D1jU&NwL-wk7Q-_InN_$ zdS$-vwkw1WV9s1v7@cp8l(S;-4Gj(3nUojB;-gA4sEcD!?Yfk9`w5d;vU`-{W`gk- zzpR_#Jm>tZPcEW-0MCK!>oQ7dZmtv!6;tI?RW{`|V6)uGOvF1Dv!{J=bC@*~3dt(* zZrQKZQr3*S{m^nY2t9EY#^$jmv?{+YZcXIb8AO5{AyGV%bm;||;-rpxui|DY_3k%R zI=9ALp0ZKZSiBmypl{X~9{r8{A|vx{t^CbY%2dm=fhs^V@pw%CsUwk4~gF&e&INy2R5RWS$)Lz(Wr<|T(YX+Xi2zBy+DAn6n zjes1&Se_X|f$dGS3(I*hWT2;-85g<~Bwy^9EMoN;VwiaaE}lA(ja z={P(C6;6d&?YH{jhfZB(b4jwtm$+;a3m zrvvvqellJCk>4?Z&Up+N>5v^}!@0??J}`~+g$_%@A|@h5*}dV3S&%%mPx2N&n6J0T zDb$9?zBAQ*0!%2EBC!4DmbTY-yEvIKzC)o~W`gZb1v?L7B{FJ|?p>YEq$7UqcqXZ5 zV%u?6PulCl)s>L9afZG^4CO$}aYah|0HCslrk2!DoS!g``@#9S%|C?)o%iOntk zIRDCz$lg}%*-g=l6EDlxU(dg8a)w*AU!b1F$Dvk|Bju``k`I(BV*WVgY-||-4bTkT zQ!{1~R#L^}$8kiYOhYQ5)RUHide2ZdEwF5xj#obaL!1A!q>il~7s5mWWnQz0F&G?A z+8=GyUNRGZ9>jfs3{=$OVZ};lSMja*qb&ZD? z`$@=~rPuU4T#?Pbr=maBbAhXlZKrM6?%Vj|l&wtxP0-#54$G|?`jw_(o?&^)C-auc z^>(?Z4kYZE@+Mg^fZ>*z-WmVaYzP{Re8^gG!gEB*7y37WzSea)r!F z583c@OQ;l}lIjovR86sH<(Hss4W`+oDm1F>6y-mpf7-;zB?wJL*+A)QhOAm-P%qgX zUSv+j#dm{u1YcZUy$hrCto1H$C^=Ul$;uM0XtRIm#gwC+L=S9X zSFZP=9|9|HM!SUJYmB?c3#oTaL4;P*4@WsIK&v;@^b(F|{0{YJYN3F|IdVv{zOrn+ zRKqN5Shc-9A{&S(BAXS&vp&;2ryWFxS}o?*AP8B&7Pd`L1|L$KZ2R=@o8WkGj)-8q z&~+LQ8O;ToVuTqo*JJaE>$@7Ynh;( z4U;dzNvuVl@D+#y`RcCpctGxt$lBbp!q+(~vadVrgEqrtZ;^%80(KrZOo^IhPpMpz z*q6C}&~Oq%Y{c!voqlucEygDW7c<+czM7U??4i(9rh4(X zdY+Es3OMC>Led$jO(R_&-JBGY@b1nZup(t_1Y5u@aUs_ z80zdi3I-6RMU6t6h{P9;3ch6y8jjEz85z0lmrFRBHC?&AB;`xWyWKdEtfxJQ%)Ge< z)5Ez+dhSslo>d0lc%`7FmD;*tmkwgt6q}y=tnzO3#weEG%^7bdO4cbVyWw#JYHBHx zZZMq`#r2fr>rkYoZIjm{n$ulfW?D=u_Bl0n5;)CHZ7)dOYx9gL?PBAoz7}FWh1oxLWAI8Y~i&JprtE}F(BC0Mbd8=a!I8q3SH<;t#XG&?B$h01n=89qx zRs=Bgao&@4?2{z(2O>*8vs&N!Wvp<^%+&Q;&X$I=q14#VPKn#@3RvDx9v!o+{L67lmcA&&SR9#CHf}nB60f zRcnXOj?=v5+Pj8@O@GGOpUCHuGjcygp1Mu)Q1vib8Prr#Dn(!rE}%zbf@59HE)7-T zt4?Q^u=`i^#D!z5Z146K(&w$K3@laGKm%^4f9FJKGlB11jY*CjaDc!mw14EyZZG>; z`>CuMuy_~o8*JXyz{rcpS)7?OCm5~99|&f3o*X!Dcpp}*4IM(1VX{LeGU7g&R&UQr zZ{U!Ey%Uex&q5{;x$~`Opuj!gcGy4MvME{xvk;P7p1ro7J+veA?D}e1zX^a`1o3;>tjSRy`k}AE+!nWwOfZPjpm81F%F{5ChgP z9`PVk-7p21ISNbx9dgSF1->rB;+Z?-7K>oE5t-JzF%f%4Wx?o*h{7G!mEf5OtkbfU zrZLec3dH&>guv;d7mk;9OFq)1K8O4YGEFMA2a4c#rk9+9kAol(J3w|O$HQ!DAcadq z4aD>GqL{B8VQPoaZ1YG49D*6sqF(_upI}YXyay#|&QlKb1O`}^qtLV?EK~SfzpUzb zm;(8}N7!R%~`QLE(vznhG*sTy58Yeb?ID`NRIXG|dpU8Dl_AoYM) zspy?aad1O}SN(MUF71^_`>rUsi*(+sx%l;!ku`94y z6ob65e}O_S@$$#Da&A0vaDpHf;sUWqz?6WyA3(NRUT^zH8y@i1%{^}8tIpI*J-b&m zP((r4!TUnzN84nq!@GbqfpNUZOh4%}anw!9U_``$NbjwZURon6H8>M)uvnB2aI%y=O-F7B(}y_Z;{Y(WGX(zHXsA=%OTOyk@>NLT#23n87|&o z!z*2`-b$C0<#?*g5hPl9p%TYS;mC^sRZfY%p_ZzH@Xo4@ZWOhsB6Bb|WBONIM_Yel z6Dalt)fEfNsB{hWINW?VOzp zm3m3u9I-_bv$~VWDxfV6Zt0#w$RhYBJA=1o;dgY;0VBASaLIBKHo`w8#P~Ywd;;xF zcg2n0K#R#BDMom&aQKE0_-qCrio@hd%2A_-K#=q$YJ^QnPiwSq+jWx_KZ=ZBPg_@9 zMsV9I2n^90B2AzW+p?}Nl#LQ0uST#2#I%-T@NmF)wI>R&?`^1h3`aP zV~uV_+Hz%$p0th0JlHk7oOiJ5m$vmJavk$peGZjb$&g3mKY<7`pepLS_r%v=>m}!S z-OHYt`2(FFvWpVafZ_S)QKtMD2`(HJDpdYjf9Lqs7HC(F#|%IH{ZFYe;6z3jRNc@_ zp=mm{DrZ()bB^U>h@P(DO!B9@%U;RXOw_T{4^VzfM-(&|=_TW+Iq_wG(j?4u$<5J4A3$%;7q`I^{lwMQc z8DxWh94_OhQ~Idi?#aZ(Pt!3Cr@-UoYN7ht<$`4@!B_8JCcOSqz>81>M3wt&<*{=V zu#4)`_N7l^g6k`iGGlq*Z4XnJ5`7NeR7e?rF-@@oHxF|ZQSfhcj5Vz?9V1HlrX(2- zQ@-NFeD4T*Ly4dkNwZVIoyw6I-T{n5+k%R=mx1SCrqdrR@ZRkU#n=UJ`|)*D@9==Z zP4I9V37Qbhi$!lSV;~Z%4SX~V0w*dIXAG=cq*3UcWRvxoJ{8wBF?6G$y5H3%vEY?v#Q^XzezW_bmBoGhFa^AVy&)Mn+3_ywx4q67 z;!APgPQKHA*>{ZlUe@OwT%yM^#^36n)ZHe7Yl2axCf!K^WE-lw zSE&p%e8Kman=v&vK;#VpYrW8fd9_yFc*G6CQhiHCa_V}0-45r9n0mh}U;(Rva;F`f z`pj$snDW-Z(&t*=b+3?N{PrrGA^c*_6MnN)WTpEFfh5noFTm%|$7w&v{8cCawbV=f z6JqJvPuI1i!w$M;v>gjhU{!AzJ@bE_L$5>9jZk6wGWMY?Zk_*)(ShkfJ1Y%e8k^CN z`~AxCc7h3go&(1WX#-|P>*yO3{+qLmCI-*3&G4Jz=qJ+&Qr(XASGx=}Ub?}B(eC}O zh#NBNX*=%JVRial&JlGZ8}7u45<|g^L8b;qTkgzb{bl6O7aEPsaJGNq*yW6mUSV-2~tv*^0(pPvwTZDlfz6cyhDu2hRFo&-Wzc>nUmOwJwji{4a zV?K8Q7Mg5BO_$^#fLFr4#464(JMO>JlhtAyb8kt;)E_tB9CHL1MwIZKxE4G;6IA03 z5^m(`bigi(IJgTzh?msyC4{NbUQGO%mR5YvepaKtq}dH?y=&pl+T1Tn^X*B`SS<^3 zHC(we>7Q&fn{?x_;GOvatD_>{CRd~HRwmpdwv+TrV8qAtAgKf^Q~0|QPi@BJ<;DtJb%L7FIv1wH>R-?SEH(r$ zI_hln06Csq)F;%%y3?pD<)znz&eoD@>=-Ue;-fo#S3P=8%3N`gC~LX7C+~06`rwh= z937Bt3!{LU9_NKcKsP*)Ejj#JK87AnvzxbQKNAs`cDZb!sQ3+6~OPy@@ zzi9Q|gBVAva5MKP^6>1UExZ4%K_g6W$&&eSpu!_z$0hbkh?{oLUr=0a6I5%VSyuZy zuOrV`+_;RsDX*+9udJo0BF{5T?sH_IHvcTmk!yV^6s3z8Bi%LF{|<* zX#r+jbCLX~`A4Ofn0niczvbxas+*d6LIKw}R|UA0HTltz9D8+vxa#cm>c;H+!V1^f zg`-`k6~<_3TN+DKgLWR3T3e%~WLaDl)v-!s9+ic9U8TOK{ewb5Cq|q4>_Rz}<+-JG zcMI9o$D%$cMS+7;gYG`2g{k1mT7^TS zlcfnzx3p@bhqM-F(85?#2NakWzUbuSSbtl2tXsF<4vUh?2r|=n&jfT=x6w?1AZA}# zw3fA7EhMYJQqtdy&tLu6KGJ9p9Pcb^NFR?&J?!~HXFm9nHg24-8kBciZ>7_CYSt^O z&d%Iabzx%FvIIU}Q?hH9p`9GHku|Y_Q&8ZD#K168ZmzlMS`%2Pi*42abM&5)Ly#of z0Iliv7Y$vGaCWE20pFQgXF6$9znK|Z=V5M=OJ=n^D@i*U_MnH2-HYJeN2}{A@ZhQ{ zeXz?4=dWUxCHe-T{p@1kUS%{u4mOG0F z%wIWMF5$QKv+Y>eRSd}ht3Jn_jZ(W7Y86+!r9btVov4IL2&WEPqL~37 zsgDH2ojoLXJh()Ccp$ZI4@J)oft7o*xo76w_Br?7C(0lEt8dXS>6al(50UWWzMDP7 z-2S7_6p}r&Ht4>y(PoE2Ok zm3Zltcu=$^O0zNL%EDOzSwHi@X4zoO^~Y;FhBQscfwf3fpMU`W>+2zoWQ8 zZuBDe6M_E-80>AZI^Bji9g;Ie%lhJ{#{IQQKTHo%=7rRTTKW&^6(&N%^Lxz1P|ydr zZ~^3|Z~~Y;Wg)l354w&hQ9szR2ffe5G49x_I6o*jsX0*_TPqUXw%w~ykWg^GLg{)h zFi>zZ8tgZ^x(vms`~beM>WYI5dQxhjhtTmWDa&%HZM)&|l}*8F*+8)nj{j=? zZpuD0&j*##oOKe@uQnIFtN7a`Febou1)u;4%UQ6_*AN3zqEXIb5fwR$H8D=vgE5Js zq0vW>g6E?^IkZjCbe-|rZubIv1|!9bqjw=cSHH*7585f=grB@<)lOn+cjV+rA_?YL zLeQNevWGt7fSxfN%aO=r-)TeE9i5|80^Tfu(MD(0k&m4SzXFAxb)AMYNPPw1;XIUl zfa59EoSb*Ao4IpouUE-rJY+zPSuqD;U(H0c77ArUit8~MV{L?>@Eo8))+aDMpdPzA zlG%yd;-%5xrBU+NMWGb811>;wsTRr*G63th{1UV~?U`U!i=rGl40}&%`C2XlzN$@* zZuw*mImALC^zoPkgYr_+P`?JhkiBd_T@Kb;JJyRrU>1H+S@A9#ezdgVNepG;SN7hc z%E52O7N+n=I^uQ`BK-Wcz4=fW+eFUAGm(Y|LsodVhqvDt5Iy7H(*P_l%9R=K1X7=L zg?IS$5g8Z4D)KFi&>+R^(aX{@#k2cTICjD0G}@)uS>eT$ZXuXwdekqlnRpc|UUt8F z^yAp-L&XQ$pE!Jet=^;-cGbf(Xg%+Kth*CY3=?y#`u50*Mze><()ciKkANAfe4c{5 zn9y5RIkXws70@aI&!D6&K(8cR|8e*&nZ4~Z6<36_1j|pz^i;^4>an5j>Y!NMV0@)g z8XMdKo^Q}ONHBgTtk6WnztMB!@ z#Ac5JVPU$9BwV4EL^@rX13@klLT1R(UWk;yKQZ?7u<%8HAG~CU37-HGK^ki=1j0YD zFs)}g{B$3}T}ZL802jdtTQ1IjVc|K#zc6+V0iR$%7iGa%E9B~4e0r+5Fo?W6%0T@P!dpK>j?v= zBq$H5whKtClBOXELg(*8Cm4XI8$i$x`6p)8gZ>NWrT+*-O8m5$`o<@PxA!eLBo>j2LTFQ`UaH_!yTRt%2}J@1TrA9eAbb2;$>Q2svz5@ocGIxFGF zc!j$u1`{1o$6OM!YPm*XsIIZ?%G_kQ$p!w`DH4D*!L*Jv|Fm7qgKdl#wnSb9t|BL z%mgVwMj?eMon{&IM3gU~F@_Z? zOo$vF2VTo`@{UJ1yCTcOlE9G!!hD7Op#^UVq2?HnHq@DuEb-?(mnI`Vy!o()El?z= zmn}So6Kg{Cta2{4H>TH0a1TSHr6>jmx=mX5CR%UrxcY^S=ts}_m$(6&2}UC2Kzl8F z<+LF?n{en>Dyih1b_`8Kr>b8y{kjjxMDI3H(V~ajzm}KS0~!C2=P%nTC|M9$FL7as z8ZBuKM5Y}1#)BH!w|^AoHLxeuj3NFPMWAVL^mbsA`AnUdkq3h zopHxMa3GaidlTJAq7Qp@GJ-w{kPaI6s(JoYwPDv=SB~R&ic#?!RyLx(ezCCBA7?=!RvQR+ngDKPD`Gy_q572 z3myFxt@dk%BeF)?DHy;e#t*W(FUb8u*oXK`XqcLyj%A5c#^N2l0$nbKBG*c>F%_^UNs56+&%mJlqd1w8E^4Ah?Ri>} zp-kR#nCZ;kZ%Ith@nmJKfW)^|ATLPMdMrCg0MVwCFfBOsU?Jq^UZNDAs^uMVZK2C~ z*QKqb(JVjQ9KaHsD-{PCg^nZ*ryOd%=iTrgyg^1@@(!Gd0nq{-fup3r2%-#)qs*Vo zDJ(pPC0!QU4xN|I5`|0xdRG($sh25i_z6DB4o65v${GYW`Q}(sLyAbk>M~zWo~xKl zg9K}fD48u4Cf;C5A6}j7=*E`3iVrgd+4Pb^qlUblun9}>dR^n&X1CQwZ2Dz49~hcI z?JfcFp`h5K3P=myZq)FSJ*mNI3)$|6wwGGjtAUBz#W+p1_vyx7g=zmB@=AwkCgog{ zn(TM$^T-cM7wy>-CLp?{70jKRdKo{9ds$-lF^xv<6W9Xhx;hs)%^a0Ex(Bai1+27k z(V|OwnLj_uK1?E7WCQS31!h0_s64}Q;m<&6al3#$v3aGAzG7_+#j*;D85nL^VdRsi zXYX&+Qa}5usA+t&sXREA0yjTJSo1^dgDJu~uF7=z=#XEqKHa#`7#)iENF)mh+Ns*Z zX*St4Bn&S+n0@IQER*;7IQTzyB$djgE|t0iOno8d1I17m13AT&nQ4_FO2(75>a6a! z$Ek}=%LYEG*LsoOC6WTp#l769?~c#T&Wu#6y0CszCp6VlGwRcs5sb1b$UDLV3nk|u zK&r6jj?IGd@|KQ-iACVBqHMi--lwxm2bGg0elUX48%hUevxy%2P z0rrn^^uNRybabqYY=6z+|5ZSKI-C4&osIbasm9>7Gu5^DN5+|?!hahYeWsKBPs$lH zJyzcU>bWk;|BZE!bU14@W7B_?_5DFJ>xN@6t)X@5QSE**9`9GPj$-GCvT;Vc} zo+INf7*6hEKh|YON7(#l7H4w{+XEQrlIl<~S%5F~n5)^kJ;yJJQb$X^mh5E5m{+ED z8dw3o8a+xkM%or7f9o@pT#XUFJw-(Uo$)R;0#&=?TVo)98Dxc=)hvMxUk3RejJ;n5 zee#g{0l-cuLuBvgA>|&IeUJy96w8qCM0PB0sI!CBuml(BIVqg+#O!NW!^TlH>^q;NpKgL6UMWZn>|05dhFX_f#_^HiP@>)3zSpT{Bpef?j@|IRGvAK!|9mwx;^p-qdy(aEL%!%F zi~WwHNJ`PMs4j0*%I$$%X>1OV38 znW4#^D7SZZ^lrS*9B?ni6_UnT`9*B+;3lN@jQWuchY4Z97tcn6Lox>h1O*BU%zR`| z(k=fA-bX%3$|Dn}=fAE@KdMjXIc%F~mj2z@f2VzZMKJxv)(!%R+$F>h%O_2vP9xIX^E+2-qwVr$_xA`v&O-Tp84l(C4 zaxu<3&Sge^6Ho*@!2I+*nAMA471-5XmwE9gQSuLQP@=E zdfn5lQf`o(Id~sCwTxHO!gMT58M%Q!KkTpvxsdBF)=D*|TIw=y8|7#>E7FhfQwPeK zQdFAG2?2|7$$l1f&F~yy86iUJ6+LFK4!mtz{Y=zN@E~$hi!ujPMxxK+kyE8>c8@Gm zII%E(WEDW%1d8bf+H}|mr4UmiDu;EUC5w8_e0NPRm+!QPX_ao?big7{s?3E|i=5^X zCp}hw&JrlBMo$_Vs3UMDa;Tzvv5%!{fp8rY5DhFg?)#YF4!jfL3(13C_rIa>@(T9buOCo2(ww zKcfiM{8-d-Ib-Dt7TH=XLhkJ>>;eVLheYkSTb9tMTe4XAleX-63SUWB3i#+1!NdOr zju62YA%i~$0pM%8Bq39iKq?A{>hEaQg5N}(+=(loCFe_{NEE{=7F>w}%9^58yJ})m zN+%c(E03&SySe&n&}7AJ#wk-`C35d$?eioek3&nQ8~GRFHvZ%%dh{j|({U$HRx8O0 zQ~lW%T&V+Bt+wEQq$UB>(dQ7)F?%qxElmNO3R$d&n5;(?t4fFW{W$vhV0C3F04WEv z^G-(*r)OG*EzTm(oI7N{ZOxGJ{w@4BDJ@+V{}5t9+kt#Sg5cFPf>>igVL>4gDKUwP zaKHMPZ4KSDETQDUP{?KV6nrmu2Q}@lGFbyrWOTy>m7+NnY*8lI|A(`846~$N^1ZvN zx@=orwr$()vTfT|mu-xn%f^3-G z@+UEkx;cLFJ>ncIC7cvnXJoH@9&>H_=1fo`G>@6-ZCxF7qXEe1_tal0x@0v!v8qti zsiYfhq=}w5!YY(d1{cYB6!yLIZV0*t2X}!bLQ6`Pc668$5eDP;rMQTg`ej*kfWG^t zpmWwPi4G!j9EGhYNfC3pgd>Q=*EjXi&HOB12n{8w569lOCzlAz+ube0^Ea^~dKnrs zIdGaef9YT7Z{)C4ROxMA5lVomX&Awuv(dyqhRLg6(rw$epM@2|Rr%drHG@wmT~hxj z3Gls9<`&7^rNjwH+z=V17}=_#)S+6vUDyUQ3CVq|UR|Q8*eJ9gUWe*8^F3t=Koeu* zZ<-MlCA|u&N;_Fs!%fju(fVI0bUHt|xN-7uhtkv8Qg{Xj)0EvawbyoHI&CG38aO`S zGY##70d(3(%*oCiR8Qum%V`^aZJq_rDQJfnJt@*b)uKOWY{S7Z=;aDXQ&u=Ss636C zkUyZQ)aEzQ=old33P{NzuPol74dh_fv8YmUZNh7qyuXy)64h&k#+p1uv5sPMVNn!6 zUXLp2xHuD=BR*ckOm6I=Q%gk8LP?ewXe^zNQ;vd<{q20MwoX@~A7N zUa-|%?7e84$LMbb+E_P2UcYWFOMN0)#7OpgStHGh(X~XKW0^Sk$~;_@or?O=WZu?B)W1(f|$5FSAj~FvRTd zV-PUqGEwrd4YP~qnfRfJm)9)HGc4UL9~H|vW4AH%z_qU=(z%Q4YVC!2k(sZ~+k3Ze zb?3142sk35M2Apf*lXTSonl=oUCgp-N%rH@`Ep<3>LRhQj9Bw-YrsvM@- zwvlE%Tl@{QDIFEmTW9c{$RC1Nfe(^5G8;L&KD#1&kQbDXgqNU{?y9jX9RPej*yMYl zLVTDc662u#IDLCK!*UiMFY!;V>=a(IZ2l=mS)yWa2QCwA6HF7-A#gVUMn3}7lF+$6 ziWt{s;Adc0AIWLb1v!3I(*1qmF!J~?;TOl;vGT)?Q1dl6k=^g79ao9h_nqV^uRR=r zz+bQ*d$1A~gUxMHs!FZD-ab*<`zq(K^tLE!ld*+!psSoU38MVy=VD`OPAjC$)iHhi6((&r0rCPo3?c99=s++liGVEIYYn)`C}>E%CcFWC%Yen7 z^R@_BB+pnR01)6&aa*ka&FpTLiQ1%pALL`XJNCpc6bU(o*K-R<#`$;dW8j}$Fn;Em zt`4YgA+T;0Xe#eCO9CgWUgAFL-r;^w7$9uyUcSLJxfc?)85`pgJvBGE#azz3H=3J) zwT77KY$RwVRr1F}%Q;0+VS)1lr0sh612px9*Q}q2H;qSA4Wpso>0~ATqW_}?197Fp z5s*xjfHZuftGz}k&jf;q+|8)k1}#8&X6Hnb| zJ7!yEYvKi9`Oyr-F|{E=UdQyu#>PS1AkLkc-xEVArl}Er59=er8C?`QJo~wZiIUD! zYIr^#_<#n92>QX!uv({%8Rux-JCeNxzHq$AHb`C9*F53%G~Pa4?xFkO2dt7nC^$eI zAx`mr-%!LtZPCK%d_uVzUl*fWqlQl8yy<$g{>rxD{nbyRG+qb-S=)}P1+Upi3MZYz_+WvAw};JiH5~cnIW_h)Wy|$fJOSCYMHj7{mK*K%i;$OB z8nYK(+AC4%IFh@^?w`|X&$44){@CNddG8<`L68Gag%!@(0lo+z;%KD0*+aD1!c!1a zKc|$Z^zkx1rG46fh@j%~DY&zqiRlnYBL=UM;8fd3}^-3J~(NAMdOomyn_ zpD+P&8GPJ)$go+T0h8WujQIq=Y9PshmgE7lQ@DLM_mK&)@2PRBaF9k2GB+_$3@vz@ zaW@qkWK`rllsP`DwXxC$z-MDj_tAuXL=6Y^tB|Dx0+qV2mIuFluA(q6NF~BI68a@! zv8_z1*K)s;oQ&txpr?q<({O7^no>r+#+i9S_o89FF$w<+8ud5 zta0FJFF!$Q(Yx^pS^00v_-C?QSc;`qHxmj32PM-iGBgfxP%M3As|b@2W)X}p=PGfW zv&>^@oX>^zlk~aBw&6nVzsTJasF=K?vFTqhH-N0 z*^fgg)KIIj-x$aN+?H8PwDxc?iKAVLRd?Dv6`;9NwYJYnjN3C76RB}nz^&Ke?$YT@ z7;n{VKC&7m2-))+(q@0+N<;5Qy@BA@x(;juq(s&!`6dCZN)F}v3#n-0A#EVGE_f!TFq zhVB|9IE<4VD-EB|Z&=Dt+mRF-Uf27Z86L->JfZ5lBLPd7BvvI=1}U4~!zeBfG}sr; z1#vT!OcI?((Y2^`;rpJ7U*kmi{e4NytBe7+Q+Qp7;WEJk{+;Xjx14T>HRKRoZ$}kx zw#!!kBuLJVsf6U$Tb-7)cr{Pm=aC3-Ws#=$>(*1U?b!&8)~BLq`{7CJf^Pdpw4=x&+wjL87D_X3n~nS@TqJ-7+*VpnSc^0kAPl$k>Hbq2 zr2fFSj&gN$eQ&S$giNOb`sXo)%0V7|Z3(r?dW!;12d5)0r=&6^QN_hU>c_zu@+XEc znAg=~u4*mDxL?CttD`AUnYVSR*n|e4!%5iGcNCK_9_w?@Z-xGw4p(U~UbEfS>`zjl zoyS`&*#9Y*^(QZ#A*Z1YN7=npu@P}0w&&_0ad54hKi8zvVRw&Mp&+7$x-swNeX&8m zn9|JxVn-Jh8nD1RMPc$^Ets z>Fa$V5VLQKd-MHx&7OCNeHTJ^f#LFTa+3g;dvR2{X(j07>(^4H%xqU{Ei-?1X>#zb zk4<5{2X*V}s=H*mNIGkPW4coQ*s!ngIO{BwI(cTJeH~VW2OITB5x^9hbJEQd6KWHr zx3T-mlGfwjxM#P2a^+UYlDz%Fp?Q%w6m=asAQ zq9AvuE!&K>t>h1xllnCDSnc?cr6`M6M%@NL-fR5i(D05zK(!g_fYb(C__L64uRLnPovw9I~lpSvEGqjTtjvuL87x zO{bUpV#BisWbkKQB{s=|wt^6>`4 zBKXnuS$2r${;COhs!=PmyAYp-2CppY>gGpatqBc{F8I*R4ZR6HrEaWfTYK;zpbxTc z!^mi98~=XIJl{dFk}I?em$|ZQ1^K1?^SAnU@&pktk&|Z4{rl24fCSub=Msym5zrF1 zOgUHol>?;&*zL`l-;aviiu0e%Bf%x-oyyNPN|QW_+?n~|b^04NiAt;6MMAp!=&YaZA^kdW?QHFA?5jSlnGKckmI0!}kJxJhDDR6Z^ps%}heQ%&>{GtgHYA zsLG_O!rID`^3ufI`gBX6Ow;UIkt%I@VWk0T{%C?CXIW81qNcSus-<|L!~PX_B5tX~ zud)E<1#8mHLX+c&!3@bcM5Lc!2a(52O}L))`iA~Quo+Qh@!4`Ode3=}W#4)Dhc|vF z?1e5%d_*I=w?Sml zUA@Snjb=L%g`vc5{`HLDv7Lq?*id-;A;oxCJL0ZcLB9d)Z(!i>k>D7}`8fzi!fEEF z#2475T#*KBpI-w}5K7=YBl%BGs|Vz#MC|4R)Tkh=H;z-V>2m=ZIu~)*j8KQ;KqHEAfCCC$z4}U6aSo?0CTGzi zMUrwD7|oal=y<@AE_vKJ`Br&6%f}omJT+)xun7i{j5Ty&+!+`>eg^;H9jOYau=Ma= zA~DWX?3TX5w~i;*&hX%(tx@?5HkvIMx}8B7)(bxMCL~?>e2Z1o^)fa|%t6GN87eT0 zV!@5#p7m^Ek@pN%yr@vmF+aTN%IQ?%YM&lwdC9Ped}>gMb$PqY_-NiMMN?^Y*oE4BhgIa_|s1B1j=6T zB!17}N~I%PEw5ReK`pLKY%$*?mhn2(M5>8d(R>QT^nQi6Lb=TLH^~ceN&??Pcrk75 z#`{kp3XbJCIB|cAX!13CSqf8ND%0MKi|ChUcI*p-_IHp8U$*Sx^c@5D2X{et3!_l` z*q^#^PnuVhSAeq+j2T#GFh%^0!qiy?sYvW}250d*SuOMF&N83J(V_k97i49{4rfsn zc6)XM*y>CguA!)0SrgQ2XF|uvhW08K+8Z7DeD9r<=Aqb%yG@?1al_M^#x-Z^GZ*hg zwfU(nFT@|^^&Be|4|;r89C6x#Bj%`(D8!AkfWfRIp5B-B`Ga7Pr<%rX!WTw=$ ztn%bo1@?yEiAe+N~6HOxiq zKai)Wp@oy7gQ>nQm5iQ+sjZ{o-?=CAf9gHQ#z2op#mM@_JegSN@aX6m*#7+}7dECZ zqV-SoN&f{x{{?;0F?@aTU(Ry*58(8l(I*}4fA2oWKu`a_qR+{%SuRimmz+XT>p6b; zRGLHqb2C3|mvAG`0ZjB-LK%oUw!{FT<0wtP}NK*33P9Imic*IY9% zG9T2`uKo%Pgf{s*pEAXdJN+vJ1oqWb_kKr2qnIDU5u5T&aINwuH>rJ1g7D1yL=HL2`hFy<;IDVw|Ib!- z|HB6RYYzG~0O%ha^bfoIbNqv8esRygo&Q5ez%la2Q z^gkY%|I9=GW83}Lt@szM@aSm&_f~jxEX@CTdWx&Nhr(dOXNr3jp@f&{Z>_!@ zw;tvoUZ{>Id`Vi>0ajIB-rqz7zLsQ(7I;MFafQ&1csWx12%`M6QtJEyzwn`9hypBH zOjm+98(NPWOu@%F#(`#j4OaEdWTISm&G<-5zLYiN{Mc)DjDp0jPbt;xpH3yXGCO!Q72sb3xF6`VK#7DnxaehKZlA=+cR>-a^5yz%+blwoF-} zI{i_LqKJX}guyhr=&XB$o)9@7@8#vRFoyN_Mr}|we2sM{7o);^1&VKJs^iw6qT@J* zF+$)7+QRZ+;VC$Mrg`w*WL<)mhST;4SS2fok=L{;qYb)F*P=!Z`#%$(z=%8rA9UVv znuC8oQ9<=mC-+rM($=G3#AB{w(KfXUctecC3{gZZ|BlZhT$a`o{9PC9)}am9M_PH$doAn* zA#^^S9N=r*YS`6P!wz4+)UU!Ps-uoEDUTjazzQ7@r8nm*Mb2Mylg@l6P0ZJ(dgeR) zZ?%1=oRgjsL&|Tewu;wKwbsGaz>+<^Q+juK?m}23-Jy<7{tDuEWxg9cTcSDtQ*bZ)C zvmP*6&YB_-k2`Dd9kvF0iOC(T%6ldu$m>@|D3xot6t;|v17Rg|h7=_Cl&|7Mp=37> zFt&RNUHh!uZ(DXzDhH&u4q1Ri`Wl`QsLYi5yb&dvJlGCq_xqY}=Q8Ym2PmFFS0uF`Z{LNFim>}M)!=P$!_ zmEH@`V32$|+0 z)aIvXq2R6(o>9VrZ_3sdO^A8sh>j5gsH0C~X3h}?C;iX+R?Ny@e~MrH^dw`V@wUrL z5NWNB;^DE)4LlM(JWPaupI;d)0F^#4^z08 z+d4S0O2`x0jHxygU@@?t5VuQ=n?W@=R+HV-CD(9+WKz2-m$${7Q`Fe@&HiHa z9kHu(;sj6%wa>E`Jgp&52D$)YU($${OWb6g**=Y0=2XVLXtk>8HH#Lrx~pED-gcg? z76=HNP*byMQY$Y%QC&DtU1%xXC13t!0E$eq$E1ZnFs&;U*8MG_V6PwqJ3Ya9mlvB} zkOU#B1*2TQv<-=p{d>Bt>DI!02(}BYWP~t-o{(N%Cm)W;Y7)58lufC1>aW6lsd$A= zgZkFcG~;|(*)-x7S}9!-hyi0yM148RT9c`8Y?#j8bFdJ);d#DA;cZ>g zQ61VbL+y%|awcHP0PwRVuIX%@f%N``lC6Qn>IZ|`vK#;|7gYugpc@=p614U6k?cbg3v!`n7V5Lt0Wg;y94d}j`(P@UCHB!1eH~FGf}D(KNqHJ0`Oq=U z#rX+KqPn{kGNRvl%T;us7A`Odx?IQ*IRQ2GHDZe-$VZIB2)C+5L?1*E^MLmX2wi(4 z2D$Ti@#7dF$b{r2Of}ezv5Ddxk=RJCXq6CI6N5mj9g&PH49@82IZG4Ehv9o!`Ne-W z)xJ~ApPREs6#z)Z9S1RTfc}Ut2QwiP@vz4eAWA`9tv+MFAOKKTuY#@|1+jbIJ|FizK~wM~Aq!5?!mf_2R8lyJyIu zQ=culv#!Qq;7*DQ-Ik8R4v=nEHQ=Qn@>YxkgAy`wA(SQfq`8!bl3};%P95P1loT+z zSQ?-FGn7)0IPA-ua-&v~5{MynBbZTf1?7T(lCotDH{!wMBstAy^+yZVI=Te{B!i#V zG(vyG+X}y1G=4Pi893 zYb}mncR+`g+bW5uiHsmxa zSohYq2B44zz?8@r)L~Yz9e-mJE|6s$&9vI4YNl=+6t7cGOPIYaP<)KNM^2v(RmL1rJ$=OhlyOi6aY;M~MBiV2{@AaFM z!VCs=R|e&N89uqK;(q_Q`;2SoSUptla5~y-q{L@>#9j4N*{~}SPOY6%+-T8%|LAqM z9xYMVP*UKgVKCOZMLC${V|$qY1p`T_&JMr_)W#o^BQ;-FAr**}Fq)AI4B*(}8i!@3 z&8BKCcCp9V8hU|9>54Yx0duX@B4|~R7U1W0GDmhXS7NN?L3iA%E=$r+2RT9%&P^l) zt#(p=hSjcvpxh$W;!u&=T$xCgw!`L-!i~=DCXTdZA6jAtUxH8z?md5V?~A#6N$L_i zU>@)sZGlg1b$gK}VvWu-%u)f9faYbfiWB8Npl&FX2GiU9~sYo+>DC4++lYdpG5x^MGfkT!?-Ryqqm?2PJ(B+^nsn+#KDrO2}1-PXV)O6|+0 zne+NHwa;a9E9Tv91e{k>0b(-{I+F{R-VTFt;87xYSnURns5E+$^a*^qBsRRCfiJ*r zm;FO!0u=kn@0K-&OZz;N5(*|K=yn=dQs)B*s2gJuze`K_YiKj2e2~z?r*0Kj(r-=c z^COZnCvVebkBTyxz8p_p_v?vx40%btKUi-vlS02+Fm=8o4TpAmn!V*k1;U(yhqqiu zUD9%7_AjMGKM#Tl#nK6sD3(MJ+Y1{J2ZJ5S*Na1?Q!GT677au!_q-Xx9|U_oVn=tH zj}xz?VC39PP4Kc@(tBJvykT4nAE35aovfiR0Id>m_i=@=OVL8F6wI8}F>=rTic2-y zdOmJWNg*GSHg`0ywOme*nw$Vh7+DkF6{a9D{sqgQm}7VnW+@YQhh>IxB;|Ba>IzZN zwVPr>L!j}QbRF&FaaVTZc{ir=_O?cu89%9+Xhc{W)VMr)M_+CR3wA&f+$+jOQ zVs=W|dbuxR{l2iUT715`OA)}-L!S&_IQ0V=f>S&Gcxc9?PqN12fw6*YbAN*V_&Xou z3PKWxlsTYM8Rp{fSY>R55TLpO0Z%B!fICk3ZJ+@TG?{eOXnu34*~FTwli*tMUQxa~ zwareIa%_cW=MwH#)9D7!!x?0eKWsedRcx*t@tAHE%~EYnD@%vo=Hpp-TBrBxe2g|c z`F$dv$4D% zK&i<~HUz9kLCFEtKIijfgZn>vRJQO{JU0*YX@*P#F4kCjG&>?G!%Z5It(@Wz1SO>) z=z@Q(Ckbv!TMcY0bh2_gr5D2WjRv`JMX(71CBz2$$Pbq=aSocw&&CVp9oPaP*436a zl!j%-A~MeaAy!cS=(D0#M}haxP#Ms_#v^l6b5F3_W{ZtZ*#%9QG;B|PbH)Z}Qv=Tk z>)gpL%f|x37mt!)P-ko+VRY#uy<(&zts?{}aM0)=9G)8If9k1PA}sRyTww;x^LqY-^x_KGmA@?ni=iZPLs-=X$y z;X!F>YT}AtlVF%MGPPnOxA4tw?5U!MdNcVN)Rs{L)Er1`hhr*0k4 zZbzYQqeNsKstcsejL(~pvya>2kW%jgTeS~^FZO1#>8~&5#H;Wem+2<2r zN!MY!fw2dL%0$5cE?&m2c0^~FUPPN;+K#*g)VcFo~O#vBF{`TWs+2ABbmjxuq@y&RM;+!e>7x=UMRt)&wxpr6QIuku%EkR`pyE9*Nyqc zJ$)GI)?X>>qV8>@k4KK;gdCbsOJNi)!JiY!p4Tm_ol^KQqMNWIjx@a& znHVDR+A@;rpYgG$YU^4|qlkKPO%>(v{Oa-HRioIJ`xsUzDx!UQ87(Vps3xR#uL5M^ zO?I9|Pl5*Du2;8tdi&`1$`YP)F)9WB`i|)zQ{0K0(#>7dUS1*T;F|Z*QG9fM8(Kz8 z(%|3>+WHo;ynh+(W@|%=!RyZgeFsX=p@TY)LYAg6h{U^+`@z@Qw1ye^LXgwJZvEXc z$M8PKSG0Ok`-c_y_g26i6l=k|XM*lX&!zQdsWy+qkajH%DQ#^NxImblIxlbMP96lX z2TLS^jgAns>qpR5LwG≪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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 f13518e04..000000000 --- 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 be8936a02..000000000 --- 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 b397b7b28..e0c2102d9 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 5c16e5f8a..cd8188c99 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 27ea5f193..3d04bf682 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 d2b990122..45dcbae31 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 860b23da4..24b97c60e 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 81f047b92..000000000 --- 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 000000000..0d102962c --- /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 5787797f8..000000000 --- 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 7355fd77a..000000000 --- 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 000000000..c7bd07a99 --- /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 000000000..090f57017 --- /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 e96a2c647..18a50015a 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 a1f5373af..60c12d430 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 e37003ac6..30d20ebbc 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 76bc763e0..450f1dff6 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 aa7ba59e6..072517395 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 6ca1d8cc2..c12c080ba 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 899c96ace..8fc1a1dc6 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 2f538a302..29d19a639 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 7b39c47e8..1464aa065 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 2cfbede41..8f96e137c 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 59e26ba0d..95b511329 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 db9caf08e..0ff157f2f 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 9e5531938..7e8e7a69c 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 5380a359d..d9ecd6172 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 de0190093..1c659ad89 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 32e551b48..e95ed8769 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); - - 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); + 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); + + 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 474cc7999..070b28e7a 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 19e087292..ada26b092 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 85d158e78..b8e6a78ce 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 0d5257a88..a235dcb47 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 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); + float &gate_delay, + float &drvr_slew) const +{ + 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 -{ - gateDelay(pvt, in_slew, load_cap, pocv_enabled, gate_delay, drvr_slew); +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 +{ + 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 -{ - 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); + const MinMax *min_max, + PocvMode pocv_mode) const +{ + 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; + } + 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; } - // order_ == 3 - trilinear interpolation + 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 5d77e931e..375ad68ea 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 eb8d40ccf..7a3de2af4 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 6aae98944..f877fc458 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; } +//////////////////////////////////////////////////////////////// + +bool +Variables::pocvEnabled() const +{ + return pocv_mode_ != PocvMode::scalar; +} + void -Variables::setPocvEnabled(bool enabled) +Variables::setPocvMode(PocvMode mode) { - pocv_enabled_ = enabled; + pocv_mode_ = mode; } - + +void +Variables::setPocvQuantile(float quantile) +{ + pocv_quantile_ = quantile; +} + } // namespace diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 93804d879..818181275 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 6cc3cd1d9..db7af6b0f 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 8682f6f15..20cb52243 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 ac1660cc0..dc64f0c00 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 bb9b9282e..195187c55 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 0f7ff320e..12c795ea9 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 2b9d6a361..e8a157499 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 767a44a17..e3e3a05a2 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 0c1867041..b7b4bac5c 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 ddbeb397d..56590fc48 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 349589e39..1ce1cf43f 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 4d1bc3a0f..21361ed29 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 8389c9c13..16bde0881 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 e1f9cba73..39f712fec 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

%;lb+P{ur&aFZ9xTf1 z$7$N<+Bl}Ms#9RM)Gu_gf+bmGj;X%EO6v47y2UVk`ED;>hL@#-A59VSVPweH?mZ_O zJK&eflf_8|a6C5X(M`A|xjW+%q)*Gr()(p{DN14hY06RRHo{o zg?qVg5r)afR1CROUCjX0Ipz%IbOV@_!#D>Wn`>zhto zrzJ&?*AmWB7`ott?EC-|!1~mWlFuJqh+=NElf3BcTt;W^gq_L8y3UGEH@V(!4lW@w zlIgq@)&y%oy4l0x?JSLD+sh)W9us)5ir2&n&$z59BT(fYngX3WCcegwpBlOLB1BaS zT9ciO#h|L?u8uE5%c4=8wF%+#e#QH4BcKCJfoT1z$p4pt9gSq`xQ}oB6dEA{1P;** ztVH=Vj6rX1bFO!I1H7f{2Z}I7!e+(jRCdN%Ouz#g6mBM5(mnF^LgyH#OsE)`U`)Z4 zO@Sjm_Uy?E2cugV@!Bn4!cgRZx_2KfGxMjW`gcr;Yc+riK_BojNKIMAqF}$=VLK%z z!#K_J7@ojnhKJcsOt8T>_@3~D)tsHMRAFBa8dMnCaWgwQruVRs=~kq{w$ClO1GR?i zRJaq0rx=!$X_zr0-BIBL>J3eSI5NBGjj_n}Gn_LRgGW0)6V8I&FH_GJ!%X{@ z_tTIJ65shxNPQ4z(SIDz{ylO1Ydm9QV*NiXBQ~~w*nqRK{V#<~ga4Oh#J9$K>?ceg zZxa9it^&a0cntQnxiVP9zr5eB&a%(a;`R|>EO9E!t~k5i$~;D*ReWu#-i}k6pO1BQ ze?qkTN6+C(Mxtt(i-}ilAu7_Q-@aYzKQN=m28@NW^8Fv3y`R5)d|W)=cP$RTcn(c? zuYG&|sNvYK!Yn#qE7)|dYiq5n5>b~|*xb-X7Uxu$_m^XwOSYSfPlij33xQGUzM5bQ zVt&tX&Db@$AEM;h+|2pi>H8WUcRf9Q9C~~|yuEOK+dm(%4LNs~D^G?%9Ir8`ML|7+|(Qigq>|JzRuM~!~o@Si9zB&Hw#qh7DktfDv zwIeV>d-O0<>V5Pq6nBY3fZ7VZ+`%M=hG46=f`tn6yRMCn_JM|qi}hw$sU^mu$7b9o zM^s!CX!Ax^j}OO*<6|$<>@+EE6Fz5l{!Jn>+GO^{$X&(-u9q$Q`VzI-a?Exa)nE2IF-2CpPj zI5)3~WXc1nlA~;i64cs-@SA9P^=9YCezv#N^q^8(9pwg(Vk-@nhWTQz4Y6RUzHV>b z&ypa*@$;h9u$GNZ8oFb!nSO`&C0aI3Uayu<&DVpU$sTW(g1ox!E5)I?r_3ZUCC|BY zTuAbldL*L-kQ#8R$EI13$_aO`=>xf$e4(`_yQIPa^=+-MA%A%AWN5ZG696sPQY0cl zI*L3yfrE2N9CL$p1(6pCHB2fjJ@;V~ZioIU>z{=XBgPS2AE3GXFgH3rYrv10Q}7## z!m(e?LdzId$L&1RA2VYm8IuRt!`Ds+LGSb8gCtwkze%gNbmUJ*c4VKo6CQzCfA zUXo{k51j+jRNy_sVh#j z(4pKSam}mQ55CWDoXgU^g}L}hjP$6MjF*peiMNcF;8ojdBgPO0AyaoMIncO#Z6S6o zN;J0=5nmVJVvIP-2G6Qks)0aG@lElc;;zg1iJlneg&-?eczq zKyXUT0NQfm;x*^u+tXe{y-AZgkZvze!8+vxU?LZ+g1usRBgGd(FE(~3)8{|}%$GU&WegvR>)u9m?z(GV&OGh#D%>SF0vb-AfFf& za2U>>!4F^g=TJ}t*Yk@sS_FK1D6q8DSnz2+?dA3WF=sodSHlXm=LIn1HbB1OfxDUS zEqrp^NF%etC>x^((LNKh@01}TgMhh#lX9$0ZzwjRRy!;oX&HZjX$Yfzd@n+@f~jW8 zoyYklu@uz9iYZ5=yCu-x03W_xOKnG?W8zQ=CpDVJ`>~V#!gLH&_0V82^G?ByX2S2D z6tMxtha5X&QBeyq;{y4kcAA?0onJ{$`##=uY%r$L<0Rg5s5V8ChsXp9Pf=OrqdU9S z1r+&}({crxR-%p*Pro3k$qljofKa~buarzlO|gGr_`>hsULGC5(P^zQKzk`}>){YR z;w>X6#yBOi$!Qd%@Z!P-#(y568@?$nWDLxb&sAG{Z;(M=ms985RxT$)M=rSm%Dy!YnhDP8G|MAxMuKcFagX{EYIn? z3sKy@!!X_QPdly#i%h^s8&ra~dv{!eW;fgZRKwPX(Z$=~PE7A_Jqj() z9|lvxV7}be_pK;#f5V7WxV6QMa(ZGF`Lx>SgFH(;QAA$!R(yo9V&aJcG&9fB`Un$G z%Rv(s9Y72NN*ZJqVrlZo8CL8Tt?0q4Mr^PouVC^%K<^-DL-8}2BcYFOc}N2G+Vzh( zvU%zIB@S5M0@=dy>Q`p-$)sz+k&sDya_IG(62yyYL4_iq4{5!&l?n>L4SoEAx9l&Z zzZ?Va&CYp0-{K6x@fb!I^^W=wx9dIMJm zzE%0BIp{y5&A;a$Muz|6iTI~}2d4k6-{JD-nri(Ap#5Bp@K9N^)oKYA$(MXN$M@G9 z{Y~yoFMX4lAfwEYpN_hntN z(R;P>t3*tADdXBDAz{bq2? z?)K^APr*YO#!NM&HkJBjXO#_nU0y+(=L&*^MIU*7y4jq9G<;$&Dj7_+t6jJ4)lV(7 zYq@In>eK$YZXp#l(r{XCZNC5LzGfzt3`;c2e zGJ*Xz!{$7cLpx2&;={~*JNMNz&)`@UZOlbw2+}1$0SzbsC!}4$ogw&pH1=&=sw|>VWg;iykJJj(NMv-BygZ z-akac?2Kh0)cNya(P9##y_`8KsE`pr`sQ+dnz48t?*28@2xv$o6?}U({uPw5hfX2X zWbdGL&i>SuMCz#09srjvM$jS+j8yaoO=3FeBgni2wha9}wvuBC8S1|i{op);ggPZ* z%&)HSqJP{XfF~bz+&J5r(K$Y$=l#R*kiK#w_j{7yf`T8p&Um%FTgQQ`h7s_u2;H<| z(jMkW%p)J|xMFTVZIZRb)bzAxL=tK}E5a7IMcjWcMDVy^W}&&H?2LD(_7;I!Hx8Zf7?pEC@|=mTM9-f}E%bDfZK2eR%VU{a z%(Q>nvz$+v4RDBctX6?aB>9-WK$8z)V}=r@^{>B@h%PJxNKo@i2TSG5>Q(+A5ddmc z3Hm#@89M$c*XH zmu}h!&sdCj%xWTzWE@qQn{%I({JAcHV2hL}RDRrdRpDFr?9D*`A^!L?QWoZ##V+nk zP-e{ZUA~_cE1(Y?-4oVixbQE)?7=na$zn|gn@F{|(vne*`UA1EmXc)pg zrz1fyQPhsv+;T6TENQP2(++BeeB*T-AkeQ3UYGcWp)%5nv8>MqrN68oyuPr_r*|YP z=$)Az5FM`Yw=@omF;U>qJIUWW0_52E&5RHwe&V8T1DKI{A0!KsaZ%lU%Fh&aC}d%8 zi=ZxiL2krEYQN4OZao+0ovah~GJ#PRWvq4P1+M2e{FLl;J&x!tW`~c+y7Bt0K<-r9 zTR<%wU8F9%QOfx-X9q(5gjuA6SV~AL$yzW2%)HqL@LU7_qfMAY?u*gmLN&=>biL$x z7jW(-IlyIOxL|f8q*`NlYSlmeks8;7U}ae}C%t3s^f?Sk%==p~ae0vP3Shzsrr z)HC$JfiS1p@V(=Y4derP#vE0}xegJl>{JO_>I2s9fv_XC0*ucj2~)hcgK@v$$^{V@ z-V{P!u&*FnT!cd%j?4IiQe|%zQcqJyfv)2h<;>_U#26nFNj7-;^+0R>0toHDF8W&6 z^avNo&VA5JL(n#3g&S?zuy|L+Rg?e40PVnziLFw;%2iiDbl97pw%JDkH6Y5UIw0y? zIjj*(-!Fu6r=}Di)-_i_h@JpLmI!$S9oI-AFM}pshMFx~+z2*FAa8*-8CFhRE#f=~ zZSZt?1)FukKfv}~4)?p>AU-}7y;5Pm@>n-mCnLl@0^Sht(l%`BJH-l4Y^_*)%?C+_ zg`OYqI7#EaR;*|BxJZ*1Ubgz-Gi9#EEjmRkyOE~bSjBl(!FA6t&29{kOl@Ih?W17gVEteE zC?+)|V}2YIUGJ(8KY^@9Y+jNcdi+&~SpdLv7k}IU+(;8P=~(HxOEBwi@6ZAv`G-I8 z&#@@{E|sCC?Au=VJ9g@jgWDMyN4KY&pRdowdEV|TAL=y$Ns+3-VC0s-!KI(q!|jvR z+vs>~I9<;3pN>917+{;9o@e)$TeX_cE}kddA>SBVw#&<*f3a?~H@YZnWXb?kHny%) z4boXE42C~1-=$sK-497{udk+m$iCjgtFp(#_v^#=$G>mxPv?h=`61;b*v*OyDT9|4 z-Fd2AD>nP10lxtBHhgTJG(RYwyoTZ4D|-r8VxC|-S`WEm-?wdawHirfs>I9Pgb6D~ z)Vv!N2RA^gFM@S9_319Ko_0rd)-(QYI$BqM=8yfhy6Bf6*8>>MLtg%)zI+m%v_>cY zJOwzY0ahU}AamgtZLtbD(HI?nA3aw@dyay}M?9kd4$?Nbc9?rsQ98EmNa`6Y1BYT> zjg$HUwbD*rxQg|WCgAY$aAWuN0+zD+LmIrZW204`%+GflEk3Eiv@nZnL(3{ZEoUkr zCsz)aXje_TZ@cI%eU4_^@<9F*h`mR|RYXsCE4#1AGBY;k%NHq}_x6tFX;;IQ1*a2W z_Af&~Uexn7?l+W3x;wXj3N;JTEsT>$W5F`mEGkKxjBqG3@B}>^c39v-t$tvRrAv>c z;XNR7^e%Q=DIP_>#hT+{rZR5Nm>}g_fuXO&v}vjk^S#e4;w2sX3v2NHf%lMSRL`8 z#eseU-n+~A;RQQ?GI0*fh|}~8xkfD=D5;SU*4V;MlM@tUq0$_jb*X^8NO5<71RwC6 zT~l^@(wK1$fDwsnw|pbzvP3qm3$lkOG1{Pe1Ko9G`gVT=BP}U~J7!w1E*pUw*Zd_? z5Y4I9iG;t<5}~LLB*(0XTGr;oQ>f zoeWHTlA3=Zi<^~(CMsxVK<<5*3jcK0N79q+%js?IN# z>ST}2HZ?do@m%G~nr71(7w@Y~RK0%-0cCE&sJd%Z^_&T&e#fgLeFC8tY65{mBOj$l zQ*KWX+Mp=bIB~(_oM$2ydgrI2MBZfrtqbM`)Vfq|}?r z!^g3klto_+8f>$`O`eDf{nuz{L13;FReN$9}FH_Xy?@cTL&tSF;m9z3v_(QEl?%+4&wY&$up?kl* zB?X*toGLmq0A$utoR!&CGW)DZ+z8u%N= z8Pw1WjU+4y`CNP1U|lXhUA>8qVsBv^_kqTGKLsNNAr4ZnrDqFjan{41QAbb=5BC{| z(IiE7+Gov|lNBNZ;gwKP@c@HCyCw!>VP+}6(HXByhZy&uYop^S!?cU!sZEx&xlSs? z;0UIu^ENqYQo9{aL|<+6w!X@TYzT!;jAkfa5hLuk+0|Y)C;P49hoZ?}nndK)20is1 zc%vw^Wt>Uu6NrDu#`D+vG$t$U2nbGJfxSy;HLs|eVbi5tvj=Nm&YNJV)H2h`20ZRB zr`;4-l^X2~5O91x;m=cYT~mf+@F}Xxl3gPT$0v@tE}1l9{ynLUkcIR%3k2RIxE!wY4G^ZEb;GiJU#1w zdm5SP{~-zf^YVX5f;XzGTW+YJ{43k=Q-rnesCwE*PoE(H>`xoQ8^oJBB!j$1?2NPa z^`70XtBpe=lL2xNcQobtq+DY>she@$zdyhJzP_EEzCS*H6a$920(6Zzxw6zyVIKAU z!yJ7*TtDhR3Mj6M!PEV|hYfvij`8KabXlg8-Oj;UxsEyZ^ZUlzwu)xUNG6Iy%Wbf* z(P3NeH2cNO_FU~UQR6f1Vpo&m@_jxTF?ncmpFjND+sm!x^M+G^32JR&W=!zfj~_bI zeu)9r^uuxogvU%LO1e!`=3{4)sxJG&DnnL;8y&%D_rr-{AYw7E`_LIu7Ok`DOc;`t0Z(T<={)W>_UV(`$h(Z9kQ|> zIjlj?gFTWQ1@t5My`ZvU$C<+n0l6b$T$6v6G?keMjU3*Rj$E4}cRA{e>#|_Buxu2# z0l$PyM5Sti^Y_A1dyrrEAYyJ2@HajOHL1!*a$LQ@=Fz!ls?KCfD=ZL|bM~Ulv23jl z-jM~gIg*Qm$JWHN)q-lM9U?+^Wis*I+qy3VWl}4@aMVAo#h3EdU6dbFFp0ruM+1ZO zJFE33nW9r1zzjEfXiG@6+92y%#!eWj!djp$^o+*$?YVgS2308U zERXC*p9c2NYFF`Z$NeMRjp}|d5aBf&wR|?iGe`M(7pe z2>aDP`X$j8g2wqyg-w3LrsjSSKtInR;nIc#mc)Cn%aQ%dVN#+?Jw`s) z8cD!>I^P!d%y)+-rP~&dx3gPvf9_$1Ou6}x0A+nd9~yljf=?%PzJw2Rg@o`Cl+);L zUrIGd0g*koN0iu64>O_!bt`-+&wspF)aY*@?Qc+yX|Ob^U^cPdkmx&&zM)q@zoAG5 zXcHO*!DT6fJmWgpjpJY5r$$0QZ#L;SossibUBlarjn#m!PwoAzvX_* zh0*t$pO?qavh1UucJFTQN4HP&*DtN#F9dXdKqMjgq;zu;oI*j;_pZ481u@^*Aah-$pnnp__+X+`8 z8N+^Q>(d^$p|WO)Qvls7zk6k%J;l4%vUK+TdAhC_Rb!q#>Ng*v$s}H1qLI(E&zFP| z5#MGy9$G!fRZ{&LP*u;AO4{1PZ=!E<8@rxvj4nVfZiZ{Aq-Nnikzj2tIR8dclst7l8mq8nbuvx_{lR ziZ$kJc3g@+KQ|Jr5wK?R&M#yst1Mqc+u*!d9SE>hH2+)DHb{K+vj?#(DN(-I5&oNC z1&t45D&gApddfvr?QQ&LyTeTF9xf?DgKY^9k5!`i_Q?DG{z8(tE}TQKwkb;R61|fw z@u5SDe>np&(=#|@j3cC^i7qmBZ!beYxX5$q43eX3D?^}(3hG2D9ELXK=`lAGW=X+Z z_mkz$ymR%|vs$w1I<#Yz_AO?dciikc!nxaV@Aj!>tjMCRfWiDo1;qulFZB|T&gO+B zm4^0&+O&nfxvj@GQCoZGc#DSl2Ik@5Dqc)2oYr5>Ct4Iy1NasFf``cto=I8?5yx>Ms5acEs zpY@l-l=4qn%CGFMa!`7cWtvXr2m+N~fAscN!u{lf%1UV2%a&+H=oS+WPH4bee7DD+ z0}ZY->6SGm8ZW~a5F>FG{~v_=|JCAu`$XB<{)=#7X84CF{?E(*#fVU;u^#<%W8nS# zb7P>Z$C7MLBALDK! zjY(+*s>~Nnb^SOcw0$0rZt-gPbFVsF*=o>Da}j`QlW)=N`T72GZ`roIco0!k`;vF_ z;~Cb}6@J>cyJA`O_mwZZ(ogUL#{>H1_|>PjU56Q)%zQ?vvg|zLtPB~o=IrgNnZ@jc zwZ=sAe%8vT+ozdbhi;4O{arRDrnnd@bMJk9ezJeQGY6zZ+=(~LA)x}JCd7W{qKET? zcBe;UX~%i^sixod1uvQ%AQa7aRR9{t$2L{ZMykH`E4D>Ac#L8WWxv?WZ_ zDtT8i9c#>M!A3l}ygGf zgG;<%>VVLtYah3Tw1=imO?&bw(NeHc6UnvpQ$0%|EmUy88`Iz1*5U^3(#ho_f`#^5J=lun>E(VZ^ zMacl48(m0Uq)}rNW)R)|JIBFr#IE;EG%Z{>T(QKP;B<>4vQdS4p)~?@t5nJ2bSX{Z2g0?vMy0FpgbIR$pB>HrFz`yj}Rs z-%cL9o4<2-zr(iMWo7-3{H55yk%D89Ii{~!%Nh^J-tU%G2ZJ_fHt$@JtBAVS{A*cL zxvJO!_O;!5YRhOmJv9?5?G3~%*gtEV%|9FywO`yvH}U1ulVwv0c{lSH&}`J3`9k&hIj0w1=H0c*44uk6CNjeku3+z=mJedDFt+InOfr93i4pA zHdbo%-*Ev9cw+ZH5wLVOGM(-j0m2fmx!HlxsF_NnAIMki9Jg32~)YKSv-p2n>?7v&$KBu{{}w_ZA=o@Q-s87@5I7a-wbDbIzL zlX%%B22Pt?06^GH__)xK3RIBrRXl3u92pQV;uXZnlU55kwyebyi7|9_>JChX^%Cd{ zVvh(oAfs3bW@p}IYn<`5!rA-hV%Mhhv3{?D@^~RiBkRPL0kK<1vfSju`uIsAN@gR6 zh>HUvc?u~*&@ZuWG7+qJtC9Yzc->0?hQ*Z0tC$U9_Whlv-w@HYEGx;>{md`~ltQLX_zJWPc6nBqTQhDISX)AOyhSUQ)HV0mIl{BYLFuhCDH6Y9nz zmO=GUd~xw>J+g-vUAl=AE8YI--kM^UFKi|Atn7o?0Nw_R^|jz({Y|0LJ{QQ-f`HS7PCYnK0S ztmB6_@V~@58r7_A_gP`SvUT-;B9hFgrRb7;i1@V$`rz0#?Vu~6`gj42osTn5HtM0) z-#&>4%_|KT1w0!BNycdfxN$LJ;Uq@i)K$yFDWN|od%D#l@7fn8Vp;mSt4w8n=HG>Oi} zLWd271mAK;aDmLm+9nBmmBW+Dc$d<0;AzW#hguTUc%@{WSqOkeyGck-`JUcHA`N?0LR4}Zw{*Td*g%{g$x z{l)vc=XlIpKjeIKlv)`1Rk}?D<#eNkhjZ8isuo1c?|Mybg4YkIa)OI=Rd+Yzj~X@Y z+3I{{q@UD@d*6YPjt7A%qaLcRsPq`!+ygMH7a#s-lWz(@++iXF;il{>!T=@M6j)qg z7?jW)aTn?zGgUxz_~cu;G!=~x;+@2SSzj(t;&CNFG2=r8IDZ%dU)D&-H3gK4BNlpz zYZXKa)SbT=@H0zvVf}2OQ6%FK87kxaZE_52t2HDo^XI*){YQN>ww;(EtH=kOahe3K z%KWNaC+czIb1p(~lb{tmQG}#_v~qRU0K;tnR94c%J>hVX2xj$9HpxIvxgETykOpou zggDnXh)uU-Q(IWu3@o>+kTp3m3TD$&lk;M~-c0d*IOJUvl>VqGL7XC-h_I{<<8^50 z`pjgwVi@I^bEU~oy${X{VAU%@RvQ=eOX$|as=aNtFFJ|B8IIJK+HSxN=j{3; zleQE2I;H8obD39Ok(-xHx92$i8-cW0`|J)aNZkt9VmU+|IpMJ%ltI0s;f8qr_k)jI z&9mwQsnh&oK;fU2*}+n??C>-v08*sesydrKY%0Ps-x7^Gb2#hYr2*ZlR=&TB9u4VM zQgpD^&}EFKMjSd4^SQ*B;-zkyL9&1bQepwtA=!x~3sRp^Q=i;m-DqjV+_KRo>q*FIR13Z)YJQ^&_8`O?IwX7LmdSqg^TurzroZ{CZ_>KM6E(qL_5GyGv?o*YiR&twWF$ST#@!jFxT|J z(e0+awwvs;6;Pu5mGV8E*x;}4g|4gCv$>+z?miU~7OC&M=!5wD)F@?h@)8?)_&%XEAO+^Y1BB(qvoP z^z6B<%A-N?Nx6h*S1HmkZcM7tl-I_+*=4#uvZQI6b+qP}nw%x03+qP}3KE3yj|9oGZ|BX2NYF1R# zMa`Sad`9NTRNDbEyv1m!i<5zIhj6D)C+eK6w>Ocvzftlo`$?EhIWpEqzf~7gbM{qv zQc-FBQEvGfGndYu4!-VUR}8*pPAqACBBe=)&DY?yL9r{ih}spd=*tRr9JM2oK@un>C;bBy@`d-p5fE zU7E)IE6Vd({9SC*M~BArk4>%>WW@Kn+u^14jHk{A!ORPs&mw>Gz4i~{zRDOgZ8tub zVRnV)koKU0kLvnw+Y1dKYG1xTmi|lrx$yn9G*r$r0GP0KnNU6GA-^*#s+EpkPUncSaX!(Qr#6$W|QsZ;p zI}_o_)0*CMS_+bmG%b=iLY*NPSUYk)8oGrCxvV*;#u5G?>fPRvLf-iVlguu(7k zAryyNJHxXFv^eTSKwVI>UbhC43H7wTc>%)=#Q-cid+WYJLYD@Su0*Uxq7?L%wPM;u zH!!RHTsZ)}$%}eRWHS@m2?SbBU5t%KR^L~~sDzVya*EYxUn}a36vJ1{bt=~D{zbNd z$gp(Bp3Pq+LTcOhG6n3H7%t)vn6txnkoNtcKcC8~a#KxNci+^Ved(1Bq`U3|7z?SU zaRbD-JRbkf*sMeEF_A3jaH+O(k-G71=ktOm=gWqKT~l9t7^B@VCy+uQ!77oXqUK_N zk;;tIWA#(DIyFiKmOixqwY?0`j-Ega=;_QCW2i+CP4*#&*wj~?CmqyBDzx2pf zh2ub(ShQQEOJq4+*G&Rr#n50>y|pIBNE|I8DGd`rRi$sXAt@>k@J=q7vW0GQHk#m1dLx zTWC~CKIY8BPRh}kOb8Q~U^tGGDD9w=R0TuB!E8h{ragOEa@l_9W9#qYBy!11mt8~( z=%u$!L>^~^tPoe!vy|dEG-d2I8{Kn~n-XJrhYzeI&RZA2jmwDS8<5DL_^~nMCe8^5 zj2j(8br_OD-*RaW^kyYlWaR{$tZa0yY5${Xhz3ONGY$piwyfr2qIR1HCnJxP!UK+mcgg-q#PO2LV$?pr zSn=6t4;VNnR3F@=7f9$NDSN6@@nTGz0`@5cGtQgZZr)iKG&=&*%t0PgGdY?NwS^*+ zG6Cr*nxSTh+y^_-*FsrIQqKC^xpa-rA40G^pLS`8`uNfxSCE$cyaFnM*_luD_;EMXE_r> z78y}uC2Qc}xHn8;O)}M~lH0fNWT*|9r*5_0I`dLb)3bx#AHCQwFo#Iz!xAi;f(k&| zHqqyomHvcyiQ%e;Z#d_pig{;^^DoB`JmS~S7BTpqM!dM!rKG6vh`@POG`{cM^G6ft6wsR*9ZU%3D*?VF zANGt+W`fkB73d$skyrxF*!}87S5Tp~Gbopjx$V;C+PN z^?ayhel3;kllK^uw0US~yZk_x_LfaLkRD3oH@lrlnqJn~)>5X7XS4fWF`lhWEnm0y zYeeHs%}wp^t>3<{D15FrJ3KH@{)(dlrI^7huM4WGT;0Y~a4pi8w6}C4u>l{p6p+RL zmAFD(&QUb$h=5b{8MW2)?K(d~Xf6px=QeVUTu)m4dgxvdok}ZGaEp|TX{$;1bFo6W zSS9F+SfMnL8AxpkX$ZkV&%!d6(4a(-qJdQ~VI2>s4}@i)$731oPduv5n9{0tE+ZJ=E^?Z4J$Q-KCts+gSczjCc`38!n#vo=S+puAwn z+~EDP4!`wat74ej%-%!=Sri03&}lmddA-Ld8VsnYpS~121Kx!iJ-^+%?=uL${_BaQ z^k+>~zu-Vw{o<`IqT|qA-Fm657qbpg9j=BTaNd(pevq$$#_PXo+F_R zzhM2GVLXG4dO~dM@~^=H#@8FQ!@pk>J+oA;wKu-8HUxK@A}Z!e=iK4-xSeqvWSBH@ z@-_}@mYP@W>xYrE5Wmmb+3s{JyYP6x?|g;i<*o6F)>D%odJOS5IW#;`f0SkvO*Ema zq2Mrlj&=my>IHxJP(4R`A)kr(`|xTvRe8n_EdTuYHwGyi{%swl7`dP>w@4e zjlO-ro7^-M>Di3k2$E1-`L-};&M4buFj^^veoXKMZuZFF6>J)={YxhV( za2Yfnc;^gSHwdGCO4EC6rMh5v($sUy&Z43BzjYAuq2r8yV&Xgb?DOLNk+7@!|Nip0Ag|t61@us4~ zHrRVStzzrr77s=em&9U0%*3^yOK9d8!BWv@d{8JSk4xxQNkuVwj^Y59n*z96tp+}Zqp_CW zgRcY696jZvq~DXDqVM;+6Zv17`gV?5r4{0WmJxp;thZVJLo1JBO>NqKd-IyV>+3Eo{c2_zZ&4+S(`S^9^rmPdkI7kgi7fql~6(Xpy@yu+Zffsj*TYji=0NLlbtvU6-Q2Xa)eV$vfP8%UwF%==5>0)S>+aM>6!;6R_Q$=G9`djB_ z@bc$UIc5}zVWXkZ!}ViW>TivyPgVdl`U$W)2_T>TzF7tF4Qeee+7qsab}8}wLr=pB zo7-^iK}c!BE4vGy3P{VB$PyzGbxSo%$N<8(IQn)Oe**O@6h@E>sKCu1m8G~d>yx`= z&VFn`^v5W}%qikPfhO2#{zW176hSOUij#wioh0l@Ejn=j?6O2oGCIpW z^Jn7F6kfGUbQn6P1R9oU^=BsTwz)7fgN!UsWNbMpyM9L>mGEks&9RaGJUExQ_mT4>h53i$y07-RBe$o3;c*6VxSzhnkq4ZZ) z!u;czDXB+&5!^Eub{$sMMtJnt*u*SZL3dGD>sx9{QckYroyt*01ZSJTa@+4=+}m8i z3DgS<{RkN+USqZeVuKHxl(eL*n1GEc0SJL1%-=pUMIVD(f4G4*^(E&?p2yppgL6f~_v!UvptNP9>;8Fv zFfed5aDj?Reo7dkit91scN7I|%EEP!tp|3)0kEC?T4nM)7M~ot;R+}bK_WXRa!8RA z2j2}o&~_w&-Lj>!PM6eWHglbFqoI$+^bNQv)LPYKyZbV-=Wwm6{H)lyLPIlH*Lnh% zgV)k880P)K>jM@xsP*;Z8h`v-W2_`?A&XUikTdot*=CwDiAXXTg?uQnOKeS*Tm^t9 z$y``0VgGQ579O(MB8fyvZz=dZ>4dmW65t{LgqH(u5~fZTfJy|&5~RjN?*XJL2y!(e znW>HxaKiBysz`5)UY_WNlmHsBQx1H-^Qk|mZmfhnzEFtX+hEATCNKsG=CeO*ZH)IW zG!4*c+HP4{KnqI}Si#9NA62|b%w1XCdsOgt5D$#d$VKK1yenRZI7m?}R85GH7AQW& z0CFO_C^J_v24R=tsaHTg#vQS?0>->yRk|LdpC~=pQg2sYo`b+<#3cV-z?*xFPrspx zYzJxu-~uRE1JQN9&pX_&>1yP&VDX#DY4NdrBImS7ytrE@lA%$YYJDxAduGb3L)_$8 z>!|wvp_Ae9G+|6k_p(IgSEuYe^o?}#R96Z;2#@a09xtEou$=p?BM*1SF0Xf#n~T`4 zky~N*x*+@-vHKcK$otAp9AwI8h1^o_3TF*Ih`k2oZtn8qupD79owc=+pK z<4zg@7!rXgGw+Do$V=6>z>|u@!M(|p*7*g*1@kT!nV@4Q z83jj@gw+7Zl4(n{bn{^pf)JwnEl_`5+44X3~zB619lE)WgA z_XN`-3T#PYAX!+?P+uQ{WR&-8M2ad)&dGN~HrfTjLDZS&pheIr2!;szie!aT$!!gU zIayBj#x?EccEBe;;Vc;Nv}2SpFq#b=Zs>zRS&sCnj%v74UfXj;8ZXd2BTx7_Cy7VJ z&(zpSm;ekt#jW<&QI~o`T8$arsI#%FPiYkFAixYapOv?!n;>&Fnd3zJmIGW|nsG4_ zFT&n*F!JHC_DONFrWkU;i~cDLML4R9vjm^qeg!h!-=bc#RccMajq@*L7n6}4wHd&G z$x?jzTMUmQ+t@zaRVhpC$daFBqu@q6y!5m#F=LS^(Zqz^%2F{P7n;B8(TO1(K^q@a zZHjL=?tz*ihII_&Re_Zk-4~C6`mTbES2?=6;}qwV5snTlP$iizKxrvkT(ufwJqOmU^DICH0ez=ukV0Cr$iX7r>V*AR zmXnEYK#Sm0Kn@Ad+SS|BIgW*JDH?`@A*b-FA&lvKj#84!9#3_lz- zo@mmtDVKRh@chDv)=?eW*prjX5Y#PqbiNG6p`zIMBJTsjh^_ z#E$i*rOQwn89RxU^W!hpL=?}^2pJvY=ypCjlMXu$k5$q#`PA{j$7SfEL~Dj@G5%Bx z-_fIPym@_CefYu0FK6AhkML2c20>9*`ouq8Z{=N5jt7PB`XYngu5&0X!ve4Jv%_0- zm{O%;iT-=RO8Ysb?R~zDPjhlG0oQ!2YKk}uJal%v<6Z~57p^mf>4Aom$tZ`fedUdr zW>HIPD45h~fi4y#(CTx|=5Rrw`gs_v8;4_o^VL_EEj;;oMUm8%JgDgR>7!KMcIFGO zNesnXiP8gvYFx#FD9(Swea6;+_=p#3$c2Nr9)x(+p{s_q#_|HhvskA(OJ$7=0WvuVQQyTE@kIL* zm@3&Zi%~>LEfJpSi8j?L8CF`Bq$U*;)8XVzL5J+MSO1Lv1-t%w+y5s#{tv|ddqBa? z^uGoaEdPYZEdLcA4{2`L5Rbrmuc*Q7wgP0(EEm)D>iI2CI$;BY{fY78Mt4UbyfTSS zUXC{&{Oj3xoob>oV}=6qd@cL(SSOC^?lx-C5-+Y33;PpqR9gD+lU5h{v30%m654~q zX4TWf!Kv`N^Dt7GxltLvXTgKT*7}m^+PDs|vFWqQpvQC2yEX}D6*T9?yWiyx$<8o_ z4D;$90BAz`WSYidn7|~Dot6ZCnjM6g3l#Euw^kn0^uDd<>+Rgh^J)9s>*44xd@Z3o zopyeIlt#e? zWPYd`*NWVRH!oM|=n&|Lrmcy0>d$4Fy@_hC+!et*7Am6 zFfgE1UqFM??%-Qe;|neEpvqSMa4P9|flpSO*isLg-1SyvWl+Vbc!`MBYW%d zi%czN$7;e&~A5C~UV z>*FkG#7`PQKJ-IdMF@O_oe-8^YAp~EyXdX26pJ{9l&Bf1bFT45+J;g})b%5`6yf$Oo^A-RddV2+29y$;F(%4i8tzJ;nn~Y0^+lda{qhYd;8y zNF%)2J6J&Bc!OVus3m}ALFwcg)zmr8ei2Yg1Et0h2y{UMIxS8LCaE{pmx^zV6Z6l^0x}=buolq4(nnI(rC?ubZ9z( ztX7ML&PdxYF5(%W|Edn~?tq?E0yu+W9u$8hj~ny`#=J;Ua#WX4oK|ZARL0OTfGldB z;+F`kUAOs?b4Z6GN0VD21WBq(=#TA4!x6r}^z;I;^25viE~D&<>St{xEbrpC2@Z?1 zGQ^*R3kPWl-L!|WzA{u2 zGFx@WyRK;P9=~)``Qq@*G^BL5 zGw|p(Y1r@}-tbm~dV9K5$M{Alp`+`!D=yiG0I%Jo@VqCa8H<+)s~=E^pv4?O$!E&> z8pzTaOWh^PQ~cq2bvx_QxJGnj48c&bIOQ*E$&5CjqCa!8clMA)^&KQxwZ}D8zPp2R zv=wg`Ff?_fD{}&^15F`P<8)*i9B5WtWs5`%35bxS=zyE@zz@0hT!(&%L*PuS7_gko za7OH7+ro5_$&qGqCU7S(aPGmnk0qap&p6EU^;kT9^T-EO)WqeX2}kQEPI7+)0D7hU zpiSHB5oSTzq?2cm0KvsY}^(0g3& zs9r~hQ3HcrwN20gu{s^)B*U~bG@ zqkUmV8F6Hcq8HVecR3Ctn0s_bVH1g{I^KM0w!AwJcHf~OYat)rEJUxR^(f#s`J;=Y z#4lS8r?-r4>wCTNhpH_#4o{c_i6G~MA%01h!W@<{=KSV_c487&^0y|^)F2KrG<6YL zu-Oc4%(^IT1{YnMlNV=Ecsx74SZS#nJ2u<(d-L7;dvM+H6(!8Z&|if`11iTYkDN3p zlD`_c&~V#l4+cUzcX)gB;J)l$yk73!u2X#vPTnRKq|`$v^-P0*Md|`dkyI&<8v!K# z0ZxKZj0MC*s_~@u`Gf83kD|mp9`7#XuNf?+@`bK-o3KOf@wfe2P6VF5im!b=V&$Xp zK0_H>jU=#t6fkzEekhCnfX%_`%a~e+I<svc>o%jHa(&I=13miL-@8{JW0Yi;$ zMcnqAqO%PS_dt4d!jM0EWBXcVUzzwIC z8ahXr%_%G2Ulq&p9oP}u?v37K(?@7Vq=)IbLXMl%zxNNvH|DNCTkEfnpO$qUm8o>S zJ!oB{B)0m_Ppx%|Yci;@Ic&n}w$*=43XVmg3n{tVESA);4U~7L#yeP;Sm0|tlx|Gs z_)`JXM#@%z)YEfT82Ds7z*d>3^gLz>BQImc94T?-M=*-iZqdP&#x#1NqFkQB`NS2j zGT9dzw-a$y>u^!f`9r55ULiiPa9%1gru}s*|B}x%q+>%bUGb%Km z>q;%&FF6vLR*vO>IRI@hg_9@yJ&t*5GfKpb9nCb*{#YC!VItCw|A}$_heZB+0>r|` z^sguWbq&SL!uEgH{QTVfZ)$#qG^`^Jek8FUi{5Fg;2+Gf1QG~;;ua}*I1lSQJW0Nw zYpI*ntdLZaoAfbO59`sfS#yWfRb5+u zwT`_8*3~^sw4xTwo96tLIT;(wi~Z=0*<#lI#y}FNtjm2xJouvY#dUcLa%1*6wJdE+ zi;k!wS^O8QU%f+Uqv{O3x(9a$lwRbme#P&h$^QC{Ft6-~yxGLYrF6LYSKLGD=-sF*}jUU6R(IByS!$ zL^+VcFR2uUMgDObu2pgBO!~))qd_Xrr7V=OQw5eIoO&q{$VHiYAOL1oijlFbDH5jQGN=BTsZaC0fH5<*^ zg2Rr=*~&}kn7)!*JMEx+G#HoZDTV8RyLn@v zW=mznRls)V)qoyRB0G!VTVtkOe^C4^G%|Azp>gYN?_xp6PpbjRt}Pk5GmT1}Vhde- zO>F}d#&N07O>YVnp>8i_T4oYPlrgG2DKa`G8?=G7kEnp)TqYtSI;{m7!bQcoF<))0 ztUkEo2n&(HwVKwCsFZ4mZkd3HgM`k`C$E_-TRfz4lRd1j12+6L9e zk;<$#OQO24+!%ljI&yjrb-5rr5g0lcwE4OgiU@zy3JK5|S;A*UKdziz*I#Y|*5J^9 zne3gF)vY03(6M7R>V*boeD9WCM{~G5T*iskzd#DC%6+H)(TcKg%l!uSfn#aCCNx$p zq1ePL%S;N^I`eSij4VyM=G^>bgC)hPNZQQI22YxFAm7v8PFi}$zk&@e#wn4g@T*J3 zPTiv3?V0^{{n-@xbz6FU%Y96Gd^nd}UcljRZZE@~lfMHCwzabME#1{XAmQfCnP*~Y zHD?PxD7=R&v@qW*3Y`}tc(+yU{v(vFRFjj3w-i~;5mz^C!+~rB9 zE5?{)xxw0|>2GtBZsHTt-zmhEg!ijyPd+d}-#$gz*I+lSpF?@M4{N()4W8kOA23ed zI~S4${GP0N5ohg|2zHTS*}7|RBP<+F8|rOJJIR5)X7lryeCDqzz9~UvR-CR!1lWl@!=kQ&j~bO;yF^dHn%9| z$=77)k<8k0s`zu4+uJ`rLLezpSh;@$pnqe^zegS{tp8J^!_3O^ukEdW-Tz-cLe_s9 z$N&F)gj+un#3t*%cCqzOwe-6>v_katXmkEgrW!qSr@0`jL@U7m(V-?S5LR*^{p!Mxmf|R_v2$@NADz9lBub! z4EcFPQH(hwDRvG70phiIqTq|WrKzdRCA&rse;4t3I1DLHWy8>W*3m5-(qHaM(|=fONhE~#pg$$`Iq^XUqk-L zZulCTTLt`*i+S>8-v+EnF3fUJMHL{53+?=>GOlIjLk0_x0VN87n)%%k`*^_s6oFjm zz(c+A&IPp~($2Za^btrwk*h|Y5$Hv+IpJs`4|w?g@0xIF09MWuN-i&dk9@aLk~lcI z7zMOg1#`GeeYBLi^DQa${H$bJGO4;rKVUz_J;|jD8D+MPBdW`T9Q!9{d>lc0MrMn) zrk>&SaYLxO%y~0RHpHcgw6`-3UMV(qYv?`Eufx`}rCMA!YOjnPuxTkhxBEE!HsOr# zhd-!3f4*Wc(Kceg!5n$oq+|OaA0V=@J_;>+cDN%=^uGW8{rh-boC}h~(tc>2I~fo< zC~ZQF6s1U9r)sh>Qx`poEXo8F9F2blL?Y7%30J^!Qr_v#EPsxa)e00loc^v_;O~$- zt7F0y3FavO^m=wh%@nHugyB83Id!R*p9nm#%?*OIHb>y7t2G+(95Z&U zxDPy-0yru!=m0=mQ0$%paO^JVrf(Mhd$IFKE5?Vo=4)5O9eZRsmMCB6AxS02I?o9&?AJ6JC^Fj`_Se=%R%3g(|p>!t1fH{=%ZKPz_Q0=8gA-iNutdls#FBXa14Gd@?*-6KQZT>(6=jt4nd$_) z38nI^r=1i0ce2jG<*v9IK!hab31K7NaZpIojTaEEw;oXxzk#x$O@2m_D{95IqI|9& z4Ob%4Az_(p;;AD+hA@)rbQUNZQc9k-oCRiJt_c&RJmW$&O~s3|cwxa6e)SX`*jZH) zOJuvBawG>&J22AW$RF2Li7i#$YHLvJV3qbnf-WkP=e&US^TQ2rv#(uI3Xush)C*GN zjOJDiXQsTFLLE=XJUNAGI*#rSL6v2RZjcr!Z_vmg!!0j&5t4K(sm@<@PDFa%YUf9G zp04k&&xndIr3(XRO9N*ExprP&c#3I-1w1j(9=x^IPr{xl#ylUI2<+TRsrKF1VKrLH zQQDXgpH|a}I9&TKmk>7?$&!`FCwP+(liRpW+?Hk!U;!)QBvXT%wMB~Zm9eF^nZNh7 z{rxt(d|tVPK~VI$Py_?h8;7iAne*9Yfc6}TAvKXh{!Bj%#+wQEQ>a?~8O|=(on#tF zlF=4z&Y_Ab$WROp;(ZNYu0b@D406KpN)Ve!qjhHa^&VzK1Ens=yg{ukkVq)YSu{+W z(kPwGV^at^$S`1?1_Ui)be8Ohpee<%D6URCr4*;b0PCE>PlcRXr9dZlgs(icg`lS~ z&4&e=PstYpr7C+)WrCk?#lXWb>&7|4Z@hHO!$IfK4Fy!eD5cB@PO2`+w*4h9zWOAV zhlS_w-f?FmsxsCFmxJppFLgRxIJOX7*jZ)pC(K4VmboJEm^!&%WEP5HB#V4Se$$E} zw;Z>;y1p>LV=Q5nE6{d{p9AO|t25AgY9R(YUobdf-O4GKj&@o!=4KNi$2FxUpp?QT zae837R$3vTY!kH6U(Fb2tVAft8z2RV&Z4!h58vO2O32$IMz|9|p#bLZoZGjSj1vzE z3F3a?YdG`LZ3RI7%_oVTMqPVRDg-NNJ6KnuiUJDCSj@Y7fl*|Gk>fC5u2E#f&u_QI z*m1;A0#DAgcni4{>%ym2l9lWM5A;sFO1`-_UwPaHN6useeH6yr@Yw<#m6cJ!)Tn(t zJ=wYs50PC80W3z8x7PO(qRc!N`N7)&(QI}+W9I9$^UxS+st)O zfU1yuRqK@Cb@b!ZG_Qy>1`0z4EIHFb>69H}rNAR5%`>z*5XvNP8?F>%G$OeT1_ZE?(Mq`L@l%9d8h8e9RQ&*YKOWh}!HA)2g z$*%7RlHr>fvE0VNTG9SWuny=5lc$I%m;KaVh+1KrILh6jd3|$O$zkENYfoxr+w4gZ zHYt#ksfJn~LuoB^MIP)GH{BH4_SAI3Jhbj>$uIJ2QKebAPqRYD>F^`cGQMS6w6+#X zgfp)vKr6ZZqRQuK(Zfn3qN7*qUN+2IR&qf0U1op552A-E{)t2UhZg+%5KI5ReVuH~ z|KRIn`*+C8{~w1q`e|tY>8kbZ=@Fc54VdBjpkar*Ze&I2e(G4*yf@O9Q&YVqOG`dVm!qQz)K1zT4I zt6Tz?d-AU}Gqu=A;F^wYZyz^yp6wddm0DN6YpNDq+n1?{S682&)op4RvGETByiLyQzgNp@uixmp zckmA%*Vc7bfv)@?Y|XzR>;66+tB{UAKV0#(y?nr*@NG;zC?qwtNL6}die<&bG#Z9P zbv}ARAt0Z1$Z3k;05C-X?+=4;?UoMBWI%p$GsdiN{bAJEk_VDd#FCDcK;C=sgMv4E zrQ3q71;z<-u3Kq`+eBkV>w$4bb0);*zV>|1>G_GtcvIuK^VW2dRvywzCOT~ySHVB( z#?vw+U_D;;hebtHzfg7TAPKdsB}~+F8SZLzj?H6{?Zf2U9#!xBt$jTu@Z3t2SQcHr zJ0IwK2w0&76up=k_S|a$@0*L;?bQ+au5tiIwWbJmm0_kFg0l)2EndK8b&GV%HDbBN zQiKc7b3KQVXx8p9g4{Ohk4pU_(qF=#W|?q{Dzn4LC9sML0bB}ski&Lr-5}mZ3Q4s= zvI(XLE*uRZC9K=XL)j{fJ&I1_7i-lS1&9n(Utuj_jc#+?2Wa%?aM{J38x5Y(2x?4> zIITAjE*KD=N@|%E4}Km8i~CpX$-V%mK_ECm9|$h!jZ%n{<>rq8eR21h%OCVAs6?!1 zXdfcR1r{o3z#W&ch!4tK%!r(*U*g*mLBWaJg6Ejfiv27z;EBBp0!#M2gx*Cs1JX+T zw^dy{5dn+GjPYum&$=E_D8sYHzpsSEnfzr41Sn-iHHD3sf`PAc8^l=l{NjyD9-B-< z$VDz)1wA;)z|Q<*D%)42_wdbXnR(u4dIe^6AzX}3jIlwop@HF|XcHaa(+B;Z`4nF1W+y1AT8*HZ*?@yG8zyCEwt*ppq1{TXg#)7QU zKXBvH*dF7tb6FrPQ-r89_3Rl^f+uNa@h-mMq`r02&R$VDq)nV!CBDH%6Yi+-E6+*r z2)QcTQjPS0O*)-UP+yps)xu1H4hHpH8g-~H@)H5q|EklR6XR8`)y>Cl^g@9~y&RM4 z!y%S8IpXsfI?^4{X|onXI~GKZ6q$^rVKY;$&cg}blLVI%4=~zo75ixHaR^NWvtOjGEpTR>PMz7Q7Icnf-qqrtdvy~jhIAjzD~$e zLYJ|WLaE^H(=-3;%3D7AxsNhFC7;bC)fM@qn09SP1cn0q+<0W}oEFG77Vc>k4CSiS zfhBEOE$6p(plV6sKZIE4n%c`2-H&AovyFN@`j!8|z;pkx1)k9r??^iCW7ppx@mcc3 z#cOb^2z!yF4CdQ5LQoO6)z&1n&4cLP`b!H9GV89b2+H#}5;XgMR|(;; zqS8B6xNNuMaFC@-x0QqCCXY5jQ)&Hx=WdK*?^o@etQ&Y%B~)OHe26hOiaot>ynym8 z8kT#${b*W?C~X&t^}Iq16Kfw6@T8y^v-TbgS+*Yg$>ni?dLD{gCo)g^K#n5$uROPH zl!{GyY%@K@{>lmt_(05DB3fF>*Vj3%HPGf(8Xfeze2S>+3Xb4`D1Gg$03lboX{YddIx#9=*L*5AUbC9 z7=<2ybDRzrYt+uf%fVX&>7HuU(-dQC2zpMjYac_YVc{tO^UBIxU%L(G(1+JU70@Ly zkdk?*EO$QYBp1h+$4!$SXV0iL&cuMNsYw%&g+F7)|azD6PXZ4>1RU+ZKrwc z73c9Bod{)jnMIE1u3u~MmaUQ1iDy9%2NFV%!VS{hmW3AdENB>5CDl&xj5o=P?tlul zi9NdEA=$Dh+LYjpMmD7xhpT@IrCR7G8#3+|!T!(`9}8b&maiw&~@(6(bB;}XXn6;+}81v%blqhX9IE54RNm`)oGH; zH{Uj`&s&S9I?U_x(ec5QZ{^a_#T9&KOw~$e_V=%E?rt5c8nlQ4yI{hi(;C~337!i- zrxn+pnp2x8)t{A(I+S+Jfe46)Sk2$FC~LLl?jXnE?lsFjIz5^&E0Ju5&GJgnc~QgK z^>z^o@N0pDS`MK6$nvKWu`4{6{`)9INxMpa@RnC@usF?S$d0NOF``AFXQ?Al{p(OM zpu<~+=lk8Pqsac6(B~qY;A8CcEsc@anf8BM_#Q}fJLc5U^(AWwJ+~cMR&Li=)b&`& zXyla;bAsKx#BF zO3zaSZKn1)E@NV)nXS`?Om|pzcHib|2>?8kG=sGRS^JP*oeAAWW8N^64X&HDAcl$BqJ3kxNd5Cu?{`5iO6ujfND#?nDkP*x|`prR3*j#cF#GVT4Y7R zne9z|Z2YCmilqDOK`{~Z!eEe3=p98k3$vzaI8~Vjyhil%PcPj$iQ`h?h&E?17>xju zmD?!wSfjlJ{4`vT+YhA~<~snP9#!a!gE zi09m<7$(?TBB=;_|1@$=Ya!T0ke-94(_vAiOYFcV0}>UaDV=H+Wn2h_9a3 z34p|`Cvnbi-w&c>+(3tzN^z>;#CVrQN=y+cNd+w5FQOXNj+d^oj}&Av`Sj7WL(%=C zS3DBHay$oC4>H<47^mojBAjIlf`M{!CRGyqP^dpBQ2dLj4}^%kAe*i{k}haH-w3CbbQXds3ThUKpcMp-Gm z?=%6UqzlkO9^qBErWdyl8srwj)h$+_xUD2@%&h;8^(0Z4dyF3`k#~fJq)R$YC>j^y z#UTNM8V;yiB@tcbfQhCx39Srhrm<{WJuZD;(NOfB(XntPP9LCX&9l|H@C6Nj(sR~m zwi(VVjcY~C1M;=-#h-fHnmf=oq*U$`*}pDu{p&nIz#r7tgB3VJXa$*@!>&X?xTgJwN5TDHUbJPEgt zzQUXNxKNmoM&Fd;XUOr7pkZe*Y)Y;JuUX++C+#;q^~5}7Z& zw3Ox(&B9COVIU9I(efoYhb1-o&M}A7g_nfe_=l%vZF1bjL`Zbf%a^G-kJrT&stBEm zJv%SrnSqlYMMBXfgGVyku{^=d%CU+?I;K1UN4agD2GATNo{|M;XG^d!>4JUbV(5hb!1B`@9Q*r&#-V z0nGQ})cnX9c6DoD;+d3Tjcx0@rF*9Kp8)wk82fK6fSu`o<;8IP+v)iK(E>Q=|0^+C zQ_J%3|7ZaOG1n;sFq1>HI_jq+v!U~$vs+fKYnL_)Nk9E6VhPUmUc@uff>QYD_0^6s zyxu40aVf06)-|rNKTRSxKHe73weXocSo3iy^jqYT8{LI6E1f+*Vefq2TVCrhw~J4E z4+8^RrEF|$E4A=DxL6Ha*Rcx*ukK#0YdbcaVE0_}Y^}B#7YeLN%&jkM!itijf)cG= zUv(HZY&%@xBdss4zCK>>Kb3OVxHsE8+um=mys^1_JnkgOO8sRGnobkhxYv)LUP7HT zT?AYYmYcfuSn{JKu}(0p9d41fwYvd+mC9`wN%F?mX=G;zIY#R~MkZf0+k7`vldIR{!pude+^ zNwu+OL|Q8Gp*ze%sNJLX|#JXFQ!#; z+7TLrd-TKz+!w>xjz7X__$ROS1HhHjPE`P2SpW?4QUxj(ZaKJ*Zw!u=)6pnu3Avh7 zy_B)C;DIyi*emmHsUMW~h}(Iqo?O$CT|3Ce*aq-`K|sS0C>E9oiBL%{L=uO9uv*0q z?8c5jxNfNk3zaOLsdyE=V4a(#C+uVLY)fu#SpPsz;;|Fp0+j^NVeBY?&i%JYpb7he z0|p5J(WjUgCQ=y60EtwL{(9X?K$48aaCY{TqoO;eUp6j=&;P^NI|f(UZrk6nZL4G3 zwr$(Sijz*#aXPkb+w9o3la6iw^X&aro%7b&Rp)$Lb=CTCuT^)|9M_y<{zjQc@v3~u zzPZwrY8q3IwRAXZ)odUEjkCG}5!zIPo%H#i)!^yP0C064wb{wpybbDVQvXd}TWHJ&hYc(lV@B-uC5O^p-4)q1$>2@t!T&n-7; zJm`J1XWn;16~n!+jK&iNdZ=?gi<(q`?CrZ%^UY8izQ`ReEHrb2+gyN!eGrl2Jk7AF>&)TYCK1WP$XiVtjXbx|1p$!_SesA%jx$>7?+y==Fx=IjhKUi z!H^k7Xs(vp0#u9dwZ2rg^-*4z6!X@ZJoq@My7fqw*F#x4UuxEcx$tyEIHZA0T=|cF zq{le-ss@As#<|YHTyK|1h@rbxR)@=XSb+*KAT%GFx`n9+C~YQ)OiJ0dA?Dk9v>0i4j7thuZbS7er~!xOp6bAm~^2 zN+&pGWn(D1|wdw7@)6)}2t zVI%Kx6(GgdnD*FUEnP@KwRVOz%zoQbej7!~4S^2bJ~G9fPf54YM|8^T_v74|U>s_< zBsEG$gL7x_6v2SE>>!W^U+{=Ae*?u}@WuAl{*^@ZKLIhnksJ{o#oR2sD>%g#0i>&G zpAiT)Z>6TP)HlbFz@ws_`_@JqYULV~+cMSjFH^IK8p{g{yotmJrchQpC#S~a2~po{ zO#e@T54WVb=Bs2Dxc8np^AEFQ@C5%}B|q)6;lyB#xw?$Inyz}k#U8e6_*?;8=g!?Y9h%-9+_HdP({@2UB*6fST33y&MN7Tf#ymFfNM-uhv) zWV2%tdfUtC^=$WMxuxdv@|y_u{Js)?0q~C^-1k>-P=9+u?jZR(&&c?3aF_J=nqC8L@1**)(Lcvdi)=k8l}% z&p_Dx8A%FHST(=C^Z{ppdLluw(gm}t9eT3!uo^=S)GV;!FLbYHrT(4&4p~>{vI!D~ zuZd@wzbRe5R|rDwM6!fYMh^|)_dnTz^Xh25FYrbHD6cd+&Y z1R2*KUc_S_6g-HT=zvQ=E>$pYFYnqt1d8tiXaiLMBCuON7n&K+snAPiq8qr_XvF6H z!$wo-X$h1kP{a^hcRY~hLe&G}2~PeV3L>!~12MSO;O9#3&^s&=>hLWC1`^-F)-pDd z#FyQlZi}o{S&>1r_YihJWjhFno=xxtarr&V5)@d=;}}9X^x9o+Ma=P)2tPf0%LH9_8)4IN;PqF7IfMqyJ}opNt!9zb@Vq$5CKPHMhf7V`DJV*1d5jbI>g7th4m7Z>QPt20rV^^Z^IhJHe z{ryWG>5(|+olelAr+)qlWZA;)+SLSp)_F1Y9(tNdyv_=;kiEorN8DCi8t2^MyGKpj z`Hc3U@By7tQ;UhNtxN>qFk2h01ZPye83fASb$u^E-lM6hLrjuacfos>g1WmD7n3px zpm4{pmRCs+xgiiZW}63!P86SVb^7~={=8FN%`66LD>gz$Q$vNWj)9kw6Zb6bcY*!E zFVWSCwOb(Z@K?3$p>gE^m5tKPMIGZX#}Tj58nbAJH7kJy|MlM7a22i?f-g&blp0%e zhU;$zp2HBzs>|sRkMwhDtZ}c{>qfOKEBSIdEZ2(e@uh=ar!J*Dt9fktCsABQpk=L= zah?g4hWy~GNn5?Tk{f%d(u&=iUv=!q+qcWw)Zpv=-aE~%qkLw_{!j2KJ@;=@&U(`x<04C*OYW-xA^TaTGzhy5^$*!qH3q)`M@EVKN8`5YQn5-k#h15Dp z&UfmrXce5oG!S)iNkqB5!Sp^;Bca@6LK7N7bqZov%J-BCkpGJRZV@ zugY$tC#JJU0LQ3MyWJi!JVw; z6z54ZnMbw{LHGd>oGq3sEJjq>Er<*3N4u`p?19w`Drl$f_4gRk7BS>n0Bu_azg6xv zc!9&yFfB5|}! zrolcKS+K z>d{UbSLoo3GfBf_w?X_#WrHRcA6o>BbY4-TmIdnrH58y)In?s&@{xyaytU!SXq1-OAT2tuIU$1haAT5oj^>Swhm{u-Pc= zg=~?<8;<_%j=G~1ea2jYF4q)*Ll@VTp%JbBp)&S>LB`;ilA4j_%Y^QFpeDJZpBxtqsI;J{(Hu_$(5Q6Q3N`IF^^V^SwzAMPpK`ssG~M0vJd4X)n3!5P==^Q4Bo4(U%?PO7 zk!jz1GoT zn6#DBLC7dNj2*?OP&=u&Z->&M9T(3$2-k@))|Q}&pD)_5zGH!>1DVE#nuA&2e(_|f z7pU{FY*}=Hf%_NG64qPYf0SkaQd<9#Wh`v}We5FUQ~0Ob^Z#!Fu`zP}6BPKrSvfYp zW!blt;j7yJO4!BKQ&QgKI}amg<3g4P z=R$$VfkReB2F|hR9}Q4+&NuacvRB!*)6avUDvniRo1rWacOpS4wC*n@Q_qa7Uv0EC z1E<&NtA^*Ami!Ao8j>4NMfQ9BCCXGOwi1p01xgdw*qn4c&~&so*4$9 zjrGnr-c&nL$}UEs#L87)T`lEmZhR58q&nSv)(34avEg<^rj$yeJ<~~MeVK2HQf{e+ zIF@CaeIgy$uf1%`3Bgeo1P$1dpMR@ zv`ocxXh;iDPfA)hp6me7CviU2*d5DKRQs~R4U1s0+}!BV;56<*ILcBAaYZzOH11_B za72-=#Du(Z|L==SSE3DzTXq_pmSmi=Jow7e@1^e>jqNW9667bX?;8#7Zr>COr~6iEMo4=T2q1}w~gzH5Aj_+wn6H23WCD0lGG z_kj7V`kG!|TKUh@6W4CgS?%=K1l9l1lc1ARL~t+NjYk2xJ?g*&C!GyLjy3IVVT!NY z*Zyrn+yGnLvIo1r@(g}SFBx=14b?HKR0u1CHgCt-0;rH6YG!h$3(H4Z$Rxit6N)0V z+bMdec@928koBjFZv?8kRIRx8GrZtkvD|<+v}r#ysWnkGfCuDR?55i;rhxNop$F6< zNKflP@D@F!B(;d zp{IM}mA^-lN~yb^sY?WFJOksxqGVA=n=lp~Df2z9Jdo?-_UT%DN65V8?=FdniAh1M z**y>a#*M^0t-j1|{4K5WKAN7RER=y7rmKphd=qoGL(aM1^il6^Q08KP0%T?u-ybVJJ-6VqOK^>oJKHy-QTY zVEKdRd*Iq@Ax=y-*6$Q2y3a#qPULaYY6(mmES!nWRUI(m#PFLUZls@Yh3rR)1v+Uc z)2cG^Ncw%_BxYj*$r{>$N%nN#YGCgiEhd{0B+j@J(~T+jUzAAE0$;}J1<8{}>oDS3 zP3bh8FawT1nuf>1^E>y^r;SHg+%g}ZgHwrH*GvsDva3l}TM<+TnU9_5v~NPT0q7+^ zcug?MMltS}QQ(i%;Z5nSYL4KGVSac+_|B9ry?uEX3!8YtSy!DYaGNig)G;!@bybX)}F(j zv{z;GwpkQpBp$Cm7K1~5WuRIN<;+wcJ~*bZF0WX2Mk6ECuYSTN6)(-+IsHgli+H^C zeGs9!f4+GzFkBD)#scx|%TtI+$7#XOzxi!k_NyL?V%*oE&L=m=SSE1umURUDAy$KL z%R3GD&13WHcxBuj-=a7oqPNA?rc29A*VKfidflB^4!O1ib+AM(`+_-1sP`n=%q~$9 zDRJ!;U^svhAS_-2voXDStkjxGGJ`{|2=ofagdQey(?e*&xM$Uv>O|aJ(oAUaAxdpF zd(C1tWy`%LQyYM~HPXkR#-Th0hjOIAUZcQ#*8IA*lHJKU_-I5vO~r^87JOPh<#w~m z@6VX2BOXP`|7%eRnGT|`aZ~*-4E#dr{9&pXPQ)xyhZ&QM$=C#T1nH8coOqg}n|!gi zR9eH&&gX@s)PQDw@3$W+6A;)e0jH}w+5m$RIO>S-W*b?~HQ18!pH$4%v(N53nQiAe z_#P)%AmDEK_sXF7dh`)?Lwh1SA~NGdD~1-D3l;?weHIDQ_vrA}Bh?K0cKEE}u!8ct zkQtZ4v-`Qw!tK!wj(F3O{&+^ptjwAX{IL}WOGM7@K@R!)YW|?$dYy;=q1OL@<@=wS z&cwyc@y|E@`3%Fx#PL7l0lp9aFYy3}Iyy0%&B%TNJ;H+9Am>b>g_1Cuc!J*aLhx9Y zbKtK?2S&&>dbl0U4NbF;+wPLmq+;+?qZ$2jFreCc9_Aq|+Z&SU7kMv7mttS{FPj-1 z2K*cxHPIagxJ$Ks`^8E?Z}K7qJYP>Ieork|>4+UH(OyoU%bWWpp>Jnrm^IP<{A}lr ztz7T|L*K0!ca9dG$+&KF^X;a|i?hFjVjR8c>m19n4J zM@(mIkFN{ZJJtNm05qImSD#JH96>;k5sZYZ$8ai;qq0lqo`z@n_!v!nx%^x`4nT;i z_f{C&EGvkJD-|0O-7YV;Vm^^{atf_Te@0km9wdT$SvV^@|D-HaKb0l7$YI8MDSB*c z`Q$8Jp2`#RuY>iSMuaG^rdEv1(v{>`)fg3|oMYzK?}2yn-#{nV!XtZV7^)y_4C-_Y zC!)1tF+gi45JjYgs6}|Qqb)SS_gzX<3wX(i!aQ5KXhNavh-h$H8T}Bk{Nb~l7HlF@ z5zoMdK`>KlO|4Q>?rH-) zGqrOdOLP^U94@HVc)mG$jWp-4dW-buOowXm$45 zlm?0NVJt$2(5sB1AN%}EnQwtgS@JVH)%o6GJ7)45i0&S&3say_%o+ z*@TS+W25SFv-~3LIZ4EBIbk{SBsl`XNW`m^W5J*yFk5o5x9pd|(++=GJ^AEEz;O~F zGE$QcL_cAM;u8*L2jDZaNl)nO^{KAc4g}d%&5REn7VseEh=0_C*O`($GO5?|O&FgG zqlzCHsgP(uufRr)@SYtGeOe4z)&*>)ql;D**on2Ia6#op(yLCCYjo}`H&k+Cfb~uL z-31aow-%hwi{Qcr@!!0&8_$iAKNF2G$uk-@jF4A|h6%=l<EyDS6mbfL-+_hUyHYjWeeMrf>-yIS==KXjB^PkxmVv~@OnAA02F}N}B29OY!^7UExXiGd48}k=yj920754rf79M z9B!L>7p5I^5C8+iy_W76PGBaR>*TCr-h^;Ta{$LvwrXUW-Hdmrc_8|TV~g5 z+iyO3`2vH!h#*^d{yEJkLwI?eT7|v)l1hd;_j9$4&pb5?PtKL}F)BP-q$0}N?|loU zITY(=d>kVEDQsI@#KNX&Sg&G1KRO0+(m)~~01AHSb9Z?{s5IjYb|ikKhR z>yCZG4!}rvy0725{L*A_(P5F8vEMHqW`6uNJph0ozZaALjc_20{+C_qcneDIK; zd2~!Py+45|LG`NnaJ^9RuG-iqCzc()&16cQ6hGPSWY*eAb)T7ru7_)dvZY~8&BE!$dp`W^8`=}dzO@wU=f}4%THdb*||bH zRrU$mxZ=A!ipc=-i@A3cqgSXJiiz(WYcoG#ZVFj<#%M;@K=P;v!!EjY%m`$;t1HSh zWDP6Y%j>n}(YYhfj?D8h5ufzvWqt5=pr%}q36!&1qhoN&X>oMJP#B?Gb~-B!5<_L@ z;m0G$gvh}#;x2Wd>F07ql-4f@q6-W`{htRaBw4DB^{hqU@M?Id+H2mlnzfuig*b+a z97CQ&EdZR3-jvSxBMDA7Oj&SPN zY~_EFLjP7b|7v?MGyS)=2Q%CMw4$;xv;AMJsEax}k>~6v-_t0g@3x1JYWr$PUS5q1 zNB}8ME#f#o99N1Qr;9wmSvcN5C*D+wOv`zlFAFs}AREw96<^-{dP>4(llNkGXZ`ek zGTag);A8!`jmQ3FqdA~G^d=74C;}u0HfR(g7PPon|xuo!MW?v_lR7~N&z-aP-k z*sr+~@aBo;9==i{ zBHZrh@>yK1Y4!AmX$h3B*(~t#rr1!)oor%Vu5B>5<&Sn*+3Kro9KaaHKG%Tz+NV(8 znvHhIY2e`T9PU?@uwo(UFKtC*8EeJpL~Z`^d`>^+Yap%BR(`5nMAf-WGvqs4UQ=GM zE9=O^|1_`-e+ns?6trix(Q#N{teY+Eo7<;UN7L`f+o)PnK02@FwC*mJ5l?a}px(L- zS7O6?Xk03^9x8;dEMA0w7`!i{#9OS6@#%d3Xt z4FF~au+2zeCWr8)_d!Nn4KE;v82>={Y*rzN55^u%h=Pf+z@yq|LP>|pR)}C?;Teq! z$~~Gl+ybrZQwrZv?-Pf!C5?Du~1w-%REkjY0fE)4s^*f0rBfaT4& z*AWQSm)L9ooT!oeE(agU5#Lc4NxVE$+rkv%1lN^ZHFS_D8a*KlYVxt-LH*z!d0J`3 zLbS06b&T9z7QfH7p>Y##V;>3PT3*(xX2Q5C>Mq)cv^Ar|z3CvY==o?+liY0^;q>wE z!C+XyPqR7e>{v|Nhls%_E-Cvsm6-FeSOJoEZ*oJo{Bes2%)t<;;z;USS9D}ymG@t? zHM9oet((EM!=M!0JBp&0lGMKgr>#aMVP{iJ7kcS2G9LOxA9qksdppT-aK^MZxpr8Z z!jV}=%M6By(0!2*pO(rNy^@0h!})k*8?fdmYLjdjoy#A=aj1vMmzuirsZULu|E8EI z6HQ#G%g~0qnt$iUd9cxxhoNSmMP zK*fRzB8w3a_ZI47E9OEMt=kn8U=(kxe>S0~E6c!RS~*b@w<}x$IY)c zg(;#AJNbKP`~q||Cp(^BXPlFNVdH)ZgPhY=_*64G56J52OAVXJN@LlPX8M#!xjKpS zO3&TdvPCF5K`RBzh-`v1E{)TS8m>vwazSJvSUtzBnMl70+9WM-wIRZ$Fmr30sJHp1 zE7QxJiMAo2&5>%0a-~pb1bUif7WPO(chevFt%enf2jCKyg7OCu7b>Mb8Y-F}H)uYarGc1Le3# zn9cZ5qP_4CcADOI_$2&MN*&8SFK|Hnp`B{ZMABhp4L8mZZd#8;_WcjVaC0t!#Uqzxe zSa8bkjnr|L#Pj6Nv55woAURCvt*ZuB46U!!XcMNI7n=Nuq-mPy2h;6gUEl3;Wr_yr5+>;g3Xw%Wj;4jB}8`aPjAl_T^XmUZ~9!PR}Vp!pg&D=UWm)& zYQ^*{93bgi`;rt-g4Mm1(*mL6QRZRt1Kl zr5I_$@)Exi-FKVnQZ#|niSS^!cXyr>tBy??il-a-(q6%~nV;c;Vxr2;>ck&3DDuY? z!3R%boC+l26+!^l@P!zIK5&6of`lRc+R=oVuzI(_i@OW3l(eoAKO6k00#w}4495T! zEMF0bX^TJK63p;OzSMgsNmufqtO1ZZQU z%U*zyh%TP#7z!!AQ2x4Pk!rE@)j}l%B^;!bm0m`Qt-JvREI*a2y&Q{KjO2GR!c=MP zm)>FzY{l!1a{szGO;$WiK-4uaFgMekBcN8*&!4mD#DlP!EvFq1 zmik16sfzVn2-lc2Er?QRakRb{Y9*TPVPG7uPvqNuu}LH2do`BrPBl<%J1gnyjk1im zALxyR?OjO_)E3IGGU5fEyw%kd`S z!v)pz(WA)9-v)vw$Nas~eQXEF@~R4ku`^?=!tYsUa0A+GR2=8QJ-^w&WUiU&8fe~f z5;EvP@o3Zu^^SK2-@mo>@28~4QOQOfYQ9h{e*T{2R)F9&qDNCp4x%k03fh*NZu@$v zg?v#0l}FWRkN^gCN+e}SjMA@j8WJ#`DkLVC!aiH9Fx!$|&P9R*?x zv0oo6hc=XRF@qp*!FE5-;NPsy>j2 zz*zhlTkOE#wL@Ll&-(ZD}3JO`| zz3KCX$RcrPmp3?e;8kp}+ioH)4T_Z&_DlYY0uCl|u2-W^3LFO%ngQf2=ZTK`gI zENuS;$6{mQVEUiKC>D-?q}u;BjGEMziNqU4?tZGFyq=t?^3lM6gXjCG?W)~rg7jhJ zhg*_d%8y$zp=1Uc|xkMqJPzx$O^GHKw)JMK5$OATe_P|=?tbOR`?+yWO%)@{Ag3r?`kn*9vQRl?4C*e10 z@cWbIDX!ufu+3==*8wwwIW{_p_0U z-^x20z~lY(dmH(X&-V8E_GbC0guyc>bB&^9-hOUTUT)zZXwa8*&U z9z16>S&F^JEc@&3`F;GTgjAls{f}lFs?ZXaSzTP#YMd0nFuh~Cq}o<2T__cNH{PjO zu=6lb3jh|X)}Jz2C6xfxwU0wHrxeHix~K!#p-{A5w5lSRS1~1*M&^8gn#xMmtT8_? zn{BUwh~ns*;zXUwv{BQRE;3aqxYh2Zr%9CruUCtGYSd<=h?K5R-YaY(cc{TV9~e!< zFt=?a0>uXOZ1HBw+fY`*c4#uBO8lZ`grOt6v zTp2Y%Fz=Juuyyu}kEd2kG&jf%S;Ag#K==G+k{z= zTkcs^r40*_LdYX`)U6qQLS~<;@B0s81IbUaMS#_d^Lil?Kn8%54|BPe{Z7Vwa^~dJe*BFG=*7DYor`puf;Gs^Mu#rGS zM@2M|jg=$VRcqexyf(TiS8#lk+krG#Cusk$m9h-$Mf<#2%aky9ii6|pR~ZGF)?F7x z(jpJb-62S%?O=RxHjB>q1c)fpvJt)_I*iPxw80jNG_k^A3oGjT7{zFm~|uC`FE|BDpI@bnfIA^@Q+{a{K_O zXW$-;+>0|c7Iu%Q(!xl|wd0us;k$-aB=ZJ#^$k$qLr>%&nihxzmF#MSSp|}XAW^HS zqNReI$w6DQ&aapi8?(~-3~-_$4BZUh@}Mf)C7y}5 z`zA8oS;I4nXh|h2+dL`Y+9DVao{954njc>ARt#03;a5sza`NPPkpj~9FOP-D7xUm^ zhg=fi*6#L>yUYmVai^T>9F-A&^m1vF4rF70u2m|5kbuke!q`zeZl|15vN-0rZG#?s!&7`)3b1Q%Dr?c zHD@kp3)aWvo3bH=IKR%wir1txq($%g=JJVi9x_lYXMC@7qbHoldRJ;8{8>ICQpFaW z#5|X2d$>r%{X=wlQas#6c7WB>d6q7Bi_Tzs+IKD{tCph9&rZzoF~)G+b+~<(q{vJEc4g(ZYm$hO#Y9ITuy)GC|a%u@6tC-Rp>MX+;^Qq15v?b47f zGsBCZ?2nr86S|*I%9?1geAus>;x3*XvLBNzcbkZ_XcZ^32T!4W;L2+$v=ukw() zPl{L@d;sI{8Tinyu8qwFtCpI+ahX1p8yS*IxDzkeK-y^5XKero(UQT>1nHv~MT7FD z&f=_{`2%SWYl^+YB!IE5xmm0xm$HL3MZInOwrth8r*)7Sf+6;(1QZXjyMiv7j=n^J z(=Pb>px)+!4WU7{ghLsxg;1RFc+?ElDWWM3Jl*Oo3z~ zSU}L2W5|&Qj2*%xB918hHF7SMJ9MY^P`bL2uHW0JoW;5)RX%sQn9zc#S+k{*gvmbb znCTfiB&q9BEKY$W=7)`tQrnafaYSqMfr`m}aT)3Q$~P2E$#2{RCvu6X7% z$X|(IzzKM|hF)hM-!VQ5lcUu4AtP7 z)~23IcKy4`($xNwGW@sL`?YxtN4Qt7?%1`;#e5b60=<9nh zInL(CL*MrQ$u+lYdS(fNbB^V25IzV-O`|z}a&37&d^`878|bhHeBtHr3v5-Xb2wBG zwqPe7n(GKe<;)KlyJ&KT-{3!YGOq5jcdT~E3IbXU0)p^Wa}DW8RS$Rfjomdn3wjqf z_xJzKU-ZbC_K78Hm082$?8Q}xP-rbUgj%R3^WS;>m5RF31Tjmtv8 zAQBlOPOE#2oYz}o@`i@9aV%jOg&`dWG9cN_M#^c+i^q(i6o8heMxGmm0u7L&p4pd$ zr-Xt8iSG4|((lv9dVe^>&OIiqsnRgL#4!{Q3`eWpx6Xgzg`HCco*+D*MCRrn^6djHU{`y`8|tk+oRPiEcf{tH)h)iU+_ z%<7tzN2v(3FI~u6N`IbCDWGv@2;d5EP@4GT#oA(zKb9klU(ib=1UO?zaf2)Hjmt#i z&bY)hvA>2^ip@;%GRZiIFBo6RNSuW}lou=lm$4QR*^6$FRl86^6xD&R6v+%UNS1_^ zi~1j+?Dfiidc3o!OVpI9w%b894SF2G5FJ(he&kL`_$yDn%*iQ}tNK&_UdHvz2iP=1 zuSug#3JnN2+3xOA$GMjsi?%QgUF8e=6mz^*)ehenyO!7DjB$E%Ep8Z=8F=mNCv~BqQS<5>VL#SD zHPn}9LiqWQ{CF-Uu%nws#_#vp6d+6>moH06&Ec5hwGqnwxXB)d7K&Y-AE0!G_h|BII#ut z83t5+(N21|r<%Q_fM|SBdEhx(mq~FEsW34WmI^8lR(nn%$uWDet!w&1i13AuTHdVV zFU;@4)}5`~$TN-UsZitl-XsGxanX0&!qL;=UR|1Rxr>4?YY~mIBK#gSL z(BYV$I8PN_s#HfJ)iB>}bTk?nb&c!cabSJs7Kv1?fM}3?$t|t=&I4i;8XmGPC?v%S zvL7^x>TMfpm+PaLp!e)!?dLCC<`m~r2CIq@fbz4GK>IDiv~x>|84ZYDD_Hnwud-Th z$N`xWThq_HG=~7oCRTD_JZ*7+m2cga@f~B{GBXa=Z+B0XrH1f+B8omV!hIi^I$XM6 zD7Z1wPEnK?d6&{_Lz99xquU`vu39UgEPVg0P1Mnq%M(IQYPpHWWN6lBHI<~P0Ht#Box?ZAP?z}P%;A57-ryLoNA z*@-7PAG1pqS9n@F2bGhBB()szQ_;CNQrGXin#bs8F!&xe4HlkJ(Wbw0?T%t+X0J6G zA;KPRRVa-#AO@MM7_?_g_yGHIMbk`c0co+=rcLVhE*~NB_M>F?DxJ$D7MzOlUADU` zdB=)O1%0^aq%Un(c!lR*^!9rX9N?I;7UC@f=3mr41`TFv#UpwxW>pGS<6|52O|Y5U z8~Qm@TlAF^c^zH+7wmto>c4=V#6q|JQ_1;v75S&+FtM@yS5_Mv8|yzN*niId3zYhO z_)mx8e``i`X{|@Ev;V8)9P0qvcw$0hf!MCn;}8ilz~1K_2hE-rL)S~SZ{}WnTV)~& zMSojbHBbgtl4al^AX-di=1Iy`^l)&z4VSn-rEKSv51iTGS%^_9qZZSMS=r%evkm$4 zZ`pn0N?u#J`bsk{=f6|d_A?x?Rt360ZXKU)tXeueH+}spCD#d}yYQc%o&heco2+of z)?wAZEA6*cIV`lkU9wwOVWc5A`OswW$QKuHlP7??{U$C-cE}=<4AF-KMPag3fqvG& zOe+!SS(y^!71gNnv2obYU6eD4d}IC;Og~~PZH+k$>FMxli&~#o~Um(`P?Ve z`bXc4?OUDS`Mu8y^q|oNwcD7=9n5U^(nq($)p6o4lZYraA7q-lh}q)74VcjzV2EiY z!0sT}Y9U2N;APj5CNj0n?OS;qlnoZfFy}@ZTP1>@A2t{DI-`IDC^R3=NZWCk&;V4M ze4l}T86`CP*0XGec$a@j5{{(HU|j;9@FYSv36wy1&2qL7A?6$KJD(J6H~(A?!9Ok2 ztOVvSoCc`NRq0bJ!X#2b!hO0C@dPqnQp5~_g~h!S>whBIV|~*vPfM;UgVZVe7;31s zyC6x+LvYTv2Zp$*`!7OIEf^!1%cW+F7V5yaw_a43HVl3T{ zOg*b!z|Y#{)R{(Ez|DtY-|+JW6g<}DS_{})vm7CUja`om_GBub;ODZCm1EiO!>81j z;f9|-+e;zUy)21ntB=Y$N2_OB!qcU|n_iA+M|pt;4_Vt2aY4?wxz7qqVNaX)TK7az zUu$17H(Dzb+XPn!0b(2E6un_J0+VH|W!Z;4$czc+`ir5V^I(+v#m4Jf8=zSbwr+?B@K+Q zd9np4jDW8oW*~FsjI_Kuj0#(1+=YdrSFbukag43a;^#iMlTwaH`UqR4 z&-zF^K|+krj6(9Uw`i0`OWGuar=pEFV;y?McnooiWR)QG#LGc7sKhEVN*OlHE}K$` z+;3Rc=wuD@mGP&mgr)f%iA-2wFvM74A`H?9)oE5pT`I+XSU|2L!9?09LN;-C+2n}JXEIZZ_J~wAM`=FI zdjDN)U0&5OaV+0K9$<1jDQVxc6zp^#UB~h-SG$S!BDKa_K;RMq?YJ;GGo4LPx5Ju= zGYO`M{uQZKK;uw>JOQbtDvzFR>)UuYXVE2F<3d5W7xBTuA^}(Tl%V=usG;m>x}4zU zi^s#;6o@BdcqYf!Hqq0z`6jeAku27)W8RfJ201ioHv)I_SatTLip3F3sA+jImjgtl zB-x!P$i6PL2)k`bm>YY?egfcw;dd}8QDV1f3$MtVjAh}d8Ex6?vx-~mdzixUTn>lq zpe-VayBzVAwpJ7%Ji;u4q9a}E$^pCXm}2-G(>K|KcA%C(q5?*Ie++vTr+pD;!Kk7~ zR_{ohKi(sj4$p~7V6KTYjM)sjs$o{UFt0XjL&5!K(*)$fHcI9dOhdj;Ti+Rg4uwk+ z1}p}4l&ySa>?+`G7zM?O?vzRM?F~>r(!9V?GGZ zMd;EQ7^gk4=@Rj}qb?mRw&`)<7b)QY94C{vF^m2)n4H`HNeBG9wEs&7aQ(N%;1sR z{zb;{x`O}C(fsfM5@je}uGVSPf5;nN>q~MQ{9njD1sZXk8j*Y&*HJZQHhOOAkYzP%U9gqNeWLYP#u_Ok-ms*XwRr*}9%CV1-JbqmM`%5gQi)!iFZa_GPe zxa!r5XHLGx)vJzYkMmeS-_QhB3C00$`Lx4*)Ozu}1pN5cAIIEM4{CTq7-|4-d?AK$ zh$e2|5x_zqpnpR=qqw z=?I;^pKhxm;?kmm8S@sQQAmt<)0+}4w9=zJz)DQ94{?Pcu<|=YXhZo-vwO-nnco&n zeW@Ar4>w#yI~@dYh)I#LVoGF3*=qU z7>^1Cx)Tn@2l3?vMYjDk{QP#^ou$vxyduM{O4CYx|3nLB`120A1 z)5b<}Yhalyqb_yFw;4OfEj5{%!k0Wpw;K+^jojtAS^Np>7$=FJhNqS6Ur8b3t4$o{ zkHJ2a_(+KuS<{V`o7<@&b&n-#!@F}dudRM*IFaG%s<0e2HW|qa3y?%hTC}5xP!$fY zKp|ihY%ZgI77QM1p3HrM$HlWs(yGe=sF!7B>W*7N8xAouw`J$4G$5lo`P5z7O6>GT z>#$24M7FuO&0Y`r$u%ORyx-64qwq1mYggWi z!GQG}!TWsKcwJE^)lj&RVoNfhy=NNIzOv_EJ~<|bcWy730&kyltxD86n2z5ZyCH)8 zbr~IyY~_xC9OX01x=uh^^gDauskdvX(Ca9;r5>@$0ZnkMJ*EsrXFUri*iD+2_Y1$! zYbEd``S_mro}4Pdf}v=Ie&kS_FJp7el$48zqv+#)6}!xKVN#$S%6T&6-XXjNYbdm# zOk55(+w)VN?kGv!Lx&qag&g^}NQ?L`PG1m*dh*^khdwUH1;unei6zOMlxf#!+35s2 z{4d&2G!1dj1`x@BPGu}r;b@do1dii)I^?Z6*^5-5%oDZ2R3ZwG+LiqS+HB9f{Z za6Hl*t^^5~knEKLx9TlHv7R!Qc$As<;81Zg#nNpV*^-rgt846ueWhT|Scl|k7ID1q z$J&uo8oFgh5oL2fzt}xTZYkQnB4^z%()CJtXVNLnY3-Jr=X|=ghv|W4#c3+oKO3F! zlhIuIHY*OL(!Dmlm-MFJmwSsI=>6j{tHd+9*ifOqzcqTG9~r5@wNkn{7@Ocggm~8% z6?!S0lk z{~uoRGyGq|OB#QYOaIFOlFi%_AgIw+B9xi=eT!ITLUQ0mjfx@nh@Ej7KHuRa!U|f5 zE~^rTLMzWm!Nvr^YF&SE<>vYJ_v88V z;;@RCoQ~`F2CwIrU9+xU_m6&|M8AF&CphKw`1aD$wcZkB)VAm6V!G^nOBrHu@)PTi zvVN`~?h|=#P!+pci2*p++vMcCifg?GDl_iYm2J1D+na2UTyspbEuJmkx4`gcag5wP zVXRgle?qljuu6w+{-Ae&)k>S*pcQ)d;Aqgt!U5Eb`JO^ zlZ3B1TR+!>Fv1Q*bM%Y2;^dNXUAO?NAo< z@8dd)Gu7hC^IkxY5dQDSg^nO$j&u2WK^w^wpghqxv8(ksqW01YAnNdhsJ@#%=-Fw} zkiTMLMb3bD2Me^Vj@@znGMBltTw=k;jJ~1R90AW9z9S&HF!+W8D*&$zZxt2f}^g=O( z%nnW=%!tq(ihSXLdkSEUvE+ju)@^5hkc()7+V_VeJ;tNY_Tg?M=z3;sU%x!CPV1>d zk$Pb>sZ~xz@Iq8SAexIXPLLhUakDh|q%7i2t&j26I=d5J&&WiRZ^?e7z$)2w~#x zsdEdZ2FSZ3tl66)X|^|`fS^^Rh#H)TzeMk^Oe}kw(+9tzf$(Nd^9lP^HN-5D^P_%- zM`1nXOyXo0%mgJzu5QPI?bMvm$war?6$DF9Tg=*}XBCs2m+zb8W))L2i$%?#ra+WM zl_rWm+n`2w7SS&=exo%cN;R(eefk)`G-MZ-PvwNj^w*o7^QWtjh^~ezqt*-mWNGOx zG!(3E3exUj(QuYc87wucjB(6+P(bJAnzNG?|uRPHaFt3n}CNGR|6YOEKR(hlcDXlIJ z!~VB+KaJ{R0?SP>tla5=JVz5L!^liaMW_f2cYBJ=lD)qoS)w%`Ct>Jq38pLVbUQ3` zcwOV+IfFS4eF4q6vI#$AY~>oIy0sq2b`V%=pX(HI@4`Nu|LLSiuXPqPL{U**A{RBr z14XO#6W)P;RV|9OSZ^_PuW2d1x`xDOPvY^DeXolK?e-{WYo(pZzOtQm4Zz)7o;fQZ zOx`cG7g?FwabmV%C+ra1dn4*XW}&`zi!Ah?g8R^t?#OE&!rj8^<}|ZIgHRzCm+s|Ogr_(gzNhITe%DS zS1o371qQV6e+0{NZRA`vq$P=^3~;OF42c6xK?HztXvRxzcln3ol`ZYYlR(y%Y^C3= zpkOu&k4NxveuXLbTH|edySGRgp^UPHN-jCn{bgU_@h#%Y8E`J&M2IOk8+D6@nVQE6swS1)h&$#X&hK zAt@2pIHxBslBz{OZBUw zi5WQ#G4CtO*T#FGmCCHH4w|i+PL-r~siFS!W#tzOn-P_4eTxkhtH@~^i9(&KD!#iW7`W`kuBa-C_$BUO1BPEmiKjR zZIWCCmj|^w`vl+etu;s^)hvq@&o1?(xnjQFz1RM5IOJh#YICdzIrdRhL56ZgxfQ$V z5vEaw75P-@9W5lNI8=Vp$3b9z2%#$wVw_+e2=(rDK*^wrM^N+6W)aAhKiC&nBqhth zo1f~nB^m_D%2a~HpDNZ){>zfZS?`6vF2W{wxE-Ger8Qhy=-(!76wYY}9Q#lbdHMtO zm&LEuJu6d}Cw5K7$=l#s6#6znRsI*WGrc~85S)Q&^8%=Ipqyx3fJ9KD!>jS`XQ+9x z*gV#w-CTVMRG-s&d{O{?C9RJ>0R!Pt&JM!^oS8)fZ)I=rb~c0m)NS^Ek@w#)&CbI9 zUzlcR_=hkHcEI?hjgU{y;bGBZWp4T=?MOBK-I-K`Y zbN!)M$-R52NmAvHZtMonyp1H6nghWLp-aEQ^ zRaG9B)!$p2K3(lsSWNwm1#YA8R*OF>NDI-9> zp)6)ZqIW+0(i$47;Gwsa@wz+xl*TTX!>4yh@d>lpR0b2`#foH9Nr~a~9?^ZKfIFp<29fjd_+OgUv)r+f(h}%WMXO0g8LRb)S#2+Hl$n1AT9_WPr9*`6%z+{N}1)5V-{N2SG#K&wS|M(70l6eQ9 zyL+h;stm#QwK7)WvnB{)Ec5_QMrfdBM^RBpClb|J8hb$;55>E#bC1-MPQI#W3ZXk1 zA+pf?YJ_w|&2rzLRIWAz4AA#5HsN?u6+U-~eaFA?5I5*JQy(OEX(mr?V?-nYba}+IJ^;d7)mgmNI zTCn6<)-+YKb>=Ve3&Z|`idEhIxHW)FF!1OEqX4DCKB9g!a%ouH)<0m?n3=4SpDc8WuN}R!*y7Tyn_2PW zGjWnJu1-lRF81XFvL6M+;TOO1M_X$<_Qqi5igmZi7R~|6*{IVY^_4d~UciYI^_TYD zMWOhtU|+0&0GY|U>|G9gw#@Xfrr5O3ezoZZ$3ll%J*${U5Sf?d)2wFw&7tAwsD*nP z_!FqG7GE&6qT3(Vgbm8jELrBsT0^)pYZ-RIOF^GQvie;weu`qMqtZZ#ihKHzR<39- zto^NHA_{!+r<Vl$X!jH;KTLBGgLI2sXV7CST zw#kmtVqP;+^bo%$Y|7oKkt!q!Ht|F^OxQqck9_S@S{dkO7^vIcI`W;Z_=Kq_S|n;j z8;!dPF!eYL7R+TEX`01lgra;XP*yeV`4=dIo;k!nEsXyGmwzMC|6B9S!ol|HWT8 z`Tss1`z<>BEQ}an#{Ygjo~NA&$%7ZI&mbMSD!Kx*`R?kEAeY3Fbg8ol$JdoAnu>K~ zHwz|G6}(+OYvbVQ?Bx1-QnuD>D(8bzt%<*Fn*k^+%_)ie{?n%O`C)Tgv$kJyez||{ z)3tkXb$L2n{P^}}^8Q(+pE2ozj&QSmI%)Z*zKk8K6hX3l@5tHS3jwrd@8~Lp1%usM ze-i6m!N&XNdU|cULih02uaC{h^Y9k&ZzGKQ;LFjGj{gSl);1oVu4_&v5a?f#LwP0x zd%yi{wD0Ab;d9`CTpUYKf5xG-saA;1zGd@RE%dNSm5;=y;5xjuKiA>a8tsTn8ESpe zeemx;VTo|`A%k!(2R+wnouJU6z?-Q9f!$3pMCnfEYlfFRlKxfuIK3bBlXlS?Y36-_ z@EP8;E=&6|^m&qsTM1B-vWkbJ-`ssDCW;gcj1-&+i;M+RVvt6KX~Gi5dh!Qa$NeKV z@7*!acw5^pVC5A8I;%nKe^i?NX_!u$Ivo>@Gbuh6Ub6DXH9Krmg`bWI%ShQcPI7yr zNmlb9^Q_ut8g zze4s;UyhtUZn(VpxOpEWMI|-VgJVfDF8s%aWFpBMEUZBc5?Odny|9_O)wK1ST&j0^ z5RO>K;@E$=Y!E=8;yTn)zw_JXp)BYWgaaH6!(|Vr-H_w(MrR@5yx|43ydWGKtiPp+ z?azc}F)?mU8vPaTed1=&g>wFaV-FzHtl^Q1M7p>XU6egHR?l$eUs#}g%U=G4yV&el z4r}^AMelv$<6HoaN2WMVG<@1}P=&0QdQ6-J_?ABHtmj zjMkP+QnKe=*g$LI`r7pZDNwqLWZf~a`!xqYP3Ee&iBm0kPg^rhg*&7btZdKZ|91(N z)>$8bKmc4Cw^6A#ywR;wZ^<1$Xp8KRrO3BNRk43dL_gIoo2;*_vhVY5mi1KU_Flg+ zulSWt0bRUs5yhyph5|JYw2*O;Vh3(|2XQNKTJ{I+kl^8K0@rjg&7g?1R>Wzi371pp zXXVf}%NExs-WijVaa~Ofb?so4F(pM*nk++e}=q(wi;Gu1V&wp+vk?y3K_?7*m ziLduTjX99>1u>yG$ny+LA^#LPAjL=eD5ry&b!rKc8qwlMNaYL9F&yU+3veCh@Q4=e z%Z*QPqtA@II119HVz}B&C5lOaVL*9?zm4vZ-;XA%sk_Q*%9MahA(Q4$oDdji-jX=r z->1b(@o=OLo^&=~t1$@-Gti@L;b}YLSooMfHn*#aTbmD?s+6R*sXCD#cqOzIVNnY= zr@I38&41xv%(#9QyrI4Pez0Swbb8#V36FH%Zn0@rUL{x5=hl-?dSoWhDvm8HaVb zQ-|O1KLNmjwO6T+(#ITu&Si@!cB#`r_%G~}{(biZXt1CF02ag+=-f5$JJp%>0 zFN)Wuq}SR=C8@C+JAO{wN_aCaBO=@2FezSmH!8PutR)>p{{(OUL9Bnn8|(kGXtC1& z1HAnV{}*`c)YORi(MEYc|NBk!$-zwm1O}LQ{a-~+&<_MZ+9;lM{Pv#~>4wkeECLan z1^M^{8)W`C5{KZ-^Y`Fzb%EREJVd;AI!B4Ewf%uV+7$-Y8wKNq1;kO<4Gps2D9 zFHfN4M_G2YUEbMg@M~O|E8O_-__Va|(CvV{d&a}Vor+;oV7ANCp_4Kk&`{Oem{;`eBDilaRCQoMIUhWSmMYJa_$c~ba9hqLJsMTP{h zPCx&a!YmSYK77@Bm}enqlb#4)&2)@6#ylHwH> zvfh4w3_nf&FnmA%`gQto>e|8Q9U#iLkR#1#>}ZNOdR2WUF7`INF`chWNpnXh~~|? z^P4st^l`F!slh2{oQ$L{n6xtOQ(;Xl3{n@mRb>!71D)O!<9BA_k+?{fq(Z?g`Jl}q zWn9NcEN+B`P@I$AxE&o~&)@v&A;Tt7GZchk8&w1wLb5H7dGItdYL^}98V?d-72?t% zI@Uy{?sEdfphy>zI=xHrJBItJgts7Itf&e5$`Wb2uuWqNTl9;PI#*HAh0gtD4h^B+ z%YV6k3SKPAtik`tnrW%3B6n#btTq#PR-tLIf=o+6qp!Dd5jYYc9Z>7lCNaN2LE4Tr zw3|E_UPmsd1UN4;Y=y0#UC79-zFE~7%xPN#!XF_ykc1qS`Ry7S-41eNNcD_qTUY$P zpfO>h1ak=e=utc;&e`D}P}owe4NEnlL}TkHbt*N@pM_DX4Mma`N!QOqycAKqsOuuM z0-s#(QS1cM4g}LCC+l4qL*w%j)5=xF+3ujsaf?xed_KEH;EzWTz6W&8E8uMujn=x0N+bD^LeI>o5<7BvD3W z0hDCpdDQ-t>Xqd_N7S1PSb&hfFMA;2u5qgyiHidMfXRBam4e0vr}^Hnte(S=95W=~ zIu&jVNHWmeQG*FDPZ7c@RhB*m?CVej=qrt`v&#eUXf3v#!6EPF+S4tT4T-glaaQ$N z3;wvlPI@TNh(3$8Ui>}{-)uvo%x$i(Nf_;#vW&6`w<^hCDJ;`fM`aa~Tt)26osfc4irWz{KLvoSN=;I`mObV`llXOmM?HRtT3R&MrKT`3pzJm?cqT1UbJV z!z)V!!R&`9=M$<#&$D(iN;+MLA{tR@i0?_Kyoa(kY9G znjk52+6F0`=On`{kh>JbHjJ+6H!o~&i@K#=FmqbmhmZ*!f2zj{4j;duCsLQ}lSE9I zj??|@+_PK&fle>a9E$PW8&Fm#LVO)A%s%@7bOMh4>?Dmf>f{jlC8XY1j^H60O~~Vu zRBCvI^8A$1z!D!>YWOI!EdU!ga$7+@oAs$<>4gRiXaFMgtF(GU8fDt%q6u*UoXM4S zr_ac*gV)fr%s=3xu#Tr$v@UQ1eH}+18^vRFJeWu}*zlY&?hR(64Mkz#G|(`bz+Ue= zo-cx5MhgArDP^4njm@v$!u;7zWda3quFnB*`!ct5ADUlK{{Wo-7YqLloNUbhy`f}f z`1fVxzvllH75@zX7g6z|hE&9lkEQEf4Z;CBvbZeUk{;|8wXP;s28M&0BXxqPJ<^PI zidy2!tE{~+nL1_dZUUPXW<|06QDs?8Zm|a?qMK}aarbq8^XmE7@Yb8-wcZz~2!E^( zUp8y7ukFRFCF|qO>y}+(zUusuul+M6WPSDG?elI}1@QjWP=!z0C**z9kgL}Jh9*&| zIO0;qzOxoou(mU!7YJKjy&s7<(Me|(N{47J9YC0A+xu@ta{_z?{XrlkGmk}Ea#3;) zq&qN#YGvJsA;v2EAQ7EE+##lMngFez7;9h$Z$LR?%Mi@&Z+&+#H8Z=$9s4lQl9~b7 z66z*{+Nd*wj3KR&-v>kxDrk#!B~>x$vYE@>6DM@wp46u?h6QRZktn&csr&%w+#0h< zxv2^Rk$$Mwcejk#S!-#fSEqBL;|ynKPrsiZcVD>x`7Ytw-}|q|@tiu{Jv?Ro8RJT` z{w6mPW0H7fO0b|4+eYg)hIff4P-)igItuaa4OOe&Q=O_=D7?NOeo%UGY9W7=23$a$|E$3T|{qWwIlUvOWD#MUrjHI`uHEQf*b-8Wyh zM;fepqQY8@QT)Yfav6_+&5+PrTPc0p#%yAhf~CcRq{UsoE~BANFK$DVoe@L(4YxcZ z*}l=iQWH`}!`;_o?GXc({M%9v7Ta=Y@MzNb4;}bcPY2^FGno8?2M4^5r-nF;g%TF8HjmhmYA~o#FZ`h!Mf^CAP(nDN zd`?M{DL2oEW!Jt$B_`!Dg69&K4qB`mgsfPv9Rio5=GkUc@=iDgie&Q^fkN-=tOK4z z+=60XCd6$qGsRi3)JJVF1LejEGuSSjCBLE5|6C|{tles!4|Hs-E}!c8v-L@U@p>49 zy!64%K^9a^qwsz2G~QM}J&d;lMNA7D8_d4CZOJhQHCz713)jP%8JwT&7M|KH?$@K? zvPOh9jUzry#dH$mok-^etQYCW0c3Lr!l4$5Mke&$CJB7SqD}H(ri9=heps6&u2AKq>NFrr2(YPqm z3A9VUgqVp5tCSd?$Du6IVx!QG`ysV9exg7m%md=(LtDah%@A?~I0lDW!rKy=R7&Rt znoKI*mRkqU<-lB`RsfR%;n25ShgVB6%^R7tkY*h~ZSN@a%ftMkiPb#8bf z(vM??AkWD1;XpCLI5>>2m@p$WFB}EsGnbjK?F9afX*CphvP9gvOH*Xhw6cV7EydI> z>vdQb{YiY(+E@6{UZ7i5KxcMlPGXTqMQ^T2O6hH$#paLKuXPtwE}X>V^v&C<%UeE_ zZ@?iDy}f@rqyKa4{`-t(V*YOm1uNq}wBr2?{}->}riRqNmYaW_(I=I@jo4knK*4~o zD*w`Y=;Vatf#1h(T4E``>-?kgEEcw#hY>1Jw zy`JvoQE5+kU&KQ#SD$PtW#+f}Rf6@a3-4N$&qvEk9mjrQ^7i_^zxdfTV{&r*N9GxB z)7&oPiTRDUUE$JP9-RLXyn6QJ?ChxcQ*i&}-H(D{stye%+hZ^c!kcz^*}iz|@)({7 zm*@nSjx-G~T^}q#3h=!GnW~15IlP^FJm4#_VqhCVQ#IV|s?__%(_#gg{Xi6`#NdQ2;uxG}F()#^!WA}CQ_G@Zwm@OOcikI7YZjicTtws}ma6g=-|8=j~ z8R8|$+(o~559)w>BrP7~9&r#0p5z*!kA=VqPTLdg`=W~jSYBwc{U@5W*JP{P>w~FM zVX|7GPO~gW59G7kx~I^Ok<(`20$Y4sAuL}Pe4gg z#yi|+vw@fsSg)q`=vGKP(H?ni4MS8*CFe6e9qa%oilrp2Rxp*{bM^~xjR#EBi zOF=jA#-WJGhmJEmz(8-XbW+5?p&$}fmVg8f+oFNQhXLYY-kWHUc7e#VDKl74GzOyA zHWc?7Bonb6IF>p5_$wlmq-^dxd#9G;Be#85Dw%6Wv`bO9mSp6OQl}uT%|Y?4Te!bq zUPUYpqS4ZI2Z0C=XrBm_HMwMw@9bDn{m(J;?KKF0<;=Dvdg^22s{Dl5Dobz zj&q|MGE#ztl2lI~tROW%EFvV>(+W~&wN*nJew^0HK8JJhzmU?M(Mxh1A0t#ah%c!( z1g6T3WABcJk&FoK*O3Slq)Q<<->Ya4HjV9Eb~w7XU(z5cK6%q#y*6AP*Bw!`%k7&< zGmbrgO7L$H_euMErjW!PuM)=_6L8}lF(Mb^L{JlpqXC(UYZ!Mq`?aG$h-0Wk+#>#N zRvGH24H#_GNpz25W7JH3Hjo0(w!7NdN8>Bp+FFs2U}=E zL*J;2=V;^xF%>s_^0k61I9<0Z4hI#ypDa$6tfVXfW>X$Qcn)X z>sO+w2KgkT0S9@M!qxCKHfvRA1P>i$$yTle)ug+P%43aU>dyGpt=FSc=@jAdJ`bBq zUu6E7R?3}ht70Y+aP$e}A0!;qlDVkSC;#f$R@Pcn<)8|q0SL!+wR!9p|6!Swl-Z7l zD)ys6(*oRjNlti4=tFy!-df`^x&2VMKxex3fSs)oPcwh5R2lL;ce=~C-mu23>)y^JbZSGSd9kx1N>52gKMZGfw`t z17;Yw%GU5i#zu`VM-AF06yd{<*%T?ej0$Cbm~t zqTeXQqX(JgSPT4%Id)4g5g7sFUBf2+{*-T_m@2oy%7?Z{o}DjHBFx2NBB|Rcs04wh zeTEZWpoPTsP^QC@_2+4JG>*4zD5DL6(#jJ_c2Id1;jpoOjm*ZUe~%4WoJuv>j9Buy zD#n)umMdr`s`edb@7r;)N*3LYiX%Yh*>p{b*>$8$GOpL8C6ipZpsFShJSs7ZQVAr@ zz&9gbO-6#>O3toxmbmY5Nd-xPt->d z_AA%aqrjEeM`scaFCLMDQpRSsTz2$fhr!kZ36hJS87H0siojMTB%#*Lixp`N2~AKO ztXlp3jhpWrgohUOmywUyyGw(4@WTR>e@Uhnr4XNYE^o93Dm-eTfCxsE*omlEdJ6Q` zWc}Q5K&0_P%KEzzleZ6_#>H?tlmbO&Vb8qJN@F}@p^UYUf=w1d)ZZwEn3A3Nx9vAwXg5qR7P2@ z7T4L>LS^eD_eE)j=OPrB@o>QBcXNEVpWpnwWwso+k;66>LBNrb9F+tDB~Q|Hke=$O8AloRLcAQ_tG3T6s|h%$dS;h2|7DqOj`2QqfzIhoWOyW0!>)b-`# z!O5dbC~NJ0f4TTQ2R-94M7balIfi}%NfT^x1sO``EX`ej`Abqf^|`i4reuE3=J?AO{%L-SPL5MW4fYf6waG_HDm5Du#MZ9rG_R$f`|6%Eli zZ%$!`%FLW|i-nwp6O+Gu9WQz8m%Y3+%rvF>B#q@Ze~tAf9C^Q**rdCNza#(cAb561^iHJsV4`DA`C@Czy=#8YU zz?=T`aZsMO}Rjh7r_{_big7h+>t$3ufisK%V^EcNP#IN z{u2r2{%DVdlo$jaHhp=XR#FI!FagEp)l49vImgParrMHJ6$-t*N$98nS3p+kI)%*M zALHU27-4c|+h>EuUSA`Xr|j#m{!Ttm_U}tJ_xhLKuSfWKsc4AHL@JKT$X6_ypvwIW_^jiI<3EYS@n7enssISZtB<|LfnPDu71 zJia>#++&B9 zEI*<;9a3S0^VPbckxgV1VVo57DTjo`;0Q=qI%-c2S3zEPI7vHRnZNI%d68yXTL5+>I4(I zWyn63`HqN`$;%pzYj=<0$w zb>SQ|Hd$I{Ko+r3N%92xLRCkn)6q1k<#0&RN~0$eUdJ{AIVI~;!-Z1bcQg&pELJS- zas<->wG)1>ZT&rJCyX_U`|B|ipL431M-x*XWK1z!GKiu_kZ|?Zx^E5;yKE|Ng6=}HOihdGW1i(0P)@i)?9(UzS^$6q0 zv`I#_V?lIU7ey=F5rn={grv-xX;28SL-J+i@Y^|91AB_&j$sZh9f{<}?E=gdzvoTM zdwFXFLYok+q=OF;Qyy~H5&(O$bPPH3&;b9;W>);_XRV?YA{R!^y4eK@J}nzh%(Zr? zB#@)Kps>A!-pJTd%pE+ULK-tK9fIhs5+`VwT^Gg|gfsmze24hd2r|^hbLN8ML}**g zRzb5&GDrTpM<1r?K^-?cO?F_^-BKWQv9m&-diL-%-Nw-L6)1$h15TF#U-iJLlO7Mv zoE50hb>m%C>YFcHPXAidOgwfheY8cWdmxZ8D8(GbiKi5b-aGzc1d%lr!D+g6m&Xe@bO=3Yv_sWaPc#NuN^Xlm^ z40RXvn2z}2(W{-amo4d|Xe zZKwFKT*UMNcBJH$ln3ch8omqX5k3XIL~jBf!!K<4QJj4L{e6BjgDt)KJviBeqDwPUM^T4y11+Gr9UIJ2|ID8&xNJLwj4mn#i9) zxgF!KQrm@8c%%RU`a5bM)0C+Vjiw4ZjA2phr!}?}@!zenY(mTAP6vP@g3dTY(9pWJ0eEP9%zyjarKc;_J6FD* zqhlEhV;K|EHhC~6>J~LL(h$S%BOojma9zIJ++96AzFznHxBt4jZfF1gs#W4@RBKyj z0eJ5U(peAbmc#q~b@#rRk^#;8cy)IjGOWkv!;Zb><@2`hUL3r8#oZP8&EwV9+TLx! zGH0vOe7$+~^?9|~xaqgH>BG%mK2@ef=Byz4!?S-nh%Im1lvH(s-}QdeKC|(GsPggh zdAfh!Jb3ScBGJLZGW3(M`KA1FrzrL)s8j*g!~{5i9iw%eAqO3X0dx?M=## z?NT^4IWsl*l)P-rfCM1#L*-n|gD~p=>Nt}xAgXJxfHWd;0c-Q$B#z?D=Z38#mcm%T z((!96`|H#^F}6rz#px7~rjG;VD4Gl4@v+{kVliVEvLV01(wp9Yy?l9pPZEeghOHo3DcUwCVJ0< zpTUkiiD}T2U?khpMTE(E0=&ZnLCejQSvk?^#_%r6o7e=c&C@3kdRtvkPH}ZiOiSiA zL0qw1H;?zr9d-)I5m0u~ug&Yt@jFdGz#5d#Fixh$EH;&Zc=qh<&RyR<<_!)-u)K-@ z*U@%4H+WCgylE=$=0vDbsarae=;*wjW5k|A>I`vdJOOm5oBKtL+mGG-e%tJ9g>N4Z zH;`Fdvu860zN|QA-VPev8GFaKPd7xXqwrR$CC0<* zmS7Y0`BO)}1PPz%PUjbsa$Y-gT2`E9yM99GXqg?4pSBAv0-nBxa4pZaaUpbk1pB{0fK zX$w{-j2kv!JEAwbY?}kyQHuHfV(3@-cUfw6P;r2#!#M|WInO7;^xAI_(A7#UhomLP zx-@-Jr@yF`iLGt|?=`JL$&Gb-=cHe~K_i`Uc(7aQw#o!Pf=%cEAdOJ6#IwOdWDCUd z-J5T}oSsCv;1~e&1V*j5hG;=;N78#WDtv~yFvnfC_4}m2xLlujA88 zPIH$E3g2-3esZ!?%A`S10t$_$SV(GI!UGYh*D#3fvidAhc!O78N?=EMNlO#&#C{Y7 zf~1rL(>iHoX4AD!96+lxO(7coTA@{(ZYxz;Dy)lwzXQt9ef?|0)0-%F1^i{u8Kvw> zQulgEzKIt-oHt3ALGC8BVzrUW{!*BahxX8OmKM-O%*-ebf)0@p%iz3Av6uu*b5de1^uOdzAB!>*sWv#g3L!)!T8AXo~)k^$0fiZsq|KA@DN zzpIbfvCBSMx>3D$SdSQhwU<4Pi>DMkrAf9F$!&^Cs1ySUGwKk>v+m4D>@wNOEH`U zHtH5!impNk;0d~9QJL0QJ<0A@mUle3aq4;Ho#ierEK1DySXkbu?XD&2zjQ%SShFk} z$TMwr*(L-=1hrYW2NUry;V~|HtR1{C;-gS*-@_92gvs@dXMJSw&58o-%i3?-Q3YYv z_U>(r-JHUvdpY9RV-C9RC z$N*KT6l0HoVV?}3B?OQ$Q{3#gyi#xv-9c9M8F#AbMIej>8wO>v(isFHbK^hbas#eT z*d_^tc|A*2c9qULBeyd|Ex}32QC7U#SY3s7@aU4Q4_g5|rMCF3Sk(cYt6Aog>Y>ME zi~JfEYw?Cgpi@UWi3o0Pjt>aUBEiZLL9=U584{QtXknGks-3#p-I#tZBvX!m)lX7O zlF0P1tE`E~kyYPy09plqBuO$8K~(;#9{WhI%k_&mQLMO(te+8)sA~Xg1S1^*tG(C# zAS!hTg*){bM%xsxS}8S!rF5hwIxRK%`S78Z9DiGrCC1zZQeQpWsq!#wBJ7Ua2+mrD z0l@ImnI?E*;wLG$ZjdLl1AZ~?54}nqb3)N@5=X{}=x>v(@znxEm04;~)Tr0MQ!7(3 zyeRMFG|_=|6axPamS7}WEkkPROga+I#av$VZ3iKO-Gf)dWi;+P_EBV;;6x)=_NiXS zoNauEPDm!1wP`5`-spJ0dsx_uz)1#D${DSY56#-TSWHJ|gCJ?d@^K|R1*SV;RAem5 zpmWoAUEh%+#`nxx#IYL<2Bh%8Su8qnfIDK8wC7i>vgq#IX$OaFP@T~N1ZE+}RVnV5 zBQ3Minx!2)vJ}|HwOZ=>!Mume2dy;z-!tbx!W>L^n-a+5kQUYoyQZadPg%{%REfF6 zqB7h!lsgTyszm!ic|yF>w5Q?&Z5hlCR%UCNEhwHSZH&C zoq}o6_M{F@E3SqndPpYax9nf3+2pK*@7)>{H9>mNq>Y5Z|=^EuZ zEMBx|ss0_fpQ~BiT2MD->t$iL>+VAjSxw z_uI<0err8isdPw`uKbe^xiyfL|4f%G1|28Scw0L|HmUJH~eJvC)TW8`45N;y-MXmJ)p zDo|rsY2w%rm6fl){=`13BZk96drvF-x6z$Y4JG`s-6$s_S1uc9OxiQeZzoIM-Y~LD zML9+rZ>J6sg!=7djt7oejAZ z>KgrI)MZ7Lj%5#}N47^5XJW^k9PZ#%l9Q->vU0Tp`959p(h7Qx{E#)=3a7qvFDy9n zZwAkL7jT*0|H#MxO|k#Q#~ImKIR5p=zrLYZnOXmTBlOSke=$N|YHCFsH2u3bhuzli zw#=y>Mn=fBor@TOodc8oUyZ>}@qtq_V23ri6`8!-j%}a0Y&ehku#)E-dzd|jo zA2;{c4>!m6`=aszU1$(4Y?1y)jOXlJ%f58R9)93lX1=dZS615kv%J{7NYlf<=IX55 zw|!fD`zYH!8c9w?a`t@C2j>69-{mL7%pOcNX2uUOEDzbZ- z8+-!ra&6-47{u9SGNGe;4rAZy&#Aoou)cLbfjbD}sy`jkeQRyPncIBt=lzqW_70Km zCvpJceQ5jk&Rm*aa#uKce0Gi-ox4;$F%7m6Q`Z>esOX^{qV?qBePecd=;U|Hc1Y@K zgWll$CfkenP>c*ph|OG?zIThLxb9(ebTkaU z7P0Sev2z!9R~X@`;ik=8Mj!#oB{Vn`lUr-sW?5Zr{+DD>3Nu9zT~S<{Sx2}1G zfH`VIQM9Rp*(0`QYu#kbkd6(esv!-*#MBF;Pi#{nqRGAtx z`Xp?P)aC(}wx@hu127SJu=iZ4LcFyOpd0fd#(20wwoIwsR)u(_7{lpkOK9?vA&FH4 zd0DOvSVs#N&XbS{##JdM zo9ZmLDo(#t%KG|UGT*SZ$8LS&gOH$*Y zv4nyJI^Eq8StHW7&hw*Cp9>T(IrEY?w$^X))Oq5{_=AI#LMD=qljx~0kUvrntE z;v~^(1}nw3VVB%k}G&PmtYy1({Cq&T&g&My&5aQbx&8+6hR6`wpZYg zLw-@x653m2i==Jx11)tuX-pnXmXC+WEoIJrBI&pbs6pYf#W&7e@Y%ffnqcY1Tck3H z+L|_BmA>qo6thc#d_u;!v!6!jDsLbV&~Ds4!EygifUuZ5ft<26UUvM8A6%&s*`qCE zt14c+Aq8T0c}Dl_^lb$9VN=Uz>}Ym({NnZdN#=$_fTpR*+*}4a=ZA)@(RRQ5RMP!PVw_~0 z;2@x)%Zg+nrQBEJlF`I==@^GFFx`y(WMLu2-Pi9^(a2~+!H3jxX{B6W$D%QF;(!#w zf>vTPQ$pMo4l=|i_q?%q;_GrjT54l-Fkqmy#hiv=(VGw$HpM8%x2!*Z3Ve&cyZDF9 zQ5M|N_&Oi->@GN8KDWkfa|bCIbv^Wyt)I^Y)=3d`5+)}%b&)8K8(LC)y?Eh|8Q@`O zovFvg$Yil)hGRD0i>^m#g0=)CaTnDHUA*#vOIT&|5wnQ{c1`J>!-WrLPwB2gNITKx zr3LjQeyfLwCpoAD!h(&%0SV}%`oCtSYXrrS0+%au=XUv%v4$^(dyZp46A_R)7=Lh+ zQ=ri-@7P{$yE5oNx)rqEGd2?Pj=@O`-ZlQBf9gpsXQK!-w%qH8S1`yaw-NRIE0h{! zMIoAm4e!(5wkK-zH>BJ>uEtr!sS6fh+g#Ba6hNXk$(hDjQ;yt%Xn+m8ioyX^%UM7>p=0)W2TujAa0Iebsm)o zjK8@^K9d`zrMw;R3@yN3pvmJp>TDw&iD`p)#*^7fc@;k)&Z2&ACsAYc`W5pyWqVUn zKChnIc{4y^Y=4YY#FQsLXNP|sS*n7tV7V%PD_UHvF%bbr`?lf`IE@x}DjFgfH9r}tlbl?Oyl@1@O*CpeTTzOwS$#q64lyz4O>e}k`H=qJr z9o>IZOaB%?|58hg%>SX5SlIq&3t3i{e?e~kx7KmfT2gUDt%yCdwFvQA{_}sfxBK}0 zi&7JR(IadEzv>^+k4@FAuKjK%Ga~-`Lcv=(Mq0j710N8h!Lvr)a8X%N8;P>{XS&yJ z=UQ)gY^6Ma&bD&Z>MW3OMj^oZwFTw7f%=O z_GZV&>tnOapmW;*PHxbz7S9&$g)ImG5IB?dB`(v=ta0Dk>H8}dcIqyI!c||4bgixP z`}oA!?;$()^)WZ>RF98m*u%p{w;wiJJuG54OOA!F+wb*raWfa>{eAFqFl;qU&o6ZK zbe7J}!Brt!$9OW;_c{0G)#C?cxbubK+IkRc@vgYe=jaxt*Ux75(%qL&=01+$p&re} zcA&abe<(ZRVD^e~v?jM`z13Dr>Q2$5C+Il=dGP+$R;ZOCN%z;6t&jdj3~$Sv3UtL# zuOq-VpY$4VB^o*k>uvOtO?Yy2c6QbubnfcwY4CPdu1x30^qMFHij)-t5S0%aIhm@g zqOrITTlejK^Ze$5yl85y^&n=~-;{3~YSHO`*n}2e{Hf_Loqi4UM-{{$B(Jy@ihsjT z>?H>4W*%|^-63(XCZ?Ad^InXbGQUw7Aoid`y&8ukzsIED=ph0l>mlo>Ln>uO1kGy7i6FpB);_sH0rH_A5z=Xb?YT@RT+7;X)PH_g5VM z6bC33oi%MRjkOjgRDud-!IX@W)~BHDsNiP?#@)faRCl@>lR+I&q!1N$HWJW@!e14( zSdAd^-|QBWu&c(fmJoEbYeTU)N&TN{0-p}7@d;Kv(mTsnG+_o*%qpVNKNT>)Ti|o? zzM(ICzJG}CWxJL$t}9E0TN%*gcI{VsxwyD|J>KGq7424(#X}*Dz{bl=eI|QAUpego zZenn^d&{`op5y__+nOv9kRzI2523i-8eJj8C*Il7s6qANctNM0ja=;RX1EXjl{K-? zJL6=|E09K~U3W%Y2@#Vyyci(2g+FT zD}z129y7@+V3Il3DoE{|U|{`<*`a8cMZHgnZ)@L3Oqg$lkIg33i z=@43Jr`>bUr*By>FOa@$k+Xo7&79Iw!DJ{T1>MDipq(#o5U&?xYc*pMHlHiR_Cq~2 zLpTmb=VXK!7RY`?szjP#bR%rfA+of{|FXw#6;{{_sschL#`{bw<)(8bxAYc#9nD^X z$xigJO6X%GNqZ5*Fqju4{A(%H90Q+$8pxh9WQBRIYyyw505NG#$#)#E6=Ovt(ZmIz znNdy4M5|mc&EJxZ|8TQDM2wf-gpocS?;-|4<)wc5_ac@*rQxnzTPz_6+58XEz>#|) zZw?W+2S?#ukAI%@J9C<-)4h)^PZ}AdAcFu=d}Eikz$a+xR|+;j{B^<t}~ zb*fy^$<9izJ1bsPQV}b!*BozS@$;FYmlVvJMAM#{r7~sD{2c(`fp0uL+<bJJTtRSg*F58%MEoc~RBnfOBC zzj;b^ij)ULMaN~-jCr>>V6TYf*2OU)?ir5b`a-;91dzOSlre?0FY-lHE&RcWb6bQA zxEe;8DvacSEci()2CDlpzt^rA5&E^e_!{g z=Z#r8h>4taG%!K*B8I42AJ0`)2+U)gkk>&Aq6Fm3l_Y5g+QcgJ$U7N^TXW!)HJ^tX zN|l)m;VaDW!!&FswBWtcyRK@$b#+WKdsZ84o|@koXV#(R3DCC7*L~pc}crY>gqpa z9dsnO^)VV7EIF31EyglCa;8@gWepsJadRrqwMK+zv9IW3EMq-I&=T~g2Az8eB z%(Jz-Lv+M`l4~p^u|yQ+aAKAkGYWD%*Pi9e?_!I9s9&8+fEwN!jiH~i9{e{TPJR{f zY(MSb0Kf9=Js2PzN{(1WO0Ch272)m|_&M(ybSMYS)J(?ImHMde2sal4w>IO@b){EQ`YQsWEcl~ivo>5A|q8=sCX!h+t1`NWvmGct{GO(bQAu>am?C-CedZj z)QsAtbYbIV(_3~ctv6@x06~2;v=-Eb?pRPs)tEN~@&IHBHbQ z3yv<8qCANR?bNK*P{M?1MuT*l+44LUgTT32?0|7BmLyR|fy*sQeM2mE&NlA{3{<2| zX0#=Q6y;ez;_sly9YQkAis@4aHzy1jvOBikZ|;FXt96@#PBkVndoP8FSg{?BP!UKv zC0PN{!sAi|p^Ih;`dDr%v6z>Nu9{_}Gv-$wS^2C88R=pxOGuCdKH4?k{i}H66SRL= zH57(k7>Ws?5i@`)JN*dgYr`$jLKWLC6@6FvQ!t@3ige zGNRa^x?4M=)`?>=hc@lSu%i_LJ*gFv0&j%9b)>!1wC8zD+s&5y55Y3`8g*JFrXM!P zVEGk~v*Y=d*m!hHDtf259ev7a#2KN)lxX}UB5|S`qq7Ch_RNCz50BA2o6ZGNCuUbW zQ+rM`PTk>AQNU_$5RThM33)HA<64YP z>6*5r7W-|(d&^NCaY?+p;d*CC#**eYO;F;d#45sIm~)xe36sdwGFUa3J&ij7hE6+o zAjU;T*;4cB&cs}Hu9$s!n%Z<~FOU9^JrtdpI~&?#kGaQt@{SK>X>s+)MqGgBleCBR zg;xSmvCR)D$>MtiU|_suDpb#7T<4_$<7n@@RqCeF7gHtQW}WVEOSLt69I`D5WBTO1 zR~>Y1cdnmp*ZTDYUH(-R!UA&ZA%@ko@?cXg6K|ks*X9d6=uOM@zv=RSOYMKpVzT~+ ztKi30@I!I<=lFkAVX^*;_wc{z@@?(^pR3?ZT4D}GdDXznpuZFMF6dt9kfUo7`|_Fk zhchYUR>YVr<`#t2D8H$af(jKv*u##(U_I;h<0)|R{&-#t3+wgS-1hV9_&XYVy_N&n ziN(IlHRv`n{LRukMswX1O)UB9^w>)mn~tp)cTjr|(N{(5`v%$(fJ#Cv<>5ZP^h zbSd&1z2PuZBTe5AWO9GHrRH*Ua({mqkPOs}z*^aas-wGUGa0ZG!L^;N{DWuy%{Zj9 zGvI{oo}NAMGV|^6^s}zv&O3;Onv3Rk;5>)aBRu;+z>T!RiC*E5fN!jZyOMGY%EyPs#fe507MGhJox0?0`=V+T zp{fJI!|bg;3Sb8GdNG<`9-yONiKTK`EH|=hJ9!tw`_!bl;t(rrB z0a-ZqXm7he^mYHogfiRf?Wt#;)OV}7vA)!%hEYE~#2)dj4a@U2oTB)d>VV|j4tHZ5 z>vDp>#ZhgTHAHZu-8m#WBQXVNvKB=$5;UkY*n!U=V7^*4zo!{egq=ElBOKp{PrY0D zHygqgv@O(V4`3BHc!167S%ff*xkSUEiw*!G4k``XDe##vj+7EE&3_L`$H1YffOE*| z$qI(U_KJx@Y+axN37VKPO|m#}ExwFeC?p+_F8{@n5VyA;fr1>1DC8`*23*1L=9f6O zQuNLbaoe5XgOLXYS3U)wSeSEs)(e6JTo4~GH?Oz1_v`WP-OJNuS_KmE05tC2>iexKSuzC!QFRiz zcz=e{HKi2G#1zZM?uA1lT%C{i$Lr~;G+T*{*Zj)9VMS;{=EBot^ZknGsDFP zLraka9*C}qG*>5nbyG&ESQ@)=oZ4{^Pt9hquf|Yf@P&EWhW|vjZfCNy1>qWr>1Xkk zVj=?Cbt200_ZTgS&N+mu#KiIWgb-nh;DmBo&;|t6Y0`m(9 z*OnIAyxR+p(!6C?b!@e(|Llcp_%TRjj)O}m@XVIN4nxG1VW!wTsItPzmy|Vg_Sp;+ zC5`>!URdysJzOM~w4~iUR?Rq^lcPvku|WiNZ_<kZvQ9BulCiQu$?|sclKIaBj?e zEm^N^qQkG(9U~e1^+*IZM{U=J`+;9BQAIhy zzAC+23uW$jiM?`euTrQ!OvqSJOzlB>r_nGts10x`1X`{03q<>IPR)JCt~g`{bQYoZ zwe1ZS=GZYa5cOZ)$xO_7KUWV1J~zA;sxyyK0o3$+clo5S)!Ei^BPHi5j5;EBLG;6W z9=XB!YmDq?(gCl5IQn%foyOYsU0~kihY_??`6A>>n|nONhVk|Nmah=zxb~gHa1OE> zgyYKhH6u0r!Z^sbd;NN2b|(?fp>dnVw|gcJC1J;R{naFJ@>x;X6nYlLF?gd8g2uvV z%K+bjKaS!Lt@~WO;3xI+Z9ZSMnjTICCz0%XTy5E00BW*?vEXb$?OM;8A5g57sC{NS zqLuwj9AF+d6+-I>sM)136#Ij`%aA@n9kY5SK zS%Z;T>)0z@<-n57Q|3X*8LsJPD_b|@O}(0@;1hFrpdR)zB7d2%pTvP@iOwQ;N?7Y; ze5+cLxK;1@9U`itZui@HPyr@jRt3Bk+!+j+Tnc5*6XN+Z`QJR}2Zr}tuu|%0` zg6;~2%FYCuK~E3a;tNQ1y1$6LXtQXK*MNgz3Sl)GWu}gZ?s|Z(zQU-EhA-kN^|IIC zDbO*(1e3j;GZLJ8AXn_XmRr&~mYe8v>H~jK%+yYkeSK~-x0T?#(A*?7J@00ifp2{S zI!ot&i9NG5Oym#D-U7~V8__dhBxEPIz7adTNB|+S!i5Q-IWLwwc@mN*%;OL8h<3%= z%z7IcC1QlYpeU2#ivBrQvxHY4t^Kru_uRrx_wR5gW#sL1}YxE z15nT=m?ky`yzn)%Dcwd8HsjL~oyv&yR%09}loNq~t{nmy>D zx3ee>^1I2u3U#MM1-DFWsECW3pu)lIJ#=)rh1|f19G7t%_#2>zK4u-TtCgqEfhnfh zxDN3ZgYPAR0y@P^f9Uyn1+NC+MFKXmfHQLcMn(wDa3a)H2!tY5jwA2`@x%!{KW%5kA8Y72p~IK z;9!8@N%8m=DO zag!!~BpLl!kZsoi?w*z+RmDs~Z_05I;hJO6^(q3FCCZayt^2xTkcs>NLs>aQV0O~- zMGQVimL$>1RzjW#0o$>@tM994dCtmNS*;o^hd?TK?6t%T)7Q zW6Y@)@lRM6aI7WNI@aFAZD7d!D$9gFnnAgo0?QJ<$1f+YezH?~w@%B8)E@Ac)`dp_ zDZjY_L8%&gu=cQu5%H3qkSe%^*hqrLhbiMK!MMNAUe5ecrH#wW5^yOG8TbU&2&zgICyO3&sZ%|446=$sM|vqvY1G$cQXj zwpIPqNqCDK{H7>fWy+_UOYU_O4qI3?;xp@Iq%J9C@gWNEIrD;> z5DQNX6T=eLP;$;~9!z90MuigMJ)^AAU6v8`X<6TIJJ{Uf~mjHqa$U+zWcS{1G%D=#Z;Q$9)@O z=l0hx-Vwf9JiKQ)_-x_7*=L#k);T+NJK3GE;nRy@)n`Kj{_=v*L|}bXA6`Hqv(JJD zcFz~#y{_&qNI?MfuOA?FUT??yKc3z~wS&8-|9l*4ErGN981JG( z{mzRA`Bj__m8&k|UJ1b>PH(eo`}N^PwY357_tzIVQ_sF56Ap~({kXr$rtt9kbXu=q z?sN6!Tfl|S+w;?GK}FSOwMD2fsA8h>t+kr_W(jSkQ>+>F5Uyol&97s-a4YamZiWjZ z=T+)l_vTLV>9v!)Gxr0!`m@iM#LKPr`V;ayfE60Q0nDvjKFt0+l#djr%YX0&k>s%~ z9tTzSCe7e9SY2fH%jcziS!TrRET~1cO39WSJZ({>btD zdN+Qit}WaE+k3yX^p>ZmRGz8YHhRC$)I`oZFsQa-aP?6}FK@N{SKbP03AP!sQ)4r$ zy%hJ!Qv&YER_PA1XJ8@;%6as26%sHa*IR-DHNP%Tq zdHqAk5W5g=6Rw{rw=jeT8RcDCFb5+G=*XWhvTWN4`O#U$Epmq(UMMaL+OCjV2PmVH z5?0t7V*+QQ*?t0$bRk5ollGzU7z4+6QfK7k>;~Yl+WgqsoPKnvc%>tRwk$>)S6=}< zRE_pLJN*&z1+6VetR#iMcb(X_qScWcM-pipU~3&RcTN>lPN<1-oT85C zF4rg;d(45t^5f5|7zLDhh12$IE-K`bf05}g(Eut(tI#eC-=+bAJy8xhS0cJTsy4oy zrgMwf3olFS(*H(d?vM$P%n(Zo!BrYLyT)vv8hpv>JLvjN{mpz^;{`Cwn7|nLu3XOq zC{7rEMO<~PTD%g=l?d#46L0b5oAsJZj5l6fis6V0o_lof5D!69X0QIDgVpbfa| z*xr7M*JIzDd%;amYhiyerWH4_$;BZkl}Z4qBcl4$u={I%OhsGdm3W=5NJ#fOvm#g+ z^Q97L?RO(;VQ~IAOtx3GDmf**z7#x#j0iU8b~7j7TEk#%6bMxk4GKV>3uqwPFQPi_ zcpx7XDew@ILBuF<1e!0#JdxFwy0ysRRXF7&uT?lv2`a7?xc$^6xY1Miv|V05p_FfD zk8W@8?vFPn-P~TJ<@JWqb@`Wugx2OJ#Utg@WI}_isdd==sh!@WMVQ;n84+z?x+v2q zbZg3FdT7DMFO4IqNt2#q!2;ZILNrgfeZ`XdF#N6$g%aVOoLbV}o*g(ziK=_B<2P|D zccxVsyEB9tM;%3m6ypXU_a{tMx49;hkK(^&>BgQZAgP0>5gZUlV&q!BB=JNj(D90#{6;3VJAqkWQO4Q_H>mn ztP#Wfm}n9h;*$m&-1U}GXAKxu#Fb?wH)=9Nra_4ly9}-g4c4K&<5P4998F%N8w6J9 zvI5s4GhJq>>)=}SPB<-Ci7i3kbqi%=Z{DW4xD#U*dAX5NZg6m#bA(6jr3#GNo1S_K zG5cB=$t*8+){o>5xHtFVXVYGCd%UIptjkAzDPr)et0|5FXFu3oDkD{m#i$DzX^=3 zDyI~A^gRO@i2;$Ky4K_D52#JB%2Kc|V@VV2CQ@{q=FDJG{jIz%{BTX*OiyY@;9p-b zFx>Q) zepAvE#j8~%4g=-{6x(iL#KIRBDM5M54jL2m?mw&%J}_OZoAD97JRZHLiG zd!L}g@wt#n7!B&OH6CO;k>#F708>2}>Vb5f*(7T@Js-_nDTQ^{d5`l3x`N;QS4rr` zZ#WIAK2jQ5g&Y_Bh1v4(ao9@{-c_|%?W67aF2xLXxN_puZIY3@R@>`D-hX#3*5#A%)N#J+0I#wkbP@ zFxx6Hn5cO&7TNzkGcCq$lNaj}A1^mTfimu@eQ-5Hs+q+8O{vRx4!8A1v4cJBKLg$JEkJh z0FDElWYl<;AiY=!+=p8rek<#_DwH~zFpJ}96j3zI(^{}jOMCIvvBXx;_ASxFcAbxi zTiEq;u!Ju+Jqg+V()DxF@WLj-W0F*)Bc%mh5~07}`!|RtE6;*Z+znsY<^jdNrwEj| z%CgbGZ;)8S+pp7@v#3t`FMskwBk0E5q`K5bwgMSp0in*HWZP` zLvZrt@O_fp__(T9E!NB11BxIYUME|0xMZ|C!5bw#CfsH-Q!hSD1~klmwly!acz8Qo zUmx`xUA(rwR_cSABkw;1NFi_;wzluwTQ)xLb#5C{?~Av){hM1lcCUxKkN45*=eMW( z%=&*kJdbF1vk#lo`=*Ny-V)$lsMFIrPCiKgz#pgMw0`Y11<`C~#@V_Jw_Y_3!duM5 zby9s`Z+}wl+pU)l8P{yUzn4kAfIHzeaZFV?avyc`Tpd7f(cobB(dqovs&iu#AQ7vA zMAbcyZAgQif4?&^Tjv?ba++E254(9+I&SrWRGQ~-*ovTe+-KyaR(D+E4R_OVp6d8O znv&^n6p5H$1H7EzKAO*qi-+ldI%#NgLdSLUsklma*_XVAJiY1A^J2DIEvX({C$)OH zlVhu8U7SAIJbny3el&b|l%x_G1#i8T0t6qCqu_J@Q~iKt-fP#Wuy)CcApe*9)8+m3 z^8I~}R?k_@twCkt??KV*v;kVX%(|>AF$J1-%y{xxh~tTk4ZEZem_?& zruoi3A=WmP(Yr%oldTE6&#t$hZP2mfP_Qu7G8mpV>&R3qf#ejxuiM^kXpV!OXBWo4E0u?~?Lp&V;$mUHYfV3RI);(Yx*Oti) z0`2{G&QsqHsd&smBOUg*MILc!=^ga(_+1I2GdP19rAo(D6|Pj7ss?+(fZV8l+M#Sf zM!x)DG<-uNyqT)x?#H+o`iSVR{RA3kSto%Tw#jWBs* zqe;6_1IK~d5T6Bi_;3Zn2zGW!0k(Xb)v0mFwUZ3l(y;t~=ied#MZIQb@Hk=-KZw7# z8&=8{obld?5SarttluX9>zVoI8&v<5TM3B7Ryavq2t8L#nI(H9 zOsJ0tn&<+0nj(P*j=VcyJiO1pP%1?NY4Z(-&g^TWPrw;&CD{aMrBVOQKl+*sS zkX3_X*Ew|l^$1i7ED=E)7;Jf{TGfFR$>;`owoM>6Wn|(hih@Am6&edj=W*a=W|NR* z=}B+y4|+RgJdEuXG#8i-uCe00ge@#jB3u$(Wp`Y_zE49kw zz+chb__}P92=&EUoVh|N!{qv8N1-v@$A#9`uw&NExR@1GvCN)DpXL-$vryKe2Fw;w zzAs#@W6}~;yc-`?8CS)#8KLMDGE^Bzk!lclDFgji$z4X0Fcnu>Osef^bI#M*`YWLg zXIZ+Eg(U(dxBtX(3@duF3lB9|=ivDm8S0n><s{6QbO70G`d+1NH{>spHwsOP2PB@|BuQRwii;zcw$|VjhjVq~DL-HW0H!$G zNy;60^TiQHB4_m!i0BXO%-olcr~stDB3smgZ=;($OnFJvi-2VV=~e^(6bZYs*7$%& z^a4dLhfW0#in!2;pNZ%avYDI2KzBgzY6 zNLZ#g(krDBPV^%IGTk&e6CyRTTN#CJQ&cyutUwQ*k+NLOu+a1Dx37&y9+yZl29J7B zy%YLS!xQ}-Ggvpi@&-#lfWe%aVsiTnB!eQ5Cl=9BX+t|N%!`01H5RHUl4p-GV?Tt; zwLHJVi~lE6;}K9{Jo{yB`Pse87gNSc{s=gm3kmQpcqC@;3g*s-l_ovy5rg4z{}G4* z0*TT_5DKsavL`)WVs5=*GE`ta%VH(rZQ>vwM_Q=Q@GK49ta}1Qq%dB_$%HcUZ!%Y^ z8QdJb%@%C?vJz{g-3lhs3Z!)9o$Vo@pg)HW}i4#c)4^Td~QZfR2)o z4p}~}>xLw*PPU^8Z-o z=V1D$g6V&o$-36jvZ7`~`sq^sYiu*{-C3f#ho6bQCjsOS0{xM}dRX15;PZYTE>I^Q zU$vB;X^Aq)I+-noC6L}#eQEKbs*ldwW4W@L{p;>@*kUW+_v^=d?>G0>59{e+SIDQV zQu8ScEf15E-8ty{yH$?uC&=7AojANz!}IxkzMg;cmA#G1_4<7rUZy#G+)aMU!H2!M zcX>ge|NeS^K0dkmTcV`Ib)84$y*EN#c)0$sr?!xwg^WFGtd)63xG{_n}^Lr>opbofgFlL}r(KD_+9E%Wn5wM}EQ zjQJ+k*)~_Rqn;J;lSt*NUvn`9fRaTQ4Zvo!zdrz|bTAe5fDE*Ic$_6W?{`>7PArpX z=EScj`=&=@Kf~Bo&#d1zZyvZF%qfiSor`#2ARr*-`fG&*Yloay?$7iN&-CM02FNiq zCh1YPPUOPNDXi{?@<5%?5Gar^4ox5q+TfdIvM>(L4hQCm5r<#w$CzYk4&2y6m_W^D zuxt()zK3XxkVDGAJ7L|o@&5c4eRZO8bt5j}_f-r-8bU^hn?Z+=QlP5icYj2$%ljLH zWtd8WiGuLn%&ka{{D6Q=07{h$0xkAJFYhp84B7x8u9XZZLW=46UglV((&aa~2ThS} z!4@>nu%#YHRgT)030lu$OE50EaznVQI>K}tk=(M$q*6F2#`aF>SF>}zs{Ul?ZQ17q zGhWG6wm~CFVstrs9~YN!uAn8yLqk9$gKY_gFAUZI$E*(eBMezG3q8RYpw-2to^O}B zaLl}h#c2Uf<%|`?q}rQ-fItDol9PX4a*iN1EVluef@P5hZh$%=89Ye7dvzc8EOehH;nePVnpJgK0cTR|?u0|Mf<)s2EuFRF4fJ}%c^(m0~WQfZX< zDZE-82d#xP#<`$B2cTZrasP#%ZFu#re~h;)g$$2yt_$v@z`ujq%VKLYG?6wj6xb*H z6VPq1-OgB+RP!R3WE3nz4&H2`f$Id63hh(Y?fJClN5gaAa!bg2aJu=O3JKALYOfFt z9UFi<2O(J$)^=wuU#U&VGnEMR%Hq_FN>*x>#b{nouL->H+rcj3wYw{QyA1FPoG^i;* zFl7P;L;#eFInC5wX&#w%;u}=Egv*T{3WMo&E)E356{I%1STZ$oT!nBBelqnGNy-qi zlHGDvXARYvFUORR<=x2Lxf6y&h2!^1xQmrv2|9v1u>OHY5+yn&w}o&%WD4|?Suw&e zT$HXV{NP*Xiw`P-I0&tx1PL_Gdo0k+8Z@#bnq|IJfxpXxMYqnYXGC;y#m1ga0Fom& zU;rBcZKMPP5^RpJC;pK5)M%!W%WDiNZ}i*hmaBkg{aY>){JWAq5&B}O1H@!;SO=Yk{w~f;?tyC-wthCF z-JIs2VUE+4)oF)u2JM-B3&N`}$y2Dq72X8i7%w`>-Z1n=`yF{pG)_3AFg0YgM$Dzl z{Odf+kENyZ^_%qu8Evk2k~RSpW;-4`9B5?CcP#_8gGZ3duGHh@8iVSrL_KB``ySb% zT}F^Jbhf$h>(Dd2)J@y9q2@#|7VoY1u91{cbuxi~x-HffdazG&jTiwXN|dVr%ct)n znA|>n!*7rIC@k()K_x8{eMC3o`6K|}k=FUhl!|Yt` zw2JZPG4By(qHq+Z5<2~D`e%$|L23cJ33tbFYMxmcliPepSyJm|eYs~9*5OX#qnDRM z7)rdP#*%L$eaC_1Z_#VAGjF%;xq^X2M(tm6_?^M+tnTp7CxCzfjQ-Fzl0<_-lVGhk z>(HYtj4^MDitI#DxECvym*z(D12-Um0WSbs5fsUup<=nPEG?AxQtcpJf#DFmcSIXx2|kr~}-wvB3 zzrLG(U+m%qr!U)Fb)4DUztu~~aBL7}FhFiRV^op?p8Hk-}27MV<{mWP6r>Und~xn3rU`u#o&ZDEJQW&S;8Mh-P=-wpWXS zxsz)j+3N$3ot|S~sbT`Dl)l@)p1rwbx9}lV3dO-ivEX*5C-0kZ=5_h*?rk*4H7jpn z%&kun2*?gf+g@?76my`W1_7-$BjKcn<}uS>xBmifgF8} zeY9BabclaoS`qwu$e**u1L)#RI~{n+kv7K7q%mkql+<`c08)FwdvwQz+ z+|b6_ke-m>AoD)M8lU0T`%*fi?$uZxGn2FRYw7ZF5lTBFK}Nm`=HkfK@c3-|=t`c< ztqjCJFEiFsk!h%>uTqkhAamh%U=HnxV$EAso#H~_sdN7P`vPRXpzexQ z^y4}^#++|AHsjc)uDP75C??*Es@KkDlv1g`a_K=VZ3`23p$EjWT*I!R)mP}bhg)8YL)n>hx^($jV69nkD-t$MP^ zf%V5F&Lmx@do5p7CRZUd9LF-v^hpoi4{+IRxcI-N6aUT-{z)g8=vnChzga*wdiwua z&G&Qg|ApOfts@h4#E#Vc(>io*Q?Qd}{SP>C=-XHzM*1LsKW-`yH5b$5je&kgW|_WlnEx6x6l2D!?fM6he^Kgr_J;8u+y;b>_195 z#~mChk|s9QvM!h3-+vwkcfUN}p7s}dFCW}`zaAg({N~#^bNRmSKDH+*ZaYfT_WykC zrup%4ck?WD_z6}!t7h%^a{Bnv$WrokfBt!K`r1N0{is(mGwG1~jO+NG?|t3ySvWmd z&aqTB@pB+kQTp|5sr$?CxcGf^^94SwH4M+^cT27jWjLeBia&|M#EvBYUS_~$*_v=g zAmQ?_Bn)8DaNi5NN){I_98!O8GQgu0p53EjAF)}`j&Bme6Bk(Cx3ddG1a|CjeOmfh z0K>>}M3Z=;if10|gw|)blo#oF^uaYM@7a9^?=ZjH!p@k^gW>O;UZ%LbE|Epz?`@R#N;wyiGP?6UpWckX-d-0|P@#&|hK zX6_w3BX{J86_IPMSTX0gdNjnd|bQ>mnMc72Q2o(y!PX8|zk zN~S4T7_#qxu1L-J-wO%#7);Uf8syGg%O?ZB5qupk5D*Cvw9Paa2=ENE=!8xN;=p82 z9;_I)_qc)I^^)oWC^rKr9QZfb8v7E(_tGpe^6z|`7w=^+Fuvo$j0#%D<3I$5UX8@b z!$K#9FlfVmk9{^SVa%$X6^j<6T;o@1Pk5Olm^YMK-Q6L>lwLP3#&DN7sOlB4Qo(`epy8@T`Z%k~Mc&Gg%G zIy<_2H|eoBOkwm*ZarX#yZsgn%oa|0!w^7@O03Nf1)C00zjWwx*+1>+9yrYycyu&- zumAp_yt~DjKCV)rTarrRo;$Arj(Y5MkE${b$>d$k)CF7azMZ}FIe8HgR=Z_F$gp(z z$cj0oV^x2|=&KXu+c@6P-&-WIs2xKTz?oDWx0SThsijxd(zPibhKMp z*I&$cLtShhzsju}An3(ly4ZmIV}YoR6{wKvyW2iFeh-1ggAn0h$HJgDNDKgN>l`k} zik6L> z+>Uv=($6dkovuX4)o>DFvDM$4IU%mmHK;fK@c3GBli%j+kf%X#cikGgTGxdUC$~>k+$id<#?9zz*!ksHQ9v>GsjM1=M{xd4nG!nsJhiXg;c zwYZ$wDA^^H49?27szsZSNRiQgg0%8=(c{`gEeKsv3xv!V6IG!0!}S7Ot|DT~-HV-u zj_vJHGx{2exgNm&JV0nerL$>rzg`{x2t(yZ7>27M_F1;GZPb>-%dD* zUxc1?c|bt$1gt?>xjt7tl&(5GyX*d31)P|-9wB{nyo?@53QaIIDpD(x{9L4jvV4}k z1`(|S8ZQHSA56h|XX{$iHMy;m890PgIw1JF|G`S3h7YJHxzi@)Ll4UWj>j{|>pQ9K z(+kh!nW-mm6+oeE?xy^TB=Yvaz%GH5agAU|PZ<8pIso=8 z4;<)JHDO(qz z9nm7uQRoqpXoz@hca0j;dHV{Mnnw~>6LpH+pHD{Ro5%4s1mIxJg5WIW9Do>6j9laH ze26(5_ZZPAQV96D8FAs~6B||Q>l>2V}|v z5&QbW(S{|VQ}fc=LSMh zjN}-=7Av<)=Q#33n2D^G9Ric7x2`PN%UF@fUQ{|IaY+I$S(uf~&N>LQq=F2UG2ecW zue!2gXO^e@vz`of^bI~o*}j^QW z6AMNX(Kp@6N@dfK&sv!yk5TYtT5O{86h8>4%27HoKr_Hj|3DU$8ZlClx_|UQG}Xwk zIIJqtIuylA7Dj=AFb+HJCdxuhzR)2Y5ZpwAX5f?2V8{J?9)z^V%*rS4e+xhD%*<55ZfQCU;5P zNoWic`S0;D+**law`L@yFbHOO1$da-J#~oO#Ic71WK$sBd`gbK;sSKLgAX-uuQ${) zfZ$FAFmt!UA&ldVeK<*__Kh^}Oo59p>t#Fb{guS)4m|JPHZqbtoMo>w&1kn#y1y<~ zwX=G&VVmo)8ibN>Qd2)!LGYb0-pt}Zz82PGS~Ivtt@e{avhVH4pBy$tI{yCvTK*Rf z`4?zmVq^YqpoRT^X7h3|vj4wi^Iqs|NA7&AwZyi@b=&*(dc9}+L>mv`%E#^b z(jm3w;$z?5?dj_7%Jpf+HTkkTPm!CY;~25SRnD~JcJprJ2{QS3L>2G0M3rDtse|Lv zWUKKFLB?u0wR!uzF3`5-Qe|}j`O){pmHItzPmLTRiGWcy2jEnrJkCGcD^<+ zA$oJ|Sinr+ZX5~K6l!U0(y-_GEzq{zv;1N2vP!mM;4xWjb-BzJ@o3@x=yoA9b^J$^ zsKtAQvMcv@6c34UfjwzN5c8OKn9p^M_=IVkeAuW|sRGjMX7(~~8>^j21+ZQC_5r(0 z=knHpZ#n1MN6-{}mwaI#S03#`F}_(XTI8(9T~m+J1GSi=EV%RFXOKrW8l7HC;@bDS z87{HI$H37FiXudumEVgpQd+CiMeBMF1Vj%APKRb3mM|&o9>iq@R=MUBT>lG9P2LpU zZWkT5-LKcAx3(!S3#dOtoFcb>UP+@n4FgklKfpu2P?>MJb|;Mrbd;dW6g($u2PzZg zouAJ~ zVYJFsj!!KGJcnDYu}v;d01+JNITd*&+7rQppse9 zP>(Nc%eRNwp;kd;;dkA)61-3X5p6$7Ot0|je2v|ldky0i-5VdR_{gXfP3EA!+Ta$v zoiLafQv$(cmqopIa(4SFRKH%aDh9pA-wMK~4Ej(gciNqKNj+<|8gPZ~HqjfOsCdhm z_^VLwm&^HGNHT9E#<#waz*QSwPe~dc<@w{X4?zAj_6hk_{!ha#<%_*X7;@$M93m=Z z4t}eJXtn<6^K&o9QKaZ6_4r;!^z>POu;6Sp8122SoX$~=$Y%KjHS87qpwt@ z5z3%-*PK1=NUl|YMtBrUpj2^hyvUs+N+SxGaFwfp>M#%*87P!T4s{#a18qkPHDz}e zMe!rJ|BN`drc`EngPYT)J+6tUv zA8Q6ZKuL)9&lH_}j^Q&H<~ewDv1?T^(snn8!7;3~j8qA(iU~0cFrSKbwxa4jRTsP?AJXpjw<`|XBvo%c zXFA|ucwCNNJ#qr4TAKqCsAlsAT^+(WUBPeEgnmF`uG;VR&U)#uLVS+Sh>`fRNYNkS!Z4BNQO5IJuu|6Iq~DDio;Zr8ag$XF1`c^8hyy;aEW2k zkSOU>IVbIKsm=wfhC{p62qw-8spF^WxoQcvkA-{A4X7S=0 zA89LD{F}~}o@51gsC(m+%m4OBUgR^_ zh4GW{{o%60Wa z!>br7mVO!m4Ghiib)+a3#+2#x3>1-*<5M9XV23cuk4k+~^R!$XGr(4nYXJ}xg!U#o zm{Q5U`n=%}U_ojAQ6zWS`bpEZSTMF6+jXrVq@2;PE~cP^+I?sBK#%o{tc_IdR@!=} zyGwWjRyNX*$_X0IwX^zIG?gVF2}rrt_tLR$0jWj2OuL{%I79 z0q`K93S^-gW_Bkt-2{?7yizgu$rbe+o8N&yo2-oxB^pvIjQwB{DoqsL4$@ym98Cd9 z`bu@I!uvpF;W`&hQLvlky6zR#dAm5(#LjG=0F^Ch{6J)(f7v5 zwl0?M7&X(*isTta-Vk4sX^w{62}WK-W?f>s3B?YO>>qAF|7?oW8&-C!S@Kdk(ku{= z8W`MYiC~gO7^xsnLR5sx?=+sOGkQ1N0P);UkGc}&-}P2LIl|qWhH0s5X`>C~D$x@; zVTz$D@X>H9Gt0y{XBHvj2I2ktSCuYFJvE6wD+(ZF1-X;B>vhCLX|8vpO*tq`#d75-L;C(0pG9}KjuG`bBA zV1EqIn9ii2U?%c;Dpmt%DWfL3ETz#+Bb8JT?({tL^x+j8Cr7hLNgUy0Un|x%`i2`D(`nd9Gjp#M%6`Y!x`(HiMe zn<)QQC)<2dbHh97_RvJf6cVsb^Vfi5HAXf?W=@Zj>DPt`dG}UUq2{wbPY^+&e_nGe z({?#qplWIz-F-Vfe0{xmVi5ScwQ2Qk&FMxkM8e1`Hb}bN`;D7b@>SE5?elK%yx%BX zk-DCf^ObR&iumLjD->k!S@`?r>Y?S&g}Wm)x2s|M$?l80o5lm-(DnQ6pUTTV*@o5# zPtFMqnBBmPU>HYq_N)h5Z72t^M;cE{kIrQSO8agX`f+>YHL?{aGe=0=JB zDqM$iDAi}{1Qy6&FgnuLC=PM3>tqrtEerw;XGaWyNpG4US@LXDC3x z74h$$)U-t`Ce*Uf_>HJ4%&1xe3qMl>6o42=2Fm1nTLGcwV{gEL72Q*QMr|p8^oYpO zrDtmt5%y#X^qOGOh;21Ix$Ji$mWU)vl`7OS+jily$~8Iz>SZkI#tcaYnP4LIP}P@K z24|A&AhEiB)$d@%BY%~2j_TTlxym5lgCt*7zO_Jk@l;hbocV$Kw)x04`=N(Yn2iAZ zF2kb$>K;eqH#cOHyp1L)sQJWQJblq|z{kU^&rdK%9mG#E`vKMcd9<gIv$x%uvVdzp;qtdopPrv zObO=So7>|@KC3+&NCY@J^v#Y*CPLg)-Mg@@DZp`0n8-{ZY@BY4pl6KbF4?fMTyJ*JV;}E6Pj8<-SMVIU&D7$tszm5R?Cu9C@m_@pME95g z&aK@`^>GIR9S9$2BL0c8(Z^5U4`L9aUEI-?tXhZH*BP*&L3Q;K9zOqHd`~6P>KOf+ zj&B(UT^jKwh?TO=(llcG$1`H5Li;^lZ|FG!*FfVP{_+XGvqBL= zINU{mVT`A0aT&r+zMh8S0;R%k!SVou&Z-qveYWEI5Mc)}bnv?sX9$X63N&?_88y7h_5UPXvF5gDYaHbyi)!_|6I{*n7JC+&_V0ylu1f||_D~X|Bt!@ByyBZ$;V{<8_ged- zq+zFN1CkYjn&2NL;#T6Im}2yW{lPNg(_uPH(n6^ANN&cEXpI@kFY42`lN zvxqnp$FK|;%z0?TzGrwN@81^ z%FGKjL6!!q0q;S}&aiuPCSSm%ut=O9r6#PFvbyyj6x!MZ=~4CrX8c52>!C_ImH}Q> z)F^q6W|j_HS$FS0Gnr;3nTbV%I#@~=AJ4&(rpA3q2d8>PgR)B(T1xe;k8r7(90kp} zao+^Kt|KT`j#(zE`fr`N1(K5Y!|9s3@4F|cI>M~zm`69DBrG>)6y!2l+Bad@lfa@y zP!4>vftq2ng1lycyu75eBt=GSlxT_js^7s|E8(cF((1;n%A+HT zGPq%NfpHWyC6b7nkPEq)_okM7L+iZ|oy7@9BVza8qfl(PYCp!r$WS+VJPfb5ASs3% zh_#F)Wg^$wxW4@;v-tds{5{+bGc1kDL(Eo(Y^zX>957fGEUM8dAfe*DJkXqY3mST) zs9gi%F^#nRe&*ud4C8(hMhbRD?mr}vOSS|1mS!6+^Q`N-845{F4jymS1oy8pt^VQD zCy%sis_|BOZ9Ro^Dy{Y-I8vz(D(9lYPOpNxk4Y|f6w8NXqD7G*^BGHLDvmB zd~pa|1`!MJaCh2Nj*TR9N}=??`$qJ$yoRc9CliOM^q1KO50CWl@cGB;(#pYMPIrfW z+gCTsLjeEoG@A)z8XHD;Tc@p$S7nUJiic3?@ji-J%6p#o$I3w%vUXQ5CXudeMaiRk z*M7liq~YZ!%DKzh#ocL?cLju9@p6r&s~B95Uz%;7!pg6YlQLyJubxuK7qC8_;Sb5do202x zKNd0iHz6#0)O5mCTYXH?V)3|9;NC&H_U<+yvJky-X9#zBWXjHy6 z06HqU9xFZV%0k-b$q~ylOh|3SI&U%`^GIQw+*m)mz*-dL$M|p1O_ZYnq#r%j%qHkV zbrwl7;)Yd8QR%3rkJGZHi=T1Zy zS_3RLILA#{Lo%!D#4U%oepa6Hi=>M{T!N32Ck=MVVp$FaTUPuz=*frsIJA|A(DQ;b zxgI*UsD#u5_mT+J% zWKbRC9KAnA+t>8-b32lh4-Geo$h1!k%T&=Vt{$rhrtk`G68jnDQQlPOi6cpdQqnE4 z9?Mu3WWdXxm!eZHUBqLT*W|}_4%N@AAeyjuE$}bTd`X2u+WZ^Cs$ue#9Fkx1A(GZw(}zjux?b_-nfv4?*GvvQV{#SP1p0g*w5Tgm=+8sDPO@ zyLO#XT&7!9&M)Q-%3M33Rhc02fNY2uc_g`tWfkC6$lfMcTzKJ~c^Kqs(C`NE zdjB>um+MVsNC|&!JQ$Y>%5t3mp;{W-9x;C=i^Q%0)jV|GQT0LplWVav&T}!R79imNbQt7jtN6JA#TW8F021y zpJI>)QnhabEge~tLTD6;vfk1-LujO3YG}F`&t#4OT;6S{DS~R?x9jt>tkeLw3M(@Y z>$X=q0N9-oF5yG}HX@~S&pn!yz`pHwXzt*E97WqB zJSay%UxawoFGUmvZRi1G4WWA6(Y*H*QW{$s$MIQGlQIXEwzE&81XeK-tJ`FMB zS|f2!G!-ceMx4W?&sW=w?T*f!ZjBC{OM7tcLO(d7QPLn&wNW#g6$?L|mi2G5+1#DN zmMN9_#RbYgWbp(Z()IS?+S0c3x^us?r*D5@rWI>brt9sj-&JL`AG0_T$gi%(@2pqL z^qjbpvcvjZ2}KX13N{hgWG@OW8`2dTNF$j4X!6x?mPe!W)}g!B@SOg`_@VOE`Kf!$ zOqRFe^LB9J3}>+>`*rcNOYC*){3G|tc*Z?ka3->gK>Y2~!|ixi;FqfeAHgGj(xY?F zZNkFSWFJRSw-qF!8QbQIj?X8=P{`NnI=_4}{O=r}CHK0k``{LChHj_##1`(@-)=Qd zZ*c+EqcC4dJ{&VG;fMr%f zUR=|9Ii8*kP0uiVd`KP(^mISBOxG|Vx;(zZn$6tvZa27o834>?aGy=(o=$)3)cozS zKiatJ=5*Ox5mNT|Dg}2TL~Dx!<=RCS3lPJE##fg_a?cu{i&?ET5^=w722KtXh2_Ph z>J{Dwtx%m+?uCi36xCe-LoA$kt+x$!0Ud2Le>@W!sM2r(c8~u;k36(rf*%oz^sy=G zjeoWawBr$<1~89~W#BQWXH>2>Z8tLn&m>to$N+8fsvid8p_oujZIp{-%JtlJ4^3aN z+a^!Fq?l`K@-t73f$Q z!ajPiLz~eB`j5cpUs&c}Yn;rC9RD5ou>6~`NZH-qghABK)>+uZ$;i>d-r3IaU#GGL zHYN-Jp?@AU@&?uh&K9D9VHj>3FHum8=x^j-Ep<+Z-anHavSIGcR`j_+oh z2%8w$8Jqlfz@`(|VLM2OB;wg^I4)BM;(6g>q!GccF(!f^k%-vK_$$s(5EJ9`?Rte& z5oQ)^steThq{#QA>GpmRizzo$!s67PE%OHy$+To*K3G{$kIE@JY)cTtRc#G^s;*qI z4YW6G*J1}A!qa?|o7hQY;z?5W?zICJfX{l#XE$ICltc#-rG)V~eBG>@P^Q|5;iuNi z2PUm=z|$siQW^cNT2OqeCjG1fABhFld2W|3Pk`p|SC=E^Rb4si~PT6`vNiMZWX@yPPNhmWHFN)~MRJc3dZCcxrdr-6;Vef|aF zsoK-zKU>N_ie&#-%6BO5|5yZrl8d49KjxwA=wkA(Qh<_3=c z^tZIZ|2pP?VNkO$b~bm?VkG2bVIgE^VkBhy&JF(O{a;UZcEW#1v~>QfyZ>#6|MdAk zmZ}KDplqjNYw=G%{!{j^4T=9hJ7E8J`~M54|MS6rmh%78&c*)&<(ZhjS-t+dX>5A9 zdnt=7nJ;#=q*itnb~HFAbju_HnaY_FS+|j%k~0Bw2MKZjC&;${#7=Zx-vl<81w-zu z4fCUh~JR7-sfkAQ=?q2CeYG z=jwjP25is=p^B-w)SUCaZt87`QFcQRO&#m#wv336T=^6OFWNP2{OpNu^dIe=|{54KwOdZ6ueSUd!y9ECWlLslnlXnL( z7-PQ|3ee&uvIHt*w_}y=O9bGu3_5_-NBHI*-UCibWEyZ}ZUbQbz`@9gD^;{=l;G|n z;v!?4O3RM7*qchEO>C$y$^Eh;San}@cP^O>xi5`=&n{b|m(7I6uf$#2-6h;(;`=!(__tj|f@Q?B{5f1b2` zi^cvBbO7a!!WZ>aIFEHIcJt|7i@+DQ+xN+R!qyCzGeB?3a}o5!bmM6yy2O}+xCb{9 z>FH%UG2;wf9k~xc75Mxs%8+Y&68lq*;3&tOqXF!Jd8VG-nINk++Cm@X1OuuL^aN`o zKot=5B8UJczbw;Ozp6Ed4z?!d{X@;zbsH1lP_R5pKD&V5NbqYrMst=}9a|8A6>bA* ztuJGTb>t+3sL{r{=3RunH_JGQf=^_x7@W?5-pg;2%YEH$d(xID`%?#oT+J=}tOkA$~g8mh;Go+FY}FV$fZv!wb8kj`&EI6yGJFkzo26@;Xw@|Lv{6GsT47 z>~61EBswM-_K0I)TT_i^#&=&gcrWcs5c5_j4 zimb*4a(8sB@xTP1K|38Wkm)fZBRH}W=E$I>4|;}(y882|CmXsCCWDnTNN2(D-X4jb zq8op3n>Dw%pZ_WxPpPu7<`#Ng%_(+clYf*OC4KXn;zjh-gvif22cThK)O1T|xZN@* zM)0(~SYj)oimJq*!igA2`DNkc+tkFu0};nAU`r0|S4-+7VdeI3va) zG-|munb=eE`!MQ*x{!qa5QmHHZlA)evvU<$u+eb~k>YOmR`4b*s~f^HsQ>GgietLd zGH;k|%W5_3fcv${4ASK1C^%rsqA<{G0NgY{$UJ+3I>KwofiDUWilOOmj8ASbqIor~ zXJ%|>?(UibX|A$!L=bJQNdoI2w*~4U(t=oYj(e_pew~Mct7p^f?guf3!pj_5BLAp9l>`RirEF&xa}FAs z4wDD<+ex7{#i6G4!@Fc>OsfgOW>M6K7Isc4cQ{9>v+gFR*d(@zJSitBt@^o!MpZl4 zx@Qw;!^@ z{!1fE7sG*PcD8Xs^bD8&j3)B;gG|cnu*vjk0^uGA(TK^akKphgYlLwxFv&PA*_Clr zB`ne+W8XS(t@2a=fBvW`&KxFt7;^gXdM6vocj|<&vgk%x`>&{s zEI5Bzm#VqH%#m`s^q>=+gP6Z4!xgPYQV3=G0#M!?+EZkab&8Qnj(?-q44Itnkpv7# zOp7l3Y@($KJkzdU+{+aGkpylDFjdFLK{65j%QYN69z9vGe`9cgm~uXx@*-4J)NK)m z)FKXsEUt?oG#DIwicRv(&y`CT=jqNC#0r1XM4Ei|8+>ky)AC7JcQQGYCq1eg`LhRTmI)%i%Z=dV_^DOGxhSUvAR?C4l>FK?N8G& zQmkuZK}xy+|AajpIVgMgd6gL1S%(ouQD<2$_E3cpMw00=5z*D8ct1?_-9a=1xXeJV zB2Es$;}e#ku<;=E^juWKAUASWnyl=D98{)u!1lq zAj|;D-KC=|4Gsn;{BW__=*QmD2;LofDjj|6R#ofHP|k4(wPpRxNH82`KZ_j zDh$$Op@*D1!B)P)l%UrH5g1foVNuJgfG3zO8tO*NN$jOcrYnOSJs##*E}{nSC+w&5 zbB2Hv&;m71#1=tT&8B7k|#4)ijNYMRJM?+-hS`MQ-g> z&PfOHXzEB<)q~umaa{QNV$_4iYHbDOut<0h5etcClZ33fbGW6w`r8BHVP>k1MqwUE z7GM6y>9$3yOYc?h5xh+~vz!Bhcl(cyD-OOlHQT;C@9-6J-3_mmc}7n9UW%V;7aX=tj1(W9FIMs(Dy z6v^QMl^Q|+idgJ5?l&4DfUMiTLd;@A@2-ZB z^e%_GKL#tGAR0O6$IC1XPt(~N_P4?DB`d`e4~xMOpZ)7uRaLi(6Dgi&fh6F2uaPsc ze+f;fPM%pV@k$pJXHi*2W>4=ell%-<)guScDop?2(J%Q?i-=e%h=@p>t~51mlpvj`cqZvFIL)fuB2}E>CPXGnO}>FN_Mhmn5peR36Vu88f;t%cMmm5wiH%B z!%XAH6R$7Xrqah7!cn;0N`@!!WPa~L^7$HG*;4=Oa%0T&^8I49A#7j<)EjM9F5rrZ zkC`xuOsUtEst$zcn=`xJ_~&gBDf<54{YfYeIfmnLL2A_V00UOE<+S6St!;_4o_5oG z+BBA{E<Hw0N?De+tU;$QuM450s+<`rrv9U2DH$4f7Zi$prtMi!~ zr|7$|t2mWkYXaeHi5G3@!G^lLhI_PZFjH%7n|8Dr`G&jaah?`_qlj2Gm+RKe!AQsT zrU(66&&z^wamv6~eX=#KGyUkg{mgm^yZ&A9<;fAc9?a*(MC^j$oLY@Nji4;rxZoI| zlX+}cv0R~iAb2>4nmQ>wCfpo~DI8O3x4Id9rccxB;y`Q7(fZ?k$|>^eg2nnw!)YdO zNownNh3ScpyN3ae>~PsYjcvHkbI0OjiL0#}>Fasb9F7cw5$)>|d2OFVtq^VoE$NWq zBqNMare=jar3$mXG|EwOG8#CSD%CJk5My)w3U=VC#as=6rakJNSS+&>LzMQA)b;X( zLeJlKc#cFQI!yPlSy!MBEsJg`43q~-Hna^2G)3`B;S9@HF~?BZYC?%X3a-GmZZg}` zTlkvHC4!>xJwNi8D)$F`Yw3+?+#IIGwv&|t5}v|I!mri)r7iPd??S=*UG7;stm_$} zKj8$%rn2*L%zy(_w)T6ERZzw z_1=mJQ=-_GjW>PnQugZY&O3Jxajva-TE|vPo|z_*F3~==lVXMyM)fSOo9}&QXM(v; z%u5Qc`vi#$ed`RzOpX%GS{=r;#r~NB{y18K5k=mC@sH;?WGfA$dTc6O`WVgyoC7zz zC@wc}mny2m^f=nES6F*~B~9kgIv?MXUgQK8jV=Uc0*|Z4J&mQWoq<@^rjV;P8`H(# zVxGhl+2?0C8~k=-1Otyz9sJ7=SBzZ8PtB+*Md{=-*4a886%F@W9nQ>eoy|w@=^AMgWUsO z2T#PEsamp}1o@{#R|8^Xgf;1&gH-V<5^&-V-&PVg63;9mL?|d))LvSx0=lqN+tCMU z^GuGuD&&i8`5u2WJ)%`*HT2X2rxre@w0m9B;Wf1X^nOb{4>3v=Q!@FhT2j4zU==N@ z`6jkyQ0)qt8JhoPw%UGetbG>eyfObDgoT^)uGE#Oxx@kpIDm&a?ROuX% zyiuiU*k){dXtPDp^LxX=*^2rtL7Tn;_QG1{!t{yk0rSLf*)w0yY6?q)bx>cfwrZP+gT2cM9pvG$H6fTQPjKKjD4gUgT?!EAa67 z9TI~e>ht_*POe)A4ZeH6pOHi%{o*vV*{1{E*T?oi>NqiJyT3GX9mIzl8m)ksh)t(x z=2xqt;g9>XATN7G1hSLx766$DoSMFF_)#Cqio&OoMPOG6l&++t7v)RJ*SCv`l+DAewmtTdnCkjc zCwn7HZ<1{HOO9B9I$#E`?PDi1n@EkuyWtxC7nb90tK)#o4+FpdOTG!n&N52RSqt(|a=EPp}`IkAWaW!PJk6(n*& zvNj9;-GXP8CKbb0M4pO=h~`JP4Tk1IX)sDf9t03qv?L{Y=R_x5_t?o=U#_rb)qSR= zkv_mQpAKeh29{s++A37J)P0;7NuLZ8WsvW)%fVJcJahoGrk~u+VGjXo_Vjoe?to$^!+d;5GrpszW1#%jM2Y9;76z(ovEX zY@rL?!ebJN#YsP+l?|@gX(3{qJ&sHTL3!!juL9Lsdy-m^TUaBhdMTJR>iCF`UZFZ zL*U$({Lpy8%=qlf$npN0P8!_E*&@UAtx(3bwr9irqqhR%U?kTWtXp2&9#8oaEQxq< zgsG~!m9r@7FfJvdGJuqpMT!}Hg%%lfke@87({&d<iGl0OYPqF)!|pA ziRVH{O*q?6OrEx)zi6*IY7|Kh^*^hFvk2yIKH@OiDsX?yI0h^ME#h$>P!A$(gn*_w z3331tq!8$1DTm5W)Tle~WTp9(Y|6&ySS}fv8$2fBVEv`2bh_H2T^kc&iF0$$Kx>^JM9sK{l;fA^zVACSy?!uGA|kD&R_PSVn;#xAa%@wt!lns-0AUK2 zQP77SV^>!QPpp&}%Qxb;-zu+)@Oh}dv8OIjlTQDbI|mPE9_SPhzV_{GusN`cv&$}j zHk?lYeg`<8hFZX6V=IY5QC75;k$Dh4G zTJ?V`Of|%$7;K&o9K(7d>b4%AmQ4oVcbn9}k5p~e%`G*0U1(1A>0NH=cDO8O>(KE~ zyC(JwlZm7^jNz+VNyu*xM+WDBa77Y7`Gq1)l~jZ_G)Z-BXen{RiROggdBGY?sf;N!c>Xr-Bk82 zY~N$C%{i{xBf$&lairXMLtjn!T0x;{B=$N4D3oknJ;u!X1)jl7;RqFzdePAN69C+P2|S; z6DtlcZaWT34%JW-(V8L-BSO)xC|$C6SpUpG&&Ez*M^HisMm!arcC@J z&KS7Y1_EuW0L`Hkz?C%dLZ%)v-zr9?9%H*3BT);YvrjKkiz1V(6SsqzMBhQ*S2t;P zYYZLp^G`Af3T|x5NRp&Ih$h~h9~eb`LfSc7w@P=dUDxt}=Y|2t_AX*2?5nCv@Jkuq zpXa^)e1K2(c2D{GgxXsmuLddQO4Fr1z25SA{PvF3Q$*{#mv2oE z{ol(+mq~aRH&4DT!km#W{SU~Wx`TRq@FAnh4n7J#r%#k%ByxjkwKEudn_iqJk8YkD zed}(%$5S9TLRZv!lz-v-3u{vCrtQX$@h)pyI3_zh`QG<#qG5S>yzz9oS9jqes{qIQLH^EY1n?2h~YID5w+QKGa< z_nfLzwr$(iDciPf+qP}nwr$(Cty}$lF*7}Vd!pyw9~rSTGIwVFcr!9`?PsmGdO3Z( z`tr`v;nsW;Z)+dwRq3(M?w$SXNb@J^ZkMsM^NZ=P1C#}X8;AdvOmej|5{}(-&`S)< z$$5gv%`UZbN=D}}2J{>I#^&aEPXO2Nl{IK)^>YYkxG!EDk+z=pr@vn*j5#?{GBnrc zV1GW%OLAOuqjHwlAwD*+o!hI|QGnGnuX5S}7l7OD9Ro_#{$hCaY^EEZL%acM!8)CG z1YN`Na>^Fsc|W6Dhv+@Kb!NfRg@Qz_L8bfHHg9`lu*ntkfa6T|Z zyK~w&$)t4WR|lg)IACETOl`tF^sD8pV*!(M<$hGzu+rw|=v=NfzFLiPW9ejhEm^Qi%ElbaHlV&mY_{o`-|xfU{}U# zWI6L1rixs}#QHDONU#zw1iv~==r|xzAV{n31#k)AE1qmMKX2F~P(3H%2s9(q)?f)N z67T*jXU|>#kF)7mmZ^t#MbM3<@H|)`Gg|L-Oj@1-*d&%vkZLm%0=AWq-G>kG!aR5} z)WMZ8yX(_O(BLZQ6`BzxFz30iBY5wg6lP8O`ukMzi}+2PRLcaj9JPSLPw6dRVaViQ zQ(%Ai_0@-5+7YLSLf{MA1!Nb0_jmC<3DW>zR`MFd9||q@JDIWoCk7oxSxS&i(FcU0 z4L7o-J)GSZSXL)F*G>dl4pK`yUxc-xRF#a>pzOz{P@+*#quS%`Z99CKduzV`4wt1@|*!oQr=%4?g@T_fLwG+Jxt zNfz_smn}kZ4v_4O(1Y6rmvRg_MJL4An72fD7T?^#lw|=w72U{qp#X)q$871>Uo4&|~ zQb0w&4dFNkCm8I7V zEJ_pMe?N*fw4dmDt*i@Ik_siAEbo5jv9T;xYp@jM z>#meVDk2-2%P<8LkyT!5<-?kd;~-W@H8T(0A+h}g;S|JrKzb9CF(krHg>TfAsrU5?CZnD7i@2Cff zf_Q z`J@$^>}S2cvA7%K@)ME-v}bAoZi;6HX;SWgB6m?1-4{chhC5;^7rtSMDlIa9QkVFy zk!JEVq+#N^eOCVRI=Z&_UL>iazt$d%EpOQ=`!@SlMAnqmChNf0u+;$5Kx@ehk@fbS zMTw&OUIV_0)If2 zst|@Yfs~u2^UDmJL9O75P-X2BvZ={0;@JzI{16--^bL;<&p5;X`fJUU$rijm1v~|v zVMsBp_eCjWe0v^cA-}_TB#n~A?Vbjefz3LZ!Ok}RaF%N@AIiTLfXjDs#la;B%t4Pmqjx>n z)=!6B#+=-x@DAx{ls}e#VwE2UQx6goYe4hL`LWbss!hpg zgLlx=^K4h(lViiQzZWwj@ad_Flv@SgAS>KLK#ns1elC#Bp6u1_a04-Z>s75lWahSZ zOZIBFw7J_#mZcm|!07A~+T7`^)hY-m47#vnS!eYHSA8^u<=JrCW~af`cgv^+up%JGc|wm*!_Xszdji*OylwyYAd4 z*{Rc`l$Y_Yl+$k164n=%#jaDun$?N=oKd7 zM_Oj^#Zl8IoD$vUv0#KfPNG+sDWR07p^3nu>c)#ImYOL(ma`_7{cV&vRNky^D?nEyBiFqu3wtCO zF?UJ+J^pt@lRrG&&h9K2DL?#-fG#QtbPIjd48L=xg%W=oop*9vErTER+QD+G7EsY} zK{zZuBen@r*>eEU=daK2UkhmbzzT=pf&9B&n76&~sC;a>HmJW$@LmROrb9tzVpy~J zlTQ$J#2HvI!9yYNLv5sRF6|3UC6@a_`tWXj4Nqc5Xanqj!HXi8#)JT02!h{4)B`mn z=3+L;!CM-ZSQFs_RKg~T4&=8N=!?kj#rw^81B}xyI{tga<9`GjurjdyQ{Vv|<3F+u z{uTZBUl7Lsf`Nqp8wQdv)ibpF!GWx;B&~mtp#P?Pe$b8of`XXY{u|=>Zz_n9^@j># z_(45b=^1d@Sn2+ECg|tL|D=Lg8R-8B3i_Gy&rlHCKS-ee0~7QQ?nCZhSdjmBvuXx5 zhX2F_#f@9})4~cqdjuw*;se0oSrG;#pvWP6mYDMR^biV?1G5>#c2`#iH_4%>4C_aG zqDpsgT|hTs?JF-Y3u7;~Hb(B7d)qUWBWSB(n9512N6%A6I4VQQUf4%JpO0+zX1#Mp z@3C|{VQp$oE=hXt1(SN&LbWexEnMikKiuzIqIR|DbCeO1gD>8!X5TA7O)C!Dj-=tB zcW*>Jw@SaR-+1Au$flUfmc9p7^DEYuUj*iJih?I58^YCEepMDyh_Yp#PmjG;u31aQ z8LGJu?_#kcY5Ab|*28CrdVpzxSN90HKzJce^-%1_v?Fi%u=Kg#D5>~6`%2!df)xGH z5X0nVhZE8W@5ZZnF~;M5#i)6~U^TyGv6+wmCQ$i+`VtK92D=^Q$)NdCwMqF{*16U0 z(GK?xLpOVo&jSqS@in~*#t8Ke(}4d*bdG(&ScoU~-VofAE+Gl!$qgd>;tmkb)fUfn;MX<#3{vLVME;3 zjr)=l{vEd)+fVq5v(9%O!LNq7MwMXo+|$&)?kUq|SwEQt5J=r%p}vA`Qqj51qCX7g zn^kZ8yt3u z!SbULPM`8DRKHiIWj)C&i};>B|6m0=4&m zkNRl>`JR`;O0fZ~25A0}0c^Jt?_bbot?_SH#vz)AHL?{4o{Lv2BDhxA*Ls(N~0 zXbH6Um)!TVa=ymub_0Z$6bHlb*??${8b)9DGP?j-V*Ach)FtmIr3C4xtOk<;6lA*W zveCFw&jEy z?8@Gu8nJfO?M1=twnK4;F2jZNQf?R?oWAu(LkP9#wP0}-%hVuL`D3R9EWCQ}okn=4 zV&VzkR`sGpkS_b$o7$Wzw=lfF@NX=$U40bGy79biz<~FH+Yi5(m*5?bt=lMi16sPO zq-lW1B#*$p6JG@1`)|lTNh`%!zY%9#JEaG_e{NGSJ1rOrjmyW zZy$fDnNu+2Xl_6_BLdToVxBqfN;I1eahNdpu@OMHR$0NbUNmh#}c*6ax*Sy}-U zpTk;47|f4t*RzPVj)B_-Q;b*$3M}G;hO6}gQ^LDdI`y>XQnO*`k8RIeTd&jg28D^) z1_$VxU`8K+p>@~Sx4N8Km5GM-9f}JAH15gdX|7Ri;JFN%guTceYTRTs z#NcfGOIQYm=lnWM_N{&w8Bw*U{($s0e%c{0UCHYWe}~#K4OS0W=huhx!%h&Sik}3@g-1x zb7U^FXkFpga6jZ3+p{b6D5E0Eq0_5+7GR9W{@JlyHAeXS;6UU?Yt_JLG6fYI?)LYT zkfpjT=uXra1v1h>0m&zzlQk1pmBdKBnly|~^!5;SMM_Ep#yq#w+`)wos;9-lMK`qW zz9-GRD{ZB4ws1z;wESKo0$kiXFQ^mH@pX!e3)R0Y4FS_yvq9=!SW5 zpRpN?=qEZ$FPyEDUJ#RtV1NcX72~sE(M?rAp&MKsxbf}6!+^#XFIq0oDm#e6Emtzi zHo9DA0LB6z`Mm|0iE?;`0%J2G1S2Xbou3hqv_HRC#5x`>vA^O~lMMg#g=bLLpN2Bm z2+-S<(4C^YuC4{phf}BP(>qcAqDs}!z*RrMZ%v@szkmn`lgd3%{lL%`*g2Jx6S{yn zVd@(QH?YbTX*x%?H^Dmxy?)Xbxx|6t)C}{^yu1htZ9DPZm&55d+RsLcM$PrVEACaH zMl48&GOJ~J5og4|9AXx2hYM*{ph#Mvt`s=SyOScnUN|%&rx}1FYW%Z#T=UEwIRmzW zVlqHQ#@t~ry}DJD3BJcqWb8iai*!EGJ&1GE4FN?TnoDl}@qTzHWZ*a#{v8mv~q(bY!(Xlpg$TwY|T_6~MFQ zu2-+0Ude#{RzAYB@ji0vd!GII!3lR%U83pc-F5#mXM6{%TTNF3LFWxZm+XtBcF0~r zT2Wo1-Ck9+f>KeNc;BVtdKv0yz{BVFHnqrFR%X6=**=;yN$HK`-++X-s9=B8ybto^ ziHDfI^4toz{k9k0>Wq_zLhrQPna%F)4NW6nl-&KqTIzvVE#B>hc%m%H_znE+=%L=s z-`$a0v7n)%VxS5rqi$HL*4)(PM5FihYL$4pJ=uqup)HHbG`Pn z$#X-}+r=evf1QQ6D1XVEO}lgdgT+cXGSe?}+`ao+@(L@Dc zBVEo?Uz*}y8lkhLS}QYxhOi?(t-Kv@X9Oi-hCt26#COZ}BQ6qWLQ*+7a~uxOPV~ls z?~S?#npyTMs^O>g=j`d*cyr8YX-P@papx@Wf~{WH=$W*rWhG6>h$@sHHZyV*E1`>0UX~=n7Y^XcLpAf4PO@QpMJZ$_ zPhp2|R^<#Bj*0AS&bjR}eS2z{a^EtOmCZ1h6rr1Dt@rVE^At_W31hCo`{2uux#vqz zw(gk9oga5Auh|CF70s%9{OCJ=y18-7ipH$hm~Wr>zT+2MY#;7!j(&Z0lzYXRy8 zb6f*hJTUX8_Oqimm>DGWeU~W~*P(juO#J@C+NzD}fM&644%vo@{=yX!kaB`Dq5KlU zivTOh?0=NAuwXR9X~E=(+5nbO)WD1K*Z(x&=hBN06}%{Y*Amqj4M5DuA1gWGI+dvm zCwf=KM--54f11>{SWe_xa!WRI#&=|9ZT+c(Ck4B&L#2o6Z`BtIHIaYQbPGzR1eXncSw|&D+Lxz zi9GXDBD5gIB+BD&C6pxdX|rHIr(}yy>J2j}##I&5W-Hjs!27P{;0Vl! zxl{ih4ovP1a^ssJX^7*;W(>yEn^EjV6)j3oW~Cr9RIs)+r$myeww;(VrAGxPmX@a1 zde4(Rr1b=0MH%a-xTsBrxG(naiP))$a-J^a>R9HfsL^p}ijf$j2?-NtC@uaWmd76x z@F33`G@&D^45-ypX>wuhC-)14l^5&VO%)}PC&%yXa=!7y16U|e6_uLQGcwDRSp=O7 zsWDz8bLbn8A3cb4;F>i+_Z6z68zw)PHNX$A5|s@Vu-R3qG;lFWdjcEJ&;%|Hm=|3r zEJ~mhN}XcWU$rnR9(R1X(Jj_%Adbki)iYQgKCNRZR>;ROMK^RagI&+gsZ7`XEDT4z z#6b*}Q^BF}LxA#EvUUB-!p4F|Rugfnz`Q>(P@FOPP+&g70m=m%EXrB$vIRiS%^}E% zbbwUQ7ZZTE0cmQ4#9s=EA-fjw>}29E=@K>*t|Yo3o!bq%DN;Neu7!lm0tRNoM>p@` z(cn}d1c~T5(T-H=^#G$E!26K?_wuX-KZ0HqGrRQ@@)2=Pw*{EqMR69t?*X6n=0hLu zD@Ch=bl6cMOSRTqI3oRK!)}!oC6ARl%@AHqa;yb3TulFcRxd&IHHD;7F3C`ww!C)S0JASbHb`015Y4FeRPA9zv_M~|6Q&nZ z-5Qvxbo*|NKIOH4v-M*kg5IH?^0-k#>J@U{5igr=sc_jMB3C3YVQG&RKi5@1LP>>- zU?N>gyYD8W#}$!>IojlfTu2+9a=5}b?yUj+F{;iy=ulDzI(An*D`T-$P>Cc^QSPSkgk+UG>HI{eGnYaZO-_vLB9+z`Tb{BxifX8cU$;{+Qp0&~xsP-+QyWD2WTsCPs zOJ^P&^5iWXQ*_;$L1cw!5UC%J+fQ2)u^*84Jh&_S!F0;2#16dNdhSL%H|S`tR-*nB zP9Gt~FZ1Q=l+WQ|c=X8Tu?h^seaKYD>9hKivUeq5SFE zjzs7djd3Ikg-qY8L+Efo9DhdB{uIFh!QD@*1KbmRf5aFq3$na0zET(dkv~N@rqi7N zYgw8*yIcOdp!1=H_z%6TZwzW`06DA(=u@2k4#{#4HU7S)kS5Ajdu*{Y$tsY&Ni)P@ zd+hb!*Tt8MhP}l=W0EZ48^+mY2cy{_Wa^G~$Sq$FiY;}hE(BM%ZGR7(E%(TFltb(e zcgQXr*AD+^#69)4a7}6Wcnw}Jn=DS}td*(w0}z2PpAWBCmBTTk-R?`*U9N4RT}|!a z8|`ZcP-79K(u8Iw&~CSOewT6enXF>b+N-;?2I+d8j^`Z3hS* zj&tO%OlOE2VluS}9vbHkRlWNzQ5T+!LC0)Mj&lwUXC)4G9k%eg?7ZD*5A#6fn-9D! zn)katahfZ5EzC36E{27ORdHA)*ZLhdUxzN6Ylsc|6xY~<%(Kb?^;3rE<3MXH6Xc5U z0u^ff?Uys7-PP(NptL1Vqfeo0v~9QCd$gYgT(>L6+AK~Nde^(!_@af6jStWJ4%TgE zn(1$yuZ<6m`=jc-NfuF{r12ob`pmkYjy!g&s}t{)*#uQ2Bc0MQ9|(jG0%xJEBri0| zM}aiAuDb#4bKj(H-1KXB9$wNQ;~Mua`)=3z(b*mu?x?cd4J0(2A!*iejs?)#(tqT> zA57|JUMcPP3#7LCKMw)1{2qM?U~B}lpDy9K1CKvd0^hGzi8kCTR9U?&l79`4EcVga z!4S8eM;YzXT%EukUA0_8TR9c+`=G^5dhm_Zv@MK0Db&YEG=PR~NrOTWIenu$cXrQA zzE6Tar5E@rFo2q8ai1U`2+DbyeJW}Vd^F6>30>Qj;)`s}@T#(k?C0+EenKg`Sf3yT zdhT2HxmefBNRodlwwwW6WPt&yyZT-q1iuh^(|luUo1mdNf%TtwOg%?M*F%HxSK)c( zEK^0oT^ zIIG?(O__0-{KS`4&VX%bP?6z=A9MxQ&L$=K4)%SF_$VE$7ss|Y7RkCL1`jjcZ=$~p zKLa6yvSD}Juh5;SvE(#H12@dH?f4+`@)ZqW!Z$fWG|nJR7}Qa{@zOIq05g!X#^}Ni zHEXbjOikxd5g9o!7-h{irW%4oaP&905TKdpItan|kc|emfY38SLI@^xrmoeD&iL=N zqKg^@mVmtA6r))I|9eI9ADY=eG;>B)#(y@}GX38gYsLOmOP4paGd22GF`f0F#dM}0 zFXWG8{^PEtXJ!5mYC0R;KdI^Tbj<&kYWjcaga4`-{u_raBh!CU(`6*3e;l@*_Y|yE z!B7qDi{kuxO6dwEFN?Z(ImBSwba-CL|LB*{}mYV`Pb*&?Hi-X-Sg<9|~h)h6-rXGF_ zwlOg2&)PLUkqHQRB|%`%oTA^9C_1W+7fel(8_}e%7%tmfGwke1Q=N*1?GAG( zs)L?z{xHiV^+SEu8C$OfW7imM`A!S}zE1u>+G_t(S^Qh+n(3dEYsQ}<`G>dme{A&s z2Vd=f73#nEYMK6xua@cG`D&T|v#*wom6i3MeYKY!?j8w)i{GiPS;pC&*GBcLn;Orn z7Ik2%e0+6yfAC%5<46T?HPQv;;YDId3Gq;1%i&Ayz_f0>N~va*a?Hk!{8Mlf7TjsN z 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%0A8hk_fC7d(`pQupM6Hr2* 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

(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 8ec27d205..84b4764b5 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 6c6445921..13e23fea5 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 ed57ed286..c5e117785 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 000000000..571861389 --- /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 3a2a973b5..dc37d4b38 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 b7d3c9d27..664af7652 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 642d43149..cad81c74e 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 92fe7f7de..eeafaace3 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 90ac5e471..f83399b53 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 f7a548c59..73ef64b37 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 d0d643eef..e8fe636c4 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 6ba49123c..661c02c91 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 d2e6d5ce4..8a19ca57f 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 3e646beac..1e844ed54 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 e29d5a6bf..732a651ae 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 8667048be..0de899c57 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 e37f73969..ba8f37b3a 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 78d2e3045..2efd97204 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 3d402e795..e018fae94 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 091/181] 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 418238bfa..954bc2ccc 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 47086f3b9..a60b6c428 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 092/181] 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 8fc1a1dc6..1fca8d5ab 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 13e23fea5..da7d0d774 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 093/181] 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 10c64e931..9d4cd733a 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 e0c2102d9..9878c6acf 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 18a50015a..6846ae898 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 c12c080ba..7095a2695 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 1464aa065..4c49b7f3e 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 84b4764b5..38470f8eb 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 e8fe636c4..d5d58bbb5 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 15443529b..3236cb93e 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 094/181] 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 | 382 +++--- 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 | 158 ++- 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 | 360 +++-- 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 | 617 ++++----- 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 | 1594 +++++++++++------------ 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, 5907 insertions(+), 6999 deletions(-) create mode 100644 include/sta/Format.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 055927fdd..3c6ab462a 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 a7147ab03..079112e12 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 6c1e7560d..00f1e3bbb 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 54143b289..867594112 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 2b44188e1..c85d19982 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 5fee6823e..046e8e8d6 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_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)); - - 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)); + 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 *)); + + 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)); 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 31762fd07..0228df301 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 7154e3d8e..c40bd429a 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 7d15b5a91..3658c2198 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 25429d68f..f153e44cd 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 31f03a525..2c3cb0595 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 69346355a..6ad491784 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 82bdfbe41..306634bae 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 48114895f..38c04648e 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 @@ class DmpAlg : public StaState 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 @@ class DmpAlg : public StaState 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 @@ class DmpAlg : public StaState 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 @@ class DmpAlg : public StaState }; 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 @@ class DmpCap : public DmpAlg 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 @@ class DmpCap : public DmpAlg 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 @@ class DmpPi : public DmpAlg 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 @@ class DmpPi : public DmpAlg }; 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 @@ class DmpOnePole : public DmpAlg }; 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 @@ class DmpZeroC2 : public DmpOnePole 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 9d4cd733a..6d8597550 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 07a68e5b2..692c01f08 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 90e31ed31..fc1b02089 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 0ac6caa82..83759119e 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 1f1871b98..bebf05b65 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 c5fad936c..40f00d292 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 9878c6acf..022504ca2 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 cd8188c99..85cb51241 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 2b9efc5e1..06215222d 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 e09cfb91e..89159a824 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 1c21131ee..1ff459342 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 24b97c60e..dae429afb 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 0d102962c..3a25cbf78 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 c7bd07a99..a413c92d6 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 090f57017..5922e03ea 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 d73775f45..a653024d1 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 b7f7e655a..9e4a555cd 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 000000000..1745f482f --- /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 d62bd9823..9b40063a4 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 954bc2ccc..d8ba2af66 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 3ca8ffe0f..fb42f00b2 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); - - 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 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...)))); + } + } + + 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); - - // Critical. + 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. // 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 127cb23c0..5f4a6cd0e 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 01e07ca71..64a413759 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 e462af6d8..3c2c84bea 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 4c49b7f3e..caffa49dd 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 b12ae00d0..c2cfb13d4 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 3390c1031..8c3475f52 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 8649c2d78..bcdddb523 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 35311f6d0..644992380 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 c3e1e5f99..1b2fc037c 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 1c659ad89..5e4328ad7 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 2e1814eee..075295262 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 72a72b1f6..52cd97d52 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 35ca71807..b20b5c6cf 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 5d16566d1..b3cefe9ac 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 e95ed8769..a873a8815 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 070b28e7a..e1307b7d3 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 ada26b092..5e22ce6d5 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 @@ class LibertyWriter 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"); - - 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_, " voltage_unit : \"1{}\";\n", + volt_unit->scaleAbbrevSuffix()); + sta::print(stream_, " library_features(report_delay_calculation);\n"); + sta::print(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"); - - 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_, " default_fanout_load : {:.2f};\n", fanout_load); + sta::print(stream_, "\n"); + + 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 a235dcb47..46dd6d6b6 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 - && axis3 == nullptr) - || (axis1 && axis1->variable() == TableAxisVariable::input_net_transition - && axis2 && axis2->variable() == TableAxisVariable::total_output_net_capacitance + && 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::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 98cad0aab..fb3fcdb68 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 50bc62b23..1d18d24dc 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 de4b04648..6291d5bc0 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 2c98c5fa0..ab76900d4 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 fe9a9c1d5..b0a7d6358 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 6495ec8b6..870399df9 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 83f097b32..7bbab7a07 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 5200367e6..4cdef63ee 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 a018782e1..702ca6d2e 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 db291c536..e85036eea 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 8aaf5fc04..ab304bda4 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 6dbab37d1..4282a7f4d 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 a67269117..f91db926c 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 42983f0b2..a88e2d265 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 99c536333..2bc516ae2 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 cf58eb514..7b01fda6b 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 7a3de2af4..d4dd239c1 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); - -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"}}; + 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"}}; 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 @@ class PropActivityVisitor : public VertexVisitor, StaState 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 3f0d0d76c..cf4803b7f 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 be4f723fa..6012d59a2 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 17345229c..c06cb512c 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 1227e1b21..c82423cf5 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 7a1b312ac..fd5ed1cdf 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 @@ class ReadVcdActivities : public StaState 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 b54f4302d..df161fc2e 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 9729a15c4..0bf76c388 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 3c674e051..a3baf7162 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 4ca26e4e6..83bb60559 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 6d035b051..8889f177c 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 1347ac25f..0b0251f18 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 50f49e959..26196d2fc 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 cd58f136d..7688fc684 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 0e516e0e3..8f667b71c 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 @@ class ReportAnnotated : public StaState 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 @@ class ReportAnnotated : public StaState 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 @@ class ReportAnnotated : public StaState 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 60e8b7ef5..6c9c3efb1 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 818181275..65a2bb8e3 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 @@ class SdfPortSpec 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, ...) -{ - 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, ...) +int +SdfReader::sdfLine() const { - 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 3b140a4e7..4ae883ba1 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 db7af6b0f..fa79139f0 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 @@ class SdfWriter : public StaState 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 9de9be6c6..559cabb7d 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 12c795ea9..cad8319e7 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 de4ad8b42..9b83593f7 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 9cdd42277..94f845b52 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 b7b4bac5c..887c009e1 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 56590fc48..8167d486c 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 21361ed29..3d1295bb1 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 ca81bb0a5..3dee07b99 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 @@ class GenClkInsertionSearchPred : public SearchPred0 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 @@ class GenclkSrcArrivalVisitor : public ArrivalVisitor 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 16bde0881..c701e7fd4 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 dff1f199f..5302be0a0 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 39f712fec..20ec61109 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 38470f8eb..c0bb35d1c 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 c5e117785..437966a84 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 @@ class PathEnumFaninVisitor : public PathVisitor 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 @@ class PathEnumFaninVisitor : public PathVisitor 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 a60b6c428..e2ca77118 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 dc37d4b38..35e6a0961 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 664af7652..158b79191 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 cad81c74e..cec91ba7e 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 eeafaace3..ae6bbfbf5 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 @@ class SearchAdj : public SearchPred 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 @@ class ReportPathLess 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 f83399b53..e6716ae1e 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 31e7ca8ea..bf459097f 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 d5d58bbb5..355ef3f03 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); - - 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); - } + 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); + } } } 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 @@ class FanInOutSrchPred : public SearchPred }; 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 a242be9c6..9e5113e4a 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 67d0d5eb6..85fc29a50 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 0c414c867..4a668d9bb 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 8a19ca57f..a58efb64b 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 95fabc422..e5c019bd3 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 1e844ed54..6a62785c0 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 @@ class SubcktEndsMissing : public Exception }; 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 ace2b78b0..74fecf000 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 732a651ae..b01986a2e 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 0de899c57..66dc529c6 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 c2429db4d..917e40fa5 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 aa4b85e53..88914f267 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 261d143f2..7be581f63 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 4a6e6bf89..d539c5741 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 d3e070481..a418a53a8 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 ac6d98dd5..07fb62a19 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 2b93704ca..cc77196c1 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 32d9e0c12..e68de5ef9 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 71f056adc..5888f993a 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 e018fae94..a539a3114 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 @@ class VerilogConstantNetNameIterator : public VerilogNetNameIterator 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 @@ class VerilogNetConcatNameIterator : public VerilogNetNameIterator 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 d8ee7c6f2..3531b865c 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 095/181] 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 870399df9..c07f6de8e 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 096/181] 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 6d8597550..8c12cdf1e 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 097/181] 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 b32369cd8..a649f5143 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 098/181] 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 fb42f00b2..7e9aab554 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 5f4a6cd0e..acdf84dca 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 a649f5143..3623940cd 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 d539c5741..d8a6a04d5 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 a418a53a8..97eef4aff 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 099/181] 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 dae429afb..d34f635c2 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 c0bb35d1c..e7f78bde7 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 100/181] 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 c40bd429a..48c867a3e 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 d34f635c2..e25475579 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 101/181] 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 7e9aab554..28f685a93 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 102/181] 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 158b79191..21a4c485b 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 103/181] 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 28f685a93..51cde2ed7 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 21a4c485b..158b79191 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 bacb61feaa0976f86a20abbdf08017618a669d83 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 21 Mar 2026 10:18:55 -0700 Subject: [PATCH 104/181] regression -j Signed-off-by: James Cherry --- test/regression.tcl | 463 +++++++++++++++++++++++++++----------------- 1 file changed, 287 insertions(+), 176 deletions(-) diff --git a/test/regression.tcl b/test/regression.tcl index 447a85adc..8137fcc80 100755 --- a/test/regression.tcl +++ b/test/regression.tcl @@ -30,7 +30,8 @@ # # This notice may not be removed or altered from any source distribution. -# regression -help | [-threads threads] [-valgrind] [-report_stats] test1 [test2...] +# Usage: regression -help | [-threads threads] [-j jobs] [-valgrind] [-report_stats] +# test1 [test2...] proc regression_main {} { setup @@ -41,12 +42,13 @@ proc regression_main {} { } proc setup {} { - global result_dir diff_file failure_file errors + global result_dir diff_file failure_file errors failed_tests global use_valgrind valgrind_shared_lib_failure - global report_stats + global report_stats max_jobs app_path set use_valgrind 0 set report_stats 0 + set max_jobs 1 if { !([file exists $result_dir] && [file isdirectory $result_dir]) } { file mkdir $result_dir @@ -60,20 +62,28 @@ proc setup {} { set errors(fail) 0 set errors(no_cmd) 0 set errors(no_ok) 0 + set failed_tests {} set valgrind_shared_lib_failure 0 + + if { ![file exists $app_path] } { + error "$app_path not found." + } elseif { ![file executable $app_path] } { + error "$app_path is not executable." + } } proc parse_args {} { global argv app_options tests test_groups cmd_paths global use_valgrind global result_dir tests - global report_stats + global report_stats max_jobs while { $argv != {} } { set arg [lindex $argv 0] if { $arg == "help" || $arg == "-help" } { - puts {Usage: regression [-help] [-threads threads] [-valgrind] [-report_stats] tests...} + puts {Usage: regression [-help] [-threads threads] [-j jobs] [-valgrind] [-report_stats] tests...} puts " -threads max|integer - number of threads to use" + puts " -j jobs - number of parallel jobs (processes) to run" puts " -valgrind - run valgrind (linux memory checker)" puts " -report_stats - report run time and memory" puts " Wildcarding for test names is supported (enclose in \"'s)" @@ -84,12 +94,20 @@ proc parse_args {} { } elseif { $arg == "-threads" } { set threads [lindex $argv 1] if { !([string is integer $threads] || $threads == "max") } { - puts "Error: -threads arg $threads is not an integer or max." - exit 0 + puts "Error: -threads arg $threads is not an integer or max." + exit 0 } lappend app_options "-threads" lappend app_options $threads set argv [lrange $argv 2 end] + } elseif { $arg == "-j" } { + set jobs [lindex $argv 1] + if { ![string is integer $jobs] || $jobs < 1 } { + puts "Error: -j arg $jobs must be a positive integer." + exit 0 + } + set max_jobs $jobs + set argv [lrange $argv 2 end] } elseif { $arg == "-valgrind" } { if { ![find_valgrind] } { error "valgrind not found." @@ -131,7 +149,7 @@ proc expand_tests { argv } { if { [info exists test_groups($arg)] } { set tests [concat $tests $test_groups($arg)] } elseif { [string first "*" $arg] != -1 \ - || [string first "?" $arg] != -1 } { + || [string first "?" $arg] != -1 } { # Find wildcard matches. foreach test [group_tests "all"] { if [string match $arg $test] { @@ -143,200 +161,296 @@ proc expand_tests { argv } { } else { puts "Error: test $arg not found." incr errors(no_cmd) - } + } } return $tests } proc run_tests {} { - global tests errors app_path + global tests errors app_path max_jobs - foreach test $tests { - run_test $test + if { $max_jobs > 1 } { + run_tests_parallel + } else { + foreach test $tests { + run_test $test + } } - # Macos debug info generated by valgrind. - file delete -force "$app_path.dSYM" + write_failure_file + write_diff_file } proc run_test { test } { - global result_dir diff_file errors diff_options report_stats + global result_dir diff_file errors diff_options - set cmd_file [test_cmd_file $test] - if [file exists $cmd_file] { - set ok_file [test_ok_file $test] + puts -nonewline $test + flush stdout + set exit_code 0 + if { [test_cmd_file_exists $test] } { + set cmd [make_cmd_file $test] set log_file [test_log_file $test] - foreach file [glob -nocomplain [file join $result_dir $test.*]] { - file delete -force $file - } - puts -nonewline $test - flush stdout - set test_errors [run_test_app $test $cmd_file $log_file] - if { [lindex $test_errors 0] == "ERROR" } { - puts " *ERROR* [lrange $test_errors 1 end]" - append_failure $test - incr errors(error) - - # For some reason seg faults aren't echoed in the log - add them. - if { [llength $test_errors] > 1 && [file exists $log_file] } { - set log_ch [open $log_file "a"] - puts $log_ch $test_errors - close $log_ch - } - - # Report partial log diff anyway. - if [file exists $ok_file] { - catch [concat exec diff $diff_options $ok_file $log_file \ - >> $diff_file] - } - } else { - set error_msg "" - if { [lsearch $test_errors "MEMORY"] != -1 } { - append error_msg " *MEMORY*" - append_failure $test - incr errors(memory) - } - if { [lsearch $test_errors "LEAK"] != -1 } { - append error_msg " *LEAK*" - append_failure $test - incr errors(leak) - } - if { $report_stats } { - append error_msg " [test_stats_summary $test]" - } - - if [file exists $ok_file] { - # Filter dos '/r's from log file. - set tmp_file [file join $result_dir $test.tmp] - exec tr -d "\r" < $log_file > $tmp_file - file rename -force $tmp_file $log_file - if [catch [concat exec diff $diff_options $ok_file $log_file \ - >> $diff_file]] { - puts " *FAIL*$error_msg" - append_failure $test - incr errors(fail) - } else { - puts " pass$error_msg" - } + if { [catch [concat "exec" "$cmd >& $log_file"] result result_options] } { + set details [dict get $result_options -errorcode] + set exit_signal [lindex $details 2] + if { $exit_signal == "SIGSEGV" } { + set exit_code 139 } else { - puts " *NO OK FILE*" - append_failure $test - incr errors(no_ok) + set exit_code 128 } } - } else { - puts "$test *NO CMD FILE*" - incr errors(no_cmd) } + puts " [test_status $test $exit_code]" } -proc test_stats { test } { - if { ![catch {open [test_stats_file $test] r} stream] } { - gets $stream line1 - close $stream - return $line1 - } else { - return {} +################################################################ + +# Parallel runs use one pipeline per test; close() yields the real exit status. +# (Non-blocking channels must be switched to blocking before close - see Tcl manual.) +proc regression_parallel_close_pipe { fh } { + fconfigure $fh -blocking 1 + if { [catch {close $fh} err opts] } { + set ec [dict get $opts -errorcode] + if { [lindex $ec 0] == "CHILDSTATUS" } { + return [lindex $ec 2] + } + return 128 } + return 0 } -proc test_stats_summary { test } { - set stats [test_stats $test] - set elapsed_time [lindex $stats 0] - set user_time [lindex $stats 1] - set memory [lindex $stats 2] - if { [string is double $elapsed_time] } { - set elapsed [format "%.1fe" $elapsed_time] - } else { - set elapsed "?" - } - if { [string is double $user_time] } { - set user [format "%.1fu" $user_time] - } else { - set user "?" - } - if { [string is double $memory] } { - set mem [format "%.0fmb" [expr $memory * 1e-6]] - } else { - set mem "?" +proc regression_pipe_readable { fh test } { + global reg_parallel_active reg_parallel_job_done + + read $fh + if { [eof $fh] } { + fileevent $fh readable {} + set exit_code [regression_parallel_close_pipe $fh] + puts "$test [test_status $test $exit_code]" + incr reg_parallel_active -1 + incr reg_parallel_job_done } - return "$elapsed $user $mem" } -proc append_failure { test } { - global failure_file - set fail_ch [open $failure_file "a"] - puts $fail_ch $test - close $fail_ch +proc open_test_pipeline { test } { + set cmd [make_cmd_file $test] + set log [test_log_file $test] + set inner [format {%s > %s 2>&1} $cmd [file nativename $log]] + set fh [open [format {|/bin/sh -c %s} [list $inner]] r] + fconfigure $fh -blocking 0 + return $fh +} + +proc run_tests_parallel {} { + global tests max_jobs reg_parallel_active reg_parallel_job_done + + set reg_parallel_active 0 + set reg_parallel_job_done 0 + set test_idx 0 + set test_count [llength $tests] + + while { $test_idx < $test_count || $reg_parallel_active > 0 } { + while { $reg_parallel_active < $max_jobs && $test_idx < $test_count } { + set test [lindex $tests $test_idx] + incr test_idx + if { ![test_cmd_file_exists $test] } { + puts -nonewline $test + flush stdout + puts " [test_status $test 0]" + continue + } + set fh [open_test_pipeline $test] + fileevent $fh readable [list regression_pipe_readable $fh $test] + incr reg_parallel_active + } + if { $reg_parallel_active > 0 } { + set before $reg_parallel_job_done + while { $reg_parallel_job_done == $before } { + vwait reg_parallel_job_done + } + } + } } -# Return error. -proc run_test_app { test cmd_file log_file } { - global app_path errorCode use_valgrind +proc make_cmd_file { test } { + global app_path app_options result_dir use_valgrind report_stats + + foreach file [glob -nocomplain [file join $result_dir $test.*]] { + file delete -force $file + } + + set cmd_file [test_cmd_file $test] + set ok_file [test_ok_file $test] + set log_file [test_log_file $test] + + set run_file [test_run_file $test] + set run_stream [open $run_file "w"] + puts $run_stream "cd [file dirname $cmd_file]" + puts $run_stream "include [file tail $cmd_file]" + if { $use_valgrind } { + puts $run_stream "sta::delete_all_memory" + } + if { $report_stats } { + puts $run_stream "sta::write_stats [test_stats_file $test]" + } + close $run_stream + if { $use_valgrind } { - return [run_test_valgrind $test $cmd_file $log_file] + global valgrind_options + set cmd "valgrind $valgrind_options $app_path $app_options $run_file" } else { - return [run_test_plain $test $cmd_file $log_file] + set cmd "$app_path $app_options $run_file" } + return $cmd } -proc run_test_plain { test cmd_file log_file } { - global app_path app_options result_dir errorCode - global report_stats +proc test_cmd_file_exists { test } { + set cmd_file [test_cmd_file $test] + return [file exists $cmd_file] +} - if { ![file exists $app_path] } { - return "ERROR $app_path not found." - } elseif { ![file executable $app_path] } { - return "ERROR $app_path is not executable." +proc test_status { test exit_code } { + global result_dir diff_options errors + global use_valgrind report_stats test_status + + set test_status {} + if { ![test_cmd_file_exists $test] } { + test_failed $test "no_cmd" } else { - set run_file [test_run_file $test] - set run_stream [open $run_file "w"] - puts $run_stream "cd [file dirname $cmd_file]" - puts $run_stream "include [file tail $cmd_file]" - if { $report_stats } { - set stat_file [file normalize [test_stats_file $test]] - puts $run_stream "sta::write_stats $stat_file" - } - close $run_stream - - if { [catch [concat exec $app_path $app_options $run_file >& $log_file]] } { - set signal [lindex $errorCode 2] - set error [lindex $errorCode 3] - # Error strings are not consistent across platforms but signal - # names are. - if { $signal == "SIGSEGV" } { - # Save corefiles to regression results directory. - set pid [lindex $errorCode 1] - set sys_corefile [test_sys_core_file $test $pid] - if { [file exists $sys_corefile] } { - file copy $sys_corefile [test_core_file $test] + set log_file [test_log_file $test] + + if { [file exists $log_file] } { + # Check log file for error patterns + set log_ch [open $log_file "r"] + set log_content [read $log_ch] + close $log_ch + + # Check if exit code indicates a segfault or signal termination + # Exit codes >= 128 typically indicate termination by a signal + # 139 = 128 + 11 (SIGSEGV), 134 = 128 + 6 (SIGABRT), etc. + if { $exit_code >= 128 + || [string match "*Segmentation fault*" $log_content] \ + || [string match "*DEADLYSIGNAL*" $log_content] \ + || [string match "*Abort*" $log_content] \ + || [string match "*Fatal*" $log_content] } { + test_failed $test "error" + } elseif { [string match "*heap-use-after-free*" $log_content] } { + # ASAN error + test_failed $test "memory" + } + set ok_file [test_ok_file $test] + if { [file exists $ok_file] } { + if { $use_valgrind } { + cleanse_valgrind_logfile $test + } + if { [catch [concat exec diff $diff_options $ok_file $log_file]] } { + if { $test_status == "" } { + test_failed $test "fail" + } + } + } else { + if { $test_status == "" } { + test_failed $test "no_ok" } } - return "ERROR $error" + } else { + # Log file doesn't exist, likely an error + test_failed $test "error" "*ERROR* no log file" } - file delete $run_file + } + if { $test_status == {} } { + append test_status "pass" + } + if { $report_stats } { + append test_status " [test_stats_summary $test]" + } + return $test_status +} + +proc test_exit_code { test } { + # Read exit code + set test_error "" + set exit_code_file [test_exit_code_file $test] + if { [file exists $exit_code_file] } { + set exit_code_ch [open $exit_code_file "r"] + set exit_code [string trim [read $exit_code_ch]] + close $exit_code_ch + + if { [string is integer $exit_code] } { + return $exit_code + } + } + return 0 +} + +proc test_stats_summary { test } { + if { ![catch {open [test_stats_file $test] r} stream] } { + gets $stream stats + close $stream + + set elapsed_time [lindex $stats 0] + set user_time [lindex $stats 1] + set memory [lindex $stats 2] + if { [string is double $elapsed_time] } { + set elapsed [format "%.1fe" $elapsed_time] + } else { + set elapsed "?" + } + if { [string is double $user_time] } { + set user [format "%.1fu" $user_time] + } else { + set user "?" + } + if { [string is double $memory] } { + set mem [format "%.0fmb" [expr $memory * 1e-6]] + } else { + set mem "?" + } + return "$elapsed $user $mem" + } else { return "" } } -proc run_test_valgrind { test cmd_file log_file } { - global app_path app_options valgrind_options result_dir errorCode +proc test_failed { test reason } { + global errors test_status failed_tests - set vg_cmd_file [test_valgrind_cmd_file $test] - set vg_stream [open $vg_cmd_file "w"] - puts $vg_stream "cd [file dirname $cmd_file]" - puts $vg_stream "include [file tail $cmd_file]" - puts $vg_stream "sta::delete_all_memory" - close $vg_stream - - set cmd [concat exec valgrind $valgrind_options \ - $app_path $app_options $vg_cmd_file >& $log_file] - set error_msg "" - if { [catch $cmd] } { - set error_msg "ERROR [lindex $errorCode 3]" + if { $reason == "error" } { + set test_status "*ERROR*" + } elseif { $reason == "no_cmd" } { + set test_status "*NO CMD FILE*" + } elseif { $reason == "memory" } { + set test_status "*MEMORY*" + } elseif { $reason == "leak" } { + set test_status "*LEAK*" + } elseif { $reason == "fail" } { + set test_status "*FAIL*" + } elseif { $reason == "no_ok" } { + set test_status "*NO OK FILE*" + } else { + error "unknown test failure reason $reason" + } + lappend failed_tests $test + incr errors($reason) +} + +proc write_failure_file {} { + global failure_file failed_tests + + set ch [open $failure_file "w"] + foreach test $failed_tests { + puts $ch $test + } + close $ch +} + +proc write_diff_file {} { + global diff_file diff_options failed_tests + + foreach test $failed_tests { + set log_file [test_log_file $test] + set ok_file [test_ok_file $test] + catch [concat exec diff $diff_options $ok_file $log_file >> $diff_file] } - file delete $vg_cmd_file - set error_msg [concat $error_msg [cleanse_valgrind_logfile $test $log_file]] - return $error_msg } # Error messages can be found in "valgrind/memcheck/mc_errcontext.c". @@ -363,25 +477,26 @@ set valgrind_shared_lib_failure_regexp "No malloc'd blocks -- no leaks are possi # Scan the log file to separate valgrind notifications and check for # valgrind errors. -proc cleanse_valgrind_logfile { test log_file } { +proc cleanse_valgrind_logfile { test } { global valgrind_mem_regexp valgrind_leak_regexp global valgrind_shared_lib_failure_regexp - global valgrind_shared_lib_failure + global valgrind_shared_lib_failure error + set log_file [test_log_file $test] set tmp_file [test_tmp_file $test] set valgrind_log_file [test_valgrind_file $test] file copy -force $log_file $tmp_file set tmp [open $tmp_file "r"] set log [open $log_file "w"] set valgrind [open $valgrind_log_file "w"] - set leaks 0 + set leak 0 set mem_errors 0 gets $tmp line while { ![eof $tmp] } { if {[regexp "^==" $line]} { puts $valgrind $line if {[regexp $valgrind_leak_regexp $line]} { - set leaks 1 + set leak 1 } if {[regexp $valgrind_mem_regexp $line]} { set mem_errors 1 @@ -399,16 +514,12 @@ proc cleanse_valgrind_logfile { test log_file } { close $log close $tmp close $valgrind - file delete $tmp_file - set errors {} if { $mem_errors } { - lappend errors "MEMORY" - } - if { $leaks } { - lappend errors "LEAK" + test_failed $test "memory" + } elseif { $leak } { + test_failed $test "leak" } - return $errors } ################################################################ @@ -533,11 +644,6 @@ proc test_tmp_file { test } { return [file join $result_dir $test.tmp] } -proc test_valgrind_cmd_file { test } { - global result_dir - return [file join $result_dir $test.vg_cmd] -} - proc test_valgrind_file { test } { global result_dir return [file join $result_dir $test.valgrind] @@ -563,6 +669,11 @@ proc test_sys_core_file { test pid } { return [file join [test_cmd_dir $test] "core"] } +proc test_exit_code_file { test } { + global result_dir + return [file join $result_dir "$test.exitcode"] +} + ################################################################ # Local Variables: From 420519c5d9f25a9a64dd8bc51f3a5f52b2ff973d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 21 Mar 2026 12:32:10 -0700 Subject: [PATCH 105/181] write_path_spice Signed-off-by: James Cherry --- doc/ChangeLog.txt | 7 + doc/OpenSTA.fodt | 3393 +++++++++++++++++++++--------------------- doc/OpenSTA.pdf | Bin 1419896 -> 1443585 bytes spice/WriteSpice.tcl | 19 +- 4 files changed, 1717 insertions(+), 1702 deletions(-) diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index bebf05b65..b62382469 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -4,6 +4,13 @@ OpenSTA Timing Analyzer Release Notes This file summarizes user visible changes for each release. See ApiChangeLog.txt for changes to the STA api. +2026/03/23 +---------- + +The write_path_spice command -spice_directory has been changed to +-spice_file, which is a prefix for the spice filenames. Successive +paths are written in files name _.sp. + Release 3.0.1 2026/03/12 ------------------------ diff --git a/doc/OpenSTA.fodt b/doc/OpenSTA.fodt index 56bfdfc27..e0d25e489 100644 --- a/doc/OpenSTA.fodt +++ b/doc/OpenSTA.fodt @@ -1,30 +1,31 @@ - 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 + Parallax STA documentationJames Cherry5282025-03-17T12:59:52.4638705382010-07-31T21:07:002026-03-21T12:31:10.522816000P123DT2H14M54SLibreOffice/26.2.1.2$MacOSX_AARCH64 LibreOffice_project/8399f6259d8c87f40e7255cdb3c9b958f5e08948PDF files: James CherryJames Cherry12.00000falsefalsefalsefalse - 138335 - 12 - 21890 - 20512 + 2413538 + 0 + 30134 + 17268 true false view2 - 5793 - 99330 - 12 - 138335 - 21900 - 158845 + 18244 + 2424328 + 0 + 2413538 + 30133 + 2430805 0 1 false 90 false false + 172,145,1382,863;1;,,,; true true false @@ -45,11 +46,13 @@ false false + true false false false false false + false false false false @@ -89,7 +92,7 @@ false true false - 27526206 + 27556207 0 false @@ -4029,8 +4032,8 @@ - - + + @@ -4141,7 +4144,7 @@ - + @@ -4200,7 +4203,7 @@ - + @@ -4824,540 +4827,544 @@ + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -5404,940 +5411,940 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -6607,24 +6614,24 @@ 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. @@ -6633,72 +6640,72 @@ 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 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> + 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) + 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 + 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. + 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. + 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. + 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 + 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 + 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_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. + 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. + 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. + 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. + 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. + 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 + Commands - all_clocks + all_clocks @@ -6711,7 +6718,7 @@ - all_inputs + all_inputs [-no_clocks] @@ -6732,7 +6739,7 @@ - all_outputs + all_outputs @@ -6746,15 +6753,15 @@ - 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. @@ -6817,21 +6824,21 @@ - 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. @@ -6839,7 +6846,7 @@ - -unconstrained_endpoints + -unconstrained_endpoints Check path endpoints for timing constraints (timing check or set_output_delay). @@ -6847,7 +6854,7 @@ - -multiple_clock + -multiple_clock Check register/latch clock pins for multiple clocks. @@ -6864,7 +6871,7 @@ - -no_input_delay + -no_input_delay Check for inputs that do not have a set_input_delay command. @@ -6887,16 +6894,16 @@ - 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 @@ -6924,7 +6931,7 @@ - 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. @@ -6965,7 +6972,7 @@ -add - Add this clock to the clocks on pin_list. + Add this clock to the clocks on pin_list. @@ -6978,7 +6985,7 @@ 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 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. @@ -6991,10 +6998,10 @@ - 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 @@ -7010,36 +7017,36 @@ -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). @@ -7056,15 +7063,15 @@ - -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. @@ -7075,7 +7082,7 @@ -add - Add this clock to the existing clocks on pin_list. + Add this clock to the existing clocks on pin_list. @@ -7083,7 +7090,7 @@ pin_list - A list of pins driven by the generated clock. + A list of pins driven by the generated clock. @@ -7108,10 +7115,10 @@ - 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 @@ -7121,7 +7128,7 @@ - current_design + current_design [design] @@ -7134,7 +7141,7 @@ - current_instance + current_instance [instance] @@ -7155,23 +7162,23 @@ - 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. @@ -7186,18 +7193,18 @@ - 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 @@ -7215,7 +7222,7 @@ - delete_from_list + delete_from_list list objects @@ -7226,7 +7233,7 @@ list - A list of objects. + A list of objects. @@ -7244,10 +7251,10 @@ - delete_generated_clock + delete_generated_clock - [-all] clocks + [-all] clocks @@ -7265,7 +7272,7 @@ - delete_instance + delete_instance instance @@ -7276,7 +7283,7 @@ instance - Instance to delete. + Instance to delete. @@ -7286,7 +7293,7 @@ - delete_net + delete_net net @@ -7294,7 +7301,7 @@ - net + net Net to delete. @@ -7307,10 +7314,10 @@ - disconnect_pin + disconnect_pin - netport | pin | -all + netport | pin | -all @@ -7353,7 +7360,7 @@ - elapsed_run_time + elapsed_run_time @@ -7366,83 +7373,83 @@ - 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. + Return rising paths through a list of instances, pins or nets. - -fall_through through_list + -fall_through through_list - Return falling paths through a list of instances, pins or nets. + Return falling paths through a list of instances, pins or nets. - -to to_list + -to to_list - Return paths to a list of clocks, instances, ports or pins. + Return paths to a list of clocks, instances, ports or pins. - -rise_to to_list + -rise_to to_list - Return rising paths to a list of clocks, instances, ports or pins. + Return rising paths to a list of clocks, instances, ports or pins. - -fall_to to_list + -fall_to to_list - Return falling paths to a list of clocks, instances, ports or pins. + Return falling paths to a list of clocks, instances, ports or pins. @@ -7450,7 +7457,7 @@ -unconstrained - Report unconstrained paths also. + Report unconstrained paths also. @@ -7511,18 +7518,18 @@ - -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. @@ -7535,7 +7542,7 @@ - -scene scene + -scene scene Return paths for one process corner. @@ -7543,18 +7550,18 @@ - -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. @@ -7562,15 +7569,15 @@ -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. @@ -7580,10 +7587,10 @@ - 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] @@ -7596,18 +7603,18 @@ - -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. @@ -7615,7 +7622,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -7636,7 +7643,7 @@ - -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. @@ -7657,10 +7664,10 @@ - get_clocks + get_clocks - [-regexp][-nocase][-filter expr][-quiet]patterns + [-regexp][-nocase][-filter expr][-quiet]patterns @@ -7668,7 +7675,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -7682,10 +7689,10 @@ - -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. @@ -7711,15 +7718,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. @@ -7727,7 +7734,7 @@ - -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. @@ -7735,7 +7742,7 @@ - -only_cells + -only_cells Return the instances connected to the pins in the fanin. @@ -7743,7 +7750,7 @@ - -startpoints_only + -startpoints_only Only return pins that are startpoints. @@ -7751,7 +7758,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7767,15 +7774,15 @@ - -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. @@ -7783,7 +7790,7 @@ - -trace_arcs all + -trace_arcs all Trace through all arcs, including disabled ones. @@ -7797,15 +7804,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. @@ -7813,7 +7820,7 @@ - -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. @@ -7821,7 +7828,7 @@ - -only_cells + -only_cells Return the instances connected to the pins in the fanout. @@ -7829,7 +7836,7 @@ - -endpoints_only + -endpoints_only Only return pins that are endpoints. @@ -7837,7 +7844,7 @@ - -level level_count + -level level_count Only return pins within level_count instance traversals. @@ -7853,15 +7860,15 @@ - -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. @@ -7869,7 +7876,7 @@ - -trace_arcs all + -trace_arcs all Trace through all arcs, including disabled ones. @@ -7882,7 +7889,7 @@ - get_full_name + get_full_name object @@ -7907,12 +7914,12 @@ 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. @@ -7923,15 +7930,15 @@ -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. @@ -7939,7 +7946,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -7960,7 +7967,7 @@ - patterns + patterns A list of library cell name patterns of the form library_name/cell_name. @@ -7973,18 +7980,18 @@ - 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. @@ -7992,16 +7999,16 @@ -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. @@ -8009,7 +8016,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -8043,18 +8050,18 @@ - 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. @@ -8062,7 +8069,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -8090,17 +8097,17 @@ - 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] @@ -8116,15 +8123,15 @@ -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. @@ -8132,7 +8139,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -8174,7 +8181,7 @@ - get_name + get_name object @@ -8196,10 +8203,10 @@ - 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] @@ -8215,15 +8222,15 @@ -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. @@ -8247,7 +8254,7 @@ -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. @@ -8267,19 +8274,19 @@ - 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. @@ -8287,7 +8294,7 @@ -regexp - Use regular expression matching instead of glob pattern matching. + Use regular expression matching instead of glob pattern matching. @@ -8308,10 +8315,10 @@ - -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. @@ -8329,18 +8336,18 @@ - 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 @@ -8348,7 +8355,7 @@ 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. @@ -8362,28 +8369,28 @@ The properties for different objects types are shown below. cell (SDC lib_cell) - base_namefilenamefull_namelibraryname + base_namefilenamefull_namelibraryname clock - full_nameis_generatedis_propagatedis_virtualnameperiodsources + 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 + 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 + filename (Liberty library only)namefull_name net - full_namename + 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 + 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 + activityslew_max_fallslew_max_riseslew_min_fallslew_min_risedirectionfull_nameliberty_portnameslack_maxslack_max_fallslack_max_riseslack_minslack_min_fallslack_min_rise point (PathRef) arrivalpinrequiredslack @@ -8391,15 +8398,15 @@ - get_scenes + get_scenes - [-mode mode_name]scene_name + [-mode mode_name]scene_name - mode_name + mode_name Get the scenes for mode_name. @@ -8407,7 +8414,7 @@ - scene_name + scene_name A scene name pattern. @@ -8420,61 +8427,61 @@ - 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. @@ -8482,7 +8489,7 @@ - -weight weight + -weight weight Not supported. @@ -8490,7 +8497,7 @@ - -critical_range range + -critical_range range Not supported. @@ -8498,74 +8505,74 @@ - -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. @@ -8583,15 +8590,15 @@ - 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. @@ -8599,7 +8606,7 @@ - -verbose|-v + -verbose|-v Print each command before evaluating it as well as the result it returns. @@ -8615,7 +8622,7 @@ - > log_filename + > log_filename Redirect command output to log_filename. @@ -8623,7 +8630,7 @@ - >> log_filename + >> log_filename Redirect command output and append log_filename. @@ -8631,29 +8638,29 @@ 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. + 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. @@ -8668,7 +8675,7 @@ - make_instance + make_instance inst_pathlib_cell @@ -8692,13 +8699,13 @@ - 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 @@ -8719,18 +8726,18 @@ - 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. @@ -8754,12 +8761,12 @@ 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. @@ -8768,18 +8775,18 @@ - 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 ‘/’. @@ -8787,26 +8794,26 @@ 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. @@ -8838,15 +8845,15 @@ - read_sdf + read_sdf - [-scene scene][-unescaped_dividers]filename + [-scene scene][-unescaped_dividers]filename - scene + scene Scene delays to annotate. @@ -8854,7 +8861,7 @@ - -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. @@ -8869,7 +8876,7 @@ - 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. @@ -8879,10 +8886,10 @@ - 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 @@ -8898,7 +8905,7 @@ path - Hierarchical block instance path to annotate with parasitics. + Hierarchical block instance path to annotate with parasitics. @@ -8914,7 +8921,7 @@ ‑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. @@ -8926,29 +8933,29 @@ - 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 + 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. + 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 ‘/’. @@ -8967,17 +8974,17 @@ 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 @@ -8992,8 +8999,8 @@ - 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. @@ -9001,16 +9008,16 @@ - replace_cell + replace_cell - instance_listreplacement_cell + instance_listreplacement_cell - instance_list + instance_list A list of instances to swap the cell. @@ -9018,57 +9025,57 @@ - replacement_cell + replacement_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. @@ -9076,7 +9083,7 @@ - -hold + -hold Report annotated hold checks. @@ -9084,7 +9091,7 @@ - -recovery + -recovery Report annotated recovery checks. @@ -9092,7 +9099,7 @@ - -removal + -removal Report annotated removal checks. @@ -9100,7 +9107,7 @@ - -nochange + -nochange Report annotated nochange checks. @@ -9108,7 +9115,7 @@ - -width + -width Report annotated width checks. @@ -9116,7 +9123,7 @@ - -period + -period Report annotated period checks. @@ -9124,7 +9131,7 @@ - -max_skew + -max_skew Report annotated max skew checks. @@ -9133,26 +9140,26 @@ - -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. @@ -9164,21 +9171,21 @@ - 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. @@ -9186,7 +9193,7 @@ - -net + -net Report annotated internal net delays. @@ -9194,7 +9201,7 @@ - -from_in_ports + -from_in_ports Report annotated delays from input ports. @@ -9202,7 +9209,7 @@ - -to_out_ports + -to_out_ports Report annotated delays to output ports. @@ -9210,26 +9217,26 @@ - -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. @@ -9241,90 +9248,90 @@ - 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. @@ -9332,7 +9339,7 @@ -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. @@ -9393,7 +9400,7 @@ - -group_path_count path_count + -group_path_count path_count The number of paths to report in each path group. The default is 1. @@ -9401,7 +9408,7 @@ - -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. @@ -9412,15 +9419,15 @@ ‑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. @@ -9428,7 +9435,7 @@ 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. @@ -9458,10 +9465,10 @@ - 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. @@ -9514,10 +9521,10 @@ - -format json + -format json - Report in json format. -fields is ignored. + Report in json format. -fields is ignored. @@ -9525,7 +9532,7 @@ fields - List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance + List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance @@ -9545,7 +9552,7 @@ - The report_checks command reports paths in the design. Paths are reported in groups by capture clock, unclocked path delays, gated clocks and unconstrained. + 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. @@ -9553,10 +9560,10 @@ - report_check_types + report_check_types - [-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][-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] @@ -9564,12 +9571,12 @@ scenes - Report checks for some scenes. 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. @@ -9585,18 +9592,18 @@ - -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. @@ -9604,7 +9611,7 @@ fields - List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance + List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr|variance @@ -9698,7 +9705,7 @@ - -digits digits + -digits digits The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. @@ -9719,10 +9726,10 @@ - report_clock_latency + report_clock_latency - [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] + [-clocks clocks][-scenes scenes][-include_internal_latency][-digits digits] @@ -9730,7 +9737,7 @@ clocks - The clocks to report. The default value is all c + The clocks to report. The default value is all c @@ -9738,7 +9745,7 @@ 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. @@ -9764,10 +9771,10 @@ - report_clock_min_period + report_clock_min_period - [-clocks clocks][-scenes scenes][-include_port_paths] + [-clocks clocks][-scenes scenes][-include_port_paths] @@ -9787,16 +9794,16 @@ - 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] @@ -9814,15 +9821,15 @@ - 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. @@ -9841,7 +9848,7 @@ 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. @@ -9849,7 +9856,7 @@ scenes - Report clocks for scenes. The default value is all scenes. + Report clocks for scenes. The default value is all scenes. @@ -9862,23 +9869,23 @@ - -digits digits + -digits digits 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] @@ -9886,7 +9893,7 @@ 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. @@ -9894,7 +9901,7 @@ to_pin - Report delay calculations for timing arcs to instance output pin to_pin. + Report delay calculations for timing arcs to instance output pin to_pin. @@ -9903,28 +9910,28 @@ scene - Report paths for process scene. The -scene keyword is required if more than one process corner is defined. + Report paths for process scene. The -scene keyword is required if more than one process corner is defined. - -min + -min - Report delay calculation for min delays. + Report delay calculation for min delays. - -max + -max - Report delay calculation for max delays. + Report delay calculation for max delays. - -digits digits + -digits digits The number of digits after the decimal point to report. The default is sta_report_default_digits. @@ -9937,7 +9944,7 @@ - report_disabled_edges + report_disabled_edges @@ -9945,16 +9952,16 @@ 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][-report_variation][-digits digits] + [-from from_pin][-to to_pin][-report_variation][-digits digits] @@ -9962,7 +9969,7 @@ from_pin - Report edges/timing arcs from pin from_pin. + Report edges/timing arcs from pin from_pin. @@ -9970,12 +9977,12 @@ to_pin - Report edges/timing arcs to pin to_pin. + Report edges/timing arcs to pin to_pin. - -report_variation + -report_variation @@ -9986,21 +9993,21 @@ 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 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] @@ -10018,10 +10025,10 @@ - report_lib_cell + report_lib_cell - cell_name[> filename][>> filename] + cell_name[> filename][>> filename] @@ -10033,24 +10040,24 @@ - 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 - 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. @@ -10062,77 +10069,77 @@ - 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 + -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. + 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. - -highest_power_instances count + -highest_power_instances count - Report the power for the count highest power instances. + Report the power for the count highest power instances. - -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 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][-report_variation][-digits digits]pin + [-scenes scenes][-report_variation][-digits digits]pin @@ -10140,133 +10147,133 @@ scenes - Report slews for process for scenes process corners. + Report slews for process for scenes process corners. - -report_variation + -report_variation - Report SSTA distribution parameters. + Report SSTA distribution parameters. - -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. - pin + 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 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] @@ -10275,7 +10282,7 @@ -max - Report the worst max/setup slack. + Report the worst max/setup slack. @@ -10283,12 +10290,12 @@ -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. @@ -10301,10 +10308,10 @@ - set_assigned_check + 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|-hold|-recovery|-removal[-rise][-fall][-scene scene][-min][-max][-from from_pins][-to to_pins][-clock rise|fall][-cond sdf_cond][-worst]margin @@ -10312,7 +10319,7 @@ -setup - Annotate setup timing checks. + Annotate setup timing checks. @@ -10320,7 +10327,7 @@ -hold - Annotate hold timing checks. + Annotate hold timing checks. @@ -10328,7 +10335,7 @@ -recovery - Annotate recovery timing checks. + Annotate recovery timing checks. @@ -10336,7 +10343,7 @@ -removal - Annotate removal timing checks. + Annotate removal timing checks. @@ -10357,10 +10364,10 @@ - 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. @@ -10398,10 +10405,10 @@ - -clock rise|fall + -clock rise|fall - The timing check clock pin transition. + The timing check clock pin transition. @@ -10409,7 +10416,7 @@ margin - The timing check margin. + The timing check margin. @@ -10419,10 +10426,10 @@ - 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 @@ -10459,10 +10466,10 @@ - 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. @@ -10513,10 +10520,10 @@ - set_assigned_transition + set_assigned_transition - [-rise][-fall][-scene scene][-min][-max]slewpin_list + [-rise][-fall][-scene scene][-min][-max]slewpin_list @@ -10537,10 +10544,10 @@ - scene + scene - Annotate delays for scene. + Annotate delays for scene. @@ -10582,10 +10589,10 @@ - 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 @@ -10598,22 +10605,22 @@ 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][-hold hold_time][-rise][-fall][-high][-low][objects] - -setup setup_time + -setup setup_time Clock enable setup margin. @@ -10621,7 +10628,7 @@ - -hold hold_time + -hold hold_time Clock enable hold margin. @@ -10678,18 +10685,18 @@ - 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. @@ -10697,7 +10704,7 @@ -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. @@ -10706,7 +10713,7 @@ -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. + 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. @@ -10714,7 +10721,7 @@ -asynchronous - The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. + The clock groups are asynchronous. Paths between clock groups are considered for noise analysis. @@ -10727,7 +10734,7 @@ - clocks + clocks A list of clocks in the group. @@ -10740,15 +10747,15 @@ - 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. @@ -10756,7 +10763,7 @@ - -clock clock + -clock clock If multiple clocks are defined at a pin this use this option to specify the latency for a specific clock. @@ -10811,16 +10818,16 @@ - 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 @@ -10828,7 +10835,7 @@ -rise - Set the transition time for the rising edge of the clock. + Set the transition time for the rising edge of the clock. @@ -10836,7 +10843,7 @@ -fall - Set the transition time for the falling edge of the clock. + Set the transition time for the falling edge of the clock. @@ -10844,7 +10851,7 @@ -min - Set the min transition time. + Set the min transition time. @@ -10852,7 +10859,7 @@ -max - Set the min transition time. + Set the min transition time. @@ -10878,26 +10885,26 @@ - 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. @@ -10905,7 +10912,7 @@ -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. @@ -10913,7 +10920,7 @@ -fall - Inter-clock target clock rise edge, alternative to ‑fall_to. + Inter-clock target clock rise edge, alternative to ‑fall_to. @@ -10921,7 +10928,7 @@ -setup - uncertainty is for setup checks. + uncertainty is for setup checks. @@ -10930,12 +10937,12 @@ -hold - uncertainty is for hold checks. + uncertainty is for hold checks. - uncertainty + uncertainty Clock uncertainty. @@ -10950,81 +10957,81 @@ - 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 An example of the set_units command is shown below. @@ -11034,15 +11041,15 @@ - 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. @@ -11050,7 +11057,7 @@ - -to to_pin + -to to_pin A pin that the setup/hold check is applied to. @@ -11074,7 +11081,7 @@ - -clock clock + -clock clock The setup/hold check clock. @@ -11095,7 +11102,7 @@ - set_disable_inferred_clock_gating + set_disable_inferred_clock_gating objects @@ -11110,32 +11117,32 @@ - 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 - + @@ -11143,12 +11150,12 @@ 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 @@ -11161,10 +11168,10 @@ - set_drive + set_drive - [-rise][-fall][-max][-min]resistanceports + [-rise][-fall][-max][-min]resistanceports @@ -11172,7 +11179,7 @@ -rise - Set the drive rise resistance. + Set the drive rise resistance. @@ -11180,7 +11187,7 @@ -fall - Set the drive fall resistance. + Set the drive fall resistance. @@ -11188,7 +11195,7 @@ -max - Set the maximum resistance. + Set the maximum resistance. @@ -11196,7 +11203,7 @@ -min - Set the minimum resistance. + Set the minimum resistance. @@ -11209,7 +11216,7 @@ - ports + ports A list of ports. @@ -11222,26 +11229,26 @@ - 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. @@ -11249,7 +11256,7 @@ -rise - Set the driving cell for a rising edge. + Set the driving cell for a rising edge. @@ -11258,7 +11265,7 @@ -fall - Set the driving cell for a falling edge. + Set the driving cell for a falling edge. @@ -11266,7 +11273,7 @@ -max - Set the driving cell for max delays. + Set the driving cell for max delays. @@ -11274,12 +11281,12 @@ -min - Set the driving cell for min delays. + Set the driving cell for min delays. - -pin pin + -pin pin The output port of the driving cell. @@ -11287,31 +11294,31 @@ - -from_pin from_pin + -from_pin from_pin - Use timing arcs from from_pin to the output pin. + Use timing arcs from from_pin to the output pin. - -input_transition_rise trans_rise + -input_transition_rise trans_rise - The transition time for a rising input at from_pin. + The transition time for a rising input at from_pin. - -input_transition_fall trans_fall + -input_transition_fall trans_fall - The transition time for a falling input at from_pin. + The transition time for a falling input at from_pin. - ports + ports A list of ports. @@ -11324,10 +11331,10 @@ - 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] @@ -11335,7 +11342,7 @@ -setup - Apply to setup checks. + Apply to setup checks. @@ -11343,7 +11350,7 @@ -hold - Apply to hold checks. + Apply to hold checks. @@ -11351,7 +11358,7 @@ -rise - Apply to rising path edges. + Apply to rising path edges. @@ -11359,7 +11366,7 @@ -fall - Apply to falling path edges. + Apply to falling path edges. @@ -11373,7 +11380,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -11381,7 +11388,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11389,7 +11396,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11405,10 +11412,10 @@ - set_fanout_load + set_fanout_load - fanoutport_list + fanoutport_list @@ -11418,7 +11425,7 @@ - set_hierarchy_separator + set_hierarchy_separator separator @@ -11439,7 +11446,7 @@ - set_ideal_latency + set_ideal_latency [-rise] [-fall] [-min] [-max] delay objects @@ -11452,7 +11459,7 @@ - set_ideal_network + set_ideal_network [-no_propagation] objects @@ -11465,7 +11472,7 @@ - set_ideal_transition + set_ideal_transition [-rise] [-fall] [-min] [-max] transition_time objects @@ -11478,10 +11485,10 @@ - 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 @@ -11489,7 +11496,7 @@ -rise - Set the arrival time for the rising edge of the input. + Set the arrival time for the rising edge of the input. @@ -11497,7 +11504,7 @@ -fall - Set the arrival time for the falling edge of the input. + Set the arrival time for the falling edge of the input. @@ -11505,7 +11512,7 @@ -max - Set the maximum arrival time. + Set the maximum arrival time. @@ -11513,15 +11520,15 @@ -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. @@ -11529,12 +11536,12 @@ -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. @@ -11545,7 +11552,7 @@ -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. @@ -11553,7 +11560,7 @@ -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. @@ -11581,22 +11588,22 @@ - 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 @@ -11604,7 +11611,7 @@ -rise - Set the rising edge transition. + Set the rising edge transition. @@ -11612,7 +11619,7 @@ -fall - Set the falling edge transition. + Set the falling edge transition. @@ -11620,7 +11627,7 @@ -max - Set the minimum transition time. + Set the minimum transition time. @@ -11628,7 +11635,7 @@ -min - Set the maximum transition time. + Set the maximum transition time. @@ -11654,10 +11661,10 @@ - set_level_shifter_strategy + set_level_shifter_strategy - [-rule rule_type] + [-rule rule_type] @@ -11668,10 +11675,10 @@ - set_level_shifter_threshold + set_level_shifter_threshold - [-voltage voltage] + [-voltage voltage] @@ -11681,10 +11688,10 @@ - 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 @@ -11692,7 +11699,7 @@ -rise - Set the external port rising capacitance (ports only). + Set the external port rising capacitance (ports only). @@ -11700,7 +11707,7 @@ -fall - Set the external port falling capacitance (ports only). + Set the external port falling capacitance (ports only). @@ -11708,7 +11715,7 @@ -max - Set the max capacitance. + Set the max capacitance. @@ -11716,7 +11723,7 @@ -min - Set the min capacitance. + Set the min capacitance. @@ -11724,7 +11731,7 @@ -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. @@ -11760,24 +11767,24 @@ - 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. @@ -11790,57 +11797,57 @@ - set_logic_one + set_logic_one - port_list + port_list - port_pin_list + port_pin_list 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. - 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 @@ -11853,15 +11860,15 @@ - set_max_capacitance + set_max_capacitance - capacitanceobjects + capacitanceobjects - capacitance + capacitance @@ -11870,7 +11877,7 @@ - objects + objects List of ports or cells. @@ -11883,10 +11890,10 @@ - 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 @@ -11894,7 +11901,7 @@ -rise - Set max delay for rising paths. + Set max delay for rising paths. @@ -11902,12 +11909,12 @@ -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. @@ -11915,7 +11922,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -11923,7 +11930,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -11939,10 +11946,10 @@ - -probe + -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). @@ -11969,10 +11976,10 @@ - set_max_dynamic_power + set_max_dynamic_power - power [unit] + power [unit] @@ -11982,15 +11989,15 @@ - set_max_fanout + set_max_fanout - fanoutobjects + fanoutobjects - fanout + fanout @@ -11998,7 +12005,7 @@ - objects + objects List of ports or cells. @@ -12011,10 +12018,10 @@ - set_max_leakage_power + set_max_leakage_power - power [unit] + power [unit] @@ -12024,15 +12031,15 @@ - set_max_time_borrow + set_max_time_borrow - delayobjects + delayobjects - delay + delay The maximum time the latches can borrow. @@ -12040,76 +12047,76 @@ - objects + objects 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. - 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. @@ -12117,23 +12124,23 @@ - set_min_capacitance + set_min_capacitance - capacitanceobjects + capacitanceobjects - capacitance + capacitance - Minimum capacitance. + Minimum capacitance. - objects + objects List of ports or cells. @@ -12146,10 +12153,10 @@ - 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 @@ -12157,7 +12164,7 @@ -rise - Set min delay for rising paths. + Set min delay for rising paths. @@ -12166,12 +12173,12 @@ -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. @@ -12179,7 +12186,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12187,7 +12194,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12203,10 +12210,10 @@ - -probe + -probe - Do not break paths at internal pins (non startpoints). + Do not break paths at internal pins (non startpoints). @@ -12222,7 +12229,7 @@ delay - The minimum delay. + The minimum delay. @@ -12233,10 +12240,10 @@ - set_min_pulse_width + set_min_pulse_width - [-high][-low]min_widthobjects + [-high][-low]min_widthobjects @@ -12257,7 +12264,7 @@ - min_width + min_width @@ -12265,7 +12272,7 @@ - objects + objects List of pins, instances or clocks. @@ -12278,24 +12285,24 @@ - 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 @@ -12303,7 +12310,7 @@ -setup - Set cycle count for setup checks. + Set cycle count for setup checks. @@ -12311,7 +12318,7 @@ -hold - Set cycle count for hold checks. + Set cycle count for hold checks. @@ -12319,7 +12326,7 @@ -rise - Set cycle count for rising path edges. + Set cycle count for rising path edges. @@ -12327,7 +12334,7 @@ -fall - Set cycle count for falling path edges. + Set cycle count for falling path edges. @@ -12348,7 +12355,7 @@ - -from from_list + -from from_list A list of clocks, instances, ports or pins. @@ -12356,7 +12363,7 @@ - -through through_list + -through through_list A list of instances, pins or nets. @@ -12364,7 +12371,7 @@ - -to to_list + -to to_list A list of clocks, instances, ports or pins. @@ -12394,15 +12401,15 @@ - 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. @@ -12410,7 +12417,7 @@ - -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. @@ -12418,7 +12425,7 @@ - ‑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. @@ -12426,7 +12433,7 @@ - -library lib + -library lib The name of the library that contains condition. @@ -12442,7 +12449,7 @@ - -min min_condition + -min min_condition The operating condition to use for min paths and hold checks. @@ -12450,7 +12457,7 @@ - -max max_condition + -max max_condition The operating condition to use for max paths and setup checks. @@ -12458,18 +12465,18 @@ - -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. @@ -12479,10 +12486,10 @@ - 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 @@ -12491,7 +12498,7 @@ -rise - Set the output delay for the rising edge of the input. + Set the output delay for the rising edge of the input. @@ -12499,7 +12506,7 @@ -fall - Set the output delay for the falling edge of the input. + Set the output delay for the falling edge of the input. @@ -12507,7 +12514,7 @@ -max - Set the maximum output delay. + Set the maximum output delay. @@ -12515,15 +12522,15 @@ -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. @@ -12531,15 +12538,15 @@ -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. @@ -12547,7 +12554,7 @@ -add_delay - Add this output delay to any existing output delays. + Add this output delay to any existing output delays. @@ -12555,7 +12562,7 @@ delay - The external delay to the check clocked by clock. + The external delay to the check clocked by clock. @@ -12567,33 +12574,33 @@ - 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. @@ -12613,21 +12620,21 @@ - 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. @@ -12643,7 +12650,7 @@ - -input_ports input_ports + -input_ports input_ports Set the input port activity/duty. @@ -12651,7 +12658,7 @@ - -pins pins + -pins pins Set the pin activity/duty. @@ -12659,46 +12666,46 @@ - -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 @@ -12720,11 +12727,11 @@ - set_pvt + set_pvt - [-min][-max][-process process][-voltage voltage] - [-temperature temperature]instances + [-min][-max][-process process][-voltage voltage] + [-temperature temperature]instances @@ -12732,7 +12739,7 @@ -min - Set the PVT values for max delays. + Set the PVT values for max delays. @@ -12740,12 +12747,12 @@ -max - Set the PVT values for min delays. + Set the PVT values for min delays. - -process process + -process process A process value (float). @@ -12753,7 +12760,7 @@ - -voltage voltage + -voltage voltage A voltage value (float). @@ -12761,7 +12768,7 @@ - -temperature temperature + -temperature temperature A temperature value (float). @@ -12782,59 +12789,59 @@ - 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. @@ -12842,7 +12849,7 @@ clocks - A list of clocks to apply the sense. + A list of clocks to apply the sense. @@ -12854,37 +12861,37 @@ - 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. @@ -12892,7 +12899,7 @@ - -late + -late Derate late (max) paths. @@ -12900,7 +12907,7 @@ - -clock + -clock Derate paths in the clock network. @@ -12908,7 +12915,7 @@ - -data + -data Derate data paths. @@ -12916,7 +12923,7 @@ - -net_delay + -net_delay Derate net (interconnect) delays. @@ -12925,7 +12932,7 @@ - -cell_delay + -cell_delay Derate cell delays. @@ -12933,7 +12940,7 @@ - -cell_check + -cell_check Derate cell timing check margins. @@ -12944,7 +12951,7 @@ derate - The derating factor to apply to delays. + The derating factor to apply to delays. @@ -12963,15 +12970,15 @@ - set_resistance + set_resistance - [-max][-min]resistancenets + [-max][-min]resistancenets - -min + -min The resistance for minimum path delay calculation. @@ -12979,7 +12986,7 @@ - -max + -max The resistance for maximum path delay calculation. @@ -12995,7 +13002,7 @@ - nets + nets A list of nets. @@ -13008,73 +13015,73 @@ - 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 @@ -13087,10 +13094,10 @@ - set_wire_load_mode + set_wire_load_mode - top|enclosed|segmented + top|enclosed|segmented @@ -13124,16 +13131,16 @@ - 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. @@ -13141,7 +13148,7 @@ - -library library + -library library Library to look for model_name. @@ -13178,10 +13185,10 @@ - set_wire_load_selection_group + set_wire_load_selection_group - [-library library][-max][-min]group_name[objects] + [-library library][-max][-min]group_name[objects] @@ -13189,7 +13196,7 @@ library - Library to look for group_name. + Library to look for group_name. @@ -13213,7 +13220,7 @@ group_name - A wire load selection group name. + A wire load selection group name. @@ -13231,28 +13238,28 @@ - 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 @@ -13273,18 +13280,18 @@ - 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). @@ -13302,7 +13309,7 @@ - unset_clock_transition + unset_clock_transition clocks @@ -13323,15 +13330,15 @@ - 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 @@ -13339,7 +13346,7 @@ - -to to_clock + -to to_clock @@ -13366,7 +13373,7 @@ -setup - uncertainty is the setup check uncertainty. + uncertainty is the setup check uncertainty. @@ -13374,12 +13381,12 @@ -hold - uncertainty is the hold uncertainty. + uncertainty is the hold uncertainty. - uncertainty + uncertainty Clock uncertainty. @@ -13400,15 +13407,15 @@ - 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. @@ -13416,7 +13423,7 @@ - -to to_object + -to to_object A pin that the setup/hold check is applied to. @@ -13440,7 +13447,7 @@ - clock + clock The setup/hold check clock. @@ -13453,7 +13460,7 @@ - unset_disable_inferred_clock_gating + unset_disable_inferred_clock_gating objects @@ -13468,16 +13475,16 @@ - 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 @@ -13485,7 +13492,7 @@ from_port - + @@ -13493,7 +13500,7 @@ to_port - + @@ -13512,10 +13519,10 @@ - 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 @@ -13523,7 +13530,7 @@ -rise - Unset the arrival time for the rising edge of the input. + Unset the arrival time for the rising edge of the input. @@ -13531,7 +13538,7 @@ -fall - Unset the arrival time for the falling edge of the input. + Unset the arrival time for the falling edge of the input. @@ -13539,7 +13546,7 @@ -max - Unset the minimum arrival time. + Unset the minimum arrival time. @@ -13547,7 +13554,7 @@ -min - Unset the maximum arrival time. + Unset the maximum arrival time. @@ -13555,7 +13562,7 @@ clock - Unset the arrival time from clock. + Unset the arrival time from clock. @@ -13563,7 +13570,7 @@ -clock_fall - Unset the arrival time from the falling edge of clock + Unset the arrival time from the falling edge of clock @@ -13581,10 +13588,10 @@ - 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 @@ -13633,7 +13640,7 @@ -clock_fall - The arrival time is from the falling edge of clock + The arrival time is from the falling edge of clock @@ -13651,10 +13658,10 @@ - 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] @@ -13662,7 +13669,7 @@ -setup - Unset path exceptions for setup checks. + Unset path exceptions for setup checks. @@ -13670,7 +13677,7 @@ -hold - Unset path exceptions for hold checks. + Unset path exceptions for hold checks. @@ -13678,7 +13685,7 @@ -rise - Unset path exceptions for rising path edges. + Unset path exceptions for rising path edges. @@ -13686,12 +13693,12 @@ -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. @@ -13699,7 +13706,7 @@ - -through through + -through through A list of instances, pins or nets. @@ -13707,7 +13714,7 @@ - -to to + -to to A list of clocks, instances, ports or pins. @@ -13715,67 +13722,67 @@ 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 @@ -13796,41 +13803,41 @@ - 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 @@ -13843,10 +13850,10 @@ - with_output_to_variable + with_output_to_variable - var { commands } + var { commands } @@ -13866,22 +13873,22 @@ - 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_file spice_file-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. @@ -13889,15 +13896,15 @@ - spice_directory + spice_file - Directory for spice to write output files. + Directory and path prefix for spice output files. - lib_subckts_file + lib_subckts_file Cell transistor level subckts. @@ -13905,15 +13912,15 @@ - 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. @@ -13921,7 +13928,7 @@ - ground + ground Ground supply name in voltage_map of the default liberty library. @@ -13929,28 +13936,28 @@ - -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 @@ -13993,15 +14000,15 @@ - 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. @@ -14025,7 +14032,7 @@ - -digits digits + -digits digits The number of digits after the decimal point to report. The default is 4. @@ -14064,75 +14071,75 @@ - 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. + 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. + 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. + 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. @@ -14140,7 +14147,7 @@ - -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. @@ -14148,16 +14155,16 @@ - filename + filename 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. @@ -14166,39 +14173,39 @@ 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. @@ -14206,7 +14213,7 @@ 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. @@ -14214,21 +14221,21 @@ 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. @@ -14238,10 +14245,10 @@ - sta_continue_on_error + sta_continue_on_error - 0|1 + 0|1 @@ -14251,24 +14258,24 @@ - 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 @@ -14278,10 +14285,10 @@ - sta_crpr_enabled + sta_crpr_enabled - 0|1 + 0|1 @@ -14291,10 +14298,10 @@ - sta_dynamic_loop_breaking + sta_dynamic_loop_breaking - 0|1 + 0|1 @@ -14304,23 +14311,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 @@ -14330,10 +14337,10 @@ - sta_internal_bidirect_instance_paths_enabled + sta_internal_bidirect_instance_paths_enabled - 0|1 + 0|1 @@ -14343,10 +14350,10 @@ - sta_pocv_mode + sta_pocv_mode - scalar|normal|skew_normal + scalar|normal|skew_normal @@ -14356,7 +14363,7 @@ - sta_pocv_quartile + sta_pocv_quartile quartile @@ -14369,14 +14376,14 @@ - 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. @@ -14384,36 +14391,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 @@ -14423,10 +14430,10 @@ - sta_preset_clear_arcs_enabled + sta_preset_clear_arcs_enabled - 0|1 + 0|1 @@ -14456,186 +14463,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 5cfe27894ec91f1fedbeb0e26bd26516ad34f4cd..745f20950d59779ddffb08b816a402366c796db9 100644 GIT binary patch literal 1443585 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 o`e;l`$YZsAgra==XDDU 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⪙D#? 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

;m-N8zDjG_}q?KTG5%=%z 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�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

vXDtfG%ix(~<^a$Q9 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 JUF|;ZhreNmA#(r# literal 1419896 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 z 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 h)4ROBFK)Xw(5RC@?cRq-eb2Nwi?1mJ*= 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%0D$ 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 diff --git a/spice/WriteSpice.tcl b/spice/WriteSpice.tcl index 7cbb9be22..a44526624 100644 --- a/spice/WriteSpice.tcl +++ b/spice/WriteSpice.tcl @@ -25,7 +25,7 @@ namespace eval sta { define_cmd_args "write_path_spice" { -path_args path_args\ - -spice_directory spice_directory\ + -spice_file spice_file\ -lib_subckt_file lib_subckts_file\ -model_file model_file\ -power power\ @@ -34,12 +34,13 @@ define_cmd_args "write_path_spice" { -path_args path_args\ proc write_path_spice { args } { parse_key_args "write_path_spice" args \ - keys {-spice_directory -lib_subckt_file -model_file \ + keys {-spice_file -lib_subckt_file -model_file \ -power -ground -path_args -simulator} \ flags {} - if { [info exists keys(-spice_directory)] } { - set spice_dir [file nativename $keys(-spice_directory)] + if { [info exists keys(-spice_file)] } { + set spice_file [file nativename $keys(-spice_file)] + set spice_dir [file dirname $spice_file] if { ![file exists $spice_dir] } { sta_error 1920 "Directory $spice_dir not found." } @@ -50,7 +51,7 @@ proc write_path_spice { args } { sta_error 1922 "Cannot write in $spice_dir." } } else { - sta_error 1923 "No -spice_directory specified." + sta_error 1923 "No -spice_file specified." } if { [info exists keys(-lib_subckt_file)] } { @@ -96,10 +97,10 @@ proc write_path_spice { args } { set path_index 1 foreach path_end $path_ends { set path [$path_end path] - set path_name "path_$path_index" - set spice_file [file join $spice_dir "$path_name.sp"] - set subckt_file [file join $spice_dir "$path_name.subckt"] - write_path_spice_cmd $path $spice_file $subckt_file \ + set path_file "${spice_file}_$path_index" + set spice_file1 ${path_file}.sp + set subckt_file ${path_file}.subckt + write_path_spice_cmd $path $spice_file1 $subckt_file \ $lib_subckt_file $model_file $power $ground $ckt_sim incr path_index } From 7b0281014e6f9dc8dcbf295b0564e39b34f9c9b0 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 22 Mar 2026 10:13:57 -0700 Subject: [PATCH 106/181] regression usage Signed-off-by: James Cherry --- test/regression.tcl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/regression.tcl b/test/regression.tcl index 8137fcc80..dcf0057b8 100755 --- a/test/regression.tcl +++ b/test/regression.tcl @@ -30,7 +30,7 @@ # # This notice may not be removed or altered from any source distribution. -# Usage: regression -help | [-threads threads] [-j jobs] [-valgrind] [-report_stats] +# Usage: regression -help | [-j jobs] [-threads threads] [-valgrind] [-report_stats] # test1 [test2...] proc regression_main {} { @@ -82,8 +82,8 @@ proc parse_args {} { set arg [lindex $argv 0] if { $arg == "help" || $arg == "-help" } { puts {Usage: regression [-help] [-threads threads] [-j jobs] [-valgrind] [-report_stats] tests...} - puts " -threads max|integer - number of threads to use" - puts " -j jobs - number of parallel jobs (processes) to run" + puts " -j jobs - number of parallel test jobs (processes) to run" + puts " -threads max|integer - number of threads the STA uses" puts " -valgrind - run valgrind (linux memory checker)" puts " -report_stats - report run time and memory" puts " Wildcarding for test names is supported (enclose in \"'s)" From ed19ed80dffb5c03372cffd5f9a02a93a9084fcc Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 22 Mar 2026 19:34:51 -0700 Subject: [PATCH 107/181] power fmt Signed-off-by: James Cherry --- power/Power.cc | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/power/Power.cc b/power/Power.cc index d4dd239c1..cd5312b08 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -1427,8 +1427,12 @@ Power::findSwitchingPower(const Instance *inst, float volt = portVoltage(scene_cell, to_port, scene, MinMax::max()); float switching = .5 * load_cap * volt * volt * activity.density(); debugPrint(debug_, "power", 2, - "switching {}/{} activity = {:.2e} volt = {:.2f} {:.3e}", cell->name(), - to_port->name(), activity.density(), volt, switching); + "switching {}/{} activity = {:.2e} volt = {:.2f} {:.3e}", + cell->name(), + to_port->name(), + activity.density(), + volt, + switching); result.incrSwitching(switching); } } @@ -1477,14 +1481,18 @@ Power::findLeakagePower(const Instance *inst, for (const LeakagePower &pwr : scene_cell->leakagePowers()) { LibertyPort *pg_port = pwr.relatedPgPort(); if (pg_port == nullptr || pg_port->pwrGndType() == PwrGndType::primary_power) { + std::string pg_name = pg_port ? pg_port->name() : "?"; 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 {}/{} {}=1 {:.3e}", cell->name(), - pg_port->name(), when->to_string(), leakage); + debugPrint(debug_, "power", 2, "leakage {}/{} {}=1 {:.3e}", + cell->name(), + pg_name, + when->to_string(), + leakage); sum.cond_true_leakage = leakage; sum.cond_true_exists = true; } @@ -1492,7 +1500,9 @@ Power::findLeakagePower(const Instance *inst, PwrActivity cond_activity = evalActivity(when, inst); float cond_duty = cond_activity.duty(); debugPrint(debug_, "power", 2, "leakage {} {} {} {:.3e} * {:.2f}", - cell->name(), pg_port->name(), when->to_string(), + cell->name(), + pg_name, + when->to_string(), leakage, cond_duty); // Leakage power average weighted by duty. sum.cond_leakage += leakage * cond_duty; @@ -1502,8 +1512,10 @@ Power::findLeakagePower(const Instance *inst, } } else { - debugPrint(debug_, "power", 2, "leakage {} {} -- {:.3e}", cell->name(), - pg_port->name(), leakage); + debugPrint(debug_, "power", 2, "leakage {} {} -- {:.3e}", + cell->name(), + pg_name, + leakage); sum.uncond_leakage = leakage; sum.uncond_exists = true; } @@ -1529,8 +1541,10 @@ 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 {}/{} {:.3e}", cell->name(), - pg_port->name(), leakage); + debugPrint(debug_, "power", 2, "leakage {}/{} {:.3e}", + cell->name(), + pg_port ? pg_port->name() : "?", + leakage); result.incrLeakage(leakage); } } From 04f4528c2c8b1080338549ab68012c3b80ad6408 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 22 Mar 2026 19:34:59 -0700 Subject: [PATCH 108/181] ccs ceff pocv Signed-off-by: James Cherry --- dcalc/CcsCeffDelayCalc.cc | 64 +++++++++++++++++++-------------------- dcalc/CcsCeffDelayCalc.hh | 9 +----- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 0228df301..3c4e82b42 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -87,7 +87,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, parasitic_ = parasitic; output_waveforms_ = nullptr; - GateTableModel *table_model = arc->gateTableModel(scene, min_max); + const GateTableModel *table_model = arc->gateTableModel(scene, min_max); if (table_model && parasitic) { OutputWaveforms *output_waveforms = table_model->outputWaveforms(); Parasitics *parasitics = scene->parasitics(min_max); @@ -112,12 +112,36 @@ 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, "{} {}", 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); + gateDelaySlew(drvr_library, gate_delay, drvr_slew); + debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}", + delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); + + // Fill in pocv parameters. + ArcDelay gate_delay2(gate_delay); + Slew drvr_slew2(drvr_slew); + if (variables_->pocvEnabled()) { + double ceff = region_ceff_[0]; + const Pvt *pvt = pinPvt(drvr_pin_, scene, min_max); + table_model->gateDelayPocv(pvt, in_slew_, ceff, min_max, + variables_->pocvMode(), + gate_delay2, drvr_slew2); + } + ArcDcalcResult dcalc_result(load_pin_index_map.size()); + dcalc_result.setGateDelay(gate_delay2); + dcalc_result.setDrvrSlew(drvr_slew2); + + for (const auto &[load_pin, load_idx] : load_pin_index_map) { + double wire_delay, load_slew; + loadDelaySlew(load_pin, drvr_library, drvr_slew, wire_delay, load_slew); + dcalc_result.setWireDelay(load_idx, wire_delay); + dcalc_result.setLoadSlew(load_idx, load_slew); + } + return dcalc_result; } } return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, @@ -126,12 +150,11 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, void CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, - const RiseFall *rf, // Return values. double &gate_delay, double &drvr_slew) { - initRegions(drvr_library, rf); + initRegions(drvr_library, drvr_rf_); findCsmWaveform(); ref_time_ = output_waveforms_->referenceTime(in_slew_); gate_delay = region_times_[region_vth_idx_] - ref_time_; @@ -300,33 +323,10 @@ CcsCeffDelayCalc::findCsmWaveform() //////////////////////////////////////////////////////////////// -ArcDcalcResult -CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library, - const RiseFall *rf, - double &gate_delay, - double &drvr_slew, - const LoadPinIndexMap &load_pin_index_map) -{ - ArcDcalcResult dcalc_result(load_pin_index_map.size()); - 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); - - for (const auto &[load_pin, load_idx] : load_pin_index_map) { - 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); - } - return dcalc_result; -} - void CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, const LibertyLibrary *drvr_library, - const RiseFall *rf, - double &drvr_slew, + double drvr_slew, // Return values. double &wire_delay, double &load_slew) @@ -349,7 +349,7 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, else loadDelaySlew(load_pin, drvr_slew, elmore, wire_delay, load_slew); - thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); + thresholdAdjust(load_pin, drvr_library, drvr_rf_, wire_delay, load_slew); } void diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 53a103bb8..46125a940 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -71,22 +71,15 @@ protected: typedef std::vector Region; void gateDelaySlew(const LibertyLibrary *drvr_library, - const RiseFall *rf, // Return values. 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, - 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, - double &drvr_slew, + double drvr_slew, // Return values. double &wire_delay, double &load_slew); From 3ec34c571e1a3d50fcdba42aec5993e351e1d307 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 24 Mar 2026 10:21:44 -0700 Subject: [PATCH 109/181] Only install fmt if std::format is not available Signed-off-by: James Cherry --- CMakeLists.txt | 52 ++++++++++++++++++++++++++++++++++-------- Dockerfile.centos7 | 24 +++++++++++++++++++ Dockerfile.ubuntu22.04 | 5 ++-- README.md | 6 +++++ 4 files changed, 75 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c6ab462a..db29b2afc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -403,15 +403,44 @@ 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) +# See if std::format is available and if nor install fmt. +include(CheckCXXSourceCompiles) +set(_sta_fmt_check_saved_flags "${CMAKE_REQUIRED_FLAGS}") +if(MSVC) + string(APPEND CMAKE_REQUIRED_FLAGS " /std:c++20") +else() + string(APPEND CMAKE_REQUIRED_FLAGS " -std=c++20") +endif() +check_cxx_source_compiles(" +#include +#include +int main() { (void)std::format(\"{}\", 42); return 0; } +" HAVE_CXX_STD_FORMAT) +set(CMAKE_REQUIRED_FLAGS "${_sta_fmt_check_saved_flags}") + +if(HAVE_CXX_STD_FORMAT) + message(STATUS "std::format: available") +else() + # Set the fmt dir for the ubuntu/centos docker files. + if(EXISTS "/usr/local/lib/cmake/fmt/fmt-config.cmake") + set(fmt_DIR "/usr/local/lib/cmake/fmt") + elseif(EXISTS "/usr/lib/x86_64-linux-gnu/cmake/fmt/fmt-config.cmake") + set(fmt_DIR "/usr/lib/x86_64-linux-gnu/cmake/fmt") + elseif(EXISTS "/usr/lib/aarch64-linux-gnu/cmake/fmt/fmt-config.cmake") + set(fmt_DIR "/usr/lib/aarch64-linux-gnu/cmake/fmt") + endif() + find_package(fmt QUIET) + if(fmt_FOUND) + message(STATUS "std::format: using installed fmt library") + else() + message(STATUS "std::format: building fmt library") + include(FetchContent) + FetchContent_Declare(fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 10.2.1 + ) + FetchContent_MakeAvailable(fmt) + endif() endif() include(cmake/FindCUDD.cmake) @@ -529,12 +558,15 @@ target_sources(OpenSTA target_link_libraries(OpenSTA Eigen3::Eigen - fmt::fmt ${TCL_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${CUDD_LIB} ) +if(NOT HAVE_CXX_STD_FORMAT) + target_link_libraries(OpenSTA fmt::fmt) +endif() + if (ZLIB_LIBRARIES) target_link_libraries(OpenSTA ${ZLIB_LIBRARIES}) endif() diff --git a/Dockerfile.centos7 b/Dockerfile.centos7 index cdcd6037e..458d0dbc9 100644 --- a/Dockerfile.centos7 +++ b/Dockerfile.centos7 @@ -53,6 +53,30 @@ RUN source /opt/rh/devtoolset-11/enable && \ make -j`nproc` && \ make install +# Download and build fmt +# Ensure the Vault redirect is applied to everything including new installs +RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo && \ + sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo && \ + sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo && \ + yum install -y ca-certificates git && \ + update-ca-trust force-enable +# clone fmt compatible version (10.2.1) +RUN git config --global http.sslVerify false && \ + git clone --depth 1 --branch 10.2.1 https://github.com/fmtlib/fmt.git /tmp/fmt +RUN source /opt/rh/devtoolset-11/enable && \ + cd /tmp/fmt && \ + mkdir build && cd build && \ + cmake3 .. \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DFMT_TEST=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DFMT_DOC=OFF && \ + make -j$(nproc) && \ + make install +RUN rm -rf /tmp/fmt + +################################################################ + FROM base-dependencies AS builder COPY . /OpenSTA diff --git a/Dockerfile.ubuntu22.04 b/Dockerfile.ubuntu22.04 index e636fc210..b8c94ca4a 100644 --- a/Dockerfile.ubuntu22.04 +++ b/Dockerfile.ubuntu22.04 @@ -13,12 +13,13 @@ RUN apt-get update && \ gdb \ tcl-dev \ tcl-tclreadline \ - libeigen3-dev \ swig \ bison \ flex \ automake \ - autotools-dev + autotools-dev \ + libeigen3-dev \ + libfmt-dev # Download CUDD RUN wget https://raw.githubusercontent.com/davidkebo/cudd/main/cudd_versions/cudd-3.0.0.tar.gz && \ diff --git a/README.md b/README.md index 8323cf74c..e30b10519 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ eigen 3.4.0 3.4.0 MPL2 required cudd 3.0.0 3.0.0 BSD required tclreadline 2.3.8 2.3.8 BSD optional zLib 1.2.5 1.2.8 zlib optional +libfmt 8.1.1 N/A MIT required if std::format not available ``` The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html) @@ -143,6 +144,11 @@ make You can use the "configure --prefix" option and "make install" to install CUDD in a different directory. +Modern c++ compilers that support c++20 include support for std::format. +With older compilers like gcc 11 on Ubuntu 22.04 and Centos7 the fmt library +is used instead. If it is not installed locally, the github repository is +downloaded and compiled in the build directory. + ### Building with CMake Use the following commands to checkout the git repository and build the From f4048cdf3efed83ccdfd79db3391a4e49df75593 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 24 Mar 2026 10:31:24 -0700 Subject: [PATCH 110/181] read_saif ref missing instance resolves #406 Signed-off-by: James Cherry --- power/SaifReader.cc | 4 +++- test/read_saif_null_instance.lib | 14 ++++++++++++++ test/read_saif_null_instance.ok | 1 + test/read_saif_null_instance.saif | 16 ++++++++++++++++ test/read_saif_null_instance.tcl | 5 +++++ test/read_saif_null_instance.v | 2 ++ test/regression_vars.tcl | 1 + 7 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 test/read_saif_null_instance.lib create mode 100644 test/read_saif_null_instance.ok create mode 100644 test/read_saif_null_instance.saif create mode 100644 test/read_saif_null_instance.tcl create mode 100644 test/read_saif_null_instance.v diff --git a/power/SaifReader.cc b/power/SaifReader.cc index c06cb512c..72a359268 100644 --- a/power/SaifReader.cc +++ b/power/SaifReader.cc @@ -139,7 +139,9 @@ SaifReader::instancePush(const char *instance_name) else { // Inside annotation scope. Instance *parent = path_.empty() ? sdc_network_->topInstance() : path_.back(); - Instance *child = sdc_network_->findChild(parent, instance_name); + Instance *child = parent + ? sdc_network_->findChild(parent, instance_name) + : nullptr; path_.push_back(child); } stringDelete(instance_name); diff --git a/test/read_saif_null_instance.lib b/test/read_saif_null_instance.lib new file mode 100644 index 000000000..da8ae595c --- /dev/null +++ b/test/read_saif_null_instance.lib @@ -0,0 +1,14 @@ +library(min) { + technology(cmos); + time_unit : "1ns"; + voltage_unit : "1V"; + current_unit : "1mA"; + capacitive_load_unit(1, pf); + cell(BUF) { + pin(A) { direction : input; } + pin(Y) { direction : output; + function : "A"; + timing() { related_pin : "A"; } + } + } +} diff --git a/test/read_saif_null_instance.ok b/test/read_saif_null_instance.ok new file mode 100644 index 000000000..42f6c7a46 --- /dev/null +++ b/test/read_saif_null_instance.ok @@ -0,0 +1 @@ +Annotated 0 pin activities. diff --git a/test/read_saif_null_instance.saif b/test/read_saif_null_instance.saif new file mode 100644 index 000000000..dcaff257c --- /dev/null +++ b/test/read_saif_null_instance.saif @@ -0,0 +1,16 @@ +(SAIFILE +(SAIFVERSION "2.0") +(DIRECTION "backward") +(DIVIDER / ) +(TIMESCALE 1ns) +(DURATION 1000) + (INSTANCE TOP + (INSTANCE child + (INSTANCE grandchild + (NET + (clk (T0 500) (T1 500) (TZ 0) (TX 0) (TB 0) (TC 1000)) + ) + ) + ) + ) +) diff --git a/test/read_saif_null_instance.tcl b/test/read_saif_null_instance.tcl new file mode 100644 index 000000000..59f4d5a3d --- /dev/null +++ b/test/read_saif_null_instance.tcl @@ -0,0 +1,5 @@ +# read_saif references missing instance +read_liberty read_saif_null_instance.lib +read_verilog read_saif_null_instance.v +link_design top +read_saif -scope TOP read_saif_null_instance.saif diff --git a/test/read_saif_null_instance.v b/test/read_saif_null_instance.v new file mode 100644 index 000000000..2f1a84ace --- /dev/null +++ b/test/read_saif_null_instance.v @@ -0,0 +1,2 @@ +module top(input clk); +endmodule diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index edc16ee06..9b6af3340 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -158,6 +158,7 @@ record_public_tests { path_group_names power_json prima3 + read_saif_null_instance report_checks_sorted report_checks_src_attr report_json1 From c38762185c8fe66d1e2016e3ddcb3c062642d587 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 24 Mar 2026 12:22:20 -0700 Subject: [PATCH 111/181] StaConfig.hh pass HAVE_CXX_STD_FORMAT Signed-off-by: James Cherry --- include/sta/Format.hh | 5 +---- util/StaConfig.hh.cmake | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/sta/Format.hh b/include/sta/Format.hh index 1745f482f..88555a1e3 100644 --- a/include/sta/Format.hh +++ b/include/sta/Format.hh @@ -35,10 +35,7 @@ #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 +#if HAVE_CXX_STD_FORMAT #include namespace sta { diff --git a/util/StaConfig.hh.cmake b/util/StaConfig.hh.cmake index 2efd97204..daf90b36e 100644 --- a/util/StaConfig.hh.cmake +++ b/util/StaConfig.hh.cmake @@ -6,4 +6,6 @@ #cmakedefine ZLIB_FOUND +#cmakedefine01 HAVE_CXX_STD_FORMAT + #define TCL_READLINE ${TCL_READLINE} From d233581e167e72a332024ba9181361f791f9fcdb Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Wed, 25 Mar 2026 08:35:18 +1300 Subject: [PATCH 112/181] Make `wire_load_tree_` default to unknown so we can detect when it's not present in a Liberty file. (#402) This is potentially a behavior change, but I think omitting this is rare. I've only seen it in some DTCD Liberty files. In those Liberty libraries, it seems to be expected that the Liberty data is valid for all `WireloadTree` types. Thus it is necessary to distinguish between "wire load tree was specified as 'balanced'" and "wire load tree was not specified". --- liberty/Liberty.cc | 2 +- parasitics/Parasitics.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 5e4328ad7..17d0b4646 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -3042,7 +3042,7 @@ OperatingConditions::OperatingConditions(const char *name) : Pvt(0.0, 0.0, 0.0), name_(name), // Default wireload tree. - wire_load_tree_(WireloadTree::balanced) + wire_load_tree_(WireloadTree::unknown) { } diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index ab304bda4..69187f959 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -257,12 +257,12 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin, makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap, wireload_res, fanout); break; + case WireloadTree::unknown: 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; From b9e439f41c1d3c8b5165378c36bf310f279115ee Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 24 Mar 2026 18:42:47 -0700 Subject: [PATCH 113/181] delay calc clip delay to 0.0 resolves #405 Signed-off-by: James Cherry --- dcalc/DmpCeff.cc | 14 +++++++------- dcalc/GraphDelayCalc.cc | 34 +++++++++++++++++----------------- liberty/TableModel.cc | 4 +++- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 38c04648e..1cccadafe 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -448,10 +448,10 @@ DmpAlg::showJacobian() { std::string line = " "; for (int j = 0; j < nr_order_; j++) - line += sta::format("{:12}", dmp_param_index_strings[j]); + line += sta::format("{:>12}", dmp_param_index_strings[j]); report_->reportLine(line); - line.clear(); for (int i = 0; i < nr_order_; i++) { + line.clear(); line += sta::format("{:4} ", dmp_func_index_strings[i]); for (int j = 0; j < nr_order_; j++) line += sta::format("{:12.3e} ", fjac_[i][j]); @@ -551,10 +551,10 @@ DmpAlg::loadDelaySlew(const Pin *, // Use the driver thresholds and rely on thresholdAdjust to // convert the delay and slew to the load's thresholds. try { - if (debug_->check("dmp_ceff", 4)) - showVl(); elmore_ = elmore; p3_ = 1.0 / elmore; + if (debug_->check("dmp_ceff", 4)) + showVl(); double t_lower = t0_; double t_upper = vlCrossingUpperBound(); double load_delay = findVlCrossing(vth_, t_lower, t_upper); @@ -1189,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_); diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 8c12cdf1e..9c95ed1bc 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -411,27 +411,27 @@ GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex, for (const MinMax *min_max : MinMax::range()) { for (const RiseFall *rf : RiseFall::range()) { InputDrive *drive = nullptr; - if (network_->isTopLevelPort(drvr_pin)) { - Port *port = network_->port(drvr_pin); + if (network_->isTopLevelPort(drvr_pin)) { + Port *port = network_->port(drvr_pin); drive = sdc->findInputDrive(port); - } - if (drive) { - const LibertyCell *drvr_cell; - const LibertyPort *from_port, *to_port; - float *from_slews; + } + if (drive) { + const LibertyCell *drvr_cell; + const LibertyPort *from_port, *to_port; + float *from_slews; drive->driveCell(rf, min_max, drvr_cell, from_port, - from_slews, to_port); - if (drvr_cell) { - if (from_port == nullptr) - from_port = driveCellDefaultFromPort(drvr_cell, to_port); - findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf, + from_slews, to_port); + if (drvr_cell) { + if (from_port == nullptr) + from_port = driveCellDefaultFromPort(drvr_cell, to_port); + findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf, from_port, from_slews, to_port, scene, min_max); - } - else + } + else seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, scene, min_max, - arc_delay_calc); - } - else + arc_delay_calc); + } + else seedNoDrvrSlew(drvr_vertex, drvr_pin, rf, scene, min_max, arc_delay_calc); } } diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 46dd6d6b6..c3e8856fa 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -116,7 +116,9 @@ GateTableModel::gateDelay(const Pvt *pvt, drvr_slew = findValue(pvt, slew_models_->model(), in_slew, load_cap, 0.0); else drvr_slew = 0.0; - // Clip negative slews to zero. + // Clip negative delays and slews to zero. + if (gate_delay < 0.0) + gate_delay = 0.0; if (drvr_slew < 0.0) drvr_slew = 0.0; } From 67426928762dd68e34a95511a63cf783c673e1c5 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 28 Mar 2026 19:13:35 -0700 Subject: [PATCH 114/181] string squash Signed-off-by: James Cherry --- CMakeLists.txt | 2 +- app/Main.cc | 33 +- app/StaMain.cc | 12 +- dcalc/ArcDelayCalc.cc | 18 +- dcalc/ArnoldiDelayCalc.cc | 2 +- dcalc/CcsCeffDelayCalc.cc | 2 +- dcalc/CcsCeffDelayCalc.hh | 4 +- dcalc/DelayCalc.cc | 12 +- dcalc/DelayCalc.i | 2 + dcalc/DelayCalcBase.cc | 2 +- dcalc/DelayCalcBase.hh | 2 +- dcalc/DmpCeff.cc | 13 +- dcalc/DmpDelayCalc.cc | 4 +- dcalc/GraphDelayCalc.cc | 4 +- dcalc/LumpedCapDelayCalc.hh | 2 +- dcalc/PrimaDelayCalc.cc | 9 +- dcalc/PrimaDelayCalc.hh | 10 +- dcalc/UnitDelayCalc.cc | 2 +- dcalc/UnitDelayCalc.hh | 4 +- doc/ApiChanges.txt | 29 +- graph/Graph.cc | 34 +- graph/Graph.i | 9 +- include/sta/ArcDelayCalc.hh | 17 +- include/sta/Clock.hh | 25 +- include/sta/ClockGroups.hh | 2 +- include/sta/ConcreteLibrary.hh | 63 +- include/sta/ConcreteNetwork.hh | 114 +-- include/sta/ContainerHelpers.hh | 66 +- include/sta/Debug.hh | 10 +- include/sta/DelayCalc.hh | 7 +- include/sta/EnumNameMap.hh | 22 +- include/sta/Error.hh | 16 +- include/sta/ExceptionPath.hh | 19 +- include/sta/Graph.hh | 4 +- include/sta/Hash.hh | 3 +- include/sta/Liberty.hh | 149 ++-- include/sta/LinearModel.hh | 2 +- include/sta/Mode.hh | 2 +- include/sta/Network.hh | 135 ++- include/sta/NetworkClass.hh | 2 +- include/sta/Parasitics.hh | 2 +- include/sta/PathGroup.hh | 23 +- include/sta/PatternMatch.hh | 30 +- include/sta/PocvMode.hh | 6 +- include/sta/PortDirection.hh | 2 +- include/sta/PowerClass.hh | 2 +- include/sta/Property.hh | 49 +- include/sta/Report.hh | 6 +- include/sta/ReportTcl.hh | 6 +- include/sta/Sdc.hh | 31 +- include/sta/SdcCmdComment.hh | 14 +- include/sta/SdcNetwork.hh | 93 ++- include/sta/SearchClass.hh | 1 - include/sta/Sta.hh | 80 +- include/sta/StaMain.hh | 6 +- include/sta/StringUtil.hh | 102 +-- include/sta/TableModel.hh | 43 +- include/sta/TclTypeHelpers.hh | 12 +- include/sta/TimingArc.hh | 14 +- include/sta/TimingModel.hh | 3 +- include/sta/Transition.hh | 33 +- include/sta/Units.hh | 2 +- include/sta/VerilogReader.hh | 70 +- include/sta/Wireload.hh | 20 +- liberty/EquivCells.cc | 8 +- liberty/InternalPower.cc | 2 +- liberty/LibExprLex.ll | 8 +- liberty/LibExprParse.yy | 24 +- liberty/LibExprReader.cc | 29 +- liberty/LibExprReader.hh | 6 +- liberty/LibExprReaderPvt.hh | 17 +- liberty/Liberty.cc | 256 +++--- liberty/Liberty.i | 23 +- liberty/LibertyBuilder.cc | 33 +- liberty/LibertyBuilder.hh | 19 +- liberty/LibertyExt.cc | 22 +- liberty/LibertyParse.yy | 4 +- liberty/LibertyParser.cc | 188 ++--- liberty/LibertyParser.hh | 71 +- liberty/LibertyReader.cc | 1173 +++++++++++++-------------- liberty/LibertyReader.hh | 4 +- liberty/LibertyReaderPvt.hh | 128 +-- liberty/LibertyScanner.hh | 5 +- liberty/LibertyWriter.cc | 8 +- liberty/LinearModel.cc | 2 +- liberty/TableModel.cc | 66 +- liberty/TimingArc.cc | 26 +- liberty/Units.cc | 16 +- liberty/Wireload.cc | 30 +- network/ConcreteLibrary.cc | 111 +-- network/ConcreteNetwork.cc | 144 ++-- network/Network.cc | 247 +++--- network/Network.i | 26 +- network/NetworkCmp.cc | 2 +- network/SdcNetwork.cc | 251 +++--- network/VerilogNamespace.cc | 4 +- parasitics/ConcreteParasitics.cc | 6 +- parasitics/ConcreteParasitics.hh | 5 +- parasitics/ConcreteParasiticsPvt.hh | 3 +- parasitics/SpefLex.ll | 12 +- parasitics/SpefNamespace.cc | 71 +- parasitics/SpefNamespace.hh | 22 +- parasitics/SpefParse.yy | 157 ++-- parasitics/SpefReader.cc | 181 ++--- parasitics/SpefReader.hh | 3 +- parasitics/SpefReaderPvt.hh | 51 +- parasitics/SpefScanner.hh | 8 +- power/Power.cc | 42 +- power/Power.hh | 8 +- power/ReportPower.cc | 44 +- power/ReportPower.hh | 44 +- power/SaifLex.ll | 9 +- power/SaifParse.yy | 51 +- power/SaifReader.cc | 34 +- power/SaifReaderPvt.hh | 10 +- power/VcdParse.cc | 7 +- power/VcdParse.hh | 18 +- power/VcdReader.cc | 60 +- power/VcdReader.hh | 6 +- sdc/Clock.cc | 21 +- sdc/ClockGroups.cc | 4 +- sdc/DisabledPorts.cc | 19 +- sdc/ExceptionPath.cc | 22 +- sdc/Sdc.cc | 46 +- sdc/Sdc.i | 164 ++-- sdc/SdcCmdComment.cc | 32 +- sdc/WriteSdc.cc | 162 ++-- sdc/WriteSdc.hh | 6 +- sdc/WriteSdcPvt.hh | 64 +- sdf/ReportAnnotation.cc | 12 +- sdf/Sdf.i | 18 +- sdf/SdfLex.ll | 18 +- sdf/SdfParse.yy | 68 +- sdf/SdfReader.cc | 324 ++++---- sdf/SdfReader.hh | 9 +- sdf/SdfReaderPvt.hh | 66 +- sdf/SdfScanner.hh | 8 +- sdf/SdfWriter.cc | 48 +- sdf/SdfWriter.hh | 4 +- search/CheckMinPulseWidths.cc | 2 +- search/ClkLatency.cc | 2 +- search/ClkSkew.cc | 16 +- search/MakeTimingModel.cc | 16 +- search/MakeTimingModel.hh | 8 +- search/MakeTimingModelPvt.hh | 14 +- search/Mode.cc | 2 +- search/PathGroup.cc | 33 +- search/PocvMode.cc | 4 +- search/Property.cc | 103 ++- search/ReportPath.cc | 411 +++++----- search/ReportPath.hh | 117 +-- search/Search.cc | 22 +- search/Search.i | 27 +- search/Sim.cc | 6 +- search/Sta.cc | 163 ++-- spice/WritePathSpice.cc | 70 +- spice/WritePathSpice.hh | 14 +- spice/WriteSpice.cc | 203 +++-- spice/WriteSpice.hh | 57 +- spice/Xyce.cc | 3 +- tcl/StaTclTypes.i | 154 ++-- tcl/TclTypeHelpers.cc | 32 +- tcl/Util.tcl | 1 - util/Debug.cc | 28 +- util/Error.cc | 10 +- util/Hash.cc | 9 +- util/MinMax.cc | 22 +- util/PatternMatch.cc | 131 ++- util/Report.cc | 18 +- util/ReportTcl.cc | 6 +- util/StringUtil.cc | 122 +-- util/Transition.cc | 42 +- util/Util.i | 26 +- verilog/VerilogLex.ll | 22 +- verilog/VerilogParse.yy | 166 ++-- verilog/VerilogReader.cc | 419 ++++------ verilog/VerilogReaderPvt.hh | 14 +- verilog/VerilogScanner.hh | 11 +- verilog/VerilogWriter.cc | 44 +- 179 files changed, 4260 insertions(+), 4749 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index db29b2afc..d843dcfe4 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.1 +project(STA VERSION 3.1.0 LANGUAGES CXX ) diff --git a/app/Main.cc b/app/Main.cc index aa28aed84..8134b35e2 100644 --- a/app/Main.cc +++ b/app/Main.cc @@ -28,6 +28,7 @@ #include #include // exit #include +#include #include #if TCL_READLINE #include @@ -58,14 +59,14 @@ static char **cmd_argv; static const char *init_filename = ".sta"; static void -showUsage(const char *prog, - const char *init_filename); +showUsage(std::string_view prog, + std::string_view init_filename); static int tclAppInit(Tcl_Interp *interp); static int staTclAppInit(int argc, char *argv[], - const char *init_filename, + std::string_view init_filename, Tcl_Interp *interp); static void initStaApp(int &argc, @@ -105,7 +106,7 @@ tclAppInit(Tcl_Interp *interp) static int staTclAppInit(int argc, char *argv[], - const char *init_filename, + std::string_view init_filename, Tcl_Interp *interp) { // source init.tcl @@ -130,7 +131,7 @@ staTclAppInit(int argc, if (home) { std::string init_path = home; init_path += "/"; - init_path += init_filename; + init_path.append(init_filename); if (std::filesystem::is_regular_file(init_path.c_str())) sourceTclFile(init_path.c_str(), true, true, interp); } @@ -183,15 +184,17 @@ initStaApp(int &argc, } static void -showUsage(const char *prog, - const char *init_filename) +showUsage(std::string_view prog, + std::string_view init_filename) { - printf("Usage: %s [-help] [-version] [-no_init] [-exit] cmd_file\n", prog); - printf(" -help show help and exit\n"); - printf(" -version show version and exit\n"); - printf(" -no_init do not read %s init file\n", init_filename); - printf(" -threads count|max use count threads\n"); - printf(" -no_splash do not show the license splash at startup\n"); - printf(" -exit exit after reading cmd_file\n"); - printf(" cmd_file source cmd_file\n"); + sta::print(stdout, "Usage: {} [-help] [-version] [-no_init] [-exit] cmd_file\n", + prog); + sta::print(stdout, " -help show help and exit\n"); + sta::print(stdout, " -version show version and exit\n"); + sta::print(stdout, " -no_init do not read {} init file\n", + init_filename); + sta::print(stdout, " -threads count|max use count threads\n"); + sta::print(stdout, " -no_splash do not show the license splash at startup\n"); + sta::print(stdout, " -exit exit after reading cmd_file\n"); + sta::print(stdout, " cmd_file source cmd_file\n"); } diff --git a/app/StaMain.cc b/app/StaMain.cc index 079112e12..f2eda7ae0 100644 --- a/app/StaMain.cc +++ b/app/StaMain.cc @@ -24,6 +24,8 @@ #include "StaMain.hh" +#include +#include #include #include #include @@ -43,7 +45,7 @@ parseThreadsArg(int &argc, if (stringEqual(thread_arg, "max")) return processorCount(); else if (isDigits(thread_arg)) - return atoi(thread_arg); + return std::stoi(thread_arg); else fprintf(stderr,"Warning: -threads must be max or a positive integer.\n"); } @@ -53,11 +55,11 @@ parseThreadsArg(int &argc, bool findCmdLineFlag(int &argc, char *argv[], - const char *flag) + std::string_view flag) { for (int i = 1; i < argc; i++) { char *arg = argv[i]; - if (stringEq(arg, flag)) { + if (std::string_view(arg) == flag) { // Remove flag from argv. for (int j = i + 1; j < argc; j++, i++) argv[i] = argv[j]; @@ -72,11 +74,11 @@ findCmdLineFlag(int &argc, char * findCmdLineKey(int &argc, char *argv[], - const char *key) + std::string_view key) { for (int i = 1; i < argc; i++) { char *arg = argv[i]; - if (stringEq(arg, key) && i + 1 < argc) { + if (std::string_view(arg) == key && i + 1 < argc) { char *value = argv[i + 1]; // Remove key and value from argv. for (int j = i + 2; j < argc; j++, i++) diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index 867594112..a2a1ad0e2 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -25,7 +25,9 @@ #include "ArcDelayCalc.hh" #include +#include +#include "StringUtil.hh" #include "Units.hh" #include "Liberty.hh" #include "TimingArc.hh" @@ -61,13 +63,14 @@ ArcDelayCalc::gateDelay(const TimingArc *arc, //////////////////////////////////////////////////////////////// +// For TCL %typemap(in) ArcDcalcArg. ArcDcalcArg -makeArcDcalcArg(const char *inst_name, - const char *in_port_name, - const char *in_rf_name, - const char *drvr_port_name, - const char *drvr_rf_name, - const char *input_delay_str, +makeArcDcalcArg(std::string_view inst_name, + std::string_view in_port_name, + std::string_view in_rf_name, + std::string_view drvr_port_name, + std::string_view drvr_rf_name, + std::string_view input_delay_str, const StaState *sta) { Report *report = sta->report(); @@ -82,7 +85,8 @@ makeArcDcalcArg(const char *inst_name, if (drvr_pin) { const RiseFall *drvr_rf = RiseFall::find(drvr_rf_name); if (drvr_rf) { - float input_delay = strtof(input_delay_str, nullptr); + const std::string input_delay_buf(input_delay_str); + auto [input_delay, valid] = stringFloat(input_delay_buf); input_delay = sta->units()->timeUnit()->userToSta(input_delay); const Graph *graph = sta->graph(); diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index c85d19982..97866a79f 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -121,7 +121,7 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc ArnoldiDelayCalc(StaState *sta); ~ArnoldiDelayCalc() override; ArcDelayCalc *copy() override; - const char *name() const override { return "arnoldi"; } + std::string_view name() const override { return "arnoldi"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const Scene *scene, diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 3c4e82b42..6ca9cb962 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -670,7 +670,7 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, } void -CcsCeffDelayCalc::fail(const char *reason) +CcsCeffDelayCalc::fail(std::string_view reason) { // Report failures with a unique debug flag. if (debug_->check("ccs_dcalc", 1) || debug_->check("dcalc_error", 1)) diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 46125a940..665588f89 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -41,7 +41,7 @@ public: CcsCeffDelayCalc(StaState *sta); virtual ~CcsCeffDelayCalc(); ArcDelayCalc *copy() override; - const char *name() const override { return "ccs_ceff"; } + std::string_view name() const override { return "ccs_ceff"; } bool reduceSupported() const override { return true; } ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, @@ -113,7 +113,7 @@ protected: double &dvl_dt); double vl(double t, double elmore); - void fail(const char *reason); + void fail(std::string_view reason); const Pin *drvr_pin_; const RiseFall *drvr_rf_; diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index c149d6cb7..614ff6104 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -38,7 +38,7 @@ namespace sta { -typedef std::map DelayCalcMap; +typedef std::map> DelayCalcMap; static DelayCalcMap delay_calcs; @@ -55,10 +55,10 @@ registerDelayCalcs() } void -registerDelayCalc(const std::string &name, +registerDelayCalc(std::string_view name, MakeArcDelayCalc maker) { - delay_calcs[name] = maker; + delay_calcs[std::string(name)] = maker; } void @@ -68,10 +68,10 @@ deleteDelayCalcs() } ArcDelayCalc * -makeDelayCalc(const std::string &name, +makeDelayCalc(const std::string_view name, StaState *sta) { - MakeArcDelayCalc maker = findKey(&delay_calcs, name); + MakeArcDelayCalc maker = findStringKey(delay_calcs, name); if (maker) return maker(sta); else @@ -79,7 +79,7 @@ makeDelayCalc(const std::string &name, } bool -isDelayCalcName(const std::string &name) +isDelayCalcName(std::string_view name) { return delay_calcs.contains(name); } diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index 7577f0e50..992ecadea 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -24,6 +24,8 @@ %module dcalc +%include + %{ #include "DelayCalc.hh" diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index f153e44cd..71e2f1169 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -174,7 +174,7 @@ std::string DelayCalcBase::reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, const Slew &to_slew, float related_out_cap, const Scene *scene, diff --git a/dcalc/DelayCalcBase.hh b/dcalc/DelayCalcBase.hh index faaa4358f..da69ed1bb 100644 --- a/dcalc/DelayCalcBase.hh +++ b/dcalc/DelayCalcBase.hh @@ -58,7 +58,7 @@ public: std::string reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, const Slew &to_slew, float related_out_cap, const Scene *scene, diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 1cccadafe..577bce5c3 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -35,6 +35,7 @@ #include #include #include +#include #include "Format.hh" #include "Report.hh" @@ -126,7 +127,7 @@ class DmpAlg : public StaState DmpAlg(int nr_order, StaState *sta); ~DmpAlg() override = default; - virtual const char *name() = 0; + virtual std::string_view name() = 0; // Set driver model and pi model parameters for delay calculation. virtual void init(const LibertyLibrary *library, const LibertyCell *drvr_cell, @@ -201,7 +202,7 @@ class DmpAlg : public StaState double lower_bound, double upper_bound); void showVl(); - void fail(const char *reason); + void fail(std::string_view reason); // Output response to vs(t) ramp driving capacitive load. double y(double t, @@ -655,7 +656,7 @@ DmpAlg::showVl() } void -DmpAlg::fail(const char *reason) +DmpAlg::fail(std::string_view reason) { // Report failures with a unique debug flag. if (debug_->check("dmp_ceff", 1) || debug_->check("dcalc_error", 1)) @@ -673,7 +674,7 @@ class DmpCap : public DmpAlg { public: DmpCap(StaState *sta); - const char *name() override { return "cap"; } + std::string_view name() override { return "cap"; } void init(const LibertyLibrary *library, const LibertyCell *drvr_cell, const Pvt *pvt, @@ -789,7 +790,7 @@ class DmpPi : public DmpAlg { public: DmpPi(StaState *sta); - const char *name() override { return "Pi"; } + std::string_view name() override { return "Pi"; } void init(const LibertyLibrary *library, const LibertyCell *drvr_cell, const Pvt *pvt, @@ -1115,7 +1116,7 @@ class DmpZeroC2 : public DmpOnePole { public: DmpZeroC2(StaState *sta); - const char *name() override { return "c2=0"; } + std::string_view name() override { return "c2=0"; } void init(const LibertyLibrary *drvr_library, const LibertyCell *drvr_cell, const Pvt *pvt, diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index aece357e3..3fefc53b1 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -43,7 +43,7 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc public: DmpCeffElmoreDelayCalc(StaState *sta); ArcDelayCalc *copy() override; - const char *name() const override { return "dmp_ceff_elmore"; } + std::string_view name() const override { return "dmp_ceff_elmore"; } ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, @@ -139,7 +139,7 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc public: DmpCeffTwoPoleDelayCalc(StaState *sta); ArcDelayCalc *copy() override; - const char *name() const override { return "dmp_ceff_two_pole"; } + std::string_view name() const override { return "dmp_ceff_two_pole"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const Scene *scene, diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 9c95ed1bc..bcfb9cc07 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include "ContainerHelpers.hh" #include "Debug.hh" @@ -1685,7 +1686,8 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, 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; + std::string_view from_slew_annotation = + from_ideal_clk ? std::string_view(" (ideal clock)") : std::string_view{}; result = arc_delay_calc_->reportCheckDelay(to_pin, arc, from_slew, from_slew_annotation, to_slew, related_out_cap, scene, min_max, digits); diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index 752edea99..eb2bf4f09 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -35,7 +35,7 @@ class LumpedCapDelayCalc : public ParallelDelayCalc public: LumpedCapDelayCalc(StaState *sta); ArcDelayCalc *copy() override; - const char *name() const override { return "lumped_cap"; } + std::string_view name() const override { return "lumped_cap"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const Scene *scene, diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index fc1b02089..9cea3ea65 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -25,6 +25,7 @@ #include "PrimaDelayCalc.hh" #include // abs +#include #include "Debug.hh" #include "Units.hh" @@ -951,7 +952,7 @@ PrimaDelayCalc::watchWaveform(const Pin *pin) //////////////////////////////////////////////////////////////// void -PrimaDelayCalc::reportMatrix(const char *name, +PrimaDelayCalc::reportMatrix(std::string_view name, MatrixSd &matrix) { report_->report("{}", name); @@ -959,7 +960,7 @@ PrimaDelayCalc::reportMatrix(const char *name, } void -PrimaDelayCalc::reportMatrix(const char *name, +PrimaDelayCalc::reportMatrix(std::string_view name, Eigen::MatrixXd &matrix) { report_->report("{}", name); @@ -967,7 +968,7 @@ PrimaDelayCalc::reportMatrix(const char *name, } void -PrimaDelayCalc::reportMatrix(const char *name, +PrimaDelayCalc::reportMatrix(std::string_view name, Eigen::VectorXd &matrix) { report_->report("{}", name); @@ -975,7 +976,7 @@ PrimaDelayCalc::reportMatrix(const char *name, } void -PrimaDelayCalc::reportVector(const char *name, +PrimaDelayCalc::reportVector(std::string_view name, std::vector &matrix) { report_->report("{}", name); diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index c295478cb..8ed47a053 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -60,7 +60,7 @@ public: ~PrimaDelayCalc(); ArcDelayCalc *copy() override; void copyState(const StaState *sta) override; - const char *name() const override { return "prima"; } + std::string_view name() const override { return "prima"; } void setPrimaReduceOrder(size_t order); Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, @@ -157,13 +157,13 @@ protected: void primaReduce(); void primaReduce2(); - void reportMatrix(const char *name, + void reportMatrix(std::string_view name, MatrixSd &matrix); - void reportMatrix(const char *name, + void reportMatrix(std::string_view name, Eigen::MatrixXd &matrix); - void reportMatrix(const char *name, + void reportMatrix(std::string_view name, Eigen::VectorXd &matrix); - void reportVector(const char *name, + void reportVector(std::string_view name, std::vector &matrix); void reportMatrix(MatrixSd &matrix); void reportMatrix(Eigen::MatrixXd &matrix); diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc index 61221c715..afda49226 100644 --- a/dcalc/UnitDelayCalc.cc +++ b/dcalc/UnitDelayCalc.cc @@ -172,7 +172,7 @@ std::string UnitDelayCalc::reportCheckDelay(const Pin *, const TimingArc *, const Slew &, - const char *, + std::string_view, const Slew &, float, const Scene *, diff --git a/dcalc/UnitDelayCalc.hh b/dcalc/UnitDelayCalc.hh index 7ae21affb..5e2a30793 100644 --- a/dcalc/UnitDelayCalc.hh +++ b/dcalc/UnitDelayCalc.hh @@ -34,7 +34,7 @@ class UnitDelayCalc : public ArcDelayCalc public: UnitDelayCalc(StaState *sta); ArcDelayCalc *copy() override; - const char *name() const override { return "unit"; } + std::string_view name() const override { return "unit"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const Scene *scene, @@ -94,7 +94,7 @@ public: std::string reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, const Slew &to_slew, float related_out_cap, const Scene *scene, diff --git a/doc/ApiChanges.txt b/doc/ApiChanges.txt index 83759119e..7b62f9813 100644 --- a/doc/ApiChanges.txt +++ b/doc/ApiChanges.txt @@ -24,6 +24,33 @@ This file summarizes STA API changes for each release. +Release 3.1.0 2026/03/25 +------------------------ + +OpenSTA now uses std::string and std::string_view instead of const char *. +Lookup funtions such as Network::findPin use std::string_view that accept +a const char *, std::string, or std::string_view caller argument. + +2026/03/19 +---------- + +LibertyCell::footprint() returns const std::string& instead of const char*. +LibertyCell::userFunctionClass returns const std::string& instead of const char*. + +The Sdc, Liberty, ConcreteLibrary, ConcreteNetwork classes have been updated to +use std::string and std::string_view instead of const char *. std::string_view +is used when the lifetime of the string argument is only while the function is +called. std::string is used when the string value outlives the function call +because it is stored in data structures. + +The LibertyPort functions + relatedGroundPin + relatedPowerPin +are renamed to + relatedGroundPort + relatedPowerPort +and return LibertyPort's instead of strings. + 2026/03/12 ---------- @@ -37,7 +64,7 @@ stdstrPrint, strintPrint, stringAppend have been removed. Use sta::format. reportLineString is now reportLine -Release 3.0.0 2025/01/03 +Release 3.0.0 2026/01/03 ------------------------ OpenSTA now requires c++ 20. diff --git a/graph/Graph.cc b/graph/Graph.cc index 022504ca2..ca65fe017 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -52,7 +52,7 @@ Graph::Graph(StaState *sta, vertices_(nullptr), edges_(nullptr), ap_count_(ap_count), - period_check_annotations_(nullptr), + period_check_annotations_(network_), reg_clk_vertices_(makeVertexSet(this)) { // For the benifit of reg_clk_vertices_ that references graph_. @@ -910,13 +910,11 @@ Graph::periodCheckAnnotation(const Pin *pin, bool &exists) { exists = false; - if (period_check_annotations_) { - float *periods = findKey(period_check_annotations_, pin); - if (periods) { - period = periods[ap_index]; - if (period >= 0.0) - exists = true; - } + float *periods = findKey(period_check_annotations_, pin); + if (periods) { + period = periods[ap_index]; + if (period >= 0.0) + exists = true; } } @@ -925,15 +923,13 @@ Graph::setPeriodCheckAnnotation(const Pin *pin, DcalcAPIndex ap_index, float period) { - if (period_check_annotations_ == nullptr) - period_check_annotations_ = new PeriodCheckAnnotations(network_); float *periods = findKey(period_check_annotations_, pin); if (periods == nullptr) { periods = new float[ap_count_]; // Use negative (illegal) period values to indicate unannotated checks. for (int i = 0; i < ap_count_; i++) periods[i] = -1; - (*period_check_annotations_)[pin] = periods; + period_check_annotations_[pin] = periods; } periods[ap_index] = period; } @@ -941,12 +937,9 @@ Graph::setPeriodCheckAnnotation(const Pin *pin, void Graph::removePeriodCheckAnnotations() { - if (period_check_annotations_) { - for (const auto [pin, periods] : *period_check_annotations_) - delete [] periods; - delete period_check_annotations_; - period_check_annotations_ = nullptr; - } + for (auto& [pin, periods] : period_check_annotations_) + delete [] periods; + period_check_annotations_.clear(); } void @@ -1026,7 +1019,7 @@ Vertex::to_string(const StaState *sta) const { const Network *network = sta->sdcNetwork(); if (network->direction(pin_)->isBidirect()) { - std::string str = network->pathName(pin_); + std::string str(network->pathName(pin_)); str += ' '; str += is_bidirect_drvr_ ? "driver" : "load"; return str; @@ -1035,11 +1028,10 @@ Vertex::to_string(const StaState *sta) const return network->pathName(pin_); } -const char * +std::string Vertex::name(const Network *network) const { - std::string name = to_string(network); - return makeTmpString(name); + return to_string(network); } bool diff --git a/graph/Graph.i b/graph/Graph.i index 85cb51241..371e929c4 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -180,7 +180,7 @@ Vertex *to() { return self->to(Sta::sta()->graph()); } Pin *from_pin() { return self->from(Sta::sta()->graph())->pin(); } Pin *to_pin() { return self->to(Sta::sta()->graph())->pin(); } const TimingRole *role() { return self->role(); } -const char *sense() { return to_string(self->sense()); } +const char *sense() { return to_string(self->sense()).c_str(); } TimingArcSeq & timing_arcs() { return self->timingArcSet()->arcs(); } bool is_disabled_loop() { return Sta::sta()->isDisabledLoop(self); } @@ -212,13 +212,16 @@ disabled_constant_pins() bool is_disabled_bidirect_inst_path() { return Sta::sta()->isDisabledBidirectInstPath(self); } + bool is_disabled_preset_clear() { return Sta::sta()->isDisabledPresetClr(self); } + const char * -sim_timing_sense(){ +sim_timing_sense() +{ Sta *sta = Sta::sta(); const Mode *mode = sta->cmdMode(); - return to_string(sta->simTimingSense(self, mode)); + return to_string(sta->simTimingSense(self, mode)).c_str(); } FloatSeq diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index 45dcbae31..85e656d1c 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include @@ -103,12 +104,12 @@ protected: ArcDcalcArg -makeArcDcalcArg(const char *inst_name, - const char *in_port_name, - const char *in_rf_name, - const char *drvr_port_name, - const char *drvr_rf_name, - const char *input_delay_str, +makeArcDcalcArg(std::string_view inst_name, + std::string_view in_port_name, + std::string_view in_rf_name, + std::string_view drvr_port_name, + std::string_view drvr_rf_name, + std::string_view input_delay_str, const StaState *sta); // Arc delay calc result. @@ -161,7 +162,7 @@ public: ArcDelayCalc(StaState *sta); virtual ~ArcDelayCalc() {} virtual ArcDelayCalc *copy() = 0; - virtual const char *name() const = 0; + virtual std::string_view name() const = 0; // Find the parasitic for drvr_pin that is acceptable to the delay // calculator by probing parasitics_. @@ -252,7 +253,7 @@ public: virtual std::string reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, const Slew &to_slew, float related_out_cap, const Scene *scene, diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index 06215222d..4422212d4 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -41,7 +41,7 @@ class Clock : public SdcCmdComment { public: ~Clock(); - const char *name() const { return name_; } + const std::string &name() const { return name_; } float period() const { return period_; } // Virtual clocks have no pins. bool isVirtual() const; @@ -135,14 +135,14 @@ public: protected: // Private to Sdc::makeClock. - Clock(const char *name, + Clock(std::string_view name, int index, const Network *network); void initClk(PinSet *pins, bool add_to_pins, float period, FloatSeq *waveform, - const char *comment, + std::string_view comment, const Network *network); void initGeneratedClk(PinSet *pins, bool add_to_pins, @@ -156,7 +156,7 @@ protected: IntSeq *edges, FloatSeq *edge_shifts, bool is_propagated, - const char *comment, + std::string_view comment, const Network *network); void setPins(PinSet *pins, const Network *network); @@ -168,7 +168,7 @@ protected: float scale); void generateEdgesClk(const Clock *src_clk); - const char *name_; + std::string name_; PinSet pins_; bool add_to_pins_; // Hierarchical pins in pins_ become driver pins through the pin. @@ -241,7 +241,10 @@ class ClockNameLess { public: bool operator()(const Clock *clk1, - const Clock *clk2); + const Clock *clk2) const + { + return clk1->name() < clk2->name(); + } }; //////////////////////////////////////////////////////////////// @@ -282,16 +285,6 @@ public: const InterClockUncertainty *inter2) const; }; -class ClkNameLess -{ -public: - bool operator()(const Clock *clk1, - const Clock *clk2) const - { - return stringLess(clk1->name(), clk2->name()); - } -}; - ClockSeq sortByName(ClockSet *set); int diff --git a/include/sta/ClockGroups.hh b/include/sta/ClockGroups.hh index 89159a824..637650881 100644 --- a/include/sta/ClockGroups.hh +++ b/include/sta/ClockGroups.hh @@ -39,7 +39,7 @@ public: bool physically_exclusive, bool asynchronous, bool allow_paths, - const char *comment); + std::string comment); ~ClockGroups(); void makeClockGroup(ClockSet *clks); const std::string &name() const { return name_; } diff --git a/include/sta/ConcreteLibrary.hh b/include/sta/ConcreteLibrary.hh index a47585dda..e39830767 100644 --- a/include/sta/ConcreteLibrary.hh +++ b/include/sta/ConcreteLibrary.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include @@ -45,9 +46,9 @@ class PatternMatch; class LibertyCell; class LibertyPort; -using ConcreteCellMap = std::map; +using ConcreteCellMap = std::map>; using ConcretePortSeq = std::vector; -using ConcretePortMap = std::map; +using ConcretePortMap = std::map>; using ConcreteLibraryCellIterator = MapIterator; using ConcreteCellPortIterator = VectorIterator; using ConcretePortMemberIterator = VectorIterator; @@ -55,22 +56,21 @@ using ConcretePortMemberIterator = VectorIterator port_msb_first); + std::function port_msb_first); size_t portCount() const; - void setName(const char *name); + void setName(std::string_view name); void addPort(ConcretePort *port); void addPortBit(ConcretePort *port); protected: - ConcreteCell(const char *name, - const char *filename, + ConcreteCell(std::string_view name, + std::string_view filename, bool is_leaf, ConcreteLibrary *library); - ConcretePort *makeBusPort(const char *name, + ConcretePort *makeBusPort(std::string_view name, int from_index, int to_index, ConcretePortSeq *members); void makeBusPortBits(ConcretePort *bus_port, - const char *name, + std::string_view bus_name, int from_index, int to_index); // Bus port bit (internal to makeBusPortBits). - ConcretePort *makePort(const char *bit_name, + ConcretePort *makePort(std::string bit_name, int bit_index); void makeBusPortBit(ConcretePort *bus_port, - const char *name, + std::string_view bus_name, int index); std::string name_; @@ -181,9 +180,9 @@ class ConcretePort { public: virtual ~ConcretePort(); - const char *name() const { return name_.c_str(); } + const std::string &name() const { return name_; } ObjectId id() const { return id_; } - const char *busName() const; + std::string busName() const; Cell *cell() const; ConcreteLibrary *library() const { return cell_->library(); } PortDirection *direction() const { return direction_; } @@ -231,7 +230,7 @@ public: protected: // Constructors for factory in cell class. - ConcretePort(const char *name, + ConcretePort(std::string_view name, bool is_bus, int from_index, int to_index, diff --git a/include/sta/ConcreteNetwork.hh b/include/sta/ConcreteNetwork.hh index 25ab0f915..b3a377933 100644 --- a/include/sta/ConcreteNetwork.hh +++ b/include/sta/ConcreteNetwork.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include #include @@ -47,9 +48,9 @@ class ConcreteBindingTbl; class ConcreteLibertyLibraryIterator; using ConcreteLibrarySeq = std::vector; -using ConcreteLibraryMap = std::map; -using ConcreteInstanceChildMap = std::map; -using ConcreteInstanceNetMap = std::map; +using ConcreteLibraryMap = std::map>; +using ConcreteInstanceChildMap = std::map>; +using ConcreteInstanceNetMap = std::map>; using ConcreteNetSeq = std::vector; using ConcretePinSeq = std::vector; using CellNetworkViewMap = std::map; @@ -63,26 +64,26 @@ public: ConcreteNetwork(); ~ConcreteNetwork(); void clear() override; - bool linkNetwork(const char *top_cell_name, + bool linkNetwork(std::string_view top_cell_name, bool make_black_boxes, Report *report) override; Instance *topInstance() const override; - const char *name(const Library *library) const override; + std::string name(const Library *library) const override; ObjectId id(const Library *library) const override; LibraryIterator *libraryIterator() const override; LibertyLibraryIterator *libertyLibraryIterator() const override; - Library *findLibrary(const char *name) override; - LibertyLibrary *findLiberty(const char *name) override; + Library *findLibrary(std::string_view name) override; + LibertyLibrary *findLiberty(std::string_view name) override; Cell *findCell(const Library *library, - const char *name) const override; - Cell *findAnyCell(const char *name) override; + std::string_view name) const override; + Cell *findAnyCell(std::string_view name) override; CellSeq findCellsMatching(const Library *library, const PatternMatch *pattern) const override; - const char *name(const Cell *cell) const override; + std::string name(const Cell *cell) const override; std::string getAttribute(const Cell *cell, - const std::string &key) const override; + std::string_view key) const override; const AttributeMap &attributeMap(const Cell *cell) const override; ObjectId id(const Cell *cell) const override; Library *library(const Cell *cell) const override; @@ -90,15 +91,15 @@ public: const LibertyCell *libertyCell(const Cell *cell) const override; Cell *cell(LibertyCell *cell) const override; const Cell *cell(const LibertyCell *cell) const override; - const char *filename(const Cell *cell) override; + std::string_view filename(const Cell *cell) const override; Port *findPort(const Cell *cell, - const char *name) const override; + std::string_view name) const override; bool isLeaf(const Cell *cell) const override; CellPortIterator *portIterator(const Cell *cell) const override; CellPortBitIterator *portBitIterator(const Cell *cell) const override; int portBitCount(const Cell *cell) const override; - const char *name(const Port *port) const override; + std::string name(const Port *port) const override; ObjectId id(const Port *port) const override; Cell *cell(const Port *port) const override; LibertyPort *libertyPort(const Port *port) const override; @@ -108,7 +109,7 @@ public: bool isBus(const Port *port) const override; int size(const Port *port) const override; - const char *busName(const Port *port) const override; + std::string busName(const Port *port) const override; Port *findBusBit(const Port *port, int index) const override; int fromIndex(const Port *port) const override; @@ -117,18 +118,18 @@ public: int index) const override; PortMemberIterator *memberIterator(const Port *port) const override; - const char *name(const Instance *instance) const override; + std::string name(const Instance *instance) const override; std::string getAttribute(const Instance *inst, - const std::string &key) const override; + std::string_view key) const override; const AttributeMap &attributeMap(const Instance *inst) const override; ObjectId id(const Instance *instance) const override; Cell *cell(const Instance *instance) const override; Instance *parent(const Instance *instance) const override; bool isLeaf(const Instance *instance) const override; Instance *findChild(const Instance *parent, - const char *name) const override; + std::string_view name) const override; Pin *findPin(const Instance *instance, - const char *port_name) const override; + std::string_view port_name) const override; Pin *findPin(const Instance *instance, const Port *port) const override; @@ -153,10 +154,10 @@ public: Net *net(const Term *term) const override; Pin *pin(const Term *term) const override; - const char *name(const Net *net) const override; + std::string name(const Net *net) const override; ObjectId id(const Net *net) const override; Net *findNet(const Instance *instance, - const char *net_name) const override; + std::string_view net_name) const override; void findInstNetsMatching(const Instance *instance, const PatternMatch *pattern, NetSeq &matches) const override; @@ -174,44 +175,44 @@ public: LogicValue value) override; // Edit methods. - Library *makeLibrary(const char *name, - const char *filename) override; - LibertyLibrary *makeLibertyLibrary(const char *name, - const char *filename) override; + Library *makeLibrary(std::string_view name, + std::string_view filename) override; + LibertyLibrary *makeLibertyLibrary(std::string_view name, + std::string_view filename) override; void deleteLibrary(Library *library) override; Cell *makeCell(Library *library, - const char *name, + std::string_view name, bool is_leaf, - const char *filename) override; + std::string_view filename) override; void deleteCell(Cell *cell) override; void setName(Cell *cell, - const char *name) override; + std::string_view name) override; void setIsLeaf(Cell *cell, bool is_leaf) override; void setAttribute(Cell *cell, - const std::string &key, - const std::string &value) override; + std::string_view key, + std::string_view value) override; Port *makePort(Cell *cell, - const char *name) override; + std::string_view name) override; Port *makeBusPort(Cell *cell, - const char *name, + std::string_view name, int from_index, int to_index) override; void groupBusPorts(Cell *cell, - std::function port_msb_first) override; + std::function port_msb_first) override; Port *makeBundlePort(Cell *cell, - const char *name, + std::string_view name, PortSeq *members) override; void setDirection(Port *port, PortDirection *dir) override; // For NetworkEdit. Instance *makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) override; void makePins(Instance *inst) override; // For linking. Instance *makeInstance(Cell *cell, - const char *name, + std::string_view name, Instance *parent) override; void replaceCell(Instance *inst, Cell *cell) override; @@ -223,11 +224,11 @@ public: LibertyPort *port, Net *net) override; void setAttribute(Instance *inst, - const std::string &key, - const std::string &value) override; + std::string_view key, + std::string_view value) override; void disconnectPin(Pin *pin) override; void deletePin(Pin *pin) override; - Net *makeNet(const char *name, + Net *makeNet(std::string_view name, Instance *parent) override; void deleteNet(Net *net) override; @@ -263,13 +264,13 @@ public: protected: void addLibrary(ConcreteLibrary *library); - void setName(const char *name); + void setName(std::string_view name); void clearConstantNets(); void visitConnectedPins(const Net *net, PinVisitor &visitor, NetSet &visited_nets) const override; Instance *makeConcreteInstance(ConcreteCell *cell, - const char *name, + std::string_view name, Instance *parent); void disconnectNetPin(ConcreteNet *cnet, ConcretePin *cpin); @@ -292,40 +293,40 @@ private: class ConcreteInstance { public: - const char *name() const { return name_; } + std::string_view name() const { return name_; } ObjectId id() const { return id_; } Cell *cell() const; ConcreteInstance *parent() const { return parent_; } - ConcretePin *findPin(const char *port_name) const; + ConcretePin *findPin(std::string_view port_name) const; ConcretePin *findPin(const Port *port) const; - ConcreteNet *findNet(const char *net_name) const; + ConcreteNet *findNet(std::string_view net_name) const; void findNetsMatching(const PatternMatch *pattern, NetSeq &matches) const; InstanceNetIterator *netIterator() const; - Instance *findChild(const char *name) const; + Instance *findChild(std::string_view name) const; InstanceChildIterator *childIterator() const; - void setAttribute(const std::string &key, - const std::string &value); - std::string getAttribute(const std::string &key) const; + void setAttribute(std::string_view key, + std::string_view value); + std::string getAttribute(std::string_view key) const; const AttributeMap &attributeMap() const { return attribute_map_; } void addChild(ConcreteInstance *child); void deleteChild(ConcreteInstance *child); void addPin(ConcretePin *pin); void deletePin(ConcretePin *pin); void addNet(ConcreteNet *net); - void addNet(const char *name, + void addNet(std::string_view name, ConcreteNet *net); void deleteNet(ConcreteNet *net); void setCell(ConcreteCell *cell); void initPins(); protected: - ConcreteInstance(const char *name, + ConcreteInstance(std::string_view name, ConcreteCell *cell, ConcreteInstance *parent); ~ConcreteInstance(); - const char *name_; + std::string name_; ObjectId id_; ConcreteCell *cell_; ConcreteInstance *parent_; @@ -343,7 +344,7 @@ private: class ConcretePin { public: - const char *name() const; + std::string_view name() const; ConcreteInstance *instance() const { return instance_; } ConcreteNet *net() const { return net_; } ConcretePort *port() const { return port_; } @@ -377,7 +378,7 @@ private: class ConcreteTerm { public: - const char *name() const; + std::string_view name() const; ObjectId id() const { return id_; } ConcreteNet *net() const { return net_; } ConcretePin *pin() const { return pin_; } @@ -402,7 +403,7 @@ private: class ConcreteNet { public: - const char *name() const { return name_; } + std::string_view name() const { return name_; } ObjectId id() const { return id_; } ConcreteInstance *instance() const { return instance_; } void addPin(ConcretePin *pin); @@ -413,10 +414,9 @@ public: ConcreteNet *mergedInto() { return merged_into_; } protected: - ConcreteNet(const char *name, + ConcreteNet(std::string_view name, ConcreteInstance *instance); - ~ConcreteNet(); - const char *name_; + std::string name_; ObjectId id_; ConcreteInstance *instance_; // Pointer to head of linked list of pins. diff --git a/include/sta/ContainerHelpers.hh b/include/sta/ContainerHelpers.hh index a626bc18c..010d30e71 100644 --- a/include/sta/ContainerHelpers.hh +++ b/include/sta/ContainerHelpers.hh @@ -143,7 +143,7 @@ struct find_return }; -// Find an pointer value in a reference to a contaiiner of pointers. +// Find a pointer value in a reference to a contaiiner of pointers. // Return nullptr if not found. template auto @@ -166,29 +166,24 @@ findKey(const AssocContainer& c, return *it; // set } -// Find an pointer value in a pointer to a contaiiner of pointers. +// Find a pointer value in a reference to a map that uses strings as keys. // Return nullptr if not found. template auto -findKey(const AssocContainer *c, - typename AssocContainer::key_type key) +findStringKey(const AssocContainer& c, + std::string_view key) -> typename find_return::type { using ReturnType = typename find_return::type; static_assert(std::is_pointer_v, - "findKey requires pointer types"); + "findStringKey requires pointer types"); - auto it = c->find(key); - if (it == c->end()) + auto it = c.find(key); + if (it == c.end()) return nullptr; - - if constexpr (has_mapped_type::value) - // map - return it->second; else - // set - return *it; + return it->second; } //////////////////////////////////////////////////////////////// @@ -212,7 +207,7 @@ findKeyValue(const AssocContainer& c, return empty; } -// Find an value reference in a reference to a contaiiner of objects. +// Find a value reference in a reference to a contaiiner of objects. // Return exists. template void @@ -239,7 +234,7 @@ findKeyValue(const AssocContainer& c, } } -// Find an value reference in a pointer to a contaiiner of objects. +// Find a value reference in a pointer to a contaiiner of objects. // Return exists. template void @@ -268,7 +263,8 @@ findKeyValue(const AssocContainer *c, //////////////////////////////////////////////////////////////// -// Find an value pointer in a reference to a contaiiner of objects. +// Find a value pointer in a reference to a contaiiner of objects. +// Return nullptr if not found. template auto findKeyValuePtr(AssocContainer& c, @@ -283,17 +279,17 @@ findKeyValuePtr(AssocContainer& c, // map return &it->second; else - // set + // sett return *it; } -// Find an pointger to a value in a const reference to a contaiiner objects. +// Find a value pointer in a reference to a contaiiner of objects. // Return nullptr if not found. template auto findKeyValuePtr(const AssocContainer& c, typename AssocContainer::key_type key) - -> const typename find_return::type* + -> typename find_return::type const* { auto it = c.find(key); if (it == c.end()) @@ -307,6 +303,38 @@ findKeyValuePtr(const AssocContainer& c, return *it; } +// Find a pointer to a value in a reference to a contaiiner of objects +// using std::string as the key. +// Return nullptr if not found. +template +auto +findStringValuePtr(AssocContainer& c, + std::string_view key) + -> typename find_return::type* +{ + auto it = c.find(key); + if (it == c.end()) + return nullptr; + else + return &it->second; +} + +// Find a const pointer to a value in a const reference to a contaiiner objects +// using std::string as the key. +// Return nullptr if not found. +template +auto +findStringValuePtr(const AssocContainer& c, + std::string_view key) + -> typename find_return::type const* +{ + auto it = c.find(key); + if (it == c.end()) + return nullptr; + else + return &it->second; +} + //////////////////////////////////////////////////////////////// // Determine if two std::set's intersect. diff --git a/include/sta/Debug.hh b/include/sta/Debug.hh index 1ff459342..afbb68e27 100644 --- a/include/sta/Debug.hh +++ b/include/sta/Debug.hh @@ -38,20 +38,20 @@ namespace sta { class Report; class Pin; -using DebugMap = std::map; +using DebugMap = std::map>; class Debug { public: Debug(Report *report); - int level(const char *what); - void setLevel(const char *what, + int level(std::string_view what); + void setLevel(std::string_view what, int level); - bool check(const char *what, + bool check(std::string_view what, int level) const; int statsLevel() const { return stats_level_; } template - void report(const char *what, + void report(std::string_view what, std::string_view fmt, Args &&...args) { diff --git a/include/sta/DelayCalc.hh b/include/sta/DelayCalc.hh index be0da6d32..4969b9566 100644 --- a/include/sta/DelayCalc.hh +++ b/include/sta/DelayCalc.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include "StringUtil.hh" @@ -40,10 +41,10 @@ void registerDelayCalcs(); // Register a delay calculator for the set_delay_calc command. void -registerDelayCalc(const std::string &name, +registerDelayCalc(std::string_view name, MakeArcDelayCalc maker); bool -isDelayCalcName(const std::string &name); +isDelayCalcName(std::string_view name); StringSeq delayCalcNames(); void @@ -51,7 +52,7 @@ deleteDelayCalcs(); // Make a registered delay calculator by name. ArcDelayCalc * -makeDelayCalc(const std::string &name, +makeDelayCalc(std::string_view name, StaState *sta); } // namespace diff --git a/include/sta/EnumNameMap.hh b/include/sta/EnumNameMap.hh index 68d1fe9aa..9ae20acb3 100644 --- a/include/sta/EnumNameMap.hh +++ b/include/sta/EnumNameMap.hh @@ -35,17 +35,17 @@ class EnumNameMap { public: EnumNameMap(std::initializer_list> enum_names); - const char *find(ENUM key) const; - ENUM find(std::string name, + const std::string &find(ENUM key) const; + ENUM find(std::string_view name, ENUM unknown_key) const; - void find(std::string name, + void find(std::string_view name, // Return values. ENUM &key, bool &exists) const; private: std::map enum_map_; - std::map name_map_; + std::map> name_map_; }; template @@ -57,19 +57,21 @@ EnumNameMap::EnumNameMap(std::initializer_list -const char * +const std::string& EnumNameMap::find(ENUM key) const { auto find_iter = enum_map_.find(key); if (find_iter != enum_map_.end()) - return find_iter->second.c_str(); - else - return nullptr; + return find_iter->second; + else { + static std::string null_ref; + return null_ref; + } } template void -EnumNameMap::find(std::string name, +EnumNameMap::find(std::string_view name, // Return values. ENUM &key, bool &exists) const @@ -85,7 +87,7 @@ EnumNameMap::find(std::string name, template ENUM -EnumNameMap::find(std::string name, +EnumNameMap::find(std::string_view name, ENUM unknown_key) const { auto find_iter = name_map_.find(name); diff --git a/include/sta/Error.hh b/include/sta/Error.hh index a653024d1..80fb1b0ae 100644 --- a/include/sta/Error.hh +++ b/include/sta/Error.hh @@ -37,7 +37,7 @@ class Exception : public std::exception public: Exception(); virtual ~Exception() {} - virtual const char *what() const noexcept = 0; + const char *what() const noexcept override = 0; }; class ExceptionMsg : public Exception @@ -45,8 +45,8 @@ class ExceptionMsg : public Exception public: ExceptionMsg(const std::string &msg, const bool suppressed); - virtual const char *what() const noexcept; - virtual bool suppressed() const { return suppressed_; } + const char *what() const noexcept override; + bool suppressed() const { return suppressed_; } private: std::string msg_; @@ -68,11 +68,10 @@ protected: class FileNotReadable : public Exception { public: - FileNotReadable(std::string filename); - virtual const char *what() const noexcept; + FileNotReadable(std::string_view filename); + const char *what() const noexcept override; protected: - std::string filename_; std::string msg_; }; @@ -80,11 +79,10 @@ protected: class FileNotWritable : public Exception { public: - FileNotWritable(std::string filename); - virtual const char *what() const noexcept; + FileNotWritable(std::string_view filename); + const char *what() const noexcept override; protected: - std::string filename_; std::string msg_; }; diff --git a/include/sta/ExceptionPath.hh b/include/sta/ExceptionPath.hh index 9e4a555cd..bfe9c16df 100644 --- a/include/sta/ExceptionPath.hh +++ b/include/sta/ExceptionPath.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include "Error.hh" @@ -57,7 +58,7 @@ public: const MinMaxAll *min_max, bool own_pts, int priority, - const char *comment); + std::string_view comment); virtual ~ExceptionPath(); size_t id() const { return id_; } void setId(size_t id); @@ -128,7 +129,7 @@ public: virtual bool useEndClk() const { return false; } virtual int pathMultiplier() const { return 0; } virtual float delay() const { return 0.0; } - virtual std::string name() const { return ""; } + virtual std::string_view name() const { return {}; } virtual bool isDefault() const { return false; } virtual bool ignoreClkLatency() const { return false; } virtual bool breakPath() const { return false; } @@ -157,14 +158,14 @@ public: ExceptionTo *to, const MinMaxAll *min_max, bool own_pts, - const char *comment); + std::string_view comment); FalsePath(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max, bool own_pts, int priority, - const char *comment); + std::string_view comment); ExceptionPath *clone(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, @@ -203,7 +204,7 @@ public: bool break_path, float delay, bool own_pts, - const char *comment); + std::string_view comment); ExceptionPath *clone(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, @@ -237,7 +238,7 @@ public: bool use_end_clk, int path_multiplier, bool own_pts, - const char *comment); + std::string_view comment); ExceptionPath *clone(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, @@ -293,13 +294,13 @@ public: class GroupPath : public ExceptionPath { public: - GroupPath(const std::string &name, + GroupPath(std::string_view name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, bool own_pts, - const char *comment); + std::string_view comment); ~GroupPath() override; ExceptionPath *clone(ExceptionFrom *from, ExceptionThruSeq *thrus, @@ -312,7 +313,7 @@ public: bool overrides(ExceptionPath *exception) const override; int typePriority() const override; bool tighterThan(ExceptionPath *exception) const override; - std::string name() const override { return name_; } + std::string_view name() const override { return name_; } bool isDefault() const override { return is_default_; } protected: diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 6846ae898..a74759628 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -220,7 +220,7 @@ protected: PinVertexMap pin_bidirect_drvr_vertex_map_; DcalcAPIndex ap_count_; // Sdf period check annotations. - PeriodCheckAnnotations *period_check_annotations_; + PeriodCheckAnnotations period_check_annotations_; // Register/latch clock vertices to search from. VertexSet reg_clk_vertices_; @@ -241,7 +241,7 @@ public: // Pin path with load/driver suffix for bidirects. std::string to_string(const StaState *sta) const; // compatibility - const char *name(const Network *network) const; + std::string name(const Network *network) const; [[nodiscard]] bool isBidirectDriver() const { return is_bidirect_drvr_; } [[nodiscard]] bool isDriver(const Network *network) const; Level level() const { return level_; } diff --git a/include/sta/Hash.hh b/include/sta/Hash.hh index ebd833a92..6f87d56cf 100644 --- a/include/sta/Hash.hh +++ b/include/sta/Hash.hh @@ -26,6 +26,7 @@ #include #include +#include namespace sta { @@ -56,7 +57,7 @@ nextMersenne(size_t n) // Sadly necessary until c++ std::hash works for char *. size_t -hashString(const char *str); +hashString(std::string_view str); // Pointer hashing is strongly discouraged because it causes results to change // from run to run. Use Network::id functions instead. diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 450f1dff6..732545a4e 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "ContainerHelpers.hh" @@ -68,7 +69,7 @@ class DriverWaveform; class ModeValueDef { public: - ModeValueDef(std::string value, FuncExpr *cond, std::string sdf_cond); + ModeValueDef(std::string value); ModeValueDef(ModeValueDef &&other) noexcept; ~ModeValueDef(); const std::string &value() const { return value_; } @@ -115,14 +116,14 @@ private: TimingArcSet *setup_check_; }; -using TableTemplateMap = std::map; +using TableTemplateMap = std::map>; using TableTemplateSeq = std::vector; -using BusDclMap = std::map; +using BusDclMap = std::map>; using BusDclSeq = std::vector; -using ScaleFactorsMap = std::map; -using WireloadMap = std::map; -using WireloadSelectionMap = std::map; -using OperatingConditionsMap = std::map; +using ScaleFactorsMap = std::map>; +using WireloadMap = std::map>; +using WireloadSelectionMap = std::map>; +using OperatingConditionsMap = std::map>; using PortToSequentialMap = std::map; using TimingArcSetSeq = std::vector; using TimingArcSetSet = std::set; @@ -135,13 +136,13 @@ using PortInternalPowerMap = std::map; using ScaledCellMap = std::map; using ScaledPortMap = std::map; -using ModeDefMap = std::map; -using ModeValueMap = std::map; +using ModeDefMap = std::map>; +using ModeValueMap = std::map>; using LatchEnableIndexMap = std::map; using LatchEnableSeq = std::vector; -using OcvDerateMap = std::map; -using SupplyVoltageMap = std::map; -using DriverWaveformMap = std::map; +using OcvDerateMap = std::map>; +using SupplyVoltageMap = std::map>; +using DriverWaveformMap = std::map>; using SceneSeq = std::vector; enum class ClockGateType { none, latch_posedge, latch_negedge, other }; @@ -175,13 +176,13 @@ void deleteLiberty(); ScaleFactorPvt -findScaleFactorPvt(const char *name); -const char * +findScaleFactorPvt(std::string_view name); +const std::string& scaleFactorPvtName(ScaleFactorPvt pvt); ScaleFactorType -findScaleFactorType(const char *name); -const char * +findScaleFactorType(std::string_view name); +const std::string& scaleFactorTypeName(ScaleFactorType type); bool scaleFactorTypeRiseFallSuffix(ScaleFactorType type); @@ -191,7 +192,7 @@ bool scaleFactorTypeLowHighSuffix(ScaleFactorType type); // Timing sense as a string. -const char * +const std::string& to_string(TimingSense sense); // Opposite timing sense. @@ -203,10 +204,10 @@ timingSenseOpposite(TimingSense sense); class LibertyLibrary : public ConcreteLibrary { public: - LibertyLibrary(const char *name, - const char *filename); + LibertyLibrary(std::string name, + std::string filename); virtual ~LibertyLibrary(); - LibertyCell *findLibertyCell(const char *name) const; + LibertyCell *findLibertyCell(std::string_view name) const; LibertyCellSeq findLibertyCellsMatching(PatternMatch *pattern); // Liberty cells that are buffers. LibertyCellSeq *buffers(); @@ -214,12 +215,14 @@ public: DelayModelType delayModelType() const { return delay_model_type_; } void setDelayModelType(DelayModelType type); - BusDcl *makeBusDcl(std::string name, int from, int to); - BusDcl *findBusDcl(const char *name) const; + BusDcl *makeBusDcl(std::string name, + int from, + int to); + BusDcl *findBusDcl(std::string_view name); BusDclSeq busDcls() const; TableTemplate *makeTableTemplate(std::string name, TableTemplateType type); - TableTemplate *findTableTemplate(const char *name, + TableTemplate *findTableTemplate(std::string_view name, TableTemplateType type); TableTemplateSeq tableTemplates() const; TableTemplateSeq tableTemplates(TableTemplateType type) const; @@ -232,8 +235,8 @@ public: void setScaleFactors(ScaleFactors *scales); // Make named scale factor group. Returns pointer to the inserted element. - ScaleFactors *makeScaleFactors(const char *name); - ScaleFactors *findScaleFactors(const char *name); + ScaleFactors *makeScaleFactors(std::string name); + ScaleFactors *findScaleFactors(std::string_view name); ScaleFactors *scaleFactors() const { return scale_factors_; } float scaleFactor(ScaleFactorType type, const Pvt *pvt) const; @@ -334,18 +337,18 @@ public: const Units *units() const { return units_; } Wireload *makeWireload(std::string name); - const Wireload *findWireload(const char *name) const; + const Wireload *findWireload(std::string_view name); void setDefaultWireload(const Wireload *wireload); const Wireload *defaultWireload() const; WireloadSelection *makeWireloadSelection(std::string name); - const WireloadSelection *findWireloadSelection(const char *name) const; + const WireloadSelection *findWireloadSelection(std::string_view name) const; const WireloadSelection *defaultWireloadSelection() const; WireloadMode defaultWireloadMode() const; void setDefaultWireloadMode(WireloadMode mode); void setDefaultWireloadSelection(const WireloadSelection *selection); OperatingConditions *makeOperatingConditions(std::string name); - OperatingConditions *findOperatingConditions(const char *name); + OperatingConditions *findOperatingConditions(std::string_view name); OperatingConditions *defaultOperatingConditions() const; void setDefaultOperatingConditions(OperatingConditions *op_cond); @@ -356,18 +359,18 @@ public: OcvDerate *defaultOcvDerate() const; void setDefaultOcvDerate(OcvDerate *derate); OcvDerate *makeOcvDerate(std::string name); - OcvDerate *findOcvDerate(const char *derate_name); - void addSupplyVoltage(const char *suppy_name, + OcvDerate *findOcvDerate(std::string_view derate_name); + void addSupplyVoltage(std::string suppy_name, float voltage); - bool supplyExists(const char *suppy_name) const; - void supplyVoltage(const char *supply_name, + bool supplyExists(std::string_view supply_name) const; + void supplyVoltage(std::string_view supply_name, // Return value. float &voltage, bool &exists) const; // Make scaled cell. Call LibertyCell::addScaledCell after it is complete. - LibertyCell *makeScaledCell(const char *name, - const char *filename); + LibertyCell *makeScaledCell(std::string name, + std::string filename); static void makeSceneMap(LibertyLibrary *lib, @@ -390,9 +393,9 @@ public: const SceneSeq &scenes, Report *report); - DriverWaveform *findDriverWaveform(const char *name); + DriverWaveform *findDriverWaveform(std::string_view name); DriverWaveform *driverWaveformDefault() { return findDriverWaveform(""); } - DriverWaveform *makeDriverWaveform(const std::string &name, + DriverWaveform *makeDriverWaveform(std::string name, TablePtr waveforms); protected: @@ -471,18 +474,18 @@ class LibertyCell : public ConcreteCell { public: LibertyCell(LibertyLibrary *library, - const char *name, - const char *filename); + std::string name, + std::string filename); virtual ~LibertyCell(); LibertyLibrary *libertyLibrary() const { return liberty_library_; } LibertyLibrary *libertyLibrary() { return liberty_library_; } - LibertyPort *findLibertyPort(const char *name) const; + LibertyPort *findLibertyPort(std::string_view name) const; LibertyPortSeq findLibertyPortsMatching(PatternMatch *pattern) const; bool hasInternalPorts() const { return has_internal_ports_; } ScaleFactors *scaleFactors() const { return scale_factors_; } void setScaleFactors(ScaleFactors *scale_factors); ModeDef *makeModeDef(std::string name); - const ModeDef *findModeDef(const char *name) const; + const ModeDef *findModeDef(std::string_view name) const; float area() const { return area_; } void setArea(float area); @@ -541,8 +544,10 @@ public: const Statetable *statetable() const { return statetable_; } // Find bus declaration local to this cell. - BusDcl *makeBusDcl(std::string name, int from, int to); - BusDcl *findBusDcl(const char *name) const; + BusDcl *makeBusDcl(std::string name, + int from, + int to); + BusDcl *findBusDcl(std::string_view name); // True when TimingArcSetBuilder::makeRegLatchArcs infers register // timing arcs. bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; } @@ -561,7 +566,7 @@ public: float ocvArcDepth() const; OcvDerate *ocvDerate() const; OcvDerate *makeOcvDerate(std::string name); - OcvDerate *findOcvDerate(const char *derate_name); + OcvDerate *findOcvDerate(std::string_view derate_name); // Build helpers. void makeSequential(int size, @@ -614,10 +619,10 @@ public: // for all the defined scenes. static void checkLibertyScenes(); void ensureVoltageWaveforms(const SceneSeq &scenes); - const char *footprint() const; - void setFootprint(const char *footprint); - const char *userFunctionClass() const; - void setUserFunctionClass(const char *user_function_class); + const std::string &footprint() const { return footprint_; } + void setFootprint(std::string footprint); + const std::string &userFunctionClass() const { return user_function_class_; } + void setUserFunctionClass(std::string user_function_class); protected: void addPort(ConcretePort *port); @@ -752,8 +757,8 @@ public: bool isPwrGnd() const; PwrGndType pwrGndType() const { return pwr_gnd_type_; } void setPwrGndType(PwrGndType type); - const char *voltageName() const { return voltage_name_.c_str(); } - void setVoltageName(const char *voltage_name); + const std::string &voltageName() const { return voltage_name_; } + void setVoltageName(std::string voltage_name); //////////////////////////////////////////////////////////////// ScanSignalType scanSignalType() const { return scan_signal_type_; } @@ -876,10 +881,10 @@ public: const LibertyPort *scenePort(int ap_index) const; void setScenePort(LibertyPort *scene_port, int ap_index); - const char *relatedGroundPin() const; - void setRelatedGroundPin(const char *related_ground_pin); - const char *relatedPowerPin() const; - void setRelatedPowerPin(const char *related_power_pin); + LibertyPort *relatedGroundPort() const { return related_ground_port_; } + void setRelatedGroundPort(LibertyPort *related_ground_port); + LibertyPort *relatedPowerPort() const { return related_power_port_; } + void setRelatedPowerPort(LibertyPort *related_power_port); const ReceiverModel *receiverModel() const { return receiver_model_.get(); } void setReceiverModel(ReceiverModelPtr receiver_model); DriverWaveform *driverWaveform(const RiseFall *rf) const; @@ -901,7 +906,7 @@ public: protected: // Constructor is internal to LibertyBuilder. LibertyPort(LibertyCell *cell, - const char *name, + std::string name, bool is_bus, BusDcl *bus_dcl, int from_index, @@ -942,8 +947,8 @@ protected: float min_pulse_width_[RiseFall::index_count]; const RiseFall *pulse_clk_trigger_; const RiseFall *pulse_clk_sense_; - std::string related_ground_pin_; - std::string related_power_pin_; + LibertyPort *related_ground_port_; + LibertyPort *related_power_port_; std::vector scene_ports_; ReceiverModelPtr receiver_model_; DriverWaveform *driver_waveform_[RiseFall::index_count]; @@ -1011,13 +1016,8 @@ protected: class OperatingConditions : public Pvt { public: - OperatingConditions(const char *name); - OperatingConditions(const char *name, - float process, - float voltage, - float temperature, - WireloadTree wire_load_tree); - const char *name() const { return name_.c_str(); } + OperatingConditions(std::string name); + const std::string &name() const { return name_; } WireloadTree wireloadTree() const { return wire_load_tree_; } void setWireloadTree(WireloadTree tree); @@ -1029,8 +1029,8 @@ protected: class ScaleFactors { public: - ScaleFactors(const char *name); - const char *name() const { return name_.c_str(); } + ScaleFactors(std::string name); + const std::string &name() const { return name_; } float scale(ScaleFactorType type, ScaleFactorPvt pvt, const RiseFall *rf); @@ -1073,14 +1073,11 @@ protected: class ModeDef { public: + ModeDef(std::string name); const std::string &name() const { return name_; } - ModeValueDef *defineValue(const char *value, - FuncExpr *cond, - const char *sdf_cond); - const ModeValueDef *findValueDef(const char *value) const; - const ModeValueMap *values() const { return &values_; } - - explicit ModeDef(std::string name); + ModeValueDef *defineValue(std::string value); + const ModeValueDef *findValueDef(std::string_view value) const; + const ModeValueMap &values() const { return values_; } protected: std::string name_; @@ -1152,12 +1149,12 @@ private: }; std::string -portLibertyToSta(const char *port_name); -const char * +portLibertyToSta(std::string_view port_name); +const std::string & scanSignalTypeName(ScanSignalType scan_type); -const char * +const std::string & pwrGndTypeName(PwrGndType pwr_gnd_type); PwrGndType -findPwrGndType(const char *pg_name); +findPwrGndType(std::string_view pg_name); } // namespace diff --git a/include/sta/LinearModel.hh b/include/sta/LinearModel.hh index 072517395..0ee16401e 100644 --- a/include/sta/LinearModel.hh +++ b/include/sta/LinearModel.hh @@ -76,7 +76,7 @@ public: PocvMode pocv_mode) const override; std::string reportCheckDelay(const Pvt *pvt, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, const MinMax *min_max, diff --git a/include/sta/Mode.hh b/include/sta/Mode.hh index ddb824ad0..112226191 100644 --- a/include/sta/Mode.hh +++ b/include/sta/Mode.hh @@ -42,7 +42,7 @@ using PathGroupSeq = std::vector; class Mode : public StaState { public: - Mode(const std::string &name, + Mode(std::string_view name, size_t mode_index, StaState *sta); virtual ~Mode(); diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 64480738d..b12db2db0 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -26,6 +26,8 @@ #include #include +#include +#include #include "StringUtil.hh" #include "LibertyClass.hh" @@ -39,11 +41,11 @@ class Report; class PatternMatch; class PinVisitor; -using LibertyLibraryMap = std::map; +using LibertyLibraryMap = std::map>; // Link network function returns top level instance. // Return nullptr if link fails. -using LinkNetworkFunc = std::function; +using LinkNetworkFunc = std::function; using NetDrvrPinsMap = std::map; // The Network class defines the network API used by sta. @@ -100,7 +102,7 @@ public: // has been linked. When the network interfaces to an external database, // linking is not necessary because the network has already been expanded. // Return true if successful. - virtual bool linkNetwork(const char *top_cell_name, + virtual bool linkNetwork(std::string_view top_cell_name, bool make_black_boxes, Report *report) = 0; virtual bool isLinked() const; @@ -108,23 +110,23 @@ public: //////////////////////////////////////////////////////////////// // Library functions. - virtual const char *name(const Library *library) const = 0; + virtual std::string name(const Library *library) const = 0; virtual ObjectId id(const Library *library) const = 0; virtual LibraryIterator *libraryIterator() const = 0; virtual LibertyLibraryIterator *libertyLibraryIterator() const = 0; - virtual Library *findLibrary(const char *name) = 0; - virtual LibertyLibrary *findLiberty(const char *name) = 0; + virtual Library *findLibrary(std::string_view name) = 0; + virtual LibertyLibrary *findLiberty(std::string_view name) = 0; // Find liberty library by filename. - virtual LibertyLibrary *findLibertyFilename(const char *filename); + virtual LibertyLibrary *findLibertyFilename(std::string_view filename); virtual Cell *findCell(const Library *library, - const char *name) const = 0; + std::string_view name) const = 0; // Search the design (non-liberty) libraries for cells matching pattern. virtual CellSeq findCellsMatching(const Library *library, const PatternMatch *pattern) const = 0; // Search liberty libraries for cell name. - virtual LibertyCell *findLibertyCell(const char *name) const; - virtual LibertyLibrary *makeLibertyLibrary(const char *name, - const char *filename) = 0; + virtual LibertyCell *findLibertyCell(std::string_view name) const; + virtual LibertyLibrary *makeLibertyLibrary(std::string_view name, + std::string_view filename) = 0; // Hook for network after reading liberty library. virtual void readLibertyAfter(LibertyLibrary *library); // First liberty library read is used to look up defaults. @@ -139,7 +141,7 @@ public: //////////////////////////////////////////////////////////////// // Cell functions. - virtual const char *name(const Cell *cell) const = 0; + virtual std::string name(const Cell *cell) const = 0; virtual ObjectId id(const Cell *cell) const = 0; virtual Library *library(const Cell *cell) const = 0; virtual LibertyLibrary *libertyLibrary(const Cell *cell) const; @@ -149,14 +151,13 @@ public: virtual const Cell *cell(const LibertyCell *cell) const = 0; virtual Cell *cell(LibertyCell *cell) const = 0; // Filename may return null. - virtual const char *filename(const Cell *cell) = 0; - // Attributes can be null + virtual std::string_view filename(const Cell *cell) const = 0; virtual std::string getAttribute(const Cell *cell, - const std::string &key) const = 0; + std::string_view key) const = 0; virtual const AttributeMap &attributeMap(const Cell *cell) const = 0; // Name can be a simple, bundle, bus, or bus bit name. virtual Port *findPort(const Cell *cell, - const char *name) const = 0; + std::string_view name) const = 0; virtual PortSeq findPortsMatching(const Cell *cell, const PatternMatch *pattern) const; virtual bool isLeaf(const Cell *cell) const = 0; @@ -168,7 +169,7 @@ public: //////////////////////////////////////////////////////////////// // Port functions - virtual const char *name(const Port *port) const = 0; + virtual std::string name(const Port *port) const = 0; virtual ObjectId id(const Port *port) const = 0; virtual Cell *cell(const Port *port) const = 0; virtual LibertyPort *libertyPort(const Port *port) const = 0; @@ -179,7 +180,7 @@ public: // Size is the bus/bundle member count (1 for non-bus/bundle ports). virtual int size(const Port *port) const = 0; // Bus range bus[from:to]. - virtual const char *busName(const Port *port) const = 0; + virtual std::string busName(const Port *port) const = 0; // Bus member, bus[subscript]. virtual Port *findBusBit(const Port *port, int index) const = 0; @@ -202,25 +203,25 @@ public: //////////////////////////////////////////////////////////////// // Instance functions // Name local to containing cell/instance. - virtual const char *name(const Instance *instance) const = 0; + virtual std::string name(const Instance *instance) const = 0; virtual ObjectId id(const Instance *instance) const = 0; // Top level instance of the design (defined after link). virtual Instance *topInstance() const = 0; virtual bool isTopInstance(const Instance *inst) const; - virtual Instance *findInstance(const char *path_name) const; + virtual Instance *findInstance(std::string_view path_name) const; // Find instance relative to hierarchical instance. virtual Instance *findInstanceRelative(const Instance *inst, - const char *path_name) const; + std::string_view path_name) const; // Default implementation uses linear search. virtual InstanceSeq findInstancesMatching(const Instance *context, const PatternMatch *pattern) const; virtual InstanceSeq findInstancesHierMatching(const Instance *instance, const PatternMatch *pattern) const; virtual std::string getAttribute(const Instance *inst, - const std::string &key) const = 0; + std::string_view key) const = 0; virtual const AttributeMap &attributeMap(const Instance *inst) const = 0; // Hierarchical path name. - virtual const char *pathName(const Instance *instance) const; + virtual std::string pathName(const Instance *instance) const; bool pathNameLess(const Instance *inst1, const Instance *inst2) const; int pathNameCmp(const Instance *inst1, @@ -230,14 +231,14 @@ public: // Return value. InstanceSeq &path) const; virtual Cell *cell(const Instance *instance) const = 0; - virtual const char *cellName(const Instance *instance) const; + virtual std::string cellName(const Instance *instance) const; virtual LibertyLibrary *libertyLibrary(const Instance *instance) const; virtual LibertyCell *libertyCell(const Instance *instance) const; virtual Instance *parent(const Instance *instance) const = 0; virtual bool isLeaf(const Instance *instance) const = 0; virtual bool isHierarchical(const Instance *instance) const; virtual Instance *findChild(const Instance *parent, - const char *name) const = 0; + std::string_view name) const = 0; virtual void findChildrenMatching(const Instance *parent, const PatternMatch *pattern, // Return value. @@ -270,18 +271,18 @@ public: //////////////////////////////////////////////////////////////// // Pin functions // Name is instance_name/port_name (the same as path name). - virtual const char *name(const Pin *pin) const; + virtual std::string name(const Pin *pin) const; virtual ObjectId id(const Pin *pin) const = 0; - virtual Pin *findPin(const char *path_name) const; + virtual Pin *findPin(std::string_view path_name) const; virtual Pin *findPin(const Instance *instance, - const char *port_name) const = 0; + std::string_view port_name) const = 0; virtual Pin *findPin(const Instance *instance, const Port *port) const; virtual Pin *findPin(const Instance *instance, const LibertyPort *port) const; // Find pin relative to hierarchical instance. Pin *findPinRelative(const Instance *inst, - const char *path_name) const; + std::string_view path_name) const; // Default implementation uses linear search. virtual PinSeq findPinsMatching(const Instance *instance, const PatternMatch *pattern) const; @@ -289,9 +290,9 @@ public: // pattern of the form instance_name/port_name. virtual PinSeq findPinsHierMatching(const Instance *instance, const PatternMatch *pattern) const; - virtual const char *portName(const Pin *pin) const; + virtual std::string portName(const Pin *pin) const; // Path name is instance_name/port_name. - virtual const char *pathName(const Pin *pin) const; + virtual std::string pathName(const Pin *pin) const; bool pathNameLess(const Pin *pin1, const Pin *pin2) const; int pathNameCmp(const Pin *pin1, @@ -349,27 +350,27 @@ public: //////////////////////////////////////////////////////////////// // Terminal functions // Name is instance_name/port_name (the same as path name). - virtual const char *name(const Term *term) const; + virtual std::string name(const Term *term) const; virtual ObjectId id(const Term *term) const = 0; - virtual const char *portName(const Term *term) const; + virtual std::string portName(const Term *term) const; // Path name is instance_name/port_name (pin name). - virtual const char *pathName(const Term *term) const; + virtual std::string pathName(const Term *term) const; virtual Net *net(const Term *term) const = 0; virtual Pin *pin(const Term *term) const = 0; //////////////////////////////////////////////////////////////// // Net functions - virtual const char *name(const Net *net) const = 0; // no hierarchy prefix + virtual std::string name(const Net *net) const = 0; // no hierarchy prefix virtual ObjectId id(const Net *net) const = 0; - virtual Net *findNet(const char *path_name) const; + virtual Net *findNet(std::string_view path_name) const; // Find net relative to hierarchical instance. virtual Net *findNetRelative(const Instance *inst, - const char *path_name) const; + std::string_view path_name) const; // Default implementation uses linear search. virtual NetSeq findNetsMatching(const Instance *context, const PatternMatch *pattern) const; virtual Net *findNet(const Instance *instance, - const char *net_name) const = 0; + std::string_view net_name) const = 0; // Traverse the hierarchy from instance down and find nets matching // pattern of the form instance_name/net_name. virtual NetSeq findNetsHierMatching(const Instance *instance, @@ -378,7 +379,7 @@ public: virtual void findInstNetsMatching(const Instance *instance, const PatternMatch *pattern, NetSeq &matches) const = 0; - virtual const char *pathName(const Net *net) const; + virtual std::string pathName(const Net *net) const; bool pathNameLess(const Net *net1, const Net *net2) const; int pathNameCmp(const Net *net1, @@ -424,17 +425,13 @@ public: //////////////////////////////////////////////////////////////// // Parse path into first/tail (first hierarchy divider separated token). - // first and tail are both null if there are no dividers in path. - // Caller must delete first and tail. - void pathNameFirst(const char *path_name, - char *&first, - char *&tail) const; + void pathNameFirst(std::string_view path_name, + std::string &first, + std::string &tail) const; // Parse path into head/last (last hierarchy divider separated token). - // head and last are both null if there are no dividers in path. - // Caller must delete head and last. - void pathNameLast(const char *path_name, - char *&head, - char *&last) const; + void pathNameLast(std::string_view path_name, + std::string &head, + std::string &last) const; // Divider between instance names in a hierarchical path name. virtual char pathDivider() const { return divider_; } @@ -445,7 +442,7 @@ public: protected: Pin *findPinLinear(const Instance *instance, - const char *port_name) const; + std::string_view port_name) const; void findInstancesMatching1(const Instance *context, size_t context_name_length, const PatternMatch *pattern, @@ -484,7 +481,7 @@ protected: PinSeq &matches) const; // findNet using linear search. Net *findNetLinear(const Instance *instance, - const char *net_name) const; + std::string_view net_name) const; // findNetsMatching using linear search. NetSeq findNetsMatchingLinear(const Instance *instance, const PatternMatch *pattern) const; @@ -506,7 +503,7 @@ public: NetworkEdit(); virtual bool isEditable() const { return true; } virtual Instance *makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) = 0; virtual void makePins(Instance *inst) = 0; virtual void replaceCell(Instance *inst, @@ -523,7 +520,7 @@ public: // Disconnect pin from net. virtual void disconnectPin(Pin *pin) = 0; virtual void deletePin(Pin *pin) = 0; - virtual Net *makeNet(const char *name, + virtual Net *makeNet(std::string_view name, Instance *parent) = 0; // Deleting net disconnects (but does not delete) net pins. virtual void deleteNet(Net *net) = 0; @@ -540,39 +537,39 @@ public: // Called before reading a netlist to delete any previously linked network. virtual void readNetlistBefore() = 0; virtual void setLinkFunc(LinkNetworkFunc link) = 0; - virtual Library *makeLibrary(const char *name, - const char *filename) = 0; + virtual Library *makeLibrary(std::string_view name, + std::string_view filename) = 0; virtual void deleteLibrary(Library *library) = 0; // Search the libraries in read order for a cell by name. - virtual Cell *findAnyCell(const char *name) = 0; + virtual Cell *findAnyCell(std::string_view name) = 0; virtual Cell *makeCell(Library *library, - const char *name, + std::string_view name, bool is_leaf, - const char *filename) = 0; + std::string_view filename) = 0; virtual void deleteCell(Cell *cell) = 0; virtual void setName(Cell *cell, - const char *name) = 0; + std::string_view name) = 0; virtual void setIsLeaf(Cell *cell, bool is_leaf) = 0; virtual void setAttribute(Cell *cell, - const std::string &key, - const std::string &value) = 0; + std::string_view key, + std::string_view value) = 0; virtual void setAttribute(Instance *instance, - const std::string &key, - const std::string &value) = 0; + std::string_view key, + std::string_view value) = 0; virtual Port *makePort(Cell *cell, - const char *name) = 0; + std::string_view name) = 0; virtual Port *makeBusPort(Cell *cell, - const char *name, + std::string_view name, int from_index, int to_index) = 0; virtual void groupBusPorts(Cell *cell, - std::function port_msb_first) = 0; + std::function port_msb_first) = 0; virtual Port *makeBundlePort(Cell *cell, - const char *name, + std::string_view name, PortSeq *members) = 0; virtual Instance *makeInstance(Cell *cell, - const char *name, + std::string_view name, Instance *parent) = 0; virtual Pin *makePin(Instance *inst, Port *port, diff --git a/include/sta/NetworkClass.hh b/include/sta/NetworkClass.hh index 3480d587a..23f63a6a5 100644 --- a/include/sta/NetworkClass.hh +++ b/include/sta/NetworkClass.hh @@ -74,7 +74,7 @@ using ConnectedPinIterator = Iterator; using NetConnectedPinIterator = ConnectedPinIterator; using PinConnectedPinIterator = ConnectedPinIterator; using ObjectId = uint32_t; -using AttributeMap = std::map; +using AttributeMap = std::map>; enum class LogicValue : unsigned { zero, one, unknown, rise, fall }; diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index 5d1800a97..8c010f07b 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -185,7 +185,7 @@ public: // Increment the grounded capacitance on node. virtual void incrCap(ParasiticNode *node, float cap) = 0; - virtual const char *name(const ParasiticNode *node) const = 0; + virtual std::string name(const ParasiticNode *node) const = 0; virtual const Pin *pin(const ParasiticNode *node) const = 0; virtual const Net *net(const ParasiticNode *node, const Network *network) const = 0; diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index d8ba2af66..c7c6c5456 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include #include @@ -50,7 +51,7 @@ class PathGroup { public: // Path group that compares compare slacks. - static PathGroup *makePathGroupArrival(const char *name, + static PathGroup *makePathGroupArrival(std::string_view name, int group_path_count, int endpoint_path_count, bool unique_pins, @@ -58,7 +59,7 @@ public: const MinMax *min_max, const StaState *sta); // Path group that compares arrival time, sorted by min_max. - static PathGroup *makePathGroupSlack(const char *name, + static PathGroup *makePathGroupSlack(std::string_view name, int group_path_count, int endpoint_path_count, bool unique_pins, @@ -82,7 +83,7 @@ public: static int group_path_count_max; protected: - PathGroup(const char *name, + PathGroup(std::string_view name, int group_path_count, int endpoint_path_count, bool unique_pins, @@ -147,10 +148,10 @@ public: PathGroupSeq pathGroups(const PathEnd *path_end) const; static StringSeq pathGroupNames(const PathEnd *path_end, const StaState *sta); - static const char *asyncPathGroupName() { return async_group_name_; } - static const char *pathDelayGroupName() { return path_delay_group_name_; } - static const char *gatedClkGroupName() { return gated_clk_group_name_; } - static const char *unconstrainedGroupName() { return unconstrained_group_name_; } + static std::string_view asyncPathGroupName() { return async_group_name_; } + static std::string_view pathDelayGroupName() { return path_delay_group_name_; } + static std::string_view gatedClkGroupName() { return gated_clk_group_name_; } + static std::string_view unconstrainedGroupName() { return unconstrained_group_name_; } protected: void makeGroupPathEnds(ExceptionTo *to, @@ -219,10 +220,10 @@ protected: // Unconstrained paths. PathGroup *unconstrained_[MinMax::index_count]; - static const char *path_delay_group_name_; - static const char *gated_clk_group_name_; - static const char *async_group_name_; - static const char *unconstrained_group_name_; + static constexpr std::string_view path_delay_group_name_ = "path delay"; + static constexpr std::string_view gated_clk_group_name_ = "gated clock"; + static constexpr std::string_view async_group_name_ = "asynchronous"; + static constexpr std::string_view unconstrained_group_name_ = "unconstrained"; }; } // namespace diff --git a/include/sta/PatternMatch.hh b/include/sta/PatternMatch.hh index 429aef04d..dc41363f8 100644 --- a/include/sta/PatternMatch.hh +++ b/include/sta/PatternMatch.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include "Error.hh" @@ -45,20 +46,17 @@ public: // Regular expressions are always anchored. // If nocase is true, ignore case in the pattern. // Tcl_Interp is optional for reporting regexp compile errors. - PatternMatch(const char *pattern, + PatternMatch(std::string_view pattern, bool is_regexp, bool nocase, Tcl_Interp *interp); // Use unix glob style matching. - PatternMatch(const char *pattern); - PatternMatch(const char *pattern, + PatternMatch(std::string_view pattern); + PatternMatch(std::string_view pattern, const PatternMatch *inherit_from); - PatternMatch(const std::string &pattern, - const PatternMatch *inherit_from); - bool match(const char *str) const; - bool match(const std::string &str) const; - bool matchNoCase(const char *str) const; - const char *pattern() const { return pattern_; } + bool match(std::string_view str) const; + bool matchNoCase(std::string_view str) const; + const std::string &pattern() const { return pattern_; } bool isRegexp() const { return is_regexp_; } bool nocase() const { return nocase_; } Tcl_Interp *tclInterp() const { return interp_; } @@ -67,7 +65,7 @@ public: private: void compileRegexp(); - const char *pattern_; + std::string pattern_; bool is_regexp_; bool nocase_; Tcl_Interp *interp_; @@ -78,7 +76,7 @@ private: class RegexpCompileError : public Exception { public: - RegexpCompileError(const char *pattern); + RegexpCompileError(std::string_view pattern); virtual ~RegexpCompileError() noexcept {} virtual const char *what() const noexcept; @@ -90,14 +88,14 @@ private: // '*' matches zero or more characters // '?' matches any character bool -patternMatch(const char *pattern, - const char *str); +patternMatch(std::string_view pattern, + std::string_view str); bool -patternMatchNoCase(const char *pattern, - const char *str, +patternMatchNoCase(std::string_view pattern, + std::string_view str, bool nocase); // Predicate to find out if there are wildcard characters in the pattern. bool -patternWildcards(const char *pattern); +patternWildcards(std::string_view pattern); } // namespace diff --git a/include/sta/PocvMode.hh b/include/sta/PocvMode.hh index 29d19a639..d75785ec5 100644 --- a/include/sta/PocvMode.hh +++ b/include/sta/PocvMode.hh @@ -24,13 +24,15 @@ #pragma once +#include + namespace sta { enum class PocvMode { scalar, normal, skew_normal }; -const char * +const std::string & pocvModeName(PocvMode mode); PocvMode -findPocvMode(const char *mode_name); +findPocvMode(std::string_view mode_name); } // namespace diff --git a/include/sta/PortDirection.hh b/include/sta/PortDirection.hh index 38ca9b5b1..0b8186f90 100644 --- a/include/sta/PortDirection.hh +++ b/include/sta/PortDirection.hh @@ -43,7 +43,7 @@ public: static PortDirection *power() { return power_; } static PortDirection *unknown() { return unknown_; } static PortDirection *find(const char *dir_name); - const char *name() const { return name_; } + std::string_view name() const { return name_; } int index() const { return index_; } bool isInput() const { return this == input_; } // Input or bidirect. diff --git a/include/sta/PowerClass.hh b/include/sta/PowerClass.hh index 55fb292fb..b3f2a6dfd 100644 --- a/include/sta/PowerClass.hh +++ b/include/sta/PowerClass.hh @@ -58,7 +58,7 @@ public: void setDuty(float duty); PwrActivityOrigin origin() const { return origin_; } void setOrigin(PwrActivityOrigin origin); - const char *originName() const; + const std::string &originName() const; void set(float density, float duty, PwrActivityOrigin origin); diff --git a/include/sta/Property.hh b/include/sta/Property.hh index ae89b599e..fd7344373 100644 --- a/include/sta/Property.hh +++ b/include/sta/Property.hh @@ -51,12 +51,12 @@ public: void defineProperty(std::string_view property, PropertyHandler handler); PropertyValue getProperty(TYPE object, - const std::string &property, - const char *type_name, + std::string_view property, + std::string_view type_name, Sta *sta); private: - std::map registry_; + std::map> registry_; }; class Properties @@ -66,33 +66,33 @@ public: virtual ~Properties() {} PropertyValue getProperty(const Library *lib, - const std::string &property); + std::string_view property); PropertyValue getProperty(const LibertyLibrary *lib, - const std::string &property); + std::string_view property); PropertyValue getProperty(const Cell *cell, - const std::string &property); + std::string_view property); PropertyValue getProperty(const LibertyCell *cell, - const std::string &property); + std::string_view property); PropertyValue getProperty(const Port *port, - const std::string &property); + std::string_view property); PropertyValue getProperty(const LibertyPort *port, - const std::string &property); + std::string_view property); PropertyValue getProperty(const Instance *inst, - const std::string &property); + std::string_view property); PropertyValue getProperty(const Pin *pin, - const std::string &property); + std::string_view property); PropertyValue getProperty(const Net *net, - const std::string &property); + std::string_view property); PropertyValue getProperty(Edge *edge, - const std::string &property); + std::string_view property); PropertyValue getProperty(const Clock *clk, - const std::string &property); + std::string_view property); PropertyValue getProperty(PathEnd *end, - const std::string &property); + std::string_view property); PropertyValue getProperty(Path *path, - const std::string &property); + std::string_view property); PropertyValue getProperty(TimingArcSet *arc_set, - const std::string &property); + std::string_view property); // Define handler for external property. // properties->defineProperty("foo", @@ -160,7 +160,7 @@ protected: }; // Adding a new property type -// value union +// value union (string values use std::string* so the union stays trivial) // enum Type // constructor // copy constructor switch clause @@ -178,11 +178,11 @@ public: instance, pin, pins, net, clk, clks, paths, pwr_activity }; PropertyValue(); - PropertyValue(const char *value); - PropertyValue(std::string &value); + PropertyValue(std::string_view value); + PropertyValue(std::string value); PropertyValue(float value, const Unit *unit); - explicit PropertyValue(bool value); + PropertyValue(bool value); PropertyValue(const Library *value); PropertyValue(const Cell *value); PropertyValue(const Port *value); @@ -209,7 +209,7 @@ public: const Unit *unit() const { return unit_; } std::string to_string(const Network *network) const; - const char *stringValue() const; // valid for type string + const std::string &stringValue() const; // valid for type string float floatValue() const; // valid for type float bool boolValue() const; // valid for type bool const LibertyLibrary *libertyLibrary() const { return liberty_library_; } @@ -233,9 +233,12 @@ public: PropertyValue &operator=(PropertyValue &&) noexcept; private: + void destroyActive(); + Type type_; union { - const char *string_; + // Use heap string to simplify initialization/destrucction. + std::string *string_; float float_; bool bool_; const Library *library_; diff --git a/include/sta/Report.hh b/include/sta/Report.hh index 51cde2ed7..e8c39de3e 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -177,13 +177,13 @@ public: } // Log output to filename until logEnd is called. - virtual void logBegin(std::string filename); + virtual void logBegin(std::string_view filename); virtual void logEnd(); // Redirect output to filename until redirectFileEnd is called. - virtual void redirectFileBegin(std::string filename); + virtual void redirectFileBegin(std::string_view filename); // Redirect append output to filename until redirectFileEnd is called. - virtual void redirectFileAppendBegin(std::string filename); + virtual void redirectFileAppendBegin(std::string_view 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 acdf84dca..5f4a6cd0e 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 filename) override; + void logBegin(std::string_view filename) override; void logEnd() override; - void redirectFileBegin(std::string filename) override; - void redirectFileAppendBegin(std::string filename) 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; diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index 64a413759..eaf65e77b 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -28,6 +28,7 @@ #include #include #include +#include #include "StringUtil.hh" #include "MinMax.hh" @@ -131,7 +132,7 @@ private: bool subtract_pin_cap_[MinMax::index_count]; }; -using ClockNameMap = std::map; +using ClockNameMap = std::map>; using ClockPinMap = std::unordered_map; using InputDelaySet = std::set; using InputDelaysPinMap = std::map; @@ -179,11 +180,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; @@ -377,14 +378,14 @@ public: float fanout); void setMaxArea(float area); float maxArea() const; - Clock *makeClock(const char *name, + Clock *makeClock(std::string_view name, PinSet *pins, bool add_to_pins, float period, FloatSeq *waveform, - const char *comment); + std::string_view comment); // edges size must be 3. - Clock *makeGeneratedClock(const char *name, + Clock *makeGeneratedClock(std::string_view name, PinSet *pins, bool add_to_pins, Pin *src_pin, @@ -396,7 +397,7 @@ public: bool combinational, IntSeq *edges, FloatSeq *edge_shifts, - const char *comment); + std::string_view comment); // Invalidate all generated clock waveforms. void invalidateGeneratedClks() const; void removeClock(Clock *clk); @@ -504,7 +505,7 @@ public: bool physically_exclusive, bool asynchronous, bool allow_paths, - const char *comment); + std::string comment); void makeClockGroup(ClockGroups *clk_groups, ClockSet *clks); void removeClockGroups(const std::string &name); @@ -730,7 +731,7 @@ public: ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max, - const char *comment); + std::string_view comment); // Loop paths are false paths used to disable paths around // combinational loops when dynamic loop breaking is enabled. void makeLoopExceptions(); @@ -742,7 +743,7 @@ public: const MinMaxAll *min_max, bool use_end_clk, int path_multiplier, - const char *comment); + std::string_view comment); void makePathDelay(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, @@ -750,7 +751,7 @@ public: bool ignore_clk_latency, bool break_path, float delay, - const char *comment); + std::string_view comment); bool pathDelaysWithoutTo() const { return path_delays_without_to_; } // Delete matching false/multicycle/path_delay exceptions. // Caller owns from, thrus, to exception points (and must delete them). @@ -758,13 +759,13 @@ public: ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max); - void makeGroupPath(const std::string &name, + void makeGroupPath(std::string_view name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, - const char *comment); - bool isGroupPathName(const char *group_name) const; + std::string_view comment); + bool isGroupPathName(std::string_view group_name) const; const GroupPathMap &groupPaths() const { return group_path_map_; } void addException(ExceptionPath *exception); // The pin/clk/instance/net set arguments passed into the following @@ -833,7 +834,7 @@ public: const MinMax *min_max, float voltage); InputDrive *findInputDrive(Port *port) const; - Clock *findClock(const char *name) const; + Clock *findClock(std::string_view name) const; ClockSeq findClocksMatching(PatternMatch *pattern) const; // True if pin is defined as a clock source (pin may be hierarchical). bool isClock(const Pin *pin) const; diff --git a/include/sta/SdcCmdComment.hh b/include/sta/SdcCmdComment.hh index 5e995fb5a..b1c70d0b0 100644 --- a/include/sta/SdcCmdComment.hh +++ b/include/sta/SdcCmdComment.hh @@ -24,22 +24,28 @@ #pragma once +#include +#include + namespace sta { class SdcCmdComment { public: SdcCmdComment(); - SdcCmdComment(const char *comment); - const char *comment() { return comment_; } - void setComment(const char *comment); + SdcCmdComment(std::string comment); + SdcCmdComment(std::string_view comment); + const std::string &comment() { return comment_; } + const std::string &comment() const { return comment_; } + void setComment(std::string comment); + void setComment(std::string_view comment); protected: // Destructor is protected to prevent deletion of a derived // class with a pointer to this base class. ~SdcCmdComment(); - char *comment_; + std::string comment_; }; } // namespace diff --git a/include/sta/SdcNetwork.hh b/include/sta/SdcNetwork.hh index 3c2c84bea..84b0b3676 100644 --- a/include/sta/SdcNetwork.hh +++ b/include/sta/SdcNetwork.hh @@ -25,6 +25,8 @@ #pragma once #include +#include +#include #include "Network.hh" @@ -36,26 +38,26 @@ class NetworkNameAdapter : public NetworkEdit { public: NetworkNameAdapter(Network *network); - bool linkNetwork(const char *top_cell_name, + bool linkNetwork(std::string_view top_cell_name, bool make_black_boxes, Report *report) override; - const char *name(const Library *library) const override; + std::string name(const Library *library) const override; ObjectId id(const Library *library) const override; LibertyLibrary *defaultLibertyLibrary() const override; LibraryIterator *libraryIterator() const override; LibertyLibraryIterator *libertyLibraryIterator() const override; - Library *findLibrary(const char *name) override; - LibertyLibrary *findLibertyFilename(const char *filename) override; - LibertyLibrary *findLiberty(const char *name) override; + Library *findLibrary(std::string_view name) override; + LibertyLibrary *findLibertyFilename(std::string_view filename) override; + LibertyLibrary *findLiberty(std::string_view name) override; Cell *findCell(const Library *library, - const char *name) const override; + std::string_view name) const override; CellSeq findCellsMatching(const Library *library, const PatternMatch *pattern) const override; - const char *name(const Cell *cell) const override; + std::string name(const Cell *cell) const override; std::string getAttribute(const Cell *cell, - const std::string &key) const override; + std::string_view key) const override; const AttributeMap &attributeMap(const Cell *cell) const override; ObjectId id(const Cell *cell) const override; Library *library(const Cell *cell) const override; @@ -63,9 +65,9 @@ public: const LibertyCell *libertyCell(const Cell *cell) const override; Cell *cell(LibertyCell *cell) const override; const Cell *cell(const LibertyCell *cell) const override; - const char *filename(const Cell *cell) override; + std::string_view filename(const Cell *cell) const override; Port *findPort(const Cell *cell, - const char *name) const override; + std::string_view name) const override; PortSeq findPortsMatching(const Cell *cell, const PatternMatch *pattern) const override; bool isLeaf(const Cell *cell) const override; @@ -73,7 +75,7 @@ public: CellPortBitIterator *portBitIterator(const Cell *cell) const override; int portBitCount(const Cell *cell) const override; - const char *name(const Port *port) const override; + std::string name(const Port *port) const override; ObjectId id(const Port *port) const override; Cell *cell(const Port *port) const override; LibertyPort *libertyPort(const Port *port) const override; @@ -81,7 +83,7 @@ public: bool isBundle(const Port *port) const override; bool isBus(const Port *port) const override; int size(const Port *port) const override; - const char *busName(const Port *port) const override; + std::string busName(const Port *port) const override; Port *findBusBit(const Port *port, int index) const override; int fromIndex(const Port *port) const override; @@ -93,7 +95,7 @@ public: ObjectId id(const Instance *instance) const override; std::string getAttribute(const Instance *inst, - const std::string &key) const override; + std::string_view key) const override; const AttributeMap &attributeMap(const Instance *inst) const override; Instance *topInstance() const override; Cell *cell(const Instance *instance) const override; @@ -145,15 +147,15 @@ public: void setPathEscape(char escape) override; bool isEditable() const override; - LibertyLibrary *makeLibertyLibrary(const char *name, - const char *filename) override; + LibertyLibrary *makeLibertyLibrary(std::string_view name, + std::string_view filename) override; Instance *makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) override; void makePins(Instance *inst) override; void replaceCell(Instance *inst, Cell *to_cell) override; - Net *makeNet(const char *name, + Net *makeNet(std::string_view name, Instance *parent) override; Pin *connect(Instance *inst, Port *port, @@ -195,47 +197,47 @@ public: SdcNetwork(Network *network); Port *findPort(const Cell *cell, - const char *name) const override; + std::string_view name) const override; PortSeq findPortsMatching(const Cell *cell, const PatternMatch *pattern) const override; - const char *name(const Port *port) const override; - const char *busName(const Port *port) const override; + std::string name(const Port *port) const override; + std::string busName(const Port *port) const override; - const char *name(const Instance *instance) const override; - const char *pathName(const Instance *instance) const override; - const char *pathName(const Pin *pin) const override; - const char *portName(const Pin *pin) const override; + std::string name(const Instance *instance) const override; + std::string pathName(const Instance *instance) const override; + std::string pathName(const Pin *pin) const override; + std::string portName(const Pin *pin) const override; - const char *name(const Net *net) const override; - const char *pathName(const Net *net) const override; + std::string name(const Net *net) const override; + std::string pathName(const Net *net) const override; - Instance *findInstance(const char *path_name) const override; + Instance *findInstance(std::string_view path_name) const override; Instance *findInstanceRelative(const Instance *inst, - const char *path_name) const override; + std::string_view path_name) const override; InstanceSeq findInstancesMatching(const Instance *context, const PatternMatch *pattern) const override; - Net *findNet(const char *path_name) const override; + Net *findNet(std::string_view path_name) const override; Net *findNetRelative(const Instance *instance, - const char *net_name) const override; + std::string_view net_name) const override; Net *findNet(const Instance *instance, - const char *net_name) const override; + std::string_view net_name) const override; NetSeq findNetsMatching(const Instance *parent, const PatternMatch *pattern) const override; void findInstNetsMatching(const Instance *instance, const PatternMatch *pattern, NetSeq &nets) const override; Instance *findChild(const Instance *parent, - const char *name) const override; - Pin *findPin(const char *path_name) const override; + std::string_view name) const override; + Pin *findPin(std::string_view path_name) const override; Pin *findPin(const Instance *instance, - const char *port_name) const override; + std::string_view port_name) const override; PinSeq findPinsMatching(const Instance *instance, const PatternMatch *pattern) const override; Instance *makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) override; - Net *makeNet(const char *name, + Net *makeNet(std::string_view name, Instance *parent) override; // The following member functions are inherited from the @@ -247,21 +249,21 @@ public: using Network::findPin; protected: - void parsePath(const char *path, + void parsePath(std::string_view path, // Return values. Instance *&inst, - const char *&path_tail) const; - void scanPath(const char *path, + std::string &path_tail) const; + void scanPath(std::string_view path, // Return values. // Unescaped divider count. int ÷r_count, int &path_length) const; - void parsePath(const char *path, + void parsePath(std::string_view path, int divider_count, int path_length, // Return values. Instance *&inst, - const char *&path_tail) const; + std::string &path_tail) const; bool visitMatches(const Instance *parent, const PatternMatch *pattern, std::function; +using ModeNameMap = std::map>; using SceneNameMap = std::map; using SlowDrvrIterator = Iterator; using CheckError = StringSeq; @@ -143,12 +143,12 @@ public: Mode *cmdMode() const { return cmd_scene_->mode(); } const std::string &cmdModeName(); - void setCmdMode(const std::string &mode_name); - Mode *findMode(const std::string &mode_name) const; + void setCmdMode(std::string_view mode_name); + Mode *findMode(std::string_view mode_name) const; ModeSeq findModes(const std::string &mode_name) const; Sdc *cmdSdc() const; - virtual LibertyLibrary *readLiberty(const char *filename, + virtual LibertyLibrary *readLiberty(std::string_view filename, Scene *scene, const MinMaxAll *min_max, bool infer_latches); @@ -156,7 +156,7 @@ public: void readLibertyAfter(LibertyLibrary *liberty, Scene *scene, const MinMax *min_max); - bool readVerilog(const char *filename); + bool readVerilog(std::string_view filename); // Network readers call this to notify the Sta to delete any previously // linked network. void readNetlistBefore(); @@ -164,6 +164,13 @@ public: bool linkDesign(const char *top_cell_name, bool make_black_boxes); + bool readSdf(std::string_view filename, + std::string_view path, + Scene *scene, + bool unescaped_dividers, + bool incremental_only, + MinMaxAll *cond_use); + // SDC Swig API. Instance *currentInstance() const; void setCurrentInstance(Instance *inst); @@ -340,15 +347,15 @@ public: void setMaxArea(float area, Sdc *sdc); - void makeClock(const char *name, + void makeClock(std::string_view name, PinSet *pins, bool add_to_pins, float period, FloatSeq *waveform, - char *comment, + std::string_view comment, const Mode *mode); // edges size must be 3. - void makeGeneratedClock(const char *name, + void makeGeneratedClock(std::string_view name, PinSet *pins, bool add_to_pins, Pin *src_pin, @@ -360,7 +367,7 @@ public: bool combinational, IntSeq *edges, FloatSeq *edge_shifts, - char *comment, + std::string_view comment, const Mode *mode); void removeClock(Clock *clk, Sdc *sdc); @@ -439,7 +446,7 @@ public: bool physically_exclusive, bool asynchronous, bool allow_paths, - const char *comment, + std::string comment, Sdc *sdc); void removeClockGroupsLogicallyExclusive(Sdc *sdc); void removeClockGroupsLogicallyExclusive(const std::string &name, @@ -623,7 +630,7 @@ public: ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max, - const char *comment, + std::string_view comment, Sdc *sdc); void makeMulticyclePath(ExceptionFrom *from, ExceptionThruSeq *thrus, @@ -631,7 +638,7 @@ public: const MinMaxAll *min_max, bool use_end_clk, int path_multiplier, - const char *comment, + std::string_view comment, Sdc *sdc); void makePathDelay(ExceptionFrom *from, ExceptionThruSeq *thrus, @@ -640,19 +647,19 @@ public: bool ignore_clk_latency, bool break_path, float delay, - const char *comment, + std::string_view comment, Sdc *sdc); - void makeGroupPath(const std::string &name, + void makeGroupPath(std::string_view name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, - const char *comment, + std::string_view comment, Sdc *sdc); // Deprecated 10/24/2025 - bool isGroupPathName(const char *group_name, + bool isGroupPathName(std::string_view group_name, const Sdc *sdc) __attribute__ ((deprecated)); - bool isPathGroupName(const char *group_name, + bool isPathGroupName(std::string_view group_name, const Sdc *sdc) const; StringSeq pathGroupNames(const Sdc *sdc) const; void resetPath(ExceptionFrom *from, @@ -667,7 +674,7 @@ public: const RiseFallBoth *from_rf, const Sdc *sdc); void checkExceptionFromPins(ExceptionFrom *from, - const char *file, + std::string_view filename, int line, const Sdc *sdc) const; void deleteExceptionFrom(ExceptionFrom *from); @@ -892,7 +899,7 @@ public: const MinMaxAll *min_max, const RiseFallBoth *rf, float slew); - void writeSdf(const char *filename, + void writeSdf(std::string_view filename, const Scene *scene, char divider, bool include_typ, @@ -986,7 +993,7 @@ public: bool report_fanout, bool report_variation, bool report_src_attr); - ReportField *findReportPathField(const char *name); + ReportField *findReportPathField(std::string_view name); void setReportPathDigits(int digits); void setReportPathNoSplit(bool no_split); void reportPathEnd(PathEnd *end); @@ -1038,7 +1045,7 @@ public: const MinMax *min_max, int digits); void writeSdc(const Sdc *sdc, - const char *filename, + std::string_view filename, // Map hierarchical pins and instances to leaf pins and instances. bool leaf, // Replace non-sdc get functions with OpenSTA equivalents. @@ -1123,7 +1130,7 @@ public: Slack (&slacks)[RiseFall::index_count][MinMax::index_count]); // Worst slack for an endpoint in a path group. Slack endpointSlack(const Pin *pin, - const std::string &path_group_name, + std::string_view path_group_name, const MinMax *min_max); void reportArrivalWrtClks(const Pin *pin, @@ -1188,8 +1195,8 @@ public: // networks (dspf) are reduced and deleted after reading each net // with reduce_to and delete_after_reduce. // Return true if successful. - bool readSpef(const std::string &name, - const std::string &filename, + bool readSpef(std::string_view name, + std::string_view filename, Instance *instance, Scene *scene, const MinMaxAll *min_max, @@ -1328,7 +1335,7 @@ public: void searchPreamble(); // Define the delay calculator implementation. - void setArcDelayCalc(const char *delay_calc_name); + void setArcDelayCalc(std::string_view delay_calc_name); void setDebugLevel(const char *what, int level); @@ -1376,9 +1383,9 @@ public: PwrActivity activity(const Pin *pin, const Scene *scene); - void writeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, + void writeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, const Scene *scene); // Find equivalent cells in equiv_libs. @@ -1388,12 +1395,12 @@ public: LibertyCellSeq *equivCells(LibertyCell *cell); void writePathSpice(const Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim); //////////////////////////////////////////////////////////////// @@ -1485,13 +1492,10 @@ protected: virtual void makeObservers(); NetworkEdit *networkCmdEdit(); - LibertyLibrary *readLibertyFile(const char *filename, + LibertyLibrary *readLibertyFile(std::string_view filename, Scene *scene, const MinMaxAll *min_max, bool infer_latches); - // Allow external Liberty reader to parse forms not used by Sta. - virtual LibertyLibrary *readLibertyFile(const char *filename, - bool infer_latches); void delayCalcPreamble(); void delaysInvalidFrom(const Port *port); void delaysInvalidFromFanin(const Port *port); diff --git a/include/sta/StaMain.hh b/include/sta/StaMain.hh index db4291b85..6d818c41c 100644 --- a/include/sta/StaMain.hh +++ b/include/sta/StaMain.hh @@ -24,6 +24,8 @@ #pragma once +#include + struct Tcl_Interp; namespace sta { @@ -58,11 +60,11 @@ unencode(const char *inits[]); bool findCmdLineFlag(int &argc, char *argv[], - const char *flag); + std::string_view flag); char * findCmdLineKey(int &argc, char *argv[], - const char *key); + std::string_view key); int parseThreadsArg(int &argc, diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index c2cfb13d4..f9161a603 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -24,9 +24,12 @@ #pragma once +#include #include #include #include +#include +#include // for strncasecmp #include #include @@ -53,14 +56,6 @@ stringEq(const char *str1, return strncmp(str1, str2, length) == 0; } -inline bool -stringEqIf(const char *str1, - const char *str2) -{ - return (str1 == nullptr && str2 == nullptr) - || (str1 && str2 && strcmp(str1, str2) == 0); -} - // Case sensitive compare the beginning of str1 to str2. inline bool stringBeginEq(const char *str1, @@ -71,78 +66,24 @@ stringBeginEq(const char *str1, // Case insensitive compare the beginning of str1 to str2. inline bool -stringBeginEqual(const char *str1, - const char *str2) +stringBeginEqual(std::string_view str, + std::string_view prefix) { - return strncasecmp(str1, str2, strlen(str2)) == 0; + if (str.size() < prefix.size()) + return false; + return strncasecmp(str.data(), prefix.data(), prefix.size()) == 0; } // Case insensitive compare. inline bool -stringEqual(const char *str1, - const char *str2) -{ - return strcasecmp(str1, str2) == 0; -} - -inline bool -stringEqualIf(const char *str1, - const char *str2) +stringEqual(std::string_view s1, + std::string_view s2) { - return (str1 == nullptr && str2 == nullptr) - || (str1 && str2 && strcasecmp(str1, str2) == 0); + return std::ranges::equal(s1, s2, [](unsigned char c1, unsigned char c2) { + return std::tolower(c1) == std::tolower(c2); + }); } -inline bool -stringLess(const char *str1, - const char *str2) -{ - return strcmp(str1, str2) < 0; -} - -inline bool -stringLessIf(const char *str1, - const char *str2) -{ - return (str1 == nullptr && str2 != nullptr) - || (str1 != nullptr && str2 != nullptr && strcmp(str1, str2) < 0); -} - -class CharPtrLess -{ -public: - bool operator()(const char *string1, - const char *string2) const - { - return stringLess(string1, string2); - } -}; - -// Case insensitive comparision. -class CharPtrCaseLess -{ -public: - bool operator()(const char *string1, - const char *string2) const - { - return strcasecmp(string1, string2) < 0; - } -}; - -class StringLessIf -{ -public: - bool operator()(const char *string1, - const char *string2) const - { - return stringLessIf(string1, string2); - } -}; - -// strdup using new instead of malloc so delete can be used on the strings. -char * -stringCopy(const char *str); - void stringDeleteCheck(const char *str); @@ -153,17 +94,14 @@ stringDelete(const char *str) delete [] str; } +std::pair +stringFloat(const std::string &str); + bool isDigits(const char *str); -char * -makeTmpString(size_t length); -char * -makeTmpString(std::string &str); bool -isTmpString(const char *str); -void -deleteTmpStrings(); +isDigits(std::string_view str); //////////////////////////////////////////////////////////////// @@ -171,9 +109,9 @@ deleteTmpStrings(); void trimRight(std::string &str); -// Spit text into delimiter separated tokens and skip whitepace. +// Parse text into delimiter separated tokens and skip whitepace. StringSeq -parseTokens(const std::string &s, - const char delimiter); +parseTokens(const std::string &text, + std::string_view delims = " \t"); } // namespace diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 95b511329..3afce3352 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "MinMax.hh" @@ -54,8 +55,8 @@ using Waveform = Table; using TableModelsEarlyLate = std::array; TableAxisVariable -stringTableAxisVariable(const char *variable); -const char * +stringTableAxisVariable(std::string_view variable); +std::string_view tableVariableString(TableAxisVariable variable); const Unit * tableVariableUnit(TableAxisVariable variable, @@ -121,7 +122,7 @@ protected: float in_slew, float load_cap, float related_out_cap) const; - std::string reportTableLookup(const char *result_name, + std::string reportTableLookup(std::string_view result_name, const Pvt *pvt, const TableModel *model, float in_slew, @@ -158,7 +159,7 @@ public: PocvMode pocv_mode) const override; std::string reportCheckDelay(const Pvt *pvt, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, const MinMax *min_max, @@ -189,11 +190,11 @@ protected: float load_cap, float in_slew, float related_out_cap) const; - std::string reportTableDelay(const char *result_name, + std::string reportTableDelay(std::string_view result_name, const Pvt *pvt, const TableModel *model, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, int digits) const; @@ -208,7 +209,7 @@ public: TableAxis(TableAxisVariable variable, FloatSeq &&values); TableAxisVariable variable() const { return variable_; } - const char *variableString() const; + std::string_view variableString() const; const Unit *unit(const Units *units); size_t size() const { return values_.size(); } bool inBounds(float value) const; @@ -287,11 +288,11 @@ public: float axis_value2, float axis_value3) const; - std::string reportValue(const char *result_name, + std::string reportValue(std::string_view result_name, const LibertyCell *cell, const Pvt *pvt, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, @@ -309,30 +310,30 @@ 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, + std::string reportValueOrder0(std::string_view result_name, + std::string_view comment1, const Unit *table_unit, int digits) const; - std::string reportValueOrder1(const char *result_name, + std::string reportValueOrder1(std::string_view result_name, const LibertyCell *cell, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, int digits) const; - std::string reportValueOrder2(const char *result_name, + std::string reportValueOrder2(std::string_view result_name, const LibertyCell *cell, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, int digits) const; - std::string reportValueOrder3(const char *result_name, + std::string reportValueOrder3(std::string_view result_name, const LibertyCell *cell, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, @@ -379,11 +380,11 @@ public: float value1, float value2, float value3) const; - std::string reportValue(const char *result_name, + std::string reportValue(std::string_view result_name, const LibertyCell *cell, const Pvt *pvt, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, @@ -535,9 +536,9 @@ private: class DriverWaveform { public: - DriverWaveform(const std::string &name, + DriverWaveform(std::string name, TablePtr waveforms); - const char *name() const { return name_.c_str(); } + std::string_view name() const { return name_; } Table waveform(float slew); private: diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index 95584b7bf..acb4e164e 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -34,14 +34,14 @@ namespace sta { #endif StringSeq -tclListSeqStdString(Tcl_Obj *const source, - Tcl_Interp *interp); +tclListStringSeq(Tcl_Obj *const source, + Tcl_Interp *interp); StringSeq * -tclListSeqStdStringPtr(Tcl_Obj *const source, - Tcl_Interp *interp); -StringSet * -tclListSetStdString(Tcl_Obj *const source, +tclListStringSeqPtr(Tcl_Obj *const source, Tcl_Interp *interp); +StringSet * +tclListStringSet(Tcl_Obj *const source, + Tcl_Interp *interp); void tclArgError(Tcl_Interp *interp, diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index fcec3c4c0..274f10e5e 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -83,10 +83,10 @@ enum class TimingType { unknown }; -const char * +std::string_view to_string(TimingType type); TimingType -findTimingType(const char *string); +findTimingType(std::string_view string); bool timingTypeIsCheck(TimingType type); ScaleFactorType @@ -107,15 +107,15 @@ public: FuncExpr *cond() const { return cond_; } void setCond(FuncExpr *cond); const std::string &sdfCond() const { return sdf_cond_; } - void setSdfCond(const std::string &cond); + void setSdfCond(std::string cond); const std::string &sdfCondStart() const { return sdf_cond_start_; } - void setSdfCondStart(const std::string &cond); + void setSdfCondStart(std::string cond); const std::string &sdfCondEnd() const { return sdf_cond_end_; } - void setSdfCondEnd(const std::string &cond); + void setSdfCondEnd(std::string cond); const std::string &modeName() const { return mode_name_; } - void setModeName(const std::string &name); + void setModeName(std::string name); const std::string &modeValue() const { return mode_value_; } - void setModeValue(const std::string &value); + void setModeValue(std::string value); TimingModel *model(const RiseFall *rf) const; void setModel(const RiseFall *rf, TimingModel *model); diff --git a/include/sta/TimingModel.hh b/include/sta/TimingModel.hh index 0ff157f2f..55e1623ec 100644 --- a/include/sta/TimingModel.hh +++ b/include/sta/TimingModel.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include "Delay.hh" #include "LibertyClass.hh" @@ -88,7 +89,7 @@ public: PocvMode pocv_mode) const = 0; virtual std::string reportCheckDelay(const Pvt *pvt, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, const MinMax *min_max, diff --git a/include/sta/Transition.hh b/include/sta/Transition.hh index 4af601d4a..9d01abbc2 100644 --- a/include/sta/Transition.hh +++ b/include/sta/Transition.hh @@ -25,8 +25,9 @@ #pragma once #include -#include +#include #include +#include #include "Iterator.hh" #include "StringUtil.hh" @@ -37,7 +38,7 @@ class Transition; class RiseFall; class RiseFallBoth; -using TransitionMap = std::map; +using TransitionMap = std::map>; // Rise/fall transition. class RiseFall @@ -49,14 +50,14 @@ public: static int riseIndex() { return rise_.sdf_triple_index_; } static int fallIndex() { return fall_.sdf_triple_index_; } const std::string &to_string(bool use_short = false) const; - const char *name() const { return name_.c_str(); } - const char *shortName() const { return short_name_.c_str(); } + const std::string &name() const { return name_; } + const std::string &shortName() const { return short_name_; } int index() const { return sdf_triple_index_; } const RiseFallBoth *asRiseFallBoth(); const RiseFallBoth *asRiseFallBoth() const; const Transition *asTransition() const; // Find transition corresponding to rf_str. - static const RiseFall *find(const char *rf_str); + static const RiseFall *find(std::string_view rf_str); // Find transition from index. static const RiseFall *find(int index); const RiseFall *opposite() const; @@ -71,8 +72,8 @@ public: static const int index_bit_count = 1; protected: - RiseFall(const char *name, - const char *short_name, + RiseFall(std::string_view name, + std::string_view short_name, int sdf_triple_index); const std::string name_; @@ -94,14 +95,14 @@ public: static const RiseFallBoth *fall() { return &fall_; } static const RiseFallBoth *riseFall() { return &rise_fall_; } const std::string &to_string(bool use_short = false) const; - const char *name() const { return name_.c_str(); } - const char *shortName() const { return short_name_.c_str(); } + const std::string &name() const { return name_; } + const std::string &shortName() const { return short_name_; } int index() const { return sdf_triple_index_; } bool matches(const RiseFall *rf) const; bool matches(const Transition *tr) const; const RiseFall *asRiseFall() const { return as_rise_fall_; } // Find transition corresponding to string. - static const RiseFallBoth *find(const char *tr_str); + static const RiseFallBoth *find(std::string_view tr_str); // for (const auto rf : rf->range()) {} const std::vector &range() const { return range_; } // for (const auto rf_index : rf->rangeIndex()) {} @@ -112,8 +113,8 @@ public: static const int index_bit_count = 2; protected: - RiseFallBoth(const char *name, - const char *short_name, + RiseFallBoth(std::string_view name, + std::string_view short_name, int sdf_triple_index, const RiseFall *as_rise_fall, std::vector range, @@ -152,19 +153,19 @@ public: static const Transition *riseFall() { return &rise_fall_; } const std::string &to_string() const { return name_; } // As initial/final value pair. - const char *asInitFinalString() const { return init_final_.c_str(); } + const std::string &asInitFinalString() const { return init_final_; } int sdfTripleIndex() const { return sdf_triple_index_; } int index() const { return sdf_triple_index_; } const RiseFall *asRiseFall() const { return as_rise_fall_; } const RiseFallBoth *asRiseFallBoth() const; bool matches(const Transition *tr) const; // Find transition corresponding to string. - static const Transition *find(const char *tr_str); + static const Transition *find(std::string_view tr_str); static int maxIndex() { return max_index_; } private: - Transition(const char *name, - const char *init_final, + Transition(std::string_view name, + std::string_view init_final, const RiseFall *as_rise_fall, int sdf_triple_index); diff --git a/include/sta/Units.hh b/include/sta/Units.hh index 8c3475f52..0067fe75f 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -75,7 +75,7 @@ class Units { public: Units(); - Unit *find(const char *unit_name); + Unit *find(std::string_view unit_name); void operator=(const Units &units); Unit *timeUnit() { return &time_unit_; } const Unit *timeUnit() const { return &time_unit_; } diff --git a/include/sta/VerilogReader.hh b/include/sta/VerilogReader.hh index 644992380..b28e0f23b 100644 --- a/include/sta/VerilogReader.hh +++ b/include/sta/VerilogReader.hh @@ -74,7 +74,7 @@ public: std::string_view msg, bool warn); const char *msg() const { return msg_.c_str(); } - const char *filename() const { return filename_.c_str(); } + std::string_view filename() const { return filename_; } int id() const { return id_; } int line() const { return line_; } bool warn() const { return warn_; } @@ -102,14 +102,14 @@ class VerilogReader public: VerilogReader(NetworkReader *network); ~VerilogReader(); - bool read(const char *filename); + bool read(std::string_view filename); - void makeModule(const std::string *module_name, + void makeModule(std::string &&module_name, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, int line); - void makeModule(const std::string *module_name, + void makeModule(std::string &&module_name, VerilogStmtSeq *port_dcls, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, @@ -122,7 +122,7 @@ public: VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, int line); - VerilogDclArg *makeDclArg(const std::string *net_name); + VerilogDclArg *makeDclArg(std::string &&net_name); VerilogDclArg*makeDclArg(VerilogAssign *assign); VerilogDclBus *makeDclBus(PortDirection *dir, int from_index, @@ -136,43 +136,43 @@ public: VerilogDclArgSeq *args, VerilogAttrStmtSeq *attr_stmts, int line); - VerilogInst *makeModuleInst(const std::string *module_name, - const std::string *inst_name, + VerilogInst *makeModuleInst(std::string &&module_name, + std::string &&inst_name, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, const int line); VerilogAssign *makeAssign(VerilogNet *lhs, VerilogNet *rhs, int line); - VerilogNetScalar *makeNetScalar(const std::string *name); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_vname); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(const std::string *port_name, - const std::string *net_name); - VerilogNetPortRef *makeNetNamedPortRefBitSelect(const std::string *port_name, - const std::string *bus_name, + VerilogNetScalar *makeNetScalar(std::string &&name); + VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string &&port_vname); + VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string &&port_name, + std::string &&net_name); + VerilogNetPortRef *makeNetNamedPortRefBitSelect(std::string &&port_name, + std::string &&bus_name, int index); - VerilogNetPortRef *makeNetNamedPortRefScalar(const std::string *port_name, + VerilogNetPortRef *makeNetNamedPortRefScalar(std::string &&port_name, VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefBit(const std::string *port_name, + VerilogNetPortRef *makeNetNamedPortRefBit(std::string &&port_name, int index, VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefPart(const std::string *port_name, + VerilogNetPortRef *makeNetNamedPortRefPart(std::string &&port_name, int from_index, int to_index, VerilogNet *net); VerilogNetConcat *makeNetConcat(VerilogNetSeq *nets); - VerilogNetConstant *makeNetConstant(const std::string *constant, - int line); - VerilogNetBitSelect *makeNetBitSelect(const std::string *name, + VerilogNetConstant *makeNetConstant(std::string &&constant, + int line); + VerilogNetBitSelect *makeNetBitSelect(std::string &&name, int index); - VerilogNetPartSelect *makeNetPartSelect(const std::string *name, + VerilogNetPartSelect *makeNetPartSelect(std::string &&name, int from_index, int to_index); VerilogModule *module(Cell *cell); - Instance *linkNetwork(const char *top_cell_name, + Instance *linkNetwork(std::string_view top_cell_name, bool make_black_boxes, bool delete_modules); - const char *filename() const { return filename_.c_str(); } + std::string_view filename() const { return filename_; } void incrLine(); Report *report() const { return report_; } template @@ -196,11 +196,10 @@ public: const std::string &zeroNetName() const { return zero_net_name_; } const std::string &oneNetName() const { return one_net_name_; } void deleteModules(); - void reportStmtCounts(); const std::string &constant10Max() const { return constant10_max_; } protected: - void init(const char *filename); + void init(std::string_view filename); void makeCellPorts(Cell *cell, VerilogModule *module, VerilogNetSeq *ports); @@ -315,29 +314,6 @@ protected: const std::string one_net_name_; std::string constant10_max_; ViewType *view_type_; - bool report_stmt_stats_; - int module_count_; - int inst_mod_count_; - int inst_lib_count_; - int inst_lib_net_arrays_; - int port_names_; - int inst_module_names_; - int inst_names_; - int dcl_count_; - int dcl_bus_count_; - int dcl_arg_count_; - int net_scalar_count_; - int net_scalar_names_; - int net_bus_names_; - int net_part_select_count_; - int net_bit_select_count_; - int net_port_ref_scalar_count_; - int net_port_ref_scalar_net_count_; - int net_port_ref_bit_count_; - int net_port_ref_part_count_; - int net_constant_count_; - int assign_count_; - int concat_count_; }; } // namespace sta diff --git a/include/sta/Wireload.hh b/include/sta/Wireload.hh index 01c54a886..6ca8b5360 100644 --- a/include/sta/Wireload.hh +++ b/include/sta/Wireload.hh @@ -24,6 +24,8 @@ #pragma once +#include +#include #include #include @@ -40,26 +42,26 @@ using WireloadForAreaSeq = std::vector; const char * wireloadTreeString(WireloadTree tree); WireloadTree -stringWireloadTree(const char *tree); +stringWireloadTree(std::string_view tree); const char * wireloadModeString(WireloadMode wire_load_mode); WireloadMode -stringWireloadMode(const char *wire_load_mode); +stringWireloadMode(std::string_view wire_load_mode); class Wireload { public: - Wireload(const char *name, + Wireload(std::string name, LibertyLibrary *library); - Wireload(const char *name, + Wireload(std::string name, LibertyLibrary *library, float area, float resistance, float capacitance, float slope); virtual ~Wireload(); - const char *name() const { return name_; } + const std::string &name() const { return name_; } void setArea(float area); void setResistance(float res); void setCapacitance(float cap); @@ -73,7 +75,7 @@ public: float &res) const; protected: - const char *name_; + std::string name_; LibertyLibrary *library_; float area_; float resistance_; @@ -86,16 +88,16 @@ protected: class WireloadSelection { public: - WireloadSelection(const char *name); + WireloadSelection(std::string name); ~WireloadSelection(); - const char *name() const { return name_; } + const std::string &name() const { return name_; } void addWireloadFromArea(float min_area, float max_area, const Wireload *wireload); const Wireload *findWireload(float area) const; private: - const char *name_; + const std::string name_; WireloadForAreaSeq wireloads_; }; diff --git a/liberty/EquivCells.cc b/liberty/EquivCells.cc index d0b96b303..e4fbba446 100644 --- a/liberty/EquivCells.cc +++ b/liberty/EquivCells.cc @@ -208,7 +208,7 @@ hashCellPorts(const LibertyCell *cell) static unsigned hashPort(const LibertyPort *port) { - return hashString(port->name()) * 3 + return hashString(port->name().c_str()) * 3 + port->direction()->index() * 5; } @@ -338,8 +338,7 @@ equivCellFuncs(const LibertyCell *cell1, LibertyCellPortIterator port_iter1(cell1); while (port_iter1.hasNext()) { LibertyPort *port1 = port_iter1.next(); - const char *name = port1->name(); - LibertyPort *port2 = cell2->findLibertyPort(name); + LibertyPort *port2 = cell2->findLibertyPort(port1->name()); if (!(port2 && FuncExpr::equiv(port1->function(), port2->function()) && FuncExpr::equiv(port1->tristateEnable(), @@ -359,8 +358,7 @@ equivCellPorts(const LibertyCell *cell1, LibertyCellPortIterator port_iter1(cell1); while (port_iter1.hasNext()) { LibertyPort *port1 = port_iter1.next(); - const char* name = port1->name(); - LibertyPort *port2 = cell2->findLibertyPort(name); + LibertyPort *port2 = cell2->findLibertyPort(port1->name()); if (!(port2 && LibertyPort::equiv(port1, port2))) return false; } diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index d9ecd6172..ed2cad244 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -108,7 +108,7 @@ InternalPowerModel::reportPower(const LibertyCell *cell, findAxisValues(in_slew, load_cap, axis_value1, axis_value2, axis_value3); const LibertyLibrary *library = cell->libertyLibrary(); - return model_->reportValue("Power", cell, pvt, axis_value1, nullptr, + return model_->reportValue("Power", cell, pvt, axis_value1, {}, axis_value2, axis_value3, library->units()->powerUnit(), digits); } diff --git a/liberty/LibExprLex.ll b/liberty/LibExprLex.ll index e9a3f70bf..69402d84f 100644 --- a/liberty/LibExprLex.ll +++ b/liberty/LibExprLex.ll @@ -25,14 +25,14 @@ // Liberty function expression lexical analyzer +#include + #include "util/FlexDisableRegister.hh" #include "Debug.hh" -#include "StringUtil.hh" #include "liberty/LibExprReaderPvt.hh" #include "liberty/LibExprReader.hh" #include "liberty/LibExprScanner.hh" -using sta::stringCopy; using sta::FuncExpr; #include "LibExprParse.hh" @@ -76,12 +76,12 @@ EOL \r?\n {ESCAPE}{QUOTE} { BEGIN(INITIAL); - yylval->string = stringCopy(token_.c_str()); + yylval->emplace(token_); return token::PORT; } {PORT} { - yylval->string = stringCopy(yytext); + yylval->emplace(yytext, yyleng); return token::PORT; } diff --git a/liberty/LibExprParse.yy b/liberty/LibExprParse.yy index fc1a4a7de..443f0ce7d 100644 --- a/liberty/LibExprParse.yy +++ b/liberty/LibExprParse.yy @@ -25,6 +25,8 @@ // Liberty function expression parser. %{ +#include + #include "FuncExpr.hh" #include "liberty/LibExprReader.hh" #include "liberty/LibExprReaderPvt.hh" @@ -39,7 +41,7 @@ void sta::LibExprParse::error(const std::string &msg) { - reader->parseError(msg.c_str()); + reader->parseError(msg); } %} @@ -52,21 +54,15 @@ sta::LibExprParse::error(const std::string &msg) %parse-param{LibExprScanner *scanner} %parse-param{LibExprReader *reader} %define api.parser.class {LibExprParse} +%define api.value.type variant -%union { - int int_val; - const char *string; - sta::FuncExpr *expr; -} - -%token PORT +%token PORT %left '+' '|' %left '*' '&' %left '^' %left '!' '\'' -%type PORT -%type expr terminal terminal_expr implicit_and +%type expr terminal terminal_expr implicit_and %% @@ -76,14 +72,14 @@ result_expr: ; terminal: - PORT { $$ = reader->makeFuncExprPort($1); } + PORT { $$ = reader->makeFuncExprPort(std::move($1)); } | '0' { $$ = sta::FuncExpr::makeZero(); } | '1' { $$ = sta::FuncExpr::makeOne(); } | '(' expr ')' { $$ = $2; } ; terminal_expr: - terminal + terminal { $$ = $1; } | '!' terminal { $$ = reader->makeFuncExprNot($2); } | terminal '\'' { $$ = reader->makeFuncExprNot($1); } ; @@ -96,8 +92,8 @@ implicit_and: ; expr: - terminal_expr -| implicit_and + terminal_expr { $$ = $1; } +| implicit_and { $$ = $1; } | expr '+' expr { $$ = reader->makeFuncExprOr($1, $3); } | expr '|' expr { $$ = reader->makeFuncExprOr($1, $3); } | expr '*' expr { $$ = reader->makeFuncExprAnd($1, $3); } diff --git a/liberty/LibExprReader.cc b/liberty/LibExprReader.cc index 1b2fc037c..d74272234 100644 --- a/liberty/LibExprReader.cc +++ b/liberty/LibExprReader.cc @@ -26,9 +26,10 @@ #include #include +#include +#include #include "Report.hh" -#include "StringUtil.hh" #include "Liberty.hh" #include "LibExprReaderPvt.hh" #include "LibExprScanner.hh" @@ -36,14 +37,14 @@ namespace sta { FuncExpr * -parseFuncExpr(const char *func, +parseFuncExpr(std::string_view func, const LibertyCell *cell, - const char *error_msg, + std::string_view error_msg, Report *report) { - if (func != nullptr && func[0] != '\0') { - std::string func1(func); - std::istringstream stream(func); + if (!func.empty()) { + std::string func_str(func); + std::istringstream stream(func_str); LibExprReader reader(func, cell, error_msg, report); LibExprScanner scanner(stream); LibExprParse parser(&scanner, &reader); @@ -55,9 +56,9 @@ parseFuncExpr(const char *func, return nullptr; } -LibExprReader::LibExprReader(const char *func, +LibExprReader::LibExprReader(std::string_view func, const LibertyCell *cell, - const char *error_msg, + std::string_view error_msg, Report *report) : func_(func), cell_(cell), @@ -70,18 +71,18 @@ LibExprReader::LibExprReader(const char *func, // defined in LibertyReader.cc LibertyPort * libertyReaderFindPort(const LibertyCell *cell, - const char *port_name); + std::string_view port_name); FuncExpr * -LibExprReader::makeFuncExprPort(const char *port_name) +LibExprReader::makeFuncExprPort(std::string &&port_name) { FuncExpr *expr = nullptr; - LibertyPort *port = libertyReaderFindPort(cell_, port_name); + const std::string_view port_view(port_name); + LibertyPort *port = libertyReaderFindPort(cell_, port_view); if (port) expr = FuncExpr::makePort(port); else - report_->warn(1130, "{} references unknown port {}.", error_msg_, port_name); - stringDelete(port_name); + report_->warn(1130, "{} references unknown port {}.", error_msg_, port_view); return expr; } @@ -131,7 +132,7 @@ LibExprReader::setResult(FuncExpr *result) } void -LibExprReader::parseError(const char *msg) +LibExprReader::parseError(std::string_view msg) { report_->error(1131, "{} {}.", error_msg_, msg); } diff --git a/liberty/LibExprReader.hh b/liberty/LibExprReader.hh index 3f65600e0..6b0c8cbe6 100644 --- a/liberty/LibExprReader.hh +++ b/liberty/LibExprReader.hh @@ -24,6 +24,8 @@ #pragma once +#include + namespace sta { class Report; @@ -31,9 +33,9 @@ class FuncExpr; class LibertyCell; FuncExpr * -parseFuncExpr(const char *func, +parseFuncExpr(std::string_view func, const LibertyCell *cell, - const char *error_msg, + std::string_view error_msg, Report *report); } // namespace diff --git a/liberty/LibExprReaderPvt.hh b/liberty/LibExprReaderPvt.hh index aa7d0646c..b6b27bc83 100644 --- a/liberty/LibExprReaderPvt.hh +++ b/liberty/LibExprReaderPvt.hh @@ -24,6 +24,9 @@ #pragma once +#include +#include + namespace sta { class Report; @@ -34,11 +37,11 @@ class LibExprScanner; class LibExprReader { public: - LibExprReader(const char *func, + LibExprReader(std::string_view func, const LibertyCell *cell, - const char *error_msg, + std::string_view error_msg, Report *report); - FuncExpr *makeFuncExprPort(const char *port_name); + FuncExpr *makeFuncExprPort(std::string &&port_name); FuncExpr *makeFuncExprOr(FuncExpr *arg1, FuncExpr *arg2); FuncExpr *makeFuncExprAnd(FuncExpr *arg1, @@ -48,15 +51,13 @@ public: FuncExpr *makeFuncExprNot(FuncExpr *arg); void setResult(FuncExpr *result); FuncExpr *result() { return result_; } - void parseError(const char *msg); - size_t copyInput(char *buf, - size_t max_size); + void parseError(std::string_view msg); Report *report() const { return report_; } private: - const char *func_; + std::string_view func_; const LibertyCell *cell_; - const char *error_msg_; + std::string_view error_msg_; Report *report_; FuncExpr *result_; }; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 17d0b4646..ed4717453 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -62,9 +62,9 @@ deleteLiberty() TimingArcSet::destroy(); } -LibertyLibrary::LibertyLibrary(const char *name, - const char *filename) : - ConcreteLibrary(name, filename, true), +LibertyLibrary::LibertyLibrary(std::string name, + std::string filename) : + ConcreteLibrary(std::move(name), std::move(filename), true), units_(new Units()), delay_model_type_(DelayModelType::table), // default nominal_process_(0.0), @@ -121,7 +121,7 @@ LibertyLibrary::~LibertyLibrary() } LibertyCell * -LibertyLibrary::findLibertyCell(const char *name) const +LibertyLibrary::findLibertyCell(std::string_view name) const { return static_cast(findCell(name)); } @@ -188,10 +188,9 @@ LibertyLibrary::makeBusDcl(std::string name, } BusDcl * -LibertyLibrary::findBusDcl(const char *name) const +LibertyLibrary::findBusDcl(std::string_view name) { - auto it = bus_dcls_.find(name); - return it != bus_dcls_.end() ? const_cast(&it->second) : nullptr; + return findStringValuePtr(bus_dcls_, name); } BusDclSeq @@ -208,16 +207,17 @@ LibertyLibrary::makeTableTemplate(std::string name, TableTemplateType type) { std::string key = name; - auto [it, inserted] = template_maps_[int(type)].try_emplace( - std::move(key), std::move(name), type); + auto [it, inserted] = template_maps_[int(type)].try_emplace(std::move(key), + std::move(name), + type); return &it->second; } TableTemplate * -LibertyLibrary::findTableTemplate(const char *name, +LibertyLibrary::findTableTemplate(std::string_view name, TableTemplateType type) { - return findKeyValuePtr(template_maps_[int(type)], name); + return findStringValuePtr(template_maps_[int(type)], name); } TableTemplateSeq @@ -265,16 +265,17 @@ LibertyLibrary::setScaleFactors(ScaleFactors *scales) } ScaleFactors * -LibertyLibrary::makeScaleFactors(const char *name) +LibertyLibrary::makeScaleFactors(std::string name) { - auto [it, inserted] = scale_factors_map_.emplace(name, name); + std::string key = name; + auto [it, inserted] = scale_factors_map_.emplace(std::move(key), std::move(name)); return &it->second; } ScaleFactors * -LibertyLibrary::findScaleFactors(const char *name) +LibertyLibrary::findScaleFactors(std::string_view name) { - return findKeyValuePtr(scale_factors_map_, name); + return findStringValuePtr(scale_factors_map_,name); } float @@ -566,16 +567,14 @@ LibertyLibrary::setDefaultOutputPinRes(const RiseFall *rf, Wireload * LibertyLibrary::makeWireload(std::string name) { - std::string key = name; - auto [it, inserted] = wireloads_.try_emplace( - std::move(key), name.c_str(), this); + auto [it, inserted] = wireloads_.try_emplace(name, name, this); return &it->second; } const Wireload * -LibertyLibrary::findWireload(const char *name) const +LibertyLibrary::findWireload(std::string_view name) { - return findKeyValuePtr(wireloads_, name); + return findStringValuePtr(wireloads_, name); } void @@ -594,14 +593,15 @@ WireloadSelection * LibertyLibrary::makeWireloadSelection(std::string name) { std::string key = name; - auto [it, inserted] = wire_load_selections_.try_emplace(std::move(key), name.c_str()); + auto [it, inserted] = wire_load_selections_.try_emplace(std::move(key), + std::move(name)); return &it->second; } const WireloadSelection * -LibertyLibrary::findWireloadSelection(const char *name) const +LibertyLibrary::findWireloadSelection(std::string_view name) const { - return findKeyValuePtr(wire_load_selections_, name); + return findStringValuePtr(wire_load_selections_, name); } const WireloadSelection * @@ -632,15 +632,14 @@ OperatingConditions * LibertyLibrary::makeOperatingConditions(std::string name) { std::string key = name; - auto [it, inserted] = operating_conditions_.try_emplace( - std::move(key), name.c_str()); + auto [it, inserted] = operating_conditions_.try_emplace(std::move(key), std::move(name)); return &it->second; } OperatingConditions * -LibertyLibrary::findOperatingConditions(const char *name) +LibertyLibrary::findOperatingConditions(std::string_view name) { - return findKeyValuePtr(operating_conditions_, name); + return findStringValuePtr(operating_conditions_, name); } OperatingConditions * @@ -720,11 +719,10 @@ LibertyLibrary::setSlewDerateFromLibrary(float derate) } LibertyCell * -LibertyLibrary::makeScaledCell(const char *name, - const char *filename) +LibertyLibrary::makeScaledCell(std::string name, + std::string filename) { - LibertyCell *cell = new LibertyCell(this, name, filename); - return cell; + return new LibertyCell(this, std::move(name), std::move(filename)); } //////////////////////////////////////////////////////////////// @@ -738,8 +736,7 @@ LibertyLibrary::makeSceneMap(LibertyLibrary *lib, LibertyCellIterator cell_iter(lib); while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); - const char *name = cell->name(); - LibertyCell *link_cell = network->findLibertyCell(name); + LibertyCell *link_cell = network->findLibertyCell(cell->name()); if (link_cell) makeSceneMap(link_cell, cell, ap_index, report); } @@ -769,8 +766,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, LibertyCellPortBitIterator port_iter1(cell1); while (port_iter1.hasNext()) { LibertyPort *port1 = port_iter1.next(); - const char *port_name = port1->name(); - LibertyPort *port2 = cell2->findLibertyPort(port_name); + LibertyPort *port2 = cell2->findLibertyPort(port1->name()); if (port2) { if (link) port1->setScenePort(port2, ap_index); @@ -779,7 +775,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.", cell1->library()->name(), cell1->name(), - port_name, + port1->name(), cell2->library()->name(), cell2->name()); } @@ -807,7 +803,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, cell1->name(), arc_set1->from() ? arc_set1->from()->name() : "", arc_set1->to()->name(), - arc_set1->role()->to_string().c_str(), + arc_set1->role()->to_string(), cell2->library()->name(), cell2->name()); } @@ -824,8 +820,8 @@ LibertyLibrary::checkScenes(LibertyCell *cell, report->error(1112, "Liberty cell {}/{} for corner {}/{} not found.", cell->libertyLibrary()->name(), cell->name(), - scene->name().c_str(), - min_max->to_string().c_str()); + scene->name(), + min_max->to_string()); } } } @@ -865,21 +861,20 @@ LibertyLibrary::makeOcvDerate(std::string name) } OcvDerate * -LibertyLibrary::findOcvDerate(const char *derate_name) +LibertyLibrary::findOcvDerate(std::string_view derate_name) { - auto it = ocv_derate_map_.find(derate_name); - return it != ocv_derate_map_.end() ? &it->second : nullptr; + return findStringValuePtr(ocv_derate_map_, derate_name); } void -LibertyLibrary::addSupplyVoltage(const char *supply_name, +LibertyLibrary::addSupplyVoltage(std::string supply_name, float voltage) { - supply_voltage_map_[supply_name] = voltage; + supply_voltage_map_[std::move(supply_name)] = voltage; } void -LibertyLibrary::supplyVoltage(const char *supply_name, +LibertyLibrary::supplyVoltage(std::string_view supply_name, // Return value. float &voltage, bool &exists) const @@ -896,26 +891,26 @@ LibertyLibrary::supplyVoltage(const char *supply_name, } bool -LibertyLibrary::supplyExists(const char *supply_name) const +LibertyLibrary::supplyExists(std::string_view supply_name) const { return supply_voltage_map_.contains(supply_name); } DriverWaveform * -LibertyLibrary::findDriverWaveform(const char *name) +LibertyLibrary::findDriverWaveform(std::string_view name) { - auto it = driver_waveform_map_.find(name); - if (it != driver_waveform_map_.end()) - return &it->second; - return nullptr; + return findStringValuePtr(driver_waveform_map_, name); } DriverWaveform * -LibertyLibrary::makeDriverWaveform(const std::string &name, +LibertyLibrary::makeDriverWaveform(std::string name, TablePtr waveforms) { - auto it = driver_waveform_map_.emplace(name, DriverWaveform(name, waveforms)); - return &it.first->second; + std::string key = name; + auto [it, inserted] = driver_waveform_map_.try_emplace(std::move(key), + std::move(name), + waveforms); + return &it->second; } //////////////////////////////////////////////////////////////// @@ -940,8 +935,8 @@ LibertyCellIterator::next() //////////////////////////////////////////////////////////////// LibertyCell::LibertyCell(LibertyLibrary *library, - const char *name, - const char *filename) : + std::string name, + std::string filename) : ConcreteCell(name, filename, true, library), liberty_library_(library), area_(0.0), @@ -982,7 +977,7 @@ LibertyCell::~LibertyCell() } LibertyPort * -LibertyCell::findLibertyPort(const char *name) const +LibertyCell::findLibertyPort(std::string_view name) const { return static_cast(findPort(name)); } @@ -1032,9 +1027,9 @@ LibertyCell::makeModeDef(std::string name) } const ModeDef * -LibertyCell::findModeDef(const char *name) const +LibertyCell::findModeDef(std::string_view name) const { - return findKeyValuePtr(mode_defs_, name); + return findStringValuePtr(mode_defs_, name); } void @@ -1054,10 +1049,9 @@ LibertyCell::makeBusDcl(std::string name, } BusDcl * -LibertyCell::findBusDcl(const char *name) const +LibertyCell::findBusDcl(std::string_view name) { - auto it = bus_dcls_.find(name); - return it != bus_dcls_.end() ? const_cast(&it->second) : nullptr; + return findStringValuePtr(bus_dcls_, name); } void @@ -1494,8 +1488,7 @@ LibertyCell::outputPortSequential(LibertyPort *port) bool LibertyCell::hasSequentials() const { - return !sequentials_.empty() - || statetable_ != nullptr; + return !sequentials_.empty(); } void @@ -1639,10 +1632,9 @@ LibertyCell::makeOcvDerate(std::string name) } OcvDerate * -LibertyCell::findOcvDerate(const char *derate_name) +LibertyCell::findOcvDerate(std::string_view derate_name) { - auto it = ocv_derate_map_.find(derate_name); - return it != ocv_derate_map_.end() ? &it->second : nullptr; + return findStringValuePtr(ocv_derate_map_, derate_name); } //////////////////////////////////////////////////////////////// @@ -1936,35 +1928,16 @@ LibertyCell::ensureVoltageWaveforms(const SceneSeq &scenes) } } -const char * -LibertyCell::footprint() const -{ - if (footprint_.empty()) - return nullptr; - else - return footprint_.c_str(); -} - - void -LibertyCell::setFootprint(const char *footprint) -{ - footprint_ = footprint; -} - -const char * -LibertyCell::userFunctionClass() const +LibertyCell::setFootprint(std::string footprint) { - if (user_function_class_.empty()) - return nullptr; - else - return user_function_class_.c_str(); + footprint_ = std::move(footprint); } void -LibertyCell::setUserFunctionClass(const char *user_function_class) +LibertyCell::setUserFunctionClass(std::string user_function_class) { - user_function_class_ = user_function_class; + user_function_class_ = std::move(user_function_class); } //////////////////////////////////////////////////////////////// @@ -2013,14 +1986,15 @@ LibertyCellPortBitIterator::next() //////////////////////////////////////////////////////////////// LibertyPort::LibertyPort(LibertyCell *cell, - const char *name, + std::string name, bool is_bus, BusDcl *bus_dcl, int from_index, int to_index, bool is_bundle, ConcretePortSeq *members) : - ConcretePort(name, is_bus, from_index, to_index, is_bundle, members, cell), + ConcretePort(name, is_bus, from_index, to_index, + is_bundle, members, cell), liberty_cell_(cell), bus_dcl_(bus_dcl), pwr_gnd_type_(PwrGndType::none), @@ -2033,6 +2007,8 @@ LibertyPort::LibertyPort(LibertyCell *cell, min_period_(0.0), pulse_clk_trigger_(nullptr), pulse_clk_sense_(nullptr), + related_ground_port_(nullptr), + related_power_port_(nullptr), receiver_model_(nullptr), driver_waveform_{nullptr, nullptr}, min_pulse_width_exists_(false), @@ -2103,9 +2079,9 @@ LibertyPort::setPwrGndType(PwrGndType type) } void -LibertyPort::setVoltageName(const char *voltage_name) +LibertyPort::setVoltageName(std::string voltage_name) { - voltage_name_ = voltage_name; + voltage_name_ = std::move(voltage_name); } static EnumNameMap pwr_gnd_type_map = @@ -2121,21 +2097,21 @@ static EnumNameMap pwr_gnd_type_map = {PwrGndType::deepnwell, "deepnwell"}, {PwrGndType::deeppwell, "deeppwell"}}; -const char * +const std::string & pwrGndTypeName(PwrGndType pg_type) { return pwr_gnd_type_map.find(pg_type); } PwrGndType -findPwrGndType(const char *pg_name) +findPwrGndType(std::string_view pg_name) { return pwr_gnd_type_map.find(pg_name, PwrGndType::none); } //////////////////////////////////////////////////////////////// -const char * +const std::string & scanSignalTypeName(ScanSignalType scan_type) { return scan_signal_type_map.find(scan_type); @@ -2491,7 +2467,7 @@ LibertyPort::equiv(const LibertyPort *port1, { return (port1 == nullptr && port2 == nullptr) || (port1 != nullptr && port2 != nullptr - && stringEq(port1->name(), port2->name()) + && port1->name() == port2->name() && port1->direction() == port2->direction() && port1->pwr_gnd_type_ == port2->pwr_gnd_type_); } @@ -2504,14 +2480,14 @@ LibertyPort::less(const LibertyPort *port1, return true; if (port1 != nullptr && port2 == nullptr) return false; - const char *name1 = port1->name(); - const char *name2 = port2->name(); - if (stringEq(name1, name2)) { + const std::string &name1 = port1->name(); + const std::string &name2 = port2->name(); + if (name1 == name2) { PortDirection *dir1 = port1->direction(); PortDirection *dir2 = port2->direction(); return dir1->index() < dir2->index(); } - return stringLess(name1, name2); + return name1 < name2; } void @@ -2674,34 +2650,16 @@ LibertyPort::setScenePort(LibertyPort *scene_port, scene_ports_[ap_index] = scene_port; } -const char * -LibertyPort::relatedGroundPin() const -{ - if (related_ground_pin_.empty()) - return nullptr; - else - return related_ground_pin_.c_str(); -} - void -LibertyPort::setRelatedGroundPin(const char *related_ground_pin) +LibertyPort::setRelatedGroundPort(LibertyPort *related_ground_port) { - related_ground_pin_ = related_ground_pin; -} - -const char * -LibertyPort::relatedPowerPin() const -{ - if (related_power_pin_.empty()) - return nullptr; - else - return related_power_pin_.c_str(); + related_ground_port_ = related_ground_port; } void -LibertyPort::setRelatedPowerPin(const char *related_power_pin) +LibertyPort::setRelatedPowerPort(LibertyPort *related_power_port) { - related_power_pin_ = related_power_pin; + related_power_port_ = related_power_port; } void @@ -2711,13 +2669,12 @@ LibertyPort::setReceiverModel(ReceiverModelPtr receiver_model) } std::string -portLibertyToSta(const char *port_name) +portLibertyToSta(std::string_view port_name) { constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; - size_t name_length = strlen(port_name); std::string sta_name; - for (size_t i = 0; i < name_length; i++) { + for (size_t i = 0; i < port_name.size(); i++) { char ch = port_name[i]; if (ch == bus_brkt_left || ch == bus_brkt_right) @@ -2841,7 +2798,7 @@ bool LibertyPortNameLess::operator()(const LibertyPort *port1, const LibertyPort *port2) const { - return stringLess(port1->name(), port2->name()); + return port1->name() < port2->name(); } bool @@ -2900,30 +2857,24 @@ ModeDef::ModeDef(std::string name) : } ModeValueDef * -ModeDef::defineValue(const char *value, - FuncExpr *cond, - const char *sdf_cond) +ModeDef::defineValue(std::string value) { std::string key = value; - std::string sdf = sdf_cond ? sdf_cond : std::string(); - auto [it, inserted] = values_.try_emplace(key, key, cond, std::move(sdf)); + auto [it, inserted] = values_.try_emplace(std::move(key), std::move(value)); return &it->second; } const ModeValueDef * -ModeDef::findValueDef(const char *value) const +ModeDef::findValueDef(std::string_view value) const { - return findKeyValuePtr(values_, value); + return findStringValuePtr(values_, value); } //////////////////////////////////////////////////////////////// -ModeValueDef::ModeValueDef(std::string value, - FuncExpr *cond, - std::string sdf_cond) : +ModeValueDef::ModeValueDef(std::string value) : value_(std::move(value)), - cond_(cond), - sdf_cond_(std::move(sdf_cond)) + cond_(nullptr) { } @@ -3038,25 +2989,14 @@ Pvt::setTemperature(float temp) temperature_ = temp; } -OperatingConditions::OperatingConditions(const char *name) : +OperatingConditions::OperatingConditions(std::string name) : Pvt(0.0, 0.0, 0.0), - name_(name), + name_(std::move(name)), // Default wireload tree. wire_load_tree_(WireloadTree::unknown) { } -OperatingConditions::OperatingConditions(const char *name, - float process, - float voltage, - float temperature, - WireloadTree wire_load_tree) : - Pvt(process, voltage, temperature), - name_(name), - wire_load_tree_(wire_load_tree) -{ -} - void OperatingConditions::setWireloadTree(WireloadTree tree) { @@ -3084,7 +3024,7 @@ static EnumNameMap scale_factor_type_map = {ScaleFactorType::unknown, "unknown"} }; -const char * +const std::string & scaleFactorTypeName(ScaleFactorType type) { return scale_factor_type_map.find(type); @@ -3137,7 +3077,7 @@ findScaleFactorPvt(const char *name) return scale_factor_pvt_names.find(name, ScaleFactorPvt::unknown); } -const char * +const std::string & scaleFactorPvtName(ScaleFactorPvt pvt) { return scale_factor_pvt_names.find(pvt); @@ -3145,8 +3085,8 @@ scaleFactorPvtName(ScaleFactorPvt pvt) //////////////////////////////////////////////////////////////// -ScaleFactors::ScaleFactors(const char *name) : - name_(name) +ScaleFactors::ScaleFactors(std::string name) : + name_(std::move(name)) { for (int type = 0; type < scale_factor_type_count; type++) { for (int pvt = 0; pvt < scale_factor_pvt_count; pvt++) { @@ -3230,7 +3170,7 @@ ScaleFactors::report(Report *report) TestCell::TestCell(LibertyLibrary *library, std::string name, std::string filename) : - LibertyCell(library, name.c_str(), filename.c_str()) + LibertyCell(library, name, filename) { } diff --git a/liberty/Liberty.i b/liberty/Liberty.i index 075295262..a87cd7999 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -173,7 +173,7 @@ find_library_buffers(LibertyLibrary *library) return library->buffers(); } -const char * +std::string_view liberty_port_direction(const LibertyPort *port) { return port->direction()->name(); @@ -220,7 +220,7 @@ timing_role_is_check(const TimingRole *role) //////////////////////////////////////////////////////////////// %extend LibertyLibrary { -const char *name() { return self->name(); } +const char *name() { return self->name().c_str(); } LibertyCell * find_liberty_cell(const char *name) @@ -264,7 +264,7 @@ default_operating_conditions() } // LibertyLibrary methods %extend LibertyCell { -const char *name() { return self->name(); } +const char *name() { return self->name().c_str(); } bool is_leaf() { return self->isLeaf(); } bool is_buffer() { return self->isBuffer(); } bool is_inverter() { return self->isInverter(); } @@ -306,7 +306,8 @@ LibertyCell *test_cell() { return self->testCell(); } } // LibertyCell methods %extend LibertyPort { -const char *bus_name() { return self->busName(); } +const char *name() { return self->name().c_str(); } +std::string bus_name() { return self->busName(); } Cell *cell() { return self->cell(); } bool is_bus() { return self->isBus(); } bool is_bus_bit() { return self->isBusBit(); } @@ -355,7 +356,7 @@ set_direction(const char *dir) const char * scan_signal_type() { - return scanSignalTypeName(self->scanSignalType()); + return scanSignalTypeName(self->scanSignalType()).c_str(); } } // LibertyPort methods @@ -370,10 +371,10 @@ const char *sdf_cond() { return self->sdfCond().c_str(); } std::string full_name() { - const char *from = self->from()->name(); - const char *to = self->to()->name(); - const char *cell_name = self->libertyCell()->name(); - return sta::format("{} {} -> {}", cell_name, from, to); + return sta::format("{} {} -> {}", + self->libertyCell()->name(), + self->from()->name(), + self->to()->name()); } const std::string @@ -395,9 +396,9 @@ timing_arcs() { return self->arcs(); } LibertyPort *from() { return self->from(); } LibertyPort *to() { return self->to(); } const Transition *from_edge() { return self->fromEdge(); } -const char *from_edge_name() { return self->fromEdge()->asRiseFall()->name(); } +const char *from_edge_name() { return self->fromEdge()->asRiseFall()->name().c_str(); } const Transition *to_edge() { return self->toEdge(); } -const char *to_edge_name() { return self->toEdge()->asRiseFall()->name(); } +const char *to_edge_name() { return self->toEdge()->asRiseFall()->name().c_str(); } const TimingRole *role() { return self->role(); } float diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index 52cd97d52..e7e927918 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -36,7 +36,7 @@ namespace sta { LibertyBuilder::LibertyBuilder(Debug *debug, - Report *report) : + Report *report) : debug_(debug), report_(report) { @@ -44,19 +44,19 @@ LibertyBuilder::LibertyBuilder(Debug *debug, LibertyCell * LibertyBuilder::makeCell(LibertyLibrary *library, - const char *name, - const char *filename) + std::string_view name, + std::string_view filename) { - LibertyCell *cell = new LibertyCell(library, name, filename); + LibertyCell *cell = new LibertyCell(library, std::string(name), std::string(filename)); library->addCell(cell); return cell; } LibertyPort * LibertyBuilder::makePort(LibertyCell *cell, - const char *port_name) + std::string_view port_name) { - LibertyPort *port = new LibertyPort(cell, port_name, false, nullptr, + LibertyPort *port = new LibertyPort(cell, std::string(port_name), false, nullptr, -1, -1, false, nullptr); cell->addPort(port); return port; @@ -64,12 +64,12 @@ LibertyBuilder::makePort(LibertyCell *cell, LibertyPort * LibertyBuilder::makeBusPort(LibertyCell *cell, - const char *bus_name, + std::string_view bus_name, int from_index, int to_index, BusDcl *bus_dcl) { - LibertyPort *port = new LibertyPort(cell, bus_name, true, bus_dcl, + LibertyPort *port = new LibertyPort(cell, std::string(bus_name), true, bus_dcl, from_index, to_index, false, new ConcretePortSeq); cell->addPort(port); @@ -81,7 +81,7 @@ void LibertyBuilder::makeBusPortBits(ConcreteLibrary *library, LibertyCell *cell, ConcretePort *bus_port, - const char *bus_name, + std::string_view bus_name, int from_index, int to_index) { @@ -99,22 +99,25 @@ void LibertyBuilder::makeBusPortBit(ConcreteLibrary *library, LibertyCell *cell, ConcretePort *bus_port, - const char *bus_name, + std::string_view bus_name, int bit_index) { - 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); + std::string bit_name; + bit_name.append(bus_name); + bit_name += library->busBrktLeft(); + bit_name += std::to_string(bit_index); + bit_name += library->busBrktRight(); + LibertyPort *port = makePort(cell, std::move(bit_name), bit_index); bus_port->addPortBit(port); cell->addPortBit(port); } LibertyPort * LibertyBuilder::makePort(LibertyCell *cell, - const char *bit_name, + std::string bit_name, int bit_index) { - LibertyPort *port = new LibertyPort(cell, bit_name, false, nullptr, + LibertyPort *port = new LibertyPort(cell, std::move(bit_name), false, nullptr, bit_index, bit_index, false, nullptr); return port; } diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh index 708790920..11e8c2b1d 100644 --- a/liberty/LibertyBuilder.hh +++ b/liberty/LibertyBuilder.hh @@ -24,6 +24,9 @@ #pragma once +#include +#include + #include "MinMax.hh" #include "Transition.hh" #include "LibertyClass.hh" @@ -41,12 +44,12 @@ public: LibertyBuilder(Debug *debug, Report *report); LibertyCell *makeCell(LibertyLibrary *library, - const char *name, - const char *filename); + std::string_view name, + std::string_view filename); LibertyPort *makePort(LibertyCell *cell, - const char *name); + std::string_view name); LibertyPort *makeBusPort(LibertyCell *cell, - const char *bus_name, + std::string_view bus_name, int from_index, int to_index, BusDcl *bus_dcl); @@ -87,24 +90,24 @@ public: TimingArcAttrsPtr attrs); protected: - ConcretePort *makeBusPort(const char *name, + ConcretePort *makeBusPort(std::string_view name, int from_index, int to_index, ConcretePortSeq *members); void makeBusPortBits(ConcreteLibrary *library, LibertyCell *cell, ConcretePort *bus_port, - const char *bus_name, + std::string_view bus_name, int from_index, int to_index); // Bus port bit (internal to makeBusPortBits). LibertyPort *makePort(LibertyCell *cell, - const char *bit_name, + std::string bit_name, int bit_index); void makeBusPortBit(ConcreteLibrary *library, LibertyCell *cell, ConcretePort *bus_port, - const char *bus_name, + std::string_view bus_name, int index); TimingArc *makeTimingArc(TimingArcSet *set, const Transition *from_rf, diff --git a/liberty/LibertyExt.cc b/liberty/LibertyExt.cc index 32c10b5e7..68f1fe77a 100644 --- a/liberty/LibertyExt.cc +++ b/liberty/LibertyExt.cc @@ -28,6 +28,7 @@ // * a string attribute named "thingy" is parsed #include +#include #include "Machine.hh" #include "StringUtil.hh" #include "LibertyReader.hh" @@ -162,15 +163,17 @@ BigcoTimingArcSet::BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, class BigcoLibertyBuilder : public LibertyBuilder { public: - virtual LibertyCell *makeCell(LibertyLibrary *library, const char *name, - const char *filename); + virtual LibertyCell *makeCell(LibertyLibrary *library, std::string_view name, + std::string_view filename); }; LibertyCell * -BigcoLibertyBuilder::makeCell(LibertyLibrary *library, const char *name, - const char *filename) +BigcoLibertyBuilder::makeCell(LibertyLibrary *library, std::string_view name, + std::string_view filename) { - LibertyCell *cell = new BigcoCell(library, name, filename); + std::string name_str(name); + std::string filename_str(filename); + LibertyCell *cell = new BigcoCell(library, name_str.c_str(), filename_str.c_str()); library->addCell(cell); return cell; } @@ -210,9 +213,8 @@ void BigcoLibertyReader::beginCell(const LibertyGroup *group, const LibertyGroup *library_group) { - const char *name = group->firstName(); - if (name - && libertyCellRequired(name)) + if (group->hasFirstParam() + && libertyCellRequired(group->firstParam().c_str())) LibertyReader::beginCell(group, library_group); } @@ -263,7 +265,7 @@ class BigcoSta : public Sta BigcoSta(); protected: - virtual LibertyLibrary *readLibertyFile(const char *filename, + virtual LibertyLibrary *readLibertyFile(std::string_view filename, bool infer_latches, Network *network); }; @@ -275,7 +277,7 @@ BigcoSta::BigcoSta() : // Replace Sta liberty file reader with Bigco's very own. LibertyLibrary * -Sta::readLibertyFile(const char *filename, +Sta::readLibertyFile(std::string_view filename, bool infer_latches, Network *network) { diff --git a/liberty/LibertyParse.yy b/liberty/LibertyParse.yy index b20b5c6cf..f19a7c1fd 100644 --- a/liberty/LibertyParse.yy +++ b/liberty/LibertyParse.yy @@ -44,7 +44,7 @@ void sta::LibertyParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(164, reader->filename().c_str(), + reader->report()->fileError(164, reader->filename(), loc.begin.line, "{}", msg); } @@ -192,7 +192,7 @@ volt_op: expr: expr_term1 | expr_term1 expr_op expr - { $$ = sta::format("{}{}{}", $1.c_str(), $2, $3.c_str()); } + { $$ = sta::format("{}{}{}", $1, $2, $3); } ; expr_term: diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index b3cefe9ac..5ee1be81f 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include "ContainerHelpers.hh" #include "Zlib.hh" @@ -38,11 +39,12 @@ namespace sta { void -parseLibertyFile(const char *filename, +parseLibertyFile(std::string_view filename, LibertyGroupVisitor *library_visitor, Report *report) { - gzstream::igzstream stream(filename); + std::string fn(filename); + gzstream::igzstream stream(fn.c_str()); if (stream.is_open()) { LibertyParser reader(filename, library_visitor, report); LibertyScanner scanner(&stream, filename, &reader, report); @@ -53,7 +55,7 @@ parseLibertyFile(const char *filename, throw FileNotReadable(filename); } -LibertyParser::LibertyParser(const char *filename, +LibertyParser::LibertyParser(std::string_view filename, LibertyGroupVisitor *library_visitor, Report *report) : filename_(filename), @@ -63,7 +65,7 @@ LibertyParser::LibertyParser(const char *filename, } void -LibertyParser::setFilename(const std::string &filename) +LibertyParser::setFilename(std::string_view filename) { filename_ = filename; } @@ -74,7 +76,7 @@ LibertyParser::makeDefine(const LibertyAttrValueSeq *values, { LibertyDefine *define = nullptr; if (values->size() == 3) { - const std::string &define_name = (*values)[0]->stringValue(); + std::string &define_name = (*values)[0]->stringValue(); const std::string &group_type_name = (*values)[1]->stringValue(); const std::string &value_type_name = (*values)[2]->stringValue(); LibertyAttrType value_type = attrValueType(value_type_name); @@ -87,7 +89,7 @@ LibertyParser::makeDefine(const LibertyAttrValueSeq *values, delete values; } else - report_->fileWarn(24, filename_.c_str(), line, + report_->fileWarn(24, filename_, line, "define does not have three arguments."); return define; } @@ -126,12 +128,15 @@ LibertyParser::groupType(const std::string &group_type_name) } void -LibertyParser::groupBegin(const std::string type, +LibertyParser::groupBegin(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); @@ -163,12 +168,13 @@ LibertyParser::deleteGroups() } LibertySimpleAttr * -LibertyParser::makeSimpleAttr(const std::string name, +LibertyParser::makeSimpleAttr(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); @@ -177,7 +183,7 @@ LibertyParser::makeSimpleAttr(const std::string name, } LibertyComplexAttr * -LibertyParser::makeComplexAttr(const std::string name, +LibertyParser::makeComplexAttr(std::string &&name, const LibertyAttrValueSeq *values, int line) { @@ -199,7 +205,7 @@ LibertyParser::makeComplexAttr(const std::string name, } LibertyVariable * -LibertyParser::makeVariable(const std::string var, +LibertyParser::makeVariable(std::string &&var, float value, int line) { @@ -211,7 +217,7 @@ LibertyParser::makeVariable(const std::string var, } LibertyAttrValue * -LibertyParser::makeAttrValueString(std::string value) +LibertyParser::makeAttrValueString(std::string &&value) { return new LibertyAttrValue(std::move(value)); } @@ -225,7 +231,7 @@ LibertyParser::makeAttrValueFloat(float value) //////////////////////////////////////////////////////////////// LibertyScanner::LibertyScanner(std::istream *stream, - const char *filename, + std::string_view filename, LibertyParser *reader, Report *report) : yyFlexLexer(stream), @@ -261,7 +267,7 @@ LibertyScanner::includeBegin() return true; } else { - report_->fileWarn(25, filename_.c_str(), yylineno, + report_->fileWarn(25, filename_, yylineno, "cannot open include file {}.", filename); delete stream; } @@ -287,7 +293,7 @@ LibertyScanner::fileEnd() void LibertyScanner::error(const char *msg) { - report_->fileError(1866, filename_.c_str(), lineno(), "{}", msg); + report_->fileError(1866, filename_, lineno(), "{}", msg); } //////////////////////////////////////////////////////////////// @@ -366,9 +372,9 @@ void LibertyGroup::addAttr(LibertySimpleAttr *attr) { // Only keep the most recent simple attribute value. - const auto &itr = simple_attr_map_.find(attr->name()); - if (itr != simple_attr_map_.end()) - delete itr->second; + const auto &it = simple_attr_map_.find(attr->name()); + if (it != simple_attr_map_.end()) + delete it->second; simple_attr_map_[attr->name()] = attr; } @@ -384,37 +390,46 @@ LibertyGroup::addVariable(LibertyVariable *var) variables_.push_back(var); } -const char * -LibertyGroup::firstName() const +bool +LibertyGroup::hasFirstParam() const { - if (params_.size() >= 1) { - LibertyAttrValue *value = params_[0]; - if (value->isString()) - return value->stringValue().c_str(); - } - return nullptr; + return !params_.empty(); } -const char * -LibertyGroup::secondName() const +const std::string & +LibertyGroup::firstParam() const +{ + LibertyAttrValue *value = params_[0]; + return value->stringValue(); +} + +bool +LibertyGroup::hasSecondParam() const +{ + return params_.size() >= 2; +} + +const std::string & +LibertyGroup::secondParam() const { LibertyAttrValue *value = params_[1]; - if (value->isString()) - return value->stringValue().c_str(); - else - return nullptr; + return value->stringValue(); } const LibertyGroupSeq & -LibertyGroup::findSubgroups(const std::string type) const +LibertyGroup::findSubgroups(std::string_view type) const { - return findKeyValue(subgroup_map_, type); + auto it = subgroup_map_.find(type); + if (it != subgroup_map_.end()) + return it->second; + static const LibertyGroupSeq empty; + return empty; } const LibertyGroup * -LibertyGroup::findSubgroup(const std::string type) const +LibertyGroup::findSubgroup(std::string_view type) const { - const LibertyGroupSeq &groups = findKeyValue(subgroup_map_, type); + const LibertyGroupSeq &groups = findSubgroups(type); if (groups.size() >= 1) return groups[0]; else @@ -422,39 +437,43 @@ LibertyGroup::findSubgroup(const std::string type) const } const LibertySimpleAttr * -LibertyGroup::findSimpleAttr(const std::string attr_name) const +LibertyGroup::findSimpleAttr(std::string_view attr_name) const { - return findKeyValue(simple_attr_map_, attr_name); + return findStringKey(simple_attr_map_, attr_name); } const LibertyComplexAttrSeq & -LibertyGroup::findComplexAttrs(const std::string attr_name) const +LibertyGroup::findComplexAttrs(std::string_view attr_name) const { - return findKeyValue(complex_attr_map_, attr_name); + auto it = complex_attr_map_.find(attr_name); + if (it != complex_attr_map_.end()) + return it->second; + static const LibertyComplexAttrSeq empty; + return empty; } const LibertyComplexAttr * -LibertyGroup::findComplexAttr(const std::string attr_name) const +LibertyGroup::findComplexAttr(std::string_view attr_name) const { - const LibertyComplexAttrSeq &attrs = findKeyValue(complex_attr_map_, attr_name); + const LibertyComplexAttrSeq &attrs = findComplexAttrs(attr_name); if (attrs.size() >= 1) return attrs[0]; else return nullptr; } -const std::string * -LibertyGroup::findAttrString(const std::string attr_name) const +const std::string & +LibertyGroup::findAttrString(std::string_view attr_name) const { const LibertySimpleAttr *attr = findSimpleAttr(attr_name); if (attr) - return &attr->value().stringValue(); - else - return nullptr; + return attr->value().stringValue(); + static const std::string null_string; + return null_string; } void -LibertyGroup::findAttrFloat(const std::string attr_name, +LibertyGroup::findAttrFloat(std::string_view attr_name, // Return values. float &value, bool &exists) const @@ -463,26 +482,25 @@ LibertyGroup::findAttrFloat(const std::string attr_name, if (attr) { const LibertyAttrValue &attr_value = attr->value(); if (attr_value.isFloat()) { - value = attr_value.floatValue(); - exists = true; + auto [value1, exists1] = attr_value.floatValue(); + value = value1; + exists = exists1; return; } else { // Possibly quoted string float. const std::string &float_str = attr_value.stringValue(); - char *end = nullptr; - value = std::strtof(float_str.c_str(), &end); - if (end) { - exists = true; - return; - } + auto [value1, valid1] = stringFloat(float_str); + value = value1; + exists = valid1; + return; } } exists = false; } void -LibertyGroup::findAttrInt(const std::string attr_name, +LibertyGroup::findAttrInt(std::string_view attr_name, // Return values. int &value, bool &exists) const @@ -491,8 +509,9 @@ LibertyGroup::findAttrInt(const std::string attr_name, if (attr) { const LibertyAttrValue &attr_value = attr->value(); if (attr_value.isFloat()) { - value = static_cast(attr_value.floatValue()); - exists = true; + auto [value1, exists1] = attr_value.floatValue(); + value = static_cast(value1); + exists = exists1; return; } } @@ -501,7 +520,7 @@ LibertyGroup::findAttrInt(const std::string attr_name, //////////////////////////////////////////////////////////////// -LibertySimpleAttr::LibertySimpleAttr(const std::string name, +LibertySimpleAttr::LibertySimpleAttr(std::string &&name, const LibertyAttrValue value, int line) : name_(std::move(name)), @@ -510,15 +529,9 @@ LibertySimpleAttr::LibertySimpleAttr(const std::string name, { } -const std::string * -LibertySimpleAttr::stringValue() const -{ - return &value().stringValue(); -} - //////////////////////////////////////////////////////////////// -LibertyComplexAttr::LibertyComplexAttr(std::string name, +LibertyComplexAttr::LibertyComplexAttr(std::string &&name, const LibertyAttrValueSeq values, int line) : name_(std::move(name)), @@ -540,7 +553,7 @@ LibertyComplexAttr::firstValue() const //////////////////////////////////////////////////////////////// -LibertyAttrValue::LibertyAttrValue(std::string value) : +LibertyAttrValue::LibertyAttrValue(std::string &&value) : string_value_(std::move(value)) { } @@ -562,39 +575,18 @@ LibertyAttrValue::isString() const return !string_value_.empty(); } -float +std::pair LibertyAttrValue::floatValue() const { - if (!string_value_.empty()) - criticalError(1127, "LibertyAttrValue::floatValue() called on string"); - return float_value_; -} - -void -LibertyAttrValue::floatValue( // Return values. - float &value, - bool &valid) const -{ - valid = false; - if (string_value_.empty()) { - value = float_value_; - valid = true; - } - else { - // Some floats are enclosed in quotes. - char *end; - value = strtof(string_value_.c_str(), &end); - if ((*end == '\0' || isspace(*end)) - // strtof support INF as a valid float. - && string_value_ != "inf") { - valid = true; - } - } + if (string_value_.empty()) + return {float_value_, true}; + else + return stringFloat(string_value_); } //////////////////////////////////////////////////////////////// -LibertyDefine::LibertyDefine(std::string name, +LibertyDefine::LibertyDefine(std::string &&name, LibertyGroupType group_type, LibertyAttrType value_type, int line) : diff --git a/liberty/LibertyParser.hh b/liberty/LibertyParser.hh index 00f2ac33d..de9ec60c1 100644 --- a/liberty/LibertyParser.hh +++ b/liberty/LibertyParser.hh @@ -24,8 +24,11 @@ #pragma once +#include +#include #include #include +#include #include "Zlib.hh" #include "StringUtil.hh" @@ -43,15 +46,15 @@ class LibertyVariable; class LibertyScanner; using LibertyGroupSeq = std::vector; -using LibertySubGroupMap = std::map; -using LibertySimpleAttrMap = std::map; +using LibertySubGroupMap = std::map>; +using LibertySimpleAttrMap = std::map>; using LibertyComplexAttrSeq = std::vector; -using LibertyComplexAttrMap = std::map; -using LibertyDefineMap = std::map; +using LibertyComplexAttrMap = std::map>; +using LibertyDefineMap = std::map>; using LibertyAttrValueSeq = std::vector; using LibertyVariableSeq = std::vector; -using LibertyVariableMap = std::map; -using LibertyGroupVisitorMap = std::map; +using LibertyVariableMap = std::map>; +using LibertyGroupVisitorMap = std::map>; enum class LibertyAttrType { attr_string, attr_int, attr_double, attr_boolean, attr_unknown }; @@ -61,31 +64,31 @@ enum class LibertyGroupType { library, cell, pin, timing, unknown }; class LibertyParser { public: - LibertyParser(const char *filename, + LibertyParser(std::string_view filename, LibertyGroupVisitor *library_visitor, Report *report); const std::string &filename() const { return filename_; } - void setFilename(const std::string &filename); + void setFilename(std::string_view filename); Report *report() const { return report_; } LibertyDefine *makeDefine(const LibertyAttrValueSeq *values, int line); LibertyAttrType attrValueType(const std::string &value_type_name); LibertyGroupType groupType(const std::string &group_type_name); - void groupBegin(const std::string type, + void groupBegin(std::string &&type, LibertyAttrValueSeq *params, int line); LibertyGroup *groupEnd(); LibertyGroup *group(); void deleteGroups(); - LibertySimpleAttr *makeSimpleAttr(const std::string name, + LibertySimpleAttr *makeSimpleAttr(std::string &&name, const LibertyAttrValue *value, int line); - LibertyComplexAttr *makeComplexAttr(const std::string name, + LibertyComplexAttr *makeComplexAttr(std::string &&name, const LibertyAttrValueSeq *values, int line); - LibertyAttrValue *makeAttrValueString(const std::string value); + LibertyAttrValue *makeAttrValueString(std::string &&value); LibertyAttrValue *makeAttrValueFloat(float value); - LibertyVariable *makeVariable(const std::string var, + LibertyVariable *makeVariable(std::string &&var, float value, int line); @@ -102,14 +105,12 @@ class LibertyAttrValue public: LibertyAttrValue() {} LibertyAttrValue(float value); - LibertyAttrValue(std::string value); + LibertyAttrValue(std::string &&value); bool isString() const; bool isFloat() const; - float floatValue() const; - void floatValue(// Return values. - float &value, - bool &valid) const; + std::pair floatValue() const; const std::string &stringValue() const { return string_value_; } + std::string &stringValue() { return string_value_; } private: float float_value_; @@ -131,23 +132,23 @@ public: bool oneGroupOnly() const; const std::string &type() const { return type_; } const LibertyAttrValueSeq ¶ms() const { return params_; } - // First param as a string. - const char *firstName() const; - // Second param as a string. - const char *secondName() const; + bool hasFirstParam() const; + const std::string &firstParam() const; + bool hasSecondParam() const; + const std::string &secondParam() const; int line() const { return line_; } - const LibertyGroupSeq &findSubgroups(const std::string type) const; - const LibertyGroup *findSubgroup(const std::string type) const; - const LibertySimpleAttr *findSimpleAttr(const std::string attr_name) const; - const LibertyComplexAttrSeq &findComplexAttrs(const std::string attr_name) const; - const LibertyComplexAttr *findComplexAttr(const std::string attr_name) const; - const std::string *findAttrString(const std::string attr_name) const; - void findAttrFloat(const std::string attr_name, + const LibertyGroupSeq &findSubgroups(std::string_view type) const; + const LibertyGroup *findSubgroup(std::string_view type) const; + const LibertySimpleAttr *findSimpleAttr(std::string_view attr_name) const; + const LibertyComplexAttrSeq &findComplexAttrs(std::string_view attr_name) const; + const LibertyComplexAttr *findComplexAttr(std::string_view attr_name) const; + const std::string &findAttrString(std::string_view attr_name) const; + void findAttrFloat(std::string_view attr_name, // Return values. float &value, bool &exists) const; - void findAttrInt(const std::string attr_name, + void findAttrInt(std::string_view attr_name, // Return values. int &value, bool &exists) const; @@ -189,12 +190,12 @@ public: class LibertySimpleAttr { public: - LibertySimpleAttr(const std::string name, + LibertySimpleAttr(std::string &&name, const LibertyAttrValue value, int line); const std::string &name() const { return name_; } const LibertyAttrValue &value() const { return value_; }; - const std::string *stringValue() const; + const std::string &stringValue() const { return value_.stringValue(); } int line() const { return line_; } private: @@ -208,7 +209,7 @@ private: class LibertyComplexAttr { public: - LibertyComplexAttr(const std::string name, + LibertyComplexAttr(std::string &&name, const LibertyAttrValueSeq values, int line); ~LibertyComplexAttr(); @@ -229,7 +230,7 @@ private: class LibertyDefine { public: - LibertyDefine(std::string name, + LibertyDefine(std::string &&name, LibertyGroupType group_type, LibertyAttrType value_type, int line); @@ -280,7 +281,7 @@ public: }; void -parseLibertyFile(const char *filename, +parseLibertyFile(std::string_view filename, LibertyGroupVisitor *library_visitor, Report *report); } // namespace diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index a873a8815..b91c10e69 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -28,11 +28,13 @@ #include #include #include +#include #include "ContainerHelpers.hh" -#include "EnumNameMap.hh" +#include "Format.hh" #include "Report.hh" #include "Debug.hh" +#include "EnumNameMap.hh" #include "Units.hh" #include "Transition.hh" #include "FuncExpr.hh" @@ -60,7 +62,7 @@ scaleFloats(FloatSeq &floats, float scale); LibertyLibrary * -readLibertyFile(const char *filename, +readLibertyFile(std::string_view filename, bool infer_latches, Network *network) { @@ -68,7 +70,7 @@ readLibertyFile(const char *filename, return reader.readLibertyFile(filename); } -LibertyReader::LibertyReader(const char *filename, +LibertyReader::LibertyReader(std::string_view filename, bool infer_latches, Network *network) : LibertyGroupVisitor(), @@ -84,7 +86,7 @@ LibertyReader::LibertyReader(const char *filename, } LibertyLibrary * -LibertyReader::readLibertyFile(const char *filename) +LibertyReader::readLibertyFile(std::string_view filename) { //::LibertyParse_debug = 1; parseLibertyFile(filename, this, report_); @@ -92,14 +94,15 @@ LibertyReader::readLibertyFile(const char *filename) } void -LibertyReader::defineGroupVisitor(const char *type, +LibertyReader::defineGroupVisitor(std::string_view type, LibraryGroupVisitor begin_visitor, LibraryGroupVisitor end_visitor) { + std::string type_str(type); if (begin_visitor) - group_begin_map_[type] = begin_visitor; + group_begin_map_[type_str] = begin_visitor; if (end_visitor) - group_end_map_[type] = end_visitor; + group_end_map_[type_str] = end_visitor; } void @@ -170,10 +173,10 @@ LibertyReader::endCell(const LibertyGroup *cell_group, if (!library_group->oneGroupOnly()) readLibraryAttributes(library_group); - const char *name = cell_group->firstName(); - if (name) { + if (cell_group->hasFirstParam()) { + const std::string &name = cell_group->firstParam(); debugPrint(debug_, "liberty", 1, "cell {}", name); - LibertyCell *cell = builder_.makeCell(library_, name, filename_); + LibertyCell *cell = builder_.makeCell(library_, name, std::string(filename_)); readCell(cell, cell_group); } else @@ -261,16 +264,16 @@ LibertyReader::readLibraryAttributes(const LibertyGroup *library_group) } void -LibertyReader::makeLibrary(const LibertyGroup *libary_group) +LibertyReader::makeLibrary(const LibertyGroup *library_group) { - const char *name = libary_group->firstName(); - if (name) { - LibertyLibrary *library = network_->findLiberty(name); + if (library_group->hasFirstParam()) { + const std::string &lib_name = library_group->firstParam(); + LibertyLibrary *library = network_->findLiberty(lib_name); if (library) - warn(1140, libary_group, "library {} already exists.", name); + warn(1140, library_group, "library {} already exists.", lib_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_); + library_ = network_->makeLibertyLibrary(lib_name, filename_); // 1ns default time_scale_ = 1E-9F; // 1ohm default @@ -296,7 +299,7 @@ LibertyReader::makeLibrary(const LibertyGroup *libary_group) library_->setDelayModelType(DelayModelType::cmos_linear); } else - error(1141, libary_group, "library missing name."); + error(1141, library_group, "library missing name."); } // Energy scale is derived from other units. @@ -341,29 +344,14 @@ LibertyReader::readLibraryUnits(const LibertyGroup *library_group) const LibertyAttrValueSeq &values = cap_attr->values(); if (values.size() == 2) { LibertyAttrValue *value = values[0]; - bool valid = false; - float scale; - if (value->isFloat()) { - scale = value->floatValue(); - valid = true; - } - else if (value->isString()) { - try { - scale = std::stof(value->stringValue()); - valid = true; - } - catch (...) { - valid = false; - } - } - + auto [scale, valid] = value->floatValue(); if (valid) { value = values[1]; if (value->isString()) { const std::string suffix = value->stringValue(); - if (stringEqual(suffix.c_str(), "ff")) + if (stringEqual(suffix, "ff")) cap_scale_ = scale * 1E-15F; - else if (stringEqual(suffix.c_str(), "pf")) + else if (stringEqual(suffix, "pf")) cap_scale_ = scale * 1E-12F; else warn(1154, cap_attr, "capacitive_load_units are not ff or pf."); @@ -383,25 +371,24 @@ LibertyReader::readLibraryUnits(const LibertyGroup *library_group) } void -LibertyReader::readUnit(const char *unit_attr_name, - const char *unit_suffix, +LibertyReader::readUnit(std::string_view unit_attr_name, + std::string_view unit_suffix, float &scale_var, Unit *unit, const LibertyGroup *library_group) { const LibertySimpleAttr *unit_attr = library_group->findSimpleAttr(unit_attr_name); if (unit_attr) { - const std::string *units = unit_attr->stringValue(); - if (units) { + const std::string &units = unit_attr->stringValue(); + if (!units.empty()) { // Unit format is . // Find the multiplier digits. - std::string units1 = *units; - size_t mult_end = units1.find_first_not_of("0123456789"); + size_t mult_end = units.find_first_not_of("0123456789"); float mult = 1.0F; std::string scale_suffix; - if (mult_end != units1.npos) { - std::string unit_mult = units1.substr(0, mult_end); - scale_suffix = units1.substr(mult_end); + if (mult_end != units.npos) { + std::string unit_mult = units.substr(0, mult_end); + scale_suffix = units.substr(mult_end); if (unit_mult == "1") mult = 1.0F; else if (unit_mult == "10") @@ -412,12 +399,12 @@ LibertyReader::readUnit(const char *unit_attr_name, warn(1150, unit_attr, "unknown unit multiplier {}.", unit_mult); } else - scale_suffix = *units; + scale_suffix = units; float scale_mult = 1.0F; - if (scale_suffix.size() == strlen(unit_suffix) + 1) { + if (scale_suffix.size() == unit_suffix.size() + 1) { std::string suffix = scale_suffix.substr(1); - if (stringEqual(suffix.c_str(), unit_suffix)) { + if (stringEqual(suffix, unit_suffix)) { char scale_char = tolower(scale_suffix[0]); if (scale_char == 'k') scale_mult = 1E+3F; @@ -437,7 +424,7 @@ LibertyReader::readUnit(const char *unit_attr_name, else warn(1152, unit_attr, "unknown unit suffix {}.", suffix); } - else if (!stringEqual(scale_suffix.c_str(), unit_suffix)) + else if (!stringEqual(scale_suffix, unit_suffix)) warn(1153, unit_attr, "unknown unit suffix {}.", scale_suffix); scale_var = scale_mult * mult; unit->setScale(scale_var); @@ -448,46 +435,46 @@ LibertyReader::readUnit(const char *unit_attr_name, void LibertyReader::readDelayModel(const LibertyGroup *library_group) { - const std::string *type_name = library_group->findAttrString("delay_model"); - if (type_name) { - if (*type_name == "table_lookup") + const std::string &type_name = library_group->findAttrString("delay_model"); + if (!type_name.empty()) { + if (type_name == "table_lookup") library_->setDelayModelType(DelayModelType::table); - else if (*type_name == "generic_cmos") + else if (type_name == "generic_cmos") library_->setDelayModelType(DelayModelType::cmos_linear); - else if (*type_name == "piecewise_cmos") { + else if (type_name == "piecewise_cmos") { library_->setDelayModelType(DelayModelType::cmos_pwl); - warn(1160, library_group, "delay_model {} not supported.", *type_name); + warn(1160, library_group, "delay_model {} not supported.", type_name); } - else if (*type_name == "cmos2") { + else if (type_name == "cmos2") { library_->setDelayModelType(DelayModelType::cmos2); - warn(1161, library_group, "delay_model {} not supported.", *type_name); + warn(1161, library_group, "delay_model {} not supported.", type_name); } - else if (*type_name == "polynomial") { + else if (type_name == "polynomial") { library_->setDelayModelType(DelayModelType::polynomial); - warn(1162, library_group, "delay_model {} not supported.", *type_name); + warn(1162, library_group, "delay_model {} not supported.", type_name); } // Evil IBM garbage. - else if (*type_name == "dcm") { + else if (type_name == "dcm") { library_->setDelayModelType(DelayModelType::dcm); - warn(1163, library_group, "delay_model {} not supported..", *type_name); + warn(1163, library_group, "delay_model {} not supported..", type_name); } else - warn(1164, library_group, "unknown delay_model {}.", *type_name); + warn(1164, library_group, "unknown delay_model {}.", type_name); } } void LibertyReader::readBusStyle(const LibertyGroup *library_group) { - const std::string *bus_style = library_group->findAttrString("bus_naming_style"); - if (bus_style) { + const std::string &bus_style = library_group->findAttrString("bus_naming_style"); + if (!bus_style.empty()) { // Assume bus style is of the form "%s[%d]". - if (bus_style->size() == 6 - && (*bus_style)[0] == '%' - && (*bus_style)[1] == 's' - && (*bus_style)[3] == '%' - && (*bus_style)[4] == 'd') - library_->setBusBrkts((*bus_style)[2], (*bus_style)[5]); + if (bus_style.size() == 6 + && bus_style[0] == '%' + && bus_style[1] == 's' + && bus_style[3] == '%' + && bus_style[4] == 'd') + library_->setBusBrkts(bus_style[2], bus_style[5]); else warn(1165, library_group, "unknown bus_naming_style format."); } @@ -498,8 +485,8 @@ LibertyReader::readBusTypes(LibertyCell *cell, const LibertyGroup *group) { for (const LibertyGroup *type_group : group->findSubgroups("type")) { - const char *name = type_group->firstName(); - if (name) { + if (type_group->hasFirstParam()) { + const std::string &name = type_group->firstParam(); int from, to; bool from_exists, to_exists; type_group->findAttrInt("bit_from", from, from_exists); @@ -522,14 +509,14 @@ void LibertyReader::readThresholds(const LibertyGroup *library_group) { for (const RiseFall *rf : RiseFall::range()) { - std::string suffix = rf->to_string(); - readLibAttrFloat(library_group, ("input_threshold_pct_" + suffix).c_str(), + std::string suffix(rf->to_string()); + readLibAttrFloat(library_group, "input_threshold_pct_" + suffix, &LibertyLibrary::setInputThreshold, rf, 0.01F); - readLibAttrFloat(library_group, ("output_threshold_pct_" + suffix).c_str(), + readLibAttrFloat(library_group, "output_threshold_pct_" + suffix, &LibertyLibrary::setOutputThreshold, rf, 0.01F); - readLibAttrFloat(library_group, ("slew_lower_threshold_pct_" + suffix).c_str(), + readLibAttrFloat(library_group, "slew_lower_threshold_pct_" + suffix, &LibertyLibrary::setSlewLowerThreshold, rf, 0.01F); - readLibAttrFloat(library_group, ("slew_upper_threshold_pct_" + suffix).c_str(), + readLibAttrFloat(library_group, "slew_upper_threshold_pct_" + suffix, &LibertyLibrary::setSlewUpperThreshold, rf, 0.01F); } } @@ -561,12 +548,13 @@ LibertyReader::readTableTemplates(const LibertyGroup *library_group) void LibertyReader::readTableTemplates(const LibertyGroup *library_group, - const char *group_name, + std::string_view group_name, TableTemplateType type) { - for (const LibertyGroup *template_group : library_group->findSubgroups(group_name)) { - const char *name = template_group->firstName(); - if (name) { + for (const LibertyGroup *template_group : + library_group->findSubgroups(group_name)) { + if (template_group->hasFirstParam()) { + const std::string &name = template_group->firstParam(); TableTemplate *tbl_template = library_->makeTableTemplate(name, type); TableAxisPtr axis1 = makeTableTemplateAxis(template_group, 1); if (axis1) @@ -587,14 +575,14 @@ TableAxisPtr LibertyReader::makeTableTemplateAxis(const LibertyGroup *template_group, int axis_index) { - std::string var_attr_name = "variable_" + std::to_string(axis_index); - const std::string *var_name = template_group->findAttrString(var_attr_name); - if (var_name) { - TableAxisVariable axis_var = stringTableAxisVariable(var_name->c_str()); + std::string var_attr_name = sta::format("variable_{}", axis_index); + const std::string &var_name = template_group->findAttrString(var_attr_name); + if (!var_name.empty()) { + TableAxisVariable axis_var = stringTableAxisVariable(var_name); if (axis_var == TableAxisVariable::unknown) - warn(1297, template_group, "axis type {} not supported.", *var_name); + warn(1297, template_group, "axis type {} not supported.", var_name); else { - std::string index_attr_name = "index_" + std::to_string(axis_index); + std::string index_attr_name = sta::format("index_{}", axis_index); const LibertyComplexAttr *index_attr = template_group->findComplexAttr(index_attr_name); FloatSeq axis_values; @@ -638,11 +626,9 @@ LibertyReader::readVoltateMaps(const LibertyGroup *library_group) const LibertyAttrValueSeq &values = volt_attr->values(); if (values.size() == 2) { const std::string &volt_name = values[0]->stringValue(); - float volt; - bool valid; - values[1]->floatValue(volt, valid); + auto [volt, valid] = values[1]->floatValue(); if (valid) - library_->addSupplyVoltage(volt_name.c_str(), volt); + library_->addSupplyVoltage(volt_name, volt); else warn(1166, volt_attr, "voltage_map voltage is not a float."); } @@ -654,8 +640,8 @@ LibertyReader::readOperatingConds(const LibertyGroup *library_group) { for (const LibertyGroup *opcond_group : library_group->findSubgroups("operating_conditions")) { - const char *name = opcond_group->firstName(); - if (name) { + if (opcond_group->hasFirstParam()) { + const std::string &name = opcond_group->firstParam(); OperatingConditions *op_cond = library_->makeOperatingConditions(name); float value; bool exists; @@ -668,24 +654,24 @@ LibertyReader::readOperatingConds(const LibertyGroup *library_group) opcond_group->findAttrFloat("voltage", value, exists); if (exists) op_cond->setVoltage(value); - const std::string *tree_type = opcond_group->findAttrString("tree_type"); - if (tree_type) { - WireloadTree wireload_tree = stringWireloadTree(tree_type->c_str()); + const std::string &tree_type = opcond_group->findAttrString("tree_type"); + if (!tree_type.empty()) { + WireloadTree wireload_tree = stringWireloadTree(tree_type); op_cond->setWireloadTree(wireload_tree); } } } - const std::string *default_op_cond = + const std::string &default_op_cond = library_group->findAttrString("default_operating_conditions"); - if (default_op_cond) { + if (!default_op_cond.empty()) { OperatingConditions *op_cond = - library_->findOperatingConditions(default_op_cond->c_str()); + library_->findOperatingConditions(default_op_cond); if (op_cond) library_->setDefaultOperatingConditions(op_cond); else warn(1144, library_group, "default_operating_condition {} not found.", - *default_op_cond); + default_op_cond); } } @@ -699,8 +685,8 @@ LibertyReader::readScaleFactors(const LibertyGroup *library_group) // Named scale factors. for (const LibertyGroup *scale_group : library_group->findSubgroups("scaling_factors")){ - const char *name = scale_group->firstName(); - if (name) { + if (scale_group->hasFirstParam()) { + const std::string &name = scale_group->firstParam(); ScaleFactors *scale_factors = library_->makeScaleFactors(name); readScaleFactors(scale_group, scale_factors); } @@ -714,7 +700,7 @@ LibertyReader::readScaleFactors(const LibertyGroup *scale_group, // Skip unknown type. for (int type_index = 0; type_index < scale_factor_type_count - 1; type_index++) { ScaleFactorType type = static_cast(type_index); - const char *type_name = scaleFactorTypeName(type); + const std::string &type_name = scaleFactorTypeName(type); // Skip unknown pvt. for (int pvt_index = 0; pvt_index < scale_factor_pvt_count - 1; pvt_index++) { ScaleFactorPvt pvt = static_cast(pvt_index); @@ -723,18 +709,18 @@ LibertyReader::readScaleFactors(const LibertyGroup *scale_group, for (const RiseFall *rf : RiseFall::range()) { if (scaleFactorTypeRiseFallSuffix(type)) { const std::string rf_name = (rf == RiseFall::rise()) ? "rise" : "fall"; - attr_name = "k_" + pvt_name + "_" + type_name + "_" + rf_name; + attr_name = sta::format("k_{}_{}_{}", pvt_name, type_name, rf_name); } else if (scaleFactorTypeRiseFallPrefix(type)) { - const char *rf_name = (rf == RiseFall::rise()) ? "rise" : "fall"; - attr_name = "k_" + pvt_name + "_" + rf_name + "_" + type_name; + const std::string rf_name = (rf == RiseFall::rise()) ? "rise" : "fall"; + attr_name = sta::format("k_{}_{}_{}", pvt_name, rf_name, type_name); } else if (scaleFactorTypeLowHighSuffix(type)) { - const char *rf_name = (rf == RiseFall::rise()) ? "high":"low"; - attr_name = "k_" + pvt_name + "_" + type_name + "_" + rf_name; + const std::string rf_name = (rf == RiseFall::rise()) ? "high" : "low"; + attr_name = sta::format("k_{}_{}_{}", pvt_name, type_name, rf_name); } else - attr_name = "k_" + pvt_name + "_" + type_name; + attr_name = sta::format("k_{}_{}", pvt_name, type_name); float value; bool exists; scale_group->findAttrFloat(attr_name, value, exists); @@ -749,8 +735,8 @@ void LibertyReader::readWireloads(const LibertyGroup *library_group) { for (const LibertyGroup *wl_group : library_group->findSubgroups("wire_load")) { - const char *name = wl_group->firstName(); - if (name) { + if (wl_group->hasFirstParam()) { + const std::string &name = wl_group->firstParam(); Wireload *wireload = library_->makeWireload(name); float value; bool exists; @@ -787,25 +773,23 @@ LibertyReader::readWireloadSelection(const LibertyGroup *library_group) { const LibertyGroup *sel_group = library_group->findSubgroup("wire_load_selection"); if (sel_group) { - const char *name = sel_group->firstName(); - if (name == nullptr) - name = ""; + std::string name; + if (sel_group->hasFirstParam()) + name = sel_group->firstParam(); WireloadSelection *wireload_selection = library_->makeWireloadSelection(name); for (const LibertyComplexAttr *area_attr : sel_group->findComplexAttrs("wire_load_from_area")) { const LibertyAttrValueSeq &values = area_attr->values(); if (values.size() == 3) { - LibertyAttrValue *value = values[0]; - if (value->isFloat()) { - float min_area = value->floatValue(); - value = values[1]; - if (value->isFloat()) { - float max_area = value->floatValue(); - value = values[2]; + auto [min_area, min_valid] = values[0]->floatValue(); + if (min_valid) { + auto [max_area, max_valid] = values[1]->floatValue(); + if (max_valid) { + LibertyAttrValue *value = values[2]; if (value->isString()) { const std::string &wireload_name = value->stringValue(); const Wireload *wireload = - library_->findWireload(wireload_name.c_str()); + library_->findWireload(wireload_name); if (wireload) wireload_selection->addWireloadFromArea(min_area, max_area, wireload); @@ -831,45 +815,46 @@ LibertyReader::readWireloadSelection(const LibertyGroup *library_group) void LibertyReader::readDefaultWireLoad(const LibertyGroup *library_group) { - const std::string *wireload_name = library_group->findAttrString("default_wire_load"); - if (wireload_name) { - const Wireload *wireload = library_->findWireload(wireload_name->c_str()); + const std::string &wireload_name = + library_group->findAttrString("default_wire_load"); + if (!wireload_name.empty()) { + const Wireload *wireload = library_->findWireload(wireload_name); if (wireload) library_->setDefaultWireload(wireload); else warn(1142, library_group, "default_wire_load {} not found.", - *wireload_name); + wireload_name); } } void LibertyReader::readDefaultWireLoadMode(const LibertyGroup *library_group) { - const std::string *wire_load_mode = + const std::string &wire_load_mode = library_group->findAttrString("default_wire_load_mode"); - if (wire_load_mode) { - WireloadMode mode = stringWireloadMode(wire_load_mode->c_str()); + if (!wire_load_mode.empty()) { + WireloadMode mode = stringWireloadMode(wire_load_mode); if (mode != WireloadMode::unknown) library_->setDefaultWireloadMode(mode); else warn(1174, library_group, "default_wire_load_mode {} not found.", - *wire_load_mode); + wire_load_mode); } } void LibertyReader::readDefaultWireLoadSelection(const LibertyGroup *library_group) { - const std::string *selection_name = + const std::string &selection_name = library_group->findAttrString("default_wire_load_selection"); - if (selection_name) { + if (!selection_name.empty()) { const WireloadSelection *selection = - library_->findWireloadSelection(selection_name->c_str()); + library_->findWireloadSelection(selection_name.c_str()); if (selection) library_->setDefaultWireloadSelection(selection); else warn(1143, library_group, "default_wire_selection {} not found.", - *selection_name); + selection_name); } } @@ -878,20 +863,20 @@ LibertyReader::readModeDefs(LibertyCell *cell, const LibertyGroup *cell_group) { for (const LibertyGroup *mode_group : cell_group->findSubgroups("mode_definition")) { - const char *name = mode_group->firstName(); - if (name) { + if (mode_group->hasFirstParam()) { + const std::string &name = mode_group->firstParam(); ModeDef *mode_def = cell->makeModeDef(name); for (const LibertyGroup *value_group : mode_group->findSubgroups("mode_value")) { - const char *value_name = value_group->firstName(); - if (value_name) { - ModeValueDef *mode_value = mode_def->defineValue(value_name, nullptr, nullptr); - const std::string *sdf_cond = value_group->findAttrString("sdf_cond"); - if (sdf_cond) - mode_value->setSdfCond(sdf_cond->c_str()); - const std::string *when = value_group->findAttrString("when"); - if (when) { + if (value_group->hasFirstParam()) { + const std::string &value_name = value_group->firstParam(); + ModeValueDef *mode_value = mode_def->defineValue(value_name); + const std::string &sdf_cond = value_group->findAttrString("sdf_cond"); + if (!sdf_cond.empty()) + mode_value->setSdfCond(sdf_cond); + const std::string &when = value_group->findAttrString("when"); + if (!when.empty()) { // line - FuncExpr *when_expr = parseFunc(when->c_str(), "when", cell, + FuncExpr *when_expr = parseFunc(when, "when", cell, value_group->line()); mode_value->setCond(when_expr); } @@ -909,9 +894,10 @@ void LibertyReader::readSlewDegradations(const LibertyGroup *library_group) { for (const RiseFall *rf : RiseFall::range()) { - const std::string group_name = rf->to_string() + "_transition_degradation"; + const std::string group_name = sta::format("{}_transition_degradation", + rf->to_string()); const LibertyGroup *degradation_group = - library_group->findSubgroup(group_name.c_str()); + library_group->findSubgroup(group_name); if (degradation_group) { TableModel *table_model = readTableModel(degradation_group, rf, TableTemplateType::delay, @@ -927,7 +913,7 @@ LibertyReader::readSlewDegradations(const LibertyGroup *library_group) void LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, - const char *attr_name, + std::string_view attr_name, void (LibertyLibrary::*set_func)(float value), float scale) { @@ -940,7 +926,7 @@ LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, void LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, - const char *attr_name, + std::string_view attr_name, void (LibertyLibrary::*set_func)(const RiseFall *rf, float value), const RiseFall *rf, @@ -955,7 +941,7 @@ LibertyReader::readLibAttrFloat(const LibertyGroup *library_group, void LibertyReader::readLibAttrFloatWarnZero(const LibertyGroup *library_group, - const char *attr_name, + std::string_view attr_name, void (LibertyLibrary::*set_func)(float value), float scale) { @@ -1008,17 +994,18 @@ LibertyReader::readCell(LibertyCell *cell, void LibertyReader::readScaledCell(const LibertyGroup *scaled_cell_group) { - const char *name = scaled_cell_group->firstName(); - if (name) { + if (scaled_cell_group->hasFirstParam()) { + const std::string &name = scaled_cell_group->firstParam(); LibertyCell *owner = library_->findLibertyCell(name); if (owner) { - const char *op_cond_name = scaled_cell_group->secondName(); - if (op_cond_name) { + if (scaled_cell_group->hasSecondParam()) { + const std::string &op_cond_name = scaled_cell_group->secondParam(); OperatingConditions *op_cond = library_->findOperatingConditions(op_cond_name); if (op_cond) { debugPrint(debug_, "liberty", 1, "scaled cell {} {}", - name, op_cond_name); - LibertyCell *scaled_cell = library_->makeScaledCell(name, filename_); + name.c_str(), op_cond_name.c_str()); + LibertyCell *scaled_cell = library_->makeScaledCell(name, + std::string(filename_)); readCell(scaled_cell, scaled_cell_group); checkScaledCell(scaled_cell, owner, scaled_cell_group, op_cond_name); // Add scaled cell AFTER ports and timing arcs are defined. @@ -1043,7 +1030,7 @@ void LibertyReader::checkScaledCell(LibertyCell *scaled_cell, LibertyCell *owner, const LibertyGroup *scaled_cell_group, - const char *op_cond_name) + std::string_view op_cond_name) { if (equivCellPorts(scaled_cell, owner)) { if (!equivCellPorts(scaled_cell, owner)) @@ -1091,7 +1078,7 @@ LibertyReader::makePinPort(LibertyCell *cell, { for (const LibertyAttrValue *port_value : pin_group->params()) { const std::string &port_name = port_value->stringValue(); - LibertyPort *port = makePort(cell, port_name.c_str()); + LibertyPort *port = makePort(cell, port_name); port_group_map[pin_group].push_back(port); } } @@ -1105,15 +1092,15 @@ LibertyReader::makeBusPort(LibertyCell *cell, const std::string &port_name = port_value->stringValue(); const LibertySimpleAttr *bus_type_attr = bus_group->findSimpleAttr("bus_type"); if (bus_type_attr) { - const std::string *bus_type = bus_type_attr->stringValue(); - if (bus_type) { + const std::string &bus_type = bus_type_attr->stringValue(); + if (!bus_type.empty()) { // Look for bus dcl local to cell first. - BusDcl *bus_dcl = cell->findBusDcl(bus_type->c_str()); + BusDcl *bus_dcl = cell->findBusDcl(bus_type); if (bus_dcl == nullptr) - bus_dcl = library_->findBusDcl(bus_type->c_str()); + bus_dcl = library_->findBusDcl(bus_type); if (bus_dcl) { debugPrint(debug_, "liberty", 1, " bus {}", port_name); - LibertyPort *bus_port = makeBusPort(cell, port_name.c_str(), + LibertyPort *bus_port = makeBusPort(cell, port_name, bus_dcl->from(), bus_dcl->to(), bus_dcl); port_group_map[bus_group].push_back(bus_port); @@ -1121,7 +1108,7 @@ LibertyReader::makeBusPort(LibertyCell *cell, makeBusPinPorts(cell, bus_group, port_group_map); } else - warn(1235, bus_type_attr, "bus_type {} not found.", *bus_type); + warn(1235, bus_type_attr, "bus_type {} not found.", bus_type); } } else @@ -1140,7 +1127,7 @@ LibertyReader::makeBusPinPorts(LibertyCell *cell, const std::string pin_name = param->stringValue(); 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()); + PortNameBitIterator name_iter(cell, pin_name, this, pin_group->line()); while (name_iter.hasNext()) { LibertyPort *pin_port = name_iter.next(); if (pin_port) { @@ -1161,25 +1148,29 @@ LibertyReader::makeBundlePort(LibertyCell *cell, const LibertyGroup *bundle_group, LibertyPortGroupMap &port_group_map) { - const std::string &bundle_name = bundle_group->firstName(); - debugPrint(debug_, "liberty", 1, " bundle {}", bundle_name); + if (bundle_group->hasFirstParam()) { + const std::string &bundle_name = bundle_group->firstParam(); + debugPrint(debug_, "liberty", 1, " bundle {}", bundle_name); - const LibertyComplexAttr *member_attr = bundle_group->findComplexAttr("members"); - ConcretePortSeq *members = new ConcretePortSeq; - for (const LibertyAttrValue *member_value : member_attr->values()) { - if (member_value->isString()) { - const char *member_name = member_value->stringValue().c_str(); - LibertyPort *member = cell->findLibertyPort(member_name); - if (member == nullptr) - member = makePort(cell, member_name); - members->push_back(member); + const LibertyComplexAttr *member_attr = bundle_group->findComplexAttr("members"); + ConcretePortSeq *members = new ConcretePortSeq; + for (const LibertyAttrValue *member_value : member_attr->values()) { + if (member_value->isString()) { + const std::string &member_name = member_value->stringValue(); + LibertyPort *member = cell->findLibertyPort(member_name); + if (member == nullptr) + member = makePort(cell, member_name); + members->push_back(member); + } } + LibertyPort *bundle_port = builder_.makeBundlePort(cell, bundle_name.c_str(), + members); + port_group_map[bundle_group].push_back(bundle_port); + // Make ports for pin groups inside the bundle group. + makeBundlePinPorts(cell, bundle_group, port_group_map); } - LibertyPort *bundle_port = builder_.makeBundlePort(cell, bundle_name.c_str(), - members); - port_group_map[bundle_group].push_back(bundle_port); - // Make ports for pin groups inside the bundle group. - makeBundlePinPorts(cell, bundle_group, port_group_map); + else + warn(1313, bundle_group, "bundle missing name."); } void @@ -1192,9 +1183,9 @@ LibertyReader::makeBundlePinPorts(LibertyCell *cell, if (param->isString()) { const std::string pin_name = param->stringValue(); debugPrint(debug_, "liberty", 1, " bundle pin port {}", pin_name); - LibertyPort *pin_port = cell->findLibertyPort(pin_name.c_str()); + LibertyPort *pin_port = cell->findLibertyPort(pin_name); if (pin_port == nullptr) - pin_port = makePort(cell, pin_name.c_str()); + pin_port = makePort(cell, pin_name); port_group_map[pin_group].push_back(pin_port); } else @@ -1207,37 +1198,41 @@ void LibertyReader::makePgPinPort(LibertyCell *cell, const LibertyGroup *pg_pin_group) { - const std::string &port_name = pg_pin_group->firstName(); - LibertyPort *pg_port = makePort(cell, port_name.c_str()); - - const std::string *type_name = pg_pin_group->findAttrString("pg_type"); - if (type_name) { - PwrGndType type = findPwrGndType(type_name->c_str()); - PortDirection *dir = PortDirection::unknown(); - switch (type) { - case PwrGndType::primary_ground: - case PwrGndType::backup_ground: - case PwrGndType::internal_ground: - dir = PortDirection::ground(); - break; - case PwrGndType::primary_power: - case PwrGndType::backup_power: - case PwrGndType::internal_power: - dir = PortDirection::power(); - break; - case PwrGndType::none: - error(1291, pg_pin_group, "unknown pg_type."); - break; - default: - break; + if (pg_pin_group->hasFirstParam()) { + const std::string &port_name = pg_pin_group->firstParam(); + LibertyPort *pg_port = makePort(cell, port_name); + + const std::string &type_name = pg_pin_group->findAttrString("pg_type"); + if (!type_name.empty()) { + PwrGndType type = findPwrGndType(type_name.c_str()); + PortDirection *dir = PortDirection::unknown(); + switch (type) { + case PwrGndType::primary_ground: + case PwrGndType::backup_ground: + case PwrGndType::internal_ground: + dir = PortDirection::ground(); + break; + case PwrGndType::primary_power: + case PwrGndType::backup_power: + case PwrGndType::internal_power: + dir = PortDirection::power(); + break; + case PwrGndType::none: + error(1291, pg_pin_group, "unknown pg_type."); + break; + default: + break; + } + pg_port->setPwrGndType(type); + pg_port->setDirection(dir); } - pg_port->setPwrGndType(type); - pg_port->setDirection(dir); - } - const std::string *voltate_name = pg_pin_group->findAttrString("voltage_name"); - if (voltate_name) - pg_port->setVoltageName(voltate_name->c_str()); + const std::string &voltate_name = pg_pin_group->findAttrString("voltage_name"); + if (!voltate_name.empty()) + pg_port->setVoltageName(voltate_name.c_str()); + } + else + warn(1314, pg_pin_group, "pg_pin missing name."); } //////////////////////////////////////////////////////////////// @@ -1275,10 +1270,12 @@ LibertyReader::readPortAttributes(LibertyCell *cell, readPortAttrBool("level_shifter_data_pin", &LibertyPort::setLevelShifterData, ports, port_group); readPortAttrBool("switch_pin", &LibertyPort::setIsSwitch, ports, port_group); - readPortAttrString("related_ground_pin", &LibertyPort::setRelatedGroundPin, - ports, port_group); - readPortAttrString("related_power_pin", &LibertyPort::setRelatedPowerPin, - ports, port_group); + readPortAttrLibertyPort("related_ground_pin", + &LibertyPort::setRelatedGroundPort, + cell, ports, port_group); + readPortAttrLibertyPort("related_power_pin", + &LibertyPort::setRelatedPowerPort, + cell, ports, port_group); readDriverWaveform(ports, port_group); } @@ -1287,11 +1284,12 @@ LibertyReader::readDriverWaveform(const LibertyPortSeq &ports, const LibertyGroup *port_group) { for (const RiseFall *rf : RiseFall::range()) { - const char *attr_name = rf == RiseFall::rise() - ? "driver_waveform_rise" : "driver_waveform_fall"; - const std::string *name = port_group->findAttrString(attr_name); - if (name) { - DriverWaveform *waveform = library_->findDriverWaveform(name->c_str()); + const std::string_view attr_name = (rf == RiseFall::rise()) + ? "driver_waveform_rise" + : "driver_waveform_fall"; + const std::string &name = port_group->findAttrString(attr_name); + if (!name.empty()) { + DriverWaveform *waveform = library_->findDriverWaveform(name.c_str()); if (waveform) { for (LibertyPort *port : ports) port->setDriverWaveform(waveform, rf); @@ -1301,20 +1299,35 @@ LibertyReader::readDriverWaveform(const LibertyPortSeq &ports, } void -LibertyReader::readPortAttrString(const char *attr_name, - void (LibertyPort::*set_func)(const char *value), +LibertyReader::readPortAttrString(std::string_view attr_name, + void (LibertyPort::*set_func)(std::string value), const LibertyPortSeq &ports, const LibertyGroup *group) { - const std::string *value = group->findAttrString(attr_name); - if (value) { + const std::string &value = group->findAttrString(attr_name); + if (!value.empty()) { for (LibertyPort *port : ports) - (port->*set_func)(value->c_str()); + (port->*set_func)(value); } } void -LibertyReader::readPortAttrFloat(const char *attr_name, +LibertyReader::readPortAttrLibertyPort(std::string_view attr_name, + void (LibertyPort::*set_func)(LibertyPort *port), + LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *group) +{ + const std::string &value = group->findAttrString(attr_name); + if (!value.empty()) { + LibertyPort *related = cell->findLibertyPort(value); + for (LibertyPort *port : ports) + (port->*set_func)(related); + } +} + +void +LibertyReader::readPortAttrFloat(std::string_view attr_name, void (LibertyPort::*set_func)(float value), const LibertyPortSeq &ports, const LibertyGroup *group, @@ -1330,7 +1343,7 @@ LibertyReader::readPortAttrFloat(const char *attr_name, } void -LibertyReader::readPortAttrBool(const char *attr_name, +LibertyReader::readPortAttrBool(std::string_view attr_name, void (LibertyPort::*set_func)(bool value), const LibertyPortSeq &ports, const LibertyGroup *group) @@ -1340,11 +1353,11 @@ LibertyReader::readPortAttrBool(const char *attr_name, const LibertyAttrValue &attr_value = attr->value(); if (attr_value.isString()) { const std::string &value = attr_value.stringValue(); - if (stringEqual(value.c_str(), "true")) { + if (stringEqual(value, "true")) { for (LibertyPort *port : ports) (port->*set_func)(true); } - else if (stringEqual(value.c_str(), "false")) { + else if (stringEqual(value, "false")) { for (LibertyPort *port : ports) (port->*set_func)(false); } @@ -1357,7 +1370,7 @@ LibertyReader::readPortAttrBool(const char *attr_name, } void -LibertyReader::readPortAttrFloatMinMax(const char *attr_name, +LibertyReader::readPortAttrFloatMinMax(std::string_view attr_name, void (LibertyPort::*set_func)(float value, const MinMax *min_max), const LibertyPortSeq &ports, @@ -1378,23 +1391,23 @@ void LibertyReader::readPulseClock(const LibertyPortSeq &ports, const LibertyGroup *port_group) { - const std::string *pulse_clk = port_group->findAttrString("pulse_clock"); - if (pulse_clk) { + const std::string &pulse_clk = port_group->findAttrString("pulse_clock"); + if (!pulse_clk.empty()) { const RiseFall *trigger = nullptr; const RiseFall *sense = nullptr; - if (*pulse_clk == "rise_triggered_high_pulse") { + if (pulse_clk == "rise_triggered_high_pulse") { trigger = RiseFall::rise(); sense = RiseFall::rise(); } - else if (*pulse_clk == "rise_triggered_low_pulse") { + else if (pulse_clk == "rise_triggered_low_pulse") { trigger = RiseFall::rise(); sense = RiseFall::fall(); } - else if (*pulse_clk == "fall_triggered_high_pulse") { + else if (pulse_clk == "fall_triggered_high_pulse") { trigger = RiseFall::fall(); sense = RiseFall::rise(); } - else if (*pulse_clk == "fall_triggered_low_pulse") { + else if (pulse_clk == "fall_triggered_low_pulse") { trigger = RiseFall::fall(); sense = RiseFall::fall(); } @@ -1414,30 +1427,30 @@ LibertyReader::readSignalType(LibertyCell *cell, { if (!dynamic_cast(cell)) return; - const std::string *type = port_group->findAttrString("signal_type"); - if (!type) + const std::string &type = port_group->findAttrString("signal_type"); + if (type.empty()) return; ScanSignalType signal_type = ScanSignalType::none; - if (*type == "test_scan_enable") + if (type == "test_scan_enable") signal_type = ScanSignalType::enable; - else if (*type == "test_scan_enable_inverted") + else if (type == "test_scan_enable_inverted") signal_type = ScanSignalType::enable_inverted; - else if (*type == "test_scan_clock") + else if (type == "test_scan_clock") signal_type = ScanSignalType::clock; - else if (*type == "test_scan_clock_a") + else if (type == "test_scan_clock_a") signal_type = ScanSignalType::clock_a; - else if (*type == "test_scan_clock_b") + else if (type == "test_scan_clock_b") signal_type = ScanSignalType::clock_b; - else if (*type == "test_scan_in") + else if (type == "test_scan_in") signal_type = ScanSignalType::input; - else if (*type == "test_scan_in_inverted") + else if (type == "test_scan_in_inverted") signal_type = ScanSignalType::input_inverted; - else if (*type == "test_scan_out") + else if (type == "test_scan_out") signal_type = ScanSignalType::output; - else if (*type == "test_scan_out_inverted") + else if (type == "test_scan_out_inverted") signal_type = ScanSignalType::output_inverted; else { - warn(1299, port_group, "unknown signal_type {}.", *type); + warn(1299, port_group, "unknown signal_type {}.", type); return; } for (LibertyPort *port : ports) @@ -1452,16 +1465,16 @@ LibertyReader::readPortDir(const LibertyPortSeq &ports, // Note missing direction attribute is not an error because a bus group // can have pin groups for the bus bits that have direcitons. if (dir_attr) { - const std::string *dir = dir_attr->stringValue(); - if (dir) { + const std::string &dir = dir_attr->stringValue(); + if (!dir.empty()) { PortDirection *port_dir = PortDirection::unknown(); - if (*dir == "input") + if (dir == "input") port_dir = PortDirection::input(); - else if (*dir == "output") + else if (dir == "output") port_dir = PortDirection::output(); - else if (*dir == "inout") + else if (dir == "inout") port_dir = PortDirection::bidirect(); - else if (*dir == "internal") + else if (dir == "internal") port_dir = PortDirection::internal(); else warn(1240, dir_attr, "unknown port direction."); @@ -1482,7 +1495,7 @@ LibertyReader::readCapacitance(const LibertyPortSeq &ports, for (LibertyPort *port : ports) { // rise/fall_capacitance for (const RiseFall *rf : RiseFall::range()) { - std::string attr_name = rf->to_string() + "_capacitance"; + std::string attr_name = sta::format("{}_capacitance", rf->to_string()); float cap; bool exists; port_group->findAttrFloat(attr_name, cap, exists); @@ -1492,16 +1505,18 @@ LibertyReader::readCapacitance(const LibertyPortSeq &ports, } // rise/fall_capacitance_range(min_cap, max_cap); - attr_name = rf->to_string() + "_capacitance_range"; + attr_name = sta::format("{}_capacitance_range", rf->to_string()); const LibertyComplexAttrSeq &range_attrs = port_group->findComplexAttrs(attr_name); if (!range_attrs.empty()) { const LibertyComplexAttr *attr = range_attrs[0]; const LibertyAttrValueSeq &values = attr->values(); if (values.size() == 2) { - float cap_min = values[0]->floatValue(); - float cap_max = values[1]->floatValue(); - port->setCapacitance(rf, MinMax::min(), cap_min * cap_scale_); - port->setCapacitance(rf, MinMax::max(), cap_max * cap_scale_); + auto [cap_min, min_valid] = values[0]->floatValue(); + if (min_valid) + port->setCapacitance(rf, MinMax::min(), cap_min * cap_scale_); + auto [cap_max, max_valid] = values[1]->floatValue(); + if (max_valid) + port->setCapacitance(rf, MinMax::max(), cap_max * cap_scale_); } } } @@ -1510,7 +1525,7 @@ LibertyReader::readCapacitance(const LibertyPortSeq &ports, for (const MinMax *min_max : MinMax::range()) { // min/max_capacitance - std::string attr_name = min_max->to_string() + "_capacitance"; + std::string attr_name = sta::format("{}_capacitance", min_max->to_string()); float limit; bool exists; port_group->findAttrFloat(attr_name, limit, exists); @@ -1518,7 +1533,7 @@ LibertyReader::readCapacitance(const LibertyPortSeq &ports, port->setCapacitanceLimit(limit * cap_scale_, min_max); // min/max_transition - attr_name = min_max->to_string() + "_transition"; + attr_name = sta::format("{}_transition", min_max->to_string()); port_group->findAttrFloat(attr_name, limit, exists); if (exists) { if (min_max == MinMax::max() && limit == 0.0) @@ -1564,7 +1579,7 @@ LibertyReader::readMinPulseWidth(LibertyCell *cell, for (LibertyPort *port : ports) { TimingArcAttrsPtr timing_attrs = nullptr; for (const RiseFall *rf : RiseFall::range()) { - const char *mpw_attr_name = rf == RiseFall::rise() + const std::string mpw_attr_name = rf == RiseFall::rise() ? "min_pulse_width_high" : "min_pulse_width_low"; float mpw; @@ -1598,9 +1613,9 @@ LibertyReader::makePortFuncs(LibertyCell *cell, { const LibertySimpleAttr *func_attr = port_group->findSimpleAttr("function"); if (func_attr) { - const std::string *func = func_attr->stringValue(); - if (func) { - FuncExpr *func_expr = parseFunc(func->c_str(), "function", cell, func_attr->line()); + const std::string &func = func_attr->stringValue(); + if (!func.empty()) { + FuncExpr *func_expr = parseFunc(func, "function", cell, func_attr->line()); for (LibertyPort *port : ports) { port->setFunction(func_expr); if (func_expr->checkSize(port)) { @@ -1614,9 +1629,9 @@ LibertyReader::makePortFuncs(LibertyCell *cell, const LibertySimpleAttr *tri_attr = port_group->findSimpleAttr("three_state"); if (tri_attr) { - const std::string *tri_disable = tri_attr->stringValue(); - if (tri_disable) { - FuncExpr *tri_disable_expr = parseFunc(tri_disable->c_str(), + const std::string tri_disable = tri_attr->stringValue(); + if (!tri_disable.empty()) { + FuncExpr *tri_disable_expr = parseFunc(tri_disable, "three_state", cell, tri_attr->line()); FuncExpr *tri_enable_expr = tri_disable_expr->invert(); @@ -1653,11 +1668,12 @@ void LibertyReader::makeSequentials(LibertyCell *cell, const LibertyGroup *cell_group, bool is_register, - const char *seq_group_name, - const char *clk_attr_name, - const char *data_attr_name) + std::string_view seq_group_name, + std::string_view clk_attr_name, + std::string_view data_attr_name) { - for (const LibertyGroup *seq_group : cell_group->findSubgroups(seq_group_name)) { + for (const LibertyGroup *seq_group : + cell_group->findSubgroups(seq_group_name)) { LibertyPort *out_port = nullptr; LibertyPort *out_port_inv = nullptr; size_t size; @@ -1686,13 +1702,13 @@ LibertyReader::makeSequentials(LibertyCell *cell, FuncExpr * LibertyReader::makeSeqFunc(LibertyCell *cell, const LibertyGroup *seq_group, - const char *attr_name, + std::string_view attr_name, int size) { FuncExpr *expr = nullptr; - const std::string *attr = seq_group->findAttrString(attr_name); - if (attr) { - expr = parseFunc(attr->c_str(), attr_name, cell, seq_group->line()); + const std::string &attr = seq_group->findAttrString(attr_name); + if (!attr.empty()) { + expr = parseFunc(attr, attr_name, cell, seq_group->line()); if (expr && expr->checkSize(size)) { warn(1196, seq_group, "{} {} bus width mismatch.", seq_group->type(), attr_name); @@ -1711,17 +1727,17 @@ LibertyReader::makeSeqPorts(LibertyCell *cell, LibertyPort *&out_port_inv, size_t &size) { - const char *out_name, *out_inv_name; + std::string out_name, out_inv_name; bool has_size; seqPortNames(seq_group, out_name, out_inv_name, has_size, size); - if (out_name) { + if (!out_name.empty()) { if (has_size) out_port = makeBusPort(cell, out_name, size - 1, 0, nullptr); else out_port = makePort(cell, out_name); out_port->setDirection(PortDirection::internal()); } - if (out_inv_name) { + if (!out_inv_name.empty()) { if (has_size) out_port_inv = makeBusPort(cell, out_inv_name, size - 1, 0, nullptr); else @@ -1732,39 +1748,39 @@ LibertyReader::makeSeqPorts(LibertyCell *cell, void LibertyReader::seqPortNames(const LibertyGroup *group, - const char *&out_name, - const char *&out_inv_name, + // Return values. + std::string &out_name, + std::string &out_inv_name, bool &has_size, size_t &size) { - out_name = nullptr; - out_inv_name = nullptr; if (group->params().size() == 1) { // out_port - out_name = group->firstName(); + out_name = group->firstParam(); size = 1; has_size = false; } if (group->params().size() == 2) { // out_port, out_port_inv - out_name = group->firstName(); - out_inv_name = group->secondName(); + out_name = group->firstParam(); + out_inv_name = group->secondParam(); size = 1; has_size = false; } else if (group->params().size() == 3) { LibertyAttrValue *third_value = group->params()[2]; - if (third_value->isFloat()) { + auto [size_flt, size_valid] = third_value->floatValue(); + if (size_valid) { // out_port, out_port_inv, bus_size - out_name = group->firstName(); - out_inv_name = group->secondName(); - size = static_cast(third_value->floatValue()); + out_name = group->firstParam(); + out_inv_name = group->secondParam(); + size = static_cast(size_flt); has_size = true; } else { // in_port (ignored), out_port, out_port_inv - out_name = group->secondName(); - out_inv_name = third_value->stringValue().c_str(); + out_name = group->secondParam(); + out_inv_name = third_value->stringValue(); has_size = true; size = 1; } @@ -1805,12 +1821,12 @@ LibertyReader::readCellAttributes(LibertyCell *cell, readGroupAttrFloat("ocv_arc_depth", cell_group, [cell](float v) { cell->setOcvArcDepth(v); }); - const std::string *clock_gate_type = + const std::string &clock_gate_type = cell_group->findAttrString("clock_gating_integrated_cell"); - if (clock_gate_type) { - if (stringBeginEqual(clock_gate_type->c_str(), "latch_posedge")) + if (!clock_gate_type.empty()) { + if (stringBeginEqual(clock_gate_type.c_str(), "latch_posedge")) cell->setClockGateType(ClockGateType::latch_posedge); - else if (stringBeginEqual(clock_gate_type->c_str(), "latch_negedge")) + else if (stringBeginEqual(clock_gate_type.c_str(), "latch_negedge")) cell->setClockGateType(ClockGateType::latch_negedge); else cell->setClockGateType(ClockGateType::other); @@ -1826,29 +1842,31 @@ void LibertyReader::readScaleFactors(LibertyCell *cell, const LibertyGroup *cell_group) { - const std::string *scale_factors_name = cell_group->findAttrString("scaling_factors"); - if (scale_factors_name) { - ScaleFactors *scale_factors = library_->findScaleFactors(scale_factors_name->c_str()); + const std::string &scale_factors_name = + cell_group->findAttrString("scaling_factors"); + if (!scale_factors_name.empty()) { + ScaleFactors *scale_factors = + library_->findScaleFactors(scale_factors_name.c_str()); if (scale_factors) cell->setScaleFactors(scale_factors); else - warn(1230, cell_group, "scaling_factors {} not found.", *scale_factors_name); + warn(1230, cell_group, "scaling_factors {} not found.", scale_factors_name); } } void -LibertyReader::readCellAttrString(const char *attr_name, - void (LibertyCell::*set_func)(const char *value), +LibertyReader::readCellAttrString(std::string_view attr_name, + void (LibertyCell::*set_func)(std::string value), LibertyCell *cell, const LibertyGroup *group) { - const std::string *value = group->findAttrString(attr_name); - if (value) - (cell->*set_func)(value->c_str()); + const std::string &value = group->findAttrString(attr_name); + if (!value.empty()) + (cell->*set_func)(value); } void -LibertyReader::readCellAttrFloat(const char *attr_name, +LibertyReader::readCellAttrFloat(std::string_view attr_name, void (LibertyCell::*set_func)(float value), LibertyCell *cell, const LibertyGroup *group, @@ -1862,7 +1880,7 @@ LibertyReader::readCellAttrFloat(const char *attr_name, } void -LibertyReader::readCellAttrBool(const char *attr_name, +LibertyReader::readCellAttrBool(std::string_view attr_name, void (LibertyCell::*set_func)(bool value), LibertyCell *cell, const LibertyGroup *group) @@ -1872,9 +1890,9 @@ LibertyReader::readCellAttrBool(const char *attr_name, const LibertyAttrValue &attr_value = attr->value(); if (attr_value.isString()) { const std::string &value = attr_value.stringValue(); - if (stringEqual(value.c_str(), "true")) + if (stringEqual(value, "true")) (cell->*set_func)(true); - else if (stringEqual(value.c_str(), "false")) + else if (stringEqual(value, "false")) (cell->*set_func)(false); else warn(1279, attr, "{} attribute is not boolean.", attr_name); @@ -1947,7 +1965,7 @@ LibertyReader::readTimingArcAttrs(LibertyCell *cell, } void -LibertyReader::readGroupAttrFloat(const char *attr_name, +LibertyReader::readGroupAttrFloat(std::string_view attr_name, const LibertyGroup *group, const std::function &set_func, float scale) @@ -1965,17 +1983,15 @@ LibertyReader::readTimingSense(const LibertyGroup *timing_group, { const LibertySimpleAttr *sense_attr = timing_group->findSimpleAttr("timing_sense"); if (sense_attr) { - const std::string *sense_name = sense_attr->stringValue(); - if (sense_name) { - if (*sense_name == "non_unate") - timing_attrs->setTimingSense(TimingSense::non_unate); - else if (*sense_name == "positive_unate") - timing_attrs->setTimingSense(TimingSense::positive_unate); - else if (*sense_name == "negative_unate") - timing_attrs->setTimingSense(TimingSense::negative_unate); - else - warn(1245, timing_group, "unknown timing_sense {}.", *sense_name); - } + const std::string &sense_name = sense_attr->stringValue(); + if (sense_name == "non_unate") + timing_attrs->setTimingSense(TimingSense::non_unate); + else if (sense_name == "positive_unate") + timing_attrs->setTimingSense(TimingSense::positive_unate); + else if (sense_name == "negative_unate") + timing_attrs->setTimingSense(TimingSense::negative_unate); + else + warn(1245, timing_group, "unknown timing_sense {}.", sense_name); } } @@ -1986,13 +2002,11 @@ LibertyReader::readTimingType(const LibertyGroup *timing_group, TimingType type = TimingType::combinational; const LibertySimpleAttr *type_attr = timing_group->findSimpleAttr("timing_type"); if (type_attr) { - const std::string *type_name = type_attr->stringValue(); - if (type_name) { - type = findTimingType(type_name->c_str()); - if (type == TimingType::unknown) { - warn(1244, type_attr, "unknown timing_type {}.", *type_name); - type = TimingType::combinational; - } + const std::string type_name = type_attr->stringValue(); + type = findTimingType(type_name); + if (type == TimingType::unknown) { + warn(1244, type_attr, "unknown timing_type {}.", type_name); + type = TimingType::combinational; } } timing_attrs->setTimingType(type); @@ -2005,30 +2019,27 @@ LibertyReader::readTimingWhen(const LibertyCell *cell, { const LibertySimpleAttr *when_attr = timing_group->findSimpleAttr("when"); if (when_attr) { - const std::string *when = when_attr->stringValue(); - if (when) { - FuncExpr *when_expr = parseFunc(when->c_str(), "when", cell, when_attr->line()); + const std::string &when = when_attr->stringValue(); + if (!when.empty()) { + FuncExpr *when_expr = parseFunc(when, "when", cell, when_attr->line()); timing_attrs->setCond(when_expr); } } const LibertySimpleAttr *cond_attr = timing_group->findSimpleAttr("sdf_cond"); if (cond_attr) { - const std::string *cond = cond_attr->stringValue(); - if (cond) - timing_attrs->setSdfCond(cond->c_str()); + const std::string &cond = cond_attr->stringValue(); + timing_attrs->setSdfCond(cond); } cond_attr = timing_group->findSimpleAttr("sdf_cond_start"); if (cond_attr) { - const std::string *cond = cond_attr->stringValue(); - if (cond) - timing_attrs->setSdfCondStart(cond->c_str()); + const std::string &cond = cond_attr->stringValue(); + timing_attrs->setSdfCondStart(cond); } cond_attr = timing_group->findSimpleAttr("sdf_cond_end"); if (cond_attr) { - const std::string *cond = cond_attr->stringValue(); - if (cond) - timing_attrs->setSdfCondEnd(cond->c_str()); + const std::string &cond = cond_attr->stringValue(); + timing_attrs->setSdfCondEnd(cond); } } @@ -2085,7 +2096,7 @@ LibertyReader::makeLinearModels(LibertyCell *cell, { LibertyLibrary *library = cell->libertyLibrary(); for (const RiseFall *rf : RiseFall::range()) { - std::string intr_attr_name = "intrinsic_" + rf->to_string(); + std::string intr_attr_name = sta::format("intrinsic_{}", rf->to_string()); float intr = 0.0; bool intr_exists; timing_group->findAttrFloat(intr_attr_name, intr, intr_exists); @@ -2098,7 +2109,7 @@ LibertyReader::makeLinearModels(LibertyCell *cell, if (timingTypeIsCheck(timing_attrs->timingType())) model = new CheckLinearModel(cell, intr); else { - std::string res_attr_name = rf->to_string() + "_resistance"; + std::string res_attr_name = sta::format("{}_resistance", rf->to_string()); float res = 0.0; bool res_exists; timing_group->findAttrFloat(res_attr_name, res, res_exists); @@ -2122,13 +2133,14 @@ LibertyReader::makeTableModels(LibertyCell *cell, bool found_model = false; for (const RiseFall *rf : RiseFall::range()) { TableModel *delay_model = readTableModel(timing_group, - "cell_" + rf->to_string(), + sta::format("cell_{}", rf->to_string()), rf, TableTemplateType::delay, time_scale_, ScaleFactorType::cell, GateTableModel::checkAxes); TableModel *slew_model = readTableModel(timing_group, - rf->to_string() + "_transition", + sta::format("{}_transition", + rf->to_string()), rf, TableTemplateType::delay, time_scale_, ScaleFactorType::transition, @@ -2136,18 +2148,18 @@ LibertyReader::makeTableModels(LibertyCell *cell, 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(), + sta::format("ocv_sigma_cell_{}", rf->to_string()), + sta::format("ocv_std_dev_cell_{}", rf->to_string()), + sta::format("ocv_mean_shift_cell_{}", rf->to_string()), + sta::format("ocv_skewness_cell_{}", rf->to_string()), rf, delay_models, GateTableModel::checkAxes); 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", + sta::format("ocv_sigma_{}_transition", rf->to_string()), + sta::format("ocv_std_dev_{}_transition", rf->to_string()), + sta::format("ocv_mean_shift_{}_transition", rf->to_string()), + sta::format("ocv_skewness_{}_transition", rf->to_string()), rf, slew_models, GateTableModel::checkAxes); ReceiverModelPtr receiver_model = readReceiverCapacitance(timing_group, rf); @@ -2167,21 +2179,21 @@ LibertyReader::makeTableModels(LibertyCell *cell, found_model = true; } - std::string constraint_attr_name = rf->to_string() + "_constraint"; + std::string constraint_attr_name = sta::format("{}_constraint", rf->to_string()); ScaleFactorType scale_factor_type = timingTypeScaleFactorType(timing_attrs->timingType()); TableModel *check_model = readTableModel(timing_group, - constraint_attr_name.c_str(), + constraint_attr_name, 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", + sta::format("ocv_sigma_{}_constraiint", rf->to_string()), + sta::format("ocv_std_dev_{}_constraiint", rf->to_string()), + sta::format("ocv_mean_shift_{}_constraiint", rf->to_string()), + sta::format("ocv_skewness_{}_constraiint", rf->to_string()), rf, check_models, CheckTableModel::checkAxes); timing_attrs->setModel(rf, new CheckTableModel(cell, check_models)); found_model = true; @@ -2237,7 +2249,7 @@ LibertyReader::readLvfModels(const LibertyGroup *timing_group, { TableModelsEarlyLate sigmas = readEarlyLateTableModels(timing_group, - sigma_group_name.c_str(), + sigma_group_name, rf, TableTemplateType::delay, time_scale_, ScaleFactorType::unknown, @@ -2274,7 +2286,7 @@ LibertyReader::readLvfModels(const LibertyGroup *timing_group, TableModelsEarlyLate LibertyReader::readEarlyLateTableModels(const LibertyGroup *timing_group, - const char *table_group_name, + std::string_view table_group_name, const RiseFall *rf, TableTemplateType template_type, float scale, @@ -2282,18 +2294,18 @@ LibertyReader::readEarlyLateTableModels(const LibertyGroup *timing_group, const std::function check_axes) { TableModelsEarlyLate models{}; - for (const LibertyGroup *table_group : timing_group->findSubgroups(table_group_name)) { + for (const LibertyGroup *table_group : timing_group->findSubgroups(table_group_name)){ TableModel *model = readTableModel(table_group, rf, template_type, scale, scale_factor_type, check_axes); - const std::string *early_late = table_group->findAttrString("sigma_type"); - if (early_late == nullptr - || *early_late == "early_and_late") { + const std::string &early_late = table_group->findAttrString("sigma_type"); + if (early_late.empty() + || early_late == "early_and_late") { models[EarlyLate::early()->index()] = model; models[EarlyLate::late()->index()] = model; } - else if (*early_late == "early") + else if (early_late == "early") models[EarlyLate::early()->index()] = model; - else if (*early_late == "late") + else if (early_late == "late") models[EarlyLate::late()->index()] = model; } return models; @@ -2315,13 +2327,12 @@ LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, void LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group, - const char *cap_group_name, + std::string_view cap_group_name, int index, const RiseFall *rf, ReceiverModelPtr &receiver_model) { - std::string cap_group_name1 = cap_group_name; - cap_group_name1 += "_" + rf->to_string(); + std::string cap_group_name1 = sta::format("{}_{}", cap_group_name, rf->to_string()); const LibertyGroup *cap_group = timing_group->findSubgroup(cap_group_name1); if (cap_group) { const LibertySimpleAttr *segment_attr = cap_group->findSimpleAttr("segment"); @@ -2351,7 +2362,7 @@ OutputWaveforms * LibertyReader::readOutputWaveforms(const LibertyGroup *timing_group, const RiseFall *rf) { - const std::string current_group_name = "output_current_" + rf->to_string(); + const std::string current_group_name=sta::format("output_current_{}",rf->to_string()); const LibertyGroup *current_group = timing_group->findSubgroup(current_group_name); if (current_group) { OutputWaveformSeq output_currents; @@ -2466,8 +2477,8 @@ LibertyReader::readTableModel(const LibertyGroup *table_group, ScaleFactorType scale_factor_type, const std::function &check_axes) { - const char *template_name = table_group->firstName(); - if (library_ && template_name) { + if (library_ && table_group->hasFirstParam()) { + const std::string &template_name = table_group->firstParam(); TableTemplate *tbl_template = library_->findTableTemplate(template_name, template_type); if (tbl_template) { @@ -2527,7 +2538,7 @@ LibertyReader::readTableModel(const LibertyGroup *table_group, TableAxisPtr LibertyReader::makeTableAxis(const LibertyGroup *table_group, - const char *index_attr_name, + std::string_view index_attr_name, TableAxisPtr template_axis) { const LibertyComplexAttr *index_attr = table_group->findComplexAttr(index_attr_name); @@ -2566,7 +2577,7 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, TimingArcAttrsPtr timing_attrs, int timing_line) { - PortNameBitIterator from_port_iter(cell, from_port_name.c_str(), this, timing_line); + PortNameBitIterator from_port_iter(cell, from_port_name, this, timing_line); if (from_port_iter.size() == 1 && !to_port->hasMembers()) { // one -> one if (from_port_iter.hasNext()) { @@ -2611,7 +2622,7 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, if (from_size != to_size) warn(1216, timing_line, "timing port {} and related port {} are different sizes.", - from_port_name.c_str(), + from_port_name, to_port->name()); // align to/from iterators for one-to-one mapping while (from_size > to_size) { @@ -2707,7 +2718,7 @@ LibertyReader::readInternalPowerGroups(LibertyCell *cell, InternalPowerModels models{}; // rise/fall_power group for (const RiseFall *rf : RiseFall::range()) { - std::string pwr_attr_name = rf->to_string() + "_power"; + std::string pwr_attr_name = sta::format("{}_power", rf->to_string()); const LibertyGroup *pwr_group = ipwr_group->findSubgroup(pwr_attr_name); if (pwr_group) { TableModel *model = readTableModel(pwr_group, rf, TableTemplateType::power, @@ -2746,11 +2757,11 @@ LibertyReader::readInternalPowerGroups(LibertyCell *cell, FuncExpr * LibertyReader::readFuncExpr(LibertyCell *cell, const LibertyGroup *group, - const char *attr_name) + std::string_view attr_name) { - const std::string *attr = group->findAttrString(attr_name); - if (attr) - return parseFunc(attr->c_str(), attr_name, cell, group->line()); + const std::string &attr = group->findAttrString(attr_name); + if (!attr.empty()) + return parseFunc(attr, attr_name, cell, group->line()); else return nullptr; } @@ -2758,32 +2769,28 @@ LibertyReader::readFuncExpr(LibertyCell *cell, LibertyPort * LibertyReader::findLibertyPort(LibertyCell *cell, const LibertyGroup *group, - const char *port_name_attr) + std::string_view port_name_attr) { const LibertySimpleAttr *attr = group->findSimpleAttr(port_name_attr); if (attr) { - const std::string *port_name = attr->stringValue(); - if (port_name) { - LibertyPort *port = cell->findLibertyPort(port_name->c_str()); - if (port) - return port; - else - warn(1290, attr, "port {} not found.", *port_name); - } + const std::string &port_name = attr->stringValue(); + LibertyPort *port = cell->findLibertyPort(port_name); + if (port) + return port; + else + warn(1290, attr, "port {} not found.", port_name); } return nullptr; } StringSeq LibertyReader::findAttributStrings(const LibertyGroup *group, - const char *name_attr) + std::string_view name_attr) { const LibertySimpleAttr *attr = group->findSimpleAttr(name_attr); if (attr) { - const std::string *strings = attr->stringValue(); - if (strings) { - return parseTokens(*strings, ' '); - } + const std::string &strings = attr->stringValue(); + return parseTokens(strings); } return StringSeq(); } @@ -2791,12 +2798,12 @@ LibertyReader::findAttributStrings(const LibertyGroup *group, LibertyPortSeq LibertyReader::findLibertyPorts(LibertyCell *cell, const LibertyGroup *group, - const char *port_name_attr) + std::string_view port_name_attr) { LibertyPortSeq ports; StringSeq port_names = findAttributStrings(group, port_name_attr); for (const std::string &port_name : port_names) { - LibertyPort *port = findPort(cell, port_name.c_str()); + LibertyPort *port = findPort(cell, port_name); if (port) ports.push_back(port); else @@ -2855,8 +2862,8 @@ LibertyReader::readNormalizedDriverWaveform(const LibertyGroup *library_group) { for (const LibertyGroup *waveform_group : library_group->findSubgroups("normalized_driver_waveform")) { - const char *template_name = waveform_group->firstName(); - if (template_name) { + if (waveform_group->hasFirstParam()) { + const std::string &template_name = waveform_group->firstParam(); TableTemplate *tbl_template = library_->findTableTemplate(template_name, TableTemplateType::delay); if (!tbl_template) { @@ -2877,9 +2884,10 @@ LibertyReader::readNormalizedDriverWaveform(const LibertyGroup *library_group) continue; } std::string driver_waveform_name; - const std::string *name_attr = waveform_group->findAttrString("driver_waveform_name"); - if (name_attr) - driver_waveform_name = *name_attr; + const std::string &name_attr = + waveform_group->findAttrString("driver_waveform_name"); + if (!name_attr.empty()) + driver_waveform_name = name_attr; library_->makeDriverWaveform(driver_waveform_name, table); } else @@ -2893,13 +2901,14 @@ void LibertyReader::readLevelShifterType(LibertyCell *cell, const LibertyGroup *cell_group) { - const std::string *level_shifter_type = cell_group->findAttrString("level_shifter_type"); - if (level_shifter_type) { - if (*level_shifter_type == "HL") + const std::string &level_shifter_type = + cell_group->findAttrString("level_shifter_type"); + if (!level_shifter_type.empty()) { + if (level_shifter_type == "HL") cell->setLevelShifterType(LevelShifterType::HL); - else if (*level_shifter_type == "LH") + else if (level_shifter_type == "LH") cell->setLevelShifterType(LevelShifterType::LH); - else if (*level_shifter_type == "HL_LH") + else if (level_shifter_type == "HL_LH") cell->setLevelShifterType(LevelShifterType::HL_LH); else warn(1228, cell_group, "level_shifter_type must be HL, LH, or HL_LH"); @@ -2910,11 +2919,12 @@ void LibertyReader::readSwitchCellType(LibertyCell *cell, const LibertyGroup *cell_group) { - const std::string *switch_cell_type = cell_group->findAttrString("switch_cell_type"); - if (switch_cell_type) { - if (*switch_cell_type == "coarse_grain") + const std::string &switch_cell_type = + cell_group->findAttrString("switch_cell_type"); + if (!switch_cell_type.empty()) { + if (switch_cell_type == "coarse_grain") cell->setSwitchCellType(SwitchCellType::coarse_grain); - else if (*switch_cell_type == "fine_grain") + else if (switch_cell_type == "fine_grain") cell->setSwitchCellType(SwitchCellType::fine_grain); else warn(1229, cell_group, "switch_cell_type must be coarse_grain or fine_grain"); @@ -2925,16 +2935,16 @@ void LibertyReader::readCellOcvDerateGroup(LibertyCell *cell, const LibertyGroup *cell_group) { - const std::string *derate_name = cell_group->findAttrString("ocv_derate_group"); - if (derate_name) { - OcvDerate *derate = cell->findOcvDerate(derate_name->c_str()); + const std::string &derate_name = cell_group->findAttrString("ocv_derate_group"); + if (!derate_name.empty()) { + OcvDerate *derate = cell->findOcvDerate(derate_name.c_str()); if (derate == nullptr) - derate = library_->findOcvDerate(derate_name->c_str()); + derate = library_->findOcvDerate(derate_name.c_str()); if (derate) cell->setOcvDerate(derate); else warn(1237, cell_group, "OCV derate group named {} not found.", - *derate_name); + derate_name); } } @@ -2943,43 +2953,45 @@ LibertyReader::readStatetable(LibertyCell *cell, const LibertyGroup *cell_group) { for (const LibertyGroup *statetable_group : cell_group->findSubgroups("statetable")) { - const char *input_ports_arg = statetable_group->firstName(); - const char *internal_ports_arg = statetable_group->params().size() >= 2 - ? statetable_group->secondName() : nullptr; StringSeq input_ports; - if (input_ports_arg) - input_ports = parseTokens(input_ports_arg, ' '); + if (statetable_group->hasFirstParam()) { + const std::string &input_ports_arg = statetable_group->firstParam(); + input_ports = parseTokens(input_ports_arg); + } + StringSeq internal_ports; - if (internal_ports_arg) - internal_ports = parseTokens(internal_ports_arg, ' '); + if (statetable_group->hasSecondParam()) { + const std::string &internal_ports_arg = statetable_group->secondParam(); + internal_ports = parseTokens(internal_ports_arg); + } const LibertySimpleAttr *table_attr = statetable_group->findSimpleAttr("table"); if (table_attr) { - const std::string *table_str = table_attr->stringValue(); - StringSeq table_rows = parseTokens(table_str->c_str(), ','); + const std::string &table_str = table_attr->stringValue(); + StringSeq table_rows = parseTokens(table_str, ","); size_t input_count = input_ports.size(); size_t internal_count = internal_ports.size(); StatetableRows table; for (const std::string &row : table_rows) { - const StringSeq row_groups = parseTokens(row, ':'); + const StringSeq row_groups = parseTokens(row, ":"); if (row_groups.size() != 3) { warn(1300, table_attr, "table row must have 3 groups separated by ':'."); break; } - StringSeq inputs = parseTokens(row_groups[0], ' '); + StringSeq inputs = parseTokens(row_groups[0]); if (inputs.size() != input_count) { warn(1301, table_attr, "table row has {} input values but {} are required.", inputs.size(), input_count); break; } - StringSeq currents = parseTokens(row_groups[1], ' '); + StringSeq currents = parseTokens(row_groups[1]); if (currents.size() != internal_count) { warn(1302,table_attr, "table row has {} current values but {} are required.", currents.size(), internal_count); break; } - StringSeq nexts = parseTokens(row_groups[2], ' '); + StringSeq nexts = parseTokens(row_groups[2]); if (nexts.size() != internal_count) { warn(1303, table_attr, "table row has {} next values but {} are required.", nexts.size(), internal_count); @@ -2994,10 +3006,10 @@ LibertyReader::readStatetable(LibertyCell *cell, LibertyPortSeq input_port_ptrs; for (const std::string &input : input_ports) { - LibertyPort *port = cell->findLibertyPort(input.c_str()); + LibertyPort *port = cell->findLibertyPort(input); if (port == nullptr && cell->testCell()) - port = cell->testCell()->findLibertyPort(input.c_str()); + port = cell->testCell()->findLibertyPort(input); if (port) input_port_ptrs.push_back(port); else @@ -3006,9 +3018,9 @@ LibertyReader::readStatetable(LibertyCell *cell, } LibertyPortSeq internal_port_ptrs; for (const std::string &internal : internal_ports) { - LibertyPort *port = cell->findLibertyPort(internal.c_str()); + LibertyPort *port = cell->findLibertyPort(internal); if (port == nullptr) - port = makePort(cell, internal.c_str()); + port = makePort(cell, internal); internal_port_ptrs.push_back(port); } cell->makeStatetable(input_port_ptrs, internal_port_ptrs, table); @@ -3039,27 +3051,27 @@ LibertyReader::readTestCell(LibertyCell *cell, LibertyPort * LibertyReader::makePort(LibertyCell *cell, - const char *port_name) + std::string_view port_name) { std::string sta_name = portLibertyToSta(port_name); - return builder_.makePort(cell, sta_name.c_str()); + return builder_.makePort(cell, sta_name); } LibertyPort * LibertyReader::makeBusPort(LibertyCell *cell, - const char *bus_name, + std::string_view bus_name, int from_index, int to_index, BusDcl *bus_dcl) { std::string sta_name = portLibertyToSta(bus_name); - return builder_.makeBusPort(cell, bus_name, from_index, to_index, bus_dcl); + return builder_.makeBusPort(cell, sta_name, from_index, to_index, bus_dcl); } -// Also used by LibExprParser::makeFuncExprPort. +// Also used by LibExprReader::makeFuncExprPort. LibertyPort * libertyReaderFindPort(const LibertyCell *cell, - const char *port_name) + std::string_view port_name) { LibertyPort *port = cell->findLibertyPort(port_name); if (port == nullptr) { @@ -3069,14 +3081,14 @@ libertyReaderFindPort(const LibertyCell *cell, const char escape = '\\'; // Pins at top level with bus names have escaped brackets. std::string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right, escape); - port = cell->findLibertyPort(escaped_port_name.c_str()); + port = cell->findLibertyPort(escaped_port_name); } return port; } LibertyPort * LibertyReader::findPort(LibertyCell *cell, - const char *port_name) + std::string_view port_name) { return libertyReaderFindPort(cell, port_name); } @@ -3128,10 +3140,9 @@ LibertyReader::parseStateInputValues(StringSeq &inputs, for (std::string input : inputs) { bool exists; StateInputValue value; - state_input_value_name_map.find(input.c_str(), value, exists); + state_input_value_name_map.find(input, value, exists); if (!exists) { - warn(1304, attr, "table input value '{}' not recognized.", - input); + warn(1304, attr, "table input value '{}' not recognized.", input); value = StateInputValue::dont_care; } input_values.push_back(value); @@ -3147,10 +3158,9 @@ LibertyReader::parseStateInternalValues(StringSeq &states, for (std::string state : states) { bool exists; StateInternalValue value; - state_internal_value_name_map.find(state.c_str(), value, exists); + state_internal_value_name_map.find(state, value, exists); if (!exists) { - warn(1305, attr, "table internal value '{}' not recognized.", - state); + warn(1305, attr, "table internal value '{}' not recognized.", state); value = StateInternalValue::unknown; } state_values.push_back(value); @@ -3173,17 +3183,19 @@ LibertyReader::makeFloatTable(const LibertyComplexAttr *values_attr, FloatSeq row; row.reserve(cols); if (value->isString()) - row = parseStringFloatList(value->stringValue(), scale, values_attr); - else if (value->isFloat()) - row.push_back(value->floatValue() * scale); + row = parseFloatList(value->stringValue(), scale, values_attr->line()); + else if (value->isFloat()) { + auto [entry, valid] = value->floatValue(); + row.push_back(entry * scale); + } else warn(1258, values_attr, "{} is not a list of floats.", - values_attr->name()); + values_attr->name()); if (row.size() != cols) { warn(1259, values_attr, "{} row has {} columns but axis has {}.", - table_group->type(), - row.size(), - cols); + table_group->type(), + row.size(), + cols); for (size_t c = row.size(); c < cols; c++) row.push_back(0.0); } @@ -3192,12 +3204,12 @@ LibertyReader::makeFloatTable(const LibertyComplexAttr *values_attr, if (table.size() != rows) { if (rows == 0) warn(1260, values_attr, "{} missing axis values.", - table_group->type()); + table_group->type()); else warn(1261, values_attr, "{} has {} rows but axis has {}.", - table_group->type(), - table.size(), - rows); + table_group->type(), + table.size(), + rows); for (size_t r = table.size(); r < rows; r++) { FloatSeq row(cols, 0.0); table.push_back(std::move(row)); @@ -3218,7 +3230,7 @@ LibertyReader::getAttrInt(const LibertySimpleAttr *attr, exists = false; const LibertyAttrValue &attr_value = attr->value(); if (attr_value.isFloat()) { - float float_val = attr_value.floatValue(); + auto [float_val, valid] = attr_value.floatValue(); value = static_cast(float_val); exists = true; } @@ -3260,21 +3272,19 @@ LibertyReader::getAttrFloat(const LibertyComplexAttr *attr, bool &valid) { if (attr_value->isFloat()) { - valid = true; - value = attr_value->floatValue(); + auto [value1, valid1] = attr_value->floatValue(); + value = value1; + valid = valid1; } else if (attr_value->isString()) { const std::string &str = attr_value->stringValue(); - variableValue(str.c_str(), value, valid); + variableValue(str, value, valid); if (!valid) { - char *end; - value = strtof(str.c_str(), &end); - if ((*end && !isspace(*end)) - || str == "inf") - warn(1183, attr->line(), "{} value {} is not a float.", - attr->name(), - str); - valid = true; + auto [value1, valid1] = stringFloat(str); + value = value1; + valid = valid1; + if (!valid1) + warn(1183, attr->line(), "{} value {} is not a float.", attr->name(), str); } } } @@ -3283,76 +3293,19 @@ LibertyReader::getAttrFloat(const LibertyComplexAttr *attr, // Note that some brain damaged vendors (that used to "Think") are not // consistent about including the delimiters. FloatSeq -LibertyReader::parseStringFloatList(const std::string &float_list, - float scale, - const LibertySimpleAttr *attr) -{ - FloatSeq values; - values.reserve(std::max(10, float_list.size() / 5)); - const char *token = float_list.c_str(); - while (*token != '\0') { - // Some (brain dead) libraries enclose floats in brackets. - if (*token == '{') - token++; - char *end; - float value = strtof(token, &end) * scale; - if (end == token - || !(*end == '\0' - || isspace(*end) - || *end == ',' - || *end == '}')) { - std::string token_end = token; - if (end != token) { - token_end.clear(); - for (const char *t = token; t <= end; t++) - token_end += *t; - } - warn(1310, attr, "{} is not a float.", token_end); - token += token_end.size(); - } - else { - values.push_back(value); - token = end; - } - while (*token == ',' || *token == ' ' || *token == '}') - token++; - } - return values; -} - -FloatSeq -LibertyReader::parseStringFloatList(const std::string &float_list, - float scale, - const LibertyComplexAttr *attr) +LibertyReader::parseFloatList(const std::string &float_list, + float scale, + int line) { + StringSeq tokens = parseTokens(float_list, " ,{}"); FloatSeq values; - values.reserve(std::max(10, float_list.size() / 5)); - const char *token = float_list.c_str(); - while (*token != '\0') { - if (*token == '{') - token++; - char *end; - float value = strtof(token, &end) * scale; - if (end == token - || !(*end == '\0' - || isspace(*end) - || *end == ',' - || *end == '}')) { - std::string token_end = token; - if (end != token) { - token_end.clear(); - for (const char *t = token; t <= end; t++) - token_end += *t; - } - warn(1275, attr, "{} is not a float.", token_end); - token += token_end.size(); - } - else { - values.push_back(value); - token = end; - } - while (*token == ',' || *token == ' ' || *token == '}') - token++; + values.reserve(tokens.size()); + for (const std::string &token : tokens) { + auto [value, valid] = stringFloat(token); + if (!valid) + warn(1310, line, "{} is not a float.", token); + else + values.push_back(value * scale); } return values; } @@ -3365,23 +3318,25 @@ LibertyReader::readFloatSeq(const LibertyComplexAttr *attr, const LibertyAttrValueSeq &attr_values = attr->values(); if (attr_values.size() == 1) { LibertyAttrValue *value = attr_values[0]; - if (value->isString()) { - values = parseStringFloatList(value->stringValue(), scale, attr); - } - else if (value->isFloat()) { - values.push_back(value->floatValue() * scale); + if (value->isString()) + values = parseFloatList(value->stringValue(), scale, attr->line()); + else { + auto [entry, valid] = value->floatValue(); + if (valid) + values.push_back(entry * scale); } - else - warn(1276, attr, "{} is missing values.", attr->name()); } else if (attr_values.size() > 1) { - for (LibertyAttrValue *val : attr_values) { - if (val->isFloat()) - values.push_back(val->floatValue() * scale); - else if (val->isString()) { - FloatSeq parsed = parseStringFloatList(val->stringValue(), scale, attr); + for (LibertyAttrValue *value : attr_values) { + if (value->isString()) { + FloatSeq parsed = parseFloatList(value->stringValue(), scale, attr->line()); values.insert(values.end(), parsed.begin(), parsed.end()); } + else { + auto [entry, valid] = value->floatValue(); + if (valid) + values.push_back(entry * scale); + } } } else @@ -3401,11 +3356,11 @@ LibertyReader::getAttrBool(const LibertySimpleAttr *attr, const LibertyAttrValue &val = attr->value(); if (val.isString()) { const std::string &str = val.stringValue(); - if (stringEqual(str.c_str(), "true")) { + if (stringEqual(str, "true")) { value = true; exists = true; } - else if (stringEqual(str.c_str(), "false")) { + else if (stringEqual(str, "false")) { value = false; exists = true; } @@ -3420,31 +3375,28 @@ LibertyReader::getAttrBool(const LibertySimpleAttr *attr, LogicValue LibertyReader::getAttrLogicValue(const LibertySimpleAttr *attr) { - const std::string *str = attr->stringValue(); - if (str) { - if (*str == "L") - return LogicValue::zero; - else if (*str == "H") - return LogicValue::one; - else if (*str == "X") - return LogicValue::unknown; - else - warn(1282, attr, "attribute {} value {} not recognized.", - attr->name(), *str); - // fall thru - } + const std::string &str = attr->stringValue(); + if (str == "L") + return LogicValue::zero; + else if (str == "H") + return LogicValue::one; + else if (str == "X") + return LogicValue::unknown; + else + warn(1282, attr, "attribute {} value {} not recognized.", attr->name(), str); + // fall thru return LogicValue::unknown; } const EarlyLateAll * LibertyReader::getAttrEarlyLate(const LibertySimpleAttr *attr) { - const std::string *value = attr->stringValue(); - if (*value == "early") + const std::string &value = attr->stringValue(); + if (value == "early") return EarlyLateAll::early(); - else if (*value == "late") + else if (value == "late") return EarlyLateAll::late(); - else if (*value == "early_and_late") + else if (value == "early_and_late") return EarlyLateAll::all(); else { warn(1283, attr, "unknown early/late value."); @@ -3455,16 +3407,16 @@ LibertyReader::getAttrEarlyLate(const LibertySimpleAttr *attr) //////////////////////////////////////////////////////////////// FuncExpr * -LibertyReader::parseFunc(const char *func, - const char *attr_name, +LibertyReader::parseFunc(std::string_view func, + std::string_view attr_name, const LibertyCell *cell, int line) { - std::string error_msg = format("{}, line {}{}", - filename_, - line, - attr_name); - return parseFuncExpr(func, cell, error_msg.c_str(), report_); + std::string error_msg = sta::format("{} line {}, {}", + filename_, + line, + attr_name); + return parseFuncExpr(func, cell, error_msg, report_); } //////////////////////////////////////////////////////////////// @@ -3480,11 +3432,11 @@ LibertyReader::visitVariable(LibertyVariable *var) } void -LibertyReader::variableValue(const char *var, +LibertyReader::variableValue(std::string_view var, float &value, bool &exists) { - findKeyValue(var_map_, var, value, exists); + findKeyValue(var_map_, std::string(var), value, exists); } //////////////////////////////////////////////////////////////// @@ -3492,15 +3444,14 @@ LibertyReader::variableValue(const char *var, void LibertyReader::readDefaultOcvDerateGroup(const LibertyGroup *library_group) { - const std::string *derate_name = + const std::string &derate_name = library_group->findAttrString("default_ocv_derate_group"); - if (derate_name) { - OcvDerate *derate = library_->findOcvDerate(derate_name->c_str()); + if (!derate_name.empty()) { + OcvDerate *derate = library_->findOcvDerate(derate_name.c_str()); if (derate) library_->setDefaultOcvDerate(derate); else - warn(1284, library_group, "OCV derate group named {} not found.", - *derate_name); + warn(1284, library_group, "OCV derate group named {} not found.", derate_name); } } @@ -3511,34 +3462,35 @@ LibertyReader::readOcvDerateFactors(LibertyCell *cell, { for (const LibertyGroup *ocv_derate_group : parent_group->findSubgroups("ocv_derate")) { - const char *name = ocv_derate_group->firstName(); - if (name) { + if (ocv_derate_group->hasFirstParam()) { + const std::string &name = ocv_derate_group->firstParam(); OcvDerate *ocv_derate = cell - ? cell->makeOcvDerate(name) + ? cell->makeOcvDerate(name) : library_->makeOcvDerate(name); for (const LibertyGroup *factors_group : ocv_derate_group->findSubgroups("ocv_derate_factors")) { const RiseFallBoth *rf_type = RiseFallBoth::riseFall(); - const std::string *rf_attr = factors_group->findAttrString("rf_type"); - if (rf_attr) { - if (*rf_attr == "rise") + const std::string &rf_attr = factors_group->findAttrString("rf_type"); + if (!rf_attr.empty()) { + if (rf_attr == "rise") rf_type = RiseFallBoth::rise(); - else if (*rf_attr == "fall") + else if (rf_attr == "fall") rf_type = RiseFallBoth::fall(); - else if (*rf_attr == "rise_and_fall") + else if (rf_attr == "rise_and_fall") rf_type = RiseFallBoth::riseFall(); else error(1286, factors_group, "unknown rise/fall."); } const EarlyLateAll *derate_type = EarlyLateAll::all(); - const std::string *derate_attr = factors_group->findAttrString("derate_type"); - if (derate_attr) { - if (*derate_attr == "early") + const std::string &derate_attr = + factors_group->findAttrString("derate_type"); + if (!derate_attr.empty()) { + if (derate_attr == "early") derate_type = EarlyLateAll::early(); - else if (*derate_attr == "late") + else if (derate_attr == "late") derate_type = EarlyLateAll::late(); - else if (*derate_attr == "early_and_late") + else if (derate_attr == "early_and_late") derate_type = EarlyLateAll::all(); else { warn(1309, factors_group, "unknown early/late value."); @@ -3546,20 +3498,20 @@ LibertyReader::readOcvDerateFactors(LibertyCell *cell, } PathType path_type = PathType::clk_and_data; - const std::string *path_attr = factors_group->findAttrString("path_type"); - if (path_attr) { - if (*path_attr == "clock") + const std::string &path_attr = factors_group->findAttrString("path_type"); + if (!path_attr.empty()) { + if (path_attr == "clock") path_type = PathType::clk; - else if (*path_attr == "data") + else if (path_attr == "data") path_type = PathType::data; - else if (*path_attr == "clock_and_data") + else if (path_attr == "clock_and_data") path_type = PathType::clk_and_data; else warn(1287, factors_group, "unknown derate type."); } - const char *template_name = factors_group->firstName(); - if (template_name) { + if (factors_group->hasFirstParam()) { + const std::string &template_name = factors_group->firstParam(); TableTemplate *tbl_template = library_->findTableTemplate(template_name, TableTemplateType::ocv); if (tbl_template) { @@ -3580,6 +3532,8 @@ LibertyReader::readOcvDerateFactors(LibertyCell *cell, else warn(1308, factors_group, "table template {} not found.", template_name); } + else + warn(1312, factors_group, "ocv_derate_factors missing template."); } } else @@ -3590,7 +3544,7 @@ LibertyReader::readOcvDerateFactors(LibertyCell *cell, //////////////////////////////////////////////////////////////// PortNameBitIterator::PortNameBitIterator(LibertyCell *cell, - const char *port_name, + std::string_view port_name, LibertyReader *visitor, int line) : cell_(cell), @@ -3606,7 +3560,7 @@ PortNameBitIterator::PortNameBitIterator(LibertyCell *cell, } void -PortNameBitIterator::init(const char *port_name) +PortNameBitIterator::init(std::string_view port_name) { LibertyPort *port = visitor_->findPort(cell_, port_name); if (port) { @@ -3637,13 +3591,12 @@ PortNameBitIterator::init(const char *port_name) range_bit_ = from; } else - visitor_->warn(1292, line_, "port {} subscript out of range.", - port_name); + visitor_->warn(1292, line_, "port {} subscript out of range.", port_name); } else visitor_->warn(1293, line_, "port range {} of non-bus port {}.", - port_name, - bus_name); + port_name, + bus_name); } else { range_bus_name_ = bus_name; @@ -3713,7 +3666,7 @@ PortNameBitIterator::findRangeBusNameNext() LibertyLibrary *library = visitor_->library(); std::string bus_bit_name = range_bus_name_ + library->busBrktLeft() + std::to_string(range_bit_) + library->busBrktRight(); - range_name_next_ = visitor_->findPort(cell_, bus_bit_name.c_str()); + range_name_next_ = visitor_->findPort(cell_, bus_bit_name); if (range_name_next_) { if (range_from_ > range_to_) range_bit_--; diff --git a/liberty/LibertyReader.hh b/liberty/LibertyReader.hh index ebf4503db..27fed80ae 100644 --- a/liberty/LibertyReader.hh +++ b/liberty/LibertyReader.hh @@ -24,13 +24,15 @@ #pragma once +#include + namespace sta { class Network; class LibertyLibrary; LibertyLibrary * -readLibertyFile(const char *filename, +readLibertyFile(std::string_view filename, bool infer_latches, Network *network); diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index e1307b7d3..a840ebb28 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -65,10 +66,10 @@ using OutputWaveformSeq = std::vector; class LibertyReader : public LibertyGroupVisitor { public: - LibertyReader(const char *filename, + LibertyReader(std::string_view filename, bool infer_latches, Network *network); - virtual LibertyLibrary *readLibertyFile(const char *filename); + virtual LibertyLibrary *readLibertyFile(std::string_view filename); LibertyLibrary *library() { return library_; } const LibertyLibrary *library() const { return library_; } @@ -90,7 +91,7 @@ public: void checkScaledCell(LibertyCell *scaled_cell, LibertyCell *owner, const LibertyGroup *scaled_cell_group, - const char *op_cond_name); + std::string_view op_cond_name); void setPortCapDefault(LibertyPort *port); void checkLatchEnableSense(FuncExpr *enable_func, @@ -102,9 +103,9 @@ public: float scale); LibertyPort *findPort(LibertyCell *cell, - const char *port_name); + std::string_view port_name); StringSeq findAttributStrings(const LibertyGroup *group, - const char *name_attr); + std::string_view name_attr); protected: virtual void begin(const LibertyGroup *group, @@ -113,7 +114,7 @@ protected: LibertyGroup *library_group); // Library gruops. - void makeLibrary(const LibertyGroup *libary_group); + void makeLibrary(const LibertyGroup *library_group); void readLibraryAttributes(const LibertyGroup *library_group); void readLibraryUnits(const LibertyGroup *library_group); void readDelayModel(const LibertyGroup *library_group); @@ -122,8 +123,8 @@ protected: void readDefaultWireLoadMode(const LibertyGroup *library_group); void readTechnology(const LibertyGroup *library_group); void readDefaultWireLoadSelection(const LibertyGroup *library_group); - void readUnit(const char *unit_attr_name, - const char *unit_suffix, + void readUnit(std::string_view unit_attr_name, + std::string_view unit_suffix, float &scale_var, Unit *unit, const LibertyGroup *library_group); @@ -131,7 +132,7 @@ protected: const LibertyGroup *type_group); void readTableTemplates(const LibertyGroup *library_group); void readTableTemplates(const LibertyGroup *library_group, - const char *group_name, + std::string_view group_name, TableTemplateType type); void readThresholds(const LibertyGroup *library_group); void checkThresholds(const LibertyGroup *library_group) const; @@ -150,17 +151,17 @@ protected: void readNormalizedDriverWaveform(const LibertyGroup *library_group); void readSlewDegradations(const LibertyGroup *library_group); void readLibAttrFloat(const LibertyGroup *library_group, - const char *attr_name, + std::string_view attr_name, void (LibertyLibrary::*set_func)(float value), float scale); void readLibAttrFloat(const LibertyGroup *library_group, - const char *attr_name, + std::string_view attr_name, void (LibertyLibrary::*set_func)(const RiseFall *rf, float value), const RiseFall *rf, float scale); void readLibAttrFloatWarnZero(const LibertyGroup *library_group, - const char *attr_name, + std::string_view attr_name, void (LibertyLibrary::*set_func)(float value), float scale); @@ -188,9 +189,9 @@ protected: void makePgPinPort(LibertyCell *cell, const LibertyGroup *pg_pin_group); LibertyPort *makePort(LibertyCell *cell, - const char *port_name); + std::string_view port_name); LibertyPort *makeBusPort(LibertyCell *cell, - const char *bus_name, + std::string_view bus_name, int from_index, int to_index, BusDcl *bus_dcl); @@ -198,22 +199,27 @@ protected: void readPortAttributes(LibertyCell *cell, const LibertyPortSeq &ports, const LibertyGroup *port_group); - void readPortAttrString(const char *attr_name, - void (LibertyPort::*set_func)(const char *value), + void readPortAttrString(std::string_view attr_name, + void (LibertyPort::*set_func)(std::string value), const LibertyPortSeq &ports, const LibertyGroup *group); - void readPortAttrFloat(const char *attr_name, + void readPortAttrLibertyPort(std::string_view attr_name, + void (LibertyPort::*set_func)(LibertyPort *port), + LibertyCell *cell, + const LibertyPortSeq &ports, + const LibertyGroup *group); + void readPortAttrFloat(std::string_view attr_name, void (LibertyPort::*set_func)(float value), const LibertyPortSeq &ports, const LibertyGroup *group, float scale); - void readPortAttrBool(const char *attr_name, + void readPortAttrBool(std::string_view attr_name, void (LibertyPort::*set_func)(bool value), const LibertyPortSeq &ports, const LibertyGroup *group); void readDriverWaveform(const LibertyPortSeq &ports, const LibertyGroup *port_group); - void readPortAttrFloatMinMax(const char *attr_name, + void readPortAttrFloatMinMax(std::string_view attr_name, void (LibertyPort::*set_func)(float value, const MinMax *min_max), const LibertyPortSeq &ports, @@ -243,7 +249,7 @@ protected: const std::function check_axes); TableModelsEarlyLate readEarlyLateTableModels(const LibertyGroup *timing_group, - const char *table_group_name, + std::string_view table_group_name, const RiseFall *rf, TableTemplateType template_type, float scale, @@ -252,7 +258,7 @@ protected: ReceiverModelPtr readReceiverCapacitance(const LibertyGroup *timing_group, const RiseFall *rf); void readReceiverCapacitance(const LibertyGroup *timing_group, - const char *cap_group_name, + std::string_view cap_group_name, int index, const RiseFall *rf, ReceiverModelPtr &receiver_model); @@ -291,9 +297,9 @@ protected: const std::function check_axes); TableAxisPtr makeTableAxis(const LibertyGroup *table_group, - const char *index_attr_name, + std::string_view index_attr_name, TableAxisPtr template_axis); - void readGroupAttrFloat(const char *attr_name, + void readGroupAttrFloat(std::string_view attr_name, const LibertyGroup *group, const std::function &set_func, float scale = 1.0F); @@ -318,12 +324,12 @@ protected: void makeSequentials(LibertyCell *cell, const LibertyGroup *cell_group, bool is_register, - const char *seq_group_name, - const char *clk_attr_name, - const char *data_attr_name); + std::string_view seq_group_name, + std::string_view clk_attr_name, + std::string_view data_attr_name); FuncExpr *makeSeqFunc(LibertyCell *cell, const LibertyGroup *seq_group, - const char *attr_name, + std::string_view attr_name, int size); void makeSeqPorts(LibertyCell *cell, const LibertyGroup *seq_group, @@ -332,8 +338,9 @@ protected: LibertyPort *&out_port_inv, size_t &size); void seqPortNames(const LibertyGroup *group, - const char *&out_name, - const char *&out_inv_name, + // Return values. + std::string &out_name, + std::string &out_inv_name, bool &has_size, size_t &size); TimingModel *makeScalarCheckModel(LibertyCell *cell, @@ -367,17 +374,17 @@ protected: const LibertyGroup *cell_group); void readScaleFactors(LibertyCell *cell, const LibertyGroup *cell_group); - void readCellAttrString(const char *attr_name, - void (LibertyCell::*set_func)(const char *value), + void readCellAttrString(std::string_view attr_name, + void (LibertyCell::*set_func)(std::string value), LibertyCell *cell, const LibertyGroup *group); - void readCellAttrFloat(const char *attr_name, + void readCellAttrFloat(std::string_view attr_name, void (LibertyCell::*set_func)(float value), LibertyCell *cell, const LibertyGroup *group, float scale); - void readCellAttrBool(const char *attr_name, - void (LibertyCell::*set_func)(bool value), + void readCellAttrBool(std::string_view attr_name, + void (LibertyCell::*set_func)(bool value), LibertyCell *cell, const LibertyGroup *group); void readLevelShifterType(LibertyCell *cell, @@ -393,18 +400,18 @@ protected: FuncExpr *readFuncExpr(LibertyCell *cell, const LibertyGroup *group, - const char *attr_name); + std::string_view attr_name); LibertyPort *findLibertyPort(LibertyCell *cell, const LibertyGroup *group, - const char *port_name_attr); + std::string_view port_name_attr); LibertyPortSeq findLibertyPorts(LibertyCell *cell, const LibertyGroup *group, - const char *port_name_attr); + std::string_view port_name_attr); float energyScale(); void defineVisitors(); - void defineGroupVisitor(const char *type, + void defineGroupVisitor(std::string_view type, LibraryGroupVisitor begin_visitor, LibraryGroupVisitor end_visitor); @@ -436,52 +443,49 @@ protected: bool &exists); const EarlyLateAll *getAttrEarlyLate(const LibertySimpleAttr *attr); - FloatSeq parseStringFloatList(const std::string &float_list, - float scale, - const LibertySimpleAttr *attr); - FloatSeq parseStringFloatList(const std::string &float_list, - float scale, - const LibertyComplexAttr *attr); + FloatSeq parseFloatList(const std::string &float_list, + float scale, + int line); TableAxisPtr makeAxis(int index, const LibertyGroup *group); FloatSeq readFloatSeq(const LibertyComplexAttr *attr, float scale); - void variableValue(const char *var, + void variableValue(std::string_view var, float &value, bool &exists); - FuncExpr *parseFunc(const char *func, - const char *attr_name, + FuncExpr *parseFunc(std::string_view func, + std::string_view attr_name, const LibertyCell *cell, int line); template void warn(int id, - const LibertyGroup *group, - std::string_view fmt, - Args &&...args) const + const LibertyGroup *group, + 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, - std::string_view fmt, - Args &&...args) const + const LibertySimpleAttr *attr, + 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, - std::string_view fmt, - Args &&...args) const + const LibertyComplexAttr *attr, + std::string_view fmt, + Args &&...args) const { report_->fileWarn(id, filename_, attr->line(), fmt, std::forward(args)...); } template void warn(int id, - int line, - std::string_view fmt, - Args &&...args) const + int line, + std::string_view fmt, + Args &&...args) const { report_->fileWarn(id, filename_, line, fmt, std::forward(args)...); } @@ -513,7 +517,7 @@ protected: std::forward(args)...); } - const char *filename_; + std::string_view filename_; bool infer_latches_; Report *report_; Debug *debug_; @@ -547,7 +551,7 @@ class PortNameBitIterator : public Iterator { public: PortNameBitIterator(LibertyCell *cell, - const char *port_name, + std::string_view port_name, LibertyReader *visitor, int line); ~PortNameBitIterator(); @@ -558,7 +562,7 @@ public: protected: void findRangeBusNameNext(); - void init(const char *port_name); + void init(std::string_view port_name); LibertyCell *cell_; LibertyReader *visitor_; int line_; diff --git a/liberty/LibertyScanner.hh b/liberty/LibertyScanner.hh index 3623940cd..bb42f4c36 100644 --- a/liberty/LibertyScanner.hh +++ b/liberty/LibertyScanner.hh @@ -24,8 +24,9 @@ #pragma once -#include #include +#include +#include #include "LibertyParse.hh" @@ -44,7 +45,7 @@ class LibertyScanner : public LibertyFlexLexer { public: LibertyScanner(std::istream *stream, - const char *filename, + std::string_view filename, LibertyParser *reader, Report *report); virtual ~LibertyScanner() {} diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 5e22ce6d5..a27490fb5 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -298,11 +298,11 @@ LibertyWriter::writeCell(const LibertyCell *cell) sta::print(stream_, " is_macro_cell : true;\n"); if (cell->interfaceTiming()) sta::print(stream_, " interface_timing : true;\n"); - const char *footprint = cell->footprint(); - if (footprint) + const std::string &footprint = cell->footprint(); + if (!footprint.empty()) sta::print(stream_, " cell_footprint : \"{}\";\n", footprint); - const char *user_function_class = cell->userFunctionClass(); - if (user_function_class) + const std::string &user_function_class = cell->userFunctionClass(); + if (!user_function_class.empty()) sta::print(stream_, " user_function_class : \"{}\";\n", user_function_class); LibertyCellPortIterator port_iter(cell); diff --git a/liberty/LinearModel.cc b/liberty/LinearModel.cc index b8e6a78ce..4d96138e6 100644 --- a/liberty/LinearModel.cc +++ b/liberty/LinearModel.cc @@ -119,7 +119,7 @@ CheckLinearModel::checkDelay(const Pvt *, std::string CheckLinearModel::reportCheckDelay(const Pvt *, float, - const char *, + std::string_view, float, float, const MinMax *, diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index c3e8856fa..09f80ee80 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -242,7 +242,7 @@ GateTableModel::reportGateDelay(const Pvt *pvt, } std::string -GateTableModel::reportTableLookup(const char *result_name, +GateTableModel::reportTableLookup(std::string_view result_name, const Pvt *pvt, const TableModel *model, float in_slew, @@ -255,7 +255,7 @@ GateTableModel::reportTableLookup(const char *result_name, 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, + return model->reportValue(result_name, cell_, pvt, axis_value1, {}, axis_value2, axis_value3, library->units()->timeUnit(), digits); } @@ -538,7 +538,7 @@ CheckTableModel::findValue(const Pvt *pvt, std::string CheckTableModel::reportCheckDelay(const Pvt *pvt, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, const MinMax *min_max, @@ -567,11 +567,11 @@ CheckTableModel::reportCheckDelay(const Pvt *pvt, } std::string -CheckTableModel::reportTableDelay(const char *result_name, +CheckTableModel::reportTableDelay(std::string_view result_name, const Pvt *pvt, const TableModel *model, float from_slew, - const char *from_slew_annotation, + std::string_view from_slew_annotation, float to_slew, float related_out_cap, int digits) const @@ -852,11 +852,11 @@ TableModel::scaleFactor(const LibertyCell *cell, } std::string -TableModel::reportValue(const char *result_name, +TableModel::reportValue(std::string_view result_name, const LibertyCell *cell, const Pvt *pvt, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, @@ -868,7 +868,7 @@ TableModel::reportValue(const char *result_name, result += reportPvtScaleFactor(cell, pvt, digits); - result += result_name; + result.append(result_name); result += " = "; result += table_unit->asString(findValue(cell, pvt, value1, value2, value3), digits); @@ -1255,11 +1255,11 @@ Table::findValue(const LibertyLibrary *, } std::string -Table::reportValue(const char *result_name, +Table::reportValue(std::string_view result_name, const LibertyCell *cell, const Pvt *, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, @@ -1283,25 +1283,24 @@ Table::reportValue(const char *result_name, } std::string -Table::reportValueOrder0(const char *result_name, - const char *comment1, +Table::reportValueOrder0(std::string_view result_name, + std::string_view comment1, const Unit *table_unit, int digits) const { - std::string result = result_name; + std::string result(result_name); result += " constant = "; result += table_unit->asString(value_, digits); - if (comment1) - result += comment1; + result.append(comment1); result += '\n'; return result; } std::string -Table::reportValueOrder1(const char *result_name, +Table::reportValueOrder1(std::string_view result_name, const LibertyCell *cell, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, @@ -1313,8 +1312,7 @@ Table::reportValueOrder1(const char *result_name, result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); - if (comment1) - result += comment1; + result.append(comment1); result += '\n'; if (axis1_->size() != 1) { size_t index1 = axis1_->findAxisIndex(value1); @@ -1330,7 +1328,7 @@ Table::reportValueOrder1(const char *result_name, result += table_unit->asString(value(index1 + 1), digits); result += '\n'; } - result += result_name; + result.append(result_name); result += " = "; result += table_unit->asString(findValue(value1, value2, value3), digits); result += '\n'; @@ -1338,10 +1336,10 @@ Table::reportValueOrder1(const char *result_name, } std::string -Table::reportValueOrder2(const char *result_name, +Table::reportValueOrder2(std::string_view result_name, const LibertyCell *cell, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, @@ -1354,8 +1352,7 @@ Table::reportValueOrder2(const char *result_name, result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); - if (comment1) - result += comment1; + result.append(comment1); result += '\n'; result += "| "; result += axis2_->variableString(); @@ -1390,7 +1387,7 @@ Table::reportValueOrder2(const char *result_name, } } result += '\n'; - result += result_name; + result.append(result_name); result += " = "; result += table_unit->asString(findValue(value1, value2, value3), digits); result += '\n'; @@ -1398,10 +1395,10 @@ Table::reportValueOrder2(const char *result_name, } std::string -Table::reportValueOrder3(const char *result_name, +Table::reportValueOrder3(std::string_view result_name, const LibertyCell *cell, float value1, - const char *comment1, + std::string_view comment1, float value2, float value3, const Unit *table_unit, @@ -1415,8 +1412,7 @@ Table::reportValueOrder3(const char *result_name, result += axis1_->variableString(); result += " = "; result += unit1->asString(value1, digits); - if (comment1) - result += comment1; + result.append(comment1); result += '\n'; result += " | ---- "; result += axis2_->variableString(); @@ -1492,7 +1488,7 @@ Table::reportValueOrder3(const char *result_name, } } result += '\n'; - result += result_name; + result.append(result_name); result += " = "; result += table_unit->asString(findValue(value1, value2, value3), digits); result += '\n'; @@ -1703,7 +1699,7 @@ TableAxis::findAxisClosestIndex(float value) const } } -const char * +std::string_view TableAxis::variableString() const { return tableVariableString(variable_); @@ -1741,12 +1737,12 @@ static EnumNameMap table_axis_variable_map = { {TableAxisVariable::normalized_voltage, "normalized_voltage"}}; TableAxisVariable -stringTableAxisVariable(const char *variable) +stringTableAxisVariable(std::string_view variable) { return table_axis_variable_map.find(variable, TableAxisVariable::unknown); } -const char * +std::string_view tableVariableString(TableAxisVariable variable) { return table_axis_variable_map.find(variable); @@ -2213,9 +2209,9 @@ OutputWaveforms::finalResistance() //////////////////////////////////////////////////////////////// -DriverWaveform::DriverWaveform(const std::string &name, +DriverWaveform::DriverWaveform(std::string name, TablePtr waveforms) : - name_(name), + name_(std::move(name)), waveforms_(waveforms) { } diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 375ad68ea..a254b7cdc 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -88,34 +88,34 @@ TimingArcAttrs::setCond(FuncExpr *cond) } void -TimingArcAttrs::setSdfCond(const std::string &cond) +TimingArcAttrs::setSdfCond(std::string cond) { - sdf_cond_ = cond; + sdf_cond_ = std::move(cond); sdf_cond_start_ = sdf_cond_end_ = sdf_cond_; } void -TimingArcAttrs::setSdfCondStart(const std::string &cond) +TimingArcAttrs::setSdfCondStart(std::string cond) { - sdf_cond_start_ = cond; + sdf_cond_start_ = std::move(cond); } void -TimingArcAttrs::setSdfCondEnd(const std::string &cond) +TimingArcAttrs::setSdfCondEnd(std::string cond) { - sdf_cond_end_ = cond; + sdf_cond_end_ = std::move(cond); } void -TimingArcAttrs::setModeName(const std::string &name) +TimingArcAttrs::setModeName(std::string name) { - mode_name_ = name; + mode_name_ = std::move(name); } void -TimingArcAttrs::setModeValue(const std::string &value) +TimingArcAttrs::setModeValue(std::string value) { - mode_value_ = value; + mode_value_ = std::move(value); } TimingModel * @@ -679,7 +679,7 @@ static EnumNameMap timing_sense_name_map = {TimingSense::unknown, "unknown"} }; -const char * +const std::string & to_string(TimingSense sense) { return timing_sense_name_map.find(sense); @@ -747,14 +747,14 @@ EnumNameMap timing_type_name_map = {TimingType::unknown, "unknown"} }; -const char * +std::string_view timingTypeString(TimingType type) { return timing_type_name_map.find(type); } TimingType -findTimingType(const char *type_name) +findTimingType(std::string_view type_name) { return timing_type_name_map.find(type_name, TimingType::unknown); } diff --git a/liberty/Units.cc b/liberty/Units.cc index fb3fcdb68..6e5a4e34d 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -193,21 +193,21 @@ Units::Units() : } Unit * -Units::find(const char *unit_name) +Units::find(std::string_view unit_name) { - if (stringEq(unit_name, "time")) + if (stringEqual(unit_name, "time")) return &time_unit_; - else if (stringEq(unit_name, "resistance")) + else if (stringEqual(unit_name, "resistance")) return &resistance_unit_; - else if (stringEq(unit_name, "capacitance")) + else if (stringEqual(unit_name, "capacitance")) return &capacitance_unit_; - else if (stringEq(unit_name, "voltage")) + else if (stringEqual(unit_name, "voltage")) return &voltage_unit_; - else if (stringEq(unit_name, "current")) + else if (stringEqual(unit_name, "current")) return ¤t_unit_; - else if (stringEq(unit_name, "power")) + else if (stringEqual(unit_name, "power")) return &power_unit_; - else if (stringEq(unit_name, "distance")) + else if (stringEqual(unit_name, "distance")) return &distance_unit_; else return nullptr; diff --git a/liberty/Wireload.cc b/liberty/Wireload.cc index cc88891bf..1c30df104 100644 --- a/liberty/Wireload.cc +++ b/liberty/Wireload.cc @@ -31,9 +31,9 @@ namespace sta { -Wireload::Wireload(const char *name, +Wireload::Wireload(std::string name, LibertyLibrary *library) : - name_(stringCopy(name)), + name_(std::move(name)), library_(library), area_(0.0F), resistance_(0.0F), @@ -42,13 +42,13 @@ Wireload::Wireload(const char *name, { } -Wireload::Wireload(const char *name, +Wireload::Wireload(std::string name, LibertyLibrary *library, float area, float resistance, float capacitance, float slope) : - name_(stringCopy(name)), + name_(std::move(name)), library_(library), area_(area), resistance_(resistance), @@ -60,7 +60,6 @@ Wireload::Wireload(const char *name, Wireload::~Wireload() { deleteContents(fanout_lengths_); - stringDelete(name_); } void @@ -186,15 +185,14 @@ WireloadForArea::WireloadForArea(float min_area, { } -WireloadSelection::WireloadSelection(const char *name) : - name_(stringCopy(name)) +WireloadSelection::WireloadSelection(std::string name) : + name_(std::move(name)) { } WireloadSelection::~WireloadSelection() { deleteContents(wireloads_); - stringDelete(name_); } struct WireloadForAreaMinLess @@ -264,13 +262,13 @@ wireloadTreeString(WireloadTree tree) } WireloadTree -stringWireloadTree(const char *wire_load_type) +stringWireloadTree(std::string_view wire_load_type) { - if (stringEq(wire_load_type, "worst_case_tree")) + if (wire_load_type == "worst_case_tree") return WireloadTree::worst_case; - else if (stringEq(wire_load_type, "best_case_tree")) + else if (wire_load_type == "best_case_tree") return WireloadTree::best_case; - else if (stringEq(wire_load_type, "balanced_tree")) + else if (wire_load_type == "balanced_tree") return WireloadTree::balanced; else return WireloadTree::unknown; @@ -294,13 +292,13 @@ wireloadModeString(WireloadMode wire_load_mode) } WireloadMode -stringWireloadMode(const char *wire_load_mode) +stringWireloadMode(std::string_view wire_load_mode) { - if (stringEq(wire_load_mode, "top")) + if (wire_load_mode == "top") return WireloadMode::top; - else if (stringEq(wire_load_mode, "enclosed")) + else if (wire_load_mode == "enclosed") return WireloadMode::enclosed; - else if (stringEq(wire_load_mode, "segmented")) + else if (wire_load_mode == "segmented") return WireloadMode::segmented; else return WireloadMode::unknown; diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index 1d18d24dc..74c37b151 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -38,12 +38,12 @@ namespace sta { static constexpr char escape_ = '\\'; -ConcreteLibrary::ConcreteLibrary(const char *name, - const char *filename, +ConcreteLibrary::ConcreteLibrary(std::string name, + std::string filename, bool is_liberty) : - name_(name), + name_(std::move(name)), id_(ConcreteNetwork::nextObjectId()), - filename_(filename ? filename : ""), + filename_(std::move(filename)), is_liberty_(is_liberty), bus_brkt_left_('['), bus_brkt_right_(']') @@ -56,9 +56,9 @@ ConcreteLibrary::~ConcreteLibrary() } ConcreteCell * -ConcreteLibrary::makeCell(const char *name, +ConcreteLibrary::makeCell(std::string_view name, bool is_leaf, - const char *filename) + std::string_view filename) { ConcreteCell *cell = new ConcreteCell(name, filename, is_leaf, this); addCell(cell); @@ -72,11 +72,9 @@ ConcreteLibrary::addCell(ConcreteCell *cell) } void -ConcreteLibrary::renameCell(ConcreteCell *cell, - const char *cell_name) +ConcreteLibrary::removeCell(ConcreteCell *cell) { cell_map_.erase(cell->name()); - cell_map_[cell_name] = cell; } void @@ -93,9 +91,9 @@ ConcreteLibrary::cellIterator() const } ConcreteCell * -ConcreteLibrary::findCell(const char *name) const +ConcreteLibrary::findCell(std::string_view name) const { - return findKey(cell_map_, name); + return findStringKey(cell_map_, name); } CellSeq @@ -119,13 +117,13 @@ ConcreteLibrary::setBusBrkts(char left, //////////////////////////////////////////////////////////////// -ConcreteCell::ConcreteCell(const char *name, - const char *filename, +ConcreteCell::ConcreteCell(std::string_view name, + std::string_view filename, bool is_leaf, ConcreteLibrary *library) : name_(name), id_(ConcreteNetwork::nextObjectId()), - filename_(filename ? filename : ""), + filename_(filename), library_(library), liberty_cell_(nullptr), ext_cell_(nullptr), @@ -140,10 +138,11 @@ ConcreteCell::~ConcreteCell() } void -ConcreteCell::setName(const char *name) +ConcreteCell::setName(std::string_view name) { - library_->renameCell(this, name); + library_->removeCell(this); name_ = name; + library_->addCell(this); } void @@ -159,18 +158,20 @@ ConcreteCell::setExtCell(void *ext_cell) } ConcretePort * -ConcreteCell::makePort(const char *name) +ConcreteCell::makePort(std::string_view name) { - ConcretePort *port = new ConcretePort(name, false, -1, -1, false, nullptr, this); + ConcretePort *port = new ConcretePort(name, false, -1, -1, + false, nullptr, this); addPort(port); return port; } ConcretePort * -ConcreteCell::makeBundlePort(const char *name, +ConcreteCell::makeBundlePort(std::string_view name, ConcretePortSeq *members) { - ConcretePort *port = new ConcretePort(name, false, -1, -1, true, members, this); + ConcretePort *port = new ConcretePort(name, false, -1, -1, true, + members, this); addPort(port); for (ConcretePort *member : *members) member->setBundlePort(port); @@ -178,19 +179,19 @@ ConcreteCell::makeBundlePort(const char *name, } ConcretePort * -ConcreteCell::makeBusPort(const char *name, +ConcreteCell::makeBusPort(std::string_view name, int from_index, int to_index) { ConcretePort *port = new ConcretePort(name, true, from_index, to_index, false, new ConcretePortSeq, this); addPort(port); - makeBusPortBits(port, name, from_index, to_index); + makeBusPortBits(port, port->name(), from_index, to_index); return port; } ConcretePort * -ConcreteCell::makeBusPort(const char *name, +ConcreteCell::makeBusPort(std::string_view name, int from_index, int to_index, ConcretePortSeq *members) @@ -203,40 +204,42 @@ ConcreteCell::makeBusPort(const char *name, void ConcreteCell::makeBusPortBits(ConcretePort *bus_port, - const char *name, + std::string_view bus_name, int from_index, int to_index) { if (from_index < to_index) { for (int index = from_index; index <= to_index; index++) - makeBusPortBit(bus_port, name, index); + makeBusPortBit(bus_port, bus_name, index); } else { for (int index = from_index; index >= to_index; index--) - makeBusPortBit(bus_port, name, index); + makeBusPortBit(bus_port, bus_name, index); } } void ConcreteCell::makeBusPortBit(ConcretePort *bus_port, - const char *bus_name, + std::string_view bus_name, int bit_index) { - 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); + std::string bit_name; + bit_name.append(bus_name); + bit_name += library_->busBrktLeft(); + bit_name += std::to_string(bit_index); + bit_name += library_->busBrktRight(); + ConcretePort *port = makePort(bit_name, bit_index); bus_port->addPortBit(port); addPortBit(port); } ConcretePort * -ConcreteCell::makePort(const char *bit_name, +ConcreteCell::makePort(std::string bit_name, int bit_index) { - ConcretePort *port = new ConcretePort(bit_name, false, bit_index, - bit_index, false, nullptr, this); + ConcretePort *port = new ConcretePort(bit_name, false, + bit_index, bit_index, false, + nullptr, this); addPortBit(port); return port; } @@ -264,14 +267,14 @@ ConcreteCell::setIsLeaf(bool is_leaf) } void -ConcreteCell::setAttribute(const std::string &key, - const std::string &value) +ConcreteCell::setAttribute(std::string_view key, + std::string_view value) { - attribute_map_[key] = value; + attribute_map_[std::string(key)] = value; } std::string -ConcreteCell::getAttribute(const std::string &key) const +ConcreteCell::getAttribute(std::string_view key) const { const auto &itr = attribute_map_.find(key); if (itr != attribute_map_.end()) @@ -280,9 +283,9 @@ ConcreteCell::getAttribute(const std::string &key) const } ConcretePort * -ConcreteCell::findPort(const char *name) const +ConcreteCell::findPort(std::string_view name) const { - return findKey(port_map_, name); + return findStringKey(port_map_, name); } size_t @@ -350,7 +353,7 @@ BusPort::addBusBit(ConcretePort *port, void ConcreteCell::groupBusPorts(const char bus_brkt_left, const char bus_brkt_right, - std::function port_msb_first) + std::function port_msb_first) { const char bus_brkts_left[2]{bus_brkt_left, '\0'}; const char bus_brkts_right[2]{bus_brkt_right, '\0'}; @@ -362,11 +365,10 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, ConcretePortSeq ports = ports_; ports_.clear(); for (ConcretePort *port : ports) { - const char *port_name = port->name(); bool is_bus; std::string bus_name; int index; - parseBusName(port_name, bus_brkts_left, bus_brkts_right, escape_, + parseBusName(port->name(), bus_brkts_left, bus_brkts_right, escape_, is_bus, bus_name, index); if (is_bus) { if (!port->isBusBit()) { @@ -385,7 +387,7 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, int from = bus_port.from(); int to = bus_port.to(); size_t size = to - from + 1; - bool msb_first = port_msb_first(bus_name.c_str()); + bool msb_first = port_msb_first(bus_name); ConcretePortSeq *members = new ConcretePortSeq(size); // Index the bus bit ports. for (ConcretePort *bus_bit : bus_port.members()) { @@ -395,14 +397,14 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, } if (msb_first) std::swap(from, to); - ConcretePort *port = makeBusPort(bus_name.c_str(), from, to, members); + ConcretePort *port = makeBusPort(bus_name, from, to, members); port->setDirection(bus_port.direction()); } } //////////////////////////////////////////////////////////////// -ConcretePort::ConcretePort(const char *name, +ConcretePort::ConcretePort(std::string_view name, bool is_bus, int from_index, int to_index, @@ -458,18 +460,17 @@ ConcretePort::setExtPort(void *port) ext_port_ = port; } -const char * +std::string ConcretePort::busName() const { if (is_bus_) { ConcreteLibrary *lib = cell_->library(); - std::string bus_name = sta::format("{}{}{}:{}{}", - name(), - lib->busBrktLeft(), - from_index_, - to_index_, - lib->busBrktRight()); - return makeTmpString(bus_name); + return sta::format("{}{}{}:{}{}", + name(), + lib->busBrktLeft(), + from_index_, + to_index_, + lib->busBrktRight()); } else return name(); diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index 6291d5bc0..44cc8a0ac 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -25,6 +25,7 @@ #include "ConcreteNetwork.hh" #include +#include #include "PatternMatch.hh" #include "Report.hh" @@ -424,19 +425,21 @@ ConcreteNetwork::libertyLibraryIterator() const //////////////////////////////////////////////////////////////// Library * -ConcreteNetwork::makeLibrary(const char *name, - const char *filename) +ConcreteNetwork::makeLibrary(std::string_view name, + std::string_view filename) { - ConcreteLibrary *library = new ConcreteLibrary(name, filename, false); + ConcreteLibrary *library = new ConcreteLibrary(std::string(name), + std::string(filename), false); addLibrary(library); return reinterpret_cast(library); } LibertyLibrary * -ConcreteNetwork::makeLibertyLibrary(const char *name, - const char *filename) +ConcreteNetwork::makeLibertyLibrary(std::string_view name, + std::string_view filename) { - LibertyLibrary *library = new LibertyLibrary(name, filename); + LibertyLibrary *library = new LibertyLibrary(std::string(name), + std::string(filename)); addLibrary(library); return library; } @@ -449,9 +452,9 @@ ConcreteNetwork::addLibrary(ConcreteLibrary *library) } Library * -ConcreteNetwork::findLibrary(const char *name) +ConcreteNetwork::findLibrary(std::string_view name) { - return reinterpret_cast(findKey(library_map_, name)); + return reinterpret_cast(findStringKey(library_map_, name)); } void @@ -463,7 +466,7 @@ ConcreteNetwork::deleteLibrary(Library *library) delete clib; } -const char * +std::string ConcreteNetwork::name(const Library *library) const { const ConcreteLibrary *clib = @@ -480,16 +483,16 @@ ConcreteNetwork::id(const Library *library) const } LibertyLibrary * -ConcreteNetwork::findLiberty(const char *name) +ConcreteNetwork::findLiberty(std::string_view name) { - ConcreteLibrary *lib = findKey(library_map_, name); + ConcreteLibrary *lib = findStringKey(library_map_, name); if (lib) { if (lib->isLiberty()) return static_cast(lib); // Potential name conflict else { for (ConcreteLibrary *lib : library_seq_) { - if (stringEq(lib->name(), name) + if (lib->name() == name && lib->isLiberty()) return static_cast(lib); } @@ -500,9 +503,9 @@ ConcreteNetwork::findLiberty(const char *name) Cell * ConcreteNetwork::makeCell(Library *library, - const char *name, + std::string_view name, bool is_leaf, - const char *filename) + std::string_view filename) { ConcreteLibrary *clib = reinterpret_cast(library); return reinterpret_cast(clib->makeCell(name, is_leaf, filename)); @@ -510,7 +513,7 @@ ConcreteNetwork::makeCell(Library *library, Cell * ConcreteNetwork::findCell(const Library *library, - const char *name) const + std::string_view name) const { const ConcreteLibrary *clib = reinterpret_cast(library); @@ -518,7 +521,7 @@ ConcreteNetwork::findCell(const Library *library, } Cell * -ConcreteNetwork::findAnyCell(const char *name) +ConcreteNetwork::findAnyCell(std::string_view name) { for (ConcreteLibrary *lib : library_seq_) { ConcreteCell *cell = lib->findCell(name); @@ -547,7 +550,7 @@ ConcreteNetwork::deleteCell(Cell *cell) //////////////////////////////////////////////////////////////// -const char * +std::string ConcreteNetwork::name(const Cell *cell) const { const ConcreteCell *ccell = reinterpret_cast(cell); @@ -563,7 +566,7 @@ ConcreteNetwork::id(const Cell *cell) const void ConcreteNetwork::setName(Cell *cell, - const char *name) + std::string_view name) { ConcreteCell *ccell = reinterpret_cast(cell); ccell->setName(name); @@ -579,8 +582,8 @@ ConcreteNetwork::setIsLeaf(Cell *cell, void ConcreteNetwork::setAttribute(Cell *cell, - const std::string &key, - const std::string &value) + std::string_view key, + std::string_view value) { ConcreteCell *ccell = reinterpret_cast(cell); ccell->setAttribute(key, value); @@ -619,8 +622,8 @@ ConcreteNetwork::cell(const LibertyCell *cell) const return reinterpret_cast(cell); } -const char * -ConcreteNetwork::filename(const Cell *cell) +std::string_view +ConcreteNetwork::filename(const Cell *cell) const { const ConcreteCell *ccell = reinterpret_cast(cell); return ccell->filename(); @@ -628,7 +631,7 @@ ConcreteNetwork::filename(const Cell *cell) std::string ConcreteNetwork::getAttribute(const Cell *cell, - const std::string &key) const + std::string_view key) const { const ConcreteCell *ccell = reinterpret_cast(cell); return ccell->getAttribute(key); @@ -643,7 +646,7 @@ ConcreteNetwork::attributeMap(const Cell *cell) const Port * ConcreteNetwork::findPort(const Cell *cell, - const char *name) const + std::string_view name) const { const ConcreteCell *ccell = reinterpret_cast(cell); return reinterpret_cast(ccell->findPort(name)); @@ -658,7 +661,7 @@ ConcreteNetwork::isLeaf(const Cell *cell) const Port * ConcreteNetwork::makePort(Cell *cell, - const char *name) + std::string_view name) { ConcreteCell *ccell = reinterpret_cast(cell); ConcretePort *port = ccell->makePort(name); @@ -667,7 +670,7 @@ ConcreteNetwork::makePort(Cell *cell, Port * ConcreteNetwork::makeBusPort(Cell *cell, - const char *name, + std::string_view name, int from_index, int to_index) { @@ -678,7 +681,7 @@ ConcreteNetwork::makeBusPort(Cell *cell, void ConcreteNetwork::groupBusPorts(Cell *cell, - std::function port_msb_first) + std::function port_msb_first) { Library *lib = library(cell); ConcreteLibrary *clib = reinterpret_cast(lib); @@ -689,7 +692,7 @@ ConcreteNetwork::groupBusPorts(Cell *cell, Port * ConcreteNetwork::makeBundlePort(Cell *cell, - const char *name, + std::string_view name, PortSeq *members) { ConcreteCell *ccell = reinterpret_cast(cell); @@ -795,7 +798,7 @@ ConcreteNetwork::portBitCount(const Cell *cell) const //////////////////////////////////////////////////////////////// -const char * +std::string ConcreteNetwork::name(const Port *port) const { const ConcretePort *cport = reinterpret_cast(port); @@ -844,7 +847,7 @@ ConcreteNetwork::isBus(const Port *port) const return cport->isBus(); } -const char * +std::string ConcreteNetwork::busName(const Port *port) const { const ConcretePort *cport = reinterpret_cast(port); @@ -949,12 +952,12 @@ ConcreteNetwork::memberIterator(const Port *port) const //////////////////////////////////////////////////////////////// -const char * +std::string ConcreteNetwork::name(const Instance *instance) const { const ConcreteInstance *inst = reinterpret_cast(instance); - return inst->name(); + return std::string(inst->name()); } ObjectId @@ -967,7 +970,7 @@ ConcreteNetwork::id(const Instance *instance) const std::string ConcreteNetwork::getAttribute(const Instance *inst, - const std::string &key) const + std::string_view key) const { const ConcreteInstance *cinst = reinterpret_cast(inst); return cinst->getAttribute(key); @@ -1007,7 +1010,7 @@ ConcreteNetwork::isLeaf(const Instance *instance) const Instance * ConcreteNetwork::findChild(const Instance *parent, - const char *name) const + std::string_view name) const { const ConcreteInstance *inst = reinterpret_cast(parent); @@ -1016,7 +1019,7 @@ ConcreteNetwork::findChild(const Instance *parent, Pin * ConcreteNetwork::findPin(const Instance *instance, - const char *port_name) const + std::string_view port_name) const { const ConcreteInstance *inst = reinterpret_cast(instance); @@ -1034,7 +1037,7 @@ ConcreteNetwork::findPin(const Instance *instance, Net * ConcreteNetwork::findNet(const Instance *instance, - const char *net_name) const + std::string_view net_name) const { const ConcreteInstance *inst = reinterpret_cast(instance); @@ -1164,11 +1167,11 @@ ConcreteNetwork::pin(const Term *term) const //////////////////////////////////////////////////////////////// -const char * +std::string ConcreteNetwork::name(const Net *net) const { const ConcreteNet *cnet = reinterpret_cast(net); - return cnet->name(); + return std::string(cnet->name()); } ObjectId @@ -1238,7 +1241,7 @@ ConcreteInstance::cell() const Instance * ConcreteNetwork::makeInstance(Cell *cell, - const char *name, + std::string_view name, Instance *parent) { ConcreteCell *ccell = reinterpret_cast(cell); @@ -1247,7 +1250,7 @@ ConcreteNetwork::makeInstance(Cell *cell, Instance * ConcreteNetwork::makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) { return makeConcreteInstance(cell, name, parent); @@ -1255,7 +1258,7 @@ ConcreteNetwork::makeInstance(LibertyCell *cell, Instance * ConcreteNetwork::makeConcreteInstance(ConcreteCell *cell, - const char *name, + std::string_view name, Instance *parent) { ConcreteInstance *cparent = @@ -1387,8 +1390,8 @@ ConcreteNetwork::connect(Instance *inst, void ConcreteNetwork::setAttribute(Instance *inst, - const std::string &key, - const std::string &value) + std::string_view key, + std::string_view value) { ConcreteInstance *cinst = reinterpret_cast(inst); cinst->setAttribute(key, value); @@ -1509,7 +1512,7 @@ ConcreteNetwork::deletePin(Pin *pin) } Net * -ConcreteNetwork::makeNet(const char *name, +ConcreteNetwork::makeNet(std::string_view name, Instance *parent) { ConcreteInstance *cparent = reinterpret_cast(parent); @@ -1608,10 +1611,10 @@ ConcreteNetwork::visitConnectedPins(const Net *net, //////////////////////////////////////////////////////////////// -ConcreteInstance::ConcreteInstance(const char *name, +ConcreteInstance::ConcreteInstance(std::string_view name, ConcreteCell *cell, ConcreteInstance *parent) : - name_(stringCopy(name)), + name_(name), id_(ConcreteNetwork::nextObjectId()), cell_(cell), parent_(parent), @@ -1630,22 +1633,21 @@ ConcreteInstance::initPins() ConcreteInstance::~ConcreteInstance() { - stringDelete(name_); delete children_; delete nets_; } Instance * -ConcreteInstance::findChild(const char *name) const +ConcreteInstance::findChild(std::string_view name) const { if (children_) - return reinterpret_cast(findKey(children_, name)); + return reinterpret_cast(findStringKey(*children_, name)); else return nullptr; } ConcretePin * -ConcreteInstance::findPin(const char *port_name) const +ConcreteInstance::findPin(std::string_view port_name) const { ConcreteCell *ccell = reinterpret_cast(cell_); const ConcretePort *cport = @@ -1669,11 +1671,11 @@ ConcreteInstance::findPin(const Port *port) const } ConcreteNet * -ConcreteInstance::findNet(const char *net_name) const +ConcreteInstance::findNet(std::string_view net_name) const { ConcreteNet *net = nullptr; if (nets_) { - net = findKey(nets_, net_name); + net = findStringKey(*nets_, net_name); // Follow merge pointer to surviving net. if (net) { while (net->mergedInto()) @@ -1716,14 +1718,14 @@ ConcreteInstance::childIterator() const } void -ConcreteInstance::setAttribute(const std::string &key, - const std::string &value) +ConcreteInstance::setAttribute(std::string_view key, + std::string_view value) { - attribute_map_[key] = value; + attribute_map_[std::string(key)] = value; } std::string -ConcreteInstance::getAttribute(const std::string &key) const +ConcreteInstance::getAttribute(std::string_view key) const { const auto &itr = attribute_map_.find(key); if (itr != attribute_map_.end()) @@ -1736,13 +1738,13 @@ ConcreteInstance::addChild(ConcreteInstance *child) { if (children_ == nullptr) children_ = new ConcreteInstanceChildMap; - (*children_)[child->name()] = child; + (*children_)[child->name().data()] = child; } void ConcreteInstance::deleteChild(ConcreteInstance *child) { - children_->erase(child->name()); + children_->erase(child->name().data()); } void @@ -1767,22 +1769,22 @@ ConcreteInstance::addNet(ConcreteNet *net) { if (nets_ == nullptr) nets_ = new ConcreteInstanceNetMap; - (*nets_)[net->name()] = net; + (*nets_)[net->name().data()] = net; } void -ConcreteInstance::addNet(const char *name, +ConcreteInstance::addNet(std::string_view, ConcreteNet *net) { if (nets_ == nullptr) nets_ = new ConcreteInstanceNetMap; - (*nets_)[name] = net; + (*nets_)[net->name().data()] = net; } void ConcreteInstance::deleteNet(ConcreteNet *net) { - nets_->erase(net->name()); + nets_->erase(net->name().data()); } void @@ -1807,7 +1809,7 @@ ConcretePin::ConcretePin(ConcreteInstance *instance, { } -const char * +std::string_view ConcretePin::name() const { return port_->name(); @@ -1821,7 +1823,7 @@ ConcretePin::setVertexId(VertexId id) //////////////////////////////////////////////////////////////// -const char * +std::string_view ConcreteTerm::name() const { ConcretePin *cpin = reinterpret_cast(pin_); @@ -1841,9 +1843,9 @@ ConcreteTerm::ConcreteTerm(ConcretePin *pin, //////////////////////////////////////////////////////////////// -ConcreteNet::ConcreteNet(const char *name, +ConcreteNet::ConcreteNet(std::string_view name, ConcreteInstance *instance) : - name_(stringCopy(name)), + name_(name), id_(ConcreteNetwork::nextObjectId()), instance_(instance), pins_(nullptr), @@ -1852,11 +1854,6 @@ ConcreteNet::ConcreteNet(const char *name, { } -ConcreteNet::~ConcreteNet() -{ - stringDelete(name_); -} - // Merged nets are kept around to serve as name aliases. // Only Instance::findNet and InstanceNetIterator need to know // the net has been merged. @@ -1990,14 +1987,15 @@ ConcreteNetwork::setLinkFunc(LinkNetworkFunc link) } bool -ConcreteNetwork::linkNetwork(const char *top_cell_name, +ConcreteNetwork::linkNetwork(std::string_view top_cell_name, bool make_black_boxes, Report *report) { if (link_func_) { clearConstantNets(); deleteTopInstance(); - top_instance_ = link_func_(top_cell_name, make_black_boxes); + top_instance_ = link_func_(top_cell_name, + make_black_boxes); if (top_instance_) checkNetworkLibertyScenes(); return top_instance_ != nullptr; diff --git a/network/Network.cc b/network/Network.cc index b0a7d6358..ca1f085dc 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -25,6 +25,7 @@ #include "Network.hh" +#include #include #include "ContainerHelpers.hh" @@ -79,7 +80,7 @@ Network::findPortsMatching(const Cell *cell, parseBusName(pattern->pattern(), '[', ']', '\\', is_bus, is_range, bus_name, from, to, subscript_wild); if (is_bus) { - PatternMatch bus_pattern(bus_name.c_str(), pattern); + PatternMatch bus_pattern(bus_name, pattern); CellPortIterator *port_iter = portIterator(cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); @@ -138,7 +139,7 @@ Network::readLibertyAfter(LibertyLibrary *) } LibertyCell * -Network::findLibertyCell(const char *name) const +Network::findLibertyCell(std::string_view name) const { LibertyLibraryIterator *iter = libertyLibraryIterator(); while (iter->hasNext()) { @@ -207,12 +208,12 @@ Network::checkNetworkLibertyScenes() } LibertyLibrary * -Network::findLibertyFilename(const char *filename) +Network::findLibertyFilename(std::string_view filename) { LibertyLibraryIterator *lib_iter = libertyLibraryIterator(); while (lib_iter->hasNext()) { LibertyLibrary *lib = lib_iter->next(); - if (stringEq(lib->filename(), filename)) { + if (lib->filename() == filename) { delete lib_iter; return lib; } @@ -257,7 +258,7 @@ Network::hasMembers(const Port *port) const return isBus(port) || isBundle(port); } -const char * +std::string Network::pathName(const Instance *instance) const { InstanceSeq inst_path; @@ -270,7 +271,7 @@ Network::pathName(const Instance *instance) const if (!inst_path.empty()) path_name += pathDivider(); } - return makeTmpString(path_name); + return path_name; } bool @@ -298,9 +299,9 @@ Network::pathNameCmp(const Instance *inst1, while (!path1.empty() && !path2.empty()) { const Instance *inst1 = path1.back(); const Instance *inst2 = path2.back(); - int cmp = strcmp(name(inst1), name(inst2)); + auto cmp = name(inst1) <=> name(inst2); if (cmp != 0) - return cmp; + return cmp < 0 ? -1 : 1; path1.pop_back(); path2.pop_back(); } @@ -350,27 +351,27 @@ Network::isHierarchical(const Instance *instance) const //////////////////////////////////////////////////////////////// -const char * +std::string Network::name(const Pin *pin) const { return pathName(pin); } -const char * +std::string Network::portName(const Pin *pin) const { return name(port(pin)); } -const char * +std::string Network::pathName(const Pin *pin) const { const Instance *inst = instance(pin); if (inst && inst != topInstance()) { - std::string path_name = pathName(inst); + std::string path_name(pathName(inst)); path_name += pathDivider(); path_name += portName(pin); - return makeTmpString(path_name); + return path_name; } else return portName(pin); @@ -388,8 +389,14 @@ Network::pathNameCmp(const Pin *pin1, const Pin *pin2) const { int inst_cmp = pathNameCmp(instance(pin1), instance(pin2)); - if (inst_cmp == 0) - return strcmp(portName(pin1), portName(pin2)); + if (inst_cmp == 0) { + auto cmp = portName(pin1) <=> portName(pin2); + if (cmp < 0) + return -1; + if (cmp > 0) + return 1; + return 0; + } else return inst_cmp; } @@ -442,18 +449,18 @@ Network::pinLess(const Pin *pin1, //////////////////////////////////////////////////////////////// -const char * +std::string Network::pathName(const Net *net) const { const Instance *inst = instance(net); if (inst && inst != topInstance()) { - std::string path_name = pathName(inst); + std::string path_name(pathName(inst)); path_name += pathDivider(); path_name += name(net); - return makeTmpString(path_name); + return path_name; } else - return name(net); + return std::string(name(net)); } bool @@ -468,8 +475,14 @@ Network::pathNameCmp(const Net *net1, const Net *net2) const { int inst_cmp = pathNameCmp(instance(net1), instance(net2)); - if (inst_cmp == 0) - return strcmp(name(net1), name(net2)); + if (inst_cmp == 0) { + auto cmp = name(net1) <=> name(net2); + if (cmp < 0) + return -1; + if (cmp > 0) + return 1; + return 0; + } else return inst_cmp; } @@ -506,7 +519,7 @@ Network::highestConnectedNet(Net *net) const int level = hierarchyLevel(net1); if (level < highest_level || (level == highest_level - && stringLess(pathName(net1), pathName(highest_net)))) { + && pathName(net1) < pathName(highest_net))) { highest_net = net1; highest_level = level; } @@ -632,19 +645,19 @@ Network::isLatchData(const Pin *pin) const //////////////////////////////////////////////////////////////// -const char * +std::string Network::name(const Term *term) const { return name(pin(term)); } -const char * +std::string Network::pathName(const Term *term) const { return pathName(pin(term)); } -const char * +std::string Network::portName(const Term *term) const { return portName(pin(term)); @@ -652,40 +665,35 @@ Network::portName(const Term *term) const //////////////////////////////////////////////////////////////// -const char * +std::string Network::cellName(const Instance *inst) const { return name(cell(inst)); } Instance * -Network::findInstance(const char *path_name) const +Network::findInstance(std::string_view path_name) const { return findInstanceRelative(topInstance(), path_name); } Instance * Network::findInstanceRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { - char *first, *tail; + std::string first, tail; pathNameFirst(path_name, first, tail); - if (first) { + if (!first.empty()) { Instance *inst1 = findChild(inst, first); - stringDelete(first); - while (inst1 && tail) { - char *next_tail; + while (inst1 && !tail.empty()) { + std::string next_tail; pathNameFirst(tail, first, next_tail); - if (first) { + if (!first.empty()) inst1 = findChild(inst1, first); - stringDelete(first); - } else inst1 = findChild(inst1, tail); - stringDelete(tail); tail = next_tail; } - stringDelete(tail); return inst1; } else @@ -701,7 +709,7 @@ Network::findInstancesMatching(const Instance *context, size_t context_name_length = 0; if (context != topInstance()) // Add one for the trailing divider. - context_name_length = strlen(pathName(context)) + 1; + context_name_length = pathName(context).size() + 1; findInstancesMatching1(context, context_name_length, pattern, matches); } else { @@ -721,9 +729,10 @@ Network::findInstancesMatching1(const Instance *context, InstanceChildIterator *child_iter = childIterator(context); while (child_iter->hasNext()) { Instance *child = child_iter->next(); - const char *child_name = pathName(child); + std::string child_name = pathName(child); // Remove context prefix from the name. - const char *child_context_name = &child_name[context_name_length]; + std::string_view child_context_name = + std::string_view(child_name).substr(context_name_length); if (pattern->match(child_context_name)) matches.push_back(child); if (!isLeaf(child)) @@ -779,27 +788,22 @@ Network::findChildrenMatching(const Instance *parent, } Pin * -Network::findPin(const char *path_name) const +Network::findPin(std::string_view path_name) const { return findPinRelative(topInstance(), path_name); } Pin * Network::findPinRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { - char *inst_path, *port_name; - pathNameLast(path_name, inst_path, port_name); - if (inst_path) { + std::string inst_path, port_name; + std::string path_storage(path_name); + pathNameLast(path_storage, inst_path, port_name); + if (!inst_path.empty()) { Instance *pin_inst = findInstanceRelative(inst, inst_path); - if (pin_inst) { - Pin *pin = findPin(pin_inst, port_name); - stringDelete(inst_path); - stringDelete(port_name); - return pin; - } - stringDelete(inst_path); - stringDelete(port_name); + if (pin_inst) + return findPin(pin_inst, port_name); return nullptr; } else @@ -809,12 +813,12 @@ Network::findPinRelative(const Instance *inst, Pin * Network::findPinLinear(const Instance *instance, - const char *port_name) const + std::string_view port_name) const { InstancePinIterator *pin_iter = pinIterator(instance); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); - if (stringEq(port_name, portName(pin))) { + if (port_name == portName(pin)) { delete pin_iter; return pin; } @@ -838,27 +842,22 @@ Network::findPin(const Instance *instance, } Net * -Network::findNet(const char *path_name) const +Network::findNet(std::string_view path_name) const { return findNetRelative(topInstance(), path_name); } Net * Network::findNetRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { - char *inst_path, *net_name; - pathNameLast(path_name, inst_path, net_name); - if (inst_path) { + std::string inst_path, net_name; + std::string path_storage(path_name); + pathNameLast(path_storage, inst_path, net_name); + if (!inst_path.empty()) { Instance *net_inst = findInstanceRelative(inst, inst_path); - if (net_inst) { - Net *net = findNet(net_inst, net_name); - stringDelete(inst_path); - stringDelete(net_name); - return net; - } - stringDelete(inst_path); - stringDelete(net_name); + if (net_inst) + return findNet(net_inst, net_name); return nullptr; } else @@ -868,12 +867,12 @@ Network::findNetRelative(const Instance *inst, Net * Network::findNetLinear(const Instance *instance, - const char *net_name) const + std::string_view net_name) const { InstanceNetIterator *net_iter = netIterator(instance); while (net_iter->hasNext()) { Net *net = net_iter->next(); - if (stringEq(name(net), net_name)) { + if (name(net) == net_name) { delete net_iter; return net; } @@ -897,16 +896,14 @@ Network::findNetsMatching(const Instance *context, NetSeq &matches) const { if (pattern->hasWildcards()) { - char *inst_path, *net_name; + std::string inst_path, net_name; pathNameLast(pattern->pattern(), inst_path, net_name); - if (inst_path) { + if (!inst_path.empty()) { PatternMatch inst_pattern(inst_path, pattern); PatternMatch net_pattern(net_name, pattern); InstanceSeq insts = findInstancesMatching(context, &inst_pattern); for (const Instance *inst : insts) findNetsMatching(inst, &net_pattern, matches); - stringDelete(inst_path); - stringDelete(net_name); } else // Top level net. @@ -963,16 +960,14 @@ Network::findPinsMatching(const Instance *instance, { PinSeq matches; if (pattern->hasWildcards()) { - char *inst_path, *port_name; + std::string inst_path, port_name; pathNameLast(pattern->pattern(), inst_path, port_name); - if (inst_path) { + if (!inst_path.empty()) { PatternMatch inst_pattern(inst_path, pattern); PatternMatch port_pattern(port_name, pattern); InstanceSeq insts = findInstancesMatching(instance, &inst_pattern); for (const Instance *inst : insts) findInstPinsMatching(inst, &port_pattern, matches); - stringDelete(inst_path); - stringDelete(port_name); } else // Top level pin. @@ -1016,13 +1011,13 @@ Network::findInstPinsHierMatching(const Instance *instance, // Return value. PinSeq &matches) const { - std::string inst_name = name(instance); + std::string inst_name(name(instance)); InstancePinIterator *pin_iter = pinIterator(instance); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); - const char *port_name = name(port(pin)); - std::string pin_name = inst_name + divider_ + port_name; - if (pattern->match(pin_name.c_str())) + std::string port_name = name(port(pin)); + std::string pin_name = inst_name + divider_ + std::string(port_name); + if (pattern->match(pin_name)) matches.push_back(pin); } delete pin_iter; @@ -1601,64 +1596,54 @@ Network::drivers(const Net *net) //////////////////////////////////////////////////////////////// void -Network::pathNameFirst(const char *path_name, - char *&first, - char *&tail) const +Network::pathNameFirst(std::string_view path_name, + std::string &first, + std::string &tail) const { + first.clear(); + tail.clear(); + char escape = pathEscape(); char divider = pathDivider(); - const char *d = strchr(path_name, divider); - // Skip escaped dividers. - while (d != nullptr - && d > path_name - && d[-1] == escape) - d = strchr(d + 1, divider); - if (d) { - first = new char[d - path_name + 1]; - strncpy(first, path_name, d - path_name); - first[d - path_name] = '\0'; - - tail = new char[strlen(d)]; - // Chop off the leading divider. - strcpy(tail, d + 1); - } - else { - // No divider in path_name. - first = nullptr; - tail = nullptr; + size_t i = 0; + while (i < path_name.size()) { + size_t d = path_name.find(divider, i); + while (d != std::string_view::npos && d > 0 + && path_name[d - 1] == escape) + d = path_name.find(divider, d + 1); + if (d != std::string_view::npos) { + first = path_name.substr(0, d); + tail = path_name.substr(d + 1); + return; + } + break; } } void -Network::pathNameLast(const char *path_name, - char *&head, - char *&last) const +Network::pathNameLast(std::string_view path_name, + std::string &head, + std::string &last) const { + head.clear(); + last.clear(); + char escape = pathEscape(); char divider = pathDivider(); - const char *d = strrchr(path_name, divider); - // Search for a non-escaped divider. - if (d) { - while (d > path_name - && (d[0] != divider - || (d[0] == divider - && d > &path_name[1] - && d[-1] == escape))) - d--; - } - if (d && d != path_name) { - head = new char[d - path_name + 1]; - strncpy(head, path_name, d - path_name); - head[d - path_name] = '\0'; - - last = new char[strlen(d)]; - // Chop off the last divider. - strcpy(last, d + 1); - } - else { - // No divider in path_name. - head = nullptr; - last = nullptr; + + size_t div_pos = path_name.rfind(divider); + size_t path_end = path_name.size(); + while (div_pos > 0) { + if (div_pos == std::string_view::npos) + return; + if (path_name[div_pos - 1] != escape) { + // Found the last non-escaped divider. + head = path_name.substr(0, div_pos); + last = path_name.substr(div_pos + 1); + return; + } + path_end = div_pos - 1; + div_pos = path_name.rfind(divider, path_end); } } diff --git a/network/Network.i b/network/Network.i index c07f6de8e..6af7c8a36 100644 --- a/network/Network.i +++ b/network/Network.i @@ -24,8 +24,12 @@ %module network +%include + %{ #include "Network.hh" +#include "StringUtil.hh" +#include %} //////////////////////////////////////////////////////////////// @@ -252,12 +256,12 @@ find_cells_matching(const char *pattern, } void -set_cmd_namespace_cmd(const char *namespc) +set_cmd_namespace_cmd(std::string namespc) { Sta *sta = Sta::sta(); - if (stringEq(namespc, "sdc")) + if (namespc == "sdc") sta->setCmdNamespace(CmdNamespace::sdc); - else if (stringEq(namespc, "sta")) + else if (namespc == "sta") sta->setCmdNamespace(CmdNamespace::sta); else sta->report()->warn(2120, "unknown namespace"); @@ -284,16 +288,16 @@ leaf_instance_iterator() return network->leafInstanceIterator(); } -const char * +std::string_view port_direction(const Port *port) { return Sta::sta()->ensureLinked()->direction(port)->name(); } -const char * +std::string pin_direction(const Pin *pin) { - return Sta::sta()->ensureLinked()->direction(pin)->name(); + return std::string(Sta::sta()->ensureLinked()->direction(pin)->name()); } PortSeq @@ -542,7 +546,7 @@ port_location(const Port *port) //////////////////////////////////////////////////////////////// %extend Library { -const char *name() +std::string name() { return Sta::sta()->ensureLinked()->name(self); } @@ -574,13 +578,13 @@ void finish() { delete self; } } // LibraryIterator methods %extend Cell { -const char *name() { return Sta::sta()->cmdNetwork()->name(self); } +std::string name() { return Sta::sta()->cmdNetwork()->name(self); } Library *library() { return Sta::sta()->cmdNetwork()->library(self); } LibertyCell *liberty_cell() { return Sta::sta()->cmdNetwork()->libertyCell(self); } bool is_leaf() { return Sta::sta()->cmdNetwork()->isLeaf(self); } CellPortIterator * port_iterator() { return Sta::sta()->cmdNetwork()->portIterator(self); } -std::string +std::string get_attribute(const char *key) { return Sta::sta()->cmdNetwork()->getAttribute(self, key); @@ -612,7 +616,7 @@ void finish() { delete self; } } // CellPortIterator methods %extend Port { -const char *bus_name() { return Sta::sta()->ensureLinked()->busName(self); } +std::string bus_name() { return Sta::sta()->ensureLinked()->busName(self); } Cell *cell() { return Sta::sta()->ensureLinked()->cell(self); } LibertyPort *liberty_port() { return Sta::sta()->ensureLibLinked()->libertyPort(self); } bool is_bus() { return Sta::sta()->ensureLinked()->isBus(self); } @@ -678,7 +682,7 @@ void finish() { delete self; } } // InstanceNetIterator methods %extend Pin { -const char *port_name() { return Sta::sta()->ensureLinked()->portName(self); } +std::string port_name() { return Sta::sta()->ensureLinked()->portName(self); } Instance *instance() { return Sta::sta()->ensureLinked()->instance(self); } Net *net() { return Sta::sta()->ensureLinked()->net(self); } Port *port() { return Sta::sta()->ensureLinked()->port(self); } diff --git a/network/NetworkCmp.cc b/network/NetworkCmp.cc index 7dbc9511f..cda43af73 100644 --- a/network/NetworkCmp.cc +++ b/network/NetworkCmp.cc @@ -41,7 +41,7 @@ bool PortNameLess::operator()(const Port *port1, const Port *port2) const { - return stringLess(network_->name(port1), network_->name(port2)); + return network_->name(port1) < network_->name(port2); } PinPathNameLess::PinPathNameLess(const Network *network) : diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index 4cdef63ee..e36b0acd0 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -30,13 +30,6 @@ namespace sta { -static std::string -escapeDividers(const char *token, - const Network *network); -static std::string -escapeBrackets(const char *token, - const Network *network); - NetworkNameAdapter::NetworkNameAdapter(Network *network) : NetworkEdit(), network_(network), @@ -45,7 +38,7 @@ NetworkNameAdapter::NetworkNameAdapter(Network *network) : } bool -NetworkNameAdapter::linkNetwork(const char *top_cell_name, +NetworkNameAdapter::linkNetwork(std::string_view top_cell_name, bool make_black_boxes, Report *report) { @@ -77,24 +70,24 @@ NetworkNameAdapter::libertyLibraryIterator() const } Library * -NetworkNameAdapter::findLibrary(const char *name) +NetworkNameAdapter::findLibrary(std::string_view name) { return network_->findLibrary(name); } LibertyLibrary * -NetworkNameAdapter::findLiberty(const char *name) +NetworkNameAdapter::findLiberty(std::string_view name) { return network_->findLiberty(name); } LibertyLibrary * -NetworkNameAdapter::findLibertyFilename(const char *filename) +NetworkNameAdapter::findLibertyFilename(std::string_view filename) { return network_->findLibertyFilename(filename); } -const char * +std::string NetworkNameAdapter::name(const Library *library) const { return network_->name(library); @@ -108,7 +101,7 @@ NetworkNameAdapter::id(const Library *library) const Cell * NetworkNameAdapter::findCell(const Library *library, - const char *name) const + std::string_view name) const { return network_->findCell(library, name); } @@ -122,7 +115,7 @@ NetworkNameAdapter::findCellsMatching(const Library *library, //////////////////////////////////////////////////////////////// -const char * +std::string NetworkNameAdapter::name(const Cell *cell) const { return network_->name(cell); @@ -136,7 +129,7 @@ NetworkNameAdapter::id(const Cell *cell) const std::string NetworkNameAdapter::getAttribute(const Cell *cell, - const std::string &key) const + std::string_view key) const { return network_->getAttribute(cell, key); } @@ -153,8 +146,8 @@ NetworkNameAdapter::library(const Cell *cell) const return network_->library(cell); } -const char * -NetworkNameAdapter::filename(const Cell *cell) +std::string_view +NetworkNameAdapter::filename(const Cell *cell) const { return network_->filename(cell); } @@ -185,7 +178,7 @@ NetworkNameAdapter::cell(const LibertyCell *cell) const Port * NetworkNameAdapter::findPort(const Cell *cell, - const char *name) const + std::string_view name) const { return network_->findPort(cell, name); } @@ -223,7 +216,7 @@ NetworkNameAdapter::portBitCount(const Cell *cell) const //////////////////////////////////////////////////////////////// -const char * +std::string NetworkNameAdapter::name(const Port *port) const { return network_->name(port); @@ -288,7 +281,7 @@ NetworkNameAdapter::isBus(const Port *port) const return network_->isBus(port); } -const char * +std::string NetworkNameAdapter::busName(const Port *port) const { return network_->busName(port); @@ -354,7 +347,7 @@ NetworkNameAdapter::cell(const Instance *instance) const std::string NetworkNameAdapter::getAttribute(const Instance *inst, - const std::string &key) const + std::string_view key) const { return network_->getAttribute(inst, key); } @@ -537,15 +530,15 @@ NetworkNameAdapter::isEditable() const LibertyLibrary * -NetworkNameAdapter::makeLibertyLibrary(const char *name, - const char *filename) +NetworkNameAdapter::makeLibertyLibrary(std::string_view name, + std::string_view filename) { return network_edit_->makeLibertyLibrary(name, filename); } Instance * NetworkNameAdapter::makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) { return network_edit_->makeInstance(cell, name, parent); @@ -571,7 +564,7 @@ NetworkNameAdapter::mergedInto(Net *net) } Net * -NetworkNameAdapter::makeNet(const char *name, +NetworkNameAdapter::makeNet(std::string_view name, Instance *parent) { return network_edit_->makeNet(name, parent); @@ -639,7 +632,7 @@ SdcNetwork::SdcNetwork(Network *network) : // Translate sta namespace to sdc namespace. // Remove all escapes. -const char * +std::string SdcNetwork::staToSdc(std::string_view sta_name) const { char escape = pathEscape(); @@ -660,12 +653,12 @@ SdcNetwork::staToSdc(std::string_view sta_name) const // Non escape. sdc_name += ch; } - return makeTmpString(sdc_name); + return sdc_name; } Port * SdcNetwork::findPort(const Cell *cell, - const char *name) const + std::string_view name) const { Port *port = network_->findPort(cell, name); if (port == nullptr) { @@ -675,22 +668,22 @@ SdcNetwork::findPort(const Cell *cell, int index; parseBusName(name, '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { - std::string escaped1 = escapeBrackets(name, this); - port = network_->findPort(cell, escaped1.c_str()); + std::string escaped1 = escapeBrackets(std::string(name), this); + port = network_->findPort(cell, escaped1); if (port == nullptr) { // Try escaping base foo\[0\][1] - std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); + std::string escaped_bus_name = escapeBrackets(bus_name, this); std::string escaped2 = escaped_bus_name + '[' + std::to_string(index) + ']'; - port = network_->findPort(cell, escaped2.c_str()); + port = network_->findPort(cell, escaped2); } } else { // Try escaping brackets foo\[0\].bar - std::string escaped = escapeBrackets(name, this); - port = network_->findPort(cell, escaped.c_str()); + std::string escaped = escapeBrackets(std::string(name), this); + port = network_->findPort(cell, escaped); } } return port; @@ -710,71 +703,71 @@ SdcNetwork::findPortsMatching(const Cell *cell, is_bus, bus_name, index); if (is_bus) { std::string escaped1 = escapeBrackets(pattern->pattern(), this); - PatternMatch escaped_pattern1(escaped1.c_str(), pattern); + PatternMatch escaped_pattern1(escaped1, pattern); matches = network_->findPortsMatching(cell, &escaped_pattern1); if (matches.empty()) { // Try escaping base foo\[0\][1] - std::string escaped_name = escapeBrackets(bus_name.c_str(), this); + std::string escaped_name = escapeBrackets(bus_name, this); escaped_name += '['; escaped_name += std::to_string(index); escaped_name += ']'; - PatternMatch escaped_pattern2(escaped_name.c_str(), pattern); + PatternMatch escaped_pattern2(escaped_name, pattern); matches = network_->findPortsMatching(cell, &escaped_pattern2); } } else { // Try escaping brackets foo\[0\].bar std::string escaped = escapeBrackets(pattern->pattern(), this); - PatternMatch escaped_pattern(escaped.c_str(), pattern); + PatternMatch escaped_pattern(escaped, pattern); matches = network_->findPortsMatching(cell, &escaped_pattern); } } return matches; } -const char * +std::string SdcNetwork::name(const Port *port) const { return staToSdc(network_->name(port)); } -const char * +std::string SdcNetwork::busName(const Port *port) const { return staToSdc(network_->busName(port)); } -const char * +std::string SdcNetwork::name(const Instance *instance) const { return staToSdc(network_->name(instance)); } -const char * +std::string SdcNetwork::pathName(const Instance *instance) const { return staToSdc(network_->pathName(instance)); } -const char * +std::string SdcNetwork::pathName(const Pin *pin) const { return staToSdc(network_->pathName(pin)); } -const char * +std::string SdcNetwork::portName(const Pin *pin) const { return staToSdc(network_->portName(pin)); } -const char * +std::string SdcNetwork::name(const Net *net) const { return staToSdc(network_->name(net)); } -const char * +std::string SdcNetwork::pathName(const Net *net) const { return staToSdc(network_->pathName(net)); @@ -783,9 +776,9 @@ SdcNetwork::pathName(const Net *net) const //////////////////////////////////////////////////////////////// Instance * -SdcNetwork::findInstance(const char *path_name) const +SdcNetwork::findInstance(std::string_view path_name) const { - const char *child_name; + std::string child_name; Instance *parent; parsePath(path_name, parent, child_name); if (parent == nullptr) @@ -793,22 +786,22 @@ SdcNetwork::findInstance(const char *path_name) const Instance *child = findChild(parent, child_name); if (child == nullptr) { std::string escaped_name = escapeDividers(child_name, this); - child = findChild(parent, escaped_name.c_str()); + child = findChild(parent, escaped_name); } return child; } Instance * SdcNetwork::findInstanceRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { Instance *inst1 = network_->findInstanceRelative(inst, path_name); if (inst1 == nullptr) { - std::string path_name1 = escapeBrackets(path_name, this); - inst1 = network_->findInstanceRelative(inst, path_name1.c_str()); + std::string path_name1 = escapeBrackets(std::string(path_name), this); + inst1 = network_->findInstanceRelative(inst, path_name1); if (inst1 == nullptr) { - std::string path_name2 = escapeDividers(path_name1.c_str(), network_); - inst1 = network_->findInstanceRelative(inst, path_name2.c_str()); + std::string path_name2 = escapeDividers(path_name1, network_); + inst1 = network_->findInstanceRelative(inst, path_name2); } } return inst1; @@ -840,12 +833,12 @@ SdcNetwork::findInstancesMatching1(const Instance *context, Instance * SdcNetwork::findChild(const Instance *parent, - const char *name) const + std::string_view name) const { Instance *child = network_->findChild(parent, name); if (child == nullptr) { - std::string escaped = escapeBrackets(name, this); - child = network_->findChild(parent, escaped.c_str()); + std::string escaped = escapeBrackets(std::string(name), this); + child = network_->findChild(parent, escaped); } return child; } @@ -853,9 +846,9 @@ SdcNetwork::findChild(const Instance *parent, //////////////////////////////////////////////////////////////// Net * -SdcNetwork::findNet(const char *path_name) const +SdcNetwork::findNet(std::string_view path_name) const { - const char *net_name; + std::string net_name; Instance *inst; parsePath(path_name, inst, net_name); if (inst == nullptr) @@ -865,33 +858,33 @@ SdcNetwork::findNet(const char *path_name) const Net * SdcNetwork::findNet(const Instance *instance, - const char *net_name) const + std::string_view net_name) const { Net *net = network_->findNet(instance, net_name); if (net == nullptr) { - std::string net_name1 = escapeBrackets(net_name, this); - std::string net_name2 = escapeDividers(net_name1.c_str(), network_); - net = network_->findNet(instance, net_name2.c_str()); + std::string net_name1 = escapeBrackets(std::string(net_name), this); + std::string net_name2 = escapeDividers(net_name1, network_); + net = network_->findNet(instance, net_name2); } return net; } Net * SdcNetwork::findNetRelative(const Instance *inst, - const char *path_name) const + std::string_view path_name) const { Net *net = network_->findNetRelative(inst, path_name); if (net == nullptr) { - std::string path_name1 = escapeDividers(path_name, network_); - net = network_->findNetRelative(inst, path_name1.c_str()); + std::string path_name1 = escapeDividers(std::string(path_name), network_); + net = network_->findNetRelative(inst, path_name1); if (net == nullptr) { - std::string path_name2 = escapeBrackets(path_name, network_); - net = network_->findNetRelative(inst, path_name2.c_str()); + std::string path_name2 = escapeBrackets(std::string(path_name), network_); + net = network_->findNetRelative(inst, path_name2); if (net == nullptr) { - std::string path_name3 = escapeDividers(path_name2.c_str(), network_); - net = network_->findNetRelative(inst, path_name3.c_str()); + std::string path_name3 = escapeDividers(path_name2, network_); + net = network_->findNetRelative(inst, path_name3); } } } @@ -923,12 +916,12 @@ SdcNetwork::findInstNetsMatching(const Instance *instance, if (matches.empty()) { // Look for matches after escaping path dividers. std::string escaped_pattern = escapeDividers(pattern->pattern(), this); - const PatternMatch escaped_dividers(escaped_pattern.c_str(), pattern); + const PatternMatch escaped_dividers(escaped_pattern, pattern); network_->findInstNetsMatching(instance, &escaped_dividers, matches); if (matches.empty()) { // Look for matches after escaping brackets. std::string escaped_pattern2 = escapeBrackets(pattern->pattern(),this); - const PatternMatch escaped_brkts(escaped_pattern2.c_str(), pattern); + const PatternMatch escaped_brkts(escaped_pattern2, pattern); network_->findInstNetsMatching(instance, &escaped_brkts, matches); } } @@ -937,9 +930,9 @@ SdcNetwork::findInstNetsMatching(const Instance *instance, //////////////////////////////////////////////////////////////// Pin * -SdcNetwork::findPin(const char *path_name) const +SdcNetwork::findPin(std::string_view path_name) const { - const char *port_name; + std::string port_name; Instance *inst; parsePath(path_name, inst, port_name); if (inst == nullptr) @@ -949,7 +942,7 @@ SdcNetwork::findPin(const char *path_name) const Pin * SdcNetwork::findPin(const Instance *instance, - const char *port_name) const + std::string_view port_name) const { Pin *pin = network_->findPin(instance, port_name); if (pin == nullptr) { @@ -960,22 +953,22 @@ SdcNetwork::findPin(const Instance *instance, parseBusName(port_name, '[', ']', pathEscape(), is_bus, bus_name, index); if (is_bus) { - std::string escaped1 = escapeBrackets(port_name, this); - pin = network_->findPin(instance, escaped1.c_str()); + std::string escaped1 = escapeBrackets(std::string(port_name), this); + pin = network_->findPin(instance, escaped1); if (pin == nullptr) { // Try escaping base foo\[0\][1] - std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this); + std::string escaped_bus_name = escapeBrackets(bus_name, this); std::string escaped2 = escaped_bus_name + '[' + std::to_string(index) + ']'; - pin = network_->findPin(instance, escaped2.c_str()); + pin = network_->findPin(instance, escaped2); } } else { // Try escaping port brackets foo\[0\].bar - std::string escaped = escapeBrackets(port_name, this); - pin = network_->findPin(instance, escaped.c_str()); + std::string escaped = escapeBrackets(std::string(port_name), this); + pin = network_->findPin(instance, escaped); } } return pin; @@ -987,7 +980,7 @@ SdcNetwork::findPinsMatching(const Instance *instance, const PatternMatch *pattern) const { PinSeq matches; - if (stringEq(pattern->pattern(), "*")) { + if (pattern->pattern() == "*") { // Pattern of '*' matches all child instance pins. InstanceChildIterator *child_iter = childIterator(instance); while (child_iter->hasNext()) { @@ -1022,11 +1015,12 @@ SdcNetwork::visitPinTail(const Instance *instance, CellPortIterator *port_iter = network_->portIterator(cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); - const char *port_name = network_->name(port); + std::string port_name = network_->name(port); if (network_->hasMembers(port)) { bool bus_matches = tail->match(port_name); if (!bus_matches) { - std::string escaped_name = escapeDividers(port_name, network_); + std::string escaped_name = escapeDividers(std::string(port_name), + network_); bus_matches = tail->match(escaped_name); } PortMemberIterator *member_iter = network_->memberIterator(port); @@ -1039,10 +1033,11 @@ SdcNetwork::visitPinTail(const Instance *instance, found_match = true; } else { - const char *member_name = network_->name(member_port); + std::string member_name = network_->name(member_port); bool member_matches = tail->match(member_name); if (!member_matches) { - std::string escaped_name = escapeDividers(member_name, network_); + std::string escaped_name = escapeDividers(std::string(member_name), + network_); member_matches = tail->match(escaped_name); } if (member_matches) { @@ -1057,7 +1052,8 @@ SdcNetwork::visitPinTail(const Instance *instance, else { bool port_matches = tail->match(port_name); if (!port_matches) { - std::string escaped_name = escapeDividers(port_name, network_); + std::string escaped_name = escapeDividers(std::string(port_name), + network_); port_matches = tail->match(escaped_name); } if (port_matches) { @@ -1076,19 +1072,19 @@ SdcNetwork::visitPinTail(const Instance *instance, Instance * SdcNetwork::makeInstance(LibertyCell *cell, - const char *name, + std::string_view name, Instance *parent) { - std::string escaped_name = escapeDividers(name, this); - return network_edit_->makeInstance(cell, escaped_name.c_str(), parent); + std::string escaped_name = escapeDividers(std::string(name), this); + return network_edit_->makeInstance(cell, escaped_name, parent); } Net * -SdcNetwork::makeNet(const char *name, +SdcNetwork::makeNet(std::string_view name, Instance *parent) { - std::string escaped_name = escapeDividers(name, this); - return network_edit_->makeNet(escaped_name.c_str(), parent); + std::string escaped_name = escapeDividers(std::string(name), this); + return network_edit_->makeNet(escaped_name, parent); } //////////////////////////////////////////////////////////////// @@ -1101,10 +1097,10 @@ SdcNetwork::makeNet(const char *name, // a\/b // a\/b\/c void -SdcNetwork::parsePath(const char *path, +SdcNetwork::parsePath(std::string_view path, // Return values. Instance *&inst, - const char *&path_tail) const + std::string &path_tail) const { int divider_count, path_length; scanPath(path, divider_count, path_length); @@ -1118,7 +1114,7 @@ SdcNetwork::parsePath(const char *path, // Scan the path for unescaped dividers. void -SdcNetwork::scanPath(const char *path, +SdcNetwork::scanPath(std::string_view path, // Return values. // Unescaped divider count. int ÷r_count, @@ -1126,12 +1122,12 @@ SdcNetwork::scanPath(const char *path, { divider_count = 0; path_length = 0; - for (const char *s = path; *s; s++) { - char ch = *s; + for (size_t i = 0; i < path.size(); i++) { + char ch = path[i]; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. - if (s[1] != '\0') { - s++; + if (i != path.size() - 1) { + i++; path_length++; } } @@ -1142,37 +1138,37 @@ SdcNetwork::scanPath(const char *path, } void -SdcNetwork::parsePath(const char *path, +SdcNetwork::parsePath(std::string_view path, int divider_count, int path_length, // Return values. Instance *&inst, - const char *&path_tail) const + std::string &path_tail) const { Instance *parent = topInstance(); std::string inst_path; - // Leave room to escape all the dividers and '\0'. - inst_path.reserve(path_length + divider_count + 1); + // Leave room to escape all the dividers. + inst_path.reserve(path_length + divider_count); inst = nullptr; path_tail = path; - for (const char *s = path; *s; s++) { - char ch = *s; + for (size_t i = 0; i < path.size(); i++) { + char ch = path[i]; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. - if (s[1] != '\0') { + if (i < path.size() - 1) { inst_path += ch; - inst_path += s[1]; - s++; + inst_path += path[i + 1]; + i++; } } else if (ch == divider_) { - Instance *child = findChild(parent, inst_path.c_str()); + Instance *child = findChild(parent, inst_path); if (child) { // Found an instance for the sub-path up to this divider. parent = inst = child; // Reset the instance path. inst_path.clear(); - path_tail = s + 1; + path_tail = path.substr(i + 1); } else { // No match for sub-path. Escape the divider and keep looking. @@ -1206,29 +1202,30 @@ SdcNetwork::visitMatches(const Instance *parent, inst_path.reserve(path_length + divider_count + 1); bool has_brkts = false; bool found_match = false; - for (const char *s = pattern->pattern(); *s; s++) { - char ch = *s; + const std::string &pattern_str = pattern->pattern(); + for (size_t i = 0; i < pattern_str.size(); i++) { + char ch = pattern_str[i]; if (ch == escape_) { // Make sure we don't skip the null if escape is the last char. - if (s[1] != '\0') { + if (i < pattern_str.size() - 1) { inst_path += ch; - inst_path += s[1]; - s++; + inst_path += pattern_str[i + 1]; + i++; } } else if (ch == divider_) { - PatternMatch matcher(inst_path.c_str(), pattern); + PatternMatch matcher(inst_path, 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.c_str(), this); + std::string escaped_brkts = escapeBrackets(inst_path, this); const PatternMatch escaped_pattern(escaped_brkts, pattern); network_->findChildrenMatching(parent, &escaped_pattern, matches); } if (!matches.empty()) { // Found instance matches for the sub-path up to this divider. - const PatternMatch tail_pattern(s + 1, pattern); + const PatternMatch tail_pattern(pattern_str.substr(i + 1), pattern); for (const Instance *match : matches) // Recurse to save the iterator state so we can iterate over // multiple nested partial matches. @@ -1245,11 +1242,11 @@ SdcNetwork::visitMatches(const Instance *parent, } } if (!found_match) { - PatternMatch tail_pattern(inst_path.c_str(), pattern); + PatternMatch tail_pattern(inst_path, 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.c_str(), this); + std::string escaped_path = escapeBrackets(inst_path, this); const PatternMatch escaped_tail(escaped_path, pattern); found_match |= visit_tail(parent, &escaped_tail); } @@ -1259,19 +1256,19 @@ SdcNetwork::visitMatches(const Instance *parent, //////////////////////////////////////////////////////////////// -static std::string -escapeDividers(const char *token, +std::string +escapeDividers(std::string_view name, const Network *network) { - return escapeChars(token, network->pathDivider(), '\0', + return escapeChars(name, network->pathDivider(), '\0', network->pathEscape()); } -static std::string -escapeBrackets(const char *token, +std::string +escapeBrackets(std::string_view name, const Network *network) { - return escapeChars(token, '[', ']', network->pathEscape()); + return escapeChars(name, '[', ']', network->pathEscape()); } } // namespace diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index 702ca6d2e..b12d9c3ae 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -58,9 +58,9 @@ netVerilogName(std::string sta_name) bool is_bus; std::string bus_name; int index; - parseBusName(sta_name.c_str(), '[', ']', verilog_escape, is_bus, bus_name, index); + parseBusName(sta_name, '[', ']', verilog_escape, is_bus, bus_name, index); if (is_bus) { - std::string bus_vname = staToVerilog(bus_name.c_str()); + std::string bus_vname = staToVerilog(bus_name); std::string vname = bus_vname + '[' + std::to_string(index) + ']'; return vname; } diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index e85036eea..d85b4fa61 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -406,14 +406,14 @@ ConcreteParasiticNode::incrCapacitance(float cap) cap_ += cap; } -const char * +std::string ConcreteParasiticNode::name(const Network *network) const { if (is_net_) { std::string name = std::string(network->pathName(net_pin_.net_)) + ':' + std::to_string(id_); - return makeTmpString(name); + return name; } else return network->pathName(net_pin_.pin_); @@ -1312,7 +1312,7 @@ ConcreteParasitics::capacitors(const Parasitic *parasitic) const } -const char * +std::string ConcreteParasitics::name(const ParasiticNode *node) const { const ConcreteParasiticNode *cnode = diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index f033ae357..a6486736c 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -24,9 +24,10 @@ #pragma once -#include #include #include +#include +#include #include "MinMax.hh" #include "Parasitics.hh" @@ -143,7 +144,7 @@ public: ParasiticNodeSeq nodes(const Parasitic *parasitic) const override; void incrCap(ParasiticNode *node, float cap) override; - const char *name(const ParasiticNode *node) const override; + std::string name(const ParasiticNode *node) const override; const Pin *pin(const ParasiticNode *node) const override; const Net *net(const ParasiticNode *node, const Network *network) const override; diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh index ef363cab2..168926de4 100644 --- a/parasitics/ConcreteParasiticsPvt.hh +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -26,6 +26,7 @@ #include #include +#include #include "Parasitics.hh" @@ -272,7 +273,7 @@ public: ConcreteParasiticNode(const Pin *pin, bool is_external); float capacitance() const { return cap_; } - const char *name(const Network *network) const; + std::string name(const Network *network) const; const Net *net(const Network *network) const; unsigned id() const { return id_; } bool isExternal() const { return is_external_; } diff --git a/parasitics/SpefLex.ll b/parasitics/SpefLex.ll index bf7bbdde5..58975b840 100644 --- a/parasitics/SpefLex.ll +++ b/parasitics/SpefLex.ll @@ -163,7 +163,7 @@ INDEX "*"{POS_INTEGER} "\"" { BEGIN INITIAL; - yylval->string = sta::stringCopy(token_.c_str()); + yylval->emplace(std::move(token_)); return token::QSTRING; } @@ -179,27 +179,27 @@ INDEX "*"{POS_INTEGER} {BLANK}*\n { loc->lines(); loc->step(); } {INTEGER} { - yylval->integer = atoi(yytext); + yylval->emplace(atoi(yytext)); return token::INTEGER; } {FLOAT} { - yylval->number = static_cast(atof(yytext)); + yylval->emplace(static_cast(atof(yytext))); return token::FLOAT; } {IDENT} { - yylval->string = reader_->translated(yytext); + yylval->emplace(reader_->translated(yytext)); return token::IDENT; } {PATH}|{NAME_PAIR} { - yylval->string = reader_->translated(yytext); + yylval->emplace(reader_->translated(yytext)); return token::NAME; } {INDEX} { - yylval->string = sta::stringCopy(yytext); + yylval->emplace(yytext); return token::INDEX; } diff --git a/parasitics/SpefNamespace.cc b/parasitics/SpefNamespace.cc index 2822875cf..4a1d70638 100644 --- a/parasitics/SpefNamespace.cc +++ b/parasitics/SpefNamespace.cc @@ -26,97 +26,92 @@ #include #include +#include namespace sta { -char * -spefToSta(const char *token, +std::string +spefToSta(std::string_view spef_name, char spef_divider, char path_divider, char path_escape) { const char spef_escape = '\\'; - char *trans_token = new char[strlen(token) + 1]; - char *t = trans_token; - - for (const char *s = token; *s ; s++) { - char ch = *s; + std::string sta_name; + for (size_t i = 0; i < spef_name.size(); i++) { + char ch = spef_name[i]; if (ch == spef_escape) { - char next_ch = s[1]; + char next_ch = spef_name[i + 1]; if (next_ch == spef_divider) { // Translate spef escape to network escape. - *t++ = path_escape; + sta_name += path_escape; // Translate spef divider to network divider. - *t++ = path_divider; + sta_name += path_divider; } else if (next_ch == '[' || next_ch == ']' || next_ch == spef_escape) { // Translate spef escape to network escape. - *t++ = path_escape; - *t++ = next_ch; + sta_name += path_escape; + sta_name += next_ch; } else // No need to escape other characters. - *t++ = next_ch; - s++; + sta_name += next_ch; + i++; } else if (ch == spef_divider) // Translate spef divider to network divider. - *t++ = path_divider; + sta_name += path_divider; else // Just the normal noises. - *t++ = ch; + sta_name += ch; } - *t++ = '\0'; - return trans_token; + return sta_name; } -char * -staToSpef(const char *token, +std::string +staToSpef(std::string_view sta_name, char spef_divider, char path_divider, char path_escape) { const char spef_escape = '\\'; - char *trans_token = new char[strlen(token) + 1]; - char *t = trans_token; - - for (const char *s = token; *s ; s++) { - char ch = *s; + std::string spef_name; + for (size_t i = 0; i < sta_name.size(); i++) { + char ch = sta_name[i]; if (ch == path_escape) { - char next_ch = s[1]; + char next_ch = sta_name[i + 1]; if (next_ch == path_divider) { // Translate network escape to spef escape. - *t++ = spef_escape; + spef_name += spef_escape; // Translate network divider to spef divider. - *t++ = spef_divider; + spef_name += spef_divider; } else if (next_ch == '[' || next_ch == ']') { // Translate network escape to spef escape. - *t++ = spef_escape; - *t++ = next_ch; + spef_name += spef_escape; + spef_name += next_ch; } else // No need to escape other characters. - *t++ = next_ch; - s++; + spef_name += next_ch; + i++; } else if (ch == path_divider) // Translate network divider to spef divider. - *t++ = spef_divider; + spef_name += spef_divider; else if (!(isdigit(ch) || isalpha(ch) || ch == '_')) { // Escape non-alphanum characters. - *t++ = spef_escape; - *t++ = ch; + spef_name += spef_escape; + spef_name += ch; } else // Just the normal noises. - *t++ = ch; + spef_name += ch; } - *t++ = '\0'; - return trans_token; + return spef_name; } } // namespace diff --git a/parasitics/SpefNamespace.hh b/parasitics/SpefNamespace.hh index 042019f4a..4ec55f29a 100644 --- a/parasitics/SpefNamespace.hh +++ b/parasitics/SpefNamespace.hh @@ -24,18 +24,22 @@ #pragma once +#include + namespace sta { // Translate from spf/spef namespace to sta namespace. -// Caller owns the result string. -char * -spefToSta(const char *token, char spef_divider, - char path_escape, char path_divider); -// Translate from sta namespace to spf/spef namespace. -// Caller owns the result string. -char * -staToSpef(const char *token, char spef_divider, - char path_divider, char path_escape); +std::string +spefToSta(std::string_view spef_name, + char spef_divider, + char path_divider, + char path_escape); +// Translate from sta namespace to spf/spef namespace. +std::string +staToSpef(std::string_view sta_name, + char spef_divider, + char path_divider, + char path_escape); } // namespace diff --git a/parasitics/SpefParse.yy b/parasitics/SpefParse.yy index a88e2d265..e527d781c 100755 --- a/parasitics/SpefParse.yy +++ b/parasitics/SpefParse.yy @@ -23,11 +23,11 @@ // This notice may not be removed or altered from any source distribution. %{ -#include +#include +#include #include "Report.hh" #include "StringUtil.hh" -#include "StringUtil.hh" #include "parasitics/SpefReaderPvt.hh" #include "parasitics/SpefScanner.hh" @@ -41,10 +41,19 @@ void sta::SpefParse::error(const location_type &loc, const std::string &msg) { - reader->report()->fileError(1670,reader->filename(), loc.begin. line, "{}", msg); + reader->report()->fileError(1670, reader->filename(), loc.begin.line, "{}", msg); } %} +%code requires { +#include + +namespace sta { +// Bison's C++ variant skeleton cannot use void as a semantic type. +struct SpefParseVoid {}; +} +} + %require "3.2" %skeleton "lalr1.cc" %debug @@ -55,19 +64,11 @@ sta::SpefParse::error(const location_type &loc, %parse-param { SpefScanner *scanner } %parse-param { SpefReader *reader } %define api.parser.class {SpefParse} +%define api.value.type variant -%union { - char ch; - char *string; - int integer; - float number; - sta::StringSeq *std_string_seq; - sta::PortDirection *port_dir; - sta::SpefRspfPi *pi; - sta::SpefTriple *triple; - sta::Pin *pin; - sta::Net *net; -} +%token INTEGER +%token FLOAT +%token QSTRING INDEX IDENT NAME %token SPEF DESIGN DATE VENDOR PROGRAM DESIGN_FLOW %token PVERSION DIVIDER DELIMITER @@ -78,48 +79,42 @@ sta::SpefParse::error(const location_type &loc, %token CONN CAP RES INDUC KW_P KW_I KW_N DRIVER CELL C2_R1_C1 LOADS %token RC KW_Q KW_K -%token INTEGER FLOAT QSTRING INDEX IDENT NAME +%type conf cap_id res_id induc_id cap_elem cap_elems +%type res_elem res_elems induc_elem induc_elems +%type pos_integer -%type INTEGER -%type FLOAT -%type QSTRING INDEX IDENT NAME +%type number pos_number threshold +%type real_component imaginary_component pole poles +%type residue residues complex_par_value cnumber -%type conf cap_id res_id induc_id cap_elem cap_elems -%type res_elem res_elems induc_elem induc_elems -%type pos_integer +%nterm name_map_entries name_map_entry net_names +%nterm net_name port_entries port_entry pport_entries pport_entry +%nterm pport_name pexternal_connection -%type number pos_number threshold -%type real_component imaginary_component pole poles -%type residue residues complex_par_value cnumber +%type name_or_index inst_name +%type mapped_item +%type physical_inst port_name pport +%type entity external_connection +%type cell_type +%type driver_cell pnet_ref +%type internal_pdspf_node +%type parasitic_node internal_parasitic_node -%type name_or_index net_name net_names inst_name -%type name_map_entries name_map_entry mapped_item -%type physical_inst port_name pport_name port_entry pport_entry -%type port_entries pport_entries pport -%type entity external_connection -%type cell_type -%type driver_cell pnet_ref -%type pexternal_connection internal_pdspf_node -%type parasitic_node internal_parasitic_node +%type hchar suffix_bus_delim prefix_bus_delim -%type hchar suffix_bus_delim prefix_bus_delim +%type qstrings +%type direction -%type qstrings -%type direction +%type par_value total_cap -%type par_value total_cap +%type pi_model -%type pi_model +%type pin_name driver_pair internal_connection -%type pin_name driver_pair internal_connection - -%type net +%type net %start file -%{ -%} - %% file: @@ -184,32 +179,26 @@ header_def: spef_version: SPEF QSTRING - { sta::stringDelete($2); } ; design_name: DESIGN QSTRING - { sta::stringDelete($2); } ; date: DATE QSTRING - { sta::stringDelete($2); } ; program_name: PROGRAM QSTRING - { sta::stringDelete($2); } ; program_version: PVERSION QSTRING - { sta::stringDelete($2); } ; vendor: VENDOR QSTRING - { sta::stringDelete($2); } ; design_flow: @@ -220,12 +209,11 @@ design_flow: qstrings: QSTRING { $$ = new sta::StringSeq; - $$->push_back($1); - sta::stringDelete($1); + $$->push_back(std::move($1)); } | qstrings QSTRING - { $$->push_back($2); - sta::stringDelete($2); + { $$ = $1; + $$->push_back(std::move($2)); } ; @@ -322,7 +310,7 @@ net_names: net_name: name_or_index - { sta::stringDelete($1); } + { $$ = sta::SpefParseVoid{}; } ; /****************************************************************/ @@ -345,14 +333,12 @@ port_entries: port_entry: port_name direction conn_attrs - { sta::stringDelete($1); } + { $$ = sta::SpefParseVoid{}; } ; direction: IDENT - { $$ = reader->portDirection($1); - sta::stringDelete($1); - } + { $$ = reader->portDirection($1); } ; port_name: @@ -374,15 +360,14 @@ pport_entries: pport_entry: pport_name IDENT conn_attrs + { $$ = sta::SpefParseVoid{}; } ; pport_name: name_or_index - { sta::stringDelete($1); } + { $$ = sta::SpefParseVoid{}; } | physical_inst ':' pport - { sta::stringDelete($1); - sta::stringDelete($3); - } + { $$ = sta::SpefParseVoid{}; } ; pport: @@ -441,7 +426,6 @@ threshold: driving_cell: KW_D cell_type - { sta::stringDelete($2); } ; cell_type: @@ -458,18 +442,8 @@ define_def: define_entry: DEFINE inst_name entity - { sta::stringDelete($2); - sta::stringDelete($3); - } | DEFINE inst_name inst_name entity - { sta::stringDelete($2); - sta::stringDelete($3); - sta::stringDelete($4); - } | PDEFINE physical_inst entity - { sta::stringDelete($2); - sta::stringDelete($3); - } ; entity: @@ -501,9 +475,7 @@ d_net: net: name_or_index - { $$ = reader->findNet($1); - sta::stringDelete($1); - } + { $$ = reader->findNet($1); } ; total_cap: @@ -538,11 +510,9 @@ conn_def: external_connection: name_or_index - { sta::stringDelete($1); } + { $$ = std::string(); } | physical_inst ':' pport - { sta::stringDelete($1); - sta::stringDelete($3); - } + { $$ = std::string(); } ; internal_connection: @@ -551,9 +521,7 @@ internal_connection: pin_name: name_or_index - { $$ = reader->findPin($1); - sta::stringDelete($1); - } + { $$ = reader->findPin($1); } ; internal_node_coords: @@ -567,7 +535,7 @@ internal_node_coord: internal_parasitic_node: name_or_index - { sta::stringDelete($1); } + { $$ = std::string(); } ; /****************************************************************/ @@ -581,6 +549,7 @@ cap_elems: /* empty */ { $$ = 0; } | cap_elems cap_elem + { $$ = $1; } ; cap_elem: @@ -609,6 +578,7 @@ res_elems: /* empty */ { $$ = 0; } | res_elems res_elem + { $$ = $1; } ; res_elem: @@ -631,6 +601,7 @@ induc_elems: /* empty */ { $$ = 0; } | induc_elems induc_elem + { $$ = $1; } ; induc_elem: @@ -658,9 +629,7 @@ driver_reducs: driver_reduc: driver_pair driver_cell pi_model - { reader->rspfDrvrBegin($1, $3); - sta::stringDelete($2); - } + { reader->rspfDrvrBegin($1, $3); } load_desc { reader->rspfDrvrFinish(); } ; @@ -709,6 +678,7 @@ pole_desc: poles: pole | poles pole + { $$ = $2; } ; pole: @@ -753,7 +723,6 @@ residue: d_pnet: D_PNET pnet_ref total_cap routing_conf pconn_sec cap_sec res_sec induc_sec END - { sta::stringDelete($2); } ; pnet_ref: @@ -789,10 +758,7 @@ internal_pnode_coord: internal_pdspf_node: name_or_index - { - sta::stringDelete($1); - $$ = 0; - } + { $$ = std::string(); } ; name_or_index: @@ -805,9 +771,7 @@ name_or_index: r_pnet: R_PNET pnet_ref total_cap routing_conf END - { sta::stringDelete($2); } | R_PNET pnet_ref total_cap routing_conf pdriver_reduc END - { sta::stringDelete($2); } ; pdriver_reduc: @@ -824,6 +788,7 @@ number: INTEGER { $$ = static_cast($1); } | FLOAT + { $$ = $1; } ; pos_integer: diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 2bc516ae2..84eacea73 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -24,6 +24,10 @@ #include "SpefReader.hh" +#include +#include +#include + #include "Zlib.hh" #include "Stats.hh" #include "Report.hh" @@ -44,7 +48,7 @@ namespace sta { bool -readSpefFile(const std::string &filename, +readSpefFile(std::string_view filename, Instance *instance, bool pin_cap_included, bool keep_coupling_caps, @@ -61,7 +65,7 @@ readSpefFile(const std::string &filename, return success; } -SpefReader::SpefReader(const std::string &filename, +SpefReader::SpefReader(std::string_view filename, Instance *instance, bool pin_cap_included, bool keep_coupling_caps, @@ -103,7 +107,7 @@ bool SpefReader::read() { bool success; - gzstream::igzstream stream(filename_.c_str()); + gzstream::igzstream stream(std::string(filename_).c_str()); if (stream.is_open()) { Stats stats(debug_, report_); SpefScanner scanner(&stream, filename_, this, report_); @@ -144,13 +148,13 @@ SpefReader::setBusBrackets(char left, } Instance * -SpefReader::findInstanceRelative(const char *name) +SpefReader::findInstanceRelative(std::string_view name) { return sdc_network_->findInstanceRelative(instance_, name); } Net * -SpefReader::findNetRelative(const char *name) +SpefReader::findNetRelative(std::string_view name) { Net *net = network_->findNetRelative(instance_, name); // Relax spef escaping requirement because some commercial tools @@ -161,21 +165,22 @@ SpefReader::findNetRelative(const char *name) } Pin * -SpefReader::findPinRelative(const char *name) +SpefReader::findPinRelative(std::string_view name) { return network_->findPinRelative(instance_, name); } Pin * -SpefReader::findPortPinRelative(const char *name) +SpefReader::findPortPinRelative(std::string_view name) { return network_->findPin(instance_, name); } -char * -SpefReader::translated(const char *token) +std::string +SpefReader::translated(std::string_view spef_name) { - return spefToSta(token, divider_, network_->pathDivider(), network_->pathEscape()); + return spefToSta(spef_name, divider_, network_->pathDivider(), + network_->pathEscape()); } int @@ -187,79 +192,74 @@ SpefReader::warnLine() const void SpefReader::setTimeScale(float scale, - const char *units) + std::string_view units) { - if (stringEq(units, "NS")) + if (stringEqual(units, "NS")) time_scale_ = scale * 1E-9F; - else if (stringEq(units, "PS")) + else if (stringEqual(units, "PS")) time_scale_ = scale * 1E-12F; else warn(1641, "unknown units {}.", units); - stringDelete(units); } void SpefReader::setCapScale(float scale, - const char *units) + std::string_view units) { - if (stringEq(units, "PF")) + if (stringEqual(units, "PF")) cap_scale_ = scale * 1E-12F; - else if (stringEq(units, "FF")) + else if (stringEqual(units, "FF")) cap_scale_ = scale * 1E-15F; else warn(1642, "unknown units {}.", units); - stringDelete(units); } void SpefReader::setResScale(float scale, - const char *units) + std::string_view units) { - if (stringEq(units, "OHM")) + if (stringEqual(units, "OHM")) res_scale_ = scale; - else if (stringEq(units, "KOHM")) + else if (stringEqual(units, "KOHM")) res_scale_ = scale * 1E+3F; else warn(1643, "unknown units {}.", units); - stringDelete(units); } void SpefReader::setInductScale(float scale, - const char *units) + std::string_view units) { - if (stringEq(units, "HENRY")) + if (stringEqual(units, "HENRY")) induct_scale_ = scale; - else if (stringEq(units, "MH")) + else if (stringEqual(units, "MH")) induct_scale_ = scale * 1E-3F; - else if (stringEq(units, "UH")) + else if (stringEqual(units, "UH")) induct_scale_ = scale * 1E-6F; else warn(1644, "unknown units {}.", units); - stringDelete(units); } void -SpefReader::makeNameMapEntry(const char *index, - const char *name) +SpefReader::makeNameMapEntry(std::string_view index, + std::string_view name) { - int i = atoi(index + 1); + int i = std::stoi(std::string(index.substr(1))); name_map_[i] = name; - stringDelete(index); - stringDelete(name); } -const char * -SpefReader::nameMapLookup(const char *name) +std::string_view +SpefReader::nameMapLookup(std::string_view name) { - if (name && name[0] == '*') { - int index = atoi(name + 1); + if (!name.empty() && name[0] == '*') { + std::string index_str(name.substr(1)); + int index = std::stoi(index_str); const auto &itr = name_map_.find(index); if (itr != name_map_.end()) - return itr->second.c_str(); + return itr->second; else { warn(1645, "no name map entry for {}.", index); - return nullptr; + return ""; } } else @@ -267,14 +267,14 @@ SpefReader::nameMapLookup(const char *name) } PortDirection * -SpefReader::portDirection(char *spef_dir) +SpefReader::portDirection(std::string_view spef_dir) { PortDirection *direction = PortDirection::unknown(); - if (stringEq(spef_dir, "I")) + if (spef_dir == "I") direction = PortDirection::input(); - else if (stringEq(spef_dir, "O")) + else if (spef_dir == "O") direction = PortDirection::output(); - else if (stringEq(spef_dir, "B")) + else if (spef_dir == "B") direction = PortDirection::bidirect(); else warn(1646, "unknown port direction {}.", spef_dir); @@ -284,31 +284,31 @@ SpefReader::portDirection(char *spef_dir) void SpefReader::setDesignFlow(StringSeq *flow) { - design_flow_ = std::move(*flow); + design_flow_ = *flow; delete flow; } Pin * -SpefReader::findPin(char *name) +SpefReader::findPin(std::string_view name) { Pin *pin = nullptr; - if (name) { - char *delim = strrchr(name, delimiter_); - if (delim) { - *delim = '\0'; - const char *name1 = nameMapLookup(name); - if (name1) { - Instance *inst = findInstanceRelative(name1); - // Replace delimiter for error messages. - *delim = delimiter_; - const char *port_name = delim + 1; + if (!name.empty()) { + size_t delim = name.rfind(delimiter_); + if (delim != std::string::npos) { + std::string inst_name_mapped(name.substr(0, delim)); + std::string_view inst_name = nameMapLookup(inst_name_mapped); + if (!inst_name.empty()) { + Instance *inst = findInstanceRelative(inst_name); + std::string port_name(name.substr(delim + 1, std::string::npos)); if (inst) { pin = network_->findPin(inst, port_name); if (pin == nullptr) - warn(1647, "pin {} not found.", name1); + warn(1647, "pin {}{}{} not found.", + inst_name, delimiter_, port_name); } else - warn(1648, "instance {} not found.", name1); + warn(1648, "instance {}{}{} not found.", + inst_name, delimiter_, port_name); } } else { @@ -321,11 +321,11 @@ SpefReader::findPin(char *name) } Net * -SpefReader::findNet(const char *name) +SpefReader::findNet(std::string_view name) { Net *net = nullptr; - const char *name1 = nameMapLookup(name); - if (name1) { + std::string_view name1 = nameMapLookup(name); + if (!name1.empty()) { net = findNetRelative(name1); if (net == nullptr) warn(1650, "net {} not found.", name1); @@ -425,16 +425,16 @@ SpefReader::dspfFinish() } ParasiticNode * -SpefReader::findParasiticNode(char *name, +SpefReader::findParasiticNode(std::string_view name, bool local_only) { - if (name && parasitic_) { - char *delim = strrchr(name, delimiter_); - if (delim) { - *delim = '\0'; - char *name2 = delim + 1; - const char *name1 = nameMapLookup(name); - if (name1) { + if (!name.empty() && parasitic_) { + size_t delim = name.rfind(delimiter_); + if (delim != std::string::npos) { + std::string name1_mapped(name.substr(0, delim)); + std::string name2(name.substr(delim + 1, std::string::npos)); + std::string_view name1 = nameMapLookup(name1_mapped); + if (!name1.empty()) { Instance *inst = findInstanceRelative(name1); if (inst) { // : @@ -445,36 +445,32 @@ SpefReader::findParasiticNode(char *name, sdc_network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, pin, network_); } - else { - // Replace delimiter for error message. - *delim = delimiter_; - warn(1652, "pin {} not found.", name1); - } + else + warn(1652, "pin {}{}{} not found.", + name1, delimiter_, name2); } else { Net *net = findNet(name1); - // Replace delimiter for error messages. - *delim = delimiter_; if (net) { // : - const char *id_str = delim + 1; - if (isDigits(id_str)) { - int id = atoi(id_str); + if (isDigits(name2)) { + int id = std::stoi(name2); if (local_only && !network_->isConnected(net, net_)) - warn(1653, "{} not connected to net {}.", name1, - network_->pathName(net_)); + warn(1653, "{}{}{} not connected to net {}.", + name1, delimiter_, name2, network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, net, id, network_); } else - warn(1654, "node {} not a pin or net:number", name1); + warn(1654, "node {}{}{} not a pin or net:number", + name1, delimiter_, name2); } } } } else { // - const char *name1 = nameMapLookup(name); - if (name1) { + std::string_view name1 = nameMapLookup(name); + if (!name1.empty()) { Pin *pin = findPortPinRelative(name1); if (pin) { if (local_only && !network_->isConnected(net_, pin)) @@ -494,7 +490,7 @@ SpefReader::findParasiticNode(char *name, void SpefReader::makeCapacitor(int, - char *node_name, + std::string_view node_name, SpefTriple *cap) { ParasiticNode *node = findParasiticNode(node_name, true); @@ -503,13 +499,12 @@ SpefReader::makeCapacitor(int, parasitics_->incrCap(node, cap1); } delete cap; - stringDelete(node_name); } void SpefReader::makeCapacitor(int id, - char *node_name1, - char *node_name2, + std::string_view node_name1, + std::string_view node_name2, SpefTriple *cap) { ParasiticNode *node1 = findParasiticNode(node_name1, false); @@ -527,14 +522,12 @@ SpefReader::makeCapacitor(int id, } } delete cap; - stringDelete(node_name1); - stringDelete(node_name2); } void SpefReader::makeResistor(int id, - char *node_name1, - char *node_name2, + std::string_view node_name1, + std::string_view node_name2, SpefTriple *res) { ParasiticNode *node1 = findParasiticNode(node_name1, true); @@ -544,8 +537,6 @@ SpefReader::makeResistor(int id, parasitics_->makeResistor(parasitic_, id, res1, node1, node2); } delete res; - stringDelete(node_name1); - stringDelete(node_name2); } //////////////////////////////////////////////////////////////// @@ -596,7 +587,7 @@ SpefTriple::value(int index) const //////////////////////////////////////////////////////////////// SpefScanner::SpefScanner(std::istream *stream, - const std::string &filename, + std::string_view filename, SpefReader *reader, Report *report) : yyFlexLexer(stream), @@ -607,9 +598,9 @@ SpefScanner::SpefScanner(std::istream *stream, } void -SpefScanner::error(const char *msg) +SpefScanner::error(std::string_view msg) { - report_->fileError(1658, filename_.c_str(), lineno(), "{}", msg); + report_->fileError(1658, filename_, lineno(), "{}", msg); } } // namespace sta diff --git a/parasitics/SpefReader.hh b/parasitics/SpefReader.hh index f5a9efcb5..d07e8a632 100644 --- a/parasitics/SpefReader.hh +++ b/parasitics/SpefReader.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include "Zlib.hh" #include "MinMax.hh" @@ -43,7 +44,7 @@ class StaState; // Min/max and operating condition op_cond are used for parasitic network reduction. // Return true if successful. bool -readSpefFile(const std::string &filename, +readSpefFile(std::string_view filename, Instance *instance, bool pin_cap_included, bool keep_coupling_caps, diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index 7b01fda6b..272d7ef1f 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include "Zlib.hh" @@ -47,7 +48,7 @@ using SpefNameMap = std::map; class SpefReader : public StaState { public: - SpefReader(const std::string &filename, + SpefReader(std::string_view filename, Instance *instance, bool pin_cap_included, bool keep_coupling_caps, @@ -63,25 +64,25 @@ public: void setDivider(char divider); char delimiter() const { return delimiter_; } void setDelimiter(char delimiter); - const std::string &filename() const { return filename_; } + std::string_view filename() const { return filename_; } // Translate from spf/spef namespace to sta namespace. - char *translated(const char *token); + std::string translated(std::string_view spef_name); void setBusBrackets(char left, char right); void setTimeScale(float scale, - const char *units); + std::string_view units); void setCapScale(float scale, - const char *units); + std::string_view units); void setResScale(float scale, - const char *units); + std::string_view units); void setInductScale(float scale, - const char *units); - void makeNameMapEntry(const char *index, - const char *name); - const char *nameMapLookup(const char *index); + std::string_view units); + void makeNameMapEntry(std::string_view index, + std::string_view name); + std::string_view nameMapLookup(std::string_view name); void setDesignFlow(StringSeq *flow_keys); - Pin *findPin(char *name); - Net *findNet(const char *name); + Pin *findPin(std::string_view name); + Net *findNet(std::string_view name); void rspfBegin(Net *net, SpefTriple *total_cap); void rspfFinish(); @@ -94,17 +95,17 @@ public: SpefTriple *total_cap); void dspfFinish(); void makeCapacitor(int id, - char *node_name, + std::string_view node_name, SpefTriple *cap); void makeCapacitor(int id, - char *node_name1, - char *node_name2, + std::string_view node_name1, + std::string_view node_name2, SpefTriple *cap); void makeResistor(int id, - char *node_name1, - char *node_name2, + std::string_view node_name1, + std::string_view node_name2, SpefTriple *res); - PortDirection *portDirection(char *spef_dir); + PortDirection *portDirection(std::string_view spef_dir); int warnLine() const; template void warn(int id, @@ -116,14 +117,14 @@ public: } private: - Pin *findPinRelative(const char *name); - Pin *findPortPinRelative(const char *name); - Net *findNetRelative(const char *name); - Instance *findInstanceRelative(const char *name); - ParasiticNode *findParasiticNode(char *name, + Pin *findPinRelative(std::string_view name); + Pin *findPortPinRelative(std::string_view name); + Net *findNetRelative(std::string_view name); + Instance *findInstanceRelative(std::string_view name); + ParasiticNode *findParasiticNode(std::string_view name, bool local_only); - const std::string filename_; + std::string_view filename_; SpefScanner *scanner_; Instance *instance_; bool pin_cap_included_; @@ -180,6 +181,4 @@ private: SpefTriple *c1_; }; -extern SpefReader *spef_reader; - } // namespace diff --git a/parasitics/SpefScanner.hh b/parasitics/SpefScanner.hh index 0ae97b072..c87a43c00 100644 --- a/parasitics/SpefScanner.hh +++ b/parasitics/SpefScanner.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "SpefLocation.hh" #include "SpefParse.hh" @@ -41,7 +43,7 @@ class SpefScanner : public SpefFlexLexer { public: SpefScanner(std::istream *stream, - const std::string &filename, + std::string_view filename, SpefReader *reader, Report *report); virtual ~SpefScanner() {} @@ -51,14 +53,14 @@ public: // YY_DECL defined in SpefLex.ll // Method body created by flex in SpefLex.cc - void error(const char *msg); + void error(std::string_view msg); int line() const { return yylineno; } // Get rid of override virtual function warning. using FlexLexer::yylex; private: - std::string filename_; + std::string_view filename_; SpefReader *reader_; Report *report_; std::string token_; diff --git a/power/Power.cc b/power/Power.cc index cd5312b08..78d9adce2 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -344,24 +344,24 @@ Power::reportInstsJson(const InstanceSeq &insts, } void -Power::reportPowerRowJson(const char *name, +Power::reportPowerRowJson(std::string_view type, const PowerResult &power, int digits, - const char *separator) + std::string_view eol) { float internal = power.internal(); float switching = power.switching(); float leakage = power.leakage(); float total = power.total(); - report_->report(" \"{}\": {{", name); + report_->report(" \"{}\": {{", type); 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; + if (!eol.empty()) + line += eol; report_->reportLine(line); } @@ -375,9 +375,8 @@ Power::reportPowerInstJson(const Instance *inst, float leakage = power.leakage(); float total = power.total(); - const char *inst_name = network_->pathName(inst); report_->report("{{"); - report_->report(" \"name\": \"{}\",", inst_name); + report_->report(" \"name\": \"{}\",", network_->pathName(inst)); report_->report(" \"internal\": {:.{}e},", internal, digits); report_->report(" \"switching\": {:.{}e},", switching, digits); report_->report(" \"leakage\": {:.{}e},", leakage, digits); @@ -1622,26 +1621,23 @@ Power::portVoltage(LibertyCell *cell, const Scene *scene, const MinMax *min_max) { - return pgNameVoltage(cell, port->relatedPowerPin(), scene, min_max); + return pgPortVoltage(cell, port->relatedPowerPort(), scene, min_max); } float -Power::pgNameVoltage(LibertyCell *cell, - const char *pg_port_name, +Power::pgPortVoltage(LibertyCell *cell, + const LibertyPort *pg_port, const Scene *scene, const MinMax *min_max) { - if (pg_port_name) { - LibertyPort *pg_port = cell->findLibertyPort(pg_port_name); - if (pg_port) { - const char *volt_name = pg_port->voltageName(); - LibertyLibrary *library = cell->libertyLibrary(); - float voltage; - bool exists; - library->supplyVoltage(volt_name, voltage, exists); - if (exists) - return voltage; - } + if (pg_port) { + const std::string &volt_name = pg_port->voltageName(); + LibertyLibrary *library = cell->libertyLibrary(); + float voltage; + bool exists; + library->supplyVoltage(volt_name, voltage, exists); + if (exists) + return voltage; } Pvt *pvt = scene->sdc()->operatingConditions(min_max); @@ -1714,7 +1710,7 @@ Power::reportActivityAnnotation(bool report_unannotated, 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); + const std::string &origin_name = pwr_activity_origin_map.find(origin); report_->report("{:>5} {}", origin_name, sdc_network_->pathName(pin)); } } @@ -1927,7 +1923,7 @@ PwrActivity::isSet() const return origin_ != PwrActivityOrigin::unknown; } -const char * +const std::string & PwrActivity::originName() const { return pwr_activity_origin_map.find(origin_); diff --git a/power/Power.hh b/power/Power.hh index 49a6dc0ab..724710473 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -151,10 +151,10 @@ protected: void setActivity(const Pin *pin, PwrActivity &activity); PwrActivity findActivity(const Pin *pin); - void reportPowerRowJson(const char *name, + void reportPowerRowJson(std::string_view type, const PowerResult &power, int digits, - const char *separator); + std::string_view eol); void reportPowerInstJson(const Instance *inst, const PowerResult &power, int digits); @@ -209,8 +209,8 @@ protected: const LibertyPort *port, const Scene *scene, const MinMax *min_max); - float pgNameVoltage(LibertyCell *cell, - const char *pg_port_name, + float pgPortVoltage(LibertyCell *cell, + const LibertyPort *pg_port, const Scene *scene, const MinMax *min_max); void seedActivities(BfsFwdIterator &bfs); diff --git a/power/ReportPower.cc b/power/ReportPower.cc index cf4803b7f..00407ff6d 100644 --- a/power/ReportPower.cc +++ b/power/ReportPower.cc @@ -137,11 +137,11 @@ ReportPower::powerColPercent(float col_total, } void -ReportPower::reportTitle5(const char *title1, - const char *title2, - const char *title3, - const char *title4, - const char *title5, +ReportPower::reportTitle5(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view title5, int field_width) { report_->report("{:<20} {:>{}} {:>{}} {:>{}} {:>{}}", title1, title2, field_width, @@ -149,12 +149,12 @@ ReportPower::reportTitle5(const char *title1, } void -ReportPower::reportTitle5Units(const char *title1, - const char *title2, - const char *title3, - const char *title4, - const char *title5, - const char *units, +ReportPower::reportTitle5Units(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view title5, + std::string_view units, int field_width) { report_->report("{:<20} {:>{}} {:>{}} {:>{}} {:>{}} {}", title1, title2, @@ -171,7 +171,7 @@ ReportPower::reportTitleDashes5(int field_width) } void -ReportPower::reportRow(const char *type, +ReportPower::reportRow(std::string_view type, const PowerResult &power, float design_total, int field_width, @@ -186,7 +186,7 @@ ReportPower::reportRow(const char *type, if (design_total != 0.0 && !std::isnan(design_total)) percent = total / design_total * 100.0; - std::string line = sta::format("{:<20}", type); + std::string line = sta::format("{:<20}", std::string(type)); line += powerCol(internal, field_width, digits); line += powerCol(switching, field_width, digits); line += powerCol(leakage, field_width, digits); @@ -196,10 +196,10 @@ ReportPower::reportRow(const char *type, } void -ReportPower::reportTitle4(const char *title1, - const char *title2, - const char *title3, - const char *title4, +ReportPower::reportTitle4(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, int field_width) { report_->report(" {:>{}} {:>{}} {:>{}} {:>{}}", title1, field_width, title2, @@ -207,11 +207,11 @@ ReportPower::reportTitle4(const char *title1, } void -ReportPower::reportTitle4Units(const char *title1, - const char *title2, - const char *title3, - const char *title4, - const char *units, +ReportPower::reportTitle4Units(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view units, int field_width) { report_->report(" {:>{}} {:>{}} {:>{}} {:>{}} {}", title1, field_width, title2, diff --git a/power/ReportPower.hh b/power/ReportPower.hh index 966e8acbf..0486f8d79 100644 --- a/power/ReportPower.hh +++ b/power/ReportPower.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "StaState.hh" #include "NetworkClass.hh" #include "PowerClass.hh" @@ -51,35 +53,35 @@ private: std::string powerColPercent(float col_total, float total, int field_width); - void reportTitle5(const char *title1, - const char *title2, - const char *title3, - const char *title4, - const char *title5, + void reportTitle5(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view title5, int field_width); - void reportTitle5Units(const char *title1, - const char *title2, - const char *title3, - const char *title4, - const char *title5, - const char *units, + void reportTitle5Units(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view title5, + std::string_view units, int field_width); void reportTitleDashes5(int field_width); - void reportRow(const char *type, + void reportRow(std::string_view type, const PowerResult &power, float design_total, int field_width, int digits); - void reportTitle4(const char *title1, - const char *title2, - const char *title3, - const char *title4, + void reportTitle4(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, int field_width); - void reportTitle4Units(const char *title1, - const char *title2, - const char *title3, - const char *title4, - const char *units, + void reportTitle4Units(std::string_view title1, + std::string_view title2, + std::string_view title3, + std::string_view title4, + std::string_view units, int field_width); void reportTitleDashes4(int field_width); void reportInst(const Instance *inst, diff --git a/power/SaifLex.ll b/power/SaifLex.ll index e602983b2..7d53c2a0b 100644 --- a/power/SaifLex.ll +++ b/power/SaifLex.ll @@ -24,9 +24,10 @@ // This notice may not be removed or altered from any source distribution. #include +#include +#include #include "util/FlexDisableRegister.hh" -#include "StringUtil.hh" #include "power/SaifReaderPvt.hh" #include "SaifParse.hh" #include "power/SaifScanner.hh" @@ -85,7 +86,7 @@ EOL \r?\n "\"" { BEGIN INITIAL; - yylval->string = sta::stringCopy(token_.c_str()); + yylval->emplace(std::move(token_)); return token::QSTRING; } @@ -101,7 +102,7 @@ EOL \r?\n "//"[^\n]*{EOL} { loc->lines(); loc->step(); } [0-9]+ { - yylval->uint = atoll(yytext); + yylval->emplace(atoll(yytext)); return token::UINT; } @@ -132,7 +133,7 @@ TC { return token::TC; } IG { return token::IG; } {ID} { - yylval->string = sta::stringCopy(yytext); + yylval->emplace(yytext); return token::ID; } diff --git a/power/SaifParse.yy b/power/SaifParse.yy index 6012d59a2..9413cbced 100644 --- a/power/SaifParse.yy +++ b/power/SaifParse.yy @@ -24,9 +24,11 @@ %{ #include +#include +#include +#include #include "Report.hh" -#include "StringUtil.hh" #include "power/SaifReaderPvt.hh" #include "power/SaifScanner.hh" @@ -56,29 +58,22 @@ sta::SaifParse::error(const location_type &loc, %parse-param { SaifScanner *scanner } %parse-param { SaifReader *reader } %define api.parser.class {SaifParse} +%define api.value.type variant // expected shift/reduce conflicts %expect 2 -%union { - char character; - const char *string; - uint64_t uint; - sta::SaifState state; - sta::SaifStateDurations state_durations; -} - %token SAIFILE SAIFVERSION DIRECTION DESIGN DATE VENDOR PROGRAM_NAME VERSION %token DIVIDER TIMESCALE DURATION %token INSTANCE NET PORT %token T0 T1 TX TZ TB TC IG -%token QSTRING ID FNUMBER DNUMBER UINT +%token FNUMBER DNUMBER +%token QSTRING ID +%token UINT -%type UINT -%type QSTRING ID -%type hchar -%type state -%type state_durations +%type hchar +%type state +%type state_durations %start file @@ -97,16 +92,16 @@ header: ; header_stmt: - '(' SAIFVERSION QSTRING ')' { sta::stringDelete($3); } -| '(' DIRECTION QSTRING ')' { sta::stringDelete($3); } -| '(' DESIGN QSTRING ')' { sta::stringDelete($3); } -| '(' DESIGN ')' { } -| '(' DATE QSTRING ')' { sta::stringDelete($3); } -| '(' VENDOR QSTRING ')' { sta::stringDelete($3); } -| '(' PROGRAM_NAME QSTRING ')' { sta::stringDelete($3); } -| '(' VERSION QSTRING ')' { sta::stringDelete($3); } + '(' SAIFVERSION QSTRING ')' +| '(' DIRECTION QSTRING ')' +| '(' DESIGN QSTRING ')' +| '(' DESIGN ')' +| '(' DATE QSTRING ')' +| '(' VENDOR QSTRING ')' +| '(' PROGRAM_NAME QSTRING ')' +| '(' VERSION QSTRING ')' | '(' DIVIDER hchar ')' { reader->setDivider($3); } -| '(' TIMESCALE UINT ID ')' { reader->setTimescale($3, $4); } +| '(' TIMESCALE UINT ID ')' { reader->setTimescale($3, std::move($4)); } | '(' DURATION UINT ')' { reader->setDuration($3); } ; @@ -119,11 +114,11 @@ hchar: instance: '(' INSTANCE ID - { reader->instancePush($3); } + { reader->instancePush(std::move($3)); } instance_contents ')' { reader->instancePop(); } | '(' INSTANCE QSTRING ID - { reader->instancePush($3); } + { reader->instancePush(std::move($3)); } instance_contents ')' { reader->instancePop(); } ; @@ -147,7 +142,7 @@ nets: net: '(' ID state_durations ')' - { reader->setNetDurations($2, $3); } + { reader->setNetDurations(std::move($2), $3); } ; ports: @@ -163,7 +158,7 @@ state_durations: '(' state UINT ')' { $$[static_cast($2)] = $3; } | state_durations '(' state UINT ')' - { $$[static_cast($3)] = $4; } + { $$ = $1; $$[static_cast($3)] = $4; } ; state: diff --git a/power/SaifReader.cc b/power/SaifReader.cc index 72a359268..98e21e76f 100644 --- a/power/SaifReader.cc +++ b/power/SaifReader.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include "Error.hh" #include "Debug.hh" @@ -93,23 +94,22 @@ SaifReader::setDivider(char divider) void SaifReader::setTimescale(uint64_t multiplier, - const char *units) + std::string &&units) { if (multiplier == 1 || multiplier == 10 || multiplier == 100) { - if (stringEq(units, "us")) + if (stringEqual(units, "us")) timescale_ = multiplier * 1E-6; - else if (stringEq(units, "ns")) + else if (stringEqual(units, "ns")) timescale_ = multiplier * 1E-9; - else if (stringEq(units, "ps")) + else if (stringEqual(units, "ps")) timescale_ = multiplier * 1E-12; - else if (stringEq(units, "fs")) + else if (stringEqual(units, "fs")) timescale_ = multiplier * 1E-15; else report_->error(1861, "SAIF TIMESCALE units not us, ns, or ps."); } else report_->error(1862, "SAIF TIMESCALE multiplier not 1, 10, or 100."); - stringDelete(units); } void @@ -119,11 +119,11 @@ SaifReader::setDuration(uint64_t duration) } void -SaifReader::instancePush(const char *instance_name) +SaifReader::instancePush(std::string &&instance_name) { if (in_scope_level_ == 0) { // Check for a match to the annotation scope. - saif_scope_.push_back(instance_name); + saif_scope_.push_back(std::move(instance_name)); std::string saif_scope; bool first = true; @@ -133,7 +133,7 @@ SaifReader::instancePush(const char *instance_name) saif_scope += inst; first = false; } - if (stringEq(saif_scope.c_str(), scope_)) + if (saif_scope == scope_) in_scope_level_ = saif_scope_.size(); } else { @@ -144,7 +144,6 @@ SaifReader::instancePush(const char *instance_name) : nullptr; path_.push_back(child); } - stringDelete(instance_name); } void @@ -159,14 +158,14 @@ SaifReader::instancePop() } void -SaifReader::setNetDurations(const char *net_name, - SaifStateDurations &durations) +SaifReader::setNetDurations(std::string &&net_name, + const SaifStateDurations &durations) { if (in_scope_level_ > 0) { Instance *parent = path_.empty() ? sdc_network_->topInstance() : path_.back(); if (parent) { std::string unescaped_name = unescaped(net_name); - const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str()); + const Pin *pin = sdc_network_->findPin(parent, unescaped_name); LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr; if (pin && !sdc_network_->isHierarchical(pin) && !sdc_network_->direction(pin)->isInternal() @@ -183,17 +182,14 @@ SaifReader::setNetDurations(const char *net_name, } } } - stringDelete(net_name); } std::string -SaifReader::unescaped(const char *token) +SaifReader::unescaped(const std::string &token) { std::string unescaped; - for (const char *t = token; *t; t++) { - char ch = *t; + for (char ch : token) { if (ch != escape_) - // Just the normal noises. unescaped += ch; } debugPrint(debug_, "saif_name", 1, "token {} -> {}", token, unescaped); @@ -216,7 +212,7 @@ SaifScanner::SaifScanner(std::istream *stream, void SaifScanner::error(const char *msg) { - report_->fileError(1860, filename_.c_str(), lineno(), "{}", msg); + report_->fileError(1860, filename_, lineno(), "{}", msg); } } // namespace sta diff --git a/power/SaifReaderPvt.hh b/power/SaifReaderPvt.hh index 4de66c5dd..af8722a65 100644 --- a/power/SaifReaderPvt.hh +++ b/power/SaifReaderPvt.hh @@ -58,16 +58,16 @@ public: void setDivider(char divider); void setTimescale(uint64_t multiplier, - const char *units); + std::string &&units); void setDuration(uint64_t duration); - void instancePush(const char *instance_name); + void instancePush(std::string &&instance_name); void instancePop(); - void setNetDurations(const char *net_name, - SaifStateDurations &durations); + void setNetDurations(std::string &&net_name, + const SaifStateDurations &durations); const char *filename() { return filename_; } private: - std::string unescaped(const char *token); + std::string unescaped(const std::string &token); const char *filename_; const char *scope_; // Divider delimited scope to begin annotation. diff --git a/power/VcdParse.cc b/power/VcdParse.cc index c82423cf5..ea0d71a7f 100644 --- a/power/VcdParse.cc +++ b/power/VcdParse.cc @@ -134,7 +134,7 @@ VcdParse::parseTimescale() } void -VcdParse::setTimeUnit(const std::string &time_unit, +VcdParse::setTimeUnit(std::string_view time_unit, double time_scale) { double time_unit_scale = 1.0; @@ -172,7 +172,7 @@ VcdParse::parseVar() { std::vector tokens = readStmtTokens(); if (tokens.size() == 4 || tokens.size() == 5) { - std::string type_name = tokens[0]; + std::string &type_name = tokens[0]; VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown); if (type == VcdVarType::unknown) report_->fileWarn(809, filename_, file_line_, "Unknown variable type {}.", @@ -180,7 +180,7 @@ VcdParse::parseVar() else { size_t width = std::stoi(tokens[1]); std::string &id = tokens[2]; - std::string name = tokens[3]; + std::string &name = tokens[3]; // iverilog separates bus base name from bit range. if (tokens.size() == 5) { // Preserve space after esacaped name. @@ -188,7 +188,6 @@ VcdParse::parseVar() name += ' '; name += tokens[4]; } - reader_->makeVar(scope_, name, type, width, id); } } diff --git a/power/VcdParse.hh b/power/VcdParse.hh index 6979b0ad7..69a3f7d02 100644 --- a/power/VcdParse.hh +++ b/power/VcdParse.hh @@ -68,7 +68,7 @@ public: private: void parseTimescale(); - void setTimeUnit(const std::string &time_unit, + void setTimeUnit(std::string_view time_unit, double time_scale); void parseVar(); void parseScope(); @@ -98,27 +98,27 @@ class VcdReader { public: virtual ~VcdReader() {} - virtual void setDate(const std::string &date) = 0; - virtual void setComment(const std::string &comment) = 0; - virtual void setVersion(const std::string &version) = 0; - virtual void setTimeUnit(const std::string &time_unit, + virtual void setDate(std::string_view date) = 0; + virtual void setComment(std::string_view comment) = 0; + virtual void setVersion(std::string_view version) = 0; + virtual void setTimeUnit(std::string_view time_unit, double time_unit_scale, double time_scale) = 0; virtual void setTimeMin(VcdTime time) = 0; virtual void setTimeMax(VcdTime time) = 0; virtual void varMinDeltaTime(VcdTime min_delta_time) = 0; - virtual bool varIdValid(const std::string &id) = 0; + virtual bool varIdValid(std::string_view id) = 0; virtual void makeVar(const VcdScope &scope, - const std::string &name, + std::string_view name, VcdVarType type, size_t width, - const std::string &id) = 0; + std::string_view id) = 0; virtual void varAppendValue(const std::string &id, VcdTime time, char value) = 0; virtual void varAppendBusValue(const std::string &id, VcdTime time, - const std::string &bus_value) = 0; + std::string_view bus_value) = 0; }; class VcdValue diff --git a/power/VcdReader.cc b/power/VcdReader.cc index fd5ed1cdf..ee6e60371 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -117,7 +117,7 @@ using VcdIdCountsMap = std::unordered_map; class VcdCountReader : public VcdReader { public: - VcdCountReader(const std::string &scope, + VcdCountReader(std::string_view scope, const Network *sdc_network, Report *report, Debug *debug); @@ -127,31 +127,31 @@ class VcdCountReader : public VcdReader double timeScale() const { return time_scale_; } // VcdParse callbacks. - void setDate(const std::string &) override {} - void setComment(const std::string &) override {} - void setVersion(const std::string &) override {} - void setTimeUnit(const std::string &time_unit, + void setDate(std::string_view ) override {} + void setComment(std::string_view ) override {} + void setVersion(std::string_view ) override {} + void setTimeUnit(std::string_view time_unit, double time_unit_scale, double time_scale) override; void setTimeMin(VcdTime time) override; void setTimeMax(VcdTime time) override; void varMinDeltaTime(VcdTime) override {} - bool varIdValid(const std::string &id) override; + bool varIdValid(std::string_view id) override; void makeVar(const VcdScope &scope, - const std::string &name, + std::string_view name, VcdVarType type, size_t width, - const std::string &id) override; + std::string_view id) override; void varAppendValue(const std::string &id, VcdTime time, char value) override; void varAppendBusValue(const std::string &id, VcdTime time, - const std::string &bus_value) override; + std::string_view bus_value) override; private: - void addVarPin(const std::string &pin_name, - const std::string &id, + void addVarPin(std::string_view pin_name, + std::string_view id, size_t width, size_t bit_idx); @@ -167,7 +167,7 @@ class VcdCountReader : public VcdReader Debug *debug_; }; -VcdCountReader::VcdCountReader(const std::string &scope, +VcdCountReader::VcdCountReader(std::string_view scope, const Network *sdc_network, Report *report, Debug *debug) : @@ -182,7 +182,7 @@ VcdCountReader::VcdCountReader(const std::string &scope, } void -VcdCountReader::setTimeUnit(const std::string &, +VcdCountReader::setTimeUnit(std::string_view , double time_unit_scale, double time_scale) { @@ -202,22 +202,22 @@ VcdCountReader::setTimeMax(VcdTime time) } bool -VcdCountReader::varIdValid(const std::string &) +VcdCountReader::varIdValid(std::string_view ) { return true; } void VcdCountReader::makeVar(const VcdScope &scope, - const std::string &name, + std::string_view name, VcdVarType type, size_t width, - const std::string &id) + std::string_view id) { if (type == VcdVarType::wire || type == VcdVarType::reg) { std::string path_name; bool first = true; - for (const std::string &context : scope) { + for (std::string_view context : scope) { if (!first) path_name += '/'; path_name += context; @@ -238,7 +238,7 @@ VcdCountReader::makeVar(const VcdScope &scope, 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, + parseBusName(var_scoped, '[', ']', '\\', is_bus, is_range, bus_name, from, to, subscript_wild); if (is_bus) { std::string sta_bus_name = netVerilogToSta(bus_name); @@ -272,18 +272,18 @@ VcdCountReader::makeVar(const VcdScope &scope, } void -VcdCountReader::addVarPin(const std::string &pin_name, - const std::string &id, +VcdCountReader::addVarPin(std::string_view pin_name, + std::string_view id, size_t width, size_t bit_idx) { - const Pin *pin = sdc_network_->findPin(pin_name.c_str()); + const Pin *pin = sdc_network_->findPin(pin_name); LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr; 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]; + VcdCounts &vcd_counts = vcd_count_map_[std::string(id)]; vcd_counts.resize(width); vcd_counts[bit_idx].addPin(pin); debugPrint(debug_, "read_vcd", 2, "id {} pin {}", id, pin_name); @@ -317,7 +317,7 @@ VcdCountReader::varAppendValue(const std::string &id, void VcdCountReader::varAppendBusValue(const std::string &id, VcdTime time, - const std::string &bus_value) + std::string_view bus_value) { const auto &itr = vcd_count_map_.find(id); if (itr != vcd_count_map_.end()) { @@ -347,8 +347,8 @@ VcdCountReader::varAppendBusValue(const std::string &id, class ReadVcdActivities : public StaState { public: - ReadVcdActivities(const std::string &filename, - const std::string &scope, + ReadVcdActivities(std::string_view filename, + std::string_view scope, const Sdc *sdc, Sta *sta); void readActivities(); @@ -370,9 +370,9 @@ class ReadVcdActivities : public StaState }; void -readVcdActivities(const std::string &filename, - const std::string &scope, - const std::string &mode_name, +readVcdActivities(std::string_view filename, + std::string_view scope, + std::string_view mode_name, Sta *sta) { const Mode *mode = sta->findMode(mode_name); @@ -381,8 +381,8 @@ readVcdActivities(const std::string &filename, reader.readActivities(); } -ReadVcdActivities::ReadVcdActivities(const std::string &filename, - const std::string &scope, +ReadVcdActivities::ReadVcdActivities(std::string_view filename, + std::string_view scope, const Sdc *sdc, Sta *sta) : StaState(sta), diff --git a/power/VcdReader.hh b/power/VcdReader.hh index 3a2e63f59..205779a6a 100644 --- a/power/VcdReader.hh +++ b/power/VcdReader.hh @@ -31,9 +31,9 @@ namespace sta { class Sta; void -readVcdActivities(const std::string &filename, - const std::string &scope, - const std::string &mode_name, +readVcdActivities(std::string_view filename, + std::string_view scope, + std::string_view mode_name, Sta *sta); } // namespace diff --git a/sdc/Clock.cc b/sdc/Clock.cc index df161fc2e..f2c916338 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -42,10 +42,10 @@ namespace sta { static bool isPowerOfTwo(int i); -Clock::Clock(const char *name, +Clock::Clock(std::string_view name, int index, const Network *network) : - name_(stringCopy(name)), + name_(name), pins_(network), add_to_pins_(false), leaf_pins_(network), @@ -76,7 +76,7 @@ Clock::initClk(PinSet *pins, bool add_to_pins, float period, FloatSeq *waveform, - const char *comment, + std::string_view comment, const Network *network) { is_generated_ = false; @@ -87,7 +87,7 @@ Clock::initClk(PinSet *pins, waveform_valid_ = true; period_ = period; setClkEdgeTimes(); - setComment(comment); + setComment(std::move(comment)); } bool @@ -132,7 +132,6 @@ Clock::makeClkEdges() Clock::~Clock() { - stringDelete(name_); if (clk_edges_) { delete clk_edges_[RiseFall::riseIndex()]; delete clk_edges_[RiseFall::fallIndex()]; @@ -328,7 +327,7 @@ Clock::initGeneratedClk(PinSet *pins, IntSeq *edges, FloatSeq *edge_shifts, bool is_propagated, - const char *comment, + std::string_view comment, const Network *network) { is_generated_ = true; @@ -344,7 +343,7 @@ Clock::initGeneratedClk(PinSet *pins, invert_ = invert; combinational_ = combinational; is_propagated_ = is_propagated; - setComment(comment); + setComment(std::move(comment)); delete edges_; if (edges @@ -680,14 +679,6 @@ InterClockUncertaintyLess::operator()(const InterClockUncertainty *inter1, //////////////////////////////////////////////////////////////// -bool -ClockNameLess::operator()(const Clock *clk1, - const Clock *clk2) -{ - return stringLess(clk1->name(), clk2->name()); -} - - bool ClockIndexLess::operator()(const Clock *clk1, const Clock *clk2) const diff --git a/sdc/ClockGroups.cc b/sdc/ClockGroups.cc index 0bf76c388..776b54a2d 100644 --- a/sdc/ClockGroups.cc +++ b/sdc/ClockGroups.cc @@ -34,8 +34,8 @@ ClockGroups::ClockGroups(const std::string &name, bool physically_exclusive, bool asynchronous, bool allow_paths, - const char *comment) : - SdcCmdComment(comment), + std::string comment) : + SdcCmdComment(std::move(comment)), name_(name), logically_exclusive_(logically_exclusive), physically_exclusive_(physically_exclusive), diff --git a/sdc/DisabledPorts.cc b/sdc/DisabledPorts.cc index 599f970ba..9c3fa9219 100644 --- a/sdc/DisabledPorts.cc +++ b/sdc/DisabledPorts.cc @@ -172,8 +172,7 @@ bool DisabledCellPortsLess::operator()(const DisabledCellPorts *disable1, const DisabledCellPorts *disable2) { - return stringLess(disable1->cell()->name(), - disable2->cell()->name()); + return disable1->cell()->name() < disable2->cell()->name(); } DisabledCellPortsSeq @@ -216,8 +215,8 @@ bool DisabledInstPortsLess::operator()(const DisabledInstancePorts *disable1, const DisabledInstancePorts *disable2) { - return stringLess(network_->pathName(disable1->instance()), - network_->pathName(disable2->instance())); + return network_->pathName(disable1->instance()) < + network_->pathName(disable2->instance()); } DisabledInstancePortsSeq @@ -246,12 +245,12 @@ bool LibertyPortPairNameLess::operator()(const LibertyPortPair &pair1, const LibertyPortPair &pair2) { - const char *from1 = pair1.first->name(); - const char *from2 = pair2.first->name(); - const char *to1 = pair1.second->name(); - const char *to2 = pair2.second->name(); - return stringLess(from1, from2) - || (stringEq(from1, from2) && stringLess(to1, to2)); + const std::string &from1 = pair1.first->name(); + const std::string &from2 = pair2.first->name(); + const std::string &to1 = pair1.second->name(); + const std::string &to2 = pair2.second->name(); + return from1 < from2 + || (from1 == from2 && to1 < to2); } LibertyPortPairSeq diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 83bb60559..e3cb2dd5c 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -92,7 +92,7 @@ ExceptionPath::ExceptionPath(ExceptionFrom *from, const MinMaxAll *min_max, bool own_pts, int priority, - const char *comment) : + std::string_view comment) : SdcCmdComment(comment), from_(from), thrus_(thrus), @@ -225,7 +225,7 @@ ExceptionPath::hash(ExceptionPt *missing_pt) const bool ExceptionPath::mergeable(ExceptionPath *exception) const { - return stringEqualIf(comment_, exception->comment()); + return comment_ == exception->comment(); } bool @@ -480,7 +480,7 @@ PathDelay::PathDelay(ExceptionFrom *from, bool break_path, float delay, bool own_pts, - const char *comment) : + std::string_view comment) : ExceptionPath(from, thrus, to, min_max->asMinMaxAll(), own_pts, pathDelayPriority() + fromThruToPriority(from, thrus, to), comment), @@ -565,7 +565,7 @@ FalsePath::FalsePath(ExceptionFrom *from, ExceptionTo *to, const MinMaxAll *min_max, bool own_pts, - const char *comment) : + std::string_view comment) : ExceptionPath(from, thrus, to, min_max, own_pts, falsePathPriority() + fromThruToPriority(from, thrus, to), comment) @@ -578,7 +578,7 @@ FalsePath::FalsePath(ExceptionFrom *from, const MinMaxAll *min_max, bool own_pts, int priority, - const char *comment) : + std::string_view comment) : ExceptionPath(from, thrus, to, min_max, own_pts, priority, comment) { } @@ -629,8 +629,7 @@ FalsePath::overrides(ExceptionPath *exception) const LoopPath::LoopPath(ExceptionThruSeq *thrus, bool own_pts) : FalsePath(nullptr, thrus, nullptr, MinMaxAll::all(), own_pts, - falsePathPriority() + fromThruToPriority(nullptr, thrus, nullptr), - nullptr) + falsePathPriority() + fromThruToPriority(nullptr, thrus, nullptr), "") { } @@ -655,7 +654,7 @@ MultiCyclePath::MultiCyclePath(ExceptionFrom *from, bool use_end_clk, int path_multiplier, bool own_pts, - const char *comment) : + std::string_view comment) : ExceptionPath(from, thrus, to, min_max, own_pts, multiCyclePathPriority() + fromThruToPriority(from, thrus, to), comment), @@ -756,8 +755,7 @@ FilterPath::FilterPath(ExceptionFrom *from, ExceptionTo *to, bool own_pts) : ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts, - filterPathPriority() + fromThruToPriority(from, thrus, to), - nullptr) + filterPathPriority() + fromThruToPriority(from, thrus, to), "") { } @@ -814,13 +812,13 @@ FilterPath::resetMatch(ExceptionFrom *, //////////////////////////////////////////////////////////////// -GroupPath::GroupPath(const std::string &name, +GroupPath::GroupPath(std::string_view name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, bool own_pts, - const char *comment) : + std::string_view comment) : ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts, groupPathPriority() + fromThruToPriority(from, thrus, to), comment), diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 8889f177c..27d645697 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -150,7 +150,7 @@ Sdc::makeDefaultArrivalClock() waveform->push_back(0.0); waveform->push_back(0.0); default_arrival_clk_ = new Clock("input port clock", clk_index_++, network_); - default_arrival_clk_->initClk(0, false, 0.0, waveform, nullptr, network_); + default_arrival_clk_->initClk(0, false, 0.0, waveform, "", network_); } Sdc::~Sdc() @@ -956,14 +956,14 @@ Sdc::maxArea() const //////////////////////////////////////////////////////////////// Clock * -Sdc::makeClock(const char *name, +Sdc::makeClock(std::string_view name, PinSet *pins, bool add_to_pins, float period, FloatSeq *waveform, - const char *comment) + std::string_view comment) { - Clock *clk = findKey(clock_name_map_, name); + Clock *clk = findStringKey(clock_name_map_, name); if (!add_to_pins) deletePinClocks(clk, pins); if (clk) @@ -971,7 +971,7 @@ Sdc::makeClock(const char *name, deleteClkPinMappings(clk); else { // Fresh clock definition. - clk = new Clock(name, clk_index_++, network_); + clk = new Clock(std::move(name), clk_index_++, network_); clk->setIsPropagated(variables_->propagateAllClocks()); clocks_.push_back(clk); // Use the copied name in the map. @@ -986,7 +986,7 @@ Sdc::makeClock(const char *name, } Clock * -Sdc::makeGeneratedClock(const char *name, +Sdc::makeGeneratedClock(std::string_view name, PinSet *pins, bool add_to_pins, Pin *src_pin, @@ -998,9 +998,9 @@ Sdc::makeGeneratedClock(const char *name, bool combinational, IntSeq *edges, FloatSeq *edge_shifts, - const char *comment) + std::string_view comment) { - Clock *clk = findKey(clock_name_map_, name); + Clock *clk = findStringKey(clock_name_map_, name); if (!add_to_pins) deletePinClocks(clk, pins); if (clk) @@ -1150,9 +1150,9 @@ Sdc::deleteMasterClkRefs(Clock *clk) } Clock * -Sdc::findClock(const char *name) const +Sdc::findClock(std::string_view name) const { - return findKey(clock_name_map_, name); + return findStringKey(clock_name_map_, name); } bool @@ -1220,7 +1220,7 @@ Sdc::sortedClocks() const ClockSeq clks; for (Clock *clk : clocks_) clks.push_back(clk); - sort(clks, ClkNameLess()); + sort(clks, ClockNameLess()); return clks; } @@ -1946,20 +1946,21 @@ Sdc::makeClockGroups(const std::string &name, bool physically_exclusive, bool asynchronous, bool allow_paths, - const char *comment) + std::string comment) { std::string group_name; if (name.empty()) group_name = makeClockGroupsName(); else { group_name = name; - ClockGroups *groups = findKey(clk_groups_name_map_, group_name); + ClockGroups *groups = findStringKey(clk_groups_name_map_, group_name); if (groups) removeClockGroups(groups); } ClockGroups *groups = new ClockGroups(group_name, logically_exclusive, physically_exclusive, - asynchronous, allow_paths, comment); + asynchronous, allow_paths, + std::move(comment)); clk_groups_name_map_[groups->name()] = groups; return groups; } @@ -3891,7 +3892,7 @@ Sdc::makeFalsePath(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max, - const char *comment) + std::string_view comment) { checkFromThrusTo(from, thrus, to); FalsePath *exception = new FalsePath(from, thrus, to, min_max, true, @@ -3906,7 +3907,7 @@ Sdc::makeMulticyclePath(ExceptionFrom *from, const MinMaxAll *min_max, bool use_end_clk, int path_multiplier, - const char *comment) + std::string_view comment) { checkFromThrusTo(from, thrus, to); MultiCyclePath *exception = new MultiCyclePath(from, thrus, to, @@ -3924,7 +3925,7 @@ Sdc::makePathDelay(ExceptionFrom *from, bool ignore_clk_latency, bool break_path, float delay, - const char *comment) + std::string_view comment) { checkFromThrusTo(from, thrus, to); PathDelay *exception = new PathDelay(from, thrus, to, min_max, @@ -4090,17 +4091,18 @@ Sdc::clearGroupPathMap() } void -Sdc::makeGroupPath(const std::string &name, +Sdc::makeGroupPath(std::string_view name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, - const char *comment) + std::string_view comment) { checkFromThrusTo(from, thrus, to); if (!name.empty() && is_default) report_->critical(1490, "group path name and is_default are mutually exclusive."); else if (!name.empty()) { + const std::string name_key(name); 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 @@ -4112,10 +4114,10 @@ Sdc::makeGroupPath(const std::string &name, ExceptionPath *clone = group_path->clone(from1, thrus1, to1, true); addException(clone); // A named group path can have multiple exceptions. - GroupPathSet *groups = findKey(group_path_map_, name); + GroupPathSet *groups = findKey(group_path_map_, name_key); if (groups == nullptr) { groups = new GroupPathSet(network_); - group_path_map_[name] = groups; + group_path_map_[name_key] = groups; } if (groups->contains(group_path)) // Exact copy of existing group path. @@ -4132,7 +4134,7 @@ Sdc::makeGroupPath(const std::string &name, } bool -Sdc::isGroupPathName(const char *group_name) const +Sdc::isGroupPathName(std::string_view group_name) const { return group_path_map_.contains(group_name); } diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 0b0251f18..411d8f493 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -23,7 +23,8 @@ // This notice may not be removed or altered from any source distribution. %module sdc -%import + +%include "std_string.i" %{ #include @@ -91,12 +92,12 @@ private: %inline %{ void -write_sdc_cmd(const char *filename, - bool leaf, - bool compatible, - int digits, - bool gzip, - bool no_timestamp) +write_sdc_cmd(std::string filename, + bool leaf, + bool compatible, + int digits, + bool gzip, + bool no_timestamp) { Sta *sta = Sta::sta(); const Sdc *sdc = sta->cmdSdc(); @@ -104,16 +105,16 @@ write_sdc_cmd(const char *filename, } void -set_analysis_type_cmd(const char *analysis_type) +set_analysis_type_cmd(std::string analysis_type) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); AnalysisType type; - if (stringEq(analysis_type, "single")) + if (analysis_type == "single") type = AnalysisType::single; - else if (stringEq(analysis_type, "bc_wc")) + else if (analysis_type == "bc_wc") type = AnalysisType::bc_wc; - else if (stringEq(analysis_type, "on_chip_variation")) + else if (analysis_type == "on_chip_variation") type = AnalysisType::ocv; else { sta->report()->warn(2121, "unknown analysis type"); @@ -249,7 +250,7 @@ set_net_wire_cap(const Net *net, } void -set_wire_load_mode_cmd(const char *mode_name) +set_wire_load_mode_cmd(std::string mode_name) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); @@ -289,20 +290,21 @@ set_net_resistance(Net *net, } void -make_clock(const char *name, +make_clock(std::string name, PinSet *pins, bool add_to_pins, float period, FloatSeq *waveform, - char *comment) + std::string comment) { Sta *sta = Sta::sta(); const Mode *mode = sta->cmdMode(); - sta->makeClock(name, pins, add_to_pins, period, waveform, comment, mode); + sta->makeClock(name.c_str(), pins, add_to_pins, period, waveform, + std::move(comment), mode); } void -make_generated_clock(const char *name, +make_generated_clock(std::string name, PinSet *pins, bool add_to_pins, Pin *src_pin, @@ -314,15 +316,15 @@ make_generated_clock(const char *name, bool combinational, IntSeq *edges, FloatSeq *edge_shifts, - char *comment) + std::string comment) { Sta *sta = Sta::sta(); const Mode *mode = sta->cmdMode(); - sta->makeGeneratedClock(name, pins, add_to_pins, + sta->makeGeneratedClock(name.c_str(), pins, add_to_pins, src_pin, master_clk, divide_by, multiply_by, duty_cycle, invert, combinational, edges, edge_shifts, - comment, mode); + std::move(comment), mode); } void @@ -796,11 +798,11 @@ make_false_path(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max, - const char *comment) + std::string comment) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); - sta->makeFalsePath(from, thrus, to, min_max, comment, sdc); + sta->makeFalsePath(from, thrus, to, min_max, std::move(comment), sdc); } void @@ -810,12 +812,12 @@ make_multicycle_path(ExceptionFrom *from, const MinMaxAll *min_max, bool use_end_clk, int path_multiplier, - const char *comment) + std::string comment) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); sta->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, - path_multiplier, comment, sdc); + path_multiplier, std::move(comment), sdc); } void @@ -826,13 +828,13 @@ make_path_delay(ExceptionFrom *from, bool ignore_clk_latency, bool break_path, float delay, - const char *comment) + std::string comment) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); sta->makePathDelay(from, thrus, to, min_max, ignore_clk_latency, break_path, - delay, comment, sdc); + delay, std::move(comment), sdc); } void @@ -851,20 +853,20 @@ reset_path_cmd(ExceptionFrom * } void -make_group_path(const char *name, +make_group_path(std::string name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, - const char *comment) + std::string comment) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); - sta->makeGroupPath(name, is_default, from, thrus, to, comment, sdc); + sta->makeGroupPath(name, is_default, from, thrus, to, std::move(comment), sdc); } bool -is_path_group_name(const char *name) +is_path_group_name(std::string name) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); @@ -879,8 +881,7 @@ make_exception_from(PinSet *from_pins, { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); - return sta->makeExceptionFrom(from_pins, from_clks, from_insts, - from_rf, sdc); + return sta->makeExceptionFrom(from_pins, from_clks, from_insts, from_rf, sdc); } void @@ -892,12 +893,12 @@ delete_exception_from(ExceptionFrom *from) void check_exception_from_pins(ExceptionFrom *from, - const char *file, + const char *filename, int line) { Sta *sta = Sta::sta(); const Sdc *sdc = sta->cmdSdc(); - sta->checkExceptionFromPins(from, file, line, sdc); + sta->checkExceptionFromPins(from, filename, line, sdc); } ExceptionThru * @@ -950,18 +951,18 @@ check_exception_to_pins(ExceptionTo *to, //////////////////////////////////////////////////////////////// ClockGroups * -make_clock_groups(const char *name, +make_clock_groups(std::string name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, - const char *comment) + std::string comment) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); return sta->makeClockGroups(name, logically_exclusive, physically_exclusive, asynchronous, - allow_paths, comment, sdc); + allow_paths, std::move(comment), sdc); } void @@ -982,7 +983,7 @@ unset_clock_groups_logically_exclusive() } void -unset_clock_groups_logically_exclusive(const char *name) +unset_clock_groups_logically_exclusive(std::string name) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); @@ -998,7 +999,7 @@ unset_clock_groups_physically_exclusive() } void -unset_clock_groups_physically_exclusive(const char *name) +unset_clock_groups_physically_exclusive(std::string name) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); @@ -1014,7 +1015,7 @@ unset_clock_groups_asynchronous() } void -unset_clock_groups_asynchronous(const char *name) +unset_clock_groups_asynchronous(std::string name) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); @@ -1332,7 +1333,7 @@ unset_timing_derate_cmd() } Clock * -find_clock(const char *name) +find_clock(std::string name) { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); @@ -1356,7 +1357,7 @@ default_arrival_clock() } ClockSeq -find_clocks_matching(const char *pattern, +find_clocks_matching(std::string pattern, bool regexp, bool nocase) { @@ -1490,28 +1491,27 @@ find_register_output_pins(ClockSet *clks, //////////////////////////////////////////////////////////////// template std::vector -filter_objects(const char *property, - const char *op, - const char *pattern, +filter_objects(std::string_view property, + std::string_view op, + std::string_view pattern, std::vector *objects) { std::vector filtered_objects; if (objects) { Sta *sta = Sta::sta(); Properties &properties = sta->properties(); - bool exact_match = stringEq(op, "=="); - bool pattern_match = stringEq(op, "=~"); - bool not_match = stringEq(op, "!="); - bool not_pattern_match = stringEq(op, "!~"); + bool exact_match = op == "=="; + bool pattern_match = op == "=~"; + bool not_match = op == "!="; + bool not_pattern_match = op == "!~"; for (T *object : *objects) { PropertyValue value(properties.getProperty(object, property)); - std::string prop_str = value.to_string(sta->network()); - const char *prop = prop_str.c_str(); - if (!prop_str.empty() - && ((exact_match && stringEq(prop, pattern)) - || (not_match && !stringEq(prop, pattern)) - || (pattern_match && patternMatch(pattern, prop)) - || (not_pattern_match && !patternMatch(pattern, prop)))) + std::string prop_value = value.to_string(sta->network()); + if (!prop_value.empty() + && ((exact_match && prop_value == pattern) + || (not_match && prop_value != pattern) + || (pattern_match && patternMatch(pattern, prop_value)) + || (not_pattern_match && !patternMatch(pattern, prop_value)))) filtered_objects.push_back(object); } delete objects; @@ -1520,81 +1520,81 @@ filter_objects(const char *property, } PortSeq -filter_ports(const char *property, - const char *op, - const char *pattern, +filter_ports(std::string_view property, + std::string_view op, + std::string_view pattern, PortSeq *ports) { return filter_objects(property, op, pattern, ports); } InstanceSeq -filter_insts(const char *property, - const char *op, - const char *pattern, +filter_insts(std::string_view property, + std::string_view op, + std::string_view pattern, InstanceSeq *insts) { return filter_objects(property, op, pattern, insts); } PinSeq -filter_pins(const char *property, - const char *op, - const char *pattern, +filter_pins(std::string_view property, + std::string_view op, + std::string_view pattern, PinSeq *pins) { return filter_objects(property, op, pattern, pins); } ClockSeq -filter_clocks(const char *property, - const char *op, - const char *pattern, +filter_clocks(std::string_view property, + std::string_view op, + std::string_view pattern, ClockSeq *clocks) { return filter_objects(property, op, pattern, clocks); } LibertyCellSeq -filter_lib_cells(const char *property, - const char *op, - const char *pattern, +filter_lib_cells(std::string_view property, + std::string_view op, + std::string_view pattern, LibertyCellSeq *cells) { return filter_objects(property, op, pattern, cells); } LibertyPortSeq -filter_lib_pins(const char *property, - const char *op, - const char *pattern, +filter_lib_pins(std::string_view property, + std::string_view op, + std::string_view pattern, LibertyPortSeq *pins) { return filter_objects(property, op, pattern, pins); } LibertyLibrarySeq -filter_liberty_libraries(const char *property, - const char *op, - const char *pattern, +filter_liberty_libraries(std::string_view property, + std::string_view op, + std::string_view pattern, LibertyLibrarySeq *libs) { return filter_objects(property, op, pattern, libs); } NetSeq -filter_nets(const char *property, - const char *op, - const char *pattern, +filter_nets(std::string_view property, + std::string_view op, + std::string_view pattern, NetSeq *nets) { return filter_objects(property, op, pattern, nets); } EdgeSeq -filter_timing_arcs(const char *property, - const char *op, - const char *pattern, +filter_timing_arcs(std::string_view property, + std::string_view op, + std::string_view pattern, EdgeSeq *edges) { return filter_objects(property, op, pattern, edges); diff --git a/sdc/SdcCmdComment.cc b/sdc/SdcCmdComment.cc index 3033ad630..083812ad7 100644 --- a/sdc/SdcCmdComment.cc +++ b/sdc/SdcCmdComment.cc @@ -27,30 +27,34 @@ namespace sta { -SdcCmdComment::SdcCmdComment() : - comment_(nullptr) +SdcCmdComment::SdcCmdComment() { } -SdcCmdComment::SdcCmdComment(const char *comment) +SdcCmdComment::SdcCmdComment(std::string comment) : + comment_(std::move(comment)) { - comment_ = nullptr; - if (comment && comment[0] != '\0') - comment_ = stringCopy(comment); } - + +SdcCmdComment::SdcCmdComment(std::string_view comment) : + comment_(comment) +{ +} + SdcCmdComment::~SdcCmdComment() { - stringDelete(comment_); } + +void +SdcCmdComment::setComment(std::string comment) +{ + comment_ = std::move(comment); +} + void -SdcCmdComment::setComment(const char *comment) +SdcCmdComment::setComment(std::string_view comment) { - if (comment_) - stringDelete(comment_); - comment_ = nullptr; - if (comment && comment[0] != '\0') - comment_ = stringCopy(comment); + comment_ = comment; } } // namespace diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index 7688fc684..3af1de6a0 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -24,12 +24,12 @@ #include "WriteSdc.hh" -#include #include #include #include #include #include +#include #include "ContainerHelpers.hh" #include "Format.hh" @@ -68,19 +68,19 @@ namespace sta { typedef std::set ClockSenseSet; typedef std::vector ClockSenseSeq; -static const char * +static std::string_view transRiseFallFlag(const RiseFall *rf); -static const char * +static std::string_view transRiseFallFlag(const RiseFallBoth *rf); -static const char * +static std::string_view minMaxFlag(const MinMaxAll *min_max); -static const char * +static std::string_view minMaxFlag(const MinMax *min_max); -static const char * +static std::string_view earlyLateFlag(const MinMax *early_late); -static const char * +static std::string_view setupHoldFlag(const MinMax *min_max); -static const char * +static std::string_view timingDerateTypeKeyword(TimingDerateType type); //////////////////////////////////////////////////////////////// @@ -287,8 +287,8 @@ WriteGetClock::write() const void writeSdc(const Sdc *sdc, Instance *instance, - const char *filename, - const char *creator, + std::string_view filename, + std::string_view creator, bool map_hpins, bool native, int digits, @@ -302,7 +302,7 @@ writeSdc(const Sdc *sdc, WriteSdc::WriteSdc(const Sdc *sdc, Instance *instance, - const char *creator, + std::string_view creator, bool map_hpins, bool native, int digits, @@ -316,7 +316,7 @@ WriteSdc::WriteSdc(const Sdc *sdc, digits_(digits), no_timestamp_(no_timestamp), top_instance_(instance == sdc_network_->topInstance()), - instance_name_length_(strlen(sdc_network_->pathName(instance))), + instance_name_length_(sdc_network_->pathName(instance).size()), cell_(sdc_network_->cell(instance)) { } @@ -326,7 +326,7 @@ WriteSdc::~WriteSdc() } void -WriteSdc::write(const char *filename, +WriteSdc::write(std::string_view filename, bool gzip) { openFile(filename, gzip); @@ -339,10 +339,11 @@ WriteSdc::write(const char *filename, } void -WriteSdc::openFile(const char *filename, +WriteSdc::openFile(std::string_view filename, bool gzip) { - stream_ = gzopen(filename, gzip ? "wb" : "wT"); + std::string filename_str(filename); + stream_ = gzopen(filename_str.c_str(), gzip ? "wb" : "wT"); if (stream_ == nullptr) throw FileNotWritable(filename); } @@ -361,9 +362,9 @@ WriteSdc::writeHeader() const if (!no_timestamp_) { time_t now; time(&now); - char *time_str = ctime(&now); - // Remove trailing \n. - time_str[strlen(time_str) - 1] = '\0'; + std::string time_str(ctime(&now)); + if (!time_str.empty() && time_str.back() == '\n') + time_str.pop_back(); sta::print(stream_, "# {}\n", time_str); } writeCommentSeparator(); @@ -521,7 +522,7 @@ WriteSdc::writeClockUncertainty(const Clock *clk) const void WriteSdc::writeClockUncertainty(const Clock *clk, - const char *setup_hold, + std::string_view setup_hold, float value) const { sta::print(stream_, "set_clock_uncertainty {}", setup_hold); @@ -558,7 +559,7 @@ WriteSdc::writeClockUncertaintyPin(const Pin *pin, } void WriteSdc::writeClockUncertaintyPin(const Pin *pin, - const char *setup_hold, + std::string_view setup_hold, float value) const { sta::print(stream_, "set_clock_uncertainty {}", setup_hold); @@ -726,7 +727,7 @@ WriteSdc::writeOutputDelays() const void WriteSdc::writePortDelay(PortDelay *port_delay, bool is_input_delay, - const char *sdc_cmd) const + std::string_view sdc_cmd) const { RiseFallMinMax *delays = port_delay->delays(); float rise_min, rise_max, fall_min, fall_max; @@ -793,7 +794,7 @@ WriteSdc::writePortDelay(PortDelay *port_delay, float delay, const RiseFallBoth *rf, const MinMaxAll *min_max, - const char *sdc_cmd) const + std::string_view sdc_cmd) const { sta::print(stream_, "{} ", sdc_cmd); writeTime(delay); @@ -870,7 +871,7 @@ void WriteSdc::writeClockSense(PinClockPair &pin_clk, ClockSense sense) const { - const char *flag = nullptr; + std::string_view flag; if (sense == ClockSense::positive) flag = "-positive"; else if (sense == ClockSense::negative) @@ -923,7 +924,7 @@ ClockGroupLess::operator()(const ClockGroup *clk_group1, && clk_iter4 != clks2.end()) { Clock *clk1 = *clk_iter3++; Clock *clk2 = *clk_iter4++; - int cmp = strcmp(clk1->name(), clk2->name()); + int cmp = clk1->name().compare(clk2->name()); if (cmp < 0) return true; else if (cmp > 0) @@ -1166,9 +1167,8 @@ void WriteSdc::writeDisabledEdgeSense(Edge *edge) const { sta::print(stream_, "set_disable_timing "); - const char *sense = to_string(edge->sense()); - std::string filter = sta::format("sense == {}", sense); - writeGetTimingArcs(edge, filter.c_str()); + std::string filter = sta::format("sense == {}", to_string(edge->sense())); + writeGetTimingArcs(edge, filter); sta::print(stream_, "\n"); } @@ -1275,11 +1275,11 @@ WriteSdc::writeExceptionTo(ExceptionTo *to) const void WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to, - const char *from_to_key, + std::string_view from_to_key, bool map_hpin_to_drvr) const { const RiseFallBoth *rf = from_to->transition(); - const char *rf_prefix = "-"; + std::string_view rf_prefix = "-"; if (rf == RiseFallBoth::rise()) rf_prefix = "-rise_"; else if (rf == RiseFallBoth::fall()) @@ -1320,7 +1320,7 @@ void WriteSdc::writeExceptionThru(ExceptionThru *thru) const { const RiseFallBoth *rf = thru->transition(); - const char *rf_prefix = "-"; + std::string_view rf_prefix = "-"; if (rf == RiseFallBoth::rise()) rf_prefix = "-rise_"; else if (rf == RiseFallBoth::fall()) @@ -1441,14 +1441,14 @@ WriteSdc::writeDataCheck(DataCheck *check, const SetupHold *setup_hold, float margin) const { - const char *from_key = "-from"; + std::string_view from_key = "-from"; if (from_rf == RiseFallBoth::rise()) from_key = "-rise_from"; else if (from_rf == RiseFallBoth::fall()) from_key = "-fall_from"; sta::print(stream_, "set_data_check {} ", from_key); writeGetPin(check->from(), true); - const char *to_key = "-to"; + std::string_view to_key = "-to"; if (to_rf == RiseFallBoth::rise()) to_key = "-rise_to"; else if (to_rf == RiseFallBoth::fall()) @@ -1755,13 +1755,13 @@ WriteSdc::writeConstants() const void WriteSdc::writeConstant(const Pin *pin) const { - const char *cmd = setConstantCmd(pin); + std::string_view cmd = setConstantCmd(pin); sta::print(stream_, "{} ", cmd); writeGetPin(pin, false); sta::print(stream_, "\n"); } -const char * +std::string_view WriteSdc::setConstantCmd(const Pin *pin) const { LogicValue value; @@ -1778,7 +1778,7 @@ WriteSdc::setConstantCmd(const Pin *pin) const case LogicValue::fall: default: report_->critical(1621, "illegal set_logic value"); - return nullptr; + return {}; } } @@ -1795,13 +1795,13 @@ WriteSdc::writeCaseAnalysis() const void WriteSdc::writeCaseAnalysis(const Pin *pin) const { - const char *value_str = caseAnalysisValueStr(pin); + std::string_view value_str = caseAnalysisValueStr(pin); sta::print(stream_, "set_case_analysis {} ", value_str); writeGetPin(pin, false); sta::print(stream_, "\n"); } -const char * +std::string_view WriteSdc::caseAnalysisValueStr(const Pin *pin) const { LogicValue value; @@ -1819,7 +1819,7 @@ WriteSdc::caseAnalysisValueStr(const Pin *pin) const case LogicValue::unknown: default: report_->critical(1622, "invalid set_case_analysis value"); - return nullptr; + return {}; } } @@ -1917,7 +1917,7 @@ WriteSdc::writeDerating(DeratingFactors *factors, const MinMax *early_late, WriteSdcObject *write_obj) const { - const char *type_key = timingDerateTypeKeyword(type); + std::string_view type_key = timingDerateTypeKeyword(type); bool is_one_value; float value; factors->isOneValue(early_late, is_one_value, value); @@ -1939,8 +1939,8 @@ WriteSdc::writeDerating(DeratingFactors *factors, clk_data_index < path_clk_or_data_count; clk_data_index++) { PathClkOrData clk_data = static_cast(clk_data_index); - static const char *clk_data_keys[] = {"-clock", "-data"}; - const char *clk_data_key = clk_data_keys[clk_data_index]; + static constexpr std::string_view clk_data_keys[] = {"-clock", "-data"}; + std::string_view clk_data_key = clk_data_keys[clk_data_index]; factors->isOneValue(clk_data, early_late, is_one_value, value); if (is_one_value) { if (value != 1.0) { @@ -1980,10 +1980,11 @@ WriteSdc::writeDerating(DeratingFactors *factors, } } -static const char * +static std::string_view timingDerateTypeKeyword(TimingDerateType type) { - static const char *type_keys[] = {"-cell_delay","-cell_check","-net_delay"}; + static constexpr std::string_view type_keys[] = { + "-cell_delay", "-cell_check", "-net_delay"}; return type_keys[static_cast(type)]; } @@ -2075,7 +2076,7 @@ WriteSdc::writeMinPulseWidths(RiseFallValues *min_widths, } void -WriteSdc::writeMinPulseWidth(const char *hi_low, +WriteSdc::writeMinPulseWidth(std::string_view hi_low, float value, WriteSdcObject &write_obj) const { @@ -2195,8 +2196,8 @@ WriteSdc::writeClkSlewLimits() const } void -WriteSdc::writeClkSlewLimit(const char *clk_data, - const char *rise_fall, +WriteSdc::writeClkSlewLimit(std::string_view clk_data, + std::string_view rise_fall, const Clock *clk, float limit) const { @@ -2216,7 +2217,7 @@ WriteSdc::writeCapLimits() const void WriteSdc::writeCapLimits(const MinMax *min_max, - const char *cmd) const + std::string_view cmd) const { float cap; bool exists; @@ -2274,7 +2275,7 @@ WriteSdc::writeFanoutLimits() const void WriteSdc::writeFanoutLimits(const MinMax *min_max, - const char *cmd) const + std::string_view cmd) const { float fanout; bool exists; @@ -2330,15 +2331,9 @@ WriteSdc::writeGetTimingArcsOfOjbects(const LibertyCell *cell) const sta::print(stream_, "]"); } -void -WriteSdc::writeGetTimingArcs(Edge *edge) const -{ - writeGetTimingArcs(edge, nullptr); -} - void WriteSdc::writeGetTimingArcs(Edge *edge, - const char *filter) const + std::string_view filter) const { sta::print(stream_, "[{} -from ", getTimingArcsCmd()); Vertex *from_vertex = edge->from(graph_); @@ -2346,12 +2341,12 @@ WriteSdc::writeGetTimingArcs(Edge *edge, sta::print(stream_, " -to "); Vertex *to_vertex = edge->to(graph_); writeGetPin(to_vertex->pin(), false); - if (filter) + if (!filter.empty()) sta::print(stream_, " -filter {{{}}}", filter); sta::print(stream_, "]"); } -const char * +std::string_view WriteSdc::getTimingArcsCmd() const { return native_ ? "get_timing_edges" : "get_timing_arcs"; @@ -2493,38 +2488,38 @@ WriteSdc::writeGetInstance(const Instance *inst) const sta::print(stream_, "[get_cells {{{}}}]", pathName(inst)); } -const char * +std::string WriteSdc::pathName(const Pin *pin) const { - const char *pin_path = sdc_network_->pathName(pin); + std::string pin_path = sdc_network_->pathName(pin); if (top_instance_) return pin_path; else - return pin_path + instance_name_length_ + 1; + return pin_path.substr(instance_name_length_ + 1); } -const char * +std::string WriteSdc::pathName(const Net *net) const { - const char *net_path = sdc_network_->pathName(net); + std::string net_path = sdc_network_->pathName(net); if (top_instance_) return net_path; else - return net_path + instance_name_length_ + 1; + return net_path.substr(instance_name_length_ + 1); } -const char * +std::string WriteSdc::pathName(const Instance *inst) const { - const char *inst_path = sdc_network_->pathName(inst); + std::string inst_path = sdc_network_->pathName(inst); if (top_instance_) return inst_path; else - return inst_path + instance_name_length_ + 1; + return inst_path.substr(instance_name_length_ + 1); } void -WriteSdc::writeCommentSection(const char *line) const +WriteSdc::writeCommentSection(std::string_view line) const { writeCommentSeparator(); sta::print(stream_, "# {}\n", line); @@ -2540,7 +2535,7 @@ WriteSdc::writeCommentSeparator() const //////////////////////////////////////////////////////////////// void -WriteSdc::writeRiseFallMinMaxTimeCmd(const char *sdc_cmd, +WriteSdc::writeRiseFallMinMaxTimeCmd(std::string_view sdc_cmd, const RiseFallMinMax *values, WriteSdcObject &write_object) const { @@ -2549,7 +2544,7 @@ WriteSdc::writeRiseFallMinMaxTimeCmd(const char *sdc_cmd, } void -WriteSdc::writeRiseFallMinMaxCapCmd(const char *sdc_cmd, +WriteSdc::writeRiseFallMinMaxCapCmd(std::string_view sdc_cmd, const RiseFallMinMax *values, WriteSdcObject &write_object) const { @@ -2558,7 +2553,7 @@ WriteSdc::writeRiseFallMinMaxCapCmd(const char *sdc_cmd, } void -WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, +WriteSdc::writeRiseFallMinMaxCmd(std::string_view sdc_cmd, const RiseFallMinMax *values, float scale, WriteSdcObject &write_object) const @@ -2625,7 +2620,7 @@ WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, } void -WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, +WriteSdc::writeRiseFallMinMaxCmd(std::string_view sdc_cmd, float value, float scale, const RiseFallBoth *rf, @@ -2652,7 +2647,7 @@ WriteSdc::writeClockKey(const Clock *clk) const //////////////////////////////////////////////////////////////// void -WriteSdc::writeMinMaxFloatValuesCmd(const char *sdc_cmd, +WriteSdc::writeMinMaxFloatValuesCmd(std::string_view sdc_cmd, const MinMaxFloatValues *values, float scale, WriteSdcObject &write_object) const @@ -2675,7 +2670,7 @@ WriteSdc::writeMinMaxFloatValuesCmd(const char *sdc_cmd, } void -WriteSdc::writeMinMaxFloatCmd(const char *sdc_cmd, +WriteSdc::writeMinMaxFloatCmd(std::string_view sdc_cmd, float value, float scale, const MinMaxAll *min_max, @@ -2691,7 +2686,7 @@ WriteSdc::writeMinMaxFloatCmd(const char *sdc_cmd, } void -WriteSdc::writeMinMaxIntValuesCmd(const char *sdc_cmd, +WriteSdc::writeMinMaxIntValuesCmd(std::string_view sdc_cmd, const MinMaxIntValues *values, WriteSdcObject &write_object) const { @@ -2713,7 +2708,7 @@ WriteSdc::writeMinMaxIntValuesCmd(const char *sdc_cmd, } void -WriteSdc::writeMinMaxIntCmd(const char *sdc_cmd, +WriteSdc::writeMinMaxIntCmd(std::string_view sdc_cmd, int value, const MinMaxAll *min_max, WriteSdcObject &write_object) const @@ -2802,13 +2797,13 @@ WriteSdc::writeIntSeq(IntSeq *ints) const //////////////////////////////////////////////////////////////// -static const char * +static std::string_view transRiseFallFlag(const RiseFall *rf) { return (rf == RiseFall::rise()) ? "-rise" : "-fall"; } -static const char * +static std::string_view transRiseFallFlag(const RiseFallBoth *rf) { if (rf == RiseFallBoth::rise()) @@ -2819,7 +2814,7 @@ transRiseFallFlag(const RiseFallBoth *rf) return ""; } -static const char * +static std::string_view minMaxFlag(const MinMaxAll *min_max) { if (min_max == MinMaxAll::min()) @@ -2830,13 +2825,13 @@ minMaxFlag(const MinMaxAll *min_max) return ""; } -static const char * +static std::string_view minMaxFlag(const MinMax *min_max) { return (min_max == MinMax::min()) ? " -min" : " -max"; } -static const char * +static std::string_view earlyLateFlag(const MinMax *early_late) { return (early_late == MinMax::min()) ? "-early" : "-late"; @@ -2851,7 +2846,7 @@ WriteSdc::writeSetupHoldFlag(const MinMaxAll *min_max) const sta::print(stream_, " -setup"); } -static const char * +static std::string_view setupHoldFlag(const MinMax *min_max) { return (min_max == MinMax::min()) ? " -hold" : " -setup"; @@ -2860,10 +2855,9 @@ setupHoldFlag(const MinMax *min_max) void WriteSdc::writeCmdComment(SdcCmdComment *cmd) const { - const char *comment = cmd->comment(); - if (comment) { + const std::string comment = cmd->comment(); + if (!comment.empty()) sta::print(stream_, " -comment {{{}}}", comment); - } } } // namespace diff --git a/sdc/WriteSdc.hh b/sdc/WriteSdc.hh index 3ac822e16..0e2b33c32 100644 --- a/sdc/WriteSdc.hh +++ b/sdc/WriteSdc.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "NetworkClass.hh" #include "SdcClass.hh" @@ -34,8 +36,8 @@ namespace sta { void writeSdc(const Sdc *sdc, Instance *instance, - const char *filename, - const char *creator, + std::string_view filename, + std::string_view creator, // Map hierarchical pins and instances to leaf pins and instances. bool map_hpins, // Replace non-sdc get functions with OpenSTA equivalents. diff --git a/sdc/WriteSdcPvt.hh b/sdc/WriteSdcPvt.hh index b4adae560..88600c77e 100644 --- a/sdc/WriteSdcPvt.hh +++ b/sdc/WriteSdcPvt.hh @@ -24,6 +24,9 @@ #pragma once +#include +#include + #include "Zlib.hh" #include "NetworkClass.hh" #include "GraphClass.hh" @@ -38,16 +41,16 @@ class WriteSdc : public StaState public: WriteSdc(const Sdc *sdc, Instance *instance, - const char *creator, + std::string_view creator, bool map_hpins, bool native, int digits, bool no_timestamp); virtual ~WriteSdc(); - void write(const char *filename, + void write(std::string_view filename, bool gzip); - void openFile(const char *filename, + void openFile(std::string_view filename, bool gzip); void closeFile(); virtual void writeHeader() const; @@ -75,13 +78,13 @@ public: void writeClockSlews(const Clock *clk) const; void writeClockUncertainty(const Clock *clk) const; void writeClockUncertainty(const Clock *clk, - const char *setup_hold, + std::string_view setup_hold, float value) const; void writeClockUncertaintyPins() const; void writeClockUncertaintyPin(const Pin *pin, ClockUncertainties *uncertainties) const; void writeClockUncertaintyPin(const Pin *pin, - const char *setup_hold, + std::string_view setup_hold, float value) const; void writeClockLatencies() const; void writeClockInsertions() const; @@ -94,13 +97,13 @@ public: void writeOutputDelays() const; void writePortDelay(PortDelay *port_delay, bool is_input_delay, - const char *sdc_cmd) const; + std::string_view sdc_cmd) const; void writePortDelay(PortDelay *port_delay, bool is_input_delay, float delay, const RiseFallBoth *rf, const MinMaxAll *min_max, - const char *sdc_cmd) const; + std::string_view sdc_cmd) const; void writeClockSenses() const; void writeClockSense(PinClockPair &pin_clk, ClockSense sense) const; @@ -113,7 +116,7 @@ public: void writeExceptionFrom(ExceptionFrom *from) const; void writeExceptionTo(ExceptionTo *to) const; void writeExceptionFromTo(ExceptionFromTo *from_to, - const char *from_to_key, + std::string_view from_to_key, bool map_hpin_to_drvr) const; void writeExceptionThru(ExceptionThru *thru) const; void mapThruHpins(ExceptionThru *thru, @@ -144,10 +147,10 @@ public: const MinMax *min_max) const; void writeConstants() const; virtual void writeConstant(const Pin *pin) const; - const char *setConstantCmd(const Pin *pin) const; + std::string_view setConstantCmd(const Pin *pin) const; void writeCaseAnalysis() const; virtual void writeCaseAnalysis(const Pin *pin) const; - const char *caseAnalysisValueStr(const Pin *pin) const; + std::string_view caseAnalysisValueStr(const Pin *pin) const; void sortedLogicValuePins(const LogicValueMap &value_map, PinSeq &pins) const; void writeNetResistances() const; @@ -158,17 +161,17 @@ public: void writeMinPulseWidths() const; void writeMinPulseWidths(RiseFallValues *min_widths, WriteSdcObject &write_obj) const; - void writeMinPulseWidth(const char *hi_low, + void writeMinPulseWidth(std::string_view hi_low, float value, WriteSdcObject &write_obj) const; void writeSlewLimits() const; void writeCapLimits() const; void writeCapLimits(const MinMax *min_max, - const char *cmd) const; + std::string_view cmd) const; void writeMaxArea() const; void writeFanoutLimits() const; void writeFanoutLimits(const MinMax *min_max, - const char *cmd) const; + std::string_view cmd) const; void writeLatchBorowLimits() const; void writeDeratings() const; void writeDerating(DeratingFactorsGlobal *factors) const; @@ -180,17 +183,16 @@ public: WriteSdcObject *write_obj) const; void writeVoltages() const; - const char *pathName(const Pin *pin) const; - const char *pathName(const Net *net) const; - const char *pathName(const Instance *inst) const; - void writeCommentSection(const char *line) const; + std::string pathName(const Pin *pin) const; + std::string pathName(const Net *net) const; + std::string pathName(const Instance *inst) const; + void writeCommentSection(std::string_view line) const; void writeCommentSeparator() const; void writeGetTimingArcsOfOjbects(const LibertyCell *cell) const; - void writeGetTimingArcs(Edge *edge) const; void writeGetTimingArcs(Edge *edge, - const char *filter) const; - const char *getTimingArcsCmd() const; + std::string_view filter = {}) const; + std::string_view getTimingArcsCmd() const; void writeGetLibCell(const LibertyCell *cell) const; void writeGetLibPin(const LibertyPort *port) const; void writeGetClock(const Clock *clk) const; @@ -217,39 +219,39 @@ public: void writeResistance(float res) const; void writeClkSlewLimits() const; - void writeClkSlewLimit(const char *clk_data, - const char *rise_fall, + void writeClkSlewLimit(std::string_view clk_data, + std::string_view rise_fall, const Clock *clk, float limit) const; - void writeRiseFallMinMaxTimeCmd(const char *sdc_cmd, + void writeRiseFallMinMaxTimeCmd(std::string_view sdc_cmd, const RiseFallMinMax *values, WriteSdcObject &write_object) const; - void writeRiseFallMinMaxCapCmd(const char *sdc_cmd, + void writeRiseFallMinMaxCapCmd(std::string_view sdc_cmd, const RiseFallMinMax *values, WriteSdcObject &write_object) const; - void writeRiseFallMinMaxCmd(const char *sdc_cmd, + void writeRiseFallMinMaxCmd(std::string_view sdc_cmd, const RiseFallMinMax *values, float scale, WriteSdcObject &write_object) const; - void writeRiseFallMinMaxCmd(const char *sdc_cmd, + void writeRiseFallMinMaxCmd(std::string_view sdc_cmd, float value, float scale, const RiseFallBoth *rf, const MinMaxAll *min_max, WriteSdcObject &write_object) const; - void writeMinMaxFloatValuesCmd(const char *sdc_cmd, + void writeMinMaxFloatValuesCmd(std::string_view sdc_cmd, const MinMaxFloatValues *values, float scale, WriteSdcObject &write_object) const; - void writeMinMaxFloatCmd(const char *sdc_cmd, + void writeMinMaxFloatCmd(std::string_view sdc_cmd, float value, float scale, const MinMaxAll *min_max, WriteSdcObject &write_object) const; - void writeMinMaxIntValuesCmd(const char *sdc_cmd, + void writeMinMaxIntValuesCmd(std::string_view sdc_cmd, const MinMaxIntValues *values, WriteSdcObject &write_object) const; - void writeMinMaxIntCmd(const char *sdc_cmd, + void writeMinMaxIntCmd(std::string_view sdc_cmd, int value, const MinMaxAll *min_max, WriteSdcObject &write_object) const; @@ -262,7 +264,7 @@ public: protected: const Sdc *sdc_; Instance *instance_; - const char *creator_; + std::string creator_; bool map_hpins_; bool native_; int digits_; diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc index 8f667b71c..dce57ee1e 100644 --- a/sdf/ReportAnnotation.cc +++ b/sdf/ReportAnnotation.cc @@ -24,6 +24,8 @@ #include "sdf/ReportAnnotation.hh" +#include + #include "StringUtil.hh" #include "Report.hh" #include "TimingRole.hh" @@ -91,7 +93,7 @@ class ReportAnnotated : public StaState void reportPeriodArcs(const Pin *pin, bool report_annotated, int &i); - void reportCount(const char *title, + void reportCount(std::string_view title, int index, int &total, int &annotated_total); @@ -301,7 +303,7 @@ ReportAnnotated::reportCheckCount(const TimingRole *role, int index = role->index(); if (edge_count_[index] > 0) { std::string title = sta::format("cell {} arcs", role->to_string()); - reportCount(title.c_str(), index, total, annotated_total); + reportCount(title, index, total, annotated_total); } } @@ -432,7 +434,7 @@ ReportAnnotated::findPeriodCount(Pin *pin) } void -ReportAnnotated::reportCount(const char *title, +ReportAnnotated::reportCount(std::string_view title, int index, int &total, int &annotated_total) @@ -496,9 +498,9 @@ ReportAnnotated::reportArcs(Vertex *vertex, const Pin *to_pin = edge->to(graph_)->pin(); if (delayAnnotated(edge) == report_annotated && report_role_[roleIndex(role, from_pin, to_pin)]) { - const char *role_name; + std::string_view role_name; if (role->isTimingCheck()) - role_name = role->to_string().c_str(); + role_name = role->to_string(); else if (role->isWire()) { if (network_->isTopLevelPort(from_pin)) role_name = "primary input net"; diff --git a/sdf/Sdf.i b/sdf/Sdf.i index 66c479249..89b1fdd28 100644 --- a/sdf/Sdf.i +++ b/sdf/Sdf.i @@ -25,7 +25,7 @@ %module sdf %{ -#include "sdf/SdfReader.hh" +#include #include "sdf/ReportAnnotation.hh" #include "sdf/SdfWriter.hh" #include "Search.hh" @@ -35,8 +35,6 @@ using sta::Sta; using sta::AnalysisType; using sta::MinMax; using sta::MinMaxAllNull; -using sta::stringEq; -using sta::readSdf; using sta::reportAnnotatedDelay; using sta::reportAnnotatedCheck; @@ -51,22 +49,16 @@ using sta::reportAnnotatedCheck; // Return true if successful. bool -read_sdf_file(const char *filename, - const char *path, +read_sdf_file(std::string filename, + std::string path, Scene *scene, bool unescaped_dividers, bool incremental_only, MinMaxAllNull *cond_use) { Sta *sta = Sta::sta(); - sta->ensureLibLinked(); - sta->ensureGraph(); - if (stringEq(path, "")) - path = NULL; - bool success = readSdf(filename, path, scene, unescaped_dividers, - incremental_only, cond_use, sta); - sta->search()->arrivalsInvalid(); - return success; + return sta->readSdf(filename, path, scene, unescaped_dividers, + incremental_only, cond_use); } void diff --git a/sdf/SdfLex.ll b/sdf/SdfLex.ll index 19849ff39..518915c71 100644 --- a/sdf/SdfLex.ll +++ b/sdf/SdfLex.ll @@ -23,6 +23,10 @@ // // This notice may not be removed or altered from any source distribution. +#include +#include +#include + #include "util/FlexDisableRegister.hh" #include "sdf/SdfReaderPvt.hh" #include "SdfParse.hh" @@ -82,7 +86,7 @@ EOL \r?\n "\"" { BEGIN INITIAL; - yylval->string = new std::string(token_); + yylval->emplace(std::move(token_)); return token::QSTRING; } @@ -98,12 +102,12 @@ EOL \r?\n "//"[^\n]*{EOL} { loc->lines(); loc->step(); } ("-"|"+")?([0-9]*)("."[0-9]+)([eE]("-"|"+")?[0-9]+)? { - yylval->number = atof(yytext); + yylval->emplace(static_cast(atof(yytext))); return token::FNUMBER; } "+"?[0-9]+ { - yylval->integer = atoi(yytext); + yylval->emplace(atoi(yytext)); return token::DNUMBER; } @@ -157,7 +161,7 @@ COND { "("{BLANK}*IOPATH { BEGIN INITIAL; - yylval->string = new std::string(token_); + yylval->emplace(std::move(token_)); return token::EXPR_OPEN_IOPATH; } @@ -167,7 +171,7 @@ COND { */ if (reader_->inTimingCheck()) { BEGIN INITIAL; - yylval->string = new std::string(token_); + yylval->emplace(std::move(token_)); return token::EXPR_OPEN; } else @@ -181,7 +185,7 @@ COND { /* remove trailing ")" */ std::string cond_id(token_); cond_id += yytext; - yylval->string = new std::string(cond_id.substr(0, cond_id.size() - 1)); + yylval->emplace(cond_id.substr(0, cond_id.size() - 1)); /* No way to pass expr and id separately, so pass them together. */ return token::EXPR_ID_CLOSE; } @@ -194,7 +198,7 @@ COND { . { token_ += yytext[0]; } {ID} { - yylval->string = new std::string(yytext); + yylval->emplace(yytext); return token::ID; } diff --git a/sdf/SdfParse.yy b/sdf/SdfParse.yy index 6c9c3efb1..7570f5df7 100644 --- a/sdf/SdfParse.yy +++ b/sdf/SdfParse.yy @@ -24,6 +24,8 @@ %{ #include +#include +#include #include "sdf/SdfReaderPvt.hh" #include "sdf/SdfScanner.hh" @@ -52,21 +54,14 @@ sta::SdfParse::error(const location_type &loc, %parse-param { SdfScanner *scanner } %parse-param { SdfReader *reader } %define api.parser.class {SdfParse} +%define api.value.type variant // expected shift/reduce conflicts %expect 4 -%union { - char character; - std::string *string; - float number; - float *number_ptr; - int integer; - sta::SdfTriple *triple; - sta::SdfTripleSeq *delval_list; - sta::SdfPortSpec *port_spec; - const sta::Transition *transition; -} +%token QSTRING ID EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE +%token FNUMBER +%token DNUMBER %token DELAYFILE SDFVERSION DESIGN DATE VENDOR PROGRAM PVERSION %token DIVIDER VOLTAGE PROCESS TEMPERATURE TIMESCALE @@ -75,21 +70,15 @@ sta::SdfParse::error(const location_type &loc, %token IOPATH TIMINGCHECK %token SETUP HOLD SETUPHOLD RECOVERY REMOVAL RECREM WIDTH PERIOD SKEW NOCHANGE %token POSEDGE NEGEDGE COND CONDELSE -%token QSTRING ID FNUMBER DNUMBER EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE - -%type FNUMBER NUMBER -%type DNUMBER -%type number_opt -%type value triple -%type delval_list -%type QSTRING ID path port port_instance -%type EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE -%type port_spec port_tchk -%type port_transition -%type hchar - -// Used by error recovery. -%destructor { delete $$; } QSTRING + +%type NUMBER +%type number_opt +%type value triple +%type delval_list +%type path port port_instance +%type port_spec port_tchk +%type port_transition +%type hchar %start file @@ -106,17 +95,17 @@ header: // technically the ordering of these statements is fixed by the spec header_stmt: - '(' SDFVERSION QSTRING ')' { delete $3; } -| '(' DESIGN QSTRING ')' { delete $3; } -| '(' DATE QSTRING ')' { delete $3; } -| '(' VENDOR QSTRING ')' { delete $3; } -| '(' PROGRAM QSTRING ')' { delete $3; } -| '(' PVERSION QSTRING ')' { delete $3; } + '(' SDFVERSION QSTRING ')' +| '(' DESIGN QSTRING ')' +| '(' DATE QSTRING ')' +| '(' VENDOR QSTRING ')' +| '(' PROGRAM QSTRING ')' +| '(' PVERSION QSTRING ')' | '(' DIVIDER hchar ')' { reader->setDivider($3); } | '(' VOLTAGE triple ')' { reader->deleteTriple($3); } | '(' VOLTAGE NUMBER ')' | '(' VOLTAGE ')' // Illegal SDF (from OC). -| '(' PROCESS QSTRING ')' { delete $3; } +| '(' PROCESS QSTRING ')' | '(' PROCESS ')' // Illegal SDF (from OC). | '(' TEMPERATURE NUMBER ')' | '(' TEMPERATURE triple ')' { reader->deleteTriple($3); } @@ -152,7 +141,7 @@ celltype: cell_instance: '(' INSTANCE ')' - { reader->setInstance(nullptr); } + { reader->setInstance(); } | '(' INSTANCE '*' ')' { reader->setInstanceWildcard(); } | '(' INSTANCE path ')' @@ -195,10 +184,10 @@ path: del_def: '(' IOPATH port_spec port_instance retains delval_list ')' - { reader->iopath($3, $4, $6, nullptr, false); } + { reader->iopath($3, $4, $6, "", false); } | '(' CONDELSE '(' IOPATH port_spec port_instance retains delval_list ')' ')' - { reader->iopath($5, $6, $8, nullptr, true); } + { reader->iopath($5, $6, $8, "", true); } | '(' COND EXPR_OPEN_IOPATH port_spec port_instance retains delval_list ')' ')' { reader->iopath($4, $5, $7, $3, false); } @@ -296,15 +285,16 @@ port: port_instance: port + { $$ = $1; } | path hchar port { $$ = reader->makePath($1, $3); } ; port_spec: port_instance - { $$=reader->makePortSpec(sta::Transition::riseFall(),$1,nullptr); } + { $$ = reader->makePortSpec(sta::Transition::riseFall(), $1); } | '(' port_transition port_instance ')' - { $$ = reader->makePortSpec($2, $3, nullptr); } + { $$ = reader->makePortSpec($2, $3); } ; port_transition: @@ -314,6 +304,7 @@ port_transition: port_tchk: port_spec + { $$ = $1; } | '(' COND EXPR_ID_CLOSE { $$ = reader->makeCondPortSpec($3); } | '(' COND EXPR_OPEN port_transition port_instance ')' ')' @@ -352,6 +343,7 @@ triple: NUMBER: FNUMBER + { $$ = $1; } | DNUMBER { $$ = static_cast($1); } | '-' DNUMBER diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 65a2bb8e3..6a5bfd0b0 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include "ContainerHelpers.hh" #include "Zlib.hh" @@ -64,22 +65,21 @@ class SdfPortSpec { public: SdfPortSpec(const Transition *tr, - const std::string *port, - const std::string *cond); - ~SdfPortSpec(); - const std::string *port() const { return port_; } + std::string_view port, + std::string_view cond); + std::string_view port() const { return port_; } const Transition *transition() const { return tr_; } - const std::string *cond() const { return cond_; } + std::string_view cond() const { return cond_; } private: const Transition *tr_; - const std::string *port_; - const std::string *cond_; // timing checks only + const std::string port_; + const std::string cond_; // timing checks only }; bool -readSdf(const char *filename, - const char *path, +readSdf(std::string_view filename, + std::string_view path, Scene *scene, bool unescaped_dividers, bool incremental_only, @@ -95,8 +95,8 @@ readSdf(const char *filename, return success; } -SdfReader::SdfReader(const char *filename, - const char *path, +SdfReader::SdfReader(std::string_view filename, + std::string_view path, int arc_min_index, int arc_max_index, AnalysisType analysis_type, @@ -118,7 +118,6 @@ SdfReader::SdfReader(const char *filename, divider_('/'), escape_('\\'), instance_(nullptr), - cell_name_(nullptr), in_timing_check_(false), in_incremental_(false), timescale_(1.0E-9F) // default units of ns @@ -136,7 +135,7 @@ SdfReader::~SdfReader() bool SdfReader::read() { - gzstream::igzstream stream(filename_.c_str()); + gzstream::igzstream stream(std::string(filename_).c_str()); if (stream.is_open()) { Stats stats(debug_, report_); SdfScanner scanner(&stream, filename_, this, report_); @@ -158,26 +157,25 @@ SdfReader::setDivider(char divider) void SdfReader::setTimescale(float multiplier, - const std::string *units) + std::string_view units) { if (multiplier == 1.0 || multiplier == 10.0 || multiplier == 100.0) { - if (*units == "us") + if (units == "us") timescale_ = multiplier * 1E-6F; - else if (*units == "ns") + else if (units == "ns") timescale_ = multiplier * 1E-9F; - else if (*units == "ps") + else if (units == "ps") timescale_ = multiplier * 1E-12F; else error(180, "TIMESCALE units not us, ns, or ps."); } else error(181, "TIMESCALE multiplier not 1, 10, or 100."); - delete units; } void -SdfReader::interconnect(const std::string *from_pin_name, - const std::string *to_pin_name, +SdfReader::interconnect(std::string_view from_pin_name, + std::string_view to_pin_name, SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. @@ -194,38 +192,36 @@ 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) - error(182, "pin {} is a hierarchical pin.", *from_pin_name); + error(182, "pin {} is a hierarchical pin.", from_pin_name); if (to_is_hier) - error(183, "pin {} is a hierarchical pin.", *to_pin_name); + error(183, "pin {} is a hierarchical pin.", to_pin_name); } else warn(184, "INTERCONNECT from {} to {} not found.", - *from_pin_name, *to_pin_name); + from_pin_name, to_pin_name); } } else { if (from_pin == nullptr) - warn(185, "pin {} not found.", *from_pin_name); + warn(185, "pin {} not found.", from_pin_name); if (to_pin == nullptr) - warn(186, "pin {} not found.", *to_pin_name); + warn(186, "pin {} not found.", to_pin_name); } } - delete from_pin_name; - delete to_pin_name; deleteTripleSeq(triples); } void -SdfReader::port(const std::string *to_pin_name, +SdfReader::port(std::string_view to_pin_name, SdfTripleSeq *triples) { // 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) + : network_->findPin(to_pin_name); if (to_pin == nullptr) - warn(187, "pin {} not found.", *to_pin_name); + warn(187, "pin {} not found.", to_pin_name); else { Vertex *vertex = graph_->pinLoadVertex(to_pin); VertexInEdgeIterator edge_iter(vertex, graph_); @@ -236,7 +232,6 @@ SdfReader::port(const std::string *to_pin_name, } } } - delete to_pin_name; deleteTripleSeq(triples); } @@ -262,7 +257,7 @@ SdfReader::findWireEdge(Pin *from_pin, void SdfReader::setEdgeDelays(Edge *edge, SdfTripleSeq *triples, - const char *sdf_cmd) + std::string_view sdf_cmd) { // Rise/fall triples. size_t triple_count = triples->size(); @@ -285,59 +280,61 @@ SdfReader::setEdgeDelays(Edge *edge, } void -SdfReader::setCell(const std::string *cell_name) +SdfReader::setCell(std::string_view cell_name) { cell_name_ = cell_name; } void -SdfReader::setInstance(const std::string *instance_name) +SdfReader::setInstance() { - if (instance_name) { - if (*instance_name == "*") { - notSupported("INSTANCE wildcards"); - instance_ = nullptr; - } - else { - instance_ = findInstance(instance_name); - if (instance_) { - 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())) - warn(190, "instance {} cell {} does not match enclosing cell {}.", - *instance_name, inst_cell_name, *cell_name_); - } + instance_ = nullptr; +} + +void +SdfReader::setInstance(std::string_view instance_name) +{ + if (instance_name == "*") { + warn(193, "INSTANCE wildcards not supported."); + instance_ = nullptr; + } + else { + instance_ = findInstance(instance_name); + if (instance_) { + Cell *inst_cell = network_->cell(instance_); + std::string inst_cell_name(network_->name(inst_cell)); + if (inst_cell_name != cell_name_) + warn(190, "instance {} cell {} does not match enclosing cell {}.", + instance_name, + inst_cell_name, + cell_name_); } } - else - instance_ = nullptr; - delete instance_name; } void SdfReader::setInstanceWildcard() { - notSupported("INSTANCE wildcards"); + warn(172, "INSTANCE wildcards not supported."); instance_ = nullptr; } void SdfReader::cellFinish() { - delete cell_name_; - cell_name_ = nullptr; + cell_name_.clear(); instance_ = nullptr; } void SdfReader::iopath(SdfPortSpec *from_edge, - const std::string *to_port_name, + std::string_view to_port_name, SdfTripleSeq *triples, - const std::string *cond, + std::string_view cond, bool condelse) { if (instance_) { - const std::string *from_port_name = from_edge->port(); + std::string_view from_port_name = from_edge->port(); Cell *cell = network_->cell(instance_); Port *from_port = findPort(cell, from_port_name); Port *to_port = findPort(cell, to_port_name); @@ -360,8 +357,8 @@ SdfReader::iopath(SdfPortSpec *from_edge, TimingArcSet *arc_set = edge->timingArcSet(); 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_); + bool cond_use_flag = cond_use_ && !cond.empty() && lib_cond.empty() + && !(!is_incremental_only_ && in_incremental_); if (edge->from(graph_)->pin() == from_pin && edge_role->sdfRole() == TimingRole::sdfIopath() && (cond_use_flag @@ -392,26 +389,25 @@ SdfReader::iopath(SdfPortSpec *from_edge, } if (!matched) warn(191, "cell {} IOPATH {} -> {} not found.", - network_->cellName(instance_), *from_port_name, - *to_port_name); + network_->cellName(instance_), + from_port_name, + to_port_name); } } } } - delete to_port_name; delete from_edge; deleteTripleSeq(triples); - delete cond; } Port * SdfReader::findPort(const Cell *cell, - const std::string *port_name) + std::string_view port_name) { - Port *port = network_->findPort(cell, port_name->c_str()); + Port *port = network_->findPort(cell, port_name); if (port == nullptr) warn(194, "instance {} port {} not found.", network_->pathName(instance_), - *port_name); + port_name); return port; } @@ -422,8 +418,8 @@ SdfReader::timingCheck(const TimingRole *role, SdfTriple *triple) { if (instance_) { - const std::string *data_port_name = data_edge->port(); - const std::string *clk_port_name = clk_edge->port(); + std::string_view data_port_name = data_edge->port(); + std::string_view clk_port_name = clk_edge->port(); Cell *cell = network_->cell(instance_); Port *data_port = findPort(cell, data_port_name); Port *clk_port = findPort(cell, clk_port_name); @@ -495,8 +491,8 @@ SdfReader::annotateCheckEdges(Pin *data_pin, bool match_generic) { bool matched = false; - const std::string *cond_start = data_edge->cond(); - const std::string *cond_end = clk_edge->cond(); + std::string_view cond_start = data_edge->cond(); + std::string_view cond_end = clk_edge->cond(); // Timing check graph edges from clk to data. Vertex *to_vertex = graph_->pinLoadVertex(data_pin); // Fanin < fanout, so search for driver from load. @@ -506,10 +502,10 @@ SdfReader::annotateCheckEdges(Pin *data_pin, if (edge->from(graph_)->pin() == clk_pin) { TimingArcSet *arc_set = edge->timingArcSet(); 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(); + std::string_view lib_cond_start = arc_set->sdfCondStart(); + std::string_view lib_cond_end = arc_set->sdfCondEnd(); bool cond_matches = - condMatch(cond_start, lib_cond_start) && condMatch(cond_end, lib_cond_end); + 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())) && cond_matches) { @@ -535,11 +531,11 @@ SdfReader::timingCheckWidth(SdfPortSpec *edge, { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_) && instance_) { - const std::string *port_name = edge->port(); + std::string_view port_name = edge->port(); Cell *cell = network_->cell(instance_); Port *port = findPort(cell, port_name); if (port) { - Pin *pin = network_->findPin(instance_, port_name->c_str()); + Pin *pin = network_->findPin(instance_, port_name); if (pin) { const RiseFall *rf = edge->transition()->asRiseFall(); Edge *edge; @@ -582,8 +578,8 @@ SdfReader::timingCheckSetupHold1(SdfPortSpec *data_edge, const TimingRole *setup_role, const TimingRole *hold_role) { - const std::string *data_port_name = data_edge->port(); - const std::string *clk_port_name = clk_edge->port(); + std::string_view data_port_name = data_edge->port(); + std::string_view clk_port_name = clk_edge->port(); Cell *cell = network_->cell(instance_); Port *data_port = findPort(cell, data_port_name); Port *clk_port = findPort(cell, clk_port_name); @@ -603,12 +599,12 @@ SdfReader::timingCheckPeriod(SdfPortSpec *edge, { // Ignore non-incremental annotations in incremental only mode. if (!(is_incremental_only_ && !in_incremental_) && instance_) { - const std::string *port_name = edge->port(); + std::string_view port_name = edge->port(); Cell *cell = network_->cell(instance_); Port *port = findPort(cell, port_name); if (port) { // Edge specifier is ignored for period checks. - Pin *pin = network_->findPin(instance_, port_name->c_str()); + Pin *pin = network_->findPin(instance_, port_name); if (pin) { float **values = triple->values(); float *value_ptr = values[triple_min_index_]; @@ -636,7 +632,7 @@ SdfReader::timingCheckNochange(SdfPortSpec *data_edge, SdfTriple *before_triple, SdfTriple *after_triple) { - notSupported("NOCHANGE"); + warn(173, "NOCHANGE not supported."); delete data_edge; delete clk_edge; deleteTriple(before_triple); @@ -659,7 +655,7 @@ SdfReader::device(SdfTripleSeq *triples) } void -SdfReader::device(const std::string *to_port_name, +SdfReader::device(std::string_view to_port_name, SdfTripleSeq *triples) { // Ignore non-incremental annotations in incremental only mode. @@ -667,11 +663,10 @@ SdfReader::device(const std::string *to_port_name, Cell *cell = network_->cell(instance_); Port *to_port = findPort(cell, to_port_name); if (to_port) { - Pin *to_pin = network_->findPin(instance_, to_port_name->c_str()); + Pin *to_pin = network_->findPin(instance_, to_port_name); setDevicePinDelays(to_pin, triples); } } - delete to_port_name; deleteTripleSeq(triples); } @@ -773,28 +768,31 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, } bool -SdfReader::condMatch(const std::string *sdf_cond, - const std::string &lib_cond) +SdfReader::condMatch(std::string_view sdf_cond, + std::string_view lib_cond) { // If the sdf is not conditional it matches any library condition. - if (sdf_cond == nullptr) + if (sdf_cond.empty()) return true; - else if (sdf_cond && !lib_cond.empty()) { + else if (!sdf_cond.empty() && !lib_cond.empty()) { // Match sdf_cond and lib_cond ignoring blanks. - const char *c1 = sdf_cond->c_str(); - const char *c2 = lib_cond.c_str(); - char ch1, ch2; - do { - ch1 = *c1++; - ch2 = *c2++; - while (ch1 && isspace(ch1)) - ch1 = *c1++; - while (ch2 && isspace(ch2)) - ch2 = *c2++; + size_t c1 = 0; + size_t c2 = 0; + while (c1 < sdf_cond.size() + && c2 < lib_cond.size()) { + char ch1 = sdf_cond[c1++]; + char ch2 = lib_cond[c2++]; + while (c1 < sdf_cond.size() + && isspace(ch1)) + ch1 = sdf_cond[c1++]; + while (c2 < lib_cond.size() + && isspace(ch2)) + ch2 = lib_cond[c2++]; if (ch1 != ch2) return false; - } while (ch1 && ch2); - return (ch1 == '\0' && ch2 == '\0'); + } + return c1 == sdf_cond.size() + && c2 == lib_cond.size(); } else return false; @@ -802,30 +800,34 @@ SdfReader::condMatch(const std::string *sdf_cond, SdfPortSpec * SdfReader::makePortSpec(const Transition *tr, - const std::string *port, - const std::string *cond) + std::string_view port) +{ + return new SdfPortSpec(tr, port, ""); +} + +SdfPortSpec * +SdfReader::makePortSpec(const Transition *tr, + std::string_view port, + std::string_view cond) { return new SdfPortSpec(tr, port, cond); } SdfPortSpec * -SdfReader::makeCondPortSpec(const std::string *cond_port) +SdfReader::makeCondPortSpec(std::string_view cond_port) { // Search from end to find port name because condition may contain spaces. - std::string cond_port1(*cond_port); + std::string cond_port1(cond_port); trimRight(cond_port1); auto port_idx = cond_port1.find_last_of(" "); if (port_idx != cond_port1.npos) { - std::string *port1 = new std::string(cond_port1.substr(port_idx + 1)); - auto cond_end = cond_port1.find_last_not_of(" ", port_idx); + std::string port1 = cond_port1.substr(port_idx + 1); + size_t 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); - delete cond_port; - return port_spec; + std::string cond1 = cond_port1.substr(0, cond_end + 1); + return new SdfPortSpec(Transition::riseFall(), port1, cond1); } } - delete cond_port; return nullptr; } @@ -888,75 +890,65 @@ SdfReader::setInIncremental(bool incr) in_incremental_ = incr; } -std::string * -SdfReader::unescaped(const std::string *token) +std::string +SdfReader::unescaped(std::string_view token) { char path_escape = network_->pathEscape(); char path_divider = network_->pathDivider(); - size_t token_length = token->size(); - std::string *unescaped = new std::string; + size_t token_length = token.size(); + std::string result; for (size_t i = 0; i < token_length; i++) { - char ch = (*token)[i]; + char ch = token[i]; if (ch == escape_) { - char next_ch = (*token)[i + 1]; + char next_ch = token[i + 1]; if (next_ch == divider_) { // Escaped divider. // Translate sdf escape to network escape. - *unescaped += path_escape; + result += path_escape; // Translate sdf divider to network divider. - *unescaped += path_divider; + result += path_divider; } else if (next_ch == '[' || next_ch == ']' || next_ch == escape_) { // Escaped bus bracket or escape. // Translate sdf escape to network escape. - *unescaped += path_escape; - *unescaped += next_ch; + result += path_escape; + result += next_ch; } else // Escaped non-divider character. - *unescaped += next_ch; + result += next_ch; i++; } else // Just the normal noises. - *unescaped += ch; + result += ch; } - debugPrint(debug_, "sdf_name", 1, "unescape {} -> {}", *token, - *unescaped); - delete token; - return unescaped; + debugPrint(debug_, "sdf_name", 1, "unescape {} -> {}", token, + result); + return result; } -std::string * -SdfReader::makePath(const std::string *head, - const std::string *tail) +std::string +SdfReader::makePath(std::string_view head, + std::string_view tail) { - std::string *path = new std::string(*head); - *path += network_->pathDivider(); - *path += *tail; - delete head; - delete tail; + std::string path(head); + path += network_->pathDivider(); + path += tail; return path; } -std::string * -SdfReader::makeBusName(std::string *base_name, +std::string +SdfReader::makeBusName(std::string_view base_name, int index) { - std::string *bus_name = unescaped(base_name); - *bus_name += '['; - *bus_name += std::to_string(index); - *bus_name += ']'; - delete base_name; + std::string bus_name = unescaped(base_name); + bus_name += '['; + bus_name += std::to_string(index); + bus_name += ']'; return bus_name; } -void -SdfReader::notSupported(const char *feature) -{ - error(193, "{} not supported.", feature); -} - int SdfReader::sdfLine() const { @@ -964,31 +956,31 @@ SdfReader::sdfLine() const } Pin * -SdfReader::findPin(const std::string *name) +SdfReader::findPin(std::string_view name) { - if (path_) { + if (!path_.empty()) { std::string path_name(path_); path_name += divider_; - path_name += *name; - Pin *pin = network_->findPin(path_name.c_str()); + path_name += name; + Pin *pin = network_->findPin(path_name); return pin; } else - return network_->findPin(name->c_str()); + return network_->findPin(name); } Instance * -SdfReader::findInstance(const std::string *name) +SdfReader::findInstance(std::string_view name) { std::string inst_name; - if (path_) { + if (!path_.empty()) { inst_name = path_; inst_name += divider_; - inst_name += *name; + inst_name += name; } else - inst_name = *name; - Instance *inst = network_->findInstance(inst_name.c_str()); + inst_name = name; + Instance *inst = network_->findInstance(inst_name); if (inst == nullptr) warn(195, "instance {} not found.", inst_name); return inst; @@ -997,20 +989,14 @@ SdfReader::findInstance(const std::string *name) //////////////////////////////////////////////////////////////// SdfPortSpec::SdfPortSpec(const Transition *tr, - const std::string *port, - const std::string *cond) : + std::string_view port, + std::string_view cond) : tr_(tr), port_(port), cond_(cond) { } -SdfPortSpec::~SdfPortSpec() -{ - delete port_; - delete cond_; -} - //////////////////////////////////////////////////////////////// SdfTriple::SdfTriple(float *min, @@ -1045,7 +1031,7 @@ SdfTriple::hasValue() const //////////////////////////////////////////////////////////////// SdfScanner::SdfScanner(std::istream *stream, - const std::string &filename, + std::string_view filename, SdfReader *reader, Report *report) : yyFlexLexer(stream), @@ -1056,9 +1042,9 @@ SdfScanner::SdfScanner(std::istream *stream, } void -SdfScanner::error(const char *msg) +SdfScanner::error(std::string_view msg) { - report_->fileError(196, filename_.c_str(), lineno(), "{}", msg); + report_->fileError(196, filename_, lineno(), "{}", msg); } } // namespace sta diff --git a/sdf/SdfReader.hh b/sdf/SdfReader.hh index f894ebd61..47598d5d5 100644 --- a/sdf/SdfReader.hh +++ b/sdf/SdfReader.hh @@ -24,6 +24,9 @@ #pragma once +#include +#include + namespace sta { class MinMaxAll; @@ -40,7 +43,7 @@ class StaState; // If incremental_only is true non-incremental annoatations are ignored. // // path is a hierararchial path prefix for instances and pins in the -// sdf file. Pass 0 (nullptr) to specify no path. +// sdf file. Pass a null string to specify no path. // // The cond_use option is used when the SDF file contains conditional // delays and the library does not have conditional delay arcs. If @@ -52,8 +55,8 @@ class StaState; // maximum operating conditions. bool -readSdf(const char *filename, - const char *path, +readSdf(std::string_view filename, + std::string_view path, Scene *scene, bool unescaped_dividers, bool incremental_only, diff --git a/sdf/SdfReaderPvt.hh b/sdf/SdfReaderPvt.hh index 4ae883ba1..3b12d02e6 100644 --- a/sdf/SdfReaderPvt.hh +++ b/sdf/SdfReaderPvt.hh @@ -24,6 +24,7 @@ #pragma once +#include #include #include @@ -48,8 +49,8 @@ using SdfTripleSeq = std::vector; class SdfReader : public StaState { public: - SdfReader(const char *filename, - const char *path, + SdfReader(std::string_view filename, + std::string_view path, int arc_min_index, int arc_max_index, AnalysisType analysis_type, @@ -62,7 +63,7 @@ public: void setDivider(char divider); void setTimescale(float multiplier, - const std::string *units); + std::string_view units); void setPortDeviceDelay(Edge *edge, SdfTripleSeq *triples, bool from_trans); @@ -83,17 +84,18 @@ public: int triple_index, int arc_delay_index, const MinMax *min_max); - void setInstance(const std::string *instance_name); + void setInstance(); + void setInstance(std::string_view instance_name); void setInstanceWildcard(); void cellFinish(); - void setCell(const std::string *cell_name); - void interconnect(const std::string *from_pin_name, - const std::string *to_pin_name, + void setCell(std::string_view cell_name); + void interconnect(std::string_view from_pin_name, + std::string_view to_pin_name, SdfTripleSeq *triples); void iopath(SdfPortSpec *from_edge, - const std::string *to_port_name, + std::string_view to_port_name, SdfTripleSeq *triples, - const std::string *cond, + std::string_view cond, bool condelse); void timingCheck(const TimingRole *role, SdfPortSpec *data_edge, @@ -121,10 +123,10 @@ public: SdfPortSpec *clk_edge, SdfTriple *before_triple, SdfTriple *after_triple); - void port(const std::string *to_pin_name, + void port(std::string_view o_pin_name, SdfTripleSeq *triples); void device(SdfTripleSeq *triples); - void device(const std::string *to_pin_name, + void device(std::string_view to_pin_name, SdfTripleSeq *triples); SdfTriple *makeTriple(); @@ -136,20 +138,22 @@ public: SdfTripleSeq *makeTripleSeq(); void deleteTripleSeq(SdfTripleSeq *triples); SdfPortSpec *makePortSpec(const Transition *tr, - const std::string *port, - const std::string *cond); - SdfPortSpec *makeCondPortSpec(const std::string *cond_port); - std::string *unescaped(const std::string *token); - std::string *makePath(const std::string *head, - const std::string *tail); + std::string_view port); + SdfPortSpec *makePortSpec(const Transition *tr, + std::string_view port, + std::string_view cond); + SdfPortSpec *makeCondPortSpec(std::string_view cond_port); + std::string unescaped(std::string_view token); + std::string makePath(std::string_view head, + std::string_view tail); // Parser state used to control lexer for COND handling. bool inTimingCheck() { return in_timing_check_; } void setInTimingCheck(bool in); bool inIncremental() const { return in_incremental_; } void setInIncremental(bool incr); - std::string *makeBusName(std::string *bus_name, - int index); - const std::string &filename() const { return filename_; } + std::string makeBusName(std::string_view bus_name, + int index); + std::string_view filename() const { return filename_; } int sdfLine() const; template void warn(int id, @@ -167,12 +171,8 @@ public: report_->fileError(id, filename_, sdfLine(), fmt, std::forward(args)...); } - void notSupported(const char *feature); private: - int readSdfFile1(Network *network, - Graph *graph, - const char *filename); Edge *findCheckEdge(Pin *from_pin, Pin *to_pin, const TimingRole *sdf_role, @@ -180,8 +180,8 @@ private: const std::string *cond_end); Edge *findWireEdge(Pin *from_pin, Pin *to_pin); - bool condMatch(const std::string *sdf_cond, - const std::string &lib_cond); + bool condMatch(std::string_view sdf_cond, + std::string_view lib_cond); void timingCheck1(const TimingRole *role, Port *data_port, SdfPortSpec *data_edge, @@ -195,19 +195,19 @@ private: const TimingRole *sdf_role, SdfTriple *triple, bool match_generic); - Pin *findPin(const std::string *name); - Instance *findInstance(const std::string *name); + Pin *findPin(std::string_view name); + Instance *findInstance(std::string_view name); void setEdgeDelays(Edge *edge, SdfTripleSeq *triples, - const char *sdf_cmd); + std::string_view sdf_cmd); void setDevicePinDelays(Pin *to_pin, SdfTripleSeq *triples); Port *findPort(const Cell *cell, - const std::string *port_name); + std::string_view port_name); - std::string filename_; + std::string_view filename_; SdfScanner *scanner_; - const char *path_; + std::string_view path_; // Which values to pull out of the sdf triples. int triple_min_index_; int triple_max_index_; @@ -222,7 +222,7 @@ private: char divider_; char escape_; Instance *instance_; - const std::string *cell_name_; + std::string cell_name_; bool in_timing_check_; bool in_incremental_; float timescale_; diff --git a/sdf/SdfScanner.hh b/sdf/SdfScanner.hh index 14407236d..bcb0c6cd3 100644 --- a/sdf/SdfScanner.hh +++ b/sdf/SdfScanner.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "SdfLocation.hh" #include "SdfParse.hh" @@ -41,7 +43,7 @@ class SdfScanner : public SdfFlexLexer { public: SdfScanner(std::istream *stream, - const std::string &filename, + std::string_view filename, SdfReader *reader, Report *report); virtual ~SdfScanner() {} @@ -51,13 +53,13 @@ public: // YY_DECL defined in SdfLex.ll // Method body created by flex in SdfLex.cc - void error(const char *msg); + void error(std::string_view msg); // Get rid of override virtual function warning. using FlexLexer::yylex; private: - std::string filename_; + std::string_view filename_; SdfReader *reader_; Report *report_; std::string token_; diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index fa79139f0..0d7500927 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -26,6 +26,8 @@ #include #include +#include +#include #include "Format.hh" #include "Zlib.hh" @@ -50,7 +52,7 @@ class SdfWriter : public StaState { public: SdfWriter(StaState *sta); - void write(const char *filename, + void write(std::string_view filename, const Scene *scene, char sdf_divider, bool include_typ, @@ -81,14 +83,14 @@ class SdfWriter : public StaState const Instance *inst, bool &inst_header); void writeCheck(Edge *edge, - const char *sdf_check); + std::string_view sdf_check); void writeCheck(Edge *edge, TimingArc *arc, - const char *sdf_check, + std::string_view sdf_check, bool use_data_edge, bool use_clk_edge); void writeEdgeCheck(Edge *edge, - const char *sdf_check, + std::string_view sdf_check, int clk_rf_index, TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]); void writeTimingCheckHeader(); @@ -99,7 +101,7 @@ class SdfWriter : public StaState float max_width); void writePeriodCheck(const Pin *pin, float min_period); - const char *sdfEdge(const Transition *tr); + std::string_view sdfEdge(const Transition *tr); void writeArcDelays(Edge *edge); void writeSdfTriple(RiseFallMinMax &delays, const RiseFall *rf); @@ -127,7 +129,7 @@ class SdfWriter : public StaState }; void -writeSdf(const char *filename, +writeSdf(std::string_view filename, const Scene *scene, char sdf_divider, bool include_typ, @@ -150,7 +152,7 @@ SdfWriter::SdfWriter(StaState *sta) : } void -SdfWriter::write(const char *filename, +SdfWriter::write(std::string_view filename, const Scene *scene, char sdf_divider, bool include_typ, @@ -170,7 +172,7 @@ SdfWriter::write(const char *filename, arc_delay_min_index_ = scene->dcalcAnalysisPtIndex(MinMax::min()); arc_delay_max_index_ = scene->dcalcAnalysisPtIndex(MinMax::max()); - stream_ = gzopen(filename, gzip ? "wb" : "wT"); + stream_ = gzopen(std::string(filename).c_str(), gzip ? "wb" : "wT"); if (stream_ == nullptr) throw FileNotWritable(filename); @@ -573,7 +575,7 @@ SdfWriter::writeTimingCheckTrailer() void SdfWriter::writeCheck(Edge *edge, - const char *sdf_check) + std::string_view sdf_check) { TimingArcSet *arc_set = edge->timingArcSet(); // Examine the arcs to see if the check requires clk or data edge specifiers. @@ -600,7 +602,7 @@ SdfWriter::writeCheck(Edge *edge, void SdfWriter::writeEdgeCheck(Edge *edge, - const char *sdf_check, + std::string_view sdf_check, int clk_rf_index, TimingArc *arcs[RiseFall::index_count][RiseFall::index_count]) { @@ -647,7 +649,7 @@ SdfWriter::writeEdgeCheck(Edge *edge, void SdfWriter::writeCheck(Edge *edge, TimingArc *arc, - const char *sdf_check, + std::string_view sdf_check, bool use_data_edge, bool use_clk_edge) { @@ -725,14 +727,14 @@ SdfWriter::writePeriodCheck(const Pin *pin, sta::print(stream_, ")\n"); } -const char * +std::string_view SdfWriter::sdfEdge(const Transition *tr) { if (tr == Transition::rise()) return "posedge"; else if (tr == Transition::fall()) return "negedge"; - return nullptr; + return {}; } //////////////////////////////////////////////////////////////// @@ -762,8 +764,7 @@ SdfWriter::sdfPathName(const Instance *instance) std::string path_name; while (!inst_path.empty()) { const Instance *inst = inst_path.back(); - std::string inst_name = sdfName(inst); - path_name += inst_name; + path_name += sdfName(inst); inst_path.pop_back(); if (!inst_path.empty()) path_name += sdf_divider_; @@ -775,11 +776,9 @@ SdfWriter::sdfPathName(const Instance *instance) std::string SdfWriter::sdfName(const Instance *inst) { - const char *name = network_->name(inst); + const std::string &name = network_->name(inst); std::string sdf_name; - const char *p = name; - while (*p) { - char ch = *p; + for (char ch : name) { // Ignore sta escapes. if (ch != network_escape_) { if (!(isalnum(ch) || ch == '_')) @@ -787,7 +786,6 @@ SdfWriter::sdfName(const Instance *inst) sdf_name += sdf_escape_; sdf_name += ch; } - p++; } return sdf_name; } @@ -795,8 +793,8 @@ SdfWriter::sdfName(const Instance *inst) std::string SdfWriter::sdfPortName(const Pin *pin) { - const char *name = network_->portName(pin); - size_t name_length = strlen(name); + const std::string &name = network_->portName(pin); + size_t name_length = name.size(); std::string sdf_name; constexpr char bus_brkt_left = '['; @@ -804,9 +802,9 @@ SdfWriter::sdfPortName(const Pin *pin) size_t bus_index = name_length; if (name_length >= 4 && name[name_length - 1] == bus_brkt_right) { - const char *left = strrchr(name, bus_brkt_left); - if (left) - bus_index = left - name; + size_t left = name.rfind(bus_brkt_left); + if (left != std::string_view::npos) + bus_index = left; } for (size_t i = 0; i < name_length; i++) { char ch = name[i]; diff --git a/sdf/SdfWriter.hh b/sdf/SdfWriter.hh index c14df631a..98ca6f7dc 100644 --- a/sdf/SdfWriter.hh +++ b/sdf/SdfWriter.hh @@ -24,13 +24,15 @@ #pragma once +#include + namespace sta { class StaState; class Scene; void -writeSdf(const char *filename, +writeSdf(std::string_view filename, const Scene *scene, char divider, bool include_typ, diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index cad8319e7..fc5b0c074 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -171,7 +171,7 @@ MinPulseWidthCheck::MinPulseWidthCheck(Path *open_path) : std::string MinPulseWidthCheck::to_string(const StaState *sta) { - std::string result = sta->network()->pathName(pin(sta)); + std::string result(sta->network()->pathName(pin(sta))); result += " "; result += (openTransition(sta) == RiseFall::rise()) ? "(high)" : "(low)"; return result; diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc index 887c009e1..bc2d75f9e 100644 --- a/search/ClkLatency.cc +++ b/search/ClkLatency.cc @@ -72,7 +72,7 @@ ClkLatency::reportClkLatency(ConstClockSeq &clks, ConstClockSeq sorted_clks; for (const Clock *clk : clks) sorted_clks.push_back(clk); - sort(sorted_clks, ClkNameLess()); + sort(sorted_clks, ClockNameLess()); for (const Clock *clk : sorted_clks) { ClkDelays clk_delays = clk_delay_map[clk]; diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 8167d486c..97626ca59 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -26,6 +26,7 @@ #include // abs #include +#include #include #include @@ -75,7 +76,7 @@ ClkSkews::reportClkSkew(ConstClockSeq &clks, ConstClockSeq sorted_clks; for (const Clock *clk : clks) sorted_clks.push_back(clk); - sort(sorted_clks, ClkNameLess()); + sort(sorted_clks, ClockNameLess()); for (const Clock *clk : sorted_clks) { report_->report("Clock {}", clk->name()); @@ -487,12 +488,13 @@ ClkSkew::srcTgtPathNameLess(ClkSkew &clk_skew1, const StaState *sta) { Network *network = sta->sdcNetwork(); - const char *src_path1 = network->pathName(clk_skew1.srcPath()->pin(sta)); - const char *src_path2 = network->pathName(clk_skew2.srcPath()->pin(sta)); - 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)); + std::string src_path1 = network->pathName(clk_skew1.srcPath()->pin(sta)); + std::string src_path2 = network->pathName(clk_skew2.srcPath()->pin(sta)); + std::string tgt_path1 = network->pathName(clk_skew1.tgtPath()->pin(sta)); + std::string tgt_path2 = network->pathName(clk_skew2.tgtPath()->pin(sta)); + return src_path1 < src_path2 + || (src_path1 == src_path2 + && tgt_path1 == tgt_path2); } //////////////////////////////////////////////////////////////// diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 20ec61109..d4fd82f09 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -53,9 +53,9 @@ namespace sta { LibertyLibrary * -makeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, +makeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, const Scene *scene, Sta *sta) { @@ -63,9 +63,9 @@ makeTimingModel(const char *lib_name, return maker.makeTimingModel(); } -MakeTimingModel::MakeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, +MakeTimingModel::MakeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, const Scene *scene, Sta *sta) : StaState(sta), @@ -186,7 +186,7 @@ MakeTimingModel::makePorts() CellPortIterator *port_iter = network_->portIterator(top_cell); while (port_iter->hasNext()) { Port *port = port_iter->next(); - const char *port_name = network_->name(port); + std::string port_name(network_->name(port)); if (network_->isBus(port)) { int from_index = network_->fromIndex(port); int to_index = network_->toIndex(port); @@ -518,7 +518,7 @@ MakeTimingModel::findClkTreeDelays() while (port_iter->hasNext()) { Port *port = port_iter->next(); if (network_->direction(port)->isInput()) { - const char *port_name = network_->name(port); + std::string port_name = network_->name(port); LibertyPort *lib_port = cell_->findLibertyPort(port_name); Pin *pin = network_->findPin(top_inst, port); if (pin && sdc_->isClock(pin)) { diff --git a/search/MakeTimingModel.hh b/search/MakeTimingModel.hh index c09634bb3..9056bce7c 100644 --- a/search/MakeTimingModel.hh +++ b/search/MakeTimingModel.hh @@ -24,6 +24,8 @@ #pragma once +#include + namespace sta { class LibertyLibrary; @@ -31,9 +33,9 @@ class Scene; class Sta; LibertyLibrary * -makeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, +makeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, const Scene *scene, Sta *sta); diff --git a/search/MakeTimingModelPvt.hh b/search/MakeTimingModelPvt.hh index aeca42671..07470e4e7 100644 --- a/search/MakeTimingModelPvt.hh +++ b/search/MakeTimingModelPvt.hh @@ -25,6 +25,8 @@ #pragma once #include +#include +#include #include "LibertyClass.hh" #include "SdcClass.hh" @@ -54,9 +56,9 @@ using OutputPinDelays = std::map; class MakeTimingModel : public StaState { public: - MakeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, + MakeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, const Scene *scene, Sta *sta); ~MakeTimingModel(); @@ -102,9 +104,9 @@ private: void restoreSdc(); void swapSdcWithBackup(); - const char *lib_name_; - const char *cell_name_; - const char *filename_; + std::string lib_name_; + std::string cell_name_; + std::string filename_; const Scene *scene_; SceneSet scenes_; LibertyLibrary *library_; diff --git a/search/Mode.cc b/search/Mode.cc index 6bced47a4..a73ea28ef 100644 --- a/search/Mode.cc +++ b/search/Mode.cc @@ -32,7 +32,7 @@ namespace sta { -Mode::Mode(const std::string &name, +Mode::Mode(std::string_view name, size_t mode_index, StaState *sta) : StaState(sta), diff --git a/search/PathGroup.cc b/search/PathGroup.cc index e2ca77118..1d3e6cd05 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -53,7 +53,7 @@ namespace sta { int PathGroup::group_path_count_max = std::numeric_limits::max(); PathGroup * -PathGroup::makePathGroupSlack(const char *name, +PathGroup::makePathGroupSlack(std::string_view name, int group_path_count, int endpoint_path_count, bool unique_pins, @@ -68,7 +68,7 @@ PathGroup::makePathGroupSlack(const char *name, } PathGroup * -PathGroup::makePathGroupArrival(const char *name, +PathGroup::makePathGroupArrival(std::string_view name, int group_path_count, int endpoint_path_count, bool unique_pins, @@ -81,7 +81,7 @@ PathGroup::makePathGroupArrival(const char *name, false, min_max, sta); } -PathGroup::PathGroup(const char *name, +PathGroup::PathGroup(std::string_view name, int group_path_count, int endpoint_path_count, bool unique_pins, @@ -247,11 +247,6 @@ PathGroup::clear() //////////////////////////////////////////////////////////////// -const char *PathGroups::path_delay_group_name_ = "path delay"; -const char *PathGroups::gated_clk_group_name_ = "gated clock"; -const char *PathGroups::async_group_name_ = "asynchronous"; -const char *PathGroups::unconstrained_group_name_ = "unconstrained"; - PathGroups::PathGroups(int group_path_count, int endpoint_path_count, bool unique_pins, @@ -309,7 +304,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.c_str(), + PathGroup *group = PathGroup::makePathGroupSlack(name, group_path_count, endpoint_path_count, unique_pins, @@ -321,7 +316,7 @@ PathGroups::makeGroups(int group_path_count, } for (Clock *clk : sdc->clocks()) { - const char *clk_name = clk->name(); + const std::string &clk_name = clk->name(); if (reportGroup(clk_name, group_names)) { PathGroup *group = PathGroup::makePathGroupSlack(clk_name, group_path_count, @@ -336,7 +331,7 @@ PathGroups::makeGroups(int group_path_count, } if (setup_hold - && reportGroup(path_delay_group_name_, group_names)) + && reportGroup(std::string(path_delay_group_name_), group_names)) path_delay_[mm_index] = PathGroup::makePathGroupSlack(path_delay_group_name_, group_path_count, endpoint_path_count, @@ -348,7 +343,7 @@ PathGroups::makeGroups(int group_path_count, path_delay_[mm_index] = nullptr; if (gated_clk - && reportGroup(gated_clk_group_name_, group_names)) + && reportGroup(std::string(gated_clk_group_name_), group_names)) gated_clk_[mm_index] = PathGroup::makePathGroupSlack(gated_clk_group_name_, group_path_count, endpoint_path_count, @@ -360,7 +355,7 @@ PathGroups::makeGroups(int group_path_count, gated_clk_[mm_index] = nullptr; if (async - && reportGroup(async_group_name_, group_names)) + && reportGroup(std::string(async_group_name_), group_names)) async_[mm_index] = PathGroup::makePathGroupSlack(async_group_name_, group_path_count, endpoint_path_count, @@ -372,7 +367,7 @@ PathGroups::makeGroups(int group_path_count, async_[mm_index] = nullptr; if (unconstrained - && reportGroup(unconstrained_group_name_, group_names)) + && reportGroup(std::string(unconstrained_group_name_), group_names)) unconstrained_[mm_index] = PathGroup::makePathGroupArrival(unconstrained_group_name_, group_path_count, endpoint_path_count, @@ -441,7 +436,7 @@ PathGroups::pathGroups(const PathEnd *path_end) const path_groups.push_back(path_delay_[mm_index]); } else { - std::string 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); @@ -484,7 +479,7 @@ PathGroups::pathGroupNames(const PathEnd *path_end, const StaState *sta) { StringSeq group_names; - const char *group_name = nullptr; + std::string group_name; const Search *search = sta->search(); ExceptionPathSeq group_paths = search->groupPathsTo(path_end); if (path_end->isUnconstrained()) @@ -493,9 +488,9 @@ PathGroups::pathGroupNames(const PathEnd *path_end, // GroupPaths have precedence. for (ExceptionPath *group_path : group_paths) { if (group_path->isDefault()) - group_names.push_back(path_delay_group_name_); + group_names.push_back(std::string(path_delay_group_name_)); else - group_names.push_back(group_path->name()); + group_names.push_back(std::string(group_path->name())); } } else if (path_end->isCheck() || path_end->isLatchCheck()) { @@ -526,7 +521,7 @@ PathGroups::pathGroupNames(const PathEnd *path_end, else group_name = path_delay_group_name_; } - if (group_name) + if (!group_name.empty()) group_names.push_back(group_name); return group_names; } diff --git a/search/PocvMode.cc b/search/PocvMode.cc index 571861389..7e7b28a5f 100644 --- a/search/PocvMode.cc +++ b/search/PocvMode.cc @@ -33,14 +33,14 @@ static EnumNameMap pocv_mode_map = {PocvMode::normal, "normal"}, {PocvMode::skew_normal, "skew_normal"}}; -const char * +const std::string & pocvModeName(PocvMode mode) { return pocv_mode_map.find(mode); } PocvMode -findPocvMode(const char *mode_name) +findPocvMode(std::string_view mode_name) { return pocv_mode_map.find(mode_name, PocvMode::scalar); } diff --git a/search/Property.cc b/search/Property.cc index 35e6a0961..cdb0c0885 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -50,8 +50,8 @@ namespace sta { class PropertyUnknown : public Exception { public: - PropertyUnknown(const std::string &type, - const std::string &property); + PropertyUnknown(std::string_view type, + std::string_view property); virtual ~PropertyUnknown() {} virtual const char *what() const noexcept; @@ -59,8 +59,8 @@ class PropertyUnknown : public Exception std::string msg_; }; -PropertyUnknown::PropertyUnknown(const std::string &type, - const std::string &property) : +PropertyUnknown::PropertyUnknown(std::string_view type, + std::string_view property) : Exception(), msg_(sta::format("{} objects do not have a {} property.", type, property)) { @@ -107,18 +107,18 @@ PropertyValue::PropertyValue() : { } -PropertyValue::PropertyValue(const char *value) : +PropertyValue::PropertyValue(std::string_view value) : type_(Type::string), - string_(stringCopy(value)), unit_(nullptr) { + string_ = new std::string(value); } -PropertyValue::PropertyValue(std::string &value) : +PropertyValue::PropertyValue(std::string value) : type_(Type::string), - string_(stringCopy(value.c_str())), unit_(nullptr) { + string_ = new std::string(std::move(value)); } PropertyValue::PropertyValue(float value, @@ -269,7 +269,7 @@ PropertyValue::PropertyValue(const PropertyValue &value) : case Type::none: break; case Type::string: - string_ = stringCopy(value.string_); + string_ = new std::string(*value.string_); break; case Type::float_: float_ = value.float_; @@ -325,7 +325,6 @@ PropertyValue::PropertyValue(const PropertyValue &value) : PropertyValue::PropertyValue(PropertyValue &&value) noexcept : type_(value.type_), unit_(value.unit_) - { switch (type_) { case Type::none: @@ -333,6 +332,7 @@ PropertyValue::PropertyValue(PropertyValue &&value) noexcept : case Type::string: string_ = value.string_; value.string_ = nullptr; + value.type_ = Type::none; break; case Type::float_: float_ = value.float_; @@ -382,7 +382,7 @@ PropertyValue::PropertyValue(PropertyValue &&value) noexcept : case Type::paths: paths_ = value.paths_; // Steal the value. - value.clks_ = nullptr; + value.paths_ = nullptr; break; case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; @@ -390,11 +390,12 @@ PropertyValue::PropertyValue(PropertyValue &&value) noexcept : } } -PropertyValue::~PropertyValue() -{ +void +PropertyValue::destroyActive() +{ switch (type_) { case Type::string: - stringDelete(string_); + delete string_; break; case Type::clks: delete clks_; @@ -410,9 +411,17 @@ PropertyValue::~PropertyValue() } } +PropertyValue::~PropertyValue() +{ + destroyActive(); +} + PropertyValue & PropertyValue::operator=(const PropertyValue &value) { + if (this == &value) + return *this; + destroyActive(); type_ = value.type_; unit_ = value.unit_; @@ -420,7 +429,7 @@ PropertyValue::operator=(const PropertyValue &value) case Type::none: break; case Type::string: - string_ = stringCopy(value.string_); + string_ = new std::string(*value.string_); break; case Type::float_: float_ = value.float_; @@ -477,6 +486,9 @@ PropertyValue::operator=(const PropertyValue &value) PropertyValue & PropertyValue::operator=(PropertyValue &&value) noexcept { + if (this == &value) + return *this; + destroyActive(); type_ = value.type_; unit_ = value.unit_; @@ -486,6 +498,7 @@ PropertyValue::operator=(PropertyValue &&value) noexcept case Type::string: string_ = value.string_; value.string_ = nullptr; + value.type_ = Type::none; break; case Type::float_: float_ = value.float_; @@ -533,7 +546,7 @@ PropertyValue::operator=(PropertyValue &&value) noexcept break; case Type::paths: paths_ = value.paths_; - value.clks_ = nullptr; + value.paths_ = nullptr; break; case Type::pwr_activity: pwr_activity_ = value.pwr_activity_; @@ -547,7 +560,7 @@ PropertyValue::to_string(const Network *network) const { switch (type_) { case Type::string: - return string_; + return *string_; case Type::float_: return unit_->asString(float_, 6); case Type::bool_: @@ -563,11 +576,11 @@ PropertyValue::to_string(const Network *network) const case Type::liberty_port: return liberty_port_->name(); case Type::library: - return network->name(library_); + return std::string(network->name(library_)); case Type::cell: - return network->name(cell_); + return std::string(network->name(cell_)); case Type::port: - return network->name(port_); + return std::string(network->name(port_)); case Type::instance: return network->pathName(inst_); case Type::pin: @@ -586,12 +599,12 @@ PropertyValue::to_string(const Network *network) const return ""; } -const char * +const std::string & PropertyValue::stringValue() const { if (type_ != Type::string) throw PropertyTypeWrong("stringValue", "string"); - return string_; + return *string_; } float @@ -619,7 +632,7 @@ Properties::Properties(Sta *sta) : PropertyValue Properties::getProperty(const Library *lib, - const std::string &property) + std::string_view property) { Network *network = sta_->cmdNetwork(); if (property == "name" @@ -639,7 +652,7 @@ Properties::getProperty(const Library *lib, PropertyValue Properties::getProperty(const LibertyLibrary *lib, - const std::string &property) + std::string_view property) { if (property == "name" || property == "full_name") @@ -661,7 +674,7 @@ Properties::getProperty(const LibertyLibrary *lib, PropertyValue Properties::getProperty(const Cell *cell, - const std::string &property) + std::string_view property) { Network *network = sta_->cmdNetwork(); if (property == "name" @@ -669,8 +682,8 @@ Properties::getProperty(const Cell *cell, return PropertyValue(network->name(cell)); else if (property == "full_name") { Library *lib = network->library(cell); - std::string lib_name = network->name(lib); - std::string cell_name = network->name(cell); + std::string lib_name(network->name(lib)); + std::string cell_name(network->name(cell)); std::string full_name = lib_name + network->pathDivider() + cell_name; return PropertyValue(full_name); } @@ -692,7 +705,7 @@ Properties::getProperty(const Cell *cell, PropertyValue Properties::getProperty(const LibertyCell *cell, - const std::string &property) + std::string_view property) { if (property == "name" || property == "base_name") @@ -733,11 +746,11 @@ Properties::getProperty(const LibertyCell *cell, PropertyValue Properties::getProperty(const Port *port, - const std::string &property) + std::string_view property) { Network *network = sta_->cmdNetwork(); if (property == "name" - || property == "full_name") + || property == "full_name") return PropertyValue(network->name(port)); else if (property == "direction" || property == "port_direction") @@ -815,7 +828,7 @@ Properties::portSlack(const Port *port, PropertyValue Properties::getProperty(const LibertyPort *port, - const std::string &property) + std::string_view property) { if (property == "name") return PropertyValue(port->name()); @@ -894,7 +907,7 @@ Properties::getProperty(const LibertyPort *port, PropertyValue Properties::getProperty(const Instance *inst, - const std::string &property) + std::string_view property) { Network *network = sta_->ensureLinked(); LibertyCell *liberty_cell = network->libertyCell(inst); @@ -934,7 +947,7 @@ Properties::getProperty(const Instance *inst, PropertyValue Properties::getProperty(const Pin *pin, - const std::string &property) + std::string_view property) { Network *network = sta_->ensureLinked(); if (property == "name" @@ -1062,7 +1075,7 @@ Properties::pinSlew(const Pin *pin, PropertyValue Properties::getProperty(const Net *net, - const std::string &property) + std::string_view property) { Network *network = sta_->ensureLinked(); if (property == "name") @@ -1082,7 +1095,7 @@ Properties::getProperty(const Net *net, PropertyValue Properties::getProperty(Edge *edge, - const std::string &property) + std::string_view property) { if (property == "full_name") { std::string full_name = edge->to_string(sta_); @@ -1135,17 +1148,17 @@ Properties::edgeDelay(Edge *edge, PropertyValue Properties::getProperty(TimingArcSet *arc_set, - const std::string &property) + std::string_view property) { if (property == "name" || property == "full_name") { if (arc_set->isWire()) return PropertyValue("wire"); else { - 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 = sta::format("{} {} -> {}", cell_name, from, to); + std::string name = sta::format("{} {} -> {}", + arc_set->libertyCell()->name(), + arc_set->from()->name(), + arc_set->to()->name()); return PropertyValue(name); } } @@ -1157,7 +1170,7 @@ Properties::getProperty(TimingArcSet *arc_set, PropertyValue Properties::getProperty(const Clock *clk, - const std::string &property) + std::string_view property) { if (property == "name" || property == "full_name") @@ -1186,7 +1199,7 @@ Properties::getProperty(const Clock *clk, PropertyValue Properties::getProperty(PathEnd *end, - const std::string &property) + std::string_view property) { if (property == "startpoint") { PathExpanded expanded(end->path(), sta_); @@ -1217,7 +1230,7 @@ Properties::getProperty(PathEnd *end, PropertyValue Properties::getProperty(Path *path, - const std::string &property) + std::string_view property) { if (property == "pin") return PropertyValue(path->pin(sta_)); @@ -1326,8 +1339,8 @@ Properties::defineProperty(std::string_view property, template PropertyValue PropertyRegistry::getProperty(TYPE object, - const std::string &property, - const char *type_name, + std::string_view property, + std::string_view type_name, Sta *sta) { diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 158b79191..037d21cff 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -24,6 +24,7 @@ #include // reverse #include +#include #include "ReportPath.hh" @@ -79,52 +80,40 @@ hierPinsThruEdge(const Edge *edge, const Network *network, const Graph *graph); -ReportField::ReportField(const char *name, - const char *title, - int width, +ReportField::ReportField(std::string_view name, + std::string_view title, + size_t width, bool left_justify, Unit *unit, bool enabled) : name_(name), - title_(stringCopy(title)), + title_(title), left_justify_(left_justify), unit_(unit), - enabled_(enabled), - blank_(nullptr) + enabled_(enabled) { setWidth(width); } ReportField::~ReportField() { - stringDelete(title_); - stringDelete(blank_); } void -ReportField::setProperties(const char *title, - int width, +ReportField::setProperties(std::string_view title, + size_t width, bool left_justify) { - if (title_) - stringDelete(title_); - title_ = stringCopy(title); + title_ = std::string(title); left_justify_ = left_justify; setWidth(width); } void -ReportField::setWidth(int width) +ReportField::setWidth(size_t width) { width_ = width; - - if (blank_) - stringDelete(blank_); - blank_ = new char[width_ + 1]; - int i; - for (i = 0; i < width_; i++) - blank_[i] = ' '; - blank_[i] = '\0'; + blank_.assign(width_, ' '); } void @@ -185,8 +174,8 @@ ReportPath::makeFields() } ReportField * -ReportPath::makeField(const char *name, - const char *title, +ReportPath::makeField(std::string_view name, + std::string_view title, int width, bool left_justify, Unit *unit, @@ -199,10 +188,10 @@ ReportPath::makeField(const char *name, } ReportField * -ReportPath::findField(const char *name) const +ReportPath::findField(std::string_view name) const { for (ReportField *field : fields_) { - if (stringEq(name, field->name())) + if (field->name() == name) return field; } return nullptr; @@ -217,7 +206,7 @@ ReportPath::setReportFieldOrder(const StringSeq &field_names) ReportFieldSeq next_fields; for (const std::string &field_name : field_names) { - ReportField *field = findField(field_name.c_str()); + ReportField *field = findField(field_name); if (field) { next_fields.push_back(field); field->setEnabled(true); @@ -481,9 +470,9 @@ void ReportPath::reportEndpoint(const PathEndCheck *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); + std::string inst_name = cmd_network_->pathName(inst); std::string clk_name = tgtClkName(end); - const char *rise_fall = asRisingFalling(end->targetClkEndTrans(this)); + std::string_view rise_fall = asRisingFalling(end->targetClkEndTrans(this)); const TimingRole *check_role = end->checkRole(this); const TimingRole *check_generic_role = check_role->genericRole(); if (check_role == TimingRole::recovery() @@ -503,7 +492,7 @@ ReportPath::reportEndpoint(const PathEndCheck *end) const reportEndpoint(inst_name, reason); } else { - const char *reg_desc = clkRegLatchDesc(end); + std::string_view reg_desc = clkRegLatchDesc(end); std::string reason = sta::format("{} clocked by {}", reg_desc, clk_name); reportEndpoint(inst_name, reason); } @@ -588,14 +577,14 @@ void ReportPath::reportEndpoint(const PathEndLatchCheck *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 char *reg_desc = latchDesc(end); + std::string_view reg_desc = latchDesc(end); std::string reason = sta::format("{} clocked by {}", reg_desc, clk_name); + std::string inst_name = cmd_network_->pathName(inst); reportEndpoint(inst_name, reason); } -const char * +std::string_view ReportPath::latchDesc(const PathEndLatchCheck *end) const { TimingArc *check_arc = end->checkArc(); @@ -626,13 +615,13 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, const Path *tgt_clk_path = end->targetClkPath(); if (tgt_clk_path->clkInfo(search_)->isPropagated()) { std::string width_msg = sta::format("{} nominal pulse width", tgt_clk_name); - reportLineTotal(width_msg.c_str(), nom_pulse_width, early_late); + reportLineTotal(width_msg, nom_pulse_width, early_late); if (!delayZero(latency_diff, this)) reportLineTotalMinus("clock latency difference", latency_diff, early_late); } else { - std::string width_msg = sta::format("{} pulse width", tgt_clk_name.c_str()); - reportLineTotal(width_msg.c_str(), tgt_clk_width, early_late); + std::string width_msg = sta::format("{} pulse width", tgt_clk_name); + reportLineTotal(width_msg, tgt_clk_width, early_late); } ArcDelay margin = end->margin(this); reportLineTotalMinus("library setup time", margin, early_late); @@ -686,11 +675,10 @@ ReportPath::reportEndpoint(const PathEndPathDelay *end) const reportEndpointOutputDelay(end); else { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); std::string reason = sta::format("{} clocked by {}", clkRegLatchDesc(end), tgtClkName(end)); - reportEndpoint(inst_name, reason); + reportEndpoint(cmd_network_->pathName(inst), reason); } } @@ -721,7 +709,7 @@ ReportPath::reportFull(const PathEndPathDelay *end) const std::string delay_msg = min_max->to_string() + "_delay"; float delay = path_delay->delay(); - reportLine(delay_msg.c_str(), delay, delay, early_late); + reportLine(delay_msg, delay, delay, early_late); if (!path_delay->ignoreClkLatency()) { const Clock *tgt_clk = end->targetClk(this); if (tgt_clk) { @@ -763,7 +751,7 @@ ReportPath::isPropagated(const Path *clk_path, return clk->isPropagated(); } -const char * +std::string_view ReportPath::clkNetworkDelayIdealProp(bool is_prop) const { if (is_prop) @@ -812,7 +800,7 @@ ReportPath::reportEndpointOutputDelay(const PathEndClkConstrained *end) const { Vertex *vertex = end->vertex(this); Pin *pin = vertex->pin(); - const char *pin_name = cmd_network_->pathName(pin); + std::string pin_name = cmd_network_->pathName(pin); const Clock *tgt_clk = end->targetClk(this); if (network_->isTopLevelPort(pin)) { // Pin direction is "output" even for bidirects. @@ -869,7 +857,7 @@ void ReportPath::reportEndpoint(const PathEndGatedClock *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); + const std::string inst_name = cmd_network_->pathName(inst); const RiseFall *clk_end_rf = end->targetClkEndTrans(this); const RiseFall *clk_rf = (end->minMax(this) == MinMax::max()) ? clk_end_rf @@ -936,7 +924,7 @@ void ReportPath::reportEndpoint(const PathEndDataCheck *end) const { Instance *inst = network_->instance(end->vertex(this)->pin()); - const char *inst_name = cmd_network_->pathName(inst); + const std::string inst_name = cmd_network_->pathName(inst); std::string reason = sta::format("{} edge-triggered data to data check clocked by {}", asRisingFalling(end->dataClkPath()->transition(this)), end->targetClk(this)->name()); @@ -976,7 +964,7 @@ ReportPath::reportEndLine(const PathEnd *end) const { std::string line; std::string endpoint = pathEndpoint(end); - reportDescription(endpoint.c_str(), line); + reportDescription(endpoint, line); const EarlyLate *early_late = end->pathEarlyLate(this); reportSpaceFieldDelay(end->requiredTimeOffset(this), early_late, line); reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); @@ -1007,10 +995,10 @@ ReportPath::reportSummaryLine(const PathEnd *end) const PathExpanded expanded(end->path(), this); const EarlyLate *early_late = end->pathEarlyLate(this); std::string startpoint = pathStartpoint(end, expanded); - reportDescription(startpoint.c_str(), line); + reportDescription(startpoint, line); line += ' '; std::string endpoint = pathEndpoint(end); - reportDescription(endpoint.c_str(), line); + reportDescription(endpoint, line); if (end->isUnconstrained()) reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); else @@ -1024,14 +1012,14 @@ ReportPath::pathStartpoint(const PathEnd *end, { const Path *start = expanded.startPath(); Pin *pin = start->pin(graph_); - const char *pin_name = cmd_network_->pathName(pin); + std::string pin_name = cmd_network_->pathName(pin); if (network_->isTopLevelPort(pin)) { PortDirection *dir = network_->direction(pin); 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)); + std::string cell_name = cmd_network_->name(network_->cell(inst)); return sta::format("{} ({})", pin_name, cell_name); } } @@ -1040,14 +1028,14 @@ std::string ReportPath::pathEndpoint(const PathEnd *end) const { Pin *pin = end->vertex(this)->pin(); - const char *pin_name = cmd_network_->pathName(pin); + std::string pin_name = cmd_network_->pathName(pin); if (network_->isTopLevelPort(pin)) { PortDirection *dir = network_->direction(pin); 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)); + std::string cell_name = cmd_network_->name(network_->cell(inst)); return sta::format("{} ({})", pin_name, cell_name); } } @@ -1157,7 +1145,7 @@ ReportPath::reportJson(const Path *path) const void ReportPath::reportJson(const Path *path, - const char *path_name, + std::string_view path_name, int indent, bool trailing_comma, std::string &result) const @@ -1168,7 +1156,7 @@ ReportPath::reportJson(const Path *path, void ReportPath::reportJson(const PathExpanded &expanded, - const char *path_name, + std::string_view path_name, int indent, bool trailing_comma, std::string &result) const @@ -1188,26 +1176,26 @@ ReportPath::reportJson(const PathExpanded &expanded, if (inst) { result += sta::format("{:>{}} \"instance\": \"{}\",\n", - "", indent, - sdc_network_->pathName(inst)); + "", indent, + sdc_network_->pathName(inst)); Cell *cell = network_->cell(inst); if (cell) result += sta::format("{:>{}} \"cell\": \"{}\",\n", - "", indent, - sdc_network_->name(cell)); + "", indent, + sdc_network_->name(cell)); result += sta::format("{:>{}} \"verilog_src\": \"{}\",\n", - "", indent, - sdc_network_->getAttribute(inst, "src").c_str()); + "", indent, + sdc_network_->getAttribute(inst, "src")); } result += sta::format("{:>{}} \"pin\": \"{}\",\n", - "", indent, - sdc_network_->pathName(pin)); + "", indent, + sdc_network_->pathName(pin)); if (net) { result += sta::format("{:>{}} \"net\": \"{}\",\n", - "", indent, - sdc_network_->pathName(net)); + "", indent, + sdc_network_->pathName(net)); } PinSeq pins_above; @@ -1216,9 +1204,9 @@ ReportPath::reportJson(const PathExpanded &expanded, result += sta::format("{:>{}} \"hier_pins\": [\n", "", indent); for (const Pin *hpin : pins_above) { result += sta::format("{:>{}} \"{}\"{}\n", - "", indent, - sdc_network_->pathName(hpin), - (hpin != pins_above.back()) ? "," : ""); + "", indent, + sdc_network_->pathName(hpin), + (hpin != pins_above.back()) ? "," : ""); } result += sta::format("{:>{}} ],\n", "", indent); } @@ -1232,22 +1220,22 @@ ReportPath::reportJson(const PathExpanded &expanded, } result += sta::format("{:>{}} \"arrival\": {:.3e},\n", - "", indent, - delayAsFloat(path->arrival())); + "", indent, + delayAsFloat(path->arrival())); if (is_driver) result += sta::format("{:>{}} \"capacitance\": {:.3e},\n", - "", indent, - graph_delay_calc_->loadCap(pin, rf, scene, min_max)); + "", indent, + graph_delay_calc_->loadCap(pin, rf, scene, min_max)); result += sta::format("{:>{}} \"slew\": {:.3e}\n", - "", indent, - delayAsFloat(path->slew(this))); + "", indent, + delayAsFloat(path->slew(this))); result += sta::format("{:>{}} }}{}\n", - "", indent, - (i < expanded.size() - 1) ? "," : ""); + "", indent, + (i < expanded.size() - 1) ? "," : ""); } result += sta::format("{:>{}}]{}\n", - "", indent, - trailing_comma ? "," : ""); + "", indent, + trailing_comma ? "," : ""); } //////////////////////////////////////////////////////////////// @@ -1269,7 +1257,7 @@ ReportPath::reportSlackOnly(const PathEnd *end) const { std::string line; const EarlyLate *early_late = end->pathEarlyLate(this); - reportDescription(end->pathGroup()->name().c_str(), line); + reportDescription(end->pathGroup()->name(), line); if (end->isUnconstrained()) reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line); else @@ -1345,7 +1333,7 @@ ReportPath::reportShort(const MinPulseWidthCheck &check) const std::string what = sta::format("{} ({})", cmd_network_->pathName(check.pin(this)), mpwCheckHiLow(check)); - reportDescription(what.c_str(), line); + reportDescription(what, line); reportSpaceFieldTime(check.minWidth(this), line); reportSpaceFieldDelay(check.width(this), EarlyLate::late(), line); reportSpaceSlack(check.slack(this), line); @@ -1355,10 +1343,8 @@ ReportPath::reportShort(const MinPulseWidthCheck &check) const void ReportPath::reportVerbose(const MinPulseWidthCheck &check) const { - std::string line; - const char *pin_name = cmd_network_->pathName(check.pin(this)); - line += "Pin: "; - line += pin_name; + std::string pin_name = cmd_network_->pathName(check.pin(this)); + std::string line = "Pin: " + pin_name; report_->reportLine(line); report_->report("Check: sequential_clock_pulse_width"); @@ -1372,11 +1358,11 @@ ReportPath::reportVerbose(const MinPulseWidthCheck &check) const 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); + reportLine(open_clk_msg, open_clk_time, open_clk_time, open_el); Arrival open_arrival = check.openArrival(this); bool is_prop = isPropagated(check.openPath()); - const char *clk_ideal_prop = clkNetworkDelayIdealProp(is_prop); + std::string_view clk_ideal_prop = clkNetworkDelayIdealProp(is_prop); reportLine(clk_ideal_prop, check.openDelay(this), open_arrival, open_el); reportLine(pin_name, delay_zero, open_arrival, open_el); reportLine("open edge arrival time", open_arrival, open_el); @@ -1390,7 +1376,7 @@ ReportPath::reportVerbose(const MinPulseWidthCheck &check) const 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); + reportLine(close_clk_msg, close_clk_time, close_clk_time, close_el); Arrival close_arrival = delaySum(check.closeArrival(this), close_offset, this); reportLine(clk_ideal_prop, check.closeDelay(this), close_arrival, close_el); @@ -1408,13 +1394,13 @@ ReportPath::reportVerbose(const MinPulseWidthCheck &check) const float min_width = check.minWidth(this); std::string rpw_msg = sta::format("required pulse width ({})", mpwCheckHiLow(check)); - reportLine(rpw_msg.c_str(), min_width, EarlyLate::early()); + reportLine(rpw_msg, min_width, EarlyLate::early()); reportLine("actual pulse width", check.width(this), EarlyLate::early()); reportDashLine(); reportSlack(check.slack(this)); } -const char * +std::string_view ReportPath::mpwCheckHiLow(const MinPulseWidthCheck &check) const { if (check.openTransition(this) == RiseFall::rise()) @@ -1490,7 +1476,7 @@ void ReportPath::reportShort(const MinPeriodCheck &check) const { std::string line; - const char *pin_name = cmd_network_->pathName(check.pin()); + const std::string pin_name = cmd_network_->pathName(check.pin()); reportDescription(pin_name, line); reportSpaceFieldDelay(check.period(), EarlyLate::early(), line); reportSpaceFieldDelay(check.minPeriod(this), EarlyLate::early(), line); @@ -1501,10 +1487,7 @@ ReportPath::reportShort(const MinPeriodCheck &check) const void ReportPath::reportVerbose(const MinPeriodCheck &check) const { - std::string line; - const char *pin_name = cmd_network_->pathName(check.pin()); - line += "Pin: "; - line += pin_name; + std::string line = "Pin: " + cmd_network_->pathName(check.pin()); report_->reportLine(line); reportLine("period", check.period(), EarlyLate::early()); @@ -1570,7 +1553,7 @@ ReportPath::reportShort(const MaxSkewCheck &check) const network_->pathName(clk_pin), check_arc->fromEdge()->to_string(), check_arc->toEdge()->to_string()); - reportDescription(what.c_str(), line); + reportDescription(what, line); const EarlyLate *early_late = EarlyLate::early(); reportSpaceFieldDelay(check.maxSkew(this), early_late, line); reportSpaceFieldDelay(check.skew(this), early_late, line); @@ -1607,7 +1590,7 @@ ReportPath::reportVerbose(const MaxSkewCheck &check) const // Based on reportTgtClk. void -ReportPath::reportSkewClkPath(const char *arrival_msg, +ReportPath::reportSkewClkPath(std::string_view arrival_msg, const Path *clk_path) const { const ClockEdge *clk_edge = clk_path->clkEdge(this); @@ -1623,7 +1606,7 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, 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); + reportClkLine(clk, clk_name, clk_end_rf, clk_time, min_max); bool is_prop = isPropagated(clk_path); if (is_prop && reportClkPath()) { @@ -1644,7 +1627,7 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, } else { reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_arrival, early_late); - reportLine(descriptionField(clk_vertex).c_str(), clk_arrival, + reportLine(descriptionField(clk_vertex), clk_arrival, early_late, clk_end_rf); } reportLine(arrival_msg, search_->clkPathArrival(clk_path), early_late); @@ -1677,7 +1660,7 @@ ReportPath::reportLimitShort(const ReportField *field, float slack) const { std::string line; - const char *pin_name = cmd_network_->pathName(pin); + const std::string pin_name = cmd_network_->pathName(pin); reportDescription(pin_name, line); line += ' '; reportField(limit, field, line); @@ -1729,11 +1712,11 @@ ReportPath::reportLimitVerbose(const ReportField *field, reportField(value, field, line); report_->reportLine(line); - int name_width = strlen(field->name()) + 5; + size_t name_width = field->name().size() + 5; reportDashLine(name_width + field->width()); line = "Slack"; - for (int i = strlen("Slack"); i < name_width; i++) + for (size_t i = strlen("Slack"); i < name_width; i++) line += ' '; reportField(slack, field, line); line += (slack >= 0.0) @@ -1756,16 +1739,16 @@ ReportPath::reportStartpoint(const PathEnd *end, const Pin *pin = start->pin(graph_); const ClockEdge *clk_edge = path->clkEdge(this); const Clock *clk = path->clock(search_); - const char *pin_name = cmd_network_->pathName(pin); + std::string pin_name = cmd_network_->pathName(pin); if (pathFromClkPin(path, pin)) { - const char *clk_name = clk->name(); + const std::string &clk_name = clk->name(); std::string reason = sta::format("clock source '{}'", clk_name); reportStartpoint(pin_name, reason); } else if (network_->isTopLevelPort(pin)) { if (clk && clk != sdc->defaultArrivalClock()) { - const char *clk_name = clk->name(); + const std::string &clk_name = clk->name(); // Pin direction is "input" even for bidirects. std::string reason = sta::format("input port clocked by {}", clk_name); reportStartpoint(pin_name, reason); @@ -1775,20 +1758,20 @@ ReportPath::reportStartpoint(const PathEnd *end, } else if (network_->isLeaf(pin) && prev_arc) { Instance *inst = network_->instance(pin); - const char *inst_name = cmd_network_->pathName(inst); + std::string inst_name = cmd_network_->pathName(inst); if (clk_edge) { const RiseFall *clk_rf = clk_edge->transition(); const Path *clk_path = expanded.clkPath(); bool clk_inverted = clk_path && clk_rf != clk_path->transition(this); std::string clk_name = clkName(clk, clk_inverted); - const char *reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); + std::string_view reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); std::string reason = sta::format("{} clocked by {}", reg_desc, clk_name); reportStartpoint(inst_name, reason); } else { - const char *reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); - reportStartpoint(inst_name, reg_desc); + std::string_view reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); + reportStartpoint(inst_name, std::string(reg_desc)); } } else if (network_->isLeaf(pin)) { @@ -1828,15 +1811,15 @@ ReportPath::pathFromClkPin(const Path *path, } void -ReportPath::reportStartpoint(const char *start, - const std::string reason) const +ReportPath::reportStartpoint(std::string_view start, + const std::string &reason) const { reportStartEndPoint(start, reason, "Startpoint"); } void ReportPath::reportUnclockedEndpoint(const PathEnd *end, - const char *default_reason) const + std::string_view default_reason) const { Vertex *vertex = end->vertex(this); Pin *pin = vertex->pin(); @@ -1853,53 +1836,53 @@ ReportPath::reportUnclockedEndpoint(const PathEnd *end, VertexOutEdgeIterator clk_edge_iter(clk_vertex, graph_); while (clk_edge_iter.hasNext()) { Edge *clk_edge = clk_edge_iter.next(); + Instance *inst = network_->instance(pin); + std::string inst_name = cmd_network_->pathName(inst); if (clk_edge->role() == TimingRole::regClkToQ()) { - Instance *inst = network_->instance(pin); - const char *inst_name = cmd_network_->pathName(inst); - const char *reason = regDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); - reportEndpoint(inst_name, reason); + std::string_view reason = + regDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); + reportEndpoint(inst_name, std::string(reason)); return; } if (clk_edge->role() == TimingRole::latchEnToQ()) { - Instance *inst = network_->instance(pin); - const char *inst_name = cmd_network_->pathName(inst); - const char *reason = latchDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); - reportEndpoint(inst_name, reason); + std::string_view reason = + latchDesc(clk_edge->timingArcSet()->isRisingFallingEdge()); + reportEndpoint(inst_name, std::string(reason)); return; } } } } - reportEndpoint(cmd_network_->pathName(pin), default_reason); + reportEndpoint(cmd_network_->pathName(pin), std::string(default_reason)); } else reportEndpoint(cmd_network_->pathName(pin), ""); } void -ReportPath::reportEndpoint(const char *end, - const std::string reason) const +ReportPath::reportEndpoint(std::string_view end, + const std::string &reason) const { reportStartEndPoint(end, reason, "Endpoint"); } void -ReportPath::reportStartEndPoint(const char *pt, - std::string reason, - const char *key) const +ReportPath::reportStartEndPoint(std::string_view pt, + const std::string &reason, + std::string_view key) const { std::string line; // Account for punctuation in the line. - int line_len = strlen(key) + 2 + strlen(pt) + 2 + reason.size() + 1; + size_t line_len = key.size() + 2 + pt.size() + 2 + reason.size() + 1; if (!no_split_ && line_len > start_end_pt_width_) { - line = key; + line = std::string(key); line += ": "; line += pt; report_->reportLine(line); line.clear(); - for (unsigned i = 0; i < strlen(key); i++) + for (size_t i = 0; i < key.size(); i++) line += ' '; line += " ("; @@ -1908,7 +1891,7 @@ ReportPath::reportStartEndPoint(const char *pt, report_->reportLine(line); } else { - line = key; + line = std::string(key); line += ": "; line += pt; line += " ("; @@ -1972,7 +1955,7 @@ ReportPath::clkName(const Clock *clk, return name; } -const char * +std::string_view ReportPath::clkRegLatchDesc(const PathEnd *end) const { // Goofy libraries can have registers with both rising and falling @@ -2103,13 +2086,13 @@ ReportPath::reportSrcClkAndPath(const Path *path, const EarlyLate *early_late = min_max; if (reportGenClkSrcPath(clk_path, clk, clk_rf, min_max, early_late, sdc) && !(path_from_input && !input_has_ref_path)) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); + reportClkLine(clk, clk_name, clk_end_rf, clk_time, min_max); reportGenClkSrcAndPath(path, clk, clk_rf, early_late, time_offset, time_offset, clk_used_as_data, mode); } else if (clk_used_as_data && pathFromGenPropClk(path, path->minMax(this))) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); + reportClkLine(clk, clk_name, clk_end_rf, clk_time, min_max); const ClkInfo *clk_info = path->tag(search_)->clkInfo(); if (clk_info->isPropagated()) reportClkSrcLatency(clk_insertion, clk_time, early_late); @@ -2118,12 +2101,12 @@ ReportPath::reportSrcClkAndPath(const Path *path, else if (is_prop && reportClkPath() && !(path_from_input && !input_has_ref_path)) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); + reportClkLine(clk, clk_name, clk_end_rf, clk_time, early_late); reportClkSrcLatency(clk_insertion, clk_time, early_late); reportPath1(path, expanded, false, time_offset); } else if (clk_used_as_data) { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); + reportClkLine(clk, clk_name, clk_end_rf, clk_time, early_late); if (delayGreater(clk_insertion, 0.0, this)) reportClkSrcLatency(clk_insertion, clk_time, early_late); if (reportClkPath()) @@ -2137,7 +2120,7 @@ ReportPath::reportSrcClkAndPath(const Path *path, reportLine("clock network delay", clk_delay, end_arrival, early_late); Vertex *end_vertex = path->vertex(this); - reportLine(descriptionField(end_vertex).c_str(), + reportLine(descriptionField(end_vertex), end_arrival, early_late, clk_end_rf); } } @@ -2148,7 +2131,7 @@ ReportPath::reportSrcClkAndPath(const Path *path, clk_end_time, early_late); } else { - reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, min_max); + reportClkLine(clk, clk_name, clk_end_rf, clk_time, min_max); Arrival clk_arrival = clk_end_time; reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_arrival, early_late); @@ -2204,7 +2187,7 @@ ReportPath::reportTgtClk(const PathEnd *end, 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); + reportClkLine(clk, clk_name, clk_end_rf, prev_time, clk_time, min_max); const TimingRole *check_role = end->checkRole(this); if (is_prop && reportClkPath()) { float time_offset = prev_time @@ -2245,12 +2228,12 @@ ReportPath::reportTgtClk(const PathEnd *end, reportCommonClkPessimism(end, clk_arrival); if (clk_path) { Vertex *clk_vertex = clk_path->vertex(this); - reportLine(descriptionField(clk_vertex).c_str(), - delaySum(delaySum(prev_time, - end->targetClkArrival(this), - this), - end->sourceClkOffset(this), - this), + reportLine(descriptionField(clk_vertex), + delaySum(delaySum(prev_time, + end->targetClkArrival(this), + this), + end->sourceClkOffset(this), + this), min_max, clk_end_rf); } } @@ -2315,7 +2298,7 @@ ReportPath::isGenPropClk(const Clock *clk, void ReportPath::reportClkLine(const Clock *clk, - const char *clk_name, + std::string_view clk_name, const RiseFall *clk_rf, Arrival clk_time, const MinMax *min_max) const @@ -2325,23 +2308,23 @@ ReportPath::reportClkLine(const Clock *clk, void ReportPath::reportClkLine(const Clock *clk, - const char *clk_name, + std::string_view clk_name, const RiseFall *clk_rf, Arrival prev_time, Arrival clk_time, const MinMax *min_max) const { - const char *rise_fall = asRiseFall(clk_rf); + std::string_view rise_fall = asRiseFall(clk_rf); std::string clk_msg = sta::format("clock {} ({} edge)", clk_name, rise_fall); if (clk->isPropagated()) - reportLine(clk_msg.c_str(), + reportLine(clk_msg, 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(), + reportLine(clk_msg, clk_slew, delayDiff(clk_time, prev_time, this), clk_time, @@ -2457,7 +2440,7 @@ void ReportPath::reportPathLine(const Path *path, const Delay &incr, const Arrival &time, - const char *line_case) const + std::string_view line_case) const { Vertex *vertex = path->vertex(this); Pin *pin = vertex->pin(); @@ -2477,9 +2460,8 @@ ReportPath::reportPathLine(const Path *path, // Don't show capacitance field for input pins. 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, field_blank_, time, false, early_late, rf, src_attr, - line_case); + reportLine(what, cap, slew, field_blank_, incr, field_blank_, + time, false, early_late, rf, src_attr, line_case); } void @@ -2497,7 +2479,7 @@ ReportPath::reportRequired(const PathEnd *end, if (macro_clk_tree_delay != 0.0) reportLine("macro clock tree delay", -macro_clk_tree_delay, delaySum(req_time, margin, this), early_late); - reportLine(margin_msg.c_str(), + reportLine(margin_msg, delayDiff(delay_zero, margin, this), req_time, early_late); @@ -2520,7 +2502,7 @@ void ReportPath::reportSlack(Slack slack) const { const EarlyLate *early_late = EarlyLate::early(); - const char *msg = (delayAsFloat(slack, early_late, this) >= 0.0) + std::string_view msg = (delayAsFloat(slack, early_late, this) >= 0.0) ? "slack (MET)" : "slack (VIOLATED)"; reportLine(msg, slack, early_late); @@ -2750,7 +2732,7 @@ ReportPath::reportPath6(const Path *path, Pin *pin = vertex->pin(); Arrival time = delaySum(path1->arrival(), time_offset, this); Delay incr = 0.0; - const char *line_case = nullptr; + std::string_view line_case = ""; bool is_clk_start = path1->vertex(this) == clk_start; bool is_clk = path1->isClock(search_); Instance *inst = network_->instance(pin); @@ -2847,13 +2829,13 @@ ReportPath::reportPath6(const Path *path, if (field_fanout_->enabled()) fanout = drvrFanout(vertex, scene, min_max); const std::string what = descriptionField(vertex); - reportLine(what.c_str(), cap, slew, fanout, + reportLine(what, cap, slew, fanout, 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_, + reportLine(what2, field_blank_, field_blank_, field_blank_, field_blank_, field_blank_, field_blank_, false, min_max, nullptr, src_attr, ""); } @@ -2866,7 +2848,7 @@ ReportPath::reportPath6(const Path *path, || (i == path_last_index) || is_clk_start) { const std::string what = descriptionField(vertex); - reportLine(what.c_str(), field_blank_, slew, field_blank_, + reportLine(what, field_blank_, slew, field_blank_, incr, field_blank_, time, false, min_max, rf, src_attr, line_case); prev_time = time; @@ -2892,28 +2874,28 @@ ReportPath::reportVariation(const Path *path) const 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); + field_blank_, std_dev, field_blank_, true, min_max, + 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); + field_blank_, mean, field_blank_, true, min_max, + 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); + field_blank_, mean_shift, field_blank_, true, min_max, + 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); + field_blank_, std_dev, field_blank_, true, min_max, + 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); + field_blank_, skewness, field_blank_, true, min_max, + nullptr, "", ""); break; } default: @@ -2939,7 +2921,7 @@ ReportPath::reportHierPinsThru(const Path *path) const if (prev_edge && prev_edge->isWire()) { 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_, + reportLine(what, field_blank_, field_blank_, field_blank_, field_blank_, field_blank_, field_blank_, false, path->minMax(this), nullptr, "", ""); } @@ -2967,8 +2949,7 @@ ReportPath::descriptionField(const Vertex *vertex) const std::string ReportPath::descriptionField(const Pin *pin) const { - const char *pin_name = cmd_network_->pathName(pin); - const char *name2; + std::string name2; if (network_->isTopLevelPort(pin)) { PortDirection *dir = network_->direction(pin); // Translate port direction. Note that this is intentionally @@ -2987,21 +2968,20 @@ ReportPath::descriptionField(const Pin *pin) const Instance *inst = network_->instance(pin); name2 = network_->cellName(inst); } + std::string pin_name = cmd_network_->pathName(pin); return sta::format("{} ({})", pin_name, name2); } std::string ReportPath::descriptionNet(const Pin *pin) const { - if (network_->isTopLevelPort(pin)) { - const char *pin_name = cmd_network_->pathName(pin); - return sta::format("{} (net)", pin_name); - } + if (network_->isTopLevelPort(pin)) + return sta::format("{} (net)", cmd_network_->pathName(pin)); else { Net *net = network_->net(pin); if (net) { Net *highest_net = network_->highestNetAbove(net); - const char *net_name = cmd_network_->pathName(highest_net); + std::string net_name = cmd_network_->pathName(highest_net); return sta::format("{} (net)", net_name); } else @@ -3131,78 +3111,73 @@ ReportPath::reportPathHeader() const // Report total. void -ReportPath::reportLine(const char *what, +ReportPath::reportLine(std::string_view what, Delay total, const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, field_blank_, - field_blank_, total, false, early_late, nullptr, - "", nullptr); + field_blank_, total, false, early_late, nullptr, "", ""); } // Report negative total. void -ReportPath::reportLineNegative(const char *what, +ReportPath::reportLineNegative(std::string_view what, Delay total, const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, field_blank_, field_blank_, total, true /* tota_with_minus */, - early_late, nullptr, "", nullptr); + early_late, nullptr, "", ""); } // Report total, and transition suffix. void -ReportPath::reportLine(const char *what, +ReportPath::reportLine(std::string_view what, Delay total, const EarlyLate *early_late, const RiseFall *rf) const { reportLine(what, field_blank_, field_blank_, field_blank_, - field_blank_, field_blank_, total, false, early_late, rf, "", - nullptr); + field_blank_, field_blank_, total, false, early_late, rf, "", ""); } // Report increment, and total. void -ReportPath::reportLine(const char *what, +ReportPath::reportLine(std::string_view what, const Delay &incr, const Delay &total, const EarlyLate *early_late) const { reportLine(what, field_blank_, field_blank_, field_blank_, - incr, field_blank_, total, false, early_late, nullptr, "", - nullptr); + incr, field_blank_, total, false, early_late, nullptr, "", ""); } // Report increment, total, and transition suffix. void -ReportPath::reportLine(const char *what, +ReportPath::reportLine(std::string_view what, const Delay &incr, const Delay &total, const EarlyLate *early_late, const RiseFall *rf) const { reportLine(what, field_blank_, field_blank_, field_blank_, - incr, field_blank_, total, false, early_late, rf, "", - nullptr); + incr, field_blank_, total, false, early_late, rf, "", ""); } // Report slew, increment, and total. void -ReportPath::reportLine(const char *what, +ReportPath::reportLine(std::string_view what, const Slew &slew, const Delay &incr, const Delay &total, const EarlyLate *early_late) const { reportLine(what, field_blank_, slew, field_blank_, - incr, field_blank_, total, false, early_late, nullptr, - "", nullptr); + incr, field_blank_, total, false, early_late, nullptr, "", ""); } void -ReportPath::reportLine(const char *what, +ReportPath::reportLine(std::string_view what, float cap, const Slew &slew, float fanout, @@ -3213,7 +3188,7 @@ ReportPath::reportLine(const char *what, const EarlyLate *early_late, const RiseFall *rf, std::string src_attr, - const char *line_case) const + std::string_view line_case) const { std::string line; size_t field_index = 0; @@ -3232,7 +3207,7 @@ ReportPath::reportLine(const char *what, reportFieldBlank(field, line); else line += sta::format("{:{}}", - static_cast(fanout), + fanout, field_fanout_->width()); } else if (field == field_capacitance_) @@ -3259,11 +3234,11 @@ ReportPath::reportLine(const char *what, reportFieldBlank(field, line); else if (field == field_src_attr_) { if (src_attr != "") - reportField(src_attr.c_str(), field, line); + reportField(src_attr, field, line); else reportFieldBlank(field, line); } - else if (field == field_case_ && line_case) + else if (field == field_case_) line += line_case; first_field = false; @@ -3280,7 +3255,7 @@ ReportPath::reportLine(const char *what, // Only the total field. void -ReportPath::reportLineTotal(const char *what, +ReportPath::reportLineTotal(std::string_view what, const Delay &incr, const EarlyLate *early_late) const { @@ -3289,7 +3264,7 @@ ReportPath::reportLineTotal(const char *what, // Only the total field and always with leading minus sign. void -ReportPath::reportLineTotalMinus(const char *what, +ReportPath::reportLineTotalMinus(std::string_view what, const Delay &decr, const EarlyLate *early_late) const { @@ -3297,7 +3272,7 @@ ReportPath::reportLineTotalMinus(const char *what, } void -ReportPath::reportLineTotal1(const char *what, +ReportPath::reportLineTotal1(std::string_view what, const Delay &incr, bool incr_with_minus, const EarlyLate *early_late) const @@ -3321,29 +3296,29 @@ ReportPath::reportDashLineTotal() const //////////////////////////////////////////////////////////////// void -ReportPath::reportDescription(const char *what, +ReportPath::reportDescription(std::string_view what, std::string &line) const { reportDescription(what, false, false, line); } void -ReportPath::reportDescription(const char *what, +ReportPath::reportDescription(std::string_view what, bool first_field, bool last_field, std::string &line) const { line += what; - int length = strlen(what); + size_t length = what.size(); if (!no_split_ && first_field && length > field_description_->width()) { reportBlankLine(); - for (int i = 0; i < field_description_->width(); i++) + for (size_t i = 0; i < field_description_->width(); i++) line += ' '; } else if (!last_field) { - for (int i = length; i < field_description_->width(); i++) + for (size_t i = length; i < field_description_->width(); i++) line += ' '; } } @@ -3360,7 +3335,7 @@ ReportPath::reportFieldTime(float value, if (str == minus_zero_) // Filter "-0.00" fields. str = plus_zero_; - reportField(str.c_str(), field, line); + reportField(str, field, line); } } @@ -3390,7 +3365,7 @@ ReportPath::reportTotalDelay(const Delay &value, if (str == minus_zero_) // Filter "-0.00" fields. str = plus_zero_; - reportField(str.c_str(), field_total_, line); + reportField(str, field_total_, line); } // Total time always with leading minus sign. @@ -3409,7 +3384,7 @@ ReportPath::reportFieldDelayMinus(const Delay &value, if (str == plus_zero_) // Force leading minus sign. str = minus_zero_; - reportField(str.c_str(), field, line); + reportField(str, field, line); } } @@ -3426,7 +3401,7 @@ ReportPath::reportFieldDelay(const Delay &value, if (str == minus_zero_) // Filter "-0.00" fields. str = plus_zero_; - reportField(str.c_str(), field, line); + reportField(str, field, line); } } @@ -3441,24 +3416,24 @@ ReportPath::reportField(float value, Unit *unit = field->unit(); if (unit) { std::string value_str = unit->asString(value, digits_); - reportField(value_str.c_str(), field, line); + reportField(value_str, field, line); } else { // fanout std::string value_str = sta::format("{:.0f}", value); - reportField(value_str.c_str(), field, line); + reportField(value_str, field, line); } } } void -ReportPath::reportField(const char *value, +ReportPath::reportField(std::string_view value, const ReportField *field, std::string &line) const { if (field->leftJustify()) line += value; - for (int i = strlen(value); i < field->width(); i++) + for (size_t i = static_cast(value.size()); i < field->width(); i++) line += ' '; if (!field->leftJustify()) line += value; @@ -3477,7 +3452,7 @@ ReportPath::reportDashLine() const std::string line; for (const ReportField *field : fields_) { if (field->enabled()) { - for (int i = 0; i < field->width(); i++) + for (size_t i = 0; i < field->width(); i++) line += '-'; } } @@ -3509,7 +3484,7 @@ ReportPath::reportClkPath() const //////////////////////////////////////////////////////////////// -const char * +std::string_view ReportPath::asRisingFalling(const RiseFall *rf) const { if (rf == RiseFall::rise()) @@ -3518,7 +3493,7 @@ ReportPath::asRisingFalling(const RiseFall *rf) const return "falling"; } -const char * +std::string_view ReportPath::asRiseFall(const RiseFall *rf) const { if (rf == RiseFall::rise()) @@ -3528,7 +3503,7 @@ ReportPath::asRiseFall(const RiseFall *rf) const } // Find the startpoint type from the first path edge. -const char * +std::string_view ReportPath::edgeRegLatchDesc(const Edge *first_edge, const TimingArc *first_arc) const { @@ -3553,7 +3528,7 @@ ReportPath::edgeRegLatchDesc(const Edge *first_edge, return regDesc(first_arc->fromEdge()->asRiseFall()); } -const char * +std::string_view ReportPath::checkRegLatchDesc(const TimingRole *role, const RiseFall *clk_rf) const { @@ -3567,7 +3542,7 @@ ReportPath::checkRegLatchDesc(const TimingRole *role, return "edge-triggered flip-flop"; } -const char * +std::string_view ReportPath::regDesc(const RiseFall *clk_rf) const { if (clk_rf == RiseFall::rise()) @@ -3578,7 +3553,7 @@ ReportPath::regDesc(const RiseFall *clk_rf) const return "edge-triggered flip-flop"; } -const char * +std::string_view ReportPath::latchDesc(const RiseFall *clk_rf) const { return (clk_rf == RiseFall::rise()) diff --git a/search/ReportPath.hh b/search/ReportPath.hh index cec91ba7e..b27e5fc11 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include "StringUtil.hh" @@ -61,7 +62,7 @@ public: int digits() const { return digits_; } void setDigits(int digits); void setNoSplit(bool no_split); - ReportField *findField(const char *name) const; + ReportField *findField(std::string_view name) const; // Header above reportPathEnd results. void reportPathEndHeader() const; @@ -99,12 +100,12 @@ public: bool last) const; void reportJson(const Path *path) const; void reportJson(const Path *path, - const char *path_name, + std::string_view path_name, int indent, bool trailing_comma, std::string &result) const; void reportJson(const PathExpanded &expanded, - const char *path_name, + std::string_view path_name, int indent, bool trailing_comma, std::string &result) const; @@ -161,8 +162,8 @@ public: protected: void makeFields(); - ReportField *makeField(const char *name, - const char *title, + ReportField *makeField(std::string_view name, + std::string_view title, int width, bool left_justify, Unit *unit, @@ -194,7 +195,7 @@ protected: Arrival &borrow, Arrival &time_given_to_startpoint) const; void reportEndpoint(const PathEndDataCheck *end) const; - const char *clkNetworkDelayIdealProp(bool is_ideal) const; + std::string_view clkNetworkDelayIdealProp(bool is_ideal) const; std::string checkRoleReason(const PathEnd *end) const; std::string checkRoleString(const PathEnd *end) const; @@ -202,19 +203,19 @@ protected: void reportStartpoint(const PathEnd *end, const PathExpanded &expanded) const; void reportUnclockedEndpoint(const PathEnd *end, - const char *default_reason) const; + std::string_view default_reason) const; void reportEndpoint(const PathEndCheck *end) const; void reportEndpoint(const PathEndLatchCheck *end) const; - const char *latchDesc(const PathEndLatchCheck *end) const; - void reportStartpoint(const char *start, - const std::string reason) const; - void reportEndpoint(const char *end, - const std::string reason) const; - void reportStartEndPoint(const char *pt, - const std::string reason, - const char *key) const; + std::string_view latchDesc(const PathEndLatchCheck *end) const; + void reportStartpoint(std::string_view start, + const std::string &reason) const; + void reportEndpoint(std::string_view end, + const std::string &reason) const; + void reportStartEndPoint(std::string_view pt, + const std::string &reason, + std::string_view key) const; std::string tgtClkName(const PathEnd *end) const; - const char *clkRegLatchDesc(const PathEnd *end) const; + std::string_view clkRegLatchDesc(const PathEnd *end) const; void reportSrcPath(const PathEnd *end, const PathExpanded &expanded) const; void reportTgtClk(const PathEnd *end) const; @@ -268,18 +269,18 @@ protected: void reportPathLine(const Path *path, const Delay &incr, const Arrival &time, - const char *line_case) const; + std::string_view line_case) const; void reportCommonClkPessimism(const PathEnd *end, Arrival &clk_arrival) const ; void reportClkUncertainty(const PathEnd *end, Arrival &clk_arrival) const ; void reportClkLine(const Clock *clk, - const char *clk_name, + std::string_view clk_name, const RiseFall *clk_rf, Arrival clk_time, const MinMax *min_max) const ; void reportClkLine(const Clock *clk, - const char *clk_name, + std::string_view clk_name, const RiseFall *clk_rf, Arrival prev_time, Arrival clk_time, @@ -334,31 +335,31 @@ protected: void reportHierPinsThru(const Path *path) const; void reportInputExternalDelay(const Path *path, float time_offset) const; - void reportLine(const char *what, + void reportLine(std::string_view what, Delay total, const EarlyLate *early_late) const; - void reportLineNegative(const char *what, + void reportLineNegative(std::string_view what, Delay total, const EarlyLate *early_late) const; - void reportLine(const char *what, + void reportLine(std::string_view what, Delay total, const EarlyLate *early_late, const RiseFall *rf) const; - void reportLine(const char *what, + void reportLine(std::string_view what, const Delay &incr, const Delay &total, const EarlyLate *early_late) const; - void reportLine(const char *what, + void reportLine(std::string_view what, const Delay &incr, const Delay &total, const EarlyLate *early_late, const RiseFall *rf) const; - void reportLine(const char *what, + void reportLine(std::string_view what, const Slew &slew, const Delay &incr, const Delay &total, const EarlyLate *early_late) const; - void reportLine(const char *what, + void reportLine(std::string_view what, float cap, const Slew &slew, float fanout, @@ -369,21 +370,21 @@ protected: const EarlyLate *early_late, const RiseFall *rf, std::string src_attr, - const char *line_case) const; - void reportLineTotal(const char *what, + std::string_view line_case) const; + void reportLineTotal(std::string_view what, const Delay &incr, const EarlyLate *early_late) const; - void reportLineTotalMinus(const char *what, + void reportLineTotalMinus(std::string_view what, const Delay &decr, const EarlyLate *early_late) const; - void reportLineTotal1(const char *what, + void reportLineTotal1(std::string_view what, const Delay &incr, bool incr_with_minus, const EarlyLate *early_late) const; void reportDashLineTotal() const; - void reportDescription(const char *what, + void reportDescription(std::string_view what, std::string &result) const; - void reportDescription(const char *what, + void reportDescription(std::string_view what, bool first_field, bool last_field, std::string &result) const; @@ -409,7 +410,7 @@ protected: void reportField(float value, const ReportField *field, std::string &result) const; - void reportField(const char *value, + void reportField(std::string_view value, const ReportField *field, std::string &result) const; void reportFieldBlank(const ReportField *field, @@ -430,15 +431,15 @@ protected: float drvrFanout(Vertex *drvr, const Scene *scene, const MinMax *min_max) const; - const char *mpwCheckHiLow(const MinPulseWidthCheck &check) const; - void reportSkewClkPath(const char *arrival_msg, + std::string_view mpwCheckHiLow(const MinPulseWidthCheck &check) const; + void reportSkewClkPath(std::string_view arrival_msg, const Path *clk_path) const; - const char *edgeRegLatchDesc(const Edge *edge, - const TimingArc *arc) const; - const char *checkRegLatchDesc(const TimingRole *role, - const RiseFall *clk_rf) const; - const char *regDesc(const RiseFall *clk_rf) const; - const char *latchDesc(const RiseFall *clk_rf) const; + std::string_view edgeRegLatchDesc(const Edge *edge, + const TimingArc *arc) const; + std::string_view checkRegLatchDesc(const TimingRole *role, + const RiseFall *clk_rf) const; + std::string_view regDesc(const RiseFall *clk_rf) const; + std::string_view latchDesc(const RiseFall *clk_rf) const; void pathClkPath(const Path *path, const Path &clk_path) const; bool isPropagated(const Path *clk_path) const; @@ -464,8 +465,8 @@ protected: const InputDelay *input_delay, // Return value. Path &ref_path) const; - const char *asRisingFalling(const RiseFall *rf) const; - const char *asRiseFall(const RiseFall *rf) const; + std::string_view asRisingFalling(const RiseFall *rf) const; + std::string_view asRiseFall(const RiseFall *rf) const; Delay delayIncr(const Delay &time, const Delay &prev, const MinMax *min_max) const; @@ -479,7 +480,7 @@ protected: bool no_split_; int digits_; - int start_end_pt_width_; + size_t start_end_pt_width_; ReportField *field_description_; ReportField *field_total_; @@ -503,34 +504,34 @@ protected: class ReportField { public: - ReportField(const char *name, - const char *title, - int width, + ReportField(std::string_view name, + std::string_view title, + size_t width, bool left_justify, Unit *unit, bool enabled); ~ReportField(); - void setProperties(const char *title, - int width, + void setProperties(std::string_view title, + size_t width, bool left_justify); - const char *name() const { return name_; } - const char *title() const { return title_; } - int width() const { return width_; } - void setWidth(int width); + const std::string &name() const { return name_; } + const std::string &title() const { return title_; } + size_t width() const { return width_; } + void setWidth(size_t width); bool leftJustify() const { return left_justify_; } Unit *unit() const { return unit_; } - const char *blank() const { return blank_; } + const std::string &blank() const { return blank_; } void setEnabled(bool enabled); bool enabled() const { return enabled_; } protected: - const char *name_; - const char *title_; - int width_; + std::string name_; + std::string title_; + size_t width_; bool left_justify_; Unit *unit_; bool enabled_; - char *blank_; + std::string blank_; }; } // namespace diff --git a/search/Search.cc b/search/Search.cc index ae6bbfbf5..f1a9658c1 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1390,7 +1390,7 @@ ArrivalVisitor::pruneCrprArrivals() ? delayDiff(max_arrival, max_crpr, this) : delaySum(max_arrival, max_crpr, this); debugPrint(debug_, "search", 4, " cmp {} {} - {} = {}", - tag->to_string(this).c_str(), + tag->to_string(this), delayAsString(max_arrival, this), delayAsString(max_crpr, this), delayAsString(max_arrival_max_crpr, this)); @@ -1767,7 +1767,7 @@ Search::seedInputDelayArrival(const Pin *pin, { debugPrint(debug_, "search", 2, input_delay ? "arrival seed input arrival {}" : "arrival seed input {}", - vertex->to_string(this).c_str()); + vertex->to_string(this)); const ClockEdge *clk_edge = nullptr; const Pin *ref_pin = nullptr; const Sdc *sdc = mode->sdc(); @@ -2673,7 +2673,7 @@ Search::findTagGroup(TagGroupBldr *tag_bldr) { TagGroup probe(tag_bldr, this); LockGuard lock(tag_group_lock_); - TagGroup *tag_group = findKey(tag_group_set_, &probe); + TagGroup *tag_group = findKey(*tag_group_set_, &probe); if (tag_group == nullptr) { TagGroupIndex tag_group_index; if (tag_group_free_indices_.empty()) @@ -2918,13 +2918,13 @@ Search::findTag(Scene *scene, 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); + Tag *tag = findKey(*tag_cache, &probe); if (tag) return tag; } LockGuard lock(tag_lock_); - Tag *tag = findKey(tag_set_, &probe); + Tag *tag = findKey(*tag_set_, &probe); if (tag == nullptr) { // Make rise/fall versions of the tag to avoid tag_set lookups when the // only change is the rise/fall edge. @@ -3014,7 +3014,7 @@ Search::findClkInfo(Scene *scene, 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); + 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, @@ -3449,7 +3449,7 @@ RequiredCmp::requiredsSave(Vertex *vertex, const Required &prev_req = path->required(); bool changed = !delayEqual(prev_req, req, sta); debugPrint(debug, "search", 3, "required {} save {} -> {}{}", - path->to_string(sta).c_str(), + path->to_string(sta), delayAsString(prev_req, sta), delayAsString(req, sta), changed ? " changed" : ""); @@ -3549,7 +3549,7 @@ RequiredVisitor::visitFromToPath(const Pin *, Required from_required = delayDiff(to_required, arc_delay, this); debugPrint(debug_, "search", 3, " to tag {:2}: {}", to_tag->index(), - to_tag->to_string(this).c_str()); + to_tag->to_string(this)); debugPrint(debug_, "search", 3, " {} - {} = {} {} {}", delayAsString(to_required, this), delayAsString(arc_delay, this), @@ -3573,7 +3573,7 @@ RequiredVisitor::visitFromToPath(const Pin *, Required from_required = delayDiff(to_required, arc_delay, this); debugPrint(debug_, "search", 3, " to tag {:2}: {}", to_path_tag->index(), - to_path_tag->to_string(this).c_str()); + to_path_tag->to_string(this)); debugPrint(debug_, "search", 3, " {} - {} = {} {} {}", delayAsString(to_required, this), delayAsString(arc_delay, this), @@ -3838,7 +3838,7 @@ Search::tnsIncr(Vertex *vertex, if (delayLess(slack, 0.0, this)) { debugPrint(debug_, "tns", 3, "tns+ {} {}", delayAsString(slack, this), - vertex->to_string(this).c_str()); + vertex->to_string(this)); delayIncr(tns_[path_ap_index], slack, this); if (tns_slacks_[path_ap_index].contains(vertex)) report_->critical(1513, "tns incr existing vertex"); @@ -3857,7 +3857,7 @@ Search::tnsDecr(Vertex *vertex, && delayLess(slack, 0.0, this)) { debugPrint(debug_, "tns", 3, "tns- {} {}", delayAsString(slack, this), - vertex->to_string(this).c_str()); + vertex->to_string(this)); 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 e6716ae1e..fe836156d 100644 --- a/search/Search.i +++ b/search/Search.i @@ -231,20 +231,21 @@ vertex_worst_slack_path(Vertex *vertex, float endpoint_slack(const Pin *pin, - const char *path_group_name, + const std::string &path_group_name, const MinMax *min_max) { Sta *sta = Sta::sta(); 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, min_max, sta)); - } - else { + if (!path_group_name.empty() + && !sta->isGroupPathName(path_group_name, sta->cmdSdc())) { sta->report()->error(1577, "{} is not a known path group name.", path_group_name); return INF; } + else { + Slack slack = sta->endpointSlack(pin, path_group_name, min_max); + return sta->units()->timeUnit()->staToUser(delayAsFloat(slack, min_max, sta)); + } } StringSeq @@ -734,8 +735,8 @@ write_timing_model_cmd(const char *lib_name, void define_scene_cmd(const char *name, const char *mode_name, - const StringSeq liberty_min_files, - const StringSeq liberty_max_files, + StringSeq liberty_min_files, + StringSeq liberty_max_files, const char *spef_min_file, const char *spef_max_file) { @@ -746,7 +747,7 @@ define_scene_cmd(const char *name, } void -define_scenes_cmd(const StringSeq &scene_names) +define_scenes_cmd(StringSeq scene_names) { Sta *sta = Sta::sta(); sta->makeScenes(scene_names); @@ -953,18 +954,18 @@ crpr_mode() } void -set_crpr_mode(const char *mode) +set_crpr_mode(std::string mode) { Sta *sta = Sta::sta(); - if (stringEq(mode, "same_pin")) + if (stringEqual(mode, "same_pin")) sta->setCrprMode(CrprMode::same_pin); - else if (stringEq(mode, "same_transition")) + else if (stringEqual(mode, "same_transition")) sta->setCrprMode(CrprMode::same_transition); else sta->report()->error(1573, "unknown common clk pessimism mode."); } -const char * +const std::string & pocv_mode() { return pocvModeName(Sta::sta()->pocvMode()); diff --git a/search/Sim.cc b/search/Sim.cc index bf459097f..f2fd65319 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -873,9 +873,9 @@ Sim::isDisabledMode(Edge *edge, const std::string &mode_value = arc_set->modeValue(); if (!mode_name.empty() && !mode_value.empty()) { LibertyCell *cell = network_->libertyCell(inst); - const ModeDef *mode_def = cell->findModeDef(mode_name.c_str()); + const ModeDef *mode_def = cell->findModeDef(mode_name); if (mode_def) { - const ModeValueDef *value_def = mode_def->findValueDef(mode_value.c_str()); + const ModeValueDef *value_def = mode_def->findValueDef(mode_value); if (value_def) { FuncExpr *cond = value_def->cond(); if (cond) { @@ -883,7 +883,7 @@ Sim::isDisabledMode(Edge *edge, if (cond_value == LogicValue::zero) { // For a mode value to be disabled by having a value of // logic zero one mode value must logic one. - for (const auto &[name, value_def] : *mode_def->values()) { + for (const auto &[name, value_def] : mode_def->values()) { FuncExpr *cond1 = value_def.cond(); if (cond1) { LogicValue cond_value1 = evalExpr(cond1, inst); diff --git a/search/Sta.cc b/search/Sta.cc index 355ef3f03..7a47319d2 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -61,6 +61,7 @@ #include "DelayCalc.hh" #include "ArcDelayCalc.hh" #include "GraphDelayCalc.hh" +#include "sdf/SdfReader.hh" #include "sdf/SdfWriter.hh" #include "Levelize.hh" #include "Sim.hh" @@ -257,7 +258,6 @@ deleteAllMemory() deleteDelayCalcs(); PortDirection::destroy(); deleteLiberty(); - deleteTmpStrings(); } //////////////////////////////////////////////////////////////// @@ -581,7 +581,7 @@ Sta::cmdSdc() const } void -Sta::setCmdMode(const std::string &mode_name) +Sta::setCmdMode(std::string_view mode_name) { if (!mode_name.empty()) { if (!mode_name_map_.contains(mode_name)) { @@ -592,7 +592,7 @@ Sta::setCmdMode(const std::string &mode_name) modes_.clear(); } Mode *mode = new Mode(mode_name, mode_name_map_.size(), this); - mode_name_map_[mode_name] = mode; + mode_name_map_[std::string(mode_name)] = mode; modes_.push_back(mode); mode->sim()->setMode(mode); mode->sim()->setObserver(new StaSimObserver(this)); @@ -605,16 +605,16 @@ Sta::setCmdMode(const std::string &mode_name) } Mode * -Sta::findMode(const std::string &mode_name) const +Sta::findMode(std::string_view mode_name) const { - return findKey(mode_name_map_, mode_name); + return findStringKey(mode_name_map_, mode_name); } ModeSeq Sta::findModes(const std::string &name) const { ModeSeq matches; - PatternMatch pattern(name.c_str()); + PatternMatch pattern(name); for (Mode *mode : modes_) { if (pattern.match(mode->name())) matches.push_back(mode); @@ -692,7 +692,7 @@ Sta::setCurrentInstance(Instance *inst) //////////////////////////////////////////////////////////////// LibertyLibrary * -Sta::readLiberty(const char *filename, +Sta::readLiberty(std::string_view filename, Scene *scene, const MinMaxAll *min_max, bool infer_latches) @@ -712,7 +712,7 @@ Sta::readLiberty(const char *filename, } LibertyLibrary * -Sta::readLibertyFile(const char *filename, +Sta::readLibertyFile(std::string_view filename, Scene *scene, const MinMaxAll *min_max, bool infer_latches) @@ -732,13 +732,6 @@ Sta::readLibertyFile(const char *filename, return liberty; } -LibertyLibrary * -Sta::readLibertyFile(const char *filename, - bool infer_latches) -{ - return sta::readLibertyFile(filename, infer_latches, network_); -} - void Sta::readLibertyAfter(LibertyLibrary *liberty, Scene *scene, @@ -750,7 +743,7 @@ Sta::readLibertyAfter(LibertyLibrary *liberty, } bool -Sta::readVerilog(const char *filename) +Sta::readVerilog(std::string_view filename) { NetworkReader *network = networkReader(); if (network) { @@ -785,6 +778,24 @@ Sta::linkDesign(const char *top_cell_name, //////////////////////////////////////////////////////////////// +bool +Sta::readSdf(std::string_view filename, + std::string_view path, + Scene *scene, + bool unescaped_dividers, + bool incremental_only, + MinMaxAll *cond_use) +{ + ensureLibLinked(); + ensureGraph(); + bool success = sta::readSdf(filename, path, scene, unescaped_dividers, + incremental_only, cond_use, this); + search_->arrivalsInvalid(); + return success; +} + +//////////////////////////////////////////////////////////////// + void Sta::setDebugLevel(const char *what, int level) @@ -1145,12 +1156,12 @@ Sta::setMaxArea(float area, } void -Sta::makeClock(const char *name, +Sta::makeClock(std::string_view name, PinSet *pins, bool add_to_pins, float period, FloatSeq *waveform, - char *comment, + std::string_view comment, const Mode *mode) { mode->sdc()->makeClock(name, pins, add_to_pins, period, waveform, comment); @@ -1161,7 +1172,7 @@ Sta::makeClock(const char *name, } void -Sta::makeGeneratedClock(const char *name, +Sta::makeGeneratedClock(std::string_view name, PinSet *pins, bool add_to_pins, Pin *src_pin, @@ -1173,7 +1184,7 @@ Sta::makeGeneratedClock(const char *name, bool combinational, IntSeq *edges, FloatSeq *edge_shifts, - char *comment, + std::string_view comment, const Mode *mode) { mode->sdc()->makeGeneratedClock(name, pins, add_to_pins, src_pin, master_clk, @@ -1375,13 +1386,13 @@ Sta::makeClockGroups(const std::string &name, bool physically_exclusive, bool asynchronous, bool allow_paths, - const char *comment, + std::string comment, Sdc *sdc) { ClockGroups *groups = sdc->makeClockGroups(name, logically_exclusive, physically_exclusive, asynchronous, allow_paths, - comment); + std::move(comment)); search_->requiredsInvalid(); return groups; } @@ -1998,7 +2009,7 @@ Sta::makeFalsePath(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max, - const char *comment, + std::string_view comment, Sdc *sdc) { sdc->makeFalsePath(from, thrus, to, min_max, comment); @@ -2012,7 +2023,7 @@ Sta::makeMulticyclePath(ExceptionFrom *from, const MinMaxAll *min_max, bool use_end_clk, int path_multiplier, - const char *comment, + std::string_view comment, Sdc *sdc) { sdc->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, path_multiplier, @@ -2028,7 +2039,7 @@ Sta::makePathDelay(ExceptionFrom *from, bool ignore_clk_latency, bool break_path, float delay, - const char *comment, + std::string_view comment, Sdc *sdc) { sdc->makePathDelay(from, thrus, to, min_max, ignore_clk_latency, break_path, delay, @@ -2049,12 +2060,12 @@ Sta::resetPath(ExceptionFrom *from, } void -Sta::makeGroupPath(const std::string &name, +Sta::makeGroupPath(std::string_view name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, - const char *comment, + std::string_view comment, Sdc *sdc) { sdc->makeGroupPath(name, is_default, from, thrus, to, comment); @@ -2062,21 +2073,21 @@ Sta::makeGroupPath(const std::string &name, } bool -Sta::isGroupPathName(const char *group_name, +Sta::isGroupPathName(std::string_view group_name, const Sdc *sdc) { return isPathGroupName(group_name, sdc); } bool -Sta::isPathGroupName(const char *group_name, +Sta::isPathGroupName(std::string_view 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()); + || group_name == PathGroups::asyncPathGroupName() + || group_name == PathGroups::pathDelayGroupName() + || group_name == PathGroups::gatedClkGroupName() + || group_name == PathGroups::unconstrainedGroupName(); } StringSeq @@ -2089,10 +2100,10 @@ Sta::pathGroupNames(const Sdc *sdc) const for (auto const &[name, group] : sdc->groupPaths()) names.push_back(name); - names.push_back(PathGroups::asyncPathGroupName()); - names.push_back(PathGroups::pathDelayGroupName()); - names.push_back(PathGroups::gatedClkGroupName()); - names.push_back(PathGroups::unconstrainedGroupName()); + names.emplace_back(PathGroups::asyncPathGroupName()); + names.emplace_back(PathGroups::pathDelayGroupName()); + names.emplace_back(PathGroups::gatedClkGroupName()); + names.emplace_back(PathGroups::unconstrainedGroupName()); return names; } @@ -2108,7 +2119,7 @@ Sta::makeExceptionFrom(PinSet *from_pins, void Sta::checkExceptionFromPins(ExceptionFrom *from, - const char *file, + std::string_view filename, int line, const Sdc *sdc) const { @@ -2118,7 +2129,7 @@ Sta::checkExceptionFromPins(ExceptionFrom *from, for (const Pin *pin : *pins) { if (!sdc->isExceptionStartpoint(pin)) { if (line) - report_->fileWarn(1554, file, line, "'{}' is not a valid start point.", + report_->fileWarn(1554, filename, line, "'{}' is not a valid start point.", cmd_network_->pathName(pin)); else report_->warn(1550, "'{}' is not a valid start point.", @@ -2193,7 +2204,7 @@ Sta::checkExceptionToPins(ExceptionTo *to, void Sta::writeSdc(const Sdc *sdc, - const char *filename, + std::string_view filename, bool leaf, bool native, int digits, @@ -2593,7 +2604,7 @@ SceneSeq Sta::findScenes(const std::string &name) const { SceneSeq matches; - PatternMatch pattern(name.c_str()); + PatternMatch pattern(name); for (Scene *scene : scenes_) { if (pattern.match(scene->name())) matches.push_back(scene); @@ -2606,7 +2617,7 @@ Sta::findScenes(const std::string &name, ModeSeq &modes) const { SceneSeq matches; - PatternMatch pattern(name.c_str()); + PatternMatch pattern(name); for (Mode *mode : modes) { for (Scene *scene : mode->scenes()) { if (pattern.match(scene->name())) @@ -2626,9 +2637,9 @@ Sta::updateSceneLiberty(Scene *scene, 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()); + LibertyLibrary *lib = network_->findLiberty(lib_file); if (lib == nullptr) - lib = network_->findLibertyFilename(lib_file.c_str()); + lib = network_->findLibertyFilename(lib_file); if (lib) LibertyLibrary::makeSceneMap(lib, scene->libertyIndex(min_max), network_, report_); @@ -2766,7 +2777,7 @@ Sta::setReportPathFields(bool report_input_pin, } ReportField * -Sta::findReportPathField(const char *name) +Sta::findReportPathField(std::string_view name) { return report_path_->findField(name); } @@ -3185,7 +3196,7 @@ Sta::slacks(Vertex *vertex, class EndpointPathEndVisitor : public PathEndVisitor { public: - EndpointPathEndVisitor(const std::string &path_group_name, + EndpointPathEndVisitor(std::string_view path_group_name, const MinMax *min_max, const StaState *sta); PathEndVisitor *copy() const; @@ -3193,13 +3204,13 @@ class EndpointPathEndVisitor : public PathEndVisitor Slack slack() const { return slack_; } private: - const std::string &path_group_name_; + std::string_view path_group_name_; const MinMax *min_max_; Slack slack_; const StaState *sta_; }; -EndpointPathEndVisitor::EndpointPathEndVisitor(const std::string &path_group_name, +EndpointPathEndVisitor::EndpointPathEndVisitor(std::string_view path_group_name, const MinMax *min_max, const StaState *sta) : path_group_name_(path_group_name), @@ -3232,7 +3243,7 @@ EndpointPathEndVisitor::visit(PathEnd *path_end) Slack Sta::endpointSlack(const Pin *pin, - const std::string &path_group_name, + std::string_view path_group_name, const MinMax *min_max) { ensureGraph(); @@ -3348,13 +3359,13 @@ Sta::reportDelaysWrtClks(Vertex *vertex, clk_name = sta::format("({})", clk_edge->name()); report_->report("{} r {}:{} f {}:{}", clk_name, formatDelay(RiseFall::rise(), MinMax::min(), - delays, report_variance, digits).c_str(), + delays, report_variance, digits), formatDelay(RiseFall::rise(), MinMax::max(), - delays, report_variance, digits).c_str(), + delays, report_variance, digits), formatDelay(RiseFall::fall(), MinMax::min(), - delays, report_variance, digits).c_str(), + delays, report_variance, digits), formatDelay(RiseFall::fall(), MinMax::max(), - delays, report_variance, digits).c_str()); + delays, report_variance, digits)); } } @@ -3557,10 +3568,10 @@ Sta::reportDelayCalc(Edge *edge, } void -Sta::setArcDelayCalc(const char *delay_calc_name) +Sta::setArcDelayCalc(std::string_view delay_calc_name) { delete arc_delay_calc_; - arc_delay_calc_ = makeDelayCalc(delay_calc_name, sta_); + arc_delay_calc_ = makeDelayCalc(std::string(delay_calc_name), sta_); // Update pointers to arc_delay_calc. updateComponentsState(); delaysInvalid(); @@ -3847,7 +3858,7 @@ Sta::setAnnotatedSlew(Vertex *vertex, } void -Sta::writeSdf(const char *filename, +Sta::writeSdf(std::string_view filename, const Scene *scene, char divider, bool include_typ, @@ -4087,8 +4098,8 @@ Sta::setResistance(const Net *net, //////////////////////////////////////////////////////////////// bool -Sta::readSpef(const std::string &name, - const std::string &filename, +Sta::readSpef(std::string_view name, + std::string_view filename, Instance *instance, Scene *scene, // -scene deprecated 11/20/2025 const MinMaxAll *min_max, @@ -4109,7 +4120,7 @@ Sta::readSpef(const std::string &name, spef_name += "_"; spef_name += min_max->to_string(); } - parasitics = makeConcreteParasitics(spef_name, filename); + parasitics = makeConcreteParasitics(spef_name, std::string(filename)); } else parasitics = findParasitics(spef_name); @@ -4122,14 +4133,14 @@ Sta::readSpef(const std::string &name, } } else { - parasitics = findParasitics(name); + parasitics = findParasitics(std::string(name)); if (parasitics == nullptr) - parasitics = makeConcreteParasitics(name, filename); + parasitics = makeConcreteParasitics(std::string(name), std::string(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, instance, pin_cap_included, + keep_coupling_caps, coupling_cap_factor, reduce, + scene, min_max, parasitics, this); delaysInvalid(); return success; } @@ -5875,16 +5886,16 @@ Sta::equivCells(LibertyCell *cell) //////////////////////////////////////////////////////////////// void -Sta::writeTimingModel(const char *lib_name, - const char *cell_name, - const char *filename, +Sta::writeTimingModel(std::string_view lib_name, + std::string_view cell_name, + std::string_view filename, const Scene *scene) { ensureLibLinked(); ensureGraph(); - LibertyLibrary *library = - makeTimingModel(lib_name, cell_name, filename, scene, this); - writeLiberty(library, filename, this); + LibertyLibrary *library = makeTimingModel(lib_name, cell_name, + filename, scene, this); + writeLiberty(library, std::string(filename).c_str(), this); } //////////////////////////////////////////////////////////////// @@ -5981,12 +5992,12 @@ Sta::activity(const Pin *pin, void Sta::writePathSpice(const Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim) { ensureLibLinked(); diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index e5c019bd3..9b3ecb005 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -26,6 +26,7 @@ #include #include +#include #include "Debug.hh" #include "Error.hh" @@ -59,12 +60,12 @@ class WritePathSpice : public WriteSpice { public: WritePathSpice(const Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, const StaState *sta); void writeSpice(); @@ -124,9 +125,9 @@ class WritePathSpice : public WriteSpice const LibertyPort *stageGateInputPort(Stage stage); const LibertyPort *stageDrvrPort(Stage stage); const Pin *stageLoadPin(Stage stage); - const char *stageGateInputPinName(Stage stage); - const char *stageDrvrPinName(Stage stage); - const char *stageLoadPinName(Stage stage); + std::string stageGateInputPinName(Stage stage); + std::string stageDrvrPinName(Stage stage); + std::string stageLoadPinName(Stage stage); const LibertyCell *stageLibertyCell(Stage stage); const Instance *stageInstance(Stage stage); @@ -154,12 +155,12 @@ class WritePathSpice : public WriteSpice void writePathSpice(const Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, StaState *sta) { @@ -170,12 +171,12 @@ writePathSpice(const Path *path, } WritePathSpice::WritePathSpice(const Path *path, - const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, const StaState *sta) : WriteSpice(spice_filename, subckt_filename, lib_subckt_filename, @@ -192,7 +193,7 @@ WritePathSpice::WritePathSpice(const Path *path, void WritePathSpice::writeSpice() { - spice_stream_.open(spice_filename_); + spice_stream_.open(std::string(spice_filename_)); if (spice_stream_.is_open()) { path_expanded_.expand(path_, true); // Find subckt port names as a side-effect of writeSubckts. @@ -288,20 +289,19 @@ WritePathSpice::writeStageInstances() for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { std::string stage_name = stageName(stage); - const char *stage_cname = stage_name.c_str(); if (stage == stageFirst()) sta::print(spice_stream_, "x{} {} {} {}\n", - stage_cname, + stage_name, stageDrvrPinName(stage), stageLoadPinName(stage), - stage_cname); + stage_name); else { sta::print(spice_stream_, "x{} {} {} {} {}\n", - stage_cname, + stage_name, stageGateInputPinName(stage), stageDrvrPinName(stage), stageLoadPinName(stage), - stage_cname); + stage_name); } } sta::print(spice_stream_, "\n"); @@ -474,8 +474,8 @@ WritePathSpice::writeInputStage(Stage stage) { // Input arc. // External driver not handled. - const char *drvr_pin_name = stageDrvrPinName(stage); - const char *load_pin_name = stageLoadPinName(stage); + std::string drvr_pin_name = stageDrvrPinName(stage); + std::string load_pin_name = stageLoadPinName(stage); std::string prefix = stageName(stage); sta::print(spice_stream_, ".subckt {} {} {}\n", prefix, @@ -490,11 +490,11 @@ void WritePathSpice::writeGateStage(Stage stage) { const Pin *input_pin = stageGateInputPin(stage); - const char *input_pin_name = stageGateInputPinName(stage); + std::string input_pin_name = stageGateInputPinName(stage); const Pin *drvr_pin = stageDrvrPin(stage); - const char *drvr_pin_name = stageDrvrPinName(stage); + std::string drvr_pin_name = stageDrvrPinName(stage); const Pin *load_pin = stageLoadPin(stage); - const char *load_pin_name = stageLoadPinName(stage); + std::string load_pin_name = stageLoadPinName(stage); std::string subckt_name = "stage" + std::to_string(stage); const Instance *inst = stageInstance(stage); @@ -721,21 +721,21 @@ WritePathSpice::stageLoadPin(Stage stage) return path->pin(this); } -const char * +std::string WritePathSpice::stageGateInputPinName(Stage stage) { const Pin *pin = stageGateInputPin(stage); return network_->pathName(pin); } -const char * +std::string WritePathSpice::stageDrvrPinName(Stage stage) { const Pin *pin = stageDrvrPin(stage); return network_->pathName(pin); } -const char * +std::string WritePathSpice::stageLoadPinName(Stage stage) { const Pin *pin = stageLoadPin(stage); diff --git a/spice/WritePathSpice.hh b/spice/WritePathSpice.hh index 027cc04dc..aa22e4fc7 100644 --- a/spice/WritePathSpice.hh +++ b/spice/WritePathSpice.hh @@ -24,6 +24,8 @@ #pragma once +#include + #include "CircuitSim.hh" namespace sta { @@ -36,15 +38,15 @@ class StaState; void writePathSpice(const Path *path, // Spice file written for path. - const char *spice_filename, + std::string_view spice_filename, // Subckts used by path included in spice file. - const char *subckt_filename, + std::string_view subckt_filename, // File of all cell spice subckt definitions. - const char *lib_subckt_filename, + std::string_view lib_subckt_filename, // Device model file included in spice file. - const char *model_filename, - const char *power_name, - const char *gnd_name, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, StaState *sta); diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 6a62785c0..1823080c4 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -28,6 +28,7 @@ #include #include #include +#include #include "cudd.h" @@ -56,41 +57,12 @@ Net * pinNet(const Pin *pin, const Network *network); -class SubcktEndsMissing : public Exception -{ -public: - SubcktEndsMissing(const char *cell_name, - const char *subckt_filename); - const char *what() const noexcept; - -protected: - std::string what_; -}; - -SubcktEndsMissing::SubcktEndsMissing(const char *cell_name, - const char *subckt_filename) : - Exception() -{ - what_ = "spice subckt for cell "; - what_ += cell_name; - what_ += " missing .ends in "; - what_ += subckt_filename; -} - -const char * -SubcktEndsMissing::what() const noexcept -{ - return what_.c_str(); -} - -//////////////////////////////////////////////////////////////// - -WriteSpice::WriteSpice(const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, +WriteSpice::WriteSpice(std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, const Scene *scene, const MinMax *min_max, @@ -165,11 +137,11 @@ WriteSpice::writePrintStmt(StringSeq &node_names) } std::string -WriteSpice::replaceFileExt(std::string filename, - const char *ext) +WriteSpice::replaceFileExt(std::string_view filename, + std::string_view ext) { size_t dot = filename.rfind('.'); - std::string ext_filename = filename.substr(0, dot + 1); + std::string ext_filename(filename.substr(0, dot + 1)); ext_filename += ext; return ext_filename; } @@ -201,29 +173,33 @@ void WriteSpice::writeSubckts(StringSet &cell_names) { findCellSubckts(cell_names); - std::ifstream lib_subckts_stream(lib_subckt_filename_); + std::ifstream lib_subckts_stream{std::string(lib_subckt_filename_)}; if (lib_subckts_stream.is_open()) { - std::ofstream subckts_stream(subckt_filename_); + std::ofstream subckts_stream{std::string(subckt_filename_)}; if (subckts_stream.is_open()) { std::string line; + int line_num = 0; while (std::getline(lib_subckts_stream, line)) { + line_num++; // .subckt [args..] - StringSeq tokens = parseTokens(line, ' '); - if (tokens.size() >= 2 && stringEqual(tokens[0].c_str(), ".subckt")) { - const char *cell_name = tokens[1].c_str(); + StringSeq tokens = parseTokens(line); + if (tokens.size() >= 2 && stringEqual(tokens[0], ".subckt")) { + const std::string &cell_name = tokens[1]; 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")) { + if (stringBeginEqual(line, ".ends")) { subckts_stream << "\n"; found_ends = true; break; } } if (!found_ends) - throw SubcktEndsMissing(cell_name, lib_subckt_filename_); + report_->fileError(1606, lib_subckt_filename_, line_num, + "spice subckt for cell {} missing .ends.", + cell_name); cell_names.erase(cell_name); } recordSpicePortNames(cell_name, tokens); @@ -252,19 +228,18 @@ WriteSpice::writeSubckts(StringSet &cell_names) } void -WriteSpice::recordSpicePortNames(const char *cell_name, +WriteSpice::recordSpicePortNames(std::string_view cell_name, StringSeq &tokens) { LibertyCell *cell = network_->findLibertyCell(cell_name); if (cell) { - StringSeq &spice_port_names = cell_spice_port_names_[cell_name]; + StringSeq &spice_port_names = cell_spice_port_names_[std::string(cell_name)]; for (size_t i = 2; i < tokens.size(); i++) { - const char *port_name = tokens[i].c_str(); + const std::string &port_name = tokens[i]; 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_)) + if (port == nullptr + && port_name != power_name_ + && port_name != gnd_name_) report_->error(1606, "subckt {} port {} has no corresponding liberty port, " "pg_port and is not power or ground.", @@ -278,14 +253,14 @@ WriteSpice::recordSpicePortNames(const char *cell_name, void WriteSpice::findCellSubckts(StringSet &cell_names) { - std::ifstream lib_subckts_stream(lib_subckt_filename_); + std::ifstream lib_subckts_stream{std::string(lib_subckt_filename_)}; if (lib_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(); + StringSeq tokens = parseTokens(line); + if (tokens.size() >= 2 && stringEqual(tokens[0], ".subckt")) { + const std::string &cell_name = tokens[1]; if (cell_names.contains(cell_name)) { // Scan the subckt definition for subckt calls. std::string stmt; @@ -295,13 +270,13 @@ WriteSpice::findCellSubckts(StringSet &cell_names) else { // Process previous statement. if (tolower(stmt[0]) == 'x') { - StringSeq tokens = parseTokens(line, ' '); + StringSeq tokens = parseTokens(line); std::string &subckt_cell = tokens[tokens.size() - 1]; cell_names.insert(subckt_cell); } stmt = line; } - if (stringBeginEqual(line.c_str(), ".ends")) + if (stringBeginEqual(line, ".ends")) break; } } @@ -317,25 +292,21 @@ WriteSpice::findCellSubckts(StringSet &cell_names) void WriteSpice::writeSubcktInst(const Instance *inst) { - const char *inst_name = network_->pathName(inst); + std::string inst_name = network_->pathName(inst); LibertyCell *cell = network_->libertyCell(inst); - const char *cell_name = cell->name(); + const std::string &cell_name = cell->name(); StringSeq &spice_port_names = cell_spice_port_names_[cell_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); - LibertyPort *pg_port = cell->findLibertyPort(subckt_port_cname); - const char *pin_name; - if (pin) { - pin_name = network_->pathName(pin); - sta::print(spice_stream_, " {}", pin_name); - } + for (std::string &subckt_port_name : spice_port_names) { + Pin *pin = network_->findPin(inst, subckt_port_name); + LibertyPort *pg_port = cell->findLibertyPort(subckt_port_name); + if (pin) + sta::print(spice_stream_, " {}", network_->pathName(pin)); else if (pg_port) - sta::print(spice_stream_, " {}/{}", inst_name, subckt_port_cname); - else if (stringEq(subckt_port_cname, power_name_) - || stringEq(subckt_port_cname, gnd_name_)) - sta::print(spice_stream_, " {}/{}", inst_name, subckt_port_cname); + sta::print(spice_stream_, " {}/{}", inst_name, subckt_port_name); + else if (subckt_port_name == power_name_ + || subckt_port_name == gnd_name_) + sta::print(spice_stream_, " {}/{}", inst_name, subckt_port_name); } sta::print(spice_stream_, " {}\n", cell_name); } @@ -347,23 +318,23 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, const PinSet &excluded_input_pins) { LibertyCell *cell = network_->libertyCell(inst); - const char *cell_name = cell->name(); + const std::string &cell_name = cell->name(); StringSeq &spice_port_names = cell_spice_port_names_[cell_name]; - const char *inst_name = network_->pathName(inst); + std::string inst_name = network_->pathName(inst); 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(); + for (std::string &subckt_port_name : spice_port_names) { 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 {}{}", 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)); - else if (stringEq(subckt_port_name, power_name_)) + else if (subckt_port_name == power_name_) writeVoltageSource(inst_name, subckt_port_name, power_voltage_); - else if (stringEq(subckt_port_name, gnd_name_)) + else if (subckt_port_name == gnd_name_) writeVoltageSource(inst_name, subckt_port_name, gnd_voltage_); else if (port && !excluded_input_pins.contains(pin) && port->direction()->isAnyInput()) { @@ -380,12 +351,12 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, switch (port_value) { case LogicValue::zero: case LogicValue::unknown: - writeVoltageSource(cell, inst_name, subckt_port_name, - port->relatedGroundPin(), gnd_voltage_); + writeVoltageSource(inst_name, subckt_port_name, + port->relatedGroundPort(), gnd_voltage_); break; case LogicValue::one: - writeVoltageSource(cell, inst_name, subckt_port_name, - port->relatedPowerPin(), power_voltage_); + writeVoltageSource(inst_name, subckt_port_name, + port->relatedPowerPort(), power_voltage_); break; case LogicValue::rise: case LogicValue::fall: @@ -396,24 +367,35 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, } void -WriteSpice::writeVoltageSource(const char *inst_name, - const char *port_name, +WriteSpice::writeVoltageSource(std::string_view inst_name, + std::string_view port_name, float voltage) { - std::string node_name = inst_name; + std::string node_name(inst_name); node_name += '/'; node_name += port_name; - writeVoltageSource(node_name.c_str(), voltage); + writeVoltageSource(node_name, voltage); +} + +void +WriteSpice::writeVoltageSource(std::string_view inst_name, + std::string_view subckt_port_name, + const LibertyPort *pg_port, + float voltage) +{ + if (pg_port) + voltage = pgPortVoltage(pg_port); + writeVoltageSource(inst_name, subckt_port_name, voltage); } void WriteSpice::writeVoltageSource(LibertyCell *cell, - const char *inst_name, - const char *subckt_port_name, - const char *pg_port_name, + std::string_view inst_name, + std::string_view subckt_port_name, + const std::string &pg_port_name, float voltage) { - if (pg_port_name) { + if (!pg_port_name.empty()) { LibertyPort *pg_port = cell->findLibertyPort(pg_port_name); if (pg_port) voltage = pgPortVoltage(pg_port); @@ -424,18 +406,18 @@ WriteSpice::writeVoltageSource(LibertyCell *cell, } float -WriteSpice::pgPortVoltage(LibertyPort *pg_port) +WriteSpice::pgPortVoltage(const LibertyPort *pg_port) { LibertyLibrary *liberty = pg_port->libertyCell()->libertyLibrary(); float voltage = 0.0; bool exists; - const char *voltage_name = pg_port->voltageName(); - if (voltage_name) { + const std::string &voltage_name = pg_port->voltageName(); + if (!voltage_name.empty()) { liberty->supplyVoltage(voltage_name, voltage, exists); if (!exists) { - if (stringEqual(voltage_name, power_name_)) + if (voltage_name == power_name_) voltage = power_voltage_; - else if (stringEqual(voltage_name, gnd_name_)) + else if (voltage_name == gnd_name_) voltage = gnd_voltage_; else report_->error(1601, "pg_pin {}/{} voltage {} not found,", @@ -501,8 +483,9 @@ 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); + std::string net_name = net + ? std::string(network_->pathName(net)) + : std::string(network_->pathName(drvr_pin)); sta::print(spice_stream_, "* Net {}\n", net_name); if (parasitics_->isParasiticNetwork(parasitic)) @@ -559,9 +542,9 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, // 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); + std::string name1 = parasitics_->name(node1); + std::string name2 = parasitics_->name(node2); + return name1 < name2; }); for (ParasiticNode *node : nodes) { @@ -677,7 +660,7 @@ WriteSpice::writeNullParasitic(const Pin *drvr_pin) //////////////////////////////////////////////////////////////// void -WriteSpice::writeVoltageSource(const char *node_name, +WriteSpice::writeVoltageSource(std::string_view node_name, float voltage) { sta::print(spice_stream_, "v{} {} 0 {:.3f}\n", volt_index_++, node_name, voltage); @@ -976,11 +959,11 @@ WriteSpice::writeMeasureDelayStmt(const Pin *from_pin, const RiseFall *from_rf, const Pin *to_pin, const RiseFall *to_rf, - std::string prefix) + std::string_view prefix) { - const char *from_pin_name = network_->pathName(from_pin); + std::string from_pin_name = network_->pathName(from_pin); float from_threshold = power_voltage_ * default_library_->inputThreshold(from_rf); - const char *to_pin_name = network_->pathName(to_pin); + std::string to_pin_name = network_->pathName(to_pin); float to_threshold = power_voltage_ * default_library_->inputThreshold(to_rf); sta::print(spice_stream_, ".measure tran {}_{}_delay_{}\n", prefix, from_pin_name, to_pin_name); @@ -993,10 +976,10 @@ WriteSpice::writeMeasureDelayStmt(const Pin *from_pin, void WriteSpice::writeMeasureSlewStmt(const Pin *pin, const RiseFall *rf, - std::string prefix) + std::string_view prefix) { - const char *pin_name = network_->pathName(pin); - const char *spice_rf = spiceTrans(rf); + std::string pin_name = network_->pathName(pin); + std::string_view spice_rf = spiceTrans(rf); float lower = power_voltage_ * default_library_->slewLowerThreshold(rf); float upper = power_voltage_ * default_library_->slewUpperThreshold(rf); float threshold1, threshold2; @@ -1017,7 +1000,7 @@ WriteSpice::writeMeasureSlewStmt(const Pin *pin, //////////////////////////////////////////////////////////////// -const char * +std::string_view WriteSpice::spiceTrans(const RiseFall *rf) { if (rf == RiseFall::rise()) diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index 74fecf000..2ac35b338 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -41,19 +42,19 @@ namespace sta { using ParasiticNodeMap = std::map; -using CellSpicePortNames = std::map; +using CellSpicePortNames = std::map>; using LibertyPortLogicValues = std::map; // Utilities for writing a spice deck. class WriteSpice : public StaState { public: - WriteSpice(const char *spice_filename, - const char *subckt_filename, - const char *lib_subckt_filename, - const char *model_filename, - const char *power_name, - const char *gnd_name, + WriteSpice(std::string_view spice_filename, + std::string_view subckt_filename, + std::string_view lib_subckt_filename, + std::string_view model_filename, + std::string_view power_name, + std::string_view gnd_name, CircuitSim ckt_sim, const Scene *scene, const MinMax *min_max, @@ -68,20 +69,24 @@ protected: void writeGnuplotFile(StringSeq &node_nanes); void writeSubckts(StringSet &cell_names); void findCellSubckts(StringSet &cell_names); - void recordSpicePortNames(const char *cell_name, + void recordSpicePortNames(std::string_view cell_name, StringSeq &tokens); void writeSubcktInst(const Instance *inst); void writeSubcktInstVoltSrcs(const Instance *inst, LibertyPortLogicValues &port_values, const PinSet &excluded_input_pins); - float pgPortVoltage(LibertyPort *pg_port); - void writeVoltageSource(const char *inst_name, - const char *port_name, + float pgPortVoltage(const LibertyPort *pg_port); + void writeVoltageSource(std::string_view inst_name, + std::string_view port_name, + float voltage); + void writeVoltageSource(std::string_view inst_name, + std::string_view subckt_port_name, + const LibertyPort *pg_port, float voltage); void writeVoltageSource(LibertyCell *cell, - const char *inst_name, - const char *subckt_port_name, - const char *pg_port_name, + std::string_view inst_name, + std::string_view subckt_port_name, + const std::string &pg_port_name, float voltage); void writeClkedStepSource(const Pin *pin, const RiseFall *rf, @@ -96,7 +101,7 @@ protected: const Parasitic *parasitic); void writeNullParasitic(const Pin *drvr_pin); - void writeVoltageSource(const char *node_name, + void writeVoltageSource(std::string_view node_name, float voltage); void writeRampVoltSource(const Pin *pin, const RiseFall *rf, @@ -121,11 +126,11 @@ protected: const RiseFall *from_rf, const Pin *to_pin, const RiseFall *to_rf, - std::string prefix); + std::string_view prefix); void writeMeasureSlewStmt(const Pin *pin, const RiseFall *rf, - std::string prefix); - const char *spiceTrans(const RiseFall *rf); + std::string_view prefix); + std::string_view spiceTrans(const RiseFall *rf); float findSlew(Vertex *vertex, const RiseFall *rf, const TimingArc *next_arc); @@ -157,15 +162,15 @@ protected: InstanceSet &written_insts); PinSeq drvrLoads(const Pin *drvr_pin); void writeSubcktInstVoltSrcs(); - std::string replaceFileExt(std::string filename, - const char *ext); + std::string replaceFileExt(std::string_view filename, + std::string_view ext); - const char *spice_filename_; - const char *subckt_filename_; - const char *lib_subckt_filename_; - const char *model_filename_; - const char *power_name_; - const char *gnd_name_; + const std::string_view spice_filename_; + const std::string_view subckt_filename_; + const std::string_view lib_subckt_filename_; + const std::string_view model_filename_; + const std::string_view power_name_; + const std::string_view gnd_name_; CircuitSim ckt_sim_; const Scene *scene_; const MinMax *min_max_; diff --git a/spice/Xyce.cc b/spice/Xyce.cc index ff3e4b597..35bf668a6 100644 --- a/spice/Xyce.cc +++ b/spice/Xyce.cc @@ -31,6 +31,7 @@ #include #include "Error.hh" +#include "StringUtil.hh" namespace sta { @@ -61,7 +62,7 @@ readXyceCsv(const char *csv_filename, std::stringstream ss(line); size_t col = 0; while (std::getline(ss, field, ',')) { - float value = std::stof(field); + auto [value, valid] = stringFloat(field); values[col].push_back(value); col++; } diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index b01986a2e..5f2430e1a 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -271,48 +271,24 @@ using namespace sta; // //////////////////////////////////////////////////////////////// -// String that is deleted after crossing over to tcland. -%typemap(out) std::string { - std::string &str = $1; +// SWIG before 4.2.0 has no std::string_view typemaps; newer SWIG defines +// them and duplicates here conflict. +#if SWIG_VERSION < 0x040200 +%typemap(out) std::string_view { + std::string str($1); // String is volatile because it is deleted. Tcl_SetResult(interp, const_cast(str.c_str()), TCL_VOLATILE); } -// String that is deleted after crossing over to tcland. -%typemap(out) TmpString* { - string *str = $1; - if (str) { - // String is volatile because it is deleted. - Tcl_SetResult(interp, const_cast(str->c_str()), TCL_VOLATILE); - delete str; - } - else - Tcl_SetResult(interp, nullptr, TCL_STATIC); -} - -%typemap(in) StringSet* { - $1 = tclListSetStdString($input, interp); -} - -%typemap(in) StringSeq { - $1 = tclListSeqStdString($input, interp); -} - -%typemap(in) const StringSeq & (StringSeq seq) { - seq = tclListSeqStdString($input, interp); - $1 = &seq; -} - -%typemap(in) StringSeq* { - $1 = tclListSeqStdStringPtr($input, interp); -} - -%typemap(in) StringSet* { - $1 = tclListSetStdString($input, interp); +%typemap(in) std::string_view { + int length; + const char *str = Tcl_GetStringFromObj($input, &length); + $1 = std::string_view(str, length); } +#endif %typemap(in) StringSeq { - $1 = tclListSeqStdString($input, interp); + $1 = tclListStringSeq($input, interp); } %typemap(out) StringSeq { @@ -442,7 +418,7 @@ using namespace sta; %typemap(in) Transition* { int length; const char *arg = Tcl_GetStringFromObj($input, &length); - Transition *tr = Transition::find(arg); + Transition *tr = Transition::find(std::string_view(arg, length)); if (tr == nullptr) { tclArgError(interp, 2150, "Unknown transition '{}'.", arg); return TCL_ERROR; @@ -453,16 +429,14 @@ using namespace sta; %typemap(out) Transition* { Transition *tr = $1; - const char *str = ""; - if (tr) - str = tr->to_string().c_str(); - Tcl_SetResult(interp, const_cast(str), TCL_STATIC); + const std::string &name = tr->to_string(); + Tcl_SetResult(interp, const_cast(name.c_str()), TCL_STATIC); } %typemap(in) RiseFall* { int length; const char *arg = Tcl_GetStringFromObj($input, &length); - const RiseFall *rf = RiseFall::find(arg); + const RiseFall *rf = RiseFall::find(std::string_view(arg, length)); if (rf == nullptr) { tclArgError(interp, 2151, "Unknown rise/fall edge '{}'.", arg); return TCL_ERROR; @@ -473,16 +447,14 @@ using namespace sta; %typemap(out) RiseFall* { const RiseFall *rf = $1; - const char *str = ""; - if (rf) - str = rf->shortName(); - Tcl_SetResult(interp, const_cast(str), TCL_STATIC); + const std::string &name = rf->shortName(); + Tcl_SetResult(interp, const_cast(name.c_str()), TCL_STATIC); } %typemap(in) RiseFallBoth* { int length; const char *arg = Tcl_GetStringFromObj($input, &length); - const RiseFallBoth *rf = RiseFallBoth::find(arg); + const RiseFallBoth *rf = RiseFallBoth::find(std::string_view(arg, length)); if (rf == nullptr) { tclArgError(interp, 2152, "Unknown transition name '{}'.", arg); return TCL_ERROR; @@ -492,11 +464,9 @@ using namespace sta; } %typemap(out) RiseFallBoth* { - RiseFallBoth *tr = $1; - const char *str = ""; - if (tr) - str = tr->shortName(); - Tcl_SetResult(interp, const_cast(str), TCL_STATIC); + RiseFallBoth *rf = $1; + const std::string &name = tr->shortName(); + Tcl_SetResult(interp, const_cast(name.c_str()), TCL_STATIC); } %typemap(in) PortDirection* { @@ -530,19 +500,19 @@ using namespace sta; %typemap(in) LogicValue { int length; - const char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "0") || stringEq(arg, "zero")) + std::string arg = Tcl_GetStringFromObj($input, &length); + if (arg == "0" || stringEqual(arg, "zero")) $1 = LogicValue::zero; - else if (stringEq(arg, "1") || stringEq(arg, "one")) + else if (arg == "1" || stringEqual(arg, "one")) $1 = LogicValue::one; - else if (stringEq(arg, "X")) + else if (stringEqual(arg, "X")) $1 = LogicValue::unknown; - else if (stringEq(arg, "rise") || stringEq(arg, "rising")) + else if (stringEqual(arg, "rise") || stringEqual(arg, "rising")) $1 = LogicValue::rise; - else if (stringEq(arg, "fall") || stringEq(arg, "falling")) + else if (stringEqual(arg, "fall") || stringEqual(arg, "falling")) $1 = LogicValue::fall; else { - tclArgError(interp, 2155, "Unknown logic value '{}'.", arg); + tclArgError(interp, 2155, "Unknown logic value '{}'.", arg.c_str()); return TCL_ERROR; } } @@ -554,10 +524,10 @@ using namespace sta; $1 = AnalysisType::single; else if (stringEqual(arg, "bc_wc")) $1 = AnalysisType::bc_wc; - else if (stringEq(arg, "on_chip_variation")) + else if (stringEqual(arg, "on_chip_variation")) $1 = AnalysisType::ocv; else { - tclArgError(interp, 2156, "Unknown analysis type '{}'.", arg); + tclArgError(interp, 2156, "Unknown analysis type '{}'.", arg.c_str()); return TCL_ERROR; } } @@ -985,11 +955,11 @@ using namespace sta; %typemap(in) TimingDerateType { int length; char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "net_delay")) + if (stringEqual(arg, "net_delay")) $1 = TimingDerateType::net_delay; - else if (stringEq(arg, "cell_delay")) + else if (stringEqual(arg, "cell_delay")) $1 = TimingDerateType::cell_delay; - else if (stringEq(arg, "cell_check")) + else if (stringEqual(arg, "cell_check")) $1 = TimingDerateType::cell_check; else { tclArgError(interp, 2166, "{} not net_delay, cell_delay or cell_check.", arg); @@ -1000,9 +970,9 @@ using namespace sta; %typemap(in) TimingDerateCellType { int length; char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "cell_delay")) + if (stringEqual(arg, "cell_delay")) $1 = TimingDerateCellType::cell_delay; - else if (stringEq(arg, "cell_check")) + else if (stringEqual(arg, "cell_check")) $1 = TimingDerateCellType::cell_check; else { tclArgError(interp, 2167, "{} not cell_delay or cell_check.", arg); @@ -1012,51 +982,51 @@ using namespace sta; %typemap(in) PathClkOrData { int length; - char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "clk")) + std::string arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "clk")) $1 = PathClkOrData::clk; - else if (stringEq(arg, "data")) + else if (stringEqual(arg, "data")) $1 = PathClkOrData::data; else { - tclArgError(interp, 2168, "{} not clk or data.", arg); + tclArgError(interp, 2168, "{} not clk or data.", arg.c_str()); return TCL_ERROR; } } %typemap(in) ReportSortBy { int length; - char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "group")) + std::string arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "group")) $1 = sort_by_group; - else if (stringEq(arg, "slack")) + else if (stringEqual(arg, "slack")) $1 = sort_by_slack; else { - tclArgError(interp, 2169, "{} not group or slack.", arg); + tclArgError(interp, 2169, "{} not group or slack.", arg.c_str()); return TCL_ERROR; } } %typemap(in) ReportPathFormat { int length; - char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "full")) + std::string arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "full")) $1 = ReportPathFormat::full; - else if (stringEq(arg, "full_clock")) + else if (stringEqual(arg, "full_clock")) $1 = ReportPathFormat::full_clock; - else if (stringEq(arg, "full_clock_expanded")) + else if (stringEqual(arg, "full_clock_expanded")) $1 = ReportPathFormat::full_clock_expanded; - else if (stringEq(arg, "short")) + else if (stringEqual(arg, "short")) $1 = ReportPathFormat::shorter; - else if (stringEq(arg, "end")) + else if (stringEqual(arg, "end")) $1 = ReportPathFormat::endpoint; - else if (stringEq(arg, "summary")) + else if (stringEqual(arg, "summary")) $1 = ReportPathFormat::summary; - else if (stringEq(arg, "slack_only")) + else if (stringEqual(arg, "slack_only")) $1 = ReportPathFormat::slack_only; - else if (stringEq(arg, "json")) + else if (stringEqual(arg, "json")) $1 = ReportPathFormat::json; else { - tclArgError(interp, 2170, "unknown path type {}.", arg); + tclArgError(interp, 2170, "unknown path type {}.", arg.c_str()); return TCL_ERROR; } } @@ -1247,16 +1217,16 @@ using namespace sta; %typemap(in) Scene* { sta::Sta *sta = Sta::sta(); int length; - char *scene_name = Tcl_GetStringFromObj($input, &length); + std::string scene_name = Tcl_GetStringFromObj($input, &length); // parse_scene_or_all support depreated 11/21/2025 - if (stringEq(scene_name, "NULL")) + if (scene_name == "NULL") $1 = nullptr; else { Scene *scene = sta->findScene(scene_name); if (scene) $1 = scene; else { - tclArgError(interp, 2173, "scene {} not found.", scene_name); + tclArgError(interp, 2173, "scene {} not found.", scene_name.c_str()); return TCL_ERROR; } } @@ -1317,7 +1287,7 @@ using namespace sta; Tcl_SetResult(interp, const_cast(""), TCL_STATIC); break; case PropertyValue::Type::string: - Tcl_SetResult(interp, const_cast(value.stringValue()), TCL_VOLATILE); + Tcl_SetResult(interp, const_cast(value.stringValue().c_str()), TCL_VOLATILE); break; case PropertyValue::Type::float_: { const Unit *unit = value.unit(); @@ -1443,15 +1413,15 @@ using namespace sta; %typemap(in) CircuitSim { int length; - char *arg = Tcl_GetStringFromObj($input, &length); - if (stringEq(arg, "hspice")) + std::string arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "hspice")) $1 = CircuitSim::hspice; - else if (stringEq(arg, "ngspice")) + else if (stringEqual(arg, "ngspice")) $1 = CircuitSim::ngspice; - else if (stringEq(arg, "xyce")) + else if (stringEqual(arg, "xyce")) $1 = CircuitSim::xyce; else { - tclArgError(interp, 2171, "unknown circuit simulator {}.", arg); + tclArgError(interp, 2171, "unknown circuit simulator {}.", arg.c_str()); return TCL_ERROR; } } diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index 66dc529c6..48ccbf57a 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -31,8 +31,8 @@ namespace sta { StringSeq -tclListSeqStdString(Tcl_Obj *const source, - Tcl_Interp *interp) +tclListStringSeq(Tcl_Obj *const source, + Tcl_Interp *interp) { Tcl_Size argc; Tcl_Obj **argv; @@ -49,8 +49,8 @@ tclListSeqStdString(Tcl_Obj *const source, } StringSeq * -tclListSeqStdStringPtr(Tcl_Obj *const source, - Tcl_Interp *interp) +tclListStringSeqPtr(Tcl_Obj *const source, + Tcl_Interp *interp) { Tcl_Size argc; Tcl_Obj **argv; @@ -69,8 +69,8 @@ tclListSeqStdStringPtr(Tcl_Obj *const source, } StringSet * -tclListSetStdString(Tcl_Obj *const source, - Tcl_Interp *interp) +tclListStringSet(Tcl_Obj *const source, + Tcl_Interp *interp) { Tcl_Size argc; Tcl_Obj **argv; @@ -150,24 +150,24 @@ tclArcDcalcArg(ArcDcalcArg &gate, Tcl_Obj *list = Tcl_NewListObj(0, nullptr); Tcl_Obj *obj; - const char *inst_name = network->pathName(drvr); - obj = Tcl_NewStringObj(inst_name, strlen(inst_name)); + std::string inst_name = network->pathName(drvr); + obj = Tcl_NewStringObj(inst_name.data(), inst_name.size()); Tcl_ListObjAppendElement(interp, list, obj); - const char *from_name = arc->from()->name(); - obj = Tcl_NewStringObj(from_name, strlen(from_name)); + const std::string &from_name = arc->from()->name(); + obj = Tcl_NewStringObj(from_name.c_str(), from_name.size()); Tcl_ListObjAppendElement(interp, list, obj); - const char *from_edge = arc->fromEdge()->to_string().c_str(); - obj = Tcl_NewStringObj(from_edge, strlen(from_edge)); + const std::string from_edge(arc->fromEdge()->to_string()); + obj = Tcl_NewStringObj(from_edge.c_str(), from_edge.size()); Tcl_ListObjAppendElement(interp, list, obj); - const char *to_name = arc->to()->name(); - obj = Tcl_NewStringObj(to_name, strlen(to_name)); + const std::string to_name = arc->to()->name(); + obj = Tcl_NewStringObj(to_name.c_str(), to_name.size()); Tcl_ListObjAppendElement(interp, list, obj); - const char *to_edge = arc->toEdge()->to_string().c_str(); - obj = Tcl_NewStringObj(to_edge, strlen(to_edge)); + const std::string to_edge(arc->toEdge()->to_string()); + obj = Tcl_NewStringObj(to_edge.c_str(), to_edge.size()); Tcl_ListObjAppendElement(interp, list, obj); std::string input_delay = delayAsString(gate.inputDelay(), sta); diff --git a/tcl/Util.tcl b/tcl/Util.tcl index 3236cb93e..15443529b 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -267,7 +267,6 @@ 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 diff --git a/util/Debug.cc b/util/Debug.cc index 917e40fa5..016dc1b51 100644 --- a/util/Debug.cc +++ b/util/Debug.cc @@ -39,44 +39,44 @@ Debug::Debug(Report *report) : } bool -Debug::check(const char *what, +Debug::check(std::string_view what, int level) const { if (debug_on_) { - int dbg_level; - bool exists; - findKeyValue(debug_map_, what, dbg_level, exists); - if (exists) + auto itr = debug_map_.find(what); + if (itr != debug_map_.end()) { + int dbg_level = itr->second; return dbg_level >= level; + } } return false; } int -Debug::level(const char *what) +Debug::level(std::string_view what) { if (debug_on_) { - int dbg_level; - bool exists; - findKeyValue(debug_map_, what, dbg_level, exists); - if (exists) + auto itr = debug_map_.find(what); + if (itr != debug_map_.end()) { + int dbg_level = itr->second; return dbg_level; + } } return 0; } void -Debug::setLevel(const char *what, +Debug::setLevel(std::string_view what, int level) { - if (stringEq(what, "stats")) + if (what == "stats") stats_level_ = level; else if (level == 0) { - debug_map_.erase(what); + debug_map_.erase(std::string(what)); debug_on_ = !debug_map_.empty(); } else { - debug_map_[what] = level; + debug_map_[std::string(what)] = level; debug_on_ = true; } } diff --git a/util/Error.cc b/util/Error.cc index 88914f267..3278d0fe4 100644 --- a/util/Error.cc +++ b/util/Error.cc @@ -59,9 +59,8 @@ ExceptionLine::ExceptionLine(const std::string &filename, { } -FileNotReadable::FileNotReadable(std::string filename) : - filename_(std::move(filename)), - msg_(sta::format("cannot read file {}.", filename_)) +FileNotReadable::FileNotReadable(std::string_view filename) : + msg_(sta::format("cannot read file {}.", filename)) { } @@ -71,9 +70,8 @@ FileNotReadable::what() const noexcept return msg_.c_str(); } -FileNotWritable::FileNotWritable(std::string filename) : - filename_(std::move(filename)), - msg_(sta::format("cannot write file {}.", filename_)) +FileNotWritable::FileNotWritable(std::string_view filename) : + msg_(sta::format("cannot write file {}.", filename)) { } diff --git a/util/Hash.cc b/util/Hash.cc index 4f51bd57d..49b497987 100644 --- a/util/Hash.cc +++ b/util/Hash.cc @@ -24,17 +24,14 @@ #include "Hash.hh" -#include - namespace sta { size_t -hashString(const char *str) +hashString(std::string_view str) { size_t hash = hash_init_value; - size_t length = strlen(str); - for (size_t i = 0; i < length; i++) - hash = ((hash << 5) + hash) ^ str[i]; + for (char ch : str) + hash = ((hash << 5) + hash) ^ ch; return hash; } diff --git a/util/MinMax.cc b/util/MinMax.cc index d3720ac31..299d31576 100644 --- a/util/MinMax.cc +++ b/util/MinMax.cc @@ -88,11 +88,11 @@ MinMax::opposite() const const MinMax * MinMax::find(const char *min_max) { - if (stringEq(min_max, "min") - || stringEq(min_max, "early")) + if (stringEqual(min_max, "min") + || stringEqual(min_max, "early")) return &min_; - else if (stringEq(min_max, "max") - || stringEq(min_max, "late")) + else if (stringEqual(min_max, "max") + || stringEqual(min_max, "late")) return &max_; else return nullptr; @@ -168,15 +168,15 @@ MinMaxAll::matches(const MinMaxAll *min_max) const const MinMaxAll * MinMaxAll::find(const char *min_max) { - if (stringEq(min_max, "min") - || stringEq(min_max, "early")) + if (stringEqual(min_max, "min") + || stringEqual(min_max, "early")) return &min_; - else if (stringEq(min_max, "max") - || stringEq(min_max, "late")) + else if (stringEqual(min_max, "max") + || stringEqual(min_max, "late")) return &max_; - else if (stringEq(min_max, "all") - || stringEq(min_max, "min_max") - || stringEq(min_max, "minmax")) + else if (stringEqual(min_max, "all") + || stringEqual(min_max, "min_max") + || stringEqual(min_max, "minmax")) return &all_; else return nullptr; diff --git a/util/PatternMatch.cc b/util/PatternMatch.cc index a8397e375..c4f15237b 100644 --- a/util/PatternMatch.cc +++ b/util/PatternMatch.cc @@ -23,12 +23,12 @@ // This notice may not be removed or altered from any source distribution. #include "PatternMatch.hh" -#include +#include #include namespace sta { -PatternMatch::PatternMatch(const char *pattern, +PatternMatch::PatternMatch(std::string_view pattern, bool is_regexp, bool nocase, Tcl_Interp *interp) : @@ -42,7 +42,7 @@ PatternMatch::PatternMatch(const char *pattern, compileRegexp(); } -PatternMatch::PatternMatch(const char *pattern) : +PatternMatch::PatternMatch(std::string_view pattern) : pattern_(pattern), is_regexp_(false), nocase_(false), @@ -51,7 +51,7 @@ PatternMatch::PatternMatch(const char *pattern) : { } -PatternMatch::PatternMatch(const char *pattern, +PatternMatch::PatternMatch(std::string_view pattern, const PatternMatch *inherit_from) : pattern_(pattern), is_regexp_(inherit_from->is_regexp_), @@ -63,18 +63,6 @@ PatternMatch::PatternMatch(const char *pattern, compileRegexp(); } -PatternMatch::PatternMatch(const std::string &pattern, - const PatternMatch *inherit_from) : - pattern_(pattern.c_str()), - is_regexp_(inherit_from->is_regexp_), - nocase_(inherit_from->nocase_), - interp_(inherit_from->interp_), - regexp_(nullptr) -{ - if (is_regexp_) - compileRegexp(); -} - void PatternMatch::compileRegexp() { @@ -95,9 +83,9 @@ PatternMatch::compileRegexp() } static bool -regexpWildcards(const char *pattern) +regexpWildcards(std::string_view pattern) { - return strpbrk(pattern, ".+*?[]") != nullptr; + return pattern.find_first_of(".+*?[]") != std::string_view::npos; } bool @@ -110,36 +98,34 @@ PatternMatch::hasWildcards() const } bool -PatternMatch::match(const std::string &str) const -{ - return match(str.c_str()); -} - -bool -PatternMatch::match(const char *str) const +PatternMatch::match(std::string_view str) const { - if (regexp_) - return Tcl_RegExpExec(nullptr, regexp_, str, str) == 1; - else - return patternMatch(pattern_, str); + if (regexp_) { + std::string buf(str); + const char *cstr = buf.c_str(); + return Tcl_RegExpExec(nullptr, regexp_, cstr, cstr) == 1; + } + return patternMatch(pattern_, str); } bool -PatternMatch::matchNoCase(const char *str) const +PatternMatch::matchNoCase(std::string_view str) const { - if (regexp_) - return Tcl_RegExpExec(0, regexp_, str, str) == 1; - else - return patternMatchNoCase(pattern_, str, nocase_); + if (regexp_) { + std::string buf(str); + const char *cstr = buf.c_str(); + return Tcl_RegExpExec(0, regexp_, cstr, cstr) == 1; + } + return patternMatchNoCase(pattern_, str, nocase_); } //////////////////////////////////////////////////////////////// -RegexpCompileError::RegexpCompileError(const char *pattern) : +RegexpCompileError::RegexpCompileError(std::string_view pattern) : Exception() { error_ = "TCL failed to compile regular expression '"; - error_ += pattern; + error_.append(pattern.data(), pattern.size()); error_ += "'."; } @@ -152,70 +138,71 @@ RegexpCompileError::what() const noexcept //////////////////////////////////////////////////////////////// bool -patternMatch(const char *pattern, - const char *str) -{ - const char *p = pattern; - const char *s = str; - - while (*p && *s && (*s == *p || *p == '?')) { - p++; - s++; +patternMatch(std::string_view pattern, + std::string_view str) +{ + size_t pi = 0; + size_t si = 0; + while (pi < pattern.size() && si < str.size() + && (str[si] == pattern[pi] || pattern[pi] == '?')) { + pi++; + si++; } - if (*p == '\0' && *s == '\0') + if (pi == pattern.size() && si == str.size()) return true; - else if (*p == '*') { - if (p[1] == '\0') + if (pi < pattern.size() && pattern[pi] == '*') { + if (pi + 1 == pattern.size()) return true; - while (*s) { - if (patternMatch(p + 1, s)) + while (si < str.size()) { + if (patternMatch(pattern.substr(pi + 1), str.substr(si))) return true; - s++; + si++; } } return false; } -inline -bool equalCase(char s, - char p, - bool nocase) +static bool +equalCase(char s, + char p, + bool nocase) { return nocase - ? tolower(s) == tolower(p) + ? std::tolower(static_cast(s)) + == std::tolower(static_cast(p)) : s == p; } bool -patternMatchNoCase(const char *pattern, - const char *str, +patternMatchNoCase(std::string_view pattern, + std::string_view str, bool nocase) { - const char *p = pattern; - const char *s = str; - - while (*p && *s && (equalCase(*s, *p, nocase) || *p == '?')) { - p++; - s++; + size_t pi = 0; + size_t si = 0; + while (pi < pattern.size() && si < str.size() + && (equalCase(str[si], pattern[pi], nocase) || pattern[pi] == '?')) { + pi++; + si++; } - if (*p == '\0' && *s == '\0') + if (pi == pattern.size() && si == str.size()) return true; - else if (*p == '*') { - if (p[1] == '\0') + if (pi < pattern.size() && pattern[pi] == '*') { + if (pi + 1 == pattern.size()) return true; - while (*s) { - if (patternMatchNoCase(p + 1, s, nocase)) + while (si < str.size()) { + if (patternMatchNoCase(pattern.substr(pi + 1), str.substr(si), nocase)) return true; - s++; + si++; } } return false; } bool -patternWildcards(const char *pattern) +patternWildcards(std::string_view pattern) { - return strpbrk(pattern, "*?") != 0; + return pattern.find_first_of("*?") != std::string_view::npos; } } // namespace diff --git a/util/Report.cc b/util/Report.cc index d8a6a04d5..ca6bc0771 100644 --- a/util/Report.cc +++ b/util/Report.cc @@ -185,11 +185,11 @@ Report::isSuppressed(int id) //////////////////////////////////////////////////////////////// void -Report::logBegin(std::string filename) +Report::logBegin(std::string_view filename) { - log_stream_ = fopen(filename.c_str(), "w"); + log_stream_ = fopen(std::string(filename).c_str(), "w"); if (log_stream_ == nullptr) - throw FileNotWritable(std::move(filename)); + throw FileNotWritable(filename); } void @@ -201,19 +201,19 @@ Report::logEnd() } void -Report::redirectFileBegin(std::string filename) +Report::redirectFileBegin(std::string_view filename) { - redirect_stream_ = fopen(filename.c_str(), "w"); + redirect_stream_ = fopen(std::string(filename).c_str(), "w"); if (redirect_stream_ == nullptr) - throw FileNotWritable(std::move(filename)); + throw FileNotWritable(filename); } void -Report::redirectFileAppendBegin(std::string filename) +Report::redirectFileAppendBegin(std::string_view filename) { - redirect_stream_ = fopen(filename.c_str(), "a"); + redirect_stream_ = fopen(std::string(filename).c_str(), "a"); if (redirect_stream_ == nullptr) - throw FileNotWritable(std::move(filename)); + throw FileNotWritable(filename); } void diff --git a/util/ReportTcl.cc b/util/ReportTcl.cc index 97eef4aff..a418a53a8 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 filename) +ReportTcl::logBegin(std::string_view filename) { flush(); Report::logBegin(filename); @@ -197,14 +197,14 @@ ReportTcl::logEnd() } void -ReportTcl::redirectFileBegin(std::string filename) +ReportTcl::redirectFileBegin(std::string_view filename) { flush(); Report::redirectFileBegin(filename); } void -ReportTcl::redirectFileAppendBegin(std::string filename) +ReportTcl::redirectFileAppendBegin(std::string_view filename) { flush(); Report::redirectFileAppendBegin(filename); diff --git a/util/StringUtil.cc b/util/StringUtil.cc index cc77196c1..00a36ec47 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -25,29 +25,12 @@ #include "StringUtil.hh" #include -#include #include -#include -#include // exit - -#include "Machine.hh" -#include "Mutex.hh" -#include "Error.hh" +#include +#include namespace sta { -char * -stringCopy(const char *str) -{ - if (str) { - char *copy = new char[strlen(str) + 1]; - strcpy(copy, str); - return copy; - } - else - return nullptr; -} - bool isDigits(const char *str) { @@ -58,73 +41,29 @@ isDigits(const char *str) return true; } -//////////////////////////////////////////////////////////////// - -static constexpr size_t tmp_string_count = 256; -static constexpr size_t tmp_string_initial_length = 256; -thread_local static std::array tmp_strings; -thread_local static std::array tmp_string_lengths; -thread_local static int tmp_string_next = 0; - -void -deleteTmpStrings() -{ - for (size_t i = 0; i < tmp_string_count; i++) { - stringDelete(tmp_strings[i]); - tmp_string_lengths[i] = 0; - tmp_strings[i] = nullptr; - } - tmp_string_next = 0; -} - -char * -makeTmpString(size_t length) +bool +isDigits(std::string_view str) { - if (tmp_string_next == tmp_string_count) - tmp_string_next = 0; - char *tmp_str = tmp_strings[tmp_string_next]; - size_t tmp_length = tmp_string_lengths[tmp_string_next]; - if (tmp_length < length) { - // String isn't long enough. Make a new one. - delete [] tmp_str; - tmp_length = std::max(tmp_string_initial_length, length); - tmp_str = new char[tmp_length]; - tmp_strings[tmp_string_next] = tmp_str; - tmp_string_lengths[tmp_string_next] = tmp_length; + for (char ch : str) { + if (!isdigit(ch)) + return false; } - tmp_string_next++; - return tmp_str; -} - -char * -makeTmpString(std::string &str) -{ - char *tmp = makeTmpString(str.length() + 1); - strcpy(tmp, str.c_str()); - return tmp; -} - -void -stringDeleteCheck(const char *str) -{ - if (isTmpString(str)) - criticalError(2600, "stringDelete for tmp string."); + return true; } -bool -isTmpString(const char *str) +// Wrapper for the absurdly named std::from_chars. +std::pair +stringFloat(const std::string &str) { - if (!tmp_strings.empty()) { - for (size_t i = 0; i < tmp_string_count; i++) { - if (str == tmp_strings[i]) - return true; - } - } - return false; + float value; + auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value); + if (ec == std::errc() + && *ptr == '\0') + return {value, true}; + else + return {0.0, false}; } -//////////////////////////////////////////////////////////////// - void trimRight(std::string &str) { @@ -132,8 +71,8 @@ trimRight(std::string &str) } StringSeq -split(const std::string &text, - const std::string &delims) +parseTokens(const std::string &text, + std::string_view delims) { StringSeq tokens; auto start = text.find_first_not_of(delims); @@ -148,25 +87,4 @@ split(const std::string &text, return tokens; } -// Parse space separated tokens. -StringSeq -parseTokens(const std::string &s, - const char delimiter) -{ - StringSeq tokens; - size_t i = 0; - while (i < s.size()) { - while (i < s.size() && std::isspace(s[i])) - ++i; - size_t start = i; - while (i < s.size() && s[i] != delimiter) - ++i; - if (start < i) { - tokens.emplace_back(s, start, i - start); - ++i; - } - } - return tokens; -} - } // namespace diff --git a/util/Transition.cc b/util/Transition.cc index 9dd7fdc7a..4318366dc 100644 --- a/util/Transition.cc +++ b/util/Transition.cc @@ -35,8 +35,8 @@ const RiseFall RiseFall::fall_("fall", "v", 1); const std::array RiseFall::range_{&rise_, &fall_}; const std::array RiseFall::range_index_{rise_.index(), fall_.index()}; -RiseFall::RiseFall(const char *name, - const char *short_name, +RiseFall::RiseFall(std::string_view name, + std::string_view short_name, int sdf_triple_index) : name_(name), short_name_(short_name), @@ -44,7 +44,7 @@ RiseFall::RiseFall(const char *name, { } -const std::string & +const std::string& RiseFall::to_string(bool use_short) const { if (use_short) @@ -63,16 +63,13 @@ RiseFall::opposite() const } const RiseFall * -RiseFall::find(const char *rf_str) +RiseFall::find(std::string_view rf_str) { - if (stringEq(rf_str, rise_.name()) - || stringEq(rf_str, rise_.shortName())) + if (rf_str == rise_.name() || rf_str == rise_.shortName()) return &rise_; - else if (stringEq(rf_str, fall_.name()) - || stringEq(rf_str, fall_.shortName())) + if (rf_str == fall_.name() || rf_str == fall_.shortName()) return &fall_; - else - return nullptr; + return nullptr; } const RiseFall * @@ -128,8 +125,8 @@ const RiseFallBoth RiseFallBoth::rise_fall_("rise_fall", "rf", 2, {RiseFall::riseIndex(), RiseFall::fallIndex()}); -RiseFallBoth::RiseFallBoth(const char *name, - const char *short_name, +RiseFallBoth::RiseFallBoth(std::string_view name, + std::string_view short_name, int sdf_triple_index, const RiseFall *as_rise_fall, std::vector range, @@ -143,7 +140,7 @@ RiseFallBoth::RiseFallBoth(const char *name, { } -const std::string & +const std::string& RiseFallBoth::to_string(bool use_short) const { if (use_short) @@ -153,16 +150,15 @@ RiseFallBoth::to_string(bool use_short) const } const RiseFallBoth * -RiseFallBoth::find(const char *tr_str) +RiseFallBoth::find(std::string_view name) { - if (stringEq(tr_str, rise_.name())) + if (name == rise_.name()) return &rise_; - else if (stringEq(tr_str, fall_.name())) + if (name == fall_.name()) return &fall_; - else if (stringEq(tr_str, rise_fall_.name())) + if (name == rise_fall_.name()) return &rise_fall_; - else - return nullptr; + return nullptr; } bool @@ -202,8 +198,8 @@ const Transition Transition::tr_XZ_{"XZ", "XZ", nullptr, 10}; const Transition Transition::tr_ZX_{"ZX", "ZX", nullptr, 11}; const Transition Transition::rise_fall_{"*", "**", nullptr, -1}; -Transition::Transition(const char *name, - const char *init_final, +Transition::Transition(std::string_view name, + std::string_view init_final, const RiseFall *as_rise_fall, int sdf_triple_index) : name_(name), @@ -223,9 +219,9 @@ Transition::matches(const Transition *tr) const } const Transition * -Transition::find(const char *tr_str) +Transition::find(std::string_view tr_str) { - return findKey(transition_map_, tr_str); + return findStringKey(transition_map_, tr_str); } const RiseFallBoth * diff --git a/util/Util.i b/util/Util.i index e68de5ef9..c34ba4a8c 100644 --- a/util/Util.i +++ b/util/Util.i @@ -34,6 +34,7 @@ #include "Error.hh" #include "Fuzzy.hh" #include "Units.hh" +#include "StringUtil.hh" using namespace sta; @@ -472,10 +473,10 @@ unit_scale(const char *unit_name) // format_unit functions print with fixed digits and suffix. // Pass value arg as string to support NaNs. std::string -format_time(const char *value, +format_time(std::string value, int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->timeUnit()->asString(value1, digits); } @@ -483,7 +484,7 @@ std::string format_capacitance(const char *value, int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->capacitanceUnit()->asString(value1, digits); } @@ -491,7 +492,7 @@ std::string format_resistance(const char *value, int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->resistanceUnit()->asString(value1, digits); } @@ -499,7 +500,7 @@ std::string format_voltage(const char *value, int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->voltageUnit()->asString(value1, digits); } @@ -507,7 +508,7 @@ std::string format_current(const char *value, int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->currentUnit()->asString(value1, digits); } @@ -515,7 +516,7 @@ std::string format_power(const char *value, int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); return Sta::sta()->units()->powerUnit()->asString(value1, digits); } @@ -523,29 +524,28 @@ std::string format_distance(const char *value, int digits) { - float value1 = strtof(value, nullptr); - Unit *dist_unit = Sta::sta()->units()->distanceUnit(); - return dist_unit->asString(value1, digits); + auto [value1, valid] = stringFloat(value); + return Sta::sta()->units()->distanceUnit()->asString(value1, digits); } std::string format_area(const char *value, int digits) { - float value1 = strtof(value, nullptr); + auto [value1, valid] = stringFloat(value); Unit *dist_unit = Sta::sta()->units()->distanceUnit(); return dist_unit->asString(value1 / dist_unit->scale(), digits); } //////////////////////////////////////////////////////////////// -const char * +std::string_view rise_short_name() { return RiseFall::rise()->shortName(); } -const char * +std::string_view fall_short_name() { return RiseFall::fall()->shortName(); diff --git a/verilog/VerilogLex.ll b/verilog/VerilogLex.ll index e55801b6a..626ac905b 100644 --- a/verilog/VerilogLex.ll +++ b/verilog/VerilogLex.ll @@ -23,6 +23,8 @@ // // This notice may not be removed or altered from any source distribution. +#include + #include "util/FlexDisableRegister.hh" #include "VerilogNamespace.hh" #include "VerilogReader.hh" @@ -93,27 +95,27 @@ ID_TOKEN {ID_ESCAPED_TOKEN}|{ID_ALPHA_TOKEN} } {SIGN}?{UNSIGNED_NUMBER}?"'"[sS]?[bB][01_xz]+ { - yylval->constant = new std::string(yytext); + yylval->emplace(yytext); return token::CONSTANT; } {SIGN}?{UNSIGNED_NUMBER}?"'"[sS]?[oO][0-7_xz]+ { - yylval->constant = new std::string(yytext); + yylval->emplace(yytext); return token::CONSTANT; } {SIGN}?{UNSIGNED_NUMBER}?"'"[sS]?[dD][0-9_]+ { - yylval->constant = new std::string(yytext); + yylval->emplace(yytext); return token::CONSTANT; } {SIGN}?{UNSIGNED_NUMBER}?"'"[sS]?[hH][0-9a-fA-F_xz]+ { - yylval->constant = new std::string(yytext); + yylval->emplace(yytext); return token::CONSTANT; } {SIGN}?[0-9]+ { - yylval->ival = atol(yytext); + yylval->emplace(static_cast(atol(yytext))); return token::INT; } @@ -143,7 +145,7 @@ wire { return token::WIRE; } wor { return token::WOR; } {ID_TOKEN}("."{ID_TOKEN})* { - yylval->string = new std::string(yytext, yyleng); + yylval->emplace(yytext, yyleng); return token::ID; } @@ -155,18 +157,20 @@ wor { return token::WOR; } {BLANK} { /* ignore blanks */ } \" { - yylval->string = new std::string; + token_.clear(); BEGIN(QSTRING); } \" { BEGIN(INITIAL); + yylval->emplace(std::move(token_)); return token::STRING; } {EOL} { error("unterminated quoted string"); BEGIN(INITIAL); + yylval->emplace(std::move(token_)); return token::STRING; } @@ -177,8 +181,8 @@ wor { return token::WOR; } } [^\r\n\"]+ { - /* Anything return token::or double quote */ - *yylval->string += yytext; + /* Anything but return or double quote */ + token_ += yytext; } <> { diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy index 5888f993a..9bd7f1621 100644 --- a/verilog/VerilogParse.yy +++ b/verilog/VerilogParse.yy @@ -25,6 +25,7 @@ %{ #include #include +#include #include "Report.hh" #include "PortDirection.hh" @@ -46,6 +47,7 @@ sta::VerilogParse::error(const location_type &loc, { reader->report()->fileError(171,reader->filename(),loc.begin.line, "{}",msg); } + %} %require "3.2" @@ -58,30 +60,11 @@ sta::VerilogParse::error(const location_type &loc, %parse-param { VerilogScanner *scanner } %parse-param { VerilogReader *reader } %define api.parser.class {VerilogParse} +%define api.value.type variant -%union { - int ival; - std::string *string; - std::string *constant; - std::string *attr_spec_value; - sta::VerilogModule *module; - sta::VerilogStmt *stmt; - sta::VerilogStmtSeq *stmt_seq; - sta::PortDirection *port_type; - sta::VerilogDclArgSeq *dcl_arg_seq; - sta::VerilogDclArg *dcl_arg; - sta::VerilogAssign *assign; - sta::VerilogInst *inst; - sta::VerilogNet *net; - sta::VerilogNetBitSelect *net_bit; - sta::VerilogNetSeq *nets; - sta::VerilogAttrEntry *attr_entry; - sta::VerilogAttrEntrySeq *attr_seq; - sta::VerilogAttrStmt *attr_stmt; - sta::VerilogAttrStmtSeq *attr_stmt_seq; -} - -%token INT CONSTANT ID STRING MODULE ENDMODULE ASSIGN PARAMETER DEFPARAM +%token INT +%token ID STRING CONSTANT +%token MODULE ENDMODULE ASSIGN PARAMETER DEFPARAM %token SPECIFY ENDSPECIFY SPECPARAM %token WIRE WAND WOR TRI INPUT OUTPUT INOUT SUPPLY1 SUPPLY0 REG %token ATTR_OPEN ATTR_CLOSED @@ -90,34 +73,26 @@ sta::VerilogParse::error(const location_type &loc, %left '*' '/' %left NEG /* negation--unary minus */ -%type ID STRING CONSTANT -%type WIRE WAND WOR TRI INPUT OUTPUT INOUT SUPPLY1 SUPPLY0 -%type ATTR_OPEN ATTR_CLOSED -%type INT parameter_exprs parameter_expr -%type attr_spec_value -%type dcl_type port_dcl_type -%type stmt declaration instance parameter parameter_dcls parameter_dcl -%type defparam param_values param_value port_dcl -%type stmts stmt_seq net_assignments continuous_assign port_dcls -%type specify_block -%type specify_stmts -%type net_assignment -%type dcl_arg -%type dcl_args -%type port net_scalar net_bit_select net_part_select net_assign_lhs -%type net_constant net_expr port_ref port_expr named_pin_net_expr -%type inst_named_pin net_named net_expr_concat -%type port_list port_refs inst_ordered_pins -%type inst_named_pins net_exprs inst_pins -%type attr_spec -%type attr_specs -%type attr_instance -%type attr_instance_seq - -// Used by error recovery. -%destructor { delete $$; } STRING -%destructor { delete $$; } CONSTANT -%destructor { delete $$; } attr_spec_value +%type attr_spec_value +%type parameter_exprs parameter_expr +%type dcl_type port_dcl_type +%type stmt declaration instance parameter parameter_dcls parameter_dcl +%type defparam param_values param_value port_dcl +%type stmts stmt_seq net_assignments continuous_assign port_dcls +%type specify_block +%type specify_stmts +%type net_assignment +%type dcl_arg +%type dcl_args +%type port net_scalar net_bit_select net_part_select net_assign_lhs +%type net_constant net_expr port_ref port_expr named_pin_net_expr +%type inst_named_pin net_named net_expr_concat +%type port_list port_refs inst_ordered_pins +%type inst_named_pins net_exprs inst_pins +%type attr_spec +%type attr_specs +%type attr_instance +%type attr_instance_seq %start file @@ -134,30 +109,30 @@ modules: module: attr_instance_seq MODULE ID ';' stmts ENDMODULE - { reader->makeModule($3, new sta::VerilogNetSeq,$5, $1, loc_line(@2));} + { reader->makeModule(std::move($3), new sta::VerilogNetSeq,$5, $1, loc_line(@2));} | attr_instance_seq MODULE ID '(' ')' ';' stmts ENDMODULE - { reader->makeModule($3, new sta::VerilogNetSeq,$7, $1, loc_line(@2));} + { reader->makeModule(std::move($3), new sta::VerilogNetSeq,$7, $1, loc_line(@2));} | attr_instance_seq MODULE ID '(' port_list ')' ';' stmts ENDMODULE - { reader->makeModule($3, $5, $8, $1, loc_line(@2)); } + { reader->makeModule(std::move($3), $5, $8, $1, loc_line(@2)); } | attr_instance_seq MODULE ID '(' port_dcls ')' ';' stmts ENDMODULE - { reader->makeModule($3, $5, $8, $1, loc_line(@2)); } + { reader->makeModule(std::move($3), $5, $8, $1, loc_line(@2)); } ; port_list: port { $$ = new sta::VerilogNetSeq; - $$->push_back($1); + $$->push_back($1); } | port_list ',' port - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; port: port_expr | '.' ID '(' ')' - { $$ = reader->makeNetNamedPortRefScalar($2, nullptr);} + { $$ = reader->makeNetNamedPortRefScalar(std::move($2), nullptr);} | '.' ID '(' port_expr ')' - { $$ = reader->makeNetNamedPortRefScalar($2, $4);} + { $$ = reader->makeNetNamedPortRefScalar(std::move($2), $4);} ; port_expr: @@ -171,7 +146,7 @@ port_refs: $$->push_back($1); } | port_refs ',' port_ref - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; port_ref: @@ -219,12 +194,13 @@ stmts: // empty { $$ = new sta::VerilogStmtSeq; } | stmts stmt - { if ($2) $1->push_back($2); } + { if ($2) $1->push_back($2); $$ = $1; } | stmts stmt_seq // Append stmt_seq to stmts. { for (sta::VerilogStmt *stmt : *$2) $1->push_back(stmt); delete $2; + $$ = $1; } ; @@ -278,18 +254,18 @@ parameter_dcls: parameter_dcl: ID '=' parameter_expr - { delete $1; $$ = nullptr; } + { $$ = nullptr; } | ID '=' STRING - { delete $1; delete $3; $$ = nullptr; } + { $$ = nullptr; } ; parameter_expr: ID - { delete $1; $$ = 0; } + { $$ = 0; } | '`' ID - { delete $2; $$ = 0; } + { $$ = 0; } | CONSTANT - { delete $1; $$ = 0; } + { $$ = 0; } | INT | '-' parameter_expr %prec NEG { $$ = - $2; } @@ -319,9 +295,9 @@ param_values: param_value: ID '=' parameter_expr - { delete $1; $$ = nullptr; } + { $$ = nullptr; } | ID '=' STRING - { delete $1; delete $3; $$ = nullptr; } + { $$ = nullptr; } ; declaration: @@ -356,9 +332,9 @@ dcl_args: dcl_arg: ID - { $$ = reader->makeDclArg($1); } + { $$ = reader->makeDclArg(std::move($1)); } | net_assignment - { $$ = reader->makeDclArg($1); } + { $$ = reader->makeDclArg(std::move($1)); } ; continuous_assign: @@ -372,7 +348,7 @@ net_assignments: $$->push_back($1); } | net_assignments ',' net_assignment - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; net_assignment: @@ -387,9 +363,9 @@ net_assign_lhs: instance: attr_instance_seq ID ID '(' inst_pins ')' ';' - { $$ = reader->makeModuleInst($2, $3, $5, $1, loc_line(@2)); } + { $$ = reader->makeModuleInst(std::move($2), std::move($3), $5, $1, loc_line(@2)); } | attr_instance_seq ID parameter_values ID '(' inst_pins ')' ';' - { $$ = reader->makeModuleInst($2, $4, $6, $1, loc_line(@2)); } + { $$ = reader->makeModuleInst(std::move($2), std::move($4), $6, $1, loc_line(@2)); } ; parameter_values: @@ -417,7 +393,7 @@ inst_ordered_pins: $$->push_back($1); } | inst_ordered_pins ',' net_expr - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; // Named pin connections. @@ -427,7 +403,7 @@ inst_named_pins: $$->push_back($1); } | inst_named_pins ',' inst_named_pin - { $1->push_back($3); } + { $1->push_back($3); $$ = $1; } ; // The port reference is split out into cases to special case @@ -435,23 +411,23 @@ inst_named_pins: inst_named_pin: // Scalar port. '.' ID '(' ')' - { $$ = reader->makeNetNamedPortRefScalarNet($2); } + { $$ = reader->makeNetNamedPortRefScalarNet(std::move($2)); } | '.' ID '(' ID ')' - { $$ = reader->makeNetNamedPortRefScalarNet($2, $4); } + { $$ = reader->makeNetNamedPortRefScalarNet(std::move($2), std::move($4)); } | '.' ID '(' ID '[' INT ']' ')' - { $$ = reader->makeNetNamedPortRefBitSelect($2, $4, $6); } + { $$ = reader->makeNetNamedPortRefBitSelect(std::move($2), std::move($4), $6); } | '.' ID '(' named_pin_net_expr ')' - { $$ = reader->makeNetNamedPortRefScalar($2, $4); } + { $$ = reader->makeNetNamedPortRefScalar(std::move($2), $4); } // Bus port bit select. | '.' ID '[' INT ']' '(' ')' - { $$ = reader->makeNetNamedPortRefBit($2, $4, nullptr); } + { $$ = reader->makeNetNamedPortRefBit(std::move($2), $4, nullptr); } | '.' ID '[' INT ']' '(' net_expr ')' - { $$ = reader->makeNetNamedPortRefBit($2, $4, $7); } + { $$ = reader->makeNetNamedPortRefBit(std::move($2), $4, $7); } // Bus port part select. | '.' ID '[' INT ':' INT ']' '(' ')' - { $$ = reader->makeNetNamedPortRefPart($2, $4, $6, nullptr); } + { $$ = reader->makeNetNamedPortRefPart(std::move($2), $4, $6, nullptr); } | '.' ID '[' INT ':' INT ']' '(' net_expr ')' - { $$ = reader->makeNetNamedPortRefPart($2, $4, $6, $9); } + { $$ = reader->makeNetNamedPortRefPart(std::move($2), $4, $6, $9); } ; named_pin_net_expr: @@ -468,22 +444,22 @@ net_named: net_scalar: ID - { $$ = reader->makeNetScalar($1); } + { $$ = reader->makeNetScalar(std::move($1)); } ; net_bit_select: ID '[' INT ']' - { $$ = reader->makeNetBitSelect($1, $3); } + { $$ = reader->makeNetBitSelect(std::move($1), $3); } ; net_part_select: ID '[' INT ':' INT ']' - { $$ = reader->makeNetPartSelect($1, $3, $5); } + { $$ = reader->makeNetPartSelect(std::move($1), $3, $5); } ; net_constant: CONSTANT - { $$ = reader->makeNetConstant($1, loc_line(@1)); } + { $$ = reader->makeNetConstant(std::move($1), loc_line(@1)); } ; net_expr_concat: @@ -497,7 +473,7 @@ net_exprs: $$->push_back($1); } | net_exprs ',' net_expr - { $$->push_back($3); } + { $1->push_back($3); $$ = $1; } ; net_expr: @@ -512,7 +488,7 @@ attr_instance_seq: // empty { $$ = new sta::VerilogAttrStmtSeq; } | attr_instance_seq attr_instance - { if ($2) $1->push_back($2); } + { if ($2) $1->push_back($2); $$ = $1; } ; attr_instance: @@ -526,23 +502,23 @@ attr_specs: $$->push_back($1); } | attr_specs ',' attr_spec - { $$->push_back($3); } + { $1->push_back($3); $$ = $1; } ; attr_spec: ID - { $$ = new sta::VerilogAttrEntry(*$1, "1"); delete $1; } + { $$ = new sta::VerilogAttrEntry(std::move($1), "1"); } | ID '=' attr_spec_value - { $$ = new sta::VerilogAttrEntry(*$1, *$3); delete $1; delete $3; } + { $$ = new sta::VerilogAttrEntry(std::move($1), std::move($3)); } ; attr_spec_value: CONSTANT - { $$ = $1; } + { $$ = std::move($1); } | STRING - { $$ = $1; } + { $$ = std::move($1); } | INT - { $$ = new std::string(std::to_string($1)); } + { $$ = std::to_string($1); } ; %% diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index a539a3114..68f99b62e 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -26,6 +26,7 @@ #include #include +#include #include "ContainerHelpers.hh" #include "Zlib.hh" @@ -59,7 +60,7 @@ makeVerilogReader(NetworkReader *network) } bool -readVerilogFile(const char *filename, +readVerilogFile(std::string_view filename, VerilogReader *verilog_reader) { return verilog_reader->read(filename); @@ -115,34 +116,34 @@ 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); - }); + network->setLinkFunc([this](std::string_view 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() { - for (const auto [name, module] : module_map_) - delete module; - module_map_.clear(); + deleteContents(module_map_); } bool -VerilogReader::read(const char *filename) +VerilogReader::read(std::string_view filename) { - gzstream::igzstream stream(filename); + gzstream::igzstream stream(std::string(filename).c_str()); if (stream.is_open()) { Stats stats(debug_, report_); VerilogScanner scanner(&stream, filename, report_); VerilogParse parser(&scanner, this); init(filename); bool success = (parser.parse() == 0); - reportStmtCounts(); stats.report("Read verilog"); return success; } @@ -151,38 +152,13 @@ VerilogReader::read(const char *filename) } void -VerilogReader::init(const char *filename) +VerilogReader::init(std::string_view filename) { filename_ = filename; library_ = network_->findLibrary("verilog"); if (library_ == nullptr) - library_ = network_->makeLibrary("verilog", nullptr); - - // Stats - report_stmt_stats_ = debug_->check("verilog", 1); - module_count_ = 0; - inst_mod_count_ = 0; - inst_lib_count_ = 0; - inst_lib_net_arrays_ = 0; - dcl_count_ = 0; - dcl_bus_count_ = 0; - dcl_arg_count_ = 0; - net_scalar_count_ = 0; - net_part_select_count_ = 0; - net_bit_select_count_ = 0; - net_port_ref_scalar_count_ = 0; - net_port_ref_scalar_net_count_ = 0; - net_port_ref_bit_count_ = 0; - net_port_ref_part_count_ = 0; - net_constant_count_ = 0; - assign_count_ = 0; - concat_count_ = 0; - inst_names_ = 0; - port_names_ = 0; - inst_module_names_ = 0; - net_scalar_names_ = 0; - net_bus_names_ = 0; + library_ = network_->makeLibrary("verilog", ""); } VerilogModule * @@ -192,14 +168,14 @@ VerilogReader::module(Cell *cell) } void -VerilogReader::makeModule(const std::string *module_vname, +VerilogReader::makeModule(std::string &&module_vname, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, int line) { - const std::string module_name = moduleVerilogToSta(*module_vname); - Cell *cell = network_->findCell(library_, module_name.c_str()); + const std::string module_name = moduleVerilogToSta(std::move(module_vname)); + Cell *cell = network_->findCell(library_, module_name); if (cell) { VerilogModule *module = module_map_[cell]; delete module; @@ -207,23 +183,23 @@ VerilogReader::makeModule(const std::string *module_vname, network_->deleteCell(cell); } - VerilogModule *module = new VerilogModule(module_name.c_str(), ports, stmts, + VerilogModule *module = new VerilogModule(module_name, ports, stmts, attr_stmts, filename_, line, this); - cell = network_->makeCell(library_, module_name.c_str(), false, filename_.c_str()); + cell = network_->makeCell(library_, module_name, false, filename_); - for (VerilogAttrStmt *stmt : *attr_stmts) { - for (VerilogAttrEntry *entry : *stmt->attrs()) - network_->setAttribute(cell, entry->key(), entry->value()); + if (attr_stmts) { + for (VerilogAttrStmt *stmt : *attr_stmts) { + for (VerilogAttrEntry *entry : *stmt->attrs()) + network_->setAttribute(cell, entry->key(), entry->value()); + } } module_map_[cell] = module; makeCellPorts(cell, module, ports); - module_count_++; - delete module_vname; } void -VerilogReader::makeModule(const std::string *module_name, +VerilogReader::makeModule(std::string &&module_vname, VerilogStmtSeq *port_dcls, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, @@ -243,7 +219,7 @@ VerilogReader::makeModule(const std::string *module_name, } } delete port_dcls; - makeModule(module_name, ports, stmts, attr_stmts, line); + makeModule(std::move(module_vname), ports, stmts, attr_stmts, line); } void @@ -265,8 +241,7 @@ VerilogReader::makeCellPorts(Cell *cell, } else warn(165, module->filename(), module->line(), - "module {} repeated port name {}.", module->name().c_str(), - port_name.c_str()); + "module {} repeated port name {}.", module->name(), port_name); } checkModuleDcls(module, port_names); } @@ -276,22 +251,22 @@ VerilogReader::makeCellPort(Cell *cell, VerilogModule *module, const std::string &port_name) { - VerilogDcl *dcl = module->declaration(port_name.c_str()); + VerilogDcl *dcl = module->declaration(port_name); if (dcl) { PortDirection *dir = dcl->direction(); VerilogDclBus *dcl_bus = dynamic_cast(dcl); Port *port = dcl->isBus() - ? network_->makeBusPort(cell, port_name.c_str(), dcl_bus->fromIndex(), + ? network_->makeBusPort(cell, port_name, dcl_bus->fromIndex(), dcl_bus->toIndex()) - : network_->makePort(cell, port_name.c_str()); + : network_->makePort(cell, port_name); network_->setDirection(port, dir); return port; } else { warn(166, module->filename(), module->line(), - "module {} missing declaration for port {}.", module->name().c_str(), - port_name.c_str()); - return network_->makePort(cell, port_name.c_str()); + "module {} missing declaration for port {}.", + module->name(), port_name); + return network_->makePort(cell, port_name); } } @@ -311,7 +286,7 @@ VerilogReader::makeNamedPortRefCellPorts(Cell *cell, } delete net_name_iter; // Note that the bundle does NOT have a port declaration. - network_->makeBundlePort(cell, mod_port->name().c_str(), member_ports); + network_->makeBundlePort(cell, mod_port->name(), member_ports); } // Make sure each declaration appears in the module port list. @@ -347,12 +322,10 @@ VerilogReader::makeDcl(PortDirection *dir, } else { delete arg; - dcl_arg_count_--; } } delete args; if (assign_args) { - dcl_count_++; return new VerilogDcl(dir, assign_args, attr_stmts, line); } else { @@ -362,7 +335,6 @@ VerilogReader::makeDcl(PortDirection *dir, } } else { - dcl_count_++; return new VerilogDcl(dir, args, attr_stmts, line); } } @@ -373,7 +345,6 @@ VerilogReader::makeDcl(PortDirection *dir, VerilogAttrStmtSeq *attr_stmts, int line) { - dcl_count_++; return new VerilogDcl(dir, arg, attr_stmts, line); } @@ -385,7 +356,6 @@ VerilogReader::makeDclBus(PortDirection *dir, VerilogAttrStmtSeq *attr_stmts, int line) { - dcl_bus_count_++; return new VerilogDclBus(dir, from_index, to_index, arg, attr_stmts, line); } @@ -397,72 +367,55 @@ VerilogReader::makeDclBus(PortDirection *dir, VerilogAttrStmtSeq *attr_stmts, int line) { - dcl_bus_count_++; return new VerilogDclBus(dir, from_index, to_index, args, attr_stmts, line); } VerilogDclArg * -VerilogReader::makeDclArg(const std::string *net_vname) +VerilogReader::makeDclArg(std::string &&net_vname) { - dcl_arg_count_++; - const std::string net_name = netVerilogToSta(*net_vname); + const std::string net_name = netVerilogToSta(std::move(net_vname)); VerilogDclArg *dcl = new VerilogDclArg(net_name); - delete net_vname; return dcl; } VerilogDclArg * VerilogReader::makeDclArg(VerilogAssign *assign) { - dcl_arg_count_++; return new VerilogDclArg(assign); } VerilogNetPartSelect * -VerilogReader::makeNetPartSelect(const std::string *net_vname, +VerilogReader::makeNetPartSelect(std::string &&net_vname, int from_index, int to_index) { - net_part_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(std::move(net_vname)); VerilogNetPartSelect *select = new VerilogNetPartSelect(net_name, from_index, to_index); - delete net_vname; return select; } VerilogNetConstant * -VerilogReader::makeNetConstant(const std::string *constant, +VerilogReader::makeNetConstant(std::string &&constant, int line) { - net_constant_count_++; - return new VerilogNetConstant(constant, this, line); + return new VerilogNetConstant(std::move(constant), this, line); } VerilogNetScalar * -VerilogReader::makeNetScalar(const std::string *net_vname) +VerilogReader::makeNetScalar(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(std::move(net_vname)); VerilogNetScalar *scalar = new VerilogNetScalar(net_name); - delete net_vname; return scalar; } VerilogNetBitSelect * -VerilogReader::makeNetBitSelect(const std::string *net_vname, +VerilogReader::makeNetBitSelect(std::string &&net_vname, int index) { - 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(std::move(net_vname)); VerilogNetBitSelect *select = new VerilogNetBitSelect(net_name, index); - delete net_vname; return select; } @@ -471,20 +424,19 @@ VerilogReader::makeAssign(VerilogNet *lhs, VerilogNet *rhs, int line) { - assign_count_++; return new VerilogAssign(lhs, rhs, line); } VerilogInst * -VerilogReader::makeModuleInst(const std::string *module_vname, - const std::string *inst_vname, +VerilogReader::makeModuleInst(std::string &&module_vname, + std::string &&inst_vname, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, const int line) { - const std::string module_name = moduleVerilogToSta(*module_vname); - const std::string inst_name = instanceVerilogToSta(*inst_vname); - Cell *cell = network_->findAnyCell(module_name.c_str()); + const std::string module_name = moduleVerilogToSta(std::move(module_vname)); + const std::string inst_name = instanceVerilogToSta(std::move(inst_vname)); + Cell *cell = network_->findAnyCell(module_name); LibertyCell *liberty_cell = nullptr; if (cell) liberty_cell = network_->libertyCell(cell); @@ -496,8 +448,8 @@ VerilogReader::makeModuleInst(const std::string *module_vname, for (VerilogNet *vnet : *pins) { VerilogNetPortRefScalarNet *vpin = dynamic_cast(vnet); - const char *port_name = vpin->name().c_str(); - const std::string &net_name = vpin->netName(); + std::string_view port_name = vpin->name(); + std::string_view net_name = vpin->netName(); Port *port = network_->findPort(cell, port_name); LibertyPort *lport = network_->libertyPort(port); if (lport->isBus()) { @@ -507,30 +459,15 @@ VerilogReader::makeModuleInst(const std::string *module_vname, int pin_index = lport->pinIndex(); net_names[pin_index] = net_name; delete vpin; - net_port_ref_scalar_net_count_--; } 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; - inst_lib_count_++; - inst_lib_net_arrays_ += port_count; - } - delete module_vname; - delete inst_vname; return inst; } else { - VerilogInst *inst = new VerilogModuleInst(module_name.c_str(), inst_name.c_str(), + VerilogInst *inst = new VerilogModuleInst(module_name, inst_name, pins, attr_stmts, line); - if (report_stmt_stats_) { - inst_module_names_ += module_name.size() + 1; - inst_names_ += inst_name.size() + 1; - inst_mod_count_++; - } - delete module_vname; - delete inst_vname; return inst; } } @@ -541,7 +478,7 @@ VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, { if (pins && pins->size() > 0 && (*pins)[0]->isNamedPortRef()) { for (VerilogNet *vpin : *pins) { - const char *port_name = vpin->name().c_str(); + std::string_view port_name = vpin->name(); LibertyPort *port = liberty_cell->findLibertyPort(port_name); if (port) { if (!(port->size() == 1 && (vpin->isNamedPortRefScalarNet()))) @@ -557,142 +494,73 @@ VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, } VerilogNetPortRef * -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); - VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name.c_str()); - delete port_vname; +VerilogReader::makeNetNamedPortRefScalarNet(std::string &&port_vname) +{ + const std::string port_name = portVerilogToSta(std::move(port_vname)); + VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalarNet(const std::string *port_vname, - const std::string *net_vname) -{ - net_port_ref_scalar_net_count_++; - if (report_stmt_stats_) { - if (net_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()); - delete port_vname; - delete net_vname; +VerilogReader::makeNetNamedPortRefScalarNet(std::string &&port_vname, + std::string &&net_vname) +{ + const std::string port_name = portVerilogToSta(std::move(port_vname)); + const std::string net_name = netVerilogToSta(std::move(net_vname)); + VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name, net_name); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefBitSelect(const std::string *port_vname, - const std::string *bus_vname, +VerilogReader::makeNetNamedPortRefBitSelect(std::string &&port_vname, + std::string &&bus_vname, int index) { - net_port_ref_scalar_net_count_++; - const std::string bus_name = portVerilogToSta(*bus_vname); + const std::string bus_name = portVerilogToSta(std::move(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); + const std::string port_name = portVerilogToSta(std::move(port_vname)); VerilogNetPortRef *ref = - new VerilogNetPortRefScalarNet(port_name.c_str(), net_name.c_str()); - delete port_vname; - delete bus_vname; + new VerilogNetPortRefScalarNet(port_name, net_name); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalar(const std::string *port_vname, +VerilogReader::makeNetNamedPortRefScalar(std::string &&port_vname, VerilogNet *net) { - net_port_ref_scalar_count_++; - if (report_stmt_stats_) - port_names_ += port_vname->size() + 1; - const std::string port_name = portVerilogToSta(*port_vname); - VerilogNetPortRef *ref = new VerilogNetPortRefScalar(port_name.c_str(), net); - delete port_vname; + const std::string port_name = portVerilogToSta(std::move(port_vname)); + VerilogNetPortRef *ref = new VerilogNetPortRefScalar(port_name, net); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefBit(const std::string *port_vname, +VerilogReader::makeNetNamedPortRefBit(std::string &&port_vname, int index, 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); - delete port_vname; + const std::string port_name = portVerilogToSta(std::move(port_vname)); + VerilogNetPortRef *ref = new VerilogNetPortRefBit(port_name, index, net); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefPart(const std::string *port_vname, +VerilogReader::makeNetNamedPortRefPart(std::string &&port_vname, int from_index, int to_index, VerilogNet *net) { - net_port_ref_part_count_++; - const std::string port_name = portVerilogToSta(*port_vname); + const std::string port_name = portVerilogToSta(std::move(port_vname)); VerilogNetPortRef *ref = new VerilogNetPortRefPart(port_name, from_index, to_index, net); - delete port_vname; return ref; } VerilogNetConcat * VerilogReader::makeNetConcat(VerilogNetSeq *nets) { - concat_count_++; return new VerilogNetConcat(nets); } -#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_->report(" {:<20} {:6.1f}Mb", name, count * 1e-6) - -void -VerilogReader::reportStmtCounts() -{ - if (debug_->check("verilog", 1)) { - report_->report("Verilog stats"); - printClassMemory("modules", VerilogModule, module_count_); - printClassMemory("module insts", VerilogModuleInst, inst_mod_count_); - printClassMemory("liberty insts", VerilogLibertyInst, inst_lib_count_); - printClassMemory("liberty net arrays", char *, inst_lib_net_arrays_); - printClassMemory("declarations", VerilogDcl, dcl_count_); - printClassMemory("bus declarations", VerilogDclBus, dcl_bus_count_); - printClassMemory("declaration args", VerilogDclArg, dcl_arg_count_); - printClassMemory("port ref scalar", VerilogNetPortRefScalar, - 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 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("constant nets", VerilogNetConstant, net_constant_count_); - printClassMemory("concats", VerilogNetConcat, concat_count_); - printClassMemory("assigns", VerilogAssign, assign_count_); - printStringMemory("instance names", inst_names_); - printStringMemory("instance mod names", inst_module_names_); - printStringMemory("port names", port_names_); - printStringMemory("net scalar names", net_scalar_names_); - printStringMemory("net bus names", net_bus_names_); - } -} - //////////////////////////////////////////////////////////////// VerilogModule::VerilogModule(const std::string &name, @@ -741,35 +609,35 @@ VerilogModule::parseDcl(VerilogDcl *dcl, for (VerilogDclArg *arg : *dcl->args()) { if (arg->isNamed()) { const std::string &net_name = arg->netName(); - VerilogDcl *existing_dcl = dcl_map_[net_name.c_str()]; + VerilogDcl *existing_dcl = dcl_map_[net_name]; if (existing_dcl) { PortDirection *existing_dir = existing_dcl->direction(); if (existing_dir->isInternal()) // wire dcl can be used as modifier for input/inout dcls. // Ignore the wire dcl. - dcl_map_[net_name.c_str()] = dcl; + dcl_map_[net_name] = dcl; else if (dcl->direction()->isTristate()) { if (existing_dir->isOutput()) // tri dcl can be used as modifier for input/output/inout dcls. // Keep the tristate dcl for outputs because it is more specific // but ignore it for inputs and bidirs. - dcl_map_[net_name.c_str()] = dcl; + dcl_map_[net_name] = dcl; } else if (dcl->direction()->isPowerGround() && (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; + dcl_map_[net_name] = dcl; else if (!dcl->direction()->isInternal()) { - std::string net_vname = netVerilogName(net_name.c_str()); + std::string net_vname = netVerilogName(net_name); reader->warn(1395, filename_, dcl->line(), "signal {} previously declared on line {}.", net_vname, existing_dcl->line()); } } else - dcl_map_[net_name.c_str()] = dcl; + dcl_map_[net_name] = dcl; } } } @@ -788,7 +656,7 @@ VerilogModule::checkInstanceName(VerilogInst *inst, do { replacement_name = sta::format("{}_{}", inst_name, i++); } while (inst_names.contains(replacement_name)); - std::string inst_vname = instanceVerilogName(inst_name.c_str()); + std::string inst_vname = instanceVerilogName(inst_name); reader->warn(1396, filename_, inst->line(), "instance name {} duplicated - renamed to {}.", inst_vname, replacement_name); @@ -799,9 +667,9 @@ VerilogModule::checkInstanceName(VerilogInst *inst, } VerilogDcl * -VerilogModule::declaration(const std::string &net_name) +VerilogModule::declaration(std::string_view net_name) { - return findKey(dcl_map_, net_name.c_str()); + return findStringKey(dcl_map_, net_name); } //////////////////////////////////////////////////////////////// @@ -1090,7 +958,7 @@ static std::string verilogBusBitName(const std::string &bus_name, int index) { - return sta::format("{}[{}]", bus_name.c_str(), index); + return sta::format("{}[{}]", bus_name, index); } class VerilogConstantNetNameIterator : public VerilogNetNameIterator @@ -1212,7 +1080,7 @@ VerilogNetScalar::VerilogNetScalar(const std::string &name) : } static int -verilogNetScalarSize(const char *name, +verilogNetScalarSize(std::string_view name, VerilogModule *module) { VerilogDcl *dcl = module->declaration(name); @@ -1226,7 +1094,7 @@ verilogNetScalarSize(const char *name, int VerilogNetScalar::size(VerilogModule *module) { - return verilogNetScalarSize(name_.c_str(), module); + return verilogNetScalarSize(name_, module); } static VerilogNetNameIterator * @@ -1248,7 +1116,7 @@ VerilogNetNameIterator * VerilogNetScalar::nameIterator(VerilogModule *module, VerilogReader *) { - return verilogNetScalarNameIterator(name_.c_str(), module); + return verilogNetScalarNameIterator(name_, module); } VerilogNetBitSelect::VerilogNetBitSelect(const std::string &name, @@ -1294,10 +1162,10 @@ VerilogNetNameIterator * VerilogNetPartSelect::nameIterator(VerilogModule *, VerilogReader *) { - return new VerilogBusNetNameIterator(name_.c_str(), from_index_, to_index_); + return new VerilogBusNetNameIterator(name_, from_index_, to_index_); } -VerilogNetConstant::VerilogNetConstant(const std::string *constant, +VerilogNetConstant::VerilogNetConstant(std::string constant, VerilogReader *reader, int line) { @@ -1305,13 +1173,13 @@ VerilogNetConstant::VerilogNetConstant(const std::string *constant, } void -VerilogNetConstant::parseConstant(const std::string *constant, +VerilogNetConstant::parseConstant(const std::string &constant, VerilogReader *reader, int line) { // Find constant size. - size_t csize_end = constant->find('\''); - std::string csize = constant->substr(0, csize_end); + size_t csize_end = constant.find('\''); + std::string csize = constant.substr(0, csize_end); // Read the constant size. size_t size = std::stol(csize); @@ -1319,7 +1187,7 @@ VerilogNetConstant::parseConstant(const std::string *constant, // Read the constant base. size_t base_idx = csize_end + 1; - char base = constant->at(base_idx); + char base = constant.at(base_idx); switch (base) { case 'b': case 'B': @@ -1343,11 +1211,10 @@ VerilogNetConstant::parseConstant(const std::string *constant, "unknown constant base."); break; } - delete constant; } void -VerilogNetConstant::parseConstant(const std::string *constant, +VerilogNetConstant::parseConstant(const std::string &constant, size_t base_idx, int base, int digit_bit_count) @@ -1358,9 +1225,9 @@ VerilogNetConstant::parseConstant(const std::string *constant, char *end; value_digit_str[1] = '\0'; size_t bit = 0; - size_t idx = constant->size() - 1; + size_t idx = constant.size() - 1; while (bit < size) { - char ch = (idx > base_idx) ? constant->at(idx--) : '0'; + char ch = (idx > base_idx) ? constant.at(idx--) : '0'; // Skip underscores. if (ch != '_') { value_digit_str[0] = ch; @@ -1376,15 +1243,15 @@ VerilogNetConstant::parseConstant(const std::string *constant, } void -VerilogNetConstant::parseConstant10(const std::string *constant, +VerilogNetConstant::parseConstant10(const std::string &constant, size_t base_idx, VerilogReader *reader, int line) { // Copy the constant skipping underscores. std::string tmp; - for (size_t i = base_idx + 1; i < constant->size(); i++) { - char ch = constant->at(i); + for (size_t i = base_idx + 1; i < constant.size(); i++) { + char ch = constant.at(i); if (ch != '_') tmp += ch; } @@ -1587,47 +1454,48 @@ VerilogAttrStmt::attrs() //////////////////////////////////////////////////////////////// // Verilog net name to network net map. -using BindingMap = std::map; +using BindingMap = std::map>; class VerilogBindingTbl { public: VerilogBindingTbl(const std::string &zero_net_name_, const std::string &one_net_name_); - Net *ensureNetBinding(const char *net_name, + Net *ensureNetBinding(std::string_view net_name, Instance *inst, NetworkReader *network); - Net *find(const char *name, + Net *find(std::string_view name, NetworkReader *network); - void bind(const char *name, + void bind(std::string_view name, Net *net); private: const std::string &zero_net_name_; const std::string &one_net_name_; - BindingMap map_; + BindingMap net_map_; }; Instance * -VerilogReader::linkNetwork(const char *top_cell_name, +VerilogReader::linkNetwork(std::string_view top_cell_name, bool make_black_boxes, bool delete_modules) { if (library_) { - Cell *top_cell = network_->findCell(library_, top_cell_name); + const std::string top_cell_str(top_cell_name); + Cell *top_cell = network_->findCell(library_, top_cell_str); 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); + network_->makeInstance(top_cell, top_cell_str, nullptr); VerilogBindingTbl bindings(zero_net_name_, one_net_name_); for (VerilogNet *mod_port : *module->ports()) { 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()); + Port *port = network_->findPort(top_cell, net_name); Net *net = - bindings.ensureNetBinding(net_name.c_str(), top_instance, network_); + bindings.ensureNetBinding(net_name, top_instance, network_); // Guard against repeated port name. if (network_->findPin(top_instance, port) == nullptr) { Pin *pin = network_->makePin(top_instance, port, nullptr); @@ -1680,12 +1548,12 @@ VerilogReader::makeModuleInstBody(VerilogModule *module, mergeAssignNet(assign, module, inst, bindings); if (dir->isGround()) { Net *net = - bindings->ensureNetBinding(arg->netName().c_str(), inst, network_); + bindings->ensureNetBinding(arg->netName(), inst, network_); network_->addConstantNet(net, LogicValue::zero); } if (dir->isPower()) { Net *net = - bindings->ensureNetBinding(arg->netName().c_str(), inst, network_); + bindings->ensureNetBinding(arg->netName(), inst, network_); network_->addConstantNet(net, LogicValue::one); } } @@ -1703,9 +1571,9 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, bool make_black_boxes) { const std::string &module_name = mod_inst->moduleName(); - Cell *cell = network_->findAnyCell(module_name.c_str()); + Cell *cell = network_->findAnyCell(module_name); if (cell == nullptr) { - std::string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); + std::string inst_vname = instanceVerilogName(mod_inst->instanceName()); if (make_black_boxes) { cell = makeBlackBox(mod_inst, parent_module); linkWarn(198, parent_module->filename(), mod_inst->line(), @@ -1722,7 +1590,7 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, if (lib_cell) cell = network_->cell(lib_cell); Instance *inst = - network_->makeInstance(cell, mod_inst->instanceName().c_str(), parent); + network_->makeInstance(cell, mod_inst->instanceName(), parent); VerilogAttrStmtSeq *attr_stmts = mod_inst->attrStmts(); for (VerilogAttrStmt *stmt : *attr_stmts) { for (VerilogAttrEntry *entry : *stmt->attrs()) { @@ -1765,10 +1633,10 @@ VerilogReader::makeNamedInstPins(Cell *cell, VerilogBindingTbl *parent_bindings, bool is_leaf) { - std::string inst_vname = instanceVerilogName(mod_inst->instanceName().c_str()); + std::string inst_vname = instanceVerilogName(mod_inst->instanceName()); for (auto mpin : *mod_inst->pins()) { VerilogNetPortRef *vpin = dynamic_cast(mpin); - const char *port_name = vpin->name().c_str(); + const std::string &port_name = vpin->name(); Port *port = network_->findPort(cell, port_name); if (port) { if (vpin->hasNet() && network_->size(port) != vpin->size(parent_module)) { @@ -1819,7 +1687,7 @@ VerilogReader::makeOrderedInstPins(Cell *cell, 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()); + std::string inst_vname = instanceVerilogName(mod_inst->instanceName()); linkWarn(202, parent_module->filename(), mod_inst->line(), "instance {} port {} size {} does not match net size {}.", inst_vname, network_->name(port), network_->size(port), @@ -1871,7 +1739,7 @@ VerilogReader::makeInstPin(Instance *inst, { Net *net = nullptr; if (!net_name.empty()) - net = parent_bindings->ensureNetBinding(net_name.c_str(), parent, network_); + net = parent_bindings->ensureNetBinding(net_name, parent, network_); if (is_leaf) { // Connect leaf pin to net. if (net) @@ -1883,7 +1751,7 @@ VerilogReader::makeInstPin(Instance *inst, Pin *pin = network_->findPin(inst, port); if (net) { network_->connect(inst, port, net); - const char *port_name = network_->name(port); + std::string port_name = network_->name(port); Net *child_net = bindings->ensureNetBinding(port_name, inst, network_); network_->makeTerm(pin, child_net); } @@ -1899,7 +1767,7 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, LibertyCell *lib_cell = lib_inst->cell(); Cell *cell = reinterpret_cast(lib_cell); Instance *inst = - network_->makeInstance(cell, lib_inst->instanceName().c_str(), parent); + network_->makeInstance(cell, lib_inst->instanceName(), parent); VerilogAttrStmtSeq *attr_stmts = lib_inst->attrStmts(); for (VerilogAttrStmt *stmt : *attr_stmts) { for (VerilogAttrEntry *entry : *stmt->attrs()) { @@ -1921,10 +1789,10 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, VerilogDclBus *dcl_bus = dynamic_cast(dcl); // Bus is only 1 bit wide. std::string bus_name = verilogBusBitName(net_name, dcl_bus->fromIndex()); - net = parent_bindings->ensureNetBinding(bus_name.c_str(), parent, network_); + net = parent_bindings->ensureNetBinding(bus_name, parent, network_); } else - net = parent_bindings->ensureNetBinding(net_name.c_str(), parent, network_); + net = parent_bindings->ensureNetBinding(net_name, parent, network_); network_->makePin(inst, reinterpret_cast(port), net); } else @@ -1940,7 +1808,7 @@ VerilogReader::makeBlackBox(VerilogModuleInst *mod_inst, VerilogModule *parent_module) { const std::string &module_name = mod_inst->moduleName(); - Cell *cell = network_->makeCell(library_, module_name.c_str(), true, + Cell *cell = network_->makeCell(library_, module_name, true, parent_module->filename()); if (mod_inst->namedPins()) makeBlackBoxNamedPorts(cell, mod_inst, parent_module); @@ -1956,7 +1824,7 @@ VerilogReader::makeBlackBoxNamedPorts(Cell *cell, { for (VerilogNet *mpin : *mod_inst->pins()) { VerilogNetNamed *vpin = dynamic_cast(mpin); - const char *port_name = vpin->name().c_str(); + const std::string &port_name = vpin->name(); size_t size = vpin->size(parent_module); Port *port = (size == 1) ? network_->makePort(cell, port_name) : network_->makeBusPort(cell, port_name, 0, size - 1); @@ -1976,8 +1844,8 @@ VerilogReader::makeBlackBoxOrderedPorts(Cell *cell, size_t size = net->size(parent_module); std::string port_name = format("p_{}", port_index); Port *port = (size == 1) - ? network_->makePort(cell, port_name.c_str()) - : network_->makeBusPort(cell, port_name.c_str(), size - 1, 0); + ? network_->makePort(cell, port_name) + : network_->makeBusPort(cell, port_name, size - 1, 0); network_->setDirection(port, PortDirection::unknown()); port_index++; } @@ -2006,8 +1874,8 @@ VerilogReader::mergeAssignNet(VerilogAssign *assign, while (lhs_iter->hasNext() && rhs_iter->hasNext()) { const std::string &lhs_name = lhs_iter->next(); const std::string &rhs_name = rhs_iter->next(); - Net *lhs_net = bindings->ensureNetBinding(lhs_name.c_str(), inst, network_); - Net *rhs_net = bindings->ensureNetBinding(rhs_name.c_str(), inst, network_); + Net *lhs_net = bindings->ensureNetBinding(lhs_name, inst, network_); + Net *rhs_net = bindings->ensureNetBinding(rhs_name, inst, network_); // 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. @@ -2053,34 +1921,35 @@ 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, +VerilogBindingTbl::find(std::string_view name, NetworkReader *network) { - Net *net = findKey(map_, name); + Net *net = findStringKey(net_map_, name); while (net && network->mergedInto(net)) net = network->mergedInto(net); return net; } void -VerilogBindingTbl::bind(const char *name, +VerilogBindingTbl::bind(std::string_view name, Net *net) { - map_[name] = net; + net_map_[std::string(name)] = net; } Net * -VerilogBindingTbl::ensureNetBinding(const char *net_name, +VerilogBindingTbl::ensureNetBinding(std::string_view net_name, Instance *inst, NetworkReader *network) { Net *net = find(net_name, network); if (net == nullptr) { - net = network->makeNet(net_name, inst); - map_[network->name(net)] = net; - if (net_name == zero_net_name_) + const std::string net_str(net_name); + net = network->makeNet(net_str, inst); + net_map_[std::string(network->name(net))] = net; + if (net_str == zero_net_name_) network->addConstantNet(net, LogicValue::zero); - if (net_name == one_net_name_) + if (net_str == one_net_name_) network->addConstantNet(net, LogicValue::one); } return net; @@ -2109,7 +1978,7 @@ VerilogReader::reportLinkErrors() //////////////////////////////////////////////////////////////// VerilogScanner::VerilogScanner(std::istream *stream, - const char *filename, + std::string_view filename, Report *report) : yyFlexLexer(stream), filename_(filename), @@ -2118,7 +1987,7 @@ VerilogScanner::VerilogScanner(std::istream *stream, } void -VerilogScanner::error(const char *msg) +VerilogScanner::error(std::string_view msg) { report_->fileError(1870, filename_, lineno(), "{}", msg); } diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index 7a445ba44..fc5381f8b 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -32,7 +32,7 @@ namespace sta { -using VerilogDclMap = std::map; +using VerilogDclMap = std::map>; using VerilogConstantValue = std::vector; class VerilogStmt @@ -63,10 +63,10 @@ public: VerilogReader *reader); ~VerilogModule() override; const std::string &name() { return name_; } - const char *filename() { return filename_.c_str(); } + const std::string &filename() { return filename_; } VerilogAttrStmtSeq *attrStmts() { return attr_stmts_; } VerilogNetSeq *ports() { return ports_; } - VerilogDcl *declaration(const std::string &net_name); + VerilogDcl *declaration(std::string_view net_name); VerilogStmtSeq *stmts() { return stmts_; } VerilogDclMap *declarationMap() { return &dcl_map_; } void parseDcl(VerilogDcl *dcl, @@ -313,7 +313,7 @@ private: class VerilogNetConstant : public VerilogNetUnnamed { public: - VerilogNetConstant(const std::string *constant, + VerilogNetConstant(std::string constant, VerilogReader *reader, int line); ~VerilogNetConstant() override; @@ -322,14 +322,14 @@ public: VerilogReader *reader) override; private: - void parseConstant(const std::string *constant, + void parseConstant(const std::string &constant, VerilogReader *reader, int line); - void parseConstant(const std::string *constant, + void parseConstant(const std::string &constant, size_t base_idx, int base, int digit_bit_count); - void parseConstant10(const std::string *constant, + void parseConstant10(const std::string &constant, size_t base_idx, VerilogReader *reader, int line); diff --git a/verilog/VerilogScanner.hh b/verilog/VerilogScanner.hh index 1776062ca..3c0c5d613 100644 --- a/verilog/VerilogScanner.hh +++ b/verilog/VerilogScanner.hh @@ -24,6 +24,9 @@ #pragma once +#include +#include + #include "VerilogLocation.hh" #include "VerilogParse.hh" @@ -41,7 +44,7 @@ class VerilogScanner : public VerilogFlexLexer { public: VerilogScanner(std::istream *stream, - const char *filename, + std::string_view filename, Report *report); virtual ~VerilogScanner() {} @@ -50,14 +53,16 @@ public: // YY_DECL defined in VerilogLex.ll // Method body created by flex in VerilogLex.cc - void error(const char *msg); + void error(std::string_view msg); // Get rid of override virtual function warning. using yyFlexLexer::yylex; private: - const char *filename_; + std::string filename_; Report *report_; + // Quoted string accumulation (see VerilogLex.ll). + std::string token_; }; } // namespace diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index 3531b865c..8edaf6990 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -28,6 +28,7 @@ #include #include #include +#include #include "Error.hh" #include "Format.hh" @@ -142,9 +143,9 @@ VerilogWriter::findHierChildren() sort(children, [this](const Instance *inst1, const Instance *inst2) { - const char *cell_name1 = network_->cellName(inst1); - const char *cell_name2 = network_->cellName(inst2); - return stringLess(cell_name1, cell_name2); + std::string cell_name1 = network_->cellName(inst1); + std::string cell_name2 = network_->cellName(inst2); + return cell_name1 < cell_name2; }); return children; @@ -173,7 +174,7 @@ void VerilogWriter::writeModule(const Instance *inst) { Cell *cell = network_->cell(inst); - std::string cell_vname = cellVerilogName(network_->name(cell)); + std::string cell_vname = cellVerilogName(std::string(network_->name(cell))); sta::print(stream_, "module {} (", cell_vname); writePorts(cell); writePortDcls(cell); @@ -196,7 +197,7 @@ VerilogWriter::writePorts(const Cell *cell) || !network_->direction(port)->isPowerGround()) { if (!first) sta::print(stream_, ",\n "); - std::string verilog_name = portVerilogName(network_->name(port)); + std::string verilog_name = portVerilogName(std::string(network_->name(port))); sta::print(stream_, "{}", verilog_name); first = false; } @@ -214,7 +215,7 @@ VerilogWriter::writePortDcls(const Cell *cell) PortDirection *dir = network_->direction(port); if (include_pwr_gnd_ || !network_->direction(port)->isPowerGround()) { - std::string port_vname = portVerilogName(network_->name(port)); + std::string port_vname = portVerilogName(std::string(network_->name(port))); const char *vtype = verilogPortDir(dir); if (vtype) { sta::print(stream_, " {}", vtype); @@ -274,7 +275,7 @@ VerilogWriter::writeWireDcls(const Instance *inst) Net *net = net_iter->next(); if (include_pwr_gnd_ || !(network_->isPower(net) || network_->isGround(net))) { - const char *net_name = network_->name(net); + std::string net_name = network_->name(net); if (network_->findPort(cell, net_name) == nullptr) { if (isBusName(net_name, '[', ']', escape)) { bool is_bus; @@ -286,7 +287,7 @@ VerilogWriter::writeWireDcls(const Instance *inst) range.second = std::min(range.second, index); } else { - std::string net_vname = netVerilogName(net_name); + std::string net_vname = netVerilogName(std::string(net_name)); sta::print(stream_, " wire {};\n", net_vname); } } @@ -294,8 +295,7 @@ VerilogWriter::writeWireDcls(const Instance *inst) } delete net_iter; - for (const auto& [bus_name1, range] : bus_ranges) { - const char *bus_name = bus_name1.c_str(); + for (const auto& [bus_name, range] : bus_ranges) { std::string net_vname = netVerilogName(bus_name); sta::print(stream_, " wire [{}:{}] {};\n", range.first, @@ -322,7 +322,7 @@ VerilogWriter::writeChildren(const Instance *inst) sort(children, [this](const Instance *inst1, const Instance *inst2) { - return stringLess(network_->name(inst1), network_->name(inst2)); + return network_->name(inst1) < network_->name(inst2); }); for (auto child : children) @@ -334,9 +334,9 @@ VerilogWriter::writeChild(const Instance *child) { Cell *child_cell = network_->cell(child); if (!remove_cells_.contains(child_cell)) { - const char *child_name = network_->name(child); - std::string child_vname = instanceVerilogName(child_name); - std::string child_cell_vname = cellVerilogName(network_->name(child_cell)); + std::string child_name = network_->name(child); + std::string child_vname = instanceVerilogName(std::string(child_name)); + std::string child_cell_vname = cellVerilogName(std::string(network_->name(child_cell))); sta::print(stream_, " {} {} (", child_cell_vname, child_vname); @@ -366,11 +366,11 @@ VerilogWriter::writeInstPin(const Instance *inst, if (pin) { Net *net = network_->net(pin); if (net) { - const char *net_name = network_->name(net); - std::string net_vname = netVerilogName(net_name); + std::string net_name = network_->name(net); + std::string net_vname = netVerilogName(std::string(net_name)); if (!first_port) sta::print(stream_, ",\n "); - std::string port_vname = portVerilogName(network_->name(port)); + std::string port_vname = portVerilogName(std::string(network_->name(port))); sta::print(stream_, ".{}({})", port_vname, net_vname); @@ -387,7 +387,7 @@ VerilogWriter::writeInstBusPin(const Instance *inst, if (!first_port) sta::print(stream_, ",\n "); - std::string port_vname = portVerilogName(network_->name(port)); + std::string port_vname = portVerilogName(std::string(network_->name(port))); sta::print(stream_, ".{}({{", port_vname); first_port = false; bool first_member = true; @@ -422,11 +422,11 @@ VerilogWriter::writeInstBusPinBit(const Instance *inst, Pin *pin = network_->findPin(inst, port); Net *net = pin ? network_->net(pin) : nullptr; std::string net_name = net - ? network_->name(net) + ? std::string(network_->name(net)) // There is no verilog syntax to "skip" a bit in the concatentation. : sta::format("_NC{}", unconnected_net_index_++); - std::string net_vname = netVerilogName(net_name.c_str()); + std::string net_vname = netVerilogName(net_name); if (!first_member) sta::print(stream_, ",\n "); sta::print(stream_, "{}", net_vname); @@ -453,8 +453,8 @@ VerilogWriter::writeAssigns(const Instance *inst) || (include_pwr_gnd_ && network_->direction(port)->isPowerGround())) && !stringEqual(network_->name(port), network_->name(net))) { // Port name is different from net name. - std::string port_vname = netVerilogName(network_->name(port)); - std::string net_vname = netVerilogName(network_->name(net)); + std::string port_vname = netVerilogName(std::string(network_->name(port))); + std::string net_vname = netVerilogName(std::string(network_->name(net))); sta::print(stream_, " assign {} = {};\n", port_vname, net_vname); From ad80dd55b34f0094016c17069e7a180be6d4ac51 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 28 Mar 2026 19:46:57 -0700 Subject: [PATCH 115/181] cmake show swig version Signed-off-by: James Cherry --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d843dcfe4..d21e91e7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -455,6 +455,7 @@ configure_file(${STA_HOME}/util/StaConfig.hh.cmake ########################################################### find_package(SWIG 3.0 REQUIRED) +message(STATUS "SWIG version: ${SWIG_VERSION}") include(UseSWIG) set(STA_SWIG_FILE app/StaApp.i) From 614385fe51570d1b40f6e9df59a6801f3c3de312 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 28 Mar 2026 19:48:21 -0700 Subject: [PATCH 116/181] Liberty unnecessary move Signed-off-by: James Cherry --- liberty/LibertyParser.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index 5ee1be81f..446ce30de 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -172,9 +172,7 @@ LibertyParser::makeSimpleAttr(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), *value, line); delete value; LibertyGroup *group = this->group(); group->addAttr(attr); @@ -194,8 +192,7 @@ LibertyParser::makeComplexAttr(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), *values, line); delete values; LibertyGroup *group = this->group(); group->addAttr(attr); From 1806e4aede3b40a72f077d73028e23a2219ea944 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 28 Mar 2026 19:55:13 -0700 Subject: [PATCH 117/181] StaTclTypes %include "std_string_view.i" Signed-off-by: James Cherry --- liberty/Liberty.i | 1 + search/Search.i | 1 + tcl/StaTclTypes.i | 11 +++++------ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/liberty/Liberty.i b/liberty/Liberty.i index a87cd7999..f0a9bc090 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -25,6 +25,7 @@ %module liberty %{ +#include "PatternMatch.hh" #include "PortDirection.hh" #include "Liberty.hh" #include "EquivCells.hh" diff --git a/search/Search.i b/search/Search.i index fe836156d..f16176707 100644 --- a/search/Search.i +++ b/search/Search.i @@ -35,6 +35,7 @@ #include "Search.hh" #include "search/Levelize.hh" #include "search/ReportPath.hh" +#include "search/Tag.hh" #include "PathExpanded.hh" #include "Bfs.hh" #include "Scene.hh" diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 5f2430e1a..9157a3fc3 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -23,24 +23,23 @@ // This notice may not be removed or altered from any source distribution. // Swig TCL input/output type parsers. + +#if SWIG_VERSION >= 0x040200 +%include "std_string_view.i" +#endif + %{ -#include "Machine.hh" -#include "StringUtil.hh" -#include "PatternMatch.hh" #include "Network.hh" #include "Liberty.hh" #include "FuncExpr.hh" #include "TimingArc.hh" -#include "TableModel.hh" #include "TimingRole.hh" #include "Graph.hh" -#include "NetworkClass.hh" #include "Clock.hh" #include "Scene.hh" #include "Search.hh" #include "Path.hh" -#include "search/Tag.hh" #include "PathEnd.hh" #include "SearchClass.hh" #include "CircuitSim.hh" From 3d0b34e88c4d4319d52fe85767ce9b2fe07715bc Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 29 Mar 2026 09:58:06 -0700 Subject: [PATCH 118/181] ReportPath::reportLine Signed-off-by: James Cherry --- search/ReportPath.cc | 6 +++--- search/ReportPath.hh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 037d21cff..1ace1d08f 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2454,7 +2454,7 @@ ReportPath::reportPathLine(const Path *path, Slew slew = graph_->slew(vertex, rf, slew_index); float cap = field_blank_; Instance *inst = network_->instance(pin); - std::string src_attr = ""; + std::string src_attr; if (inst) src_attr = network_->getAttribute(inst, "src"); // Don't show capacitance field for input pins. @@ -2736,7 +2736,7 @@ ReportPath::reportPath6(const Path *path, bool is_clk_start = path1->vertex(this) == clk_start; bool is_clk = path1->isClock(search_); Instance *inst = network_->instance(pin); - std::string src_attr = ""; + std::string src_attr; if (inst) src_attr = network_->getAttribute(inst, "src"); // Always show the search start point (register clk pin). @@ -3187,7 +3187,7 @@ ReportPath::reportLine(std::string_view what, bool total_with_minus, const EarlyLate *early_late, const RiseFall *rf, - std::string src_attr, + std::string_view src_attr, std::string_view line_case) const { std::string line; diff --git a/search/ReportPath.hh b/search/ReportPath.hh index b27e5fc11..98b7dd232 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -369,7 +369,7 @@ protected: bool total_with_minus, const EarlyLate *early_late, const RiseFall *rf, - std::string src_attr, + std::string_view src_attr, std::string_view line_case) const; void reportLineTotal(std::string_view what, const Delay &incr, From 725a6e5e1c48950dbd892d0d57d41645cc95a63d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 29 Mar 2026 10:56:45 -0700 Subject: [PATCH 119/181] require cmake 3.16 Signed-off-by: James Cherry --- CMakeLists.txt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d21e91e7e..25b87766b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,15 +22,7 @@ # # This notice may not be removed or altered from any source distribution. -cmake_minimum_required (VERSION 3.10) -if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) -# Use standard target names -cmake_policy(SET CMP0078 NEW) -endif() -if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14) -# Allows SWIG_MODULE_NAME to be set -cmake_policy(SET CMP0086 NEW) -endif() +cmake_minimum_required (VERSION 3.16) project(STA VERSION 3.1.0 LANGUAGES CXX From 5aa56bcfeea05d963d3c5655935516bc4e284389 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 29 Mar 2026 11:23:26 -0700 Subject: [PATCH 120/181] cmake swig Signed-off-by: James Cherry --- CMakeLists.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25b87766b..3f5ef070f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -455,14 +455,12 @@ set(STA_SWIG_FILE app/StaApp.i) set_property(SOURCE ${STA_SWIG_FILE} PROPERTY CPLUSPLUS ON ) -# Ubuntu 18.04 cmake version 3.10.2 does not support the -# COMPILE_OPTIONS and INCLUDE_DIRECTORIES properties, so cram -# them into SWIG_FLAGS for the time being. +# SWIG flags (CMake UseSWIG). set_property(SOURCE ${STA_SWIG_FILE} - PROPERTY SWIG_FLAGS + PROPERTY COMPILE_OPTIONS -module sta - -namespace -prefix sta - -I${STA_HOME} + -namespace + -prefix sta ) set(SWIG_FILES @@ -519,6 +517,9 @@ target_include_directories(sta_swig ${CMAKE_CURRENT_BINARY_DIR}/include/sta ) +# Pass sta_swig include directories to SWIG as -I (same paths as the C++ build). +set_property(TARGET sta_swig PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE) + ########################################################### # Library ########################################################### From 46472e0eedbb854cd10ed41334eb275bd21dca1f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sun, 29 Mar 2026 15:19:00 -0700 Subject: [PATCH 121/181] regresssion update failures/diffs Signed-off-by: James Cherry --- test/regression.tcl | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/test/regression.tcl b/test/regression.tcl index dcf0057b8..8afefbf4d 100755 --- a/test/regression.tcl +++ b/test/regression.tcl @@ -177,7 +177,6 @@ proc run_tests {} { } } write_failure_file - write_diff_file } proc run_test { test } { @@ -268,6 +267,8 @@ proc run_tests_parallel {} { vwait reg_parallel_job_done } } + # update results/failures and results/diffs + write_failure_file } } @@ -434,23 +435,23 @@ proc test_failed { test reason } { } proc write_failure_file {} { - global failure_file failed_tests + global failure_file failed_tests failed_tests_summery + global diff_file diff_options - set ch [open $failure_file "w"] + set fail_ch [open $failure_file "a"] foreach test $failed_tests { - puts $ch $test - } - close $ch -} + if { ![info exists failed_tests_summery($test)] } { + puts $fail_ch $test -proc write_diff_file {} { - global diff_file diff_options failed_tests + # Append diff to results/diffs + set log_file [test_log_file $test] + set ok_file [test_ok_file $test] + catch [concat exec diff $diff_options $ok_file $log_file >> $diff_file] - foreach test $failed_tests { - set log_file [test_log_file $test] - set ok_file [test_ok_file $test] - catch [concat exec diff $diff_options $ok_file $log_file >> $diff_file] + set failed_tests_summery($test) 1 + } } + close $fail_ch } # Error messages can be found in "valgrind/memcheck/mc_errcontext.c". From 538db6211ff174a5e63b64eb0cbf08023336bab7 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 30 Mar 2026 09:34:03 -0700 Subject: [PATCH 122/181] filter_objects via Akash Levy resolves #399 --- CMakeLists.txt | 1 + include/sta/FilterObjects.hh | 106 ++++++++ sdc/FilterObjects.cc | 508 +++++++++++++++++++++++++++++++++++ sdc/Sdc.i | 142 +++++----- sdc/Sdc.tcl | 52 +--- tcl/Sta.tcl | 2 +- test/get_filter.ok | 8 +- test/get_filter.tcl | 16 +- 8 files changed, 712 insertions(+), 123 deletions(-) create mode 100644 include/sta/FilterObjects.hh create mode 100644 sdc/FilterObjects.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f5ef070f..2f3495ba6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,7 @@ set(STA_SOURCE sdc/DeratingFactors.cc sdc/DisabledPorts.cc sdc/ExceptionPath.cc + sdc/FilterObjects.cc sdc/InputDrive.cc sdc/PinPair.cc sdc/PortDelay.cc diff --git a/include/sta/FilterObjects.hh b/include/sta/FilterObjects.hh new file mode 100644 index 000000000..bbabd4b02 --- /dev/null +++ b/include/sta/FilterObjects.hh @@ -0,0 +1,106 @@ +// 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 +#include +#include + +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "StringUtil.hh" + +namespace sta { + +class Sta; +class Report; + +PortSeq +filterPorts(std::string_view filter_expression, + PortSeq *objects, + bool bool_props_as_int, + Sta *sta); + +InstanceSeq +filterInstances(std::string_view filter_expression, + InstanceSeq *objects, + bool bool_props_as_int, + Sta *sta); + +PinSeq +filterPins(std::string_view filter_expression, + PinSeq *objects, + bool bool_props_as_int, + Sta *sta); + +NetSeq +filterNets(std::string_view filter_expression, + NetSeq *objects, + bool bool_props_as_int, + Sta *sta); + +ClockSeq +filterClocks(std::string_view filter_expression, + ClockSeq *objects, + bool bool_props_as_int, + Sta *sta); + +LibertyCellSeq +filterLibCells(std::string_view filter_expression, + LibertyCellSeq *objects, + bool bool_props_as_int, + Sta *sta); + +LibertyPortSeq +filterLibPins(std::string_view filter_expression, + LibertyPortSeq *objects, + bool bool_props_as_int, + Sta *sta); + +LibertyLibrarySeq +filterLibertyLibraries(std::string_view filter_expression, + LibertyLibrarySeq *objects, + bool bool_props_as_int, + Sta *sta); + +EdgeSeq +filterTimingArcs(std::string_view filter_expression, + EdgeSeq *objects, + bool bool_props_as_int, + Sta *sta); + +PathEndSeq +filterPathEnds(std::string_view filter_expression, + PathEndSeq *objects, + bool bool_props_as_int, + Sta *sta); + +// For FilterExpr unit tests. +StringSeq +filterExprToPostfix(std::string_view expr, + bool bool_props_as_int, + Report *report); + +} // namespace diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc new file mode 100644 index 000000000..2e18473e1 --- /dev/null +++ b/sdc/FilterObjects.cc @@ -0,0 +1,508 @@ +// 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 "FilterObjects.hh" + +#include +#include +#include +#include + +#include "Property.hh" +#include "PatternMatch.hh" +#include "Sta.hh" + +namespace sta { + +class FilterExpr +{ +public: + struct Token + { + enum class Kind { + skip = 0, + predicate, + op_lparen, + op_rparen, + op_or, + op_and, + op_inv, + defined, + undefined + }; + + Token(std::string text, + Kind kind); + + std::string text; + Kind kind; + }; + + struct PredicateToken : public Token + { + PredicateToken(std::string property, + std::string op, + std::string arg); + + std::string property; + std::string op; + std::string arg; + }; + + FilterExpr(std::string_view expression, + Report *report); + + std::vector> postfix(bool bool_props_as_int); +private: + std::vector> lex(bool bool_props_as_int); + std::vector> shuntingYard(std::vector> &infix); + + std::string raw_; + Report *report_; +}; + +FilterExpr::Token::Token(std::string text, + Token::Kind kind) : + text (text), + kind(kind) +{ +} + +FilterExpr::PredicateToken::PredicateToken(std::string property, + std::string op, + std::string arg) : + Token(property + " " + op + " " + arg, + Token::Kind::predicate), + property(property), op(op), arg(arg) +{ +} + +FilterExpr::FilterExpr(std::string_view expression, + Report *report) : + raw_(expression), + report_(report) +{ +} + +std::vector> +FilterExpr::postfix(bool bool_props_as_int) +{ + auto infix = lex(bool_props_as_int); + return shuntingYard(infix); +} + +std::vector> +FilterExpr::lex(bool bool_props_as_int) +{ + std::vector> token_regexes = { + {std::regex("^\\s+"), Token::Kind::skip}, + {std::regex("^defined\\(([a-zA-Z_]+)\\)"), Token::Kind::defined}, + {std::regex("^undefined\\(([a-zA-Z_]+)\\)"), Token::Kind::undefined}, + {std::regex("^@?([a-zA-Z_]+) *((==|!=|=~|!~) *([0-9a-zA-Z_\\/$\\[\\]*?]+))?"), Token::Kind::predicate}, + {std::regex("^(&&)"), Token::Kind::op_and}, + {std::regex("^(\\|\\|)"), Token::Kind::op_or}, + {std::regex("^(!)"), Token::Kind::op_inv}, + {std::regex("^(\\()"), Token::Kind::op_lparen}, + {std::regex("^(\\))"), Token::Kind::op_rparen}, + }; + + std::vector> result; + const char* ptr = &raw_[0]; + bool match = false; + while (*ptr != '\0') { + match = false; + for (auto& [regex, kind]: token_regexes) { + std::cmatch token_match; + if (std::regex_search(ptr, token_match, regex)) { + if (kind == Token::Kind::predicate) { + std::string property = token_match[1].str(); + + // The default operation on a predicate if an op and arg are + // omitted is 'arg == 1' / 'arg == true'. + std::string op = "=="; + std::string arg = (bool_props_as_int ? "1" : "true"); + + if (token_match[2].length() != 0) { + op = token_match[3].str(); + arg = token_match[4].str(); + } + result.push_back(std::make_unique(property, op, arg)); + } + else if (kind == Token::Kind::defined) + result.push_back(std::make_unique(token_match[1].str(), kind)); + else if (kind == Token::Kind::undefined) + result.push_back(std::make_unique(token_match[1].str(), kind)); + else if (kind != Token::Kind::skip) + result.push_back(std::make_unique(std::string(ptr,token_match.length()), + kind)); + ptr += token_match.length(); + match = true; + break; + }; + } + if (!match) + report_->error(2600, "-filter parsing failed at '{}'.", ptr); + } + return result; +} + +std::vector> +FilterExpr::shuntingYard(std::vector> &infix) +{ + std::vector> output; + std::stack> operator_stack; + + for (auto &token : infix) { + switch (token->kind) { + case Token::Kind::predicate: + output.push_back(std::move(token)); + break; + case Token::Kind::op_or: + [[fallthrough]]; + case Token::Kind::op_and: + // The operators' enum values are ascending by precedence: + // inv > and > or + while (operator_stack.size() + && operator_stack.top()->kind > token->kind) { + output.push_back(std::move(operator_stack.top())); + operator_stack.pop(); + } + operator_stack.push(std::move(token)); + break; + case Token::Kind::op_inv: + // Unary with highest precedence, no need for the while loop. + operator_stack.push(std::move(token)); + break; + case Token::Kind::defined: + operator_stack.push(std::move(token)); + break; + case Token::Kind::undefined: + operator_stack.push(std::move(token)); + break; + case Token::Kind::op_lparen: + operator_stack.push(std::move(token)); + break; + case Token::Kind::op_rparen: + if (operator_stack.empty()) + report_->error(2601, "-filter extraneous )."); + while (operator_stack.size() + && operator_stack.top()->kind != Token::Kind::op_lparen) { + output.push_back(std::move(operator_stack.top())); + operator_stack.pop(); + if (operator_stack.empty()) + report_->error(2602, "-filter extraneous )."); + } + // Guaranteed to be lparen at this point. + operator_stack.pop(); + break; + default: + // Unhandled/skip. + break; + } + } + + while (operator_stack.size()) { + if (operator_stack.top()->kind == Token::Kind::op_lparen) + report_->error(2603, "-filter unmatched (."); + output.push_back(std::move(operator_stack.top())); + operator_stack.pop(); + } + + return output; +} + +//////////////////////////////////////////////////////////////// + +template std::set +filterObjects(const char *property, + const char *op, + const char *pattern, + std::set &all, + Sta *sta) +{ + Properties &properties = sta->properties(); + Network *network = sta->network(); + auto filtered_objects = std::set(); + bool exact_match = stringEq(op, "=="); + bool pattern_match = stringEq(op, "=~"); + bool not_match = stringEq(op, "!="); + bool not_pattern_match = stringEq(op, "!~"); + for (T *object : all) { + PropertyValue value = properties.getProperty(object, property); + std::string prop_str = value.to_string(network); + const char *prop = prop_str.c_str(); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (not_match && !stringEq(prop, pattern)) + || (pattern_match && patternMatch(pattern, prop)) + || (not_pattern_match && !patternMatch(pattern, prop)))) + filtered_objects.insert(object); + } + return filtered_objects; +} + +template std::vector +filterObjects(std::string_view filter_expression, + std::vector *objects, + bool bool_props_as_int, + Sta *sta) +{ + Report *report = sta->report(); + Network *network = sta->network(); + Properties &properties = sta->properties(); + std::vector result; + if (objects) { + std::set all; + for (auto object: *objects) + all.insert(object); + + FilterExpr filter(filter_expression, report); + auto postfix = filter.postfix(bool_props_as_int); + std::stack> eval_stack; + for (auto &token : postfix) { + if (token->kind == FilterExpr::Token::Kind::op_or) { + if (eval_stack.size() < 2) + report->error(2604, "-filter logical OR requires at least two operands."); + auto arg0 = eval_stack.top(); + eval_stack.pop(); + auto arg1 = eval_stack.top(); + eval_stack.pop(); + auto union_result = std::set(); + std::set_union(arg0.cbegin(), arg0.cend(), arg1.cbegin(), arg1.cend(), + std::inserter(union_result, union_result.begin())); + eval_stack.push(union_result); + } + else if (token->kind == FilterExpr::Token::Kind::op_and) { + if (eval_stack.size() < 2) { + report->error(2605, "-filter logical AND requires two operands."); + } + auto arg0 = eval_stack.top(); + eval_stack.pop(); + auto arg1 = eval_stack.top(); + eval_stack.pop(); + auto intersection_result = std::set(); + std::set_intersection(arg0.cbegin(), arg0.cend(), + arg1.cbegin(), arg1.cend(), + std::inserter(intersection_result, + intersection_result.begin())); + eval_stack.push(intersection_result); + } + else if (token->kind == FilterExpr::Token::Kind::op_inv) { + if (eval_stack.size() < 1) { + report->error(2606, "-filter NOT missing operand."); + } + auto arg0 = eval_stack.top(); + eval_stack.pop(); + + auto difference_result = std::set(); + std::set_difference(all.cbegin(), all.cend(), + arg0.cbegin(), arg0.cend(), + std::inserter(difference_result, + difference_result.begin())); + eval_stack.push(difference_result); + } + else if (token->kind == FilterExpr::Token::Kind::defined + || token->kind == FilterExpr::Token::Kind::undefined) { + bool should_be_defined = + (token->kind == FilterExpr::Token::Kind::defined); + auto result = std::set(); + for (auto object : all) { + PropertyValue value = properties.getProperty(object, token->text); + bool is_defined = false; + switch (value.type()) { + case PropertyValue::Type::float_: + is_defined = value.floatValue() != 0; + break; + case PropertyValue::Type::bool_: + is_defined = value.boolValue(); + break; + case PropertyValue::Type::string: + case PropertyValue::Type::liberty_library: + case PropertyValue::Type::liberty_cell: + case PropertyValue::Type::liberty_port: + case PropertyValue::Type::library: + case PropertyValue::Type::cell: + case PropertyValue::Type::port: + case PropertyValue::Type::instance: + case PropertyValue::Type::pin: + case PropertyValue::Type::net: + case PropertyValue::Type::clk: + is_defined = value.to_string(network) != ""; + break; + case PropertyValue::Type::none: + is_defined = false; + break; + case PropertyValue::Type::pins: + is_defined = value.pins()->size() > 0; + break; + case PropertyValue::Type::clks: + is_defined = value.clocks()->size() > 0; + break; + case PropertyValue::Type::paths: + is_defined = value.paths()->size() > 0; + break; + case PropertyValue::Type::pwr_activity: + is_defined = value.pwrActivity().isSet(); + break; + } + if (is_defined == should_be_defined) { + result.insert(object); + } + } + eval_stack.push(result); + } + else if (token->kind == FilterExpr::Token::Kind::predicate) { + auto *predicate_token = + static_cast(token.get()); + auto result = filterObjects(predicate_token->property.c_str(), + predicate_token->op.c_str(), + predicate_token->arg.c_str(), + all, sta); + eval_stack.push(result); + } + } + if (eval_stack.size() == 0) + report->error(2607, "-filter expression is empty."); + if (eval_stack.size() > 1) + // huh? + report->error(2608, "-filter expression evaluated to multiple sets."); + auto result_set = eval_stack.top(); + result.resize(result_set.size()); + std::copy(result_set.begin(), result_set.end(), result.begin()); + std::map objects_i; + for (size_t i = 0; i < objects->size(); ++i) + objects_i[objects->at(i)] = i; + std::sort(result.begin(), result.end(), + [&](T* a, T* b) { + return objects_i[a] < objects_i[b]; + }); + delete objects; + } + return result; +} + +PortSeq +filterPorts(std::string_view filter_expression, + PortSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +InstanceSeq +filterInstances(std::string_view filter_expression, + InstanceSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +PinSeq +filterPins(std::string_view filter_expression, + PinSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +NetSeq +filterNets(std::string_view filter_expression, + NetSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +ClockSeq +filterClocks(std::string_view filter_expression, + ClockSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +LibertyCellSeq +filterLibCells(std::string_view filter_expression, + LibertyCellSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +LibertyPortSeq +filterLibPins(std::string_view filter_expression, + LibertyPortSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +LibertyLibrarySeq +filterLibertyLibraries(std::string_view filter_expression, + LibertyLibrarySeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +EdgeSeq +filterTimingArcs(std::string_view filter_expression, + EdgeSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +PathEndSeq +filterPathEnds(std::string_view filter_expression, + PathEndSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +StringSeq +filterExprToPostfix(std::string_view expr, + bool bool_props_as_int, + Report *report) +{ + FilterExpr filter(expr, report); + auto postfix = filter.postfix(bool_props_as_int); + StringSeq result; + for (auto &token : postfix) + result.push_back(token->text); + return result; +} + +} // namespace sta diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 411d8f493..eccd234ec 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -34,6 +34,7 @@ #include "Clock.hh" #include "PortDelay.hh" #include "Property.hh" +#include "FilterObjects.hh" #include "Sta.hh" using namespace sta; @@ -1490,114 +1491,103 @@ find_register_output_pins(ClockSet *clks, //////////////////////////////////////////////////////////////// -template std::vector -filter_objects(std::string_view property, - std::string_view op, - std::string_view pattern, - std::vector *objects) -{ - std::vector filtered_objects; - if (objects) { - Sta *sta = Sta::sta(); - Properties &properties = sta->properties(); - bool exact_match = op == "=="; - bool pattern_match = op == "=~"; - bool not_match = op == "!="; - bool not_pattern_match = op == "!~"; - for (T *object : *objects) { - PropertyValue value(properties.getProperty(object, property)); - std::string prop_value = value.to_string(sta->network()); - if (!prop_value.empty() - && ((exact_match && prop_value == pattern) - || (not_match && prop_value != pattern) - || (pattern_match && patternMatch(pattern, prop_value)) - || (not_pattern_match && !patternMatch(pattern, prop_value)))) - filtered_objects.push_back(object); - } - delete objects; - } - return filtered_objects; -} - PortSeq -filter_ports(std::string_view property, - std::string_view op, - std::string_view pattern, - PortSeq *ports) +filter_ports(const char *filter_expression, + PortSeq *ports, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, ports); + sta::Sta *sta = Sta::sta(); + return filterPorts(filter_expression, ports, bool_props_as_int, sta); } InstanceSeq -filter_insts(std::string_view property, - std::string_view op, - std::string_view pattern, - InstanceSeq *insts) +filter_insts(const char *filter_expression, + InstanceSeq *insts, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, insts); + sta::Sta *sta = Sta::sta(); + return filterInstances(filter_expression, insts, bool_props_as_int, sta); } PinSeq -filter_pins(std::string_view property, - std::string_view op, - std::string_view pattern, - PinSeq *pins) +filter_pins(const char *filter_expression, + PinSeq *pins, + bool bool_props_as_int) +{ + sta::Sta *sta = Sta::sta(); + return filterPins(filter_expression, pins, bool_props_as_int, sta); +} + +NetSeq +filter_nets(const char *filter_expression, + NetSeq *nets, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, pins); + sta::Sta *sta = Sta::sta(); + return filterNets(filter_expression, nets, bool_props_as_int, sta); } ClockSeq -filter_clocks(std::string_view property, - std::string_view op, - std::string_view pattern, - ClockSeq *clocks) +filter_clocks(const char *filter_expression, + ClockSeq *clocks, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, clocks); + sta::Sta *sta = Sta::sta(); + return filterClocks(filter_expression, clocks, bool_props_as_int, sta); } LibertyCellSeq -filter_lib_cells(std::string_view property, - std::string_view op, - std::string_view pattern, - LibertyCellSeq *cells) +filter_lib_cells(const char *filter_expression, + LibertyCellSeq *cells, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, cells); + sta::Sta *sta = Sta::sta(); + return filterLibCells(filter_expression, cells, bool_props_as_int, sta); } LibertyPortSeq -filter_lib_pins(std::string_view property, - std::string_view op, - std::string_view pattern, - LibertyPortSeq *pins) +filter_lib_pins(const char *filter_expression, + LibertyPortSeq *pins, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, pins); + sta::Sta *sta = Sta::sta(); + return filterLibPins(filter_expression, pins, bool_props_as_int, sta); } LibertyLibrarySeq -filter_liberty_libraries(std::string_view property, - std::string_view op, - std::string_view pattern, - LibertyLibrarySeq *libs) +filter_liberty_libraries(const char *filter_expression, + LibertyLibrarySeq *libs, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, libs); + sta::Sta *sta = Sta::sta(); + return filterLibertyLibraries(filter_expression, libs, bool_props_as_int, sta); } -NetSeq -filter_nets(std::string_view property, - std::string_view op, - std::string_view pattern, - NetSeq *nets) +EdgeSeq +filter_timing_arcs(const char *filter_expression, + EdgeSeq *edges, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, nets); + sta::Sta *sta = Sta::sta(); + return filterTimingArcs(filter_expression, edges, bool_props_as_int, sta); } -EdgeSeq -filter_timing_arcs(std::string_view property, - std::string_view op, - std::string_view pattern, - EdgeSeq *edges) +PathEndSeq +filter_path_ends(const char *filter_expression, + PathEndSeq *path_ends, + bool bool_props_as_int) +{ + sta::Sta *sta = Sta::sta(); + return filterPathEnds(filter_expression, path_ends, bool_props_as_int, sta); +} + +// For FilterExpr unit tests. +StringSeq +filter_expr_to_postfix(const char* expr, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, edges); + Report *report = Sta::sta()->report(); + return filterExprToPostfix(expr, bool_props_as_int, report); } //////////////////////////////////////////////////////////////// diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index 26196d2fc..dd5374cdc 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -315,42 +315,6 @@ proc current_design { {design ""} } { ################################################################ -# Generic get_* filter. -proc filter_objs { filter objects filter_function object_type } { - # Regexp for attr op arg (e.g., full_name =~ *blk*) - set filter_regexp_op {@?([a-zA-Z_]+) *(==|!=|=~|!~) *([0-9a-zA-Z_\*]+)} - # Regexp for bool attr (e.g., is_hierarchical) - anchored for standalone use - set filter_regexp_bool {^@?([a-zA-Z_]+)$} - # Regexp for wildcard attr (e.g., full_name *blk*) - set filter_regexp_wild_op {@?([a-zA-Z_]+) *(.+) *([0-9a-zA-Z_\*]+)} - # Regexp for term in compound expression (no anchors) - set filter_regexp_term {@?([a-zA-Z_]+)( *(==|!=|=~|!~) *([0-9a-zA-Z_\*]+))?} - set filter_or_regexp "($filter_regexp_term) *\\|\\| *($filter_regexp_term)" - set filter_and_regexp "($filter_regexp_term) *&& *($filter_regexp_term)" - set filtered_objects {} - # Ignore sub-exprs in filter_regexp for expr2 match var. - if { [regexp $filter_or_regexp $filter ignore expr1 ignore ignore ignore ignore expr2] } { - set filtered_objects1 [filter_objs $expr1 $objects $filter_function $object_type] - set filtered_objects2 [filter_objs $expr2 $objects $filter_function $object_type] - set filtered_objects [concat $filtered_objects1 $filtered_objects2] - } elseif { [regexp $filter_and_regexp $filter ignore expr1 ignore ignore ignore ignore expr2] } { - set filtered_objects [filter_objs $expr1 $objects $filter_function $object_type] - set filtered_objects [filter_objs $expr2 $filtered_objects $filter_function $object_type] - } elseif { [regexp $filter_regexp_op $filter ignore attr_name op arg] } { - set filtered_objects [$filter_function $attr_name $op $arg $objects] - } elseif { [regexp $filter_regexp_bool $filter ignore attr_name] } { - # Bool property: use ==1 by default. - set filtered_objects [$filter_function $attr_name "==" "1" $objects] - } elseif { [regexp $filter_regexp_wild_op $filter ignore attr_name op arg] } { - sta_error 336 "unknown filter operand." - } else { - sta_error 350 "unsupported $object_type -filter expression." - } - return $filtered_objects -} - -################################################################ - define_cmd_args "get_cells" \ {[-hierarchical] [-hsc separator] [-filter expr]\ [-regexp] [-nocase] [-quiet] [-of_objects objects] [patterns]} @@ -429,7 +393,7 @@ proc get_cells { args } { } } if [info exists keys(-filter)] { - set insts [filter_objs $keys(-filter) $insts filter_insts "instance"] + set insts [filter_insts $keys(-filter) $insts 1] } return $insts } @@ -472,7 +436,7 @@ proc get_clocks { args } { } } if [info exists keys(-filter)] { - set clocks [filter_objs $keys(-filter) $clocks filter_clocks "clock"] + set clocks [filter_clocks $keys(-filter) $clocks 1] } return $clocks } @@ -553,7 +517,7 @@ proc get_lib_cells { args } { } } if [info exists keys(-filter)] { - set cells [filter_objs $keys(-filter) $cells filter_lib_cells "liberty cell"] + set cells [filter_lib_cells $keys(-filter) $cells 1] } return $cells } @@ -657,7 +621,7 @@ proc get_lib_pins { args } { } } if [info exists keys(-filter)] { - set ports [filter_objs $keys(-filter) $ports filter_lib_pins "liberty port"] + set ports [filter_lib_pins $keys(-filter) $ports 1] } return $ports } @@ -707,7 +671,7 @@ proc get_libs { args } { } } if [info exists keys(-filter)] { - set libs [filter_objs $keys(-filter) $libs filter_liberty_libraries "liberty library"] + set libs [filter_liberty_libraries $keys(-filter) $libs 1] } return $libs } @@ -808,7 +772,7 @@ proc get_nets { args } { } } if [info exists keys(-filter)] { - set nets [filter_objs $keys(-filter) $nets filter_nets "net"] + set nets [filter_nets $keys(-filter) $nets 1] } return $nets } @@ -899,7 +863,7 @@ proc get_pins { args } { } } if [info exists keys(-filter)] { - set pins [filter_objs $keys(-filter) $pins filter_pins "pin"] + set pins [filter_pins $keys(-filter) $pins 1] } return $pins } @@ -955,7 +919,7 @@ proc get_ports { args } { } } if [info exists keys(-filter)] { - set ports [filter_objs $keys(-filter) $ports filter_ports "port"] + set ports [filter_ports $keys(-filter) $ports 1] } return $ports } diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index c1ceaf147..f6daed10b 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -300,7 +300,7 @@ proc get_timing_edges_cmd { cmd cmd_args } { cmd_usage_error $cmd } if [info exists keys(-filter)] { - set arcs [filter_objs $keys(-filter) $arcs filter_timing_arcs "timing arc"] + set arcs [filter_timing_arcs $keys(-filter) $arcs 1] } return $arcs } diff --git a/test/get_filter.ok b/test/get_filter.ok index b66978cd3..8bd8c18ae 100644 --- a/test/get_filter.ok +++ b/test/get_filter.ok @@ -53,4 +53,10 @@ in2 [get_ports -filter direction==output *] out [get_cells -filter {name ~= *r1*} *] -Error 336: get_filter.tcl line 48, unknown filter operand. +Error: 2600 -filter parsing failed at '~= *r1*'. +clk1 +clk2 +clk3 +clk1 +clk2 +clk3 diff --git a/test/get_filter.tcl b/test/get_filter.tcl index 7263811e6..d6009a891 100644 --- a/test/get_filter.tcl +++ b/test/get_filter.tcl @@ -5,9 +5,10 @@ link_design top create_clock -name clk -period 500 {clk1 clk2 clk3} create_clock -name vclk -period 1000 -# Test filters for each SDC command +# Test filters for each SDC get_* command. puts {[get_cells -filter liberty_cell==BUFx2_ASAP7_75t_R *]} report_object_full_names [get_cells -filter liberty_cell==BUFx2_ASAP7_75t_R *] + puts {[get_clocks -filter is_virtual==0 *]} report_object_full_names [get_clocks -filter is_virtual==0 *] puts {[get_clocks -filter is_virtual==1 *]} @@ -22,22 +23,28 @@ puts {[get_clocks -filter is_virtual||is_generated *]} report_object_full_names [get_clocks -filter is_virtual||is_generated *] puts {[get_clocks -filter is_virtual==0||is_generated *]} report_object_full_names [get_clocks -filter is_virtual==0||is_generated *] + puts {[get_lib_cells -filter is_buffer==1 *]} report_object_full_names [get_lib_cells -filter is_buffer==1 *] puts {[get_lib_cells -filter is_inverter==0 *]} report_object_full_names [get_lib_cells -filter is_inverter==0 *] + puts {[get_lib_pins -filter direction==input BUFx2_ASAP7_75t_R/*]} report_object_full_names [get_lib_pins -filter direction==input BUFx2_ASAP7_75t_R/*] puts {[get_lib_pins -filter direction==output BUFx2_ASAP7_75t_R/*]} report_object_full_names [get_lib_pins -filter direction==output BUFx2_ASAP7_75t_R/*] + puts {[get_libs -filter name==asap7_small *]} report_object_full_names [get_libs -filter name==asap7_small *] + puts {[get_nets -filter name=~*q *]} report_object_full_names [get_nets -filter name=~*q *] + puts {[get_pins -filter direction==input *]} report_object_full_names [get_pins -filter direction==input *] puts {[get_pins -filter direction==output *]} report_object_full_names [get_pins -filter direction==output *] + puts {[get_ports -filter direction==input *]} report_object_full_names [get_ports -filter direction==input *] puts {[get_ports -filter direction==output *]} @@ -47,3 +54,10 @@ report_object_full_names [get_ports -filter direction==output *] puts {[get_cells -filter {name ~= *r1*} *]} catch {get_cells -filter {name ~= *r1*} *} result puts $result + +# AND pattern match expr +report_object_names [get_ports -filter "direction == input && name =~ clk*" *] +# parens around sub-exprs +report_object_names [get_ports -filter "(direction == input) && (name =~ clk*)" *] + +sta::filter_expr_to_postfix "direction == input && name =~ clk* && is_clock" 1 From 5955958564a3b0ce85defd2b2ea336ea75d447b6 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 30 Mar 2026 11:01:15 -0700 Subject: [PATCH 123/181] findScaleFactorType, findScaleFactorPvt string_view Signed-off-by: James Cherry --- liberty/Liberty.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index ed4717453..b57b60838 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -3031,7 +3031,7 @@ scaleFactorTypeName(ScaleFactorType type) } ScaleFactorType -findScaleFactorType(const char *name) +findScaleFactorType(std::string_view name) { return scale_factor_type_map.find(name, ScaleFactorType::unknown); } @@ -3072,7 +3072,7 @@ EnumNameMap scale_factor_pvt_names = }; ScaleFactorPvt -findScaleFactorPvt(const char *name) +findScaleFactorPvt(std::string_view name) { return scale_factor_pvt_names.find(name, ScaleFactorPvt::unknown); } From 1d4b51623985153a879290195d036d404dc6fda0 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 31 Mar 2026 13:59:59 -0700 Subject: [PATCH 124/181] GraphDelayCalc::findInputArcDelay thread issue Signed-off-by: James Cherry --- dcalc/GraphDelayCalc.cc | 30 +++++++++++++++++------------- include/sta/GraphDelayCalc.hh | 6 ++++-- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index bcfb9cc07..d421665ef 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -426,7 +426,8 @@ GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex, if (from_port == nullptr) from_port = driveCellDefaultFromPort(drvr_cell, to_port); findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf, - from_port, from_slews, to_port, scene, min_max); + from_port, from_slews, to_port, scene, min_max, + arc_delay_calc); } else seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, scene, min_max, @@ -601,7 +602,8 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, float *from_slews, const LibertyPort *to_port, const Scene *scene, - const MinMax *min_max) + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc) { debugPrint(debug_, "delay_calc", 2, " driver cell {} {}", drvr_cell->name(), @@ -610,7 +612,8 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, for (TimingArc *arc : arc_set->arcs()) { if (arc->toEdge()->asRiseFall() == rf) { float from_slew = from_slews[arc->fromEdge()->index()]; - findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, scene, min_max); + findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, scene, min_max, + arc_delay_calc); } } } @@ -626,7 +629,8 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, const TimingArc *arc, float from_slew, const Scene *scene, - const MinMax *min_max) + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc) { debugPrint(debug_, "delay_calc", 3, " {} {} -> {} {} ({})", arc->from()->name(), @@ -640,20 +644,20 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, const Parasitic *parasitic; float load_cap; - parasiticLoad(drvr_pin, drvr_rf, scene, min_max, nullptr, arc_delay_calc_, + parasiticLoad(drvr_pin, drvr_rf, scene, min_max, nullptr, arc_delay_calc, load_cap, parasitic); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult intrinsic_result = - arc_delay_calc_->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr, - load_pin_index_map, scene, min_max); + arc_delay_calc->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr, + load_pin_index_map, scene, min_max); 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); + ArcDcalcResult gate_result = arc_delay_calc->gateDelay(drvr_pin, arc, + Slew(from_slew), load_cap, + parasitic, + load_pin_index_map, + scene, min_max); const ArcDelay &gate_delay = gate_result.gateDelay(); const Slew &gate_slew = gate_result.drvrSlew(); @@ -666,7 +670,7 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); annotateLoadDelays(drvr_vertex, drvr_rf, gate_result, load_pin_index_map, load_delay, false, scene, min_max); - arc_delay_calc_->finishDrvrPin(); + arc_delay_calc->finishDrvrPin(); } } diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 60c12d430..85d198c66 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -174,7 +174,8 @@ protected: float *from_slews, const LibertyPort *to_port, const Scene *scene, - const MinMax *min_max); + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc); LibertyPort *driveCellDefaultFromPort(const LibertyCell *cell, const LibertyPort *to_port); int findPortIndex(const LibertyCell *cell, @@ -184,7 +185,8 @@ protected: const TimingArc *arc, float from_slew, const Scene *scene, - const MinMax *min_max); + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc); void findDriverDelays(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map); From 9eb9edb0b37e58cdb4114a8e8bf83b374f09be4c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 31 Mar 2026 14:20:50 -0700 Subject: [PATCH 125/181] GateTableModel::gateDelay do not clip delays Signed-off-by: James Cherry --- liberty/TableModel.cc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 09f80ee80..676d29afb 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -108,19 +108,18 @@ GateTableModel::gateDelay(const Pvt *pvt, float &gate_delay, float &drvr_slew) const { - if (delay_models_) + if (delay_models_ && delay_models_->model()) gate_delay = findValue(pvt, delay_models_->model(), in_slew, load_cap, 0.0); else gate_delay = 0.0; - if (slew_models_) + if (slew_models_ && slew_models_->model()) { drvr_slew = findValue(pvt, slew_models_->model(), in_slew, load_cap, 0.0); + // Clip negative slews to zero. + if (drvr_slew < 0.0) + drvr_slew = 0.0; + } else drvr_slew = 0.0; - // Clip negative delays and slews to zero. - if (gate_delay < 0.0) - gate_delay = 0.0; - if (drvr_slew < 0.0) - drvr_slew = 0.0; } void From 8c1ebce6955852fe80e353d8d251d4b237cad4eb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 31 Mar 2026 16:37:38 -0700 Subject: [PATCH 126/181] GraphDelayCalc::findInputDriverDelay rm finishDrvrPin Signed-off-by: James Cherry --- dcalc/GraphDelayCalc.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index d421665ef..3057e1d44 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -617,7 +617,6 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, } } } - arc_delay_calc_->finishDrvrPin(); } // Driving cell delay is the load dependent delay, which is the gate From 638ffa57d4648a49143862e1f415f085957007c9 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 31 Mar 2026 16:45:47 -0700 Subject: [PATCH 127/181] cleanup Signed-off-by: James Cherry --- dcalc/DmpCeff.cc | 67 +++++++++++++++++++++-------------------- dcalc/GraphDelayCalc.cc | 48 ++++++++++++++--------------- 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 577bce5c3..6828aa672 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -44,7 +44,6 @@ #include "TimingArc.hh" #include "TableModel.hh" #include "Liberty.hh" -#include "Network.hh" #include "Sdc.hh" #include "Parasitics.hh" #include "ArcDelayCalc.hh" @@ -78,11 +77,11 @@ exp2(double x); class DmpError : public Exception { public: - DmpError(const char *what); - virtual const char *what() const noexcept { return what_; } + DmpError(std::string_view what); + virtual const char *what() const noexcept { return what_.c_str(); } private: - const char *what_; + std::string what_; }; static double @@ -139,9 +138,9 @@ class DmpAlg : public StaState 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. @@ -685,9 +684,9 @@ class DmpCap : public DmpAlg 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. @@ -726,15 +725,15 @@ 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 = {}", units_->capacitanceUnit()->asString(ceff_)); @@ -801,9 +800,9 @@ class DmpPi : public DmpAlg 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; @@ -868,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_); @@ -893,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 { @@ -1127,9 +1126,9 @@ class DmpZeroC2 : public DmpOnePole 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, @@ -1176,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_); @@ -1275,8 +1274,10 @@ newtonRaphson(const int max_iter, all_under_x_tol = false; x[i] += p[i]; } - if (all_under_x_tol) + if (all_under_x_tol) { + eval(); return; + } } throw DmpError("Newton-Raphson max iterations exceeded"); } @@ -1557,8 +1558,8 @@ 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 = {} c2 = {} rpi = {} c1 = {} Rd = {} ({} alg)", units_->timeUnit()->asString(in_slew), @@ -1667,10 +1668,10 @@ DmpCeffDelayCalc::copyState(const StaState *sta) dmp_zero_c2_->copyState(sta); } -DmpError::DmpError(const char *what) : +DmpError::DmpError(std::string_view what) : what_(what) { - // printf("DmpError %s\n", what); + //sta::print(stdout, "DmpError {}\n", what); } // This saves about 2.5% in overall run time on designs with SPEF. diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 3057e1d44..f8984474a 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -1601,41 +1601,41 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, for (Scene *scene : scenes_) { for (const MinMax *min_max : MinMax::range()) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { - const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, + 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); - debugPrint(debug_, "delay_calc", 3, + debugPrint(debug_, "delay_calc", 3, " {} {} -> {} {} ({}) scene:{}/{}", - arc_set->from()->name(), - arc->fromEdge()->to_string(), - arc_set->to()->name(), - arc->toEdge()->to_string(), - arc_set->role()->to_string(), + arc_set->from()->name(), + arc->fromEdge()->to_string(), + arc_set->to()->name(), + arc->toEdge()->to_string(), + arc_set->role()->to_string(), scene->name(), min_max->to_string()); - debugPrint(debug_, "delay_calc", 3, - " from_slew = {} to_slew = {}", - delayAsString(from_slew, this), - delayAsString(to_slew, this)); - float related_out_cap = 0.0; - if (related_out_pin) + debugPrint(debug_, "delay_calc", 3, + " from_slew = {} to_slew = {}", + delayAsString(from_slew, this), + delayAsString(to_slew, this)); + float related_out_cap = 0.0; + if (related_out_pin) related_out_cap = loadCap(related_out_pin, to_rf,scene,min_max, arc_delay_calc); - ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew, - to_slew, related_out_cap, + ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew, + to_slew, related_out_cap, scene, min_max); - debugPrint(debug_, "delay_calc", 3, - " check_delay = {}", - delayAsString(check_delay, this)); - graph_->setArcDelay(edge, arc, ap_index, check_delay); - delay_changed = true; - arc_delay_calc_->finishDrvrPin(); - } + debugPrint(debug_, "delay_calc", 3, + " check_delay = {}", + delayAsString(check_delay, this)); + graph_->setArcDelay(edge, arc, ap_index, check_delay); + delay_changed = true; + arc_delay_calc_->finishDrvrPin(); + } + } } } } - } if (delay_changed && observer_) observer_->checkDelayChangedTo(to_vertex); From 70659c2328dcdc64b8498813bf148e15ee4642e0 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 31 Mar 2026 21:03:55 -0700 Subject: [PATCH 128/181] PathVisitor::visitFaninPaths filter check edges Signed-off-by: James Cherry --- search/Bfs.cc | 2 +- search/ClkSkew.cc | 2 +- search/PathGroup.cc | 2 +- search/Search.cc | 20 +++++++++----------- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/search/Bfs.cc b/search/Bfs.cc index 559cabb7d..d13f9700d 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -194,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](size_t) { for (size_t i = from; i < to; i++) { Vertex *vertex = level_vertices[i]; if (vertex) { diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 97626ca59..89eab1ea4 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -196,7 +196,7 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, std::vector partial_skews(thread_count_); for (Vertex *src_vertex : graph_->regClkVertices()) { if (hasClkPaths(src_vertex)) { - dispatch_queue_->dispatch([this, src_vertex, &partial_skews](int i) { + dispatch_queue_->dispatch([this, src_vertex, &partial_skews](size_t i) { findClkSkewFrom(src_vertex, partial_skews[i]); }); } diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 1d3e6cd05..e2e20a809 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -1025,7 +1025,7 @@ PathGroups::makeGroupPathEnds(VertexSet &endpoints, MakeEndpointPathEnds(visitor, Scene::sceneSet(scenes), min_max, this)); for (const auto endpoint : endpoints) { - dispatch_queue_->dispatch( [endpoint, &visitors](int i) + dispatch_queue_->dispatch( [endpoint, &visitors](size_t i) { visitors[i].visit(endpoint); } ); } dispatch_queue_->finishTasks(); diff --git a/search/Search.cc b/search/Search.cc index f1a9658c1..246d5dd6e 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1102,9 +1102,7 @@ Search::findArrivalsSeed() //////////////////////////////////////////////////////////////// ArrivalVisitor::ArrivalVisitor(const StaState *sta) : - PathVisitor(nullptr, - false, - sta) + PathVisitor(nullptr, false, sta) { init0(); init(true, false, nullptr); @@ -1114,9 +1112,7 @@ 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); @@ -1997,11 +1993,13 @@ PathVisitor::visitFaninPaths(Vertex *to_vertex) VertexInEdgeIterator edge_iter(to_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - const Pin *from_pin = from_vertex->pin(); - const Pin *to_pin = to_vertex->pin(); - if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) - break; + if (!edge->role()->isTimingCheck()) { + Vertex *from_vertex = edge->from(graph_); + const Pin *from_pin = from_vertex->pin(); + const Pin *to_pin = to_vertex->pin(); + if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) + break; + } } } From 4180288868d65a80f2580de4443c085e780e120f Mon Sep 17 00:00:00 2001 From: Mike Inouye Date: Thu, 2 Apr 2026 15:59:01 -0700 Subject: [PATCH 129/181] Add string_view header and sort includes in ContainerHelpers.hh (#413) --- include/sta/ContainerHelpers.hh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/sta/ContainerHelpers.hh b/include/sta/ContainerHelpers.hh index 010d30e71..9c5aeae47 100644 --- a/include/sta/ContainerHelpers.hh +++ b/include/sta/ContainerHelpers.hh @@ -24,14 +24,15 @@ #pragma once -#include -#include // for std::declval +#include +#include #include +#include #include +#include +#include +#include // for std::declval #include -#include -#include -#include namespace sta { From e8218f2db1f2fbdc759e41b4f1dab3ab068cc8b3 Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Fri, 3 Apr 2026 20:02:25 +0200 Subject: [PATCH 130/181] Tcl 9 compatibility changes (#414) * Provide close2proc function to prevent tcl9 from crashing. Tcl 9 does not test if the close2Proc function pointer is non-null, but calls it unconditionally: https://github.com/tcltk/tcl/blob/core-9-0-3/generic/tclIO.c#L384 So we need to provide a non-null function pointer for our code to not crash with Tcl9. Use the same implementation as the previous close channel had. Signed-off-by: Henner Zeller * Use non-deprecated trace add variable syntax. In modern tcl, `trace variable` is now i`trace add variable`, and `"rw"` should be spelled out as `{read write}` There were backwards compatible forms in Tcl 8.x but now loudly complains in Tcl 9 Signed-off-by: Henner Zeller * Use `Tcl_Size` for all tcl functions returning sizes. This is the type the Tcl-API provides in its prototypes and starting from Tcl9 this typedef actually changes from `int` to `long`, so will no longer compile when passing an `int*`. So whenever we get a return value of this type, use the correct typedef to declare the variable. This makes it forward and backward compatible. Signed-off-by: Henner Zeller * Address review comments: compare with `read`/`write` not `r`, `w` Signed-off-by: Henner Zeller --------- Signed-off-by: Henner Zeller --- tcl/StaTclTypes.i | 50 +++++++++++++++++++++---------------------- tcl/TclTypeHelpers.cc | 10 ++++----- tcl/Variables.tcl | 48 ++++++++++++++++++++--------------------- util/ReportTcl.cc | 33 ++++++++++++++++++++++------ 4 files changed, 80 insertions(+), 61 deletions(-) diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 9157a3fc3..afbc74f58 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -280,7 +280,7 @@ using namespace sta; } %typemap(in) std::string_view { - int length; + Tcl_Size length; const char *str = Tcl_GetStringFromObj($input, &length); $1 = std::string_view(str, length); } @@ -415,7 +415,7 @@ using namespace sta; } %typemap(in) Transition* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); Transition *tr = Transition::find(std::string_view(arg, length)); if (tr == nullptr) { @@ -433,7 +433,7 @@ using namespace sta; } %typemap(in) RiseFall* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); const RiseFall *rf = RiseFall::find(std::string_view(arg, length)); if (rf == nullptr) { @@ -451,7 +451,7 @@ using namespace sta; } %typemap(in) RiseFallBoth* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); const RiseFallBoth *rf = RiseFallBoth::find(std::string_view(arg, length)); if (rf == nullptr) { @@ -469,7 +469,7 @@ using namespace sta; } %typemap(in) PortDirection* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); PortDirection *dir = PortDirection::find(arg); if (dir == nullptr) { @@ -481,7 +481,7 @@ using namespace sta; } %typemap(in) TimingRole* { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); const TimingRole *role = TimingRole::find(arg); if (role) @@ -498,7 +498,7 @@ using namespace sta; } %typemap(in) LogicValue { - int length; + Tcl_Size length; std::string arg = Tcl_GetStringFromObj($input, &length); if (arg == "0" || stringEqual(arg, "zero")) $1 = LogicValue::zero; @@ -517,7 +517,7 @@ using namespace sta; } %typemap(in) AnalysisType { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "single")) $1 = AnalysisType::single; @@ -831,7 +831,7 @@ using namespace sta; } %typemap(in) MinMax* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. MinMax *min_max = const_cast(MinMax::find(arg)); @@ -852,7 +852,7 @@ using namespace sta; } %typemap(in) MinMaxAll* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. MinMaxAll *min_max = const_cast(MinMaxAll::find(arg)); @@ -865,7 +865,7 @@ using namespace sta; } %typemap(in) MinMaxAllNull* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "NULL")) $1 = nullptr; @@ -887,7 +887,7 @@ using namespace sta; // SetupHold is typedef'd to MinMax. %typemap(in) const SetupHold* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. if (stringEqual(arg, "hold") @@ -904,7 +904,7 @@ using namespace sta; // SetupHoldAll is typedef'd to MinMaxAll. %typemap(in) const SetupHoldAll* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. if (stringEqual(arg, "hold") @@ -925,7 +925,7 @@ using namespace sta; // EarlyLate is typedef'd to MinMax. %typemap(in) const EarlyLate* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. EarlyLate *early_late = const_cast(EarlyLate::find(arg)); @@ -939,7 +939,7 @@ using namespace sta; // EarlyLateAll is typedef'd to MinMaxAll. %typemap(in) const EarlyLateAll* { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); // Swig is retarded and drops const on args. EarlyLateAll *early_late = const_cast(EarlyLateAll::find(arg)); @@ -952,7 +952,7 @@ using namespace sta; } %typemap(in) TimingDerateType { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "net_delay")) $1 = TimingDerateType::net_delay; @@ -967,7 +967,7 @@ using namespace sta; } %typemap(in) TimingDerateCellType { - int length; + Tcl_Size length; char *arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "cell_delay")) $1 = TimingDerateCellType::cell_delay; @@ -980,7 +980,7 @@ using namespace sta; } %typemap(in) PathClkOrData { - int length; + Tcl_Size length; std::string arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "clk")) $1 = PathClkOrData::clk; @@ -993,7 +993,7 @@ using namespace sta; } %typemap(in) ReportSortBy { - int length; + Tcl_Size length; std::string arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "group")) $1 = sort_by_group; @@ -1006,7 +1006,7 @@ using namespace sta; } %typemap(in) ReportPathFormat { - int length; + Tcl_Size length; std::string arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "full")) $1 = ReportPathFormat::full; @@ -1188,7 +1188,7 @@ using namespace sta; if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK && argc > 0) { for (int i = 0; i < argc; i++) { - int length; + Tcl_Size length; const char *mode_name = Tcl_GetStringFromObj(argv[i], &length); Mode *mode = sta->findMode(mode_name); if (mode) @@ -1215,7 +1215,7 @@ using namespace sta; %typemap(in) Scene* { sta::Sta *sta = Sta::sta(); - int length; + Tcl_Size length; std::string scene_name = Tcl_GetStringFromObj($input, &length); // parse_scene_or_all support depreated 11/21/2025 if (scene_name == "NULL") @@ -1248,7 +1248,7 @@ using namespace sta; if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK && argc > 0) { for (int i = 0; i < argc; i++) { - int length; + Tcl_Size length; const char *scene_name = Tcl_GetStringFromObj(argv[i], &length); Scene *scene = sta->findScene(scene_name); if (scene) @@ -1274,7 +1274,7 @@ using namespace sta; } %typemap(in) PropertyValue { - int length; + Tcl_Size length; const char *arg = Tcl_GetStringFromObj($input, &length); $1 = PropertyValue(arg); } @@ -1411,7 +1411,7 @@ using namespace sta; } %typemap(in) CircuitSim { - int length; + Tcl_Size length; std::string arg = Tcl_GetStringFromObj($input, &length); if (stringEqual(arg, "hspice")) $1 = CircuitSim::hspice; diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index 48ccbf57a..bcd4f2640 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -40,7 +40,7 @@ tclListStringSeq(Tcl_Obj *const source, StringSeq seq; if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { for (int i = 0; i < argc; i++) { - int length; + Tcl_Size length; const char *str = Tcl_GetStringFromObj(argv[i], &length); seq.push_back(str); } @@ -58,7 +58,7 @@ tclListStringSeqPtr(Tcl_Obj *const source, if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { StringSeq *seq = new StringSeq; for (int i = 0; i < argc; i++) { - int length; + Tcl_Size length; const char *str = Tcl_GetStringFromObj(argv[i], &length); seq->push_back(str); } @@ -78,7 +78,7 @@ tclListStringSet(Tcl_Obj *const source, if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { StringSet *set = new StringSet; for (int i = 0; i < argc; i++) { - int length; + Tcl_Size length; const char *str = Tcl_GetStringFromObj(argv[i], &length); set->insert(str); } @@ -183,11 +183,11 @@ arcDcalcArgTcl(Tcl_Obj *obj, { Sta *sta = Sta::sta(); sta->ensureGraph(); - int list_argc; + Tcl_Size list_argc; Tcl_Obj **list_argv; if (Tcl_ListObjGetElements(interp, obj, &list_argc, &list_argv) == TCL_OK) { const char *input_delay = "0.0"; - int length; + Tcl_Size length; if (list_argc == 6) input_delay = Tcl_GetStringFromObj(list_argv[5], &length); if (list_argc == 5 || list_argc == 6) { diff --git a/tcl/Variables.tcl b/tcl/Variables.tcl index ba8f37b3a..0ea666905 100644 --- a/tcl/Variables.tcl +++ b/tcl/Variables.tcl @@ -33,13 +33,13 @@ namespace eval sta { # Default digits to print after decimal point for reporting commands. set ::sta_report_default_digits 2 -trace variable ::sta_report_default_digits "rw" \ +trace add variable ::sta_report_default_digits {read write} \ sta::trace_report_default_digits proc trace_report_default_digits { name1 name2 op } { global sta_report_default_digits - if { $op == "w" } { + if { $op == "write" } { if { !([string is integer $sta_report_default_digits] \ && $sta_report_default_digits >= 0) } { sta_error 590 "sta_report_default_digits must be a positive integer." @@ -47,7 +47,7 @@ proc trace_report_default_digits { name1 name2 op } { } } -trace variable ::sta_crpr_enabled "rw" \ +trace add variable ::sta_crpr_enabled {read write} \ sta::trace_crpr_enabled proc trace_crpr_enabled { name1 name2 op } { @@ -55,15 +55,15 @@ proc trace_crpr_enabled { name1 name2 op } { crpr_enabled set_crpr_enabled } -trace variable ::sta_crpr_mode "rw" \ +trace add variable ::sta_crpr_mode {read write} \ sta::trace_crpr_mode proc trace_crpr_mode { name1 name2 op } { global sta_crpr_mode - if { $op == "r" } { + if { $op == "read" } { set sta_crpr_mode [crpr_mode] - } elseif { $op == "w" } { + } elseif { $op == "write" } { if { $sta_crpr_mode == "same_pin" || $sta_crpr_mode == "same_transition" } { set_crpr_mode $sta_crpr_mode } else { @@ -72,7 +72,7 @@ proc trace_crpr_mode { name1 name2 op } { } } -trace variable ::sta_cond_default_arcs_enabled "rw" \ +trace add variable ::sta_cond_default_arcs_enabled {read write} \ sta::trace_cond_default_arcs_enabled proc trace_cond_default_arcs_enabled { name1 name2 op } { @@ -80,7 +80,7 @@ proc trace_cond_default_arcs_enabled { name1 name2 op } { cond_default_arcs_enabled set_cond_default_arcs_enabled } -trace variable ::sta_gated_clock_checks_enabled "rw" \ +trace add variable ::sta_gated_clock_checks_enabled {read write} \ sta::trace_gated_clk_checks_enabled proc trace_gated_clk_checks_enabled { name1 name2 op } { @@ -88,7 +88,7 @@ proc trace_gated_clk_checks_enabled { name1 name2 op } { gated_clk_checks_enabled set_gated_clk_checks_enabled } -trace variable ::sta_internal_bidirect_instance_paths_enabled "rw" \ +trace add variable ::sta_internal_bidirect_instance_paths_enabled {read write} \ sta::trace_internal_bidirect_instance_paths_enabled proc trace_internal_bidirect_instance_paths_enabled { name1 name2 op } { @@ -96,7 +96,7 @@ proc trace_internal_bidirect_instance_paths_enabled { name1 name2 op } { bidirect_inst_paths_enabled set_bidirect_inst_paths_enabled } -trace variable ::sta_clock_through_tristate_enabled "rw" \ +trace add variable ::sta_clock_through_tristate_enabled {read write} \ sta::trace_clock_through_tristate_enabled proc trace_clock_through_tristate_enabled { name1 name2 op } { @@ -104,7 +104,7 @@ proc trace_clock_through_tristate_enabled { name1 name2 op } { clk_thru_tristate_enabled set_clk_thru_tristate_enabled } -trace variable ::sta_preset_clear_arcs_enabled "rw" \ +trace add variable ::sta_preset_clear_arcs_enabled {read write} \ sta::trace_preset_clr_arcs_enabled proc trace_preset_clr_arcs_enabled { name1 name2 op } { @@ -112,7 +112,7 @@ proc trace_preset_clr_arcs_enabled { name1 name2 op } { preset_clr_arcs_enabled set_preset_clr_arcs_enabled } -trace variable ::sta_recovery_removal_checks_enabled "rw" \ +trace add variable ::sta_recovery_removal_checks_enabled {read write} \ sta::trace_recovery_removal_checks_enabled proc trace_recovery_removal_checks_enabled { name1 name2 op } { @@ -120,7 +120,7 @@ proc trace_recovery_removal_checks_enabled { name1 name2 op } { recovery_removal_checks_enabled set_recovery_removal_checks_enabled } -trace variable ::sta_dynamic_loop_breaking "rw" \ +trace add variable ::sta_dynamic_loop_breaking {read write} \ sta::trace_dynamic_loop_breaking proc trace_dynamic_loop_breaking { name1 name2 op } { @@ -128,7 +128,7 @@ proc trace_dynamic_loop_breaking { name1 name2 op } { dynamic_loop_breaking set_dynamic_loop_breaking } -trace variable ::sta_input_port_default_clock "rw" \ +trace add variable ::sta_input_port_default_clock {read write} \ sta::trace_input_port_default_clock proc trace_input_port_default_clock { name1 name2 op } { @@ -136,7 +136,7 @@ proc trace_input_port_default_clock { name1 name2 op } { use_default_arrival_clock set_use_default_arrival_clock } -trace variable ::sta_propagate_all_clocks "rw" \ +trace add variable ::sta_propagate_all_clocks {read write} \ sta::trace_propagate_all_clocks proc trace_propagate_all_clocks { name1 name2 op } { @@ -144,7 +144,7 @@ proc trace_propagate_all_clocks { name1 name2 op } { propagate_all_clocks set_propagate_all_clocks } -trace variable ::sta_propagate_gated_clock_enable "rw" \ +trace add variable ::sta_propagate_gated_clock_enable {read write} \ sta::trace_propagate_gated_clock_enable proc trace_propagate_gated_clock_enable { name1 name2 op } { @@ -152,15 +152,15 @@ proc trace_propagate_gated_clock_enable { name1 name2 op } { propagate_gated_clock_enable set_propagate_gated_clock_enable } -trace variable ::sta_pocv_mode "rw" \ +trace add variable ::sta_pocv_mode {read write} \ sta::trace_pocv_mode proc trace_pocv_mode { name1 name2 op } { global sta_pocv_mode - if { $op == "r" } { + if { $op == "read" } { set sta_pocv_mode [pocv_mode] - } elseif { $op == "w" } { + } elseif { $op == "write" } { if { $sta_pocv_mode == "scalar" \ || $sta_pocv_mode == "normal" \ || $sta_pocv_mode == "skew_normal" } { @@ -171,15 +171,15 @@ proc trace_pocv_mode { name1 name2 op } { } } -trace variable ::sta_pocv_quantile "rw" \ +trace add variable ::sta_pocv_quantile {read write} \ sta::trace_pocv_quantile proc trace_pocv_quantile { name1 name2 op } { global sta_pocv_quantile - if { $op == "r" } { + if { $op == "read" } { set sta_pocv_quantile [pocv_quantile] - } elseif { $op == "w" } { + } elseif { $op == "write" } { if { [string is double $sta_pocv_quantile] \ && $sta_pocv_quantile >= 0.0 } { set_pocv_quantile $sta_pocv_quantile @@ -194,9 +194,9 @@ proc trace_pocv_quantile { name1 name2 op } { proc trace_boolean_var { op var_name get_proc set_proc } { upvar 1 $var_name var - if { $op == "r" } { + if { $op == "read" } { set var [$get_proc] - } elseif { $op == "w" } { + } elseif { $op == "write" } { if { $var == 0 } { $set_proc 0 } elseif { $var == 1 } { diff --git a/util/ReportTcl.cc b/util/ReportTcl.cc index a418a53a8..e782c23c7 100644 --- a/util/ReportTcl.cc +++ b/util/ReportTcl.cc @@ -72,6 +72,11 @@ encapGetHandleProc(ClientData instanceData, static int encapBlockModeProc(ClientData instanceData, int mode); +static int +encapClose2Proc(ClientData instanceData, + Tcl_Interp *interp, + int flags); + #if TCL_MAJOR_VERSION < 9 static int encapCloseProc(ClientData instanceData, Tcl_Interp *interp); @@ -97,13 +102,13 @@ Tcl_ChannelType tcl_encap_type_stdout = { #if TCL_MAJOR_VERSION < 9 encapSeekProc, #else - nullptr, // close2Proc + nullptr, // seekProc unused #endif encapSetOptionProc, encapGetOptionProc, encapWatchProc, encapGetHandleProc, - nullptr, // close2Proc + encapClose2Proc, encapBlockModeProc, nullptr, // flushProc nullptr, // handlerProc @@ -290,19 +295,33 @@ encapBlockModeProc(ClientData, return 0; } -#if TCL_MAJOR_VERSION < 9 - +// Close channel implementing CloseProc() or Close2Proc() static int -encapCloseProc(ClientData instanceData, - Tcl_Interp *) +closeChannel(ReportTcl *report) { - ReportTcl *report = reinterpret_cast(instanceData); report->logEnd(); report->redirectFileEnd(); report->redirectStringEnd(); return 0; } +static int +encapClose2Proc(ClientData instanceData, + Tcl_Interp *, + int) +{ + return closeChannel(reinterpret_cast(instanceData)); +} + +#if TCL_MAJOR_VERSION < 9 + +static int +encapCloseProc(ClientData instanceData, + Tcl_Interp *) +{ + return closeChannel(reinterpret_cast(instanceData)); +} + static int encapSeekProc(ClientData, long, From a5921d1ca964971ada83be2c7c65bb84504fe179 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Fri, 3 Apr 2026 20:21:57 +0200 Subject: [PATCH 131/181] util/StringUtil.cc: feature-test std::from_chars (#415) Support for std::{from,to}_chars isn't finalized in libcxx as of the time of writing, see __cpp_lib_to_chars in https://github.com/llvm/llvm-project/blob/6331bfa41ab529558ec9d645c0effb7a4146591c/libcxx/docs/FeatureTestMacroTable.rst This patch adds a fallback using strtof. There are two differences: * strtof is locale-dependent * strtof tolerates leading spaces Signed-off-by: Mohamed Gaber --- util/StringUtil.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/util/StringUtil.cc b/util/StringUtil.cc index 00a36ec47..d1f7fae6d 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -28,6 +28,7 @@ #include #include #include +#include namespace sta { @@ -56,12 +57,21 @@ std::pair stringFloat(const std::string &str) { float value; +#if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value); if (ec == std::errc() && *ptr == '\0') return {value, true}; else return {0.0, false}; +#else + char *ptr; + value = strtof(str.data(), &ptr); + if (!errno || *ptr != '\0') + return {0.0, false}; + else + return {value, true}; +#endif } void From f873c6520a9dae692ea5a922e6963ddd21581e9d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 2 Apr 2026 19:22:08 -0700 Subject: [PATCH 132/181] Library/Cell name/filename args string_view Signed-off-by: James Cherry --- include/sta/ConcreteLibrary.hh | 4 +- include/sta/Liberty.hh | 70 +++++++-------- liberty/Liberty.cc | 155 +++++++++++++++++---------------- liberty/LibertyBuilder.cc | 2 +- liberty/LibertyBuilder.hh | 2 +- liberty/LibertyReader.cc | 32 ++++--- liberty/LibertyReaderPvt.hh | 2 +- network/ConcreteLibrary.cc | 8 +- 8 files changed, 137 insertions(+), 138 deletions(-) diff --git a/include/sta/ConcreteLibrary.hh b/include/sta/ConcreteLibrary.hh index e39830767..fc831a99c 100644 --- a/include/sta/ConcreteLibrary.hh +++ b/include/sta/ConcreteLibrary.hh @@ -56,8 +56,8 @@ using ConcretePortMemberIterator = VectorIteratorsecond; } @@ -203,13 +203,13 @@ LibertyLibrary::busDcls() const } TableTemplate * -LibertyLibrary::makeTableTemplate(std::string name, - TableTemplateType type) +LibertyLibrary::makeTableTemplate(std::string_view name, + TableTemplateType type) { - std::string key = name; + std::string key(name); auto [it, inserted] = template_maps_[int(type)].try_emplace(std::move(key), - std::move(name), - type); + std::string(name), + type); return &it->second; } @@ -265,10 +265,10 @@ LibertyLibrary::setScaleFactors(ScaleFactors *scales) } ScaleFactors * -LibertyLibrary::makeScaleFactors(std::string name) +LibertyLibrary::makeScaleFactors(std::string_view name) { - std::string key = name; - auto [it, inserted] = scale_factors_map_.emplace(std::move(key), std::move(name)); + std::string key(name); + auto [it, inserted] = scale_factors_map_.emplace(std::move(key), std::string(name)); return &it->second; } @@ -565,9 +565,10 @@ LibertyLibrary::setDefaultOutputPinRes(const RiseFall *rf, } Wireload * -LibertyLibrary::makeWireload(std::string name) +LibertyLibrary::makeWireload(std::string_view name) { - auto [it, inserted] = wireloads_.try_emplace(name, name, this); + std::string key(name); + auto [it, inserted] = wireloads_.try_emplace(std::move(key), std::string(name), this); return &it->second; } @@ -590,11 +591,11 @@ LibertyLibrary::defaultWireload() const } WireloadSelection * -LibertyLibrary::makeWireloadSelection(std::string name) +LibertyLibrary::makeWireloadSelection(std::string_view name) { - std::string key = name; + std::string key(name); auto [it, inserted] = wire_load_selections_.try_emplace(std::move(key), - std::move(name)); + std::string(name)); return &it->second; } @@ -629,10 +630,10 @@ LibertyLibrary::setDefaultWireloadMode(WireloadMode mode) } OperatingConditions * -LibertyLibrary::makeOperatingConditions(std::string name) +LibertyLibrary::makeOperatingConditions(std::string_view name) { - std::string key = name; - auto [it, inserted] = operating_conditions_.try_emplace(std::move(key), std::move(name)); + std::string key(name); + auto [it, inserted] = operating_conditions_.try_emplace(std::move(key), std::string(name)); return &it->second; } @@ -719,10 +720,10 @@ LibertyLibrary::setSlewDerateFromLibrary(float derate) } LibertyCell * -LibertyLibrary::makeScaledCell(std::string name, - std::string filename) +LibertyLibrary::makeScaledCell(std::string_view name, + std::string_view filename) { - return new LibertyCell(this, std::move(name), std::move(filename)); + return new LibertyCell(this, name, filename); } //////////////////////////////////////////////////////////////// @@ -853,10 +854,10 @@ LibertyLibrary::setDefaultOcvDerate(OcvDerate *derate) } OcvDerate * -LibertyLibrary::makeOcvDerate(std::string name) +LibertyLibrary::makeOcvDerate(std::string_view name) { - std::string key = name; - auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::move(name)); + std::string key(name); + auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::string(name)); return &it->second; } @@ -867,10 +868,10 @@ LibertyLibrary::findOcvDerate(std::string_view derate_name) } void -LibertyLibrary::addSupplyVoltage(std::string supply_name, +LibertyLibrary::addSupplyVoltage(std::string_view supply_name, float voltage) { - supply_voltage_map_[std::move(supply_name)] = voltage; + supply_voltage_map_[std::string(supply_name)] = voltage; } void @@ -903,13 +904,13 @@ LibertyLibrary::findDriverWaveform(std::string_view name) } DriverWaveform * -LibertyLibrary::makeDriverWaveform(std::string name, +LibertyLibrary::makeDriverWaveform(std::string_view name, TablePtr waveforms) { - std::string key = name; + std::string key(name); auto [it, inserted] = driver_waveform_map_.try_emplace(std::move(key), - std::move(name), - waveforms); + std::string(name), + waveforms); return &it->second; } @@ -935,8 +936,8 @@ LibertyCellIterator::next() //////////////////////////////////////////////////////////////// LibertyCell::LibertyCell(LibertyLibrary *library, - std::string name, - std::string filename) : + std::string_view name, + std::string_view filename) : ConcreteCell(name, filename, true, library), liberty_library_(library), area_(0.0), @@ -1019,10 +1020,10 @@ LibertyCell::setHasInternalPorts(bool has_internal) } ModeDef * -LibertyCell::makeModeDef(std::string name) +LibertyCell::makeModeDef(std::string_view name) { - std::string key = name; - auto [it, inserted] = mode_defs_.try_emplace(std::move(key), std::move(name)); + std::string key(name); + auto [it, inserted] = mode_defs_.try_emplace(std::move(key), std::string(name)); return &it->second; } @@ -1039,12 +1040,12 @@ LibertyCell::setScaleFactors(ScaleFactors *scale_factors) } BusDcl * -LibertyCell::makeBusDcl(std::string name, +LibertyCell::makeBusDcl(std::string_view name, int from, int to) { - std::string key = name; - auto [it, inserted] = bus_dcls_.try_emplace(std::move(key), std::move(name), from, to); + std::string key(name); + auto [it, inserted] = bus_dcls_.try_emplace(std::move(key), std::string(name), from, to); return &it->second; } @@ -1624,10 +1625,10 @@ LibertyCell::setOcvDerate(OcvDerate *derate) } OcvDerate * -LibertyCell::makeOcvDerate(std::string name) +LibertyCell::makeOcvDerate(std::string_view name) { - std::string key = name; - auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::move(name)); + std::string key(name); + auto [it, inserted] = ocv_derate_map_.try_emplace(std::move(key), std::string(name)); return &it->second; } @@ -1929,15 +1930,15 @@ LibertyCell::ensureVoltageWaveforms(const SceneSeq &scenes) } void -LibertyCell::setFootprint(std::string footprint) +LibertyCell::setFootprint(std::string_view footprint) { - footprint_ = std::move(footprint); + footprint_ = footprint; } void -LibertyCell::setUserFunctionClass(std::string user_function_class) +LibertyCell::setUserFunctionClass(std::string_view user_function_class) { - user_function_class_ = std::move(user_function_class); + user_function_class_ = user_function_class; } //////////////////////////////////////////////////////////////// @@ -1986,7 +1987,7 @@ LibertyCellPortBitIterator::next() //////////////////////////////////////////////////////////////// LibertyPort::LibertyPort(LibertyCell *cell, - std::string name, + std::string_view name, bool is_bus, BusDcl *bus_dcl, int from_index, @@ -2079,9 +2080,9 @@ LibertyPort::setPwrGndType(PwrGndType type) } void -LibertyPort::setVoltageName(std::string voltage_name) +LibertyPort::setVoltageName(std::string_view voltage_name) { - voltage_name_ = std::move(voltage_name); + voltage_name_ = voltage_name; } static EnumNameMap pwr_gnd_type_map = @@ -2840,10 +2841,10 @@ LibertyPortMemberIterator::next() //////////////////////////////////////////////////////////////// -BusDcl::BusDcl(std::string name, +BusDcl::BusDcl(std::string_view name, int from, int to) : - name_(std::move(name)), + name_(name), from_(from), to_(to) { @@ -2851,16 +2852,16 @@ BusDcl::BusDcl(std::string name, //////////////////////////////////////////////////////////////// -ModeDef::ModeDef(std::string name) : - name_(std::move(name)) +ModeDef::ModeDef(std::string_view name) : + name_(name) { } ModeValueDef * -ModeDef::defineValue(std::string value) +ModeDef::defineValue(std::string_view value) { - std::string key = value; - auto [it, inserted] = values_.try_emplace(std::move(key), std::move(value)); + std::string key(value); + auto [it, inserted] = values_.try_emplace(std::move(key), std::string(value)); return &it->second; } @@ -2872,8 +2873,8 @@ ModeDef::findValueDef(std::string_view value) const //////////////////////////////////////////////////////////////// -ModeValueDef::ModeValueDef(std::string value) : - value_(std::move(value)), +ModeValueDef::ModeValueDef(std::string_view value) : + value_(value), cond_(nullptr) { } @@ -2905,8 +2906,8 @@ ModeValueDef::setSdfCond(std::string sdf_cond) //////////////////////////////////////////////////////////////// -TableTemplate::TableTemplate(std::string name) : - name_(std::move(name)), +TableTemplate::TableTemplate(std::string_view name) : + name_(name), type_(TableTemplateType::delay), axis1_(nullptr), axis2_(nullptr), @@ -2914,9 +2915,9 @@ TableTemplate::TableTemplate(std::string name) : { } -TableTemplate::TableTemplate(std::string name, +TableTemplate::TableTemplate(std::string_view name, TableTemplateType type) : - name_(std::move(name)), + name_(name), type_(type), axis1_(nullptr), axis2_(nullptr), @@ -2924,11 +2925,11 @@ TableTemplate::TableTemplate(std::string name, { } -TableTemplate::TableTemplate(std::string name, +TableTemplate::TableTemplate(std::string_view name, TableAxisPtr axis1, TableAxisPtr axis2, TableAxisPtr axis3) : - name_(std::move(name)), + name_(name), type_(TableTemplateType::delay), axis1_(axis1), axis2_(axis2), @@ -2937,9 +2938,9 @@ TableTemplate::TableTemplate(std::string name, } void -TableTemplate::setName(std::string name) +TableTemplate::setName(std::string_view name) { - name_ = std::move(name); + name_ = name; } void @@ -2989,9 +2990,9 @@ Pvt::setTemperature(float temp) temperature_ = temp; } -OperatingConditions::OperatingConditions(std::string name) : +OperatingConditions::OperatingConditions(std::string_view name) : Pvt(0.0, 0.0, 0.0), - name_(std::move(name)), + name_(name), // Default wireload tree. wire_load_tree_(WireloadTree::unknown) { @@ -3085,8 +3086,8 @@ scaleFactorPvtName(ScaleFactorPvt pvt) //////////////////////////////////////////////////////////////// -ScaleFactors::ScaleFactors(std::string name) : - name_(std::move(name)) +ScaleFactors::ScaleFactors(std::string_view name) : + name_(name) { for (int type = 0; type < scale_factor_type_count; type++) { for (int pvt = 0; pvt < scale_factor_pvt_count; pvt++) { @@ -3168,16 +3169,16 @@ ScaleFactors::report(Report *report) } TestCell::TestCell(LibertyLibrary *library, - std::string name, - std::string filename) : + std::string_view name, + std::string_view filename) : LibertyCell(library, name, filename) { } //////////////////////////////////////////////////////////////// -OcvDerate::OcvDerate(std::string name) : - name_(std::move(name)) +OcvDerate::OcvDerate(std::string_view name) : + name_(name) { for (auto el_index : EarlyLate::rangeIndex()) { for (auto rf_index : RiseFall::rangeIndex()) { diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index e7e927918..f296fdec8 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -124,7 +124,7 @@ LibertyBuilder::makePort(LibertyCell *cell, LibertyPort * LibertyBuilder::makeBundlePort(LibertyCell *cell, - const char *name, + std::string_view name, ConcretePortSeq *members) { LibertyPort *port = new LibertyPort(cell, name, false, nullptr, -1, -1, true, members); diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh index 11e8c2b1d..27a076777 100644 --- a/liberty/LibertyBuilder.hh +++ b/liberty/LibertyBuilder.hh @@ -54,7 +54,7 @@ public: int to_index, BusDcl *bus_dcl); LibertyPort *makeBundlePort(LibertyCell *cell, - const char *name, + std::string_view name, ConcretePortSeq *members); // Build timing arc sets and their arcs given a type and sense. // Port functions and cell latches are also used by this builder diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index b91c10e69..b473b80ee 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -176,7 +176,7 @@ LibertyReader::endCell(const LibertyGroup *cell_group, if (cell_group->hasFirstParam()) { const std::string &name = cell_group->firstParam(); debugPrint(debug_, "liberty", 1, "cell {}", name); - LibertyCell *cell = builder_.makeCell(library_, name, std::string(filename_)); + LibertyCell *cell = builder_.makeCell(library_, name, filename_); readCell(cell, cell_group); } else @@ -849,7 +849,7 @@ LibertyReader::readDefaultWireLoadSelection(const LibertyGroup *library_group) library_group->findAttrString("default_wire_load_selection"); if (!selection_name.empty()) { const WireloadSelection *selection = - library_->findWireloadSelection(selection_name.c_str()); + library_->findWireloadSelection(selection_name); if (selection) library_->setDefaultWireloadSelection(selection); else @@ -1003,9 +1003,8 @@ LibertyReader::readScaledCell(const LibertyGroup *scaled_cell_group) OperatingConditions *op_cond = library_->findOperatingConditions(op_cond_name); if (op_cond) { debugPrint(debug_, "liberty", 1, "scaled cell {} {}", - name.c_str(), op_cond_name.c_str()); - LibertyCell *scaled_cell = library_->makeScaledCell(name, - std::string(filename_)); + name, op_cond_name); + LibertyCell *scaled_cell = library_->makeScaledCell(name, filename_); readCell(scaled_cell, scaled_cell_group); checkScaledCell(scaled_cell, owner, scaled_cell_group, op_cond_name); // Add scaled cell AFTER ports and timing arcs are defined. @@ -1163,8 +1162,7 @@ LibertyReader::makeBundlePort(LibertyCell *cell, members->push_back(member); } } - LibertyPort *bundle_port = builder_.makeBundlePort(cell, bundle_name.c_str(), - members); + LibertyPort *bundle_port = builder_.makeBundlePort(cell, bundle_name, members); port_group_map[bundle_group].push_back(bundle_port); // Make ports for pin groups inside the bundle group. makeBundlePinPorts(cell, bundle_group, port_group_map); @@ -1204,7 +1202,7 @@ LibertyReader::makePgPinPort(LibertyCell *cell, const std::string &type_name = pg_pin_group->findAttrString("pg_type"); if (!type_name.empty()) { - PwrGndType type = findPwrGndType(type_name.c_str()); + PwrGndType type = findPwrGndType(type_name); PortDirection *dir = PortDirection::unknown(); switch (type) { case PwrGndType::primary_ground: @@ -1229,7 +1227,7 @@ LibertyReader::makePgPinPort(LibertyCell *cell, const std::string &voltate_name = pg_pin_group->findAttrString("voltage_name"); if (!voltate_name.empty()) - pg_port->setVoltageName(voltate_name.c_str()); + pg_port->setVoltageName(voltate_name); } else warn(1314, pg_pin_group, "pg_pin missing name."); @@ -1289,7 +1287,7 @@ LibertyReader::readDriverWaveform(const LibertyPortSeq &ports, : "driver_waveform_fall"; const std::string &name = port_group->findAttrString(attr_name); if (!name.empty()) { - DriverWaveform *waveform = library_->findDriverWaveform(name.c_str()); + DriverWaveform *waveform = library_->findDriverWaveform(name); if (waveform) { for (LibertyPort *port : ports) port->setDriverWaveform(waveform, rf); @@ -1824,9 +1822,9 @@ LibertyReader::readCellAttributes(LibertyCell *cell, const std::string &clock_gate_type = cell_group->findAttrString("clock_gating_integrated_cell"); if (!clock_gate_type.empty()) { - if (stringBeginEqual(clock_gate_type.c_str(), "latch_posedge")) + if (stringBeginEqual(clock_gate_type, "latch_posedge")) cell->setClockGateType(ClockGateType::latch_posedge); - else if (stringBeginEqual(clock_gate_type.c_str(), "latch_negedge")) + else if (stringBeginEqual(clock_gate_type, "latch_negedge")) cell->setClockGateType(ClockGateType::latch_negedge); else cell->setClockGateType(ClockGateType::other); @@ -1846,7 +1844,7 @@ LibertyReader::readScaleFactors(LibertyCell *cell, cell_group->findAttrString("scaling_factors"); if (!scale_factors_name.empty()) { ScaleFactors *scale_factors = - library_->findScaleFactors(scale_factors_name.c_str()); + library_->findScaleFactors(scale_factors_name); if (scale_factors) cell->setScaleFactors(scale_factors); else @@ -1856,7 +1854,7 @@ LibertyReader::readScaleFactors(LibertyCell *cell, void LibertyReader::readCellAttrString(std::string_view attr_name, - void (LibertyCell::*set_func)(std::string value), + void (LibertyCell::*set_func)(std::string_view value), LibertyCell *cell, const LibertyGroup *group) { @@ -2937,9 +2935,9 @@ LibertyReader::readCellOcvDerateGroup(LibertyCell *cell, { const std::string &derate_name = cell_group->findAttrString("ocv_derate_group"); if (!derate_name.empty()) { - OcvDerate *derate = cell->findOcvDerate(derate_name.c_str()); + OcvDerate *derate = cell->findOcvDerate(derate_name); if (derate == nullptr) - derate = library_->findOcvDerate(derate_name.c_str()); + derate = library_->findOcvDerate(derate_name); if (derate) cell->setOcvDerate(derate); else @@ -3447,7 +3445,7 @@ LibertyReader::readDefaultOcvDerateGroup(const LibertyGroup *library_group) const std::string &derate_name = library_group->findAttrString("default_ocv_derate_group"); if (!derate_name.empty()) { - OcvDerate *derate = library_->findOcvDerate(derate_name.c_str()); + OcvDerate *derate = library_->findOcvDerate(derate_name); if (derate) library_->setDefaultOcvDerate(derate); else diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index a840ebb28..28683aefe 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -375,7 +375,7 @@ protected: void readScaleFactors(LibertyCell *cell, const LibertyGroup *cell_group); void readCellAttrString(std::string_view attr_name, - void (LibertyCell::*set_func)(std::string value), + void (LibertyCell::*set_func)(std::string_view value), LibertyCell *cell, const LibertyGroup *group); void readCellAttrFloat(std::string_view attr_name, diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index 74c37b151..3864239c7 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -38,12 +38,12 @@ namespace sta { static constexpr char escape_ = '\\'; -ConcreteLibrary::ConcreteLibrary(std::string name, - std::string filename, +ConcreteLibrary::ConcreteLibrary(std::string_view name, + std::string_view filename, bool is_liberty) : - name_(std::move(name)), + name_(name), id_(ConcreteNetwork::nextObjectId()), - filename_(std::move(filename)), + filename_(filename), is_liberty_(is_liberty), bus_brkt_left_('['), bus_brkt_right_(']') From 5d401342394f7156f90fb5e6aeaa0f68b7eb3cc2 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Apr 2026 11:00:29 -0700 Subject: [PATCH 133/181] ArnoldiDelayCalc rcmodel Signed-off-by: James Cherry --- dcalc/ArnoldiDelayCalc.cc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 97866a79f..2523686ec 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -236,7 +236,6 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc int pin_n_; ArnoldiReduce *reduce_; delay_work *delay_work_; - std::vector unsaved_parasitics_; }; ArcDelayCalc * @@ -247,6 +246,7 @@ makeArnoldiDelayCalc(StaState *sta) ArnoldiDelayCalc::ArnoldiDelayCalc(StaState *sta) : LumpedCapDelayCalc(sta), + rcmodel_(nullptr), reduce_(new ArnoldiReduce(sta)), delay_work_(delay_work_create()) { @@ -267,6 +267,7 @@ ArnoldiDelayCalc::~ArnoldiDelayCalc() free(_delayV); free(_slewV); delete reduce_; + delete rcmodel_; } Parasitic * @@ -297,12 +298,11 @@ ArnoldiDelayCalc::findParasitic(const Pin *drvr_pin, } if (parasitic_network) { - rcmodel *rcmodel = reduce_->reduceToArnoldi(parasitic_network, drvr_pin, - parasitics->couplingCapFactor(), - drvr_rf, scene, min_max); + rcmodel_ = reduce_->reduceToArnoldi(parasitic_network, drvr_pin, + parasitics->couplingCapFactor(), + drvr_rf, scene, min_max); // Arnoldi parasitics are their own class that are not saved in the parasitic db. - unsaved_parasitics_.push_back(rcmodel); - parasitic = rcmodel; + parasitic = rcmodel_; } return parasitic; } @@ -321,9 +321,8 @@ ArnoldiDelayCalc::reduceParasitic(const Parasitic *, void ArnoldiDelayCalc::finishDrvrPin() { - for (auto parasitic : unsaved_parasitics_) - delete parasitic; - unsaved_parasitics_.clear(); + delete rcmodel_; + rcmodel_ = nullptr; } ArcDcalcResult From e04f36b541f80f2a839bf9697108549f68c74426 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Apr 2026 11:11:01 -0700 Subject: [PATCH 134/181] GraphDelayCalc::findInputArcDelay rm Slew Signed-off-by: James Cherry --- dcalc/GraphDelayCalc.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index f8984474a..67536ef0a 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -648,12 +648,12 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult intrinsic_result = - arc_delay_calc->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr, + arc_delay_calc->gateDelay(drvr_pin, arc, from_slew, 0.0, nullptr, load_pin_index_map, scene, min_max); const ArcDelay &intrinsic_delay = intrinsic_result.gateDelay(); ArcDcalcResult gate_result = arc_delay_calc->gateDelay(drvr_pin, arc, - Slew(from_slew), load_cap, + from_slew, load_cap, parasitic, load_pin_index_map, scene, min_max); From 6bb888b218e6847897f9641a3bf96a3a24735bdc Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Apr 2026 11:25:33 -0700 Subject: [PATCH 135/181] stringFloat comment Signed-off-by: James Cherry --- util/StringUtil.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/util/StringUtil.cc b/util/StringUtil.cc index d1f7fae6d..c070cb6ec 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -57,6 +57,7 @@ std::pair stringFloat(const std::string &str) { float value; + // OsX 15.xx and earlier clang do not support std::from_chars. #if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value); if (ec == std::errc() From 2f85fdfee30ab385664e86641525a0399407500d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Apr 2026 16:46:05 -0700 Subject: [PATCH 136/181] Dmp cleanup Signed-off-by: James Cherry --- dcalc/CcsCeffDelayCalc.cc | 15 +- dcalc/DmpCeff.cc | 802 +++++++++++++++----------------------- dcalc/DmpCeff.hh | 14 +- dcalc/DmpDelayCalc.cc | 8 +- dcalc/FindRoot.cc | 35 +- dcalc/FindRoot.hh | 15 +- 6 files changed, 350 insertions(+), 539 deletions(-) diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 6ca9cb962..fec6f0326 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -445,14 +445,13 @@ 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); - vl_fail_ |= root_fail; + auto [time, failed] = + findRoot([&](double t, double &y, double &dy) { + vl(t, elmore, y, dy); + y -= v; + }, + t_init, t_final + elmore * 3.0, .001, 20); + vl_fail_ |= failed; return time; } diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 6828aa672..216b71c93 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -34,8 +34,10 @@ #include #include -#include +#include #include +#include +#include #include "Format.hh" #include "Report.hh" @@ -52,15 +54,6 @@ namespace sta { -// Tolerance (as a scale of value) for driver parameters (Ceff, delta t, t0). -static const double driver_param_tol = .01; -// Waveform threshold crossing time tolerance (1.0 = 100%). -static const double vth_time_tol = .01; -// A small number used by luDecomp. -static const double tiny_double = 1.0e-20; -// Max iterations for findRoot. -static const int find_root_max_iter = 20; - // Indices of Newton-Raphson parameter vector. enum DmpParam { t0, dt, ceff }; @@ -92,30 +85,6 @@ gateModelRd(const LibertyCell *cell, double c2, double c1, const Pvt *pvt); -static void -newtonRaphson(const int max_iter, - double x[], - const int n, - const double x_tol, - // eval(state) is called to fill fvec and fjac. - std::function eval, - // Temporaries supplied by caller. - double *fvec, - double **fjac, - int *index, - double *p, - double *scale); -static void -luSolve(double **a, - const int size, - const int *index, - double b[]); -static void -luDecomp(double **a, - const int size, - int *index, - double *scale); - //////////////////////////////////////////////////////////////// // Base class for Dartu/Menezes/Pileggi algorithm. @@ -138,51 +107,32 @@ class DmpAlg : public StaState double c2, double rpi, double c1); - virtual void gateDelaySlew(// Return values. - double &delay, - double &slew) = 0; - virtual void loadDelaySlew(const Pin *load_pin, - double elmore, - // Return values. - double &delay, - double &slew); + virtual std::pair gateDelaySlew() = 0; + virtual std::pair loadDelaySlew(const Pin *load_pin, + double elmore); double ceff() { return ceff_; } // Given x_ as a vector of input parameters, fill fvec_ with the // equations evaluated at x_ and fjac_ with the jabobian evaluated at x_. virtual void evalDmpEqns() = 0; - // Output response to vs(t) ramp driving pi model load. - void Vo(double t, - // Return values. - double &vo, - double &dol_dt); - // Load response to driver waveform. - void Vl(double t, - // Return values. - double &vl, - double &dvl_dt); + // Output response to vs(t) ramp driving pi model load (vo, dvo_dt). + std::pair Vo(double t); + // Load response to driver waveform (vl, dvl/dt). + std::pair Vl(double t); protected: + void luDecomp(); + void luSolve(); + void newtonRaphson(); // Find driver parameters t0, delta_t, Ceff. void findDriverParams(double ceff); - void gateCapDelaySlew(double cl, - // Return values. - double &delay, - double &slew); - void gateDelays(double ceff, - // Return values. - double &t_vth, - double &t_vl, - double &slew); - // Partial derivatives of y(t) (jacobian). - void dy(double t, - double t0, - double dt, - double cl, - // Return values. - double &dydt0, - double &dyddt, - double &dydcl); + std::pair gateCapDelaySlew(double cl); + std::tuple gateDelays(double ceff); + // Partial derivatives of y(t) jacobian (dydt0, dyddt, dydcl). + std::tuple dy(double t, + double t0, + double dt, + double cl); double y0dt(double t, double cl); double y0dcl(double t, @@ -190,9 +140,7 @@ class DmpAlg : public StaState void showX(); void showFvec(); void showJacobian(); - void findDriverDelaySlew( // Return values. - double &delay, - double &slew); + std::pair findDriverDelaySlew(); double findVoCrossing(double vth, double lower_bound, double upper_bound); @@ -203,26 +151,22 @@ class DmpAlg : public StaState void showVl(); void fail(std::string_view reason); - // Output response to vs(t) ramp driving capacitive load. - double y(double t, - double t0, - double dt, - double cl); + // Output response to vs(t) ramp driving capacitive load (y, t1). + std::pair y(double t, + double t0, + double dt, + double cl); // Output response to unit ramp driving capacitive load. double y0(double t, double cl); // Output response to unit ramp driving pi model load. - virtual void V0(double t, - // Return values. - double &vo, - double &dvo_dt) = 0; + // Unit ramp output at pi load (vo, dvo_dt). + virtual std::pair V0(double t) = 0; // Upper bound on time that vo crosses vh. virtual double voCrossingUpperBound() = 0; // Load responce to driver unit ramp. - virtual void Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) = 0; + // Unit ramp load response (vl, dvl_dt). + virtual std::pair Vl0(double t) = 0; // Upper bound on time that vl crosses vh. double vlCrossingUpperBound(); @@ -273,6 +217,16 @@ class DmpAlg : public StaState // Load rspf elmore delay. double elmore_; double p3_; + + // Tolerance (as a scale of value) for driver parameters (Ceff, delta t, t0). + static constexpr double driver_param_tol_ = .01; + // Waveform threshold crossing time tolerance (1.0 = 100%). + static constexpr double vth_time_tol_ = .01; + // Max iterations for findRoot. + static constexpr int find_root_max_iter_ = 20; + static inline int newton_raphson_max_iter_ = 100; + // A small number used by luDecomp. + static constexpr double tiny_double_ = 1.0e-20; }; DmpAlg::DmpAlg(int nr_order, @@ -323,16 +277,13 @@ DmpAlg::findDriverParams(double ceff) { if (nr_order_ == 3) x_[DmpParam::ceff] = ceff; - double t_vth, t_vl, slew; - gateDelays(ceff, t_vth, t_vl, slew); + auto [t_vth, t_vl, slew] = gateDelays(ceff); // Scale slew to 0-100% double dt = slew / (vh_ - vl_); 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(); t0_ = x_[DmpParam::t0]; dt_ = x_[DmpParam::dt]; debugPrint(debug_, "dmp_ceff", 3, " t0 = {} dt = {} ceff = {}", @@ -342,45 +293,43 @@ DmpAlg::findDriverParams(double ceff) showVo(); } -void -DmpAlg::gateCapDelaySlew(double ceff, - // Return values. - double &delay, - double &slew) +std::pair +DmpAlg::gateCapDelaySlew(double ceff) { float model_delay, model_slew; gate_model_->gateDelay(pvt_, in_slew_, ceff, model_delay, model_slew); - delay = model_delay; - slew = model_slew; + double delay = model_delay; + double slew = model_slew; + return {delay, slew}; } -void -DmpAlg::gateDelays(double ceff, - // Return values. - double &t_vth, - double &t_vl, - double &slew) -{ - double table_slew; - gateCapDelaySlew(ceff, t_vth, table_slew); +std::tuple +DmpAlg::gateDelays(double ceff) +{ + auto [t_vth, table_slew] = gateCapDelaySlew(ceff); // Convert reported/table slew to measured slew. - slew = table_slew * slew_derate_; - t_vl = t_vth - slew * (vth_ - vl_) / (vh_ - vl_); + double slew = table_slew * slew_derate_; + double t_vl = t_vth - slew * (vth_ - vl_) / (vh_ - vl_); + return {t_vth, t_vl, slew}; } -double +std::pair DmpAlg::y(double t, double t0, double dt, double cl) { double t1 = t - t0; - if (t1 <= 0.0) - return 0.0; - else if (t1 <= dt) - return y0(t1, cl) / dt; - else - return (y0(t1, cl) - y0(t1 - dt, cl)) / dt; + if (t1 <= 0.0) { + double y = 0.0; + return {y, t1}; + } + if (t1 <= dt) { + double y = y0(t1, cl) / dt; + return {y, t1}; + } + double y = (y0(t1, cl) - y0(t1 - dt, cl)) / dt; + return {y, t1}; } double @@ -390,29 +339,29 @@ DmpAlg::y0(double t, return t - rd_ * cl * (1.0 - exp2(-t / (rd_ * cl))); } -void +std::tuple DmpAlg::dy(double t, double t0, double dt, - double cl, - // Return values. - double &dydt0, - double &dyddt, - double &dydcl) + double cl) { double t1 = t - t0; - if (t1 <= 0.0) - dydt0 = dyddt = dydcl = 0.0; - else if (t1 <= dt) { - dydt0 = -y0dt(t1, cl) / dt; - dyddt = -y0(t1, cl) / (dt * dt); - dydcl = y0dcl(t1, cl) / dt; + if (t1 <= 0.0) { + double dydt0 = 0.0; + double dyddt = 0.0; + double dydcl = 0.0; + return {dydt0, dyddt, dydcl}; } - 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; - dydcl = (y0dcl(t1, cl) - y0dcl(t1 - dt, cl)) / dt; + if (t1 <= dt) { + double dydt0 = -y0dt(t1, cl) / dt; + double dyddt = -y0(t1, cl) / (dt * dt); + double dydcl = y0dcl(t1, cl) / dt; + return {dydt0, dyddt, dydcl}; } + double dydt0 = -(y0dt(t1, cl) - y0dt(t1 - dt, cl)) / dt; + double dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt) + y0dt(t1 - dt, cl) / dt; + double dydcl = (y0dcl(t1, cl) - y0dcl(t1 - dt, cl)) / dt; + return {dydt0, dyddt, dydcl}; } double @@ -459,17 +408,16 @@ DmpAlg::showJacobian() } } -void -DmpAlg::findDriverDelaySlew( // Return values. - double &delay, - double &slew) +std::pair +DmpAlg::findDriverDelaySlew() { double t_upper = voCrossingUpperBound(); - delay = findVoCrossing(vth_, t0_, t_upper); + double delay = findVoCrossing(vth_, t0_, t_upper); double tl = findVoCrossing(vl_, t0_, delay); double th = findVoCrossing(vh_, delay, t_upper); // Convert measured slew to table slew. - slew = (th - tl) / slew_derate_; + double slew = (th - tl) / slew_derate_; + return {delay, slew}; } // Find t such that vo(t)=v. @@ -479,47 +427,40 @@ DmpAlg::findVoCrossing(double vth, double t_upper) { FindRootFunc vo_func = [&](double t, double &y, double &dy) { - double vo, vo_dt; - Vo(t, vo, vo_dt); + auto [vo, dvo_dt] = Vo(t); y = vo - vth; - dy = vo_dt; + dy = dvo_dt; }; - bool fail; - double t_vth = - findRoot(vo_func, t_lower, t_upper, vth_time_tol, find_root_max_iter, fail); - if (fail) + auto [t_vth, failed] = findRoot(vo_func, t_lower, t_upper, vth_time_tol_, + find_root_max_iter_); + if (failed) throw DmpError("find Vo crossing failed"); return t_vth; } -void -DmpAlg::Vo(double t, - // Return values. - double &vo, - double &dvo_dt) +std::pair +DmpAlg::Vo(double t) { double t1 = t - t0_; if (t1 <= 0.0) { - vo = 0.0; - dvo_dt = 0.0; + double vo = 0.0; + double dvo_dt = 0.0; + return {vo, dvo_dt}; } - else if (t1 <= dt_) { - double v0, dv0_dt; - V0(t1, v0, dv0_dt); + if (t1 <= dt_) { + auto [v0, dv0_dt] = V0(t1); - vo = v0 / dt_; - dvo_dt = dv0_dt / dt_; + double vo = v0 / dt_; + double dvo_dt = dv0_dt / dt_; + return {vo, dvo_dt}; } - else { - double v0, dv0_dt; - V0(t1, v0, dv0_dt); + auto [v0, dv0_dt] = V0(t1); - double v0_dt, dv0_dt_dt; - V0(t1 - dt_, v0_dt, dv0_dt_dt); + auto [v0_dt, dv0_dt_dt] = V0(t1 - dt_); - vo = (v0 - v0_dt) / dt_; - dvo_dt = (dv0_dt - dv0_dt_dt) / dt_; - } + double vo = (v0 - v0_dt) / dt_; + double dvo_dt = (dv0_dt - dv0_dt_dt) / dt_; + return {vo, dvo_dt}; } void @@ -527,63 +468,57 @@ DmpAlg::showVo() { 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_->report(" {:g} {:g}", t, vo); - } + for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) + report_->report(" {:g} {:g}", t, Vo(t).first); } -void +std::pair DmpAlg::loadDelaySlew(const Pin *, - double elmore, - double &delay, - double &slew) + double elmore) { if (!driver_valid_ || elmore == 0.0 // Elmore delay is small compared to driver slew. || elmore < drvr_slew_ * 1e-3) { - delay = elmore; - slew = drvr_slew_; + double delay = elmore; + double slew = drvr_slew_; + return {delay, slew}; } - else { - // Use the driver thresholds and rely on thresholdAdjust to - // convert the delay and slew to the load's thresholds. - try { - elmore_ = elmore; - p3_ = 1.0 / elmore; - if (debug_->check("dmp_ceff", 4)) - showVl(); - double t_lower = t0_; - double t_upper = vlCrossingUpperBound(); - double load_delay = findVlCrossing(vth_, t_lower, t_upper); - double tl = findVlCrossing(vl_, t_lower, load_delay); - double th = findVlCrossing(vh_, load_delay, t_upper); - // Measure delay from Vo, the load dependent source excitation. - double delay1 = load_delay - vo_delay_; - // Convert measured slew to reported/table slew. - double slew1 = (th - tl) / slew_derate_; - if (delay1 < 0.0) { - // Only report a problem if the difference is significant. - if (-delay1 > vth_time_tol * vo_delay_) - fail("load delay less than zero"); - // Use elmore delay. - delay1 = elmore; - } - if (slew1 < drvr_slew_) { - // Only report a problem if the difference is significant. - if ((drvr_slew_ - slew1) > vth_time_tol * drvr_slew_) - fail("load slew less than driver slew"); - slew1 = drvr_slew_; - } - delay = delay1; - slew = slew1; - } catch (DmpError &error) { - fail(error.what()); - delay = elmore_; + // Use the driver thresholds and rely on thresholdAdjust to + // convert the delay and slew to the load's thresholds. + try { + elmore_ = elmore; + p3_ = 1.0 / elmore; + if (debug_->check("dmp_ceff", 4)) + showVl(); + double t_lower = t0_; + double t_upper = vlCrossingUpperBound(); + double load_delay = findVlCrossing(vth_, t_lower, t_upper); + double tl = findVlCrossing(vl_, t_lower, load_delay); + double th = findVlCrossing(vh_, load_delay, t_upper); + // Measure delay from Vo, the load dependent source excitation. + double delay = load_delay - vo_delay_; + // Convert measured slew to reported/table slew. + double slew = (th - tl) / slew_derate_; + if (delay < 0.0) { + // Only report a problem if the difference is significant. + if (-delay > vth_time_tol_ * vo_delay_) + fail("load delay less than zero"); + // Use elmore delay. + delay = elmore; + } + if (slew < drvr_slew_) { + // Only report a problem if the difference is significant. + if ((drvr_slew_ - slew) > vth_time_tol_ * drvr_slew_) + fail("load slew less than driver slew"); slew = drvr_slew_; } + return {delay, slew}; + } catch (DmpError &error) { + fail(error.what()); + double delay = elmore_; + double slew = drvr_slew_; + return {delay, slew}; } } @@ -594,15 +529,13 @@ DmpAlg::findVlCrossing(double vth, double t_upper) { FindRootFunc vl_func = [&](double t, double &y, double &dy) { - double vl, vl_dt; - Vl(t, vl, vl_dt); + auto [vl, vl_dt] = Vl(t); 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); - if (fail) + auto [t_vth, failed] = findRoot(vl_func, t_lower, t_upper, vth_time_tol_, + find_root_max_iter_); + if (failed) throw DmpError("find Vl crossing failed"); return t_vth; } @@ -613,33 +546,23 @@ DmpAlg::vlCrossingUpperBound() return voCrossingUpperBound() + elmore_ * 2.0; } -void -DmpAlg::Vl(double t, - // Return values. - double &vl, - double &dvl_dt) +std::pair +DmpAlg::Vl(double t) { double t1 = t - t0_; - if (t1 <= 0.0) { - vl = 0.0; - dvl_dt = 0.0; - } - else if (t1 <= dt_) { - double vl0, dvl0_dt; - Vl0(t1, vl0, dvl0_dt); - vl = vl0 / dt_; - dvl_dt = dvl0_dt / dt_; + if (t1 <= 0.0) + return {0.0, 0.0}; + if (t1 <= dt_) { + auto [vl0, dvl0_dt] = Vl0(t1); + return {vl0 / dt_, dvl0_dt / dt_}; } - else { - double vl0, dvl0_dt; - Vl0(t1, vl0, dvl0_dt); + auto [vl0, dvl0_dt] = Vl0(t1); - double vl0_dt, dvl0_dt_dt; - Vl0(t1 - dt_, vl0_dt, dvl0_dt_dt); + auto [vl0_dt, dvl0_dt_dt] = Vl0(t1 - dt_); - vl = (vl0 - vl0_dt) / dt_; - dvl_dt = (dvl0_dt - dvl0_dt_dt) / dt_; - } + double vl = (vl0 - vl0_dt) / dt_; + double dvl_dt = (dvl0_dt - dvl0_dt_dt) / dt_; + return {vl, dvl_dt}; } void @@ -647,11 +570,8 @@ DmpAlg::showVl() { 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_->report(" {:g} {:g}", t, vl); - } + for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) + report_->report(" {:g} {:g}", t, Vl(t).first); } void @@ -684,26 +604,15 @@ class DmpCap : public DmpAlg double c2, double rpi, double c1) override; - void gateDelaySlew(// Return values. - double &delay, - double &slew) override; - void loadDelaySlew(const Pin *, - double elmore, - // Return values. - double &delay, - double &slew) override; + std::pair gateDelaySlew() override; + std::pair loadDelaySlew(const Pin *, + double elmore) override; void evalDmpEqns() override; double voCrossingUpperBound() 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; + std::pair V0(double t) override; + std::pair Vl0(double t) override; }; DmpCap::DmpCap(StaState *sta) : @@ -730,25 +639,23 @@ DmpCap::init(const LibertyLibrary *drvr_library, ceff_ = c1 + c2; } -void -DmpCap::gateDelaySlew(// Return values. - double &delay, - double &slew) +std::pair +DmpCap::gateDelaySlew() { debugPrint(debug_, "dmp_ceff", 3, " ceff = {}", units_->capacitanceUnit()->asString(ceff_)); - gateCapDelaySlew(ceff_, delay, slew); + auto [delay, slew] = gateCapDelaySlew(ceff_); drvr_slew_ = slew; + return {delay, slew}; } -void +std::pair DmpCap::loadDelaySlew(const Pin *, - double elmore, - double &delay, - double &slew) + double elmore) { - delay = elmore; - slew = drvr_slew_; + double delay = elmore; + double slew = drvr_slew_; + return {delay, slew}; } void @@ -756,14 +663,12 @@ DmpCap::evalDmpEqns() { } -void -DmpCap::V0(double, - // Return values. - double &vo, - double &dvo_dt) +std::pair +DmpCap::V0(double) { - vo = 0.0; - dvo_dt = 0.0; + double vo = 0.0; + double dvo_dt = 0.0; + return {vo, dvo_dt}; } double @@ -772,14 +677,12 @@ DmpCap::voCrossingUpperBound() return 0.0; } -void -DmpCap::Vl0(double, - // Return values. - double &vl, - double &dvl_dt) +std::pair +DmpCap::Vl0(double) { - vl = 0.0; - dvl_dt = 0.0; + double vl = 0.0; + double dvl_dt = 0.0; + return {vl, dvl_dt}; } //////////////////////////////////////////////////////////////// @@ -800,9 +703,7 @@ class DmpPi : public DmpAlg double c2, double rpi, double c1) override; - void gateDelaySlew(// Return values. - double &delay, - double &slew) override; + std::pair gateDelaySlew() override; void evalDmpEqns() override; double voCrossingUpperBound() override; @@ -812,14 +713,8 @@ class DmpPi : public DmpAlg double dt, double ceff_time, double ceff); - void V0(double t, - // Return values. - double &vo, - double &dvo_dt) override; - void Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) override; + std::pair V0(double t) override; + std::pair Vl0(double t) override; // Poles/zero. double p1_; @@ -891,22 +786,20 @@ DmpPi::init(const LibertyLibrary *drvr_library, D_ = (z_ - p2_) / (p2_ * (p2_ - p1_)); } -void -DmpPi::gateDelaySlew(// Return values. - double &delay, - double &slew) +std::pair +DmpPi::gateDelaySlew() { driver_valid_ = false; + double delay = 0.0; + double slew = 0.0; try { findDriverParamsPi(); ceff_ = x_[DmpParam::ceff]; - double table_delay, table_slew; - gateCapDelaySlew(ceff_, table_delay, table_slew); + auto [table_delay, table_slew] = gateCapDelaySlew(ceff_); delay = table_delay; // slew = table_slew; try { - double vo_delay, vo_slew; - findDriverDelaySlew(vo_delay, vo_slew); + auto [vo_delay, vo_slew] = findDriverDelaySlew(); driver_valid_ = true; // Save Vo delay to measure load wire delay waveform. vo_delay_ = vo_delay; @@ -921,9 +814,10 @@ DmpPi::gateDelaySlew(// Return values. fail(error.what()); // Driver calculation failed - use Ceff=c1+c2. ceff_ = c1_ + c2_; - gateCapDelaySlew(ceff_, delay, slew); + std::tie(delay, slew) = gateCapDelaySlew(ceff_); } drvr_slew_ = slew; + return {delay, slew}; } void @@ -950,8 +844,7 @@ DmpPi::evalDmpEqns() if (ceff > (c1_ + c2_)) throw DmpError("eqn eval failed: ceff > c2 + c1"); - double t_vth, t_vl, slew; - gateDelays(ceff, t_vth, t_vl, slew); + auto [t_vth, t_vl, slew] = gateDelays(ceff); if (slew == 0.0) throw DmpError("eqn eval failed: slew = 0"); @@ -966,9 +859,9 @@ DmpPi::evalDmpEqns() double exp_p2_dt = exp2(-p2_ * dt); double exp_dt_rd_ceff = exp2(-dt / (rd_ * ceff)); - double y50 = y(t_vth, t0, dt, ceff); + double y50 = y(t_vth, t0, dt, ceff).first; // Match Vl. - double y20 = y(t_vl, t0, dt, ceff); + double y20 = y(t_vl, t0, dt, ceff).first; fvec_[DmpFunc::ipi] = ipiIceff(t0, dt, ceff_time, ceff); fvec_[DmpFunc::y50] = y50 - vth_; fvec_[DmpFunc::y20] = y20 - vl_; @@ -983,11 +876,13 @@ DmpPi::evalDmpEqns() (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]); + std::tie(fjac_[DmpFunc::y20][DmpParam::t0], + fjac_[DmpFunc::y20][DmpParam::dt], + fjac_[DmpFunc::y20][DmpParam::ceff]) = dy(t_vl, t0, dt, ceff); - dy(t_vth, t0, dt, ceff, fjac_[DmpFunc::y50][DmpParam::t0], - fjac_[DmpFunc::y50][DmpParam::dt], fjac_[DmpFunc::y50][DmpParam::ceff]); + std::tie(fjac_[DmpFunc::y50][DmpParam::t0], + fjac_[DmpFunc::y50][DmpParam::dt], + fjac_[DmpFunc::y50][DmpParam::ceff]) = dy(t_vth, t0, dt, ceff); if (debug_->check("dmp_ceff", 4)) { showX(); @@ -1016,23 +911,18 @@ DmpPi::ipiIceff(double, return ipi - iceff; } -void -DmpPi::V0(double t, - // Return values. - double &vo, - double &dvo_dt) +std::pair +DmpPi::V0(double t) { double exp_p1 = exp2(-p1_ * t); double exp_p2 = exp2(-p2_ * t); - vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1 + k4_ * exp_p2); - dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1 - k4_ * p2_ * exp_p2); + double vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1 + k4_ * exp_p2); + double dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1 - k4_ * p2_ * exp_p2); + return {vo, dvo_dt}; } -void -DmpPi::Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) +std::pair +DmpPi::Vl0(double t) { double D1 = k0_ * (k1_ - k2_ / p3_); double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_); @@ -1042,8 +932,9 @@ DmpPi::Vl0(double t, 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; + double vl = D1 + t + D3 * exp_p1 + D4 * exp_p2 + D5 * exp_p3; + double dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2 - D5 * p3_ * exp_p3; + return {vl, dvl_dt}; } double @@ -1076,25 +967,27 @@ DmpOnePole::evalDmpEqns() double t0 = x_[DmpParam::t0]; double dt = x_[DmpParam::dt]; - double t_vth, t_vl, ignore1, ignore2; - gateDelays(ceff_, t_vth, t_vl, ignore1); + auto [t_vth, t_vl, ignore1] = gateDelays(ceff_); + double ignore2; if (dt <= 0.0) dt = x_[DmpParam::dt] = (t_vl - t_vth) / 100; - fvec_[DmpFunc::y50] = y(t_vth, t0, dt, ceff_) - vth_; - fvec_[DmpFunc::y20] = y(t_vl, t0, dt, ceff_) - vl_; + fvec_[DmpFunc::y50] = y(t_vth, t0, dt, ceff_).first - vth_; + fvec_[DmpFunc::y20] = y(t_vl, t0, dt, ceff_).first - vl_; if (debug_->check("dmp_ceff", 4)) { showX(); showFvec(); } - dy(t_vl, t0, dt, ceff_, fjac_[DmpFunc::y20][DmpParam::t0], - fjac_[DmpFunc::y20][DmpParam::dt], ignore2); + std::tie(fjac_[DmpFunc::y20][DmpParam::t0], + fjac_[DmpFunc::y20][DmpParam::dt], + ignore2) = dy(t_vl, t0, dt, ceff_); - dy(t_vth, t0, dt, ceff_, fjac_[DmpFunc::y50][DmpParam::t0], - fjac_[DmpFunc::y50][DmpParam::dt], ignore2); + std::tie(fjac_[DmpFunc::y50][DmpParam::t0], + fjac_[DmpFunc::y50][DmpParam::dt], + ignore2) = dy(t_vth, t0, dt, ceff_); if (debug_->check("dmp_ceff", 4)) { showJacobian(); @@ -1126,19 +1019,11 @@ class DmpZeroC2 : public DmpOnePole double c2, double rpi, double c1) override; - void gateDelaySlew(// Return values. - double &delay, - double &slew) override; + std::pair gateDelaySlew() 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; + std::pair V0(double t) override; + std::pair Vl0(double t) override; double voCrossingUpperBound() override; // Pole/zero. @@ -1188,51 +1073,49 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library, k3_ = -k1_; } -void -DmpZeroC2::gateDelaySlew(// Return values. - double &delay, - double &slew) +std::pair +DmpZeroC2::gateDelaySlew() { + double delay = 0.0; + double slew = 0.0; try { findDriverParams(c1_); ceff_ = c1_; - findDriverDelaySlew(delay, slew); + std::tie(delay, slew) = findDriverDelaySlew(); driver_valid_ = true; vo_delay_ = delay; - } catch (DmpError &error) { + } + catch (DmpError &error) { fail(error.what()); // Fall back to table slew. driver_valid_ = false; ceff_ = c1_; - gateCapDelaySlew(ceff_, delay, slew); + std::tie(delay, slew) = gateCapDelaySlew(ceff_); } drvr_slew_ = slew; + return {delay, slew}; } -void -DmpZeroC2::V0(double t, - // Return values. - double &vo, - double &dvo_dt) +std::pair +DmpZeroC2::V0(double t) { double exp_p1 = exp2(-p1_ * t); - vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1); - dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1); + double vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1); + double dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1); + return {vo, dvo_dt}; } -void -DmpZeroC2::Vl0(double t, - // Return values. - double &vl, - double &dvl_dt) +std::pair +DmpZeroC2::Vl0(double t) { double D1 = k0_ * (k1_ - k2_ / p3_); double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_); double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_)); double exp_p1 = exp2(-p1_ * t); double exp_p3 = exp2(-p3_ * t); - vl = D1 + t + D3 * exp_p1 + D5 * exp_p3; - dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D5 * p3_ * exp_p3; + double vl = D1 + t + D3 * exp_p1 + D5 * exp_p3; + double dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D5 * p3_ * exp_p3; + return {vl, dvl_dt}; } double @@ -1244,38 +1127,27 @@ DmpZeroC2::voCrossingUpperBound() //////////////////////////////////////////////////////////////// // Newton-Raphson iteration to find zeros of a function. -// x_tol is percentage that all changes in x must be less than (1.0 = 100%). -// Eval(state) is called to fill fvec and fjac (returns false if fails). -// Return error msg on failure. -static void -newtonRaphson(const int max_iter, - double x[], - const int size, - const double x_tol, - std::function eval, - // Temporaries supplied by caller. - double *fvec, - double **fjac, - int *index, - double *p, - double *scale) -{ - for (int k = 0; k < max_iter; k++) { - eval(); - for (int i = 0; i < size; i++) +// driver_param_tol_ is the scale that all changes in x must be under (1.0 = 100%). +// evalDmpEqns() fills fvec_ and fjac_. +void +DmpAlg::newtonRaphson() +{ + for (int k = 0; k < newton_raphson_max_iter_; k++) { + evalDmpEqns(); + for (int i = 0; i < nr_order_; i++) // Right-hand side of linear equations. - p[i] = -fvec[i]; - luDecomp(fjac, size, index, scale); - luSolve(fjac, size, index, p); + p_[i] = -fvec_[i]; + luDecomp(); + luSolve(); bool all_under_x_tol = true; - for (int i = 0; i < size; i++) { - if (std::abs(p[i]) > std::abs(x[i]) * x_tol) + for (int i = 0; i < nr_order_; i++) { + if (std::abs(p_[i]) > std::abs(x_[i]) * driver_param_tol_) all_under_x_tol = false; - x[i] += p[i]; + x_[i] += p_[i]; } if (all_under_x_tol) { - eval(); + evalDmpEqns(); return; } } @@ -1287,41 +1159,37 @@ newtonRaphson(const int max_iter, // ftp://ftp.mcc.ac.uk/pub/matclass/libmat.tar.Z // Crout's Method of LU decomposition of square matrix, with implicit -// partial pivoting. A is overwritten. U is explicit in the upper +// partial pivoting. fjac_ is overwritten. U is explicit in the upper // triangle and L is in multiplier form in the subdiagionals i.e. subdiag // a[i,j] is the multiplier used to eliminate the [i,j] term. // -// Replaces a[0..size-1][0..size-1] by the LU decomposition. -// index[0..size-1] is an output vector of the row permutations. -// Return error msg on failure. +// Replaces fjac_[0..nr_order_-1][*] by the LU decomposition. +// index_[0..nr_order_-1] is an output vector of the row permutations. void -luDecomp(double **a, - const int size, - int *index, - // Temporary supplied by caller. - // scale stores the implicit scaling of each row. - double *scale) +DmpAlg::luDecomp() { + const int size = nr_order_; + // Find implicit scaling factors. for (int i = 0; i < size; i++) { double big = 0.0; for (int j = 0; j < size; j++) { - double temp = std::abs(a[i][j]); + double temp = std::abs(fjac_[i][j]); if (temp > big) big = temp; } if (big == 0.0) throw DmpError("LU decomposition: no non-zero row element"); - scale[i] = 1.0 / big; + scale_[i] = 1.0 / big; } int size_1 = size - 1; for (int j = 0; j < size; j++) { // Run down jth column from top to diag, to form the elements of U. for (int i = 0; i < j; i++) { - double sum = a[i][j]; + double sum = fjac_[i][j]; for (int k = 0; k < i; k++) - sum -= a[i][k] * a[k][j]; - a[i][j] = sum; + sum -= fjac_[i][k] * fjac_[k][j]; + fjac_[i][j] = sum; } // Run down jth subdiag to form the residuals after the elimination // of the first j-1 subdiags. These residuals diviyded by the @@ -1331,11 +1199,11 @@ luDecomp(double **a, double big = 0.0; int imax = 0; for (int i = j; i < size; i++) { - double sum = a[i][j]; + double sum = fjac_[i][j]; for (int k = 0; k < j; k++) - sum -= a[i][k] * a[k][j]; - a[i][j] = sum; - double dum = scale[i] * std::abs(sum); + sum -= fjac_[i][k] * fjac_[k][j]; + fjac_[i][j] = sum; + double dum = scale_[i] * std::abs(sum); if (dum >= big) { big = dum; imax = i; @@ -1345,100 +1213,56 @@ luDecomp(double **a, if (j != imax) { // Yes, do so... for (int k = 0; k < size; k++) { - double dum = a[imax][k]; - a[imax][k] = a[j][k]; - a[j][k] = dum; + double dum = fjac_[imax][k]; + fjac_[imax][k] = fjac_[j][k]; + fjac_[j][k] = dum; } - scale[imax] = scale[j]; + scale_[imax] = scale_[j]; } - index[j] = imax; + index_[j] = imax; // If diag term is not zero divide subdiag to form multipliers. - if (a[j][j] == 0.0) - a[j][j] = tiny_double; + if (fjac_[j][j] == 0.0) + fjac_[j][j] = tiny_double_; if (j != size_1) { - double pivot = 1.0 / a[j][j]; + double pivot = 1.0 / fjac_[j][j]; for (int i = j + 1; i < size; i++) - a[i][j] *= pivot; + fjac_[i][j] *= pivot; } } } -// Solves the set of size linear equations a*x=b, assuming A is LU form -// but assume b has not been transformed. -// a[0..size-1] is LU decomposition -// Returns the solution vector x in b. -// a and index are not modified. +// Solves fjac_ * x = p_ for x, assuming fjac_ is LU form from luDecomp. +// Solution overwrites p_. void -luSolve(double **a, - const int size, - const int *index, - double b[]) +DmpAlg::luSolve() { - // Transform b allowing for leading zeros. + const int size = nr_order_; + + // Transform p_ allowing for leading zeros. int non_zero = -1; for (int i = 0; i < size; i++) { - int iperm = index[i]; - double sum = b[iperm]; - b[iperm] = b[i]; + int iperm = index_[i]; + double sum = p_[iperm]; + p_[iperm] = p_[i]; if (non_zero != -1) { for (int j = non_zero; j <= i - 1; j++) - sum -= a[i][j] * b[j]; + sum -= fjac_[i][j] * p_[j]; } else { if (sum != 0.0) non_zero = i; } - b[i] = sum; + p_[i] = sum; } // Backsubstitution. for (int i = size - 1; i >= 0; i--) { - double sum = b[i]; + double sum = p_[i]; for (int j = i + 1; j < size; j++) - sum -= a[i][j] * b[j]; - b[i] = sum / a[i][i]; + sum -= fjac_[i][j] * p_[j]; + p_[i] = sum / fjac_[i][i]; } } -#if 0 -// Solve: -// x + y = 5 -// x - y = 1 -// x = 3 -// y = 2 -void -testLuDecomp1() -{ - double a0[2] = {1, 1}; - double a1[2] = {1, -1}; - double *a[2] = {a0, a1}; - int index[2]; - double b[2] = {5, 1}; - double scale[2]; - luDecomp(a, 2, index, scale); - luSolve(a, 2, index, b); - printf("x = %f y= %f\n", b[0], b[1]); -} - -// Solve -// x + 2y = 3 -// 3x - 4y = 19 -// x = 5 -// y = -1 -void -testLuDecomp2() -{ - double a0[2] = {1, 2}; - double a1[2] = {3, -4}; - double *a[2] = {a0, a1}; - int index[2]; - double b[2] = {3, 19}; - double scale[2]; - luDecomp(a, 2, index, scale); - luSolve(a, 2, index, b); - printf("x = %f y= %f\n", b[0], b[1]); -} -#endif - //////////////////////////////////////////////////////////////// bool DmpCeffDelayCalc::unsuppored_model_warned_ = false; @@ -1484,8 +1308,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, 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); + auto [gate_delay, drvr_slew] = gateDelaySlew(); // Fill in pocv parameters. double ceff = dmp_alg_->ceff(); @@ -1640,22 +1463,19 @@ gateModelRd(const LibertyCell *cell, return rd; } -void -DmpCeffDelayCalc::gateDelaySlew( // Return values. - double &delay, - double &slew) +std::pair +DmpCeffDelayCalc::gateDelaySlew() { - dmp_alg_->gateDelaySlew(delay, slew); + return dmp_alg_->gateDelaySlew(); } -void +std::optional> DmpCeffDelayCalc::loadDelaySlewElmore(const Pin *load_pin, - double elmore, - double &delay, - double &slew) + double elmore) { if (dmp_alg_) - dmp_alg_->loadDelaySlew(load_pin, elmore, delay, slew); + return dmp_alg_->loadDelaySlew(load_pin, elmore); + return std::nullopt; } // Notify algorithm components. diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index 4d3b7fd57..065a00267 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -24,6 +24,9 @@ #pragma once +#include +#include + #include "LibertyClass.hh" #include "LumpedCapDelayCalc.hh" @@ -71,13 +74,10 @@ protected: // Return values. double &wire_delay, double &load_slew) = 0; - void gateDelaySlew(// Return values. - double &delay, - double &slew); - void loadDelaySlewElmore(const Pin *load_pin, - double elmore, - double &delay, - double &slew); + std::pair gateDelaySlew(); + std::optional> + loadDelaySlewElmore(const Pin *load_pin, + double elmore); // 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 3fefc53b1..d89b319d0 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -125,8 +125,12 @@ DmpCeffElmoreDelayCalc::loadDelaySlew(const Pin *load_pin, float elmore = 0.0; if (parasitic) parasitics_->findElmore(parasitic, load_pin, elmore, elmore_exists); - if (elmore_exists) - loadDelaySlewElmore(load_pin, elmore, wire_delay, load_slew); + if (elmore_exists) { + if (auto r = loadDelaySlewElmore(load_pin, elmore)) { + wire_delay = r->first; + load_slew = r->second; + } + } thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); } diff --git a/dcalc/FindRoot.cc b/dcalc/FindRoot.cc index dd35e7ba6..5ca987df2 100644 --- a/dcalc/FindRoot.cc +++ b/dcalc/FindRoot.cc @@ -28,47 +28,38 @@ namespace sta { -double +std::pair findRoot(FindRootFunc func, double x1, double x2, double x_tol, - int max_iter, - // Return value. - bool &fail) + int max_iter) { double y1, y2, dy1; func(x1, y1, dy1); func(x2, y2, dy1); - return findRoot(func, x1, y1, x2, y2, x_tol, max_iter, fail); + return findRoot(func, x1, y1, x2, y2, x_tol, max_iter); } -double +std::pair findRoot(FindRootFunc func, double x1, double y1, double x2, double y2, double x_tol, - int max_iter, - // Return value. - bool &fail) + int max_iter) { if ((y1 > 0.0 && y2 > 0.0) || (y1 < 0.0 && y2 < 0.0)) { // Initial bounds do not surround a root. - fail = true; - return 0.0; + return {0.0, true}; } - if (y1 == 0.0) { - fail = false; - return x1; - } + if (y1 == 0.0) + return {x1, false}; - if (y2 == 0.0) { - fail = false; - return x2; - } + if (y2 == 0.0) + return {x2, false}; if (y1 > 0.0) // Swap x1/x2 so func(x1) < 0. @@ -95,8 +86,7 @@ findRoot(FindRootFunc func, } if (std::abs(dx) <= x_tol * std::abs(root)) { // Converged. - fail = false; - return root; + return {root, false}; } func(root, y, dy); @@ -105,8 +95,7 @@ findRoot(FindRootFunc func, else x2 = root; } - fail = true; - return root; + return {root, true}; } } // namespace diff --git a/dcalc/FindRoot.hh b/dcalc/FindRoot.hh index a137b48ae..ab35ab4e4 100644 --- a/dcalc/FindRoot.hh +++ b/dcalc/FindRoot.hh @@ -25,6 +25,7 @@ #pragma once #include +#include namespace sta { @@ -33,24 +34,22 @@ using FindRootFunc = const std::function; -double +// first: root estimate; second: true if the search failed. +std::pair findRoot(FindRootFunc func, double x1, double x2, double x_tol, - int max_iter, - // Return value. - bool &fail); + int max_iter); -double +// first: root estimate; second: true if the search failed. +std::pair findRoot(FindRootFunc func, double x1, double y1, double x2, double y2, double x_tol, - int max_iter, - // Return value. - bool &fail); + int max_iter); } // namespace From fe23c4530fa9b0e86018d0e64e9e00244e98d2bb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 3 Apr 2026 16:46:13 -0700 Subject: [PATCH 137/181] regression.tcl Signed-off-by: James Cherry --- test/regression.tcl | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/test/regression.tcl b/test/regression.tcl index 8afefbf4d..cc9d2cbc7 100755 --- a/test/regression.tcl +++ b/test/regression.tcl @@ -176,11 +176,10 @@ proc run_tests {} { run_test $test } } - write_failure_file } proc run_test { test } { - global result_dir diff_file errors diff_options + global result_dir diff_file errors diff_options failed_tests puts -nonewline $test flush stdout @@ -267,8 +266,6 @@ proc run_tests_parallel {} { vwait reg_parallel_job_done } } - # update results/failures and results/diffs - write_failure_file } } @@ -432,26 +429,21 @@ proc test_failed { test reason } { } lappend failed_tests $test incr errors($reason) + append_diff_file $test } -proc write_failure_file {} { - global failure_file failed_tests failed_tests_summery +proc append_diff_file { test } { + global failure_file global diff_file diff_options set fail_ch [open $failure_file "a"] - foreach test $failed_tests { - if { ![info exists failed_tests_summery($test)] } { - puts $fail_ch $test - - # Append diff to results/diffs - set log_file [test_log_file $test] - set ok_file [test_ok_file $test] - catch [concat exec diff $diff_options $ok_file $log_file >> $diff_file] - - set failed_tests_summery($test) 1 - } - } + puts $fail_ch $test close $fail_ch + + # Append diff to results/diffs + set log_file [test_log_file $test] + set ok_file [test_ok_file $test] + catch [concat exec diff $diff_options $ok_file $log_file >> $diff_file] } # Error messages can be found in "valgrind/memcheck/mc_errcontext.c". @@ -530,6 +522,10 @@ proc show_summary {} { global app_path app puts "------------------------------------------------------" + if { $valgrind_shared_lib_failure } { + puts "WARNING: valgrind failed because the executable is not statically linked." + } + puts "See $result_dir for log files" set test_count [llength $tests] if { [found_errors] } { if { $errors(error) != 0 } { @@ -556,10 +552,6 @@ proc show_summary {} { } else { puts "Passed $test_count" } - if { $valgrind_shared_lib_failure } { - puts "WARNING: valgrind failed because the executable is not statically linked." - } - puts "See $result_dir for log files" } proc found_errors {} { From aedddcecd86cc325947a538d84ddd570134ed31f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 4 Apr 2026 09:21:01 -0700 Subject: [PATCH 138/181] DMP use std::array Signed-off-by: James Cherry --- dcalc/DmpCeff.cc | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 216b71c93..108bf7989 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -33,6 +33,7 @@ #include "DmpCeff.hh" #include +#include #include #include #include @@ -201,13 +202,12 @@ class DmpAlg : public StaState static constexpr int max_nr_order_ = 3; - double x_[max_nr_order_]; - double fvec_[max_nr_order_]; - double fjac_storage_[max_nr_order_ * max_nr_order_]; - double *fjac_[max_nr_order_]; - double scale_[max_nr_order_]; - double p_[max_nr_order_]; - int index_[max_nr_order_]; + std::array x_; + std::array fvec_; + std::array, max_nr_order_> fjac_; + std::array scale_; + std::array p_; + std::array index_; // Driver slew used to check load delay. double drvr_slew_; @@ -237,9 +237,6 @@ DmpAlg::DmpAlg(int nr_order, c1_(0.0), nr_order_(nr_order) { - for (int i = 0; i < nr_order_; i++) - // Only use the upper left block of the matrix - fjac_[i] = fjac_storage_ + i * max_nr_order_; } void From 84150e925b96cde239336722d56d4ff916a9b809 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 4 Apr 2026 09:21:34 -0700 Subject: [PATCH 139/181] prima ceff Signed-off-by: James Cherry --- dcalc/PrimaDelayCalc.cc | 53 ++++++++++++++++++++++++++++++++++------- dcalc/PrimaDelayCalc.hh | 6 +++++ 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 9cea3ea65..be0a3d895 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -194,8 +194,8 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin, 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); + ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, + scene, min_max); return dcalc_results[0]; } @@ -399,6 +399,7 @@ void PrimaDelayCalc::initSim() { ceff_.resize(drvr_count_); + ceff_vth_.resize(drvr_count_); drvr_current_.resize(drvr_count_); findNodeCount(); @@ -615,8 +616,12 @@ PrimaDelayCalc::updateCeffIdrvr() if (drvr_rf_ == RiseFall::rise()) { if (drvr_current != 0.0 && dv > 0.0) { double ceff = drvr_current * time_step_ / dv; - if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) + if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) { ceff_[drvr_idx] = ceff; + // Record the Ceff at Vth. + if (v1 >= vth_ && v2 < vth_) + ceff_vth_[drvr_idx] = ceff; + } } if (v1 > (vdd_ - .01)) // Whoa partner. Head'n for the weeds. @@ -628,8 +633,12 @@ PrimaDelayCalc::updateCeffIdrvr() else { if (drvr_current != 0.0 && dv < 0.0) { double ceff = drvr_current * time_step_ / dv; - if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) + if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) { ceff_[drvr_idx] = ceff; + // Record the Ceff at Vth. + if (v1 <= vth_ && v2 > vth_) + ceff_vth_[drvr_idx] = ceff; + } } if (v1 < 0.01) { // Whoa partner. Head'n for the weeds. @@ -711,8 +720,13 @@ PrimaDelayCalc::dcalcResults() float ref_time = output_waveforms_[drvr_idx]->referenceTime(dcalc_arg.inSlewFlt()); 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); + + ArcDelay gate_delay2(gate_delay); + Slew drvr_slew2(drvr_slew); + delaySlewPocv(dcalc_arg, drvr_idx, gate_delay2, drvr_slew2); + dcalc_result.setGateDelay(gate_delay2); + dcalc_result.setDrvrSlew(drvr_slew2); + debugPrint(debug_, "ccs_dcalc", 2, "{} gate delay {} slew {}", network_->pathName(drvr_pin), delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); @@ -740,6 +754,28 @@ PrimaDelayCalc::dcalcResults() return dcalc_results; } +// Fill in pocv parameters in gate_delay/drvr_slew. +void +PrimaDelayCalc::delaySlewPocv(ArcDcalcArg &dcalc_arg, + size_t drvr_idx, + ArcDelay &gate_delay, + Slew &drvr_slew) +{ + if (variables_->pocvEnabled()) { + GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(scene_, min_max_); + if (table_model) { + double ceff = ceff_vth_[drvr_idx]; + if (ceff == 0.0) + ceff = dcalc_arg.loadCap(); + float in_slew = delayAsFloat(dcalc_arg.inSlew()); + const Pvt *pvt = pinPvt(dcalc_arg.drvrPin(), scene_, min_max_); + table_model->gateDelayPocv(pvt, in_slew, ceff, min_max_, + variables_->pocvMode(), + gate_delay, drvr_slew); + } + } +} + //////////////////////////////////////////////////////////////// void @@ -895,7 +931,7 @@ std::string PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, - float load_cap, + float, const Parasitic *, const LoadPinIndexMap &, const Scene *scene, @@ -905,8 +941,9 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, GateTimingModel *model = arc->gateModel(scene, min_max); if (model) { float in_slew1 = delayAsFloat(in_slew); + float ceff = ceff_vth_[0]; return model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), - in_slew1, load_cap, min_max, + in_slew1, ceff, min_max, PocvMode::scalar, digits); } return ""; diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index 8ed47a053..c0b39ba77 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -108,6 +108,10 @@ public: Waveform watchWaveform(const Pin *pin) override; protected: + void delaySlewPocv(ArcDcalcArg &dcalc_arg, + size_t drvr_idx, + ArcDelay &gate_delay, + Slew &drvr_slew); ArcDcalcResultSeq tableDcalcResults(); void simulate(); void simulate1(const MatrixSd &G, @@ -215,6 +219,8 @@ protected: // Indexed by driver index. std::vector ceff_; + // Ceff at Vth + std::vector ceff_vth_; std::vector drvr_current_; double time_step_; From e3d1bb09b0b6ab1af248ca463d7ad2dcea028f87 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 4 Apr 2026 09:26:03 -0700 Subject: [PATCH 140/181] Network [[nodiscard]] Signed-off-by: James Cherry --- include/sta/Network.hh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/sta/Network.hh b/include/sta/Network.hh index b12db2db0..32dd25b3f 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -307,18 +307,18 @@ public: [[nodiscard]] bool isHierarchical(const Pin *pin) const; [[nodiscard]] bool isTopLevelPort(const Pin *pin) const; // Is pin inside the instance hier_pin is attached to? - bool isInside(const Pin *pin, - const Pin *hier_pin) const; + [[nodiscard]] bool isInside(const Pin *pin, + const Pin *hier_pin) const; // Is pin inside of hier_inst? - bool isInside(const Pin *pin, - const Instance *hier_inst) const; - bool isDriver(const Pin *pin) const; - bool isLoad(const Pin *pin) const; + [[nodiscard]] bool isInside(const Pin *pin, + const Instance *hier_inst) const; + [[nodiscard]] bool isDriver(const Pin *pin) const; + [[nodiscard]] bool isLoad(const Pin *pin) const; // Has register/latch rise/fall edges from pin. - bool isRegClkPin(const Pin *pin) const; + [[nodiscard]] bool isRegClkPin(const Pin *pin) const; // Pin clocks a timing check. - bool isCheckClk(const Pin *pin) const; - bool isLatchData(const Pin *pin) const; + [[nodiscard]] bool isCheckClk(const Pin *pin) const; + [[nodiscard]] bool isLatchData(const Pin *pin) const; // Iterate over all of the pins connected to a pin and the parent // and child nets it is hierarchically connected to (port, leaf and From d6e826ef8baf2ae67fe3e12322248814a5dd0d5a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 4 Apr 2026 14:58:25 -0700 Subject: [PATCH 141/181] save_ok hook Signed-off-by: James Cherry --- test/regression.tcl | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/test/regression.tcl b/test/regression.tcl index cc9d2cbc7..3afe8287e 100755 --- a/test/regression.tcl +++ b/test/regression.tcl @@ -582,22 +582,27 @@ proc save_ok_main {} { } } else { foreach test $argv { - save_ok $test + if { [lsearch [group_tests "all"] $test] == -1 } { + puts "Error: test $test not found." + } else { + save_ok $test + } } } } +# hook for pvt/public sync. proc save_ok { test } { - if { [lsearch [group_tests "all"] $test] == -1 } { - puts "Error: test $test not found." + save_ok_file $test +} + +proc save_ok_file { test } { + set ok_file [test_ok_file $test] + set log_file [test_log_file $test] + if { ! [file exists $log_file] } { + puts "Error: log file $log_file not found." } else { - set ok_file [test_ok_file $test] - set log_file [test_log_file $test] - if { ! [file exists $log_file] } { - puts "Error: log file $log_file not found." - } else { - file copy -force $log_file $ok_file - } + file copy -force $log_file $ok_file } } From 548b66541253089fcf01edbaabc55b85f90c475a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 4 Apr 2026 16:19:41 -0700 Subject: [PATCH 142/181] get_* -filter allow true/false, '.' in glob pattern resolves #416 Signed-off-by: James Cherry --- include/sta/FilterObjects.hh | 11 ----- sdc/FilterObjects.cc | 72 +++++++++++++++----------------- sdc/Sdc.i | 55 ++++++++++-------------- sdc/Sdc.tcl | 16 +++---- search/Property.cc | 2 +- tcl/Sta.tcl | 2 +- test/asap7_small.lib.gz | Bin 72249 -> 90153 bytes test/get_filter.ok | 32 +++++++------- test/get_filter.tcl | 48 ++++++++++++--------- test/get_lib_pins_of_objects.ok | 2 + test/get_noargs.ok | 3 ++ test/get_objrefs.ok | 3 ++ 12 files changed, 118 insertions(+), 128 deletions(-) diff --git a/include/sta/FilterObjects.hh b/include/sta/FilterObjects.hh index bbabd4b02..7420b4507 100644 --- a/include/sta/FilterObjects.hh +++ b/include/sta/FilterObjects.hh @@ -40,67 +40,56 @@ class Report; PortSeq filterPorts(std::string_view filter_expression, PortSeq *objects, - bool bool_props_as_int, Sta *sta); InstanceSeq filterInstances(std::string_view filter_expression, InstanceSeq *objects, - bool bool_props_as_int, Sta *sta); PinSeq filterPins(std::string_view filter_expression, PinSeq *objects, - bool bool_props_as_int, Sta *sta); NetSeq filterNets(std::string_view filter_expression, NetSeq *objects, - bool bool_props_as_int, Sta *sta); ClockSeq filterClocks(std::string_view filter_expression, ClockSeq *objects, - bool bool_props_as_int, Sta *sta); LibertyCellSeq filterLibCells(std::string_view filter_expression, LibertyCellSeq *objects, - bool bool_props_as_int, Sta *sta); LibertyPortSeq filterLibPins(std::string_view filter_expression, LibertyPortSeq *objects, - bool bool_props_as_int, Sta *sta); LibertyLibrarySeq filterLibertyLibraries(std::string_view filter_expression, LibertyLibrarySeq *objects, - bool bool_props_as_int, Sta *sta); EdgeSeq filterTimingArcs(std::string_view filter_expression, EdgeSeq *objects, - bool bool_props_as_int, Sta *sta); PathEndSeq filterPathEnds(std::string_view filter_expression, PathEndSeq *objects, - bool bool_props_as_int, Sta *sta); // For FilterExpr unit tests. StringSeq filterExprToPostfix(std::string_view expr, - bool bool_props_as_int, Report *report); } // namespace diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc index 2e18473e1..6e263037b 100644 --- a/sdc/FilterObjects.cc +++ b/sdc/FilterObjects.cc @@ -73,9 +73,9 @@ class FilterExpr FilterExpr(std::string_view expression, Report *report); - std::vector> postfix(bool bool_props_as_int); + std::vector> postfix(); private: - std::vector> lex(bool bool_props_as_int); + std::vector> lex(); std::vector> shuntingYard(std::vector> &infix); std::string raw_; @@ -106,20 +106,21 @@ FilterExpr::FilterExpr(std::string_view expression, } std::vector> -FilterExpr::postfix(bool bool_props_as_int) +FilterExpr::postfix() { - auto infix = lex(bool_props_as_int); + auto infix = lex(); return shuntingYard(infix); } std::vector> -FilterExpr::lex(bool bool_props_as_int) +FilterExpr::lex() { std::vector> token_regexes = { {std::regex("^\\s+"), Token::Kind::skip}, {std::regex("^defined\\(([a-zA-Z_]+)\\)"), Token::Kind::defined}, {std::regex("^undefined\\(([a-zA-Z_]+)\\)"), Token::Kind::undefined}, - {std::regex("^@?([a-zA-Z_]+) *((==|!=|=~|!~) *([0-9a-zA-Z_\\/$\\[\\]*?]+))?"), Token::Kind::predicate}, + {std::regex("^@?([a-zA-Z_]+) *((==|!=|=~|!~) *([0-9a-zA-Z_\\/$\\[\\]*?.]+))?"), + Token::Kind::predicate}, {std::regex("^(&&)"), Token::Kind::op_and}, {std::regex("^(\\|\\|)"), Token::Kind::op_or}, {std::regex("^(!)"), Token::Kind::op_inv}, @@ -139,9 +140,9 @@ FilterExpr::lex(bool bool_props_as_int) std::string property = token_match[1].str(); // The default operation on a predicate if an op and arg are - // omitted is 'arg == 1' / 'arg == true'. + // omitted is 'prop == 1 || true'. std::string op = "=="; - std::string arg = (bool_props_as_int ? "1" : "true"); + std::string arg = "1"; if (token_match[2].length() != 0) { op = token_match[3].str(); @@ -250,13 +251,18 @@ filterObjects(const char *property, bool not_pattern_match = stringEq(op, "!~"); for (T *object : all) { PropertyValue value = properties.getProperty(object, property); - std::string prop_str = value.to_string(network); - const char *prop = prop_str.c_str(); - if (prop && - ((exact_match && stringEq(prop, pattern)) - || (not_match && !stringEq(prop, pattern)) - || (pattern_match && patternMatch(pattern, prop)) - || (not_pattern_match && !patternMatch(pattern, prop)))) + std::string prop = value.to_string(network); + if (value.type() == PropertyValue::Type::bool_) { + // Canonicalize bool true/false to 1/0. + if (stringEqual(pattern, "true")) + pattern = "1"; + else if (stringEqual(pattern, "false")) + pattern = "0"; + } + if ((exact_match && stringEq(prop.c_str(), pattern)) + || (not_match && !stringEq(prop.c_str(), pattern)) + || (pattern_match && patternMatch(pattern, prop)) + || (not_pattern_match && !patternMatch(pattern, prop))) filtered_objects.insert(object); } return filtered_objects; @@ -265,7 +271,6 @@ filterObjects(const char *property, template std::vector filterObjects(std::string_view filter_expression, std::vector *objects, - bool bool_props_as_int, Sta *sta) { Report *report = sta->report(); @@ -278,7 +283,7 @@ filterObjects(std::string_view filter_expression, all.insert(object); FilterExpr filter(filter_expression, report); - auto postfix = filter.postfix(bool_props_as_int); + auto postfix = filter.postfix(); std::stack> eval_stack; for (auto &token : postfix) { if (token->kind == FilterExpr::Token::Kind::op_or) { @@ -405,100 +410,89 @@ filterObjects(std::string_view filter_expression, PortSeq filterPorts(std::string_view filter_expression, PortSeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } InstanceSeq filterInstances(std::string_view filter_expression, InstanceSeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } PinSeq filterPins(std::string_view filter_expression, PinSeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } NetSeq filterNets(std::string_view filter_expression, NetSeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } ClockSeq filterClocks(std::string_view filter_expression, ClockSeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } LibertyCellSeq filterLibCells(std::string_view filter_expression, LibertyCellSeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } LibertyPortSeq filterLibPins(std::string_view filter_expression, LibertyPortSeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } LibertyLibrarySeq filterLibertyLibraries(std::string_view filter_expression, LibertyLibrarySeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } EdgeSeq filterTimingArcs(std::string_view filter_expression, EdgeSeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } PathEndSeq filterPathEnds(std::string_view filter_expression, PathEndSeq *objects, - bool bool_props_as_int, Sta *sta) { - return filterObjects(filter_expression, objects, bool_props_as_int, sta); + return filterObjects(filter_expression, objects, sta); } StringSeq filterExprToPostfix(std::string_view expr, - bool bool_props_as_int, Report *report) { FilterExpr filter(expr, report); - auto postfix = filter.postfix(bool_props_as_int); + auto postfix = filter.postfix(); StringSeq result; for (auto &token : postfix) result.push_back(token->text); diff --git a/sdc/Sdc.i b/sdc/Sdc.i index eccd234ec..601077040 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -1493,101 +1493,90 @@ find_register_output_pins(ClockSet *clks, PortSeq filter_ports(const char *filter_expression, - PortSeq *ports, - bool bool_props_as_int) + PortSeq *ports) { sta::Sta *sta = Sta::sta(); - return filterPorts(filter_expression, ports, bool_props_as_int, sta); + return filterPorts(filter_expression, ports, sta); } InstanceSeq filter_insts(const char *filter_expression, - InstanceSeq *insts, - bool bool_props_as_int) + InstanceSeq *insts) { sta::Sta *sta = Sta::sta(); - return filterInstances(filter_expression, insts, bool_props_as_int, sta); + return filterInstances(filter_expression, insts, sta); } PinSeq filter_pins(const char *filter_expression, - PinSeq *pins, - bool bool_props_as_int) + PinSeq *pins) { sta::Sta *sta = Sta::sta(); - return filterPins(filter_expression, pins, bool_props_as_int, sta); + return filterPins(filter_expression, pins, sta); } NetSeq filter_nets(const char *filter_expression, - NetSeq *nets, - bool bool_props_as_int) + NetSeq *nets) { sta::Sta *sta = Sta::sta(); - return filterNets(filter_expression, nets, bool_props_as_int, sta); + return filterNets(filter_expression, nets, sta); } ClockSeq filter_clocks(const char *filter_expression, - ClockSeq *clocks, - bool bool_props_as_int) + ClockSeq *clocks) { sta::Sta *sta = Sta::sta(); - return filterClocks(filter_expression, clocks, bool_props_as_int, sta); + return filterClocks(filter_expression, clocks, sta); } LibertyCellSeq filter_lib_cells(const char *filter_expression, - LibertyCellSeq *cells, - bool bool_props_as_int) + LibertyCellSeq *cells) { sta::Sta *sta = Sta::sta(); - return filterLibCells(filter_expression, cells, bool_props_as_int, sta); + return filterLibCells(filter_expression, cells, sta); } LibertyPortSeq filter_lib_pins(const char *filter_expression, - LibertyPortSeq *pins, - bool bool_props_as_int) + LibertyPortSeq *pins) { sta::Sta *sta = Sta::sta(); - return filterLibPins(filter_expression, pins, bool_props_as_int, sta); + return filterLibPins(filter_expression, pins, sta); } LibertyLibrarySeq filter_liberty_libraries(const char *filter_expression, - LibertyLibrarySeq *libs, - bool bool_props_as_int) + LibertyLibrarySeq *libs) { sta::Sta *sta = Sta::sta(); - return filterLibertyLibraries(filter_expression, libs, bool_props_as_int, sta); + return filterLibertyLibraries(filter_expression, libs, sta); } EdgeSeq filter_timing_arcs(const char *filter_expression, - EdgeSeq *edges, - bool bool_props_as_int) + EdgeSeq *edges) { sta::Sta *sta = Sta::sta(); - return filterTimingArcs(filter_expression, edges, bool_props_as_int, sta); + return filterTimingArcs(filter_expression, edges, sta); } PathEndSeq filter_path_ends(const char *filter_expression, - PathEndSeq *path_ends, - bool bool_props_as_int) + PathEndSeq *path_ends) { sta::Sta *sta = Sta::sta(); - return filterPathEnds(filter_expression, path_ends, bool_props_as_int, sta); + return filterPathEnds(filter_expression, path_ends, sta); } // For FilterExpr unit tests. StringSeq -filter_expr_to_postfix(const char* expr, - bool bool_props_as_int) +filter_expr_to_postfix(const char* expr) { Report *report = Sta::sta()->report(); - return filterExprToPostfix(expr, bool_props_as_int, report); + return filterExprToPostfix(expr, report); } //////////////////////////////////////////////////////////////// diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index dd5374cdc..d8e5aef1a 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -393,7 +393,7 @@ proc get_cells { args } { } } if [info exists keys(-filter)] { - set insts [filter_insts $keys(-filter) $insts 1] + set insts [filter_insts $keys(-filter) $insts] } return $insts } @@ -436,7 +436,7 @@ proc get_clocks { args } { } } if [info exists keys(-filter)] { - set clocks [filter_clocks $keys(-filter) $clocks 1] + set clocks [filter_clocks $keys(-filter) $clocks] } return $clocks } @@ -517,7 +517,7 @@ proc get_lib_cells { args } { } } if [info exists keys(-filter)] { - set cells [filter_lib_cells $keys(-filter) $cells 1] + set cells [filter_lib_cells $keys(-filter) $cells] } return $cells } @@ -621,7 +621,7 @@ proc get_lib_pins { args } { } } if [info exists keys(-filter)] { - set ports [filter_lib_pins $keys(-filter) $ports 1] + set ports [filter_lib_pins $keys(-filter) $ports] } return $ports } @@ -671,7 +671,7 @@ proc get_libs { args } { } } if [info exists keys(-filter)] { - set libs [filter_liberty_libraries $keys(-filter) $libs 1] + set libs [filter_liberty_libraries $keys(-filter) $libs] } return $libs } @@ -772,7 +772,7 @@ proc get_nets { args } { } } if [info exists keys(-filter)] { - set nets [filter_nets $keys(-filter) $nets 1] + set nets [filter_nets $keys(-filter) $nets] } return $nets } @@ -863,7 +863,7 @@ proc get_pins { args } { } } if [info exists keys(-filter)] { - set pins [filter_pins $keys(-filter) $pins 1] + set pins [filter_pins $keys(-filter) $pins] } return $pins } @@ -919,7 +919,7 @@ proc get_ports { args } { } } if [info exists keys(-filter)] { - set ports [filter_ports $keys(-filter) $ports 1] + set ports [filter_ports $keys(-filter) $ports] } return $ports } diff --git a/search/Property.cc b/search/Property.cc index cdb0c0885..067298d79 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -564,7 +564,7 @@ PropertyValue::to_string(const Network *network) const case Type::float_: return unit_->asString(float_, 6); case Type::bool_: - // true/false would be better but these are TCL true/false values. + // These are TCL true/false values. if (bool_) return "1"; else diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index f6daed10b..8796df34b 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -300,7 +300,7 @@ proc get_timing_edges_cmd { cmd cmd_args } { cmd_usage_error $cmd } if [info exists keys(-filter)] { - set arcs [filter_timing_arcs $keys(-filter) $arcs 1] + set arcs [filter_timing_arcs $keys(-filter) $arcs] } return $arcs } diff --git a/test/asap7_small.lib.gz b/test/asap7_small.lib.gz index 3c47419fcd41fca93e55ccf1dec192ec885b822d..f0381ded3fe85f21efba168bd2d5210ca5f99669 100644 GIT binary patch literal 90153 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

2U(^*{0Im?U6;g$bpJm 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⪚H3?2-y0h?i2*; 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

%;lb+P{ur&aFZ9xTf1 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 z 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%0A8hk_fC7d(`pQupM6Hr2* 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 Date: Tue, 7 Apr 2026 10:45:04 -0700 Subject: [PATCH 143/181] report_dcalc prima resolves #418 Signed-off-by: James Cherry --- dcalc/PrimaDelayCalc.cc | 9 ++++--- test/prima3.ok | 52 +++++++++++++++++++++++++++++++++++++++++ test/prima3.tcl | 1 + 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index be0a3d895..561b7953c 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -931,15 +931,18 @@ std::string PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, - float, - const Parasitic *, - const LoadPinIndexMap &, + float load_cap, + const Parasitic *parasitic, + const LoadPinIndexMap &load_pin_index_map, const Scene *scene, const MinMax *min_max, int digits) { GateTimingModel *model = arc->gateModel(scene, min_max); if (model) { + // Delay calc to find ceff. + gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, + load_pin_index_map, scene, min_max); float in_slew1 = delayAsFloat(in_slew); float ceff = ceff_vth_[0]; return model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), diff --git a/test/prima3.ok b/test/prima3.ok index a4ac1d9c8..cd2c8cac4 100644 --- a/test/prima3.ok +++ b/test/prima3.ok @@ -31,3 +31,55 @@ Path Type: max 228.48 slack (MET) +Library: asap7_small +Cell: BUFx2_ASAP7_75t_R +Arc sense: positive_unate +Arc type: combinational +A ^ -> Y ^ +P = 1.00 V = 0.70 T = 25.00 +------- input_net_transition = 59.28 +| total_output_net_capacitance = 13.54 +| 11.52 23.04 +v -------------------- +40.00 | 48.68 71.50 +80.00 | 56.23 79.10 +Table value = 56.33 +PVT scale factor = 1.00 +Delay = 56.33 + +------- input_net_transition = 59.28 +| total_output_net_capacitance = 13.54 +| 11.52 23.04 +v -------------------- +40.00 | 53.99 104.08 +80.00 | 54.58 104.40 +Table value = 63.04 +PVT scale factor = 1.00 +Slew = 63.04 + +............................................. + +A v -> Y v +P = 1.00 V = 0.70 T = 25.00 +------- input_net_transition = 52.93 +| total_output_net_capacitance = 12.09 +| 11.52 23.04 +v -------------------- +40.00 | 48.42 67.20 +80.00 | 57.92 76.86 +Table value = 52.43 +PVT scale factor = 1.00 +Delay = 52.43 + +------- input_net_transition = 52.93 +| total_output_net_capacitance = 12.09 +| 11.52 23.04 +v -------------------- +40.00 | 42.77 80.89 +80.00 | 43.84 81.48 +Table value = 45.00 +PVT scale factor = 1.00 +Slew = 45.00 + +............................................. + diff --git a/test/prima3.tcl b/test/prima3.tcl index b2e20eab3..0b285c728 100644 --- a/test/prima3.tcl +++ b/test/prima3.tcl @@ -9,3 +9,4 @@ set_propagated_clock {clk1 clk2 clk3} read_spef reg1_asap7.spef sta::set_delay_calculator prima report_checks -fields {input_pins slew} -format full_clock +report_dcalc -from u1/A -to u1/Y From c887b2e4b37df2e17f324ee993e164ed5a704b5e Mon Sep 17 00:00:00 2001 From: Deepashree Sengupta Date: Tue, 7 Apr 2026 14:00:01 -0400 Subject: [PATCH 144/181] Bias pin handling (#409) * Update STA to exclude bias pins from timing graph and subsequently in write_verilog Signed-off-by: dsengupta0628 * unnecessary space in orig verilog Signed-off-by: dsengupta0628 * Update to use well supplies rather than bias pins Signed-off-by: dsengupta0628 --------- Signed-off-by: dsengupta0628 --- include/sta/PortDirection.hh | 5 ++++- liberty/LibertyReader.cc | 6 ++++++ liberty/LibertyWriter.cc | 4 +++- network/PortDirection.cc | 11 +++++++++-- test/regression_vars.tcl | 1 + test/verilog_well_supplies.ok | 26 ++++++++++++++++++++++++++ test/verilog_well_supplies.tcl | 12 ++++++++++++ test/verilog_well_supplies.v | 17 +++++++++++++++++ verilog/VerilogWriter.cc | 2 ++ 9 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 test/verilog_well_supplies.ok create mode 100644 test/verilog_well_supplies.tcl create mode 100644 test/verilog_well_supplies.v diff --git a/include/sta/PortDirection.hh b/include/sta/PortDirection.hh index 0b8186f90..1d54271cc 100644 --- a/include/sta/PortDirection.hh +++ b/include/sta/PortDirection.hh @@ -41,6 +41,7 @@ public: static PortDirection *internal() { return internal_; } static PortDirection *ground() { return ground_; } static PortDirection *power() { return power_; } + static PortDirection *well() { return well_; } static PortDirection *unknown() { return unknown_; } static PortDirection *find(const char *dir_name); std::string_view name() const { return name_; } @@ -57,7 +58,8 @@ public: bool isAnyTristate() const; bool isGround() const { return this == ground_; } bool isPower() const { return this == power_; } - // Ground or power. + bool isWell() const { return this == well_; } + // Ground, power, or well. bool isPowerGround() const; bool isInternal() const { return this == internal_; } bool isUnknown() const { return this == unknown_; } @@ -76,6 +78,7 @@ private: static PortDirection *internal_; static PortDirection *ground_; static PortDirection *power_; + static PortDirection *well_; static PortDirection *unknown_; }; diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index b473b80ee..a76b68934 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -1215,6 +1215,12 @@ LibertyReader::makePgPinPort(LibertyCell *cell, case PwrGndType::internal_power: dir = PortDirection::power(); break; + case PwrGndType::nwell: + case PwrGndType::pwell: + case PwrGndType::deepnwell: + case PwrGndType::deeppwell: + dir = PortDirection::well(); + break; case PwrGndType::none: error(1291, pg_pin_group, "unknown pg_type."); break; diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index a27490fb5..79d4f7805 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -569,7 +569,9 @@ LibertyWriter::asString(const PortDirection *dir) 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() + || dir == PortDirection::well()) return "input"; return "unknown"; } diff --git a/network/PortDirection.cc b/network/PortDirection.cc index 5a6aaae5b..608eb2b01 100644 --- a/network/PortDirection.cc +++ b/network/PortDirection.cc @@ -35,6 +35,7 @@ PortDirection *PortDirection::bidirect_; PortDirection *PortDirection::internal_; PortDirection *PortDirection::ground_; PortDirection *PortDirection::power_; +PortDirection *PortDirection::well_; PortDirection *PortDirection::unknown_; void @@ -47,7 +48,8 @@ PortDirection::init() internal_ = new PortDirection("internal", 4); ground_ = new PortDirection("ground", 5); power_ = new PortDirection("power", 6); - unknown_ = new PortDirection("unknown", 7); + well_ = new PortDirection("well", 7); + unknown_ = new PortDirection("unknown", 8); } void @@ -67,6 +69,8 @@ PortDirection::destroy() ground_ = nullptr; delete power_; power_ = nullptr; + delete well_; + well_ = nullptr; delete unknown_; unknown_ = nullptr; } @@ -95,6 +99,8 @@ PortDirection::find(const char *dir_name) return ground_; else if (stringEqual(dir_name, "power")) return power_; + else if (stringEqual(dir_name, "well")) + return well_; else return nullptr; } @@ -125,7 +131,8 @@ bool PortDirection::isPowerGround() const { return this == ground_ - || this == power_; + || this == power_ + || this == well_; } } // namespace diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index 9b6af3340..b0cab790f 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -165,6 +165,7 @@ record_public_tests { report_json2 suppress_msg verilog_attribute + verilog_well_supplies verilog_specify verilog_write_escape verilog_unconnected_hpin diff --git a/test/verilog_well_supplies.ok b/test/verilog_well_supplies.ok new file mode 100644 index 000000000..41b724820 --- /dev/null +++ b/test/verilog_well_supplies.ok @@ -0,0 +1,26 @@ +module top (y, + a); + output y; + input a; + + + sky130_fd_sc_hd__buf_1 u1 (.A(a), + .X(y)); +endmodule +module top (y, + a); + output y; + input a; + + wire VGND; + wire VNB; + wire VPB; + wire VPWR; + + sky130_fd_sc_hd__buf_1 u1 (.VGND(VGND), + .VNB(VNB), + .VPB(VPB), + .VPWR(VPWR), + .A(a), + .X(y)); +endmodule diff --git a/test/verilog_well_supplies.tcl b/test/verilog_well_supplies.tcl new file mode 100644 index 000000000..b40287118 --- /dev/null +++ b/test/verilog_well_supplies.tcl @@ -0,0 +1,12 @@ +# Check that write_verilog excludes well pins along with power/ground pins. +source helpers.tcl +read_liberty ../examples/sky130hd_tt.lib.gz +read_verilog verilog_well_supplies.v +link_design top +set verilog_file [make_result_file "verilog_well_supplies.v"] +write_verilog $verilog_file +report_file $verilog_file + +set verilog_pwr_file [make_result_file "verilog_well_supplies_pwr.v"] +write_verilog -include_pwr_gnd $verilog_pwr_file +report_file $verilog_pwr_file diff --git a/test/verilog_well_supplies.v b/test/verilog_well_supplies.v new file mode 100644 index 000000000..6ae135800 --- /dev/null +++ b/test/verilog_well_supplies.v @@ -0,0 +1,17 @@ +module top ( + output y, + input a +); + supply1 VPWR; + supply0 VGND; + supply1 VPB; + supply0 VNB; + sky130_fd_sc_hd__buf_1 u1 ( + .X(y), + .A(a), + .VPWR(VPWR), + .VGND(VGND), + .VPB(VPB), + .VNB(VNB) + ); +endmodule diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index 8edaf6990..49ecedf31 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -253,6 +253,8 @@ VerilogWriter::verilogPortDir(PortDirection *dir) return "inout"; else if (dir == PortDirection::ground()) return "inout"; + else if (dir == PortDirection::well()) + return "inout"; else if (dir == PortDirection::internal() || dir == PortDirection::unknown()) return "inout"; From b136ba309a66ca015d438e04329b8c326a16d92a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 9 Apr 2026 15:13:40 -0700 Subject: [PATCH 145/181] PrimaDelayCalc::reportGateDelay resolves #418 Signed-off-by: James Cherry --- dcalc/PrimaDelayCalc.cc | 136 +++++++++++++++++++++++++++++----------- dcalc/PrimaDelayCalc.hh | 7 +++ 2 files changed, 108 insertions(+), 35 deletions(-) diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 561b7953c..93388a10c 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -216,48 +216,107 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, parasitics_ = scene->parasitics(min_max); node_index_map_ = NodeIndexMap(ParasiticNodeLess(parasitics_, network_)); - bool failed = false; + bool arg_fail = checkArgs(dcalc_args, scene, min_max); + if (arg_fail) + return tableDcalcResults(); + else { + simulate(); + return dcalcResults(); + } +} + +// Return true on failure. +// Use falureReason() to get failure string. +bool +PrimaDelayCalc::checkArgs(ArcDcalcArgSeq &dcalc_args, + const Scene *scene, + const MinMax *min_max) +{ + drvr_count_ = dcalc_args.size(); output_waveforms_.resize(drvr_count_); + failure_reason_ = nullptr; + failure_arg_ = nullptr; for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(scene, min_max); - if (table_model && dcalc_arg.parasitic()) { - OutputWaveforms *output_waveforms = table_model->outputWaveforms(); - float in_slew = dcalc_arg.inSlewFlt(); - if (output_waveforms + if (table_model) { + if (dcalc_arg.parasitic()) { + OutputWaveforms *output_waveforms = table_model->outputWaveforms(); + float in_slew = dcalc_arg.inSlewFlt(); + if (output_waveforms) { + const LibertyLibrary *drvr_library = dcalc_arg.drvrLibrary(); + float vdd; + bool vdd_exists; + drvr_library->supplyVoltage("VDD", vdd, vdd_exists); + if (vdd_exists) { + if (drvr_idx == 0) { + // Assume drivers are in the same library. + const RiseFall *drvr_rf = dcalc_arg.drvrEdge(); + vdd_ = vdd; + vth_ = drvr_library->outputThreshold(drvr_rf) * vdd_; + vl_ = drvr_library->slewLowerThreshold(drvr_rf) * vdd_; + vh_ = drvr_library->slewUpperThreshold(drvr_rf) * vdd_; + } + } + else { + failure_reason_ = "vdd not defined"; + failure_arg_ = &dcalc_arg; + } + // Bounds check because extrapolating waveforms does not work for shit. - && output_waveforms->slewAxis()->inBounds(in_slew) - && output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) { - output_waveforms_[drvr_idx] = output_waveforms; - 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 {}", - drvr_library->name()); - drvr_cell->ensureVoltageWaveforms(scenes_); - if (drvr_idx == 0) { - vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; - vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; - vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_; + if (output_waveforms->slewAxis()->inBounds(in_slew)) { + if (output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) { + output_waveforms_[drvr_idx] = output_waveforms; + debugPrint(debug_, "prima", 1, "{} {}", + dcalc_arg.drvrCell()->name(), + dcalc_arg.drvrEdge()->to_string().c_str()); + LibertyCell *drvr_cell = dcalc_arg.drvrCell(); + drvr_cell->ensureVoltageWaveforms(scenes_); + } + else { + failure_reason_ = "load cap out of bounds"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "input slew out of bounds"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "no output waveforms"; + failure_arg_ = &dcalc_arg; } } - else - failed = true; + else { + failure_reason_ = "no parasitic"; + failure_arg_ = &dcalc_arg; + } + } + else { + failure_reason_ = "no table model"; + failure_arg_ = &dcalc_arg; } - else - failed = true; } - - if (failed) - return tableDcalcResults(); - else { - simulate(); - return dcalcResults(); + if (failure_reason_) { + std::string reason = failureReason(); + debugPrint(debug_,"prima", 1, "arg check failed {}.", reason.c_str()); } + return failure_reason_ != nullptr; +} + +std::string +PrimaDelayCalc::failureReason() +{ + const Pin *drvr_pin = failure_arg_->drvrPin(); + const Instance *inst = network_->instance(drvr_pin); + LibertyPort *from = failure_arg_->arc()->from(); + LibertyPort *to = failure_arg_->arc()->to(); + return sta::format("{} {} -> {} {}", + sdc_network_->pathName(inst), + from->name(), + to->name(), + failure_reason_); } ArcDcalcResultSeq @@ -938,8 +997,16 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, const MinMax *min_max, int digits) { - GateTimingModel *model = arc->gateModel(scene, min_max); - if (model) { + ArcDcalcArgSeq dcalc_args; + dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, + load_cap, parasitic); + bool arg_fail = checkArgs(dcalc_args, scene, min_max); + if (arg_fail) + return table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, + parasitic, load_pin_index_map, scene, + min_max, digits); + else { + GateTimingModel *model = arc->gateModel(scene, min_max); // Delay calc to find ceff. gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, scene, min_max); @@ -949,7 +1016,6 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, in_slew1, ceff, min_max, PocvMode::scalar, digits); } - return ""; } //////////////////////////////////////////////////////////////// diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index c0b39ba77..086e29ce1 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -100,6 +100,10 @@ public: const Scene *scene, const MinMax *min_max, int digits) override; + bool checkArgs(ArcDcalcArgSeq &dcalc_args, + const Scene *scene, + const MinMax *min_max); + std::string failureReason(); // Record waveform for drvr/load pin. void watchPin(const Pin *pin) override; @@ -253,6 +257,9 @@ protected: // Delay calculator to use when ccs waveforms are missing from liberty. ArcDelayCalc *table_dcalc_; + const char *failure_reason_; + ArcDcalcArg *failure_arg_; + using ArcDelayCalc::reduceParasitic; }; From d6268da88f93f695ee8b427959bd49422276c11f Mon Sep 17 00:00:00 2001 From: Mike Inouye Date: Fri, 10 Apr 2026 10:28:01 -0700 Subject: [PATCH 146/181] Consider multi-bit flops as having sequentials. (#419) --- liberty/Liberty.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 31e17cbe1..ae375e21b 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -1489,7 +1489,7 @@ LibertyCell::outputPortSequential(LibertyPort *port) bool LibertyCell::hasSequentials() const { - return !sequentials_.empty(); + return !sequentials_.empty() || statetable_ != nullptr; } void From 53f53e464a4d24109b246854bddd439846e9e03d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 10 Apr 2026 10:22:17 -0700 Subject: [PATCH 147/181] CmakeLists Signed-off-by: James Cherry --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f3495ba6..57d080bec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -595,13 +595,13 @@ set(CXX_FLAGS -Wall -Wextra -pedantic -Wcast-qual -Wredundant-decls if(ENABLE_TSAN) message(STATUS "Thread sanitizer: ${ENABLE_TSAN}") set(CXX_FLAGS "${CXX_FLAGS};-fsanitize=thread") - set(CMAKE_EXE_LINKER_FLAGS "-fsanitize=thread") + string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=thread") endif() if(ENABLE_ASAN) message(STATUS "Address sanitizer: ${ENABLE_ASAN}") set(CXX_FLAGS "${CXX_FLAGS};-fsanitize=address") - set(CMAKE_EXE_LINKER_FLAGS "-fsanitize=address") + string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=address") endif() target_compile_options(OpenSTA @@ -627,6 +627,7 @@ message(STATUS "STA library: ${CMAKE_BINARY_DIR}/libOpenSTA.a") add_executable(sta app/Main.cc) target_link_libraries(sta + PRIVATE sta_swig OpenSTA ) From 6ef92c5fc09356b4691e6f29c09162534d5ac489 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 10 Apr 2026 10:35:36 -0700 Subject: [PATCH 148/181] LibertyPort::less Signed-off-by: James Cherry --- liberty/Liberty.cc | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index ae375e21b..1817a7a28 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -2477,18 +2477,10 @@ bool LibertyPort::less(const LibertyPort *port1, const LibertyPort *port2) { - if (port1 == nullptr && port2 != nullptr) - return true; - if (port1 != nullptr && port2 == nullptr) - return false; - const std::string &name1 = port1->name(); - const std::string &name2 = port2->name(); - if (name1 == name2) { - PortDirection *dir1 = port1->direction(); - PortDirection *dir2 = port2->direction(); - return dir1->index() < dir2->index(); - } - return name1 < name2; + if (port1 && port2) + return port1->pinIndex() < port2->pinIndex(); + else + return port1 == nullptr && port2 != nullptr; } void From 094aa1adc4e7db14f89f6372ca05a0775df3f120 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 10 Apr 2026 10:53:55 -0700 Subject: [PATCH 149/181] VerilogNamespace use string_view Signed-off-by: James Cherry --- include/sta/VerilogNamespace.hh | 17 ++++---- network/VerilogNamespace.cc | 76 ++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/include/sta/VerilogNamespace.hh b/include/sta/VerilogNamespace.hh index bcdddb523..5b8741892 100644 --- a/include/sta/VerilogNamespace.hh +++ b/include/sta/VerilogNamespace.hh @@ -25,25 +25,26 @@ #pragma once #include +#include namespace sta { std::string -cellVerilogName(std::string sta_name); +cellVerilogName(std::string_view sta_name); std::string -instanceVerilogName(std::string sta_name); +instanceVerilogName(std::string_view sta_name); std::string -netVerilogName(std::string sta_name); +netVerilogName(std::string_view sta_name); std::string -portVerilogName(std::string sta_name); +portVerilogName(std::string_view sta_name); std::string -moduleVerilogToSta(std::string sta_name); +moduleVerilogToSta(std::string_view sta_name); std::string -instanceVerilogToSta(std::string sta_name); +instanceVerilogToSta(std::string_view sta_name); std::string -netVerilogToSta(std::string sta_name); +netVerilogToSta(std::string_view sta_name); std::string -portVerilogToSta(std::string sta_name); +portVerilogToSta(std::string_view sta_name); } // namespace diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index b12d9c3ae..28f07dc0e 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -34,26 +34,26 @@ namespace sta { constexpr char verilog_escape = '\\'; static std::string -staToVerilog(std::string sta_name); +staToVerilog(std::string_view sta_name); static std::string -staToVerilog2(std::string sta_name); +staToVerilog2(std::string_view sta_name); static std::string -verilogToSta(const std::string verilog_name); +verilogToSta(const std::string_view verilog_name); std::string -cellVerilogName(std::string sta_name) +cellVerilogName(std::string_view sta_name) { return staToVerilog(sta_name); } std::string -instanceVerilogName(std::string sta_name) +instanceVerilogName(std::string_view sta_name) { return staToVerilog(sta_name); } std::string -netVerilogName(std::string sta_name) +netVerilogName(std::string_view sta_name) { bool is_bus; std::string bus_name; @@ -69,13 +69,20 @@ netVerilogName(std::string sta_name) } std::string -portVerilogName(std::string sta_name) +portVerilogName(std::string_view sta_name) { return staToVerilog2(sta_name); } +// functions expect a value representable as unsigned char or EOF. +static bool +isAlnumUnderscore(char ch) +{ + return std::isalnum(static_cast(ch)) != 0 || ch == '_'; +} + static std::string -staToVerilog(std::string sta_name) +staToVerilog(std::string_view sta_name) { // Leave room for leading escape and trailing space if the name // needs to be escaped. @@ -87,14 +94,18 @@ staToVerilog(std::string sta_name) char ch = sta_name[i]; if (ch == verilog_escape) { escaped = true; - char next_ch = sta_name[i + 1]; - if (next_ch == verilog_escape) { - escaped_name += next_ch; - i++; + if (i + 1 < sta_length) { + char next_ch = sta_name[i + 1]; + if (next_ch == verilog_escape) { + escaped_name += next_ch; + i++; + } } + else + escaped_name += ch; } else { - if ((!(isalnum(ch) || ch == '_'))) + if (!isAlnumUnderscore(ch)) escaped = true; escaped_name += ch; } @@ -105,11 +116,11 @@ staToVerilog(std::string sta_name) return escaped_name; } else - return sta_name; + return std::string(sta_name); } static std::string -staToVerilog2(std::string sta_name) +staToVerilog2(std::string_view sta_name) { constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; @@ -123,16 +134,19 @@ staToVerilog2(std::string sta_name) char ch = sta_name[i]; if (ch == verilog_escape) { escaped = true; - char next_ch = sta_name[i + 1]; - if (next_ch == verilog_escape) { - escaped_name += next_ch; - i++; + if (i + 1 < sta_length) { + char next_ch = sta_name[i + 1]; + if (next_ch == verilog_escape) { + escaped_name += next_ch; + i++; + } } + else + escaped_name += ch; } else { bool is_brkt = (ch == bus_brkt_left || ch == bus_brkt_right); - if ((!(isalnum(ch) || ch == '_') && !is_brkt) - || is_brkt) + if ((!isAlnumUnderscore(ch) && !is_brkt) || is_brkt) escaped = true; escaped_name += ch; } @@ -143,45 +157,49 @@ staToVerilog2(std::string sta_name) return escaped_name; } else - return sta_name; + return std::string(sta_name); } //////////////////////////////////////////////////////////////// std::string -moduleVerilogToSta(std::string module_name) +moduleVerilogToSta(std::string_view module_name) { return verilogToSta(module_name); } std::string -instanceVerilogToSta(std::string inst_name) +instanceVerilogToSta(std::string_view inst_name) { return verilogToSta(inst_name); } std::string -netVerilogToSta(std::string net_name) +netVerilogToSta(std::string_view net_name) { return verilogToSta(net_name); } std::string -portVerilogToSta(std::string port_name) +portVerilogToSta(std::string_view port_name) { return verilogToSta(port_name); } static std::string -verilogToSta(std::string verilog_name) +verilogToSta(std::string_view verilog_name) { + if (verilog_name.empty()) + return std::string(verilog_name); + 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())) + if (verilog_name_length > 1 + && std::isspace(static_cast(verilog_name.back())) != 0) verilog_name_length--; std::string sta_name; // Ignore leading '\'. @@ -198,7 +216,7 @@ verilogToSta(std::string verilog_name) return sta_name; } else - return verilog_name; + return std::string(verilog_name); } } // namespace From 0a8a86d606f7db262b7611823ad1cb757498d5dc Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 10 Apr 2026 14:49:25 -0700 Subject: [PATCH 150/181] FilterObjects use string_view Signed-off-by: James Cherry --- CMakeLists.txt | 3 + include/sta/FilterObjects.hh | 1 + sdc/FilterObjects.cc | 111 +++++++++++++++++++---------------- 3 files changed, 66 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 57d080bec..b93082936 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,9 @@ option(ENABLE_ASAN "Compile with address santizer enabled" OFF) # Turn on to debug compiler args. set(CMAKE_VERBOSE_MAKEFILE OFF) +# Write compile_commands.json to the build directory for clang-tidy. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + set(STA_HOME ${PROJECT_SOURCE_DIR}) message(STATUS "STA version: ${PROJECT_VERSION}") diff --git a/include/sta/FilterObjects.hh b/include/sta/FilterObjects.hh index 7420b4507..dbeec3c5a 100644 --- a/include/sta/FilterObjects.hh +++ b/include/sta/FilterObjects.hh @@ -28,6 +28,7 @@ #include #include +#include "NetworkClass.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StringUtil.hh" diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc index 6e263037b..45e59997a 100644 --- a/sdc/FilterObjects.cc +++ b/sdc/FilterObjects.cc @@ -24,11 +24,17 @@ #include "FilterObjects.hh" -#include -#include #include #include +#include +#include +#include +#include +#include +#include "NetworkClass.hh" +#include "Network.hh" +#include "StringUtil.hh" #include "Property.hh" #include "PatternMatch.hh" #include "Sta.hh" @@ -52,22 +58,27 @@ class FilterExpr undefined }; - Token(std::string text, + Token(std::string_view text, Kind kind); - - std::string text; - Kind kind; + const std::string &text() const { return text_; } + Kind kind() const { return kind_; } + + std::string text_; + Kind kind_; }; struct PredicateToken : public Token { - PredicateToken(std::string property, - std::string op, - std::string arg); + PredicateToken(std::string_view property, + std::string_view op, + std::string_view arg); + const std::string &property() const { return property_; } + const std::string &op() const { return op_; } + const std::string &arg() const { return arg_; } - std::string property; - std::string op; - std::string arg; + std::string property_; + std::string op_; + std::string arg_; }; FilterExpr(std::string_view expression, @@ -82,19 +93,21 @@ class FilterExpr Report *report_; }; -FilterExpr::Token::Token(std::string text, +FilterExpr::Token::Token(std::string_view text, Token::Kind kind) : - text (text), - kind(kind) + text_(text), + kind_(kind) { } -FilterExpr::PredicateToken::PredicateToken(std::string property, - std::string op, - std::string arg) : - Token(property + " " + op + " " + arg, +FilterExpr::PredicateToken::PredicateToken(std::string_view property, + std::string_view op, + std::string_view arg) : + Token(sta::format("{} {} {}", property, op, arg), Token::Kind::predicate), - property(property), op(op), arg(arg) + property_(property), + op_(op), + arg_(arg) { } @@ -140,7 +153,7 @@ FilterExpr::lex() std::string property = token_match[1].str(); // The default operation on a predicate if an op and arg are - // omitted is 'prop == 1 || true'. + // omitted is 'prop == 1'. std::string op = "=="; std::string arg = "1"; @@ -175,7 +188,7 @@ FilterExpr::shuntingYard(std::vector> &infix) std::stack> operator_stack; for (auto &token : infix) { - switch (token->kind) { + switch (token->kind()) { case Token::Kind::predicate: output.push_back(std::move(token)); break; @@ -185,7 +198,7 @@ FilterExpr::shuntingYard(std::vector> &infix) // The operators' enum values are ascending by precedence: // inv > and > or while (operator_stack.size() - && operator_stack.top()->kind > token->kind) { + && operator_stack.top()->kind() > token->kind()) { output.push_back(std::move(operator_stack.top())); operator_stack.pop(); } @@ -208,7 +221,7 @@ FilterExpr::shuntingYard(std::vector> &infix) if (operator_stack.empty()) report_->error(2601, "-filter extraneous )."); while (operator_stack.size() - && operator_stack.top()->kind != Token::Kind::op_lparen) { + && operator_stack.top()->kind() != Token::Kind::op_lparen) { output.push_back(std::move(operator_stack.top())); operator_stack.pop(); if (operator_stack.empty()) @@ -224,7 +237,7 @@ FilterExpr::shuntingYard(std::vector> &infix) } while (operator_stack.size()) { - if (operator_stack.top()->kind == Token::Kind::op_lparen) + if (operator_stack.top()->kind() == Token::Kind::op_lparen) report_->error(2603, "-filter unmatched (."); output.push_back(std::move(operator_stack.top())); operator_stack.pop(); @@ -235,20 +248,20 @@ FilterExpr::shuntingYard(std::vector> &infix) //////////////////////////////////////////////////////////////// -template std::set -filterObjects(const char *property, - const char *op, - const char *pattern, +template static std::set +filterObjects(std::string_view property, + std::string_view op, + std::string_view pattern, std::set &all, Sta *sta) { Properties &properties = sta->properties(); Network *network = sta->network(); auto filtered_objects = std::set(); - bool exact_match = stringEq(op, "=="); - bool pattern_match = stringEq(op, "=~"); - bool not_match = stringEq(op, "!="); - bool not_pattern_match = stringEq(op, "!~"); + bool exact_match = (op == "=="); + bool pattern_match = (op == "=~"); + bool not_match = (op == "!="); + bool not_pattern_match = (op == "!~"); for (T *object : all) { PropertyValue value = properties.getProperty(object, property); std::string prop = value.to_string(network); @@ -259,8 +272,8 @@ filterObjects(const char *property, else if (stringEqual(pattern, "false")) pattern = "0"; } - if ((exact_match && stringEq(prop.c_str(), pattern)) - || (not_match && !stringEq(prop.c_str(), pattern)) + if ((exact_match && prop == pattern) + || (not_match && prop != pattern) || (pattern_match && patternMatch(pattern, prop)) || (not_pattern_match && !patternMatch(pattern, prop))) filtered_objects.insert(object); @@ -268,9 +281,9 @@ filterObjects(const char *property, return filtered_objects; } -template std::vector +template static std::vector filterObjects(std::string_view filter_expression, - std::vector *objects, + const std::vector *objects, Sta *sta) { Report *report = sta->report(); @@ -286,7 +299,7 @@ filterObjects(std::string_view filter_expression, auto postfix = filter.postfix(); std::stack> eval_stack; for (auto &token : postfix) { - if (token->kind == FilterExpr::Token::Kind::op_or) { + if (token->kind() == FilterExpr::Token::Kind::op_or) { if (eval_stack.size() < 2) report->error(2604, "-filter logical OR requires at least two operands."); auto arg0 = eval_stack.top(); @@ -298,7 +311,7 @@ filterObjects(std::string_view filter_expression, std::inserter(union_result, union_result.begin())); eval_stack.push(union_result); } - else if (token->kind == FilterExpr::Token::Kind::op_and) { + else if (token->kind() == FilterExpr::Token::Kind::op_and) { if (eval_stack.size() < 2) { report->error(2605, "-filter logical AND requires two operands."); } @@ -313,7 +326,7 @@ filterObjects(std::string_view filter_expression, intersection_result.begin())); eval_stack.push(intersection_result); } - else if (token->kind == FilterExpr::Token::Kind::op_inv) { + else if (token->kind() == FilterExpr::Token::Kind::op_inv) { if (eval_stack.size() < 1) { report->error(2606, "-filter NOT missing operand."); } @@ -327,13 +340,13 @@ filterObjects(std::string_view filter_expression, difference_result.begin())); eval_stack.push(difference_result); } - else if (token->kind == FilterExpr::Token::Kind::defined - || token->kind == FilterExpr::Token::Kind::undefined) { + else if (token->kind() == FilterExpr::Token::Kind::defined + || token->kind() == FilterExpr::Token::Kind::undefined) { bool should_be_defined = - (token->kind == FilterExpr::Token::Kind::defined); + (token->kind() == FilterExpr::Token::Kind::defined); auto result = std::set(); for (auto object : all) { - PropertyValue value = properties.getProperty(object, token->text); + PropertyValue value = properties.getProperty(object, token->text()); bool is_defined = false; switch (value.type()) { case PropertyValue::Type::float_: @@ -377,12 +390,12 @@ filterObjects(std::string_view filter_expression, } eval_stack.push(result); } - else if (token->kind == FilterExpr::Token::Kind::predicate) { + else if (token->kind() == FilterExpr::Token::Kind::predicate) { auto *predicate_token = static_cast(token.get()); - auto result = filterObjects(predicate_token->property.c_str(), - predicate_token->op.c_str(), - predicate_token->arg.c_str(), + auto result = filterObjects(predicate_token->property(), + predicate_token->op(), + predicate_token->arg(), all, sta); eval_stack.push(result); } @@ -495,7 +508,7 @@ filterExprToPostfix(std::string_view expr, auto postfix = filter.postfix(); StringSeq result; for (auto &token : postfix) - result.push_back(token->text); + result.push_back(token->text()); return result; } From 63efee64bfefb7dffa6636b3092fdd644069a097 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 13 Apr 2026 14:58:16 -0700 Subject: [PATCH 151/181] tidy round1 --- .clang-tidy | 77 ++++++ .cursor/rules/cpp-coding-standards.mdc | 26 ++ CMakeLists.txt | 2 +- app/StaMain.cc | 2 +- dcalc/ArcDcalcWaveforms.cc | 2 +- dcalc/ArcDcalcWaveforms.hh | 2 +- dcalc/ArcDelayCalc.cc | 4 +- dcalc/Arnoldi.hh | 2 +- dcalc/ArnoldiDelayCalc.cc | 2 +- dcalc/ArnoldiDelayCalc.hh | 2 +- dcalc/ArnoldiReduce.hh | 2 +- dcalc/CcsCeffDelayCalc.hh | 2 +- dcalc/Delay.cc | 22 +- dcalc/DelayCalc.cc | 2 +- dcalc/DelayCalcBase.cc | 2 +- dcalc/DelayCalcBase.hh | 2 +- dcalc/DelayNormal.cc | 4 +- dcalc/DelayScalar.cc | 4 +- dcalc/DelaySkewNormal.cc | 4 +- dcalc/DmpCeff.hh | 2 +- dcalc/DmpDelayCalc.cc | 2 +- dcalc/DmpDelayCalc.hh | 2 +- dcalc/FindRoot.cc | 2 +- dcalc/FindRoot.hh | 2 +- dcalc/GraphDelayCalc.cc | 2 +- dcalc/LumpedCapDelayCalc.cc | 2 +- dcalc/LumpedCapDelayCalc.hh | 2 +- dcalc/NetCaps.cc | 2 +- dcalc/NetCaps.hh | 2 +- dcalc/ParallelDelayCalc.cc | 2 +- dcalc/ParallelDelayCalc.hh | 2 +- dcalc/PrimaDelayCalc.hh | 2 +- dcalc/UnitDelayCalc.cc | 2 +- dcalc/UnitDelayCalc.hh | 2 +- doc/StaApi.txt | 5 +- graph/Graph.cc | 2 +- include/sta/ArcDelayCalc.hh | 5 +- include/sta/Bdd.hh | 4 +- include/sta/Bfs.hh | 10 +- include/sta/CircuitSim.hh | 2 +- include/sta/ClkNetwork.hh | 8 +- include/sta/Clock.hh | 6 +- include/sta/ClockGatingCheck.hh | 2 +- include/sta/ClockGroups.hh | 6 +- include/sta/ClockInsertion.hh | 2 +- include/sta/ClockLatency.hh | 2 +- include/sta/ConcreteLibrary.hh | 8 +- include/sta/ConcreteNetwork.hh | 8 +- include/sta/ContainerHelpers.hh | 70 ++--- include/sta/CycleAccting.hh | 2 +- include/sta/DataCheck.hh | 2 +- include/sta/Debug.hh | 6 +- include/sta/Delay.hh | 20 +- include/sta/DelayCalc.hh | 2 +- include/sta/DelayNormal.hh | 4 +- include/sta/DelayScalar.hh | 4 +- include/sta/DelaySkewNormal.hh | 4 +- include/sta/DeratingFactors.hh | 2 +- include/sta/DisabledPorts.hh | 2 +- include/sta/DispatchQueue.hh | 6 +- include/sta/EnumNameMap.hh | 7 +- include/sta/EquivCells.hh | 2 +- include/sta/Error.hh | 5 +- include/sta/ExceptionPath.hh | 43 ++-- include/sta/FilterObjects.hh | 5 +- include/sta/FuncExpr.hh | 2 +- include/sta/Fuzzy.hh | 2 +- include/sta/Graph.hh | 8 +- include/sta/GraphClass.hh | 2 +- include/sta/GraphCmp.hh | 2 +- include/sta/GraphDelayCalc.hh | 9 +- include/sta/Hash.hh | 2 +- include/sta/HpinDrvrLoad.hh | 5 +- include/sta/InputDrive.hh | 2 +- include/sta/InternalPower.hh | 2 +- include/sta/Iterator.hh | 4 +- include/sta/LeakagePower.hh | 2 +- include/sta/Liberty.hh | 256 ++++++++++--------- include/sta/LibertyClass.hh | 12 +- include/sta/LibertyWriter.hh | 2 +- include/sta/LinearModel.hh | 8 +- include/sta/Machine.hh | 8 +- include/sta/MakeConcreteNetwork.hh | 2 +- include/sta/MinMax.hh | 46 ++-- include/sta/MinMaxValues.hh | 2 +- include/sta/Mode.hh | 18 +- include/sta/Mutex.hh | 4 +- include/sta/Network.hh | 22 +- include/sta/NetworkClass.hh | 2 +- include/sta/NetworkCmp.hh | 2 +- include/sta/ObjectId.hh | 2 +- include/sta/ObjectTable.hh | 14 +- include/sta/Parasitics.hh | 3 +- include/sta/ParasiticsClass.hh | 2 +- include/sta/ParseBus.hh | 22 +- include/sta/Path.hh | 5 +- include/sta/PathEnd.hh | 271 ++++++++++---------- include/sta/PathExpanded.hh | 2 +- include/sta/PathGroup.hh | 52 ++-- include/sta/PatternMatch.hh | 6 +- include/sta/PinPair.hh | 2 +- include/sta/PocvMode.hh | 4 +- include/sta/PortDelay.hh | 2 +- include/sta/PortDirection.hh | 8 +- include/sta/PortExtCap.hh | 2 +- include/sta/PowerClass.hh | 18 +- include/sta/Property.hh | 29 +-- include/sta/Report.hh | 26 +- include/sta/ReportStd.hh | 2 +- include/sta/ReportTcl.hh | 14 +- include/sta/RiseFallMinMax.hh | 2 +- include/sta/RiseFallMinMaxDelay.hh | 2 +- include/sta/RiseFallValues.hh | 2 +- include/sta/Scene.hh | 4 +- include/sta/Sdc.hh | 33 ++- include/sta/SdcClass.hh | 2 +- include/sta/SdcCmdComment.hh | 8 +- include/sta/SdcNetwork.hh | 2 +- include/sta/Search.hh | 165 ++++++------ include/sta/SearchClass.hh | 2 +- include/sta/SearchPred.hh | 4 +- include/sta/Sequential.hh | 2 +- include/sta/Sta.hh | 75 +++--- include/sta/StaMain.hh | 2 +- include/sta/StaState.hh | 6 +- include/sta/Stats.hh | 10 +- include/sta/StringUtil.hh | 29 +-- include/sta/TableModel.hh | 29 +-- include/sta/TclTypeHelpers.hh | 2 +- include/sta/TimingArc.hh | 20 +- include/sta/TimingModel.hh | 4 +- include/sta/TimingRole.hh | 2 +- include/sta/Transition.hh | 68 ++--- include/sta/Units.hh | 6 +- include/sta/Variables.hh | 2 +- include/sta/VectorMap.hh | 19 +- include/sta/VerilogNamespace.hh | 2 +- include/sta/VerilogReader.hh | 36 +-- include/sta/VerilogWriter.hh | 2 +- include/sta/VertexId.hh | 2 +- include/sta/VertexVisitor.hh | 10 +- include/sta/VisitPathEnds.hh | 4 +- include/sta/Wireload.hh | 4 +- liberty/EquivCells.cc | 23 +- liberty/FuncExpr.cc | 9 +- liberty/InternalPower.cc | 9 +- liberty/LeakagePower.cc | 3 +- liberty/LibExprLex.ll | 2 +- liberty/LibExprReader.cc | 4 +- liberty/LibExprReader.hh | 2 +- liberty/LibExprReaderPvt.hh | 4 +- liberty/LibExprScanner.hh | 6 +- liberty/Liberty.cc | 324 ++++++++---------------- liberty/Liberty.i | 1 - liberty/LibertyBuilder.cc | 44 ++-- liberty/LibertyBuilder.hh | 25 +- liberty/LibertyExt.cc | 287 --------------------- liberty/LibertyLex.ll | 2 +- liberty/LibertyParser.cc | 24 +- liberty/LibertyParser.hh | 13 +- liberty/LibertyReader.cc | 187 +++++++------- liberty/LibertyReader.hh | 2 +- liberty/LibertyReaderPvt.hh | 88 +++---- liberty/LibertyScanner.hh | 8 +- liberty/LibertyWriter.cc | 9 +- liberty/LinearModel.cc | 10 +- liberty/Sequential.cc | 4 +- liberty/TableModel.cc | 79 +++--- liberty/TimingArc.cc | 65 +++-- liberty/TimingModel.cc | 4 +- liberty/TimingRole.cc | 4 +- liberty/Units.cc | 14 +- liberty/Wireload.cc | 12 +- network/ConcreteLibrary.cc | 6 +- network/ConcreteNetwork.cc | 2 +- network/HpinDrvrLoad.cc | 2 +- network/Network.cc | 2 +- network/NetworkCmp.cc | 2 +- network/ParseBus.cc | 2 +- network/PortDirection.cc | 4 +- network/SdcNetwork.cc | 2 +- network/VerilogNamespace.cc | 2 +- parasitics/ConcreteParasitics.cc | 6 +- parasitics/ConcreteParasitics.hh | 8 +- parasitics/ConcreteParasiticsPvt.hh | 2 +- parasitics/EstimateParasitics.cc | 2 +- parasitics/EstimateParasitics.hh | 2 +- parasitics/ReduceParasitics.cc | 2 +- parasitics/ReduceParasitics.hh | 2 +- parasitics/ReportParasiticAnnotation.hh | 2 +- parasitics/SpefNamespace.cc | 2 +- parasitics/SpefNamespace.hh | 2 +- parasitics/SpefReader.cc | 2 - parasitics/SpefReader.hh | 2 +- parasitics/SpefReaderPvt.hh | 4 +- parasitics/SpefScanner.hh | 4 +- power/Power.cc | 122 ++++----- power/Power.hh | 23 +- power/ReportPower.hh | 2 +- power/SaifReader.hh | 2 +- power/SaifReaderPvt.hh | 2 +- power/SaifScanner.hh | 4 +- power/VcdParse.hh | 4 +- power/VcdReader.hh | 2 +- sdc/Clock.cc | 6 +- sdc/ClockGatingCheck.cc | 2 +- sdc/ClockGroups.cc | 8 +- sdc/ClockInsertion.cc | 2 +- sdc/ClockLatency.cc | 2 +- sdc/CycleAccting.cc | 2 +- sdc/DataCheck.cc | 2 +- sdc/DeratingFactors.cc | 2 +- sdc/ExceptionPath.cc | 14 +- sdc/FilterObjects.cc | 139 +++++++--- sdc/InputDrive.cc | 2 +- sdc/PinPair.cc | 2 +- sdc/PortDelay.cc | 2 +- sdc/PortExtCap.cc | 2 +- sdc/Sdc.cc | 139 +++++----- sdc/SdcCmdComment.cc | 21 +- sdc/Variables.cc | 2 +- sdc/WriteSdc.cc | 4 +- sdc/WriteSdc.hh | 2 +- sdc/WriteSdcPvt.hh | 2 +- sdf/ReportAnnotation.hh | 2 +- sdf/SdfReader.hh | 2 +- sdf/SdfReaderPvt.hh | 2 +- sdf/SdfScanner.hh | 4 +- sdf/SdfWriter.cc | 2 +- sdf/SdfWriter.hh | 2 +- search/Bdd.cc | 9 +- search/Bfs.cc | 17 +- search/CheckCapacitances.cc | 36 +-- search/CheckCapacitances.hh | 14 +- search/CheckFanouts.cc | 19 +- search/CheckFanouts.hh | 7 +- search/CheckMaxSkews.cc | 19 +- search/CheckMaxSkews.hh | 14 +- search/CheckMinPeriods.cc | 16 +- search/CheckMinPeriods.hh | 12 +- search/CheckMinPulseWidths.cc | 28 +- search/CheckMinPulseWidths.hh | 12 +- search/CheckSlews.cc | 29 ++- search/CheckSlews.hh | 16 +- search/CheckTiming.cc | 39 ++- search/CheckTiming.hh | 22 +- search/ClkDelays.hh | 9 +- search/ClkInfo.cc | 12 +- search/ClkInfo.hh | 19 +- search/ClkLatency.cc | 12 +- search/ClkLatency.hh | 9 +- search/ClkNetwork.cc | 13 +- search/ClkSkew.cc | 40 ++- search/ClkSkew.hh | 18 +- search/Crpr.cc | 18 +- search/Crpr.hh | 14 +- search/FindRegister.cc | 113 +++++---- search/FindRegister.hh | 14 +- search/GatedClk.cc | 10 +- search/GatedClk.hh | 10 +- search/Genclks.cc | 70 +++-- search/Genclks.hh | 16 +- search/Latches.cc | 22 +- search/Latches.hh | 9 +- search/Levelize.cc | 27 +- search/Levelize.hh | 24 +- search/MakeTimingModel.cc | 50 ++-- search/MakeTimingModel.hh | 2 +- search/MakeTimingModelPvt.hh | 17 +- search/Mode.cc | 18 +- search/Path.cc | 29 ++- search/PathEnd.cc | 58 ++--- search/PathEnum.cc | 50 ++-- search/PathEnum.hh | 20 +- search/PathExpanded.cc | 14 +- search/PathGroup.cc | 90 +++---- search/PocvMode.cc | 4 +- search/Property.cc | 61 +++-- search/ReportPath.cc | 74 +++--- search/ReportPath.hh | 62 +++-- search/Scene.cc | 6 +- search/Search.cc | 102 +++----- search/Search.i | 10 +- search/SearchPred.cc | 18 +- search/Sim.cc | 11 +- search/Sim.hh | 26 +- search/Sta.cc | 209 ++++++++------- search/StaState.cc | 14 +- search/Tag.cc | 14 +- search/Tag.hh | 24 +- search/TagGroup.cc | 13 +- search/TagGroup.hh | 20 +- search/VertexVisitor.cc | 2 +- search/VisitPathEnds.cc | 24 +- search/WorstSlack.cc | 22 +- search/WorstSlack.hh | 13 +- spice/WritePathSpice.cc | 2 +- spice/WritePathSpice.hh | 2 +- spice/Xyce.cc | 2 +- spice/Xyce.hh | 2 +- tcl/StaTclTypes.i | 2 +- tcl/TclTypeHelpers.cc | 2 +- util/Debug.cc | 6 +- util/DispatchQueue.cc | 12 +- util/Error.cc | 11 +- util/Fuzzy.cc | 2 +- util/Hash.cc | 2 +- util/MachineApple.cc | 10 +- util/MachineLinux.cc | 12 +- util/MachineUnknown.cc | 2 +- util/MachineWin32.cc | 6 +- util/MinMax.cc | 20 +- util/PatternMatch.cc | 8 +- util/Report.cc | 70 +---- util/ReportStd.cc | 11 +- util/ReportTcl.cc | 49 ++-- util/RiseFallMinMax.cc | 38 +-- util/RiseFallMinMaxDelay.cc | 10 +- util/RiseFallValues.cc | 2 +- util/Stats.cc | 8 +- util/StringUtil.cc | 6 +- util/Transition.cc | 38 +-- util/Util.i | 8 +- verilog/VerilogParse.yy | 53 ++-- verilog/VerilogReader.cc | 179 ++++++------- verilog/VerilogReaderPvt.hh | 64 +++-- verilog/VerilogScanner.hh | 4 +- verilog/VerilogWriter.cc | 2 +- 328 files changed, 2945 insertions(+), 3356 deletions(-) create mode 100644 .clang-tidy delete mode 100644 liberty/LibertyExt.cc diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..19aed6568 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,77 @@ +Checks: > + clang-diagnostic-*, + clang-analyzer-*, + -clang-analyzer-core.NonNullParamChecker, + -clang-analyzer-core.CallAndMessage, + -clang-analyzer-core.uninitialized.UndefReturn, + -clang-analyzer-cplusplus.NewDeleteLeaks, + -clang-analyzer-optin.performance.Padding, + readability-*, + -readability-identifier-naming, + -readability-braces-around-statements, + -readability-convert-member-functions-to-static, + -readability-else-after-return, + -readability-function-cognitive-complexity, + -readability-inconsistent-ifelse-braces, + -readability-identifier-length, + -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-make-member-function-const, + -readability-math-missing-parentheses, + -readability-named-parameter, + -readability-qualified-auto, + -readability-redundant-access-specifiers, + -readability-simplify-boolean-expr, + -readability-static-definition-in-anonymous-namespace, + -readability-suspicious-call-argument, + -readability-uppercase-literal-suffix, + -readability-use-anyofallof, + google-*, + -google-readability-avoid-underscore-in-googletest-name, + -google-readability-braces-around-statements, + -google-readability-casting, + -google-readability-todo, + -google-runtime-references, + -google-explicit-constructor, + performance-*, + -performance-enum-size, + bugprone-*, + -bugprone-branch-clone, + -bugprone-easily-swappable-parameters, + -bugprone-exception-escape, + -bugprone-macro-parentheses, + -bugprone-move-forwarding-reference, + -bugprone-narrowing-conversions, + -bugprone-suspicious-missing-comma, + -bugprone-throwing-static-initialization, + modernize-*, + -modernize-avoid-bind, + -modernize-avoid-c-arrays, + -modernize-concat-nested-namespaces, + -modernize-macro-to-enum, + -modernize-pass-by-value, + -modernize-raw-string-literal, + -modernize-return-braced-init-list, + -modernize-use-auto, + -modernize-use-nodiscard, + -modernize-use-trailing-return-type, + -modernize-use-transparent-functors, + misc-*, + -misc-const-correctness, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + -misc-redundant-expression, + -misc-unused-parameters, + -misc-use-anonymous-namespace, + -misc-use-internal-linkage, + -misc-include-cleaner + +# Only report diagnostics in headers under this tree (app/, include/sta/, build-generated +# headers, etc.). +# Excludes system and third-party paths such as /opt/local/include. +HeaderFilterRegex: '.*/(app|cmake|dcalc|graph|liberty|network|parasitics|power|sdc|sdf|search|spice|tcl|util|verilog|include/sta|build/include/sta)/.*' + +# Bison-generated parser headers (build/{Liberty,Verilog,...}Parse.hh); gzstream (e.g. util/gzstream.hh). +ExcludeHeaderFilterRegex: '.*/(gzstream\.h(?:h)?|(?:Liberty|Verilog|Sdf|Spef|Saif|LibExpr)Parse\.hh)$' +FormatStyle: none diff --git a/.cursor/rules/cpp-coding-standards.mdc b/.cursor/rules/cpp-coding-standards.mdc index 34c6cfe6b..538991fbc 100644 --- a/.cursor/rules/cpp-coding-standards.mdc +++ b/.cursor/rules/cpp-coding-standards.mdc @@ -31,3 +31,29 @@ alwaysApply: false - C++ source: `.cc` - C++ headers: `.hh` + +## Include order + +Sort `#include` lines **lexicographically by the path inside** `<>` +or `""` (case-sensitive). Separate **groups** with a single blank line. + +### Translation units (`*.cc`) + +1. **Primary header(s)** for this file only: + - `#include "Stem.hh"` where `Stem` matches the basename of the `.cc` file (e.g. `Foo.cc` → `Foo.hh`). + - If the file uses a paired private header, include `#include "StemPvt.hh"` **immediately after** `Stem.hh` (e.g. `MakeTimingModel.cc` → `MakeTimingModel.hh` then `MakeTimingModelPvt.hh`). + - Do **not** pull in other headers just because their names share a prefix with `Stem` (e.g. `SearchClass.hh` is not a “primary” include for `Search.cc`; it belongs in the project group below). + +2. **System / standard library** headers: `#include <...>` lines, sorted alphabetically. + +3. **All other project** headers: `#include "..."` lines, sorted alphabetically (including `search/Crpr.hh`-style paths and includes from `liberty/`, etc.). + +If a **comment or special block** intentionally splits the include list (e.g. a third-party note before `#include "cudd.h"`), keep that structure; sort only within each contiguous run of `#include` lines unless merging groups is clearly safe. + +### Headers (`*.hh`) + +After `#pragma once` (and the file’s license block, if present): + +1. **System** `#include <...>` lines, sorted alphabetically. + +2. **Project** `#include "..."` lines, sorted alphabetically. diff --git a/CMakeLists.txt b/CMakeLists.txt index b93082936..33bc1edec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # OpenSTA, Static Timing Analyzer -# Copyright (c) 2025, Parallax Software, Inc. +# 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 diff --git a/app/StaMain.cc b/app/StaMain.cc index f2eda7ae0..84b0540f7 100644 --- a/app/StaMain.cc +++ b/app/StaMain.cc @@ -148,4 +148,4 @@ unencode(const char *inits[]) return unencoded; } -} // namespace +} // namespace sta diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index 00f1e3bbb..6de6bdb63 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -83,4 +83,4 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, return Waveform(); } -} // namespace +} // namespace sta diff --git a/dcalc/ArcDcalcWaveforms.hh b/dcalc/ArcDcalcWaveforms.hh index 8c9964b9a..8b84f72f9 100644 --- a/dcalc/ArcDcalcWaveforms.hh +++ b/dcalc/ArcDcalcWaveforms.hh @@ -51,5 +51,5 @@ protected: const StaState *sta); }; -} // namespace +} // namespace sta diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index a2a1ad0e2..38000cd09 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -135,7 +135,7 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin, const Pin *drvr_pin, Edge *edge, const TimingArc *arc, - const Slew in_slew, + Slew in_slew, float load_cap, const Parasitic *parasitic) : in_pin_(in_pin), @@ -305,4 +305,4 @@ ArcDcalcResult::setLoadSlew(size_t load_idx, load_slews_[load_idx] = load_slew; } -} // namespace +} // namespace sta diff --git a/dcalc/Arnoldi.hh b/dcalc/Arnoldi.hh index 51453bd6d..73fc26b29 100644 --- a/dcalc/Arnoldi.hh +++ b/dcalc/Arnoldi.hh @@ -86,4 +86,4 @@ struct timing_table float in_slew; }; -} // namespace +} // namespace sta diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 2523686ec..efacb54a7 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -1488,4 +1488,4 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, } } -} // namespace +} // namespace sta diff --git a/dcalc/ArnoldiDelayCalc.hh b/dcalc/ArnoldiDelayCalc.hh index de16171ec..03441eeb6 100644 --- a/dcalc/ArnoldiDelayCalc.hh +++ b/dcalc/ArnoldiDelayCalc.hh @@ -32,4 +32,4 @@ class StaState; ArcDelayCalc * makeArnoldiDelayCalc(StaState *sta); -} // namespace +} // namespace sta diff --git a/dcalc/ArnoldiReduce.hh b/dcalc/ArnoldiReduce.hh index aed95e524..0ee70c841 100644 --- a/dcalc/ArnoldiReduce.hh +++ b/dcalc/ArnoldiReduce.hh @@ -120,4 +120,4 @@ protected: int order; }; -} // namespace +} // namespace sta diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 665588f89..81fb24506 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -155,4 +155,4 @@ protected: ArcDelayCalc *table_dcalc_; }; -} // namespace +} // namespace sta diff --git a/dcalc/Delay.cc b/dcalc/Delay.cc index 48c867a3e..bdec1906e 100644 --- a/dcalc/Delay.cc +++ b/dcalc/Delay.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -43,18 +43,18 @@ initDelayConstants() delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue(); } -Delay::Delay() : +Delay::Delay() noexcept : values_{0.0, 0.0, 0.0, 0.0} { } -Delay::Delay(float mean) : +Delay::Delay(float mean) noexcept : values_{mean, 0.0, 0.0, 0.0} { } Delay::Delay(float mean, - float std_dev2) : + float std_dev2) noexcept : values_{mean, 0.0, std_dev2, 0.0} { } @@ -62,18 +62,19 @@ Delay::Delay(float mean, Delay::Delay(float mean, float mean_shift, float std_dev2, - float skewness) : + float skewness) noexcept : values_{mean, mean_shift, std_dev2, skewness} { } -void +Delay & Delay::operator=(float delay) { values_[0] = delay; values_[1] = 0.0; values_[2] = 0.0; values_[3] = 0.0; + return *this; } void @@ -126,12 +127,12 @@ Delay::setSkewness(float skewness) //////////////////////////////////////////////////////////////// -DelayDbl::DelayDbl() : +DelayDbl::DelayDbl() noexcept : values_{0.0, 0.0, 0.0, 0.0} { } -DelayDbl::DelayDbl(double mean) : +DelayDbl::DelayDbl(double mean) noexcept : values_{mean, 0.0, 0.0, 0.0} { } @@ -166,13 +167,14 @@ DelayDbl::setValues(double mean, values_[3] = skewnes; } -void +DelayDbl & DelayDbl::operator=(double delay) { values_[0] = delay; values_[1] = 0.0; values_[2] = 0.0; values_[3] = 0.0; + return *this; } //////////////////////////////////////////////////////////////// @@ -520,4 +522,4 @@ delayStdDev2(const Delay &delay, return sta->delayOps()->stdDev2(delay, early_late); } -} // namespace +} // namespace sta diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index 614ff6104..3ce3cbc92 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -93,4 +93,4 @@ delayCalcNames() return names; } -} // namespace +} // namespace sta diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index 71e2f1169..c41eb94f8 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -239,4 +239,4 @@ DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, setDcalcArgParasiticSlew(gate, scene, min_max); } -} // namespace +} // namespace sta diff --git a/dcalc/DelayCalcBase.hh b/dcalc/DelayCalcBase.hh index da69ed1bb..2cfdc6504 100644 --- a/dcalc/DelayCalcBase.hh +++ b/dcalc/DelayCalcBase.hh @@ -89,4 +89,4 @@ protected: using ArcDelayCalc::reduceParasitic; }; -} // namespace +} // namespace sta diff --git a/dcalc/DelayNormal.cc b/dcalc/DelayNormal.cc index 2c3cb0595..2120bded0 100644 --- a/dcalc/DelayNormal.cc +++ b/dcalc/DelayNormal.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -229,4 +229,4 @@ DelayOpsNormal::asStringVariance(const Delay &delay, unit->asString(delay.stdDev(), digits)); } -} // namespace +} // namespace sta diff --git a/dcalc/DelayScalar.cc b/dcalc/DelayScalar.cc index 6ad491784..73f217b4e 100644 --- a/dcalc/DelayScalar.cc +++ b/dcalc/DelayScalar.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -202,5 +202,5 @@ DelayOpsScalar::asStringVariance(const Delay &delay, return unit->asString(delay.mean(), digits); } -} // namespace +} // namespace sta diff --git a/dcalc/DelaySkewNormal.cc b/dcalc/DelaySkewNormal.cc index 306634bae..b8c9c8e53 100644 --- a/dcalc/DelaySkewNormal.cc +++ b/dcalc/DelaySkewNormal.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -290,4 +290,4 @@ DelayOpsSkewNormal::asStringVariance(const Delay &delay, sta->units()->scalarUnit()->asString(delay.skewness(), digits)); } -} // namespace +} // namespace sta diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index 065a00267..15f446404 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -101,4 +101,4 @@ private: DmpAlg *dmp_alg_; }; -} // namespace +} // namespace sta diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index d89b319d0..c9c63f5a1 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -436,4 +436,4 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double vth, } } -} // namespace +} // namespace sta diff --git a/dcalc/DmpDelayCalc.hh b/dcalc/DmpDelayCalc.hh index 34ecbd7b3..518d78184 100644 --- a/dcalc/DmpDelayCalc.hh +++ b/dcalc/DmpDelayCalc.hh @@ -34,4 +34,4 @@ makeDmpCeffElmoreDelayCalc(StaState *sta); ArcDelayCalc * makeDmpCeffTwoPoleDelayCalc(StaState *sta); -} // namespace +} // namespace sta diff --git a/dcalc/FindRoot.cc b/dcalc/FindRoot.cc index 5ca987df2..97e1c62db 100644 --- a/dcalc/FindRoot.cc +++ b/dcalc/FindRoot.cc @@ -98,4 +98,4 @@ findRoot(FindRootFunc func, return {root, true}; } -} // namespace +} // namespace sta diff --git a/dcalc/FindRoot.hh b/dcalc/FindRoot.hh index ab35ab4e4..d0c1921ff 100644 --- a/dcalc/FindRoot.hh +++ b/dcalc/FindRoot.hh @@ -52,4 +52,4 @@ findRoot(FindRootFunc func, double x_tol, int max_iter); -} // namespace +} // namespace sta diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 67536ef0a..eddd32353 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -1828,4 +1828,4 @@ MultiDrvrNet::parallelGates(const Network *network) const return network->direction(dcalc_drvr_->pin())->isOutput(); } -} // namespace +} // namespace sta diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 692c01f08..936fc77a5 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -203,4 +203,4 @@ LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin, return ""; } -} // namespace +} // namespace sta diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index eb2bf4f09..7c7643da8 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -84,4 +84,4 @@ protected: ArcDelayCalc * makeLumpedCapDelayCalc(StaState *sta); -} // namespace +} // namespace sta diff --git a/dcalc/NetCaps.cc b/dcalc/NetCaps.cc index 3ca8cd6aa..80a7c8018 100644 --- a/dcalc/NetCaps.cc +++ b/dcalc/NetCaps.cc @@ -53,4 +53,4 @@ NetCaps::init(float pin_cap, has_net_load_ = has_net_load; } -} // namespace +} // namespace sta diff --git a/dcalc/NetCaps.hh b/dcalc/NetCaps.hh index cb0db7eac..7c09cc66a 100644 --- a/dcalc/NetCaps.hh +++ b/dcalc/NetCaps.hh @@ -51,4 +51,4 @@ private: bool has_net_load_; }; -} // namespace +} // namespace sta diff --git a/dcalc/ParallelDelayCalc.cc b/dcalc/ParallelDelayCalc.cc index 38b71c329..1cb7c94e7 100644 --- a/dcalc/ParallelDelayCalc.cc +++ b/dcalc/ParallelDelayCalc.cc @@ -118,4 +118,4 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, return dcalc_results; } -} // namespace +} // namespace sta diff --git a/dcalc/ParallelDelayCalc.hh b/dcalc/ParallelDelayCalc.hh index c7d3c921e..46d3f826b 100644 --- a/dcalc/ParallelDelayCalc.hh +++ b/dcalc/ParallelDelayCalc.hh @@ -47,4 +47,4 @@ protected: const MinMax *min_max); }; -} // namespace +} // namespace sta diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index 086e29ce1..a925efdff 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -263,4 +263,4 @@ protected: using ArcDelayCalc::reduceParasitic; }; -} // namespace +} // namespace sta diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc index afda49226..f376ec7b8 100644 --- a/dcalc/UnitDelayCalc.cc +++ b/dcalc/UnitDelayCalc.cc @@ -187,4 +187,4 @@ UnitDelayCalc::finishDrvrPin() { } -} // namespace +} // namespace sta diff --git a/dcalc/UnitDelayCalc.hh b/dcalc/UnitDelayCalc.hh index 5e2a30793..3eaca081a 100644 --- a/dcalc/UnitDelayCalc.hh +++ b/dcalc/UnitDelayCalc.hh @@ -109,4 +109,4 @@ protected: ArcDelayCalc * makeUnitDelayCalc(StaState *sta); -} // namespace +} // namespace sta diff --git a/doc/StaApi.txt b/doc/StaApi.txt index ecf7d35a9..e48eff981 100644 --- a/doc/StaApi.txt +++ b/doc/StaApi.txt @@ -108,9 +108,6 @@ in a class derived from Sta. Because the components refer to each other, Sta::updateComponentsState() must be called to notify the components if any of them are changed after creation. -The file liberty/LibertyExt.cc contains an example that shows how the -liberty reader is replaced with a custom one on the Sta object. - Units ----- @@ -291,7 +288,7 @@ in this arc set: S r -> Z f The liberty file reader can be customized to read attributes that are -not used by the STA. See liberty/LibertyExt.cc for an example. +not used by the STA. Graph ----- diff --git a/graph/Graph.cc b/graph/Graph.cc index ca65fe017..fa92605ce 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -1580,4 +1580,4 @@ VertexIdLess::operator()(const Vertex *vertex1, } -} // namespace +} // namespace sta diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index 85e656d1c..b21d524cc 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -63,7 +63,7 @@ public: const Pin *drvr_pin, Edge *edge, const TimingArc *arc, - const Slew in_slew, + Slew in_slew, float load_cap, const Parasitic *parasitic); ArcDcalcArg(const Pin *in_pin, @@ -160,7 +160,6 @@ class ArcDelayCalc : public StaState { public: ArcDelayCalc(StaState *sta); - virtual ~ArcDelayCalc() {} virtual ArcDelayCalc *copy() = 0; virtual std::string_view name() const = 0; @@ -262,4 +261,4 @@ public: virtual void finishDrvrPin() = 0; }; -} // namespace +} // namespace sta diff --git a/include/sta/Bdd.hh b/include/sta/Bdd.hh index df565a5a3..0cc101bfe 100644 --- a/include/sta/Bdd.hh +++ b/include/sta/Bdd.hh @@ -41,7 +41,7 @@ class Bdd : public StaState { public: Bdd(const StaState *sta); - ~Bdd(); + ~Bdd() override; DdNode *funcBdd(const FuncExpr *expr); DdNode *findNode(const LibertyPort *port); const LibertyPort *nodePort(DdNode *node); @@ -58,4 +58,4 @@ private: BddVarIdxPortMap bdd_var_idx_port_map_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Bfs.hh b/include/sta/Bfs.hh index 162bd9a80..10c90f175 100644 --- a/include/sta/Bfs.hh +++ b/include/sta/Bfs.hh @@ -50,10 +50,10 @@ using LevelQueue = std::vector; // Vertices are marked as being in the queue by using a flag on // the vertex indexed by bfs_index. A unique flag is only needed // if the BFS in in use when other BFS's are simultaneously in use. -class BfsIterator : public StaState, Iterator +class BfsIterator : public StaState, + public Iterator { public: - virtual ~BfsIterator(); // Make sure that the BFS queue is deep enough for the max logic level. void ensureSize(); // Reset to virgin state. @@ -130,7 +130,7 @@ public: BfsFwdIterator(BfsIndex bfs_index, SearchPred *search_pred, StaState *sta); - virtual ~BfsFwdIterator(); + ~BfsFwdIterator() override; void enqueueAdjacentVertices(Vertex *vertex, SearchPred *search_pred) override; void enqueueAdjacentVertices(Vertex *vertex, @@ -152,7 +152,7 @@ public: BfsBkwdIterator(BfsIndex bfs_index, SearchPred *search_pred, StaState *sta); - virtual ~BfsBkwdIterator(); + ~BfsBkwdIterator() override; void enqueueAdjacentVertices(Vertex *vertex, SearchPred *search_pred) override; void enqueueAdjacentVertices(Vertex *vertex, @@ -168,4 +168,4 @@ protected: void incrLevel(Level &level) const override; }; -} // namespace +} // namespace sta diff --git a/include/sta/CircuitSim.hh b/include/sta/CircuitSim.hh index 3e5756e4a..8e366d095 100644 --- a/include/sta/CircuitSim.hh +++ b/include/sta/CircuitSim.hh @@ -28,4 +28,4 @@ namespace sta { enum class CircuitSim { hspice, ngspice, xyce }; -} // namespace +} // namespace sta diff --git a/include/sta/ClkNetwork.hh b/include/sta/ClkNetwork.hh index 4bbfacfa4..1d25b57c8 100644 --- a/include/sta/ClkNetwork.hh +++ b/include/sta/ClkNetwork.hh @@ -44,7 +44,7 @@ class ClkNetwork : public StaState public: ClkNetwork(Mode *mode, StaState *sta); - ~ClkNetwork(); + ~ClkNetwork() override; void ensureClkNetwork(); void clear(); bool isClock(const Pin *pin) const; @@ -73,9 +73,9 @@ private: void findClkPins(); void findClkPins(bool ideal_only, - PinClksMap &clk_pin_map); + PinClksMap &pin_clks_map); - bool clk_pins_valid_; + bool clk_pins_valid_{false}; // pin -> clks PinClksMap pin_clks_map_; // pin -> ideal clks @@ -84,4 +84,4 @@ private: ClkPinsMap clk_pins_map_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index 4422212d4..aae3e58b8 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -82,11 +82,11 @@ public: void removeSlew(); const RiseFallMinMax &slews() const { return slews_; } void setSlewLimit(const RiseFallBoth *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, float slew); void slewLimit(const RiseFall *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, // Return values. float &slew, @@ -291,4 +291,4 @@ int compare(const ClockSet *set1, const ClockSet *set2); -} // namespace +} // namespace sta diff --git a/include/sta/ClockGatingCheck.hh b/include/sta/ClockGatingCheck.hh index 414173841..dad7bdeb1 100644 --- a/include/sta/ClockGatingCheck.hh +++ b/include/sta/ClockGatingCheck.hh @@ -42,4 +42,4 @@ private: LogicValue active_value_; }; -} // namespace +} // namespace sta diff --git a/include/sta/ClockGroups.hh b/include/sta/ClockGroups.hh index 637650881..f725b6e9c 100644 --- a/include/sta/ClockGroups.hh +++ b/include/sta/ClockGroups.hh @@ -34,12 +34,12 @@ namespace sta { class ClockGroups : public SdcCmdComment { public: - ClockGroups(const std::string &name, + ClockGroups(std::string_view name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, - std::string comment); + std::string_view comment); ~ClockGroups(); void makeClockGroup(ClockSet *clks); const std::string &name() const { return name_; } @@ -59,4 +59,4 @@ private: ClockGroupSet groups_; }; -} // namespace +} // namespace sta diff --git a/include/sta/ClockInsertion.hh b/include/sta/ClockInsertion.hh index 1a1aef85f..256e7dbef 100644 --- a/include/sta/ClockInsertion.hh +++ b/include/sta/ClockInsertion.hh @@ -57,4 +57,4 @@ private: RiseFallMinMax delays_[EarlyLate::index_count]; }; -} // namespace +} // namespace sta diff --git a/include/sta/ClockLatency.hh b/include/sta/ClockLatency.hh index 33479fd70..87ddb3b7e 100644 --- a/include/sta/ClockLatency.hh +++ b/include/sta/ClockLatency.hh @@ -61,4 +61,4 @@ private: RiseFallMinMax delays_; }; -} // namespace +} // namespace sta diff --git a/include/sta/ConcreteLibrary.hh b/include/sta/ConcreteLibrary.hh index fc831a99c..50182a1ec 100644 --- a/include/sta/ConcreteLibrary.hh +++ b/include/sta/ConcreteLibrary.hh @@ -127,12 +127,12 @@ public: ConcretePort *makeBundlePort(std::string_view name, ConcretePortSeq *members); // Group previously defined bus bit ports together. - void groupBusPorts(const char bus_brkt_left, - const char bus_brkt_right, + void groupBusPorts(char bus_brkt_left, + char bus_brkt_right, std::function port_msb_first); size_t portCount() const; void setName(std::string_view name); - void addPort(ConcretePort *port); + virtual void addPort(ConcretePort *port); void addPortBit(ConcretePort *port); protected: @@ -275,4 +275,4 @@ private: ConcretePort *next_; }; -} // Namespace +} // namespace sta diff --git a/include/sta/ConcreteNetwork.hh b/include/sta/ConcreteNetwork.hh index b3a377933..592a65a6d 100644 --- a/include/sta/ConcreteNetwork.hh +++ b/include/sta/ConcreteNetwork.hh @@ -62,7 +62,7 @@ class ConcreteNetwork : public NetworkReader { public: ConcreteNetwork(); - ~ConcreteNetwork(); + ~ConcreteNetwork() override; void clear() override; bool linkNetwork(std::string_view top_cell_name, bool make_black_boxes, @@ -354,7 +354,7 @@ public: void setVertexId(VertexId id); protected: - ~ConcretePin() {} + ~ConcretePin() = default; ConcretePin(ConcreteInstance *instance, ConcretePort *port, ConcreteNet *net); @@ -384,7 +384,7 @@ public: ConcretePin *pin() const { return pin_; } protected: - ~ConcreteTerm() {} + ~ConcreteTerm() = default; ConcreteTerm(ConcretePin *pin, ConcreteNet *net); @@ -432,4 +432,4 @@ protected: friend class ConcreteNetPinIterator; }; -} // namespace +} // namespace sta diff --git a/include/sta/ContainerHelpers.hh b/include/sta/ContainerHelpers.hh index 9c5aeae47..9b1a447a4 100644 --- a/include/sta/ContainerHelpers.hh +++ b/include/sta/ContainerHelpers.hh @@ -42,7 +42,8 @@ namespace sta { // 1. Sequence containers (vector, list, deque, …) // ------------------------------------------------------------ template -std::enable_if_t> +requires std::is_pointer_v +void deleteContents(Container& c) { for (auto ptr : c) @@ -51,7 +52,8 @@ deleteContents(Container& c) } template -std::enable_if_t> +requires std::is_pointer_v +void deleteContents(Container *c) { for (auto ptr : *c) @@ -63,8 +65,8 @@ deleteContents(Container *c) // 2. Maps (map, unordered_map) // ------------------------------------------------------------ template -std::enable_if_t -> +requires std::is_pointer_v +void deleteContents(Map& m) { for (auto& kv : m) @@ -73,8 +75,8 @@ deleteContents(Map& m) } template -std::enable_if_t -> +requires std::is_pointer_v +void deleteContents(Map *m) { for (auto& kv : *m) @@ -86,10 +88,10 @@ deleteContents(Map *m) // 3. Sets (set, unordered_set) // ------------------------------------------------------------ template -std::enable_if_t< - std::is_pointer_v && - !std::is_same_v -> +requires (std::is_pointer_v && + requires { typename Set::mapped_type; } && + !std::is_same_v) +void deleteContents(Set& s) { for (auto ptr : s) @@ -119,28 +121,28 @@ struct find_return; template struct find_return { - using type = typename C::mapped_type; + using type = C::mapped_type; }; // pointer to set template struct find_return { - using type = typename C::key_type; + using type = C::key_type; }; // map ref template struct find_return { - using type = typename C::mapped_type; + using type = C::mapped_type; }; // set ref template struct find_return { - using type = typename C::key_type; + using type = C::key_type; }; @@ -149,10 +151,10 @@ struct find_return template auto findKey(const AssocContainer& c, - typename AssocContainer::key_type key) - -> typename find_return::type + const typename AssocContainer::key_type& key) + -> find_return::type { - using ReturnType = typename find_return::type; + using ReturnType = find_return::type; static_assert(std::is_pointer_v, "findKey requires pointer types"); @@ -173,9 +175,9 @@ template auto findStringKey(const AssocContainer& c, std::string_view key) - -> typename find_return::type + -> find_return::type { - using ReturnType = typename find_return::type; + using ReturnType = find_return::type; static_assert(std::is_pointer_v, "findStringKey requires pointer types"); @@ -194,8 +196,8 @@ findStringKey(const AssocContainer& c, template auto findKeyValue(const AssocContainer& c, - typename AssocContainer::key_type key) - -> const typename find_return::type & + const typename AssocContainer::key_type& key) + -> const find_return::type & { auto it = c.find(key); if (it != c.end()) { @@ -213,7 +215,7 @@ findKeyValue(const AssocContainer& c, template void findKeyValue(const AssocContainer& c, - typename AssocContainer::key_type key, + const typename AssocContainer::key_type& key, typename find_return::type &value, bool &exists) { @@ -240,7 +242,7 @@ findKeyValue(const AssocContainer& c, template void findKeyValue(const AssocContainer *c, - typename AssocContainer::key_type key, + const typename AssocContainer::key_type& key, typename find_return::type &value, bool &exists) { @@ -269,8 +271,8 @@ findKeyValue(const AssocContainer *c, template auto findKeyValuePtr(AssocContainer& c, - typename AssocContainer::key_type key) - -> typename find_return::type* + const typename AssocContainer::key_type& key) + -> find_return::type* { auto it = c.find(key); if (it == c.end()) @@ -289,8 +291,8 @@ findKeyValuePtr(AssocContainer& c, template auto findKeyValuePtr(const AssocContainer& c, - typename AssocContainer::key_type key) - -> typename find_return::type const* + const typename AssocContainer::key_type& key) + -> find_return::type const* { auto it = c.find(key); if (it == c.end()) @@ -310,8 +312,8 @@ findKeyValuePtr(const AssocContainer& c, template auto findStringValuePtr(AssocContainer& c, - std::string_view key) - -> typename find_return::type* + std::string_view key) + -> find_return::type* { auto it = c.find(key); if (it == c.end()) @@ -326,8 +328,8 @@ findStringValuePtr(AssocContainer& c, template auto findStringValuePtr(const AssocContainer& c, - std::string_view key) - -> typename find_return::type const* + std::string_view key) + -> find_return::type const* { auto it = c.find(key); if (it == c.end()) @@ -435,7 +437,7 @@ requires std::predicate> void sort(Range& r, - Comp comp = Comp{}) + const Comp &comp = Comp{}) { std::sort(std::ranges::begin(r), std::ranges::end(r), comp); } @@ -450,9 +452,9 @@ requires std::ranges::random_access_range && std::ranges::range_reference_t> void sort(Range* r, - Comp comp = Comp{}) + const Comp &comp = Comp{}) { std::sort(std::ranges::begin(*r), std::ranges::end(*r), comp); } -} // namespace +} // namespace sta diff --git a/include/sta/CycleAccting.hh b/include/sta/CycleAccting.hh index ad18a0cd7..ca4aa3b81 100644 --- a/include/sta/CycleAccting.hh +++ b/include/sta/CycleAccting.hh @@ -130,4 +130,4 @@ private: bool max_cycles_exceeded_; }; -} // namespace +} // namespace sta diff --git a/include/sta/DataCheck.hh b/include/sta/DataCheck.hh index a1bb30ee0..e96e067c2 100644 --- a/include/sta/DataCheck.hh +++ b/include/sta/DataCheck.hh @@ -79,4 +79,4 @@ private: const Network *network_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Debug.hh b/include/sta/Debug.hh index afbb68e27..0bc4be1d5 100644 --- a/include/sta/Debug.hh +++ b/include/sta/Debug.hh @@ -64,9 +64,9 @@ public: protected: Report *report_; std::mutex buffer_lock_; - bool debug_on_; + bool debug_on_{false}; DebugMap debug_map_; - int stats_level_; + int stats_level_{0}; }; // Inlining a varargs function would eval the args, which can @@ -76,4 +76,4 @@ protected: debug->report(what, fmt __VA_OPT__(,) __VA_ARGS__); \ } -} // namespace +} // namespace sta diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index e25475579..983e68891 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -37,16 +37,16 @@ class StaState; class Delay { public: - Delay(); - Delay(float mean); + Delay() noexcept; + Delay(float mean) noexcept; Delay(float mean, // std_dev^2 - float std_dev2); + float std_dev2) noexcept; Delay(float mean, float mean_shift, // std_dev^2 float std_dev2, - float skewness); + float skewness) noexcept; void setValues(float mean, float mean_shift, float std_dev2, @@ -62,7 +62,7 @@ public: float skewness() const { return values_[3]; } void setSkewness(float skewness); - void operator=(float delay); + Delay &operator=(float delay); // This allows applications that do not support statistical timing // to treat Delays as floats without explicitly converting with // delayAsFloat. @@ -77,8 +77,8 @@ private: class DelayDbl { public: - DelayDbl(); - DelayDbl(double value); + DelayDbl() noexcept; + DelayDbl(double value) noexcept; double mean() const { return values_[0]; } void setMean(double mean); double meanShift() const { return values_[1]; } @@ -91,7 +91,7 @@ public: double std_dev2, double skewnes); - void operator=(double delay); + DelayDbl &operator=(double delay); private: std::array values_; @@ -108,7 +108,7 @@ const Delay delay_zero(0.0); class DelayOps { public: - virtual ~DelayOps() {} + virtual ~DelayOps() = default; virtual float stdDev2(const Delay &delay, const EarlyLate *early_late) const = 0; virtual float asFloat(const Delay &delay, @@ -356,4 +356,4 @@ Delay delayRemove(const Delay &delay1, const Delay &delay2); -} // namespace +} // namespace sta diff --git a/include/sta/DelayCalc.hh b/include/sta/DelayCalc.hh index 4969b9566..051279762 100644 --- a/include/sta/DelayCalc.hh +++ b/include/sta/DelayCalc.hh @@ -55,4 +55,4 @@ ArcDelayCalc * makeDelayCalc(std::string_view name, StaState *sta); -} // namespace +} // namespace sta diff --git a/include/sta/DelayNormal.hh b/include/sta/DelayNormal.hh index 3a25cbf78..8b94725e4 100644 --- a/include/sta/DelayNormal.hh +++ b/include/sta/DelayNormal.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -87,4 +87,4 @@ public: const StaState *sta) const override; }; -} // namespace +} // namespace sta diff --git a/include/sta/DelayScalar.hh b/include/sta/DelayScalar.hh index a413c92d6..d255be3c1 100644 --- a/include/sta/DelayScalar.hh +++ b/include/sta/DelayScalar.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -87,4 +87,4 @@ public: const StaState *sta) const override; }; -} // namespace +} // namespace sta diff --git a/include/sta/DelaySkewNormal.hh b/include/sta/DelaySkewNormal.hh index 5922e03ea..dacce47cd 100644 --- a/include/sta/DelaySkewNormal.hh +++ b/include/sta/DelaySkewNormal.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -95,4 +95,4 @@ private: double skewness2) const; }; -} // namespace +} // namespace sta diff --git a/include/sta/DeratingFactors.hh b/include/sta/DeratingFactors.hh index 07678695b..a068a6da7 100644 --- a/include/sta/DeratingFactors.hh +++ b/include/sta/DeratingFactors.hh @@ -117,4 +117,4 @@ public: DeratingFactorsNet(); }; -} // namespace +} // namespace sta diff --git a/include/sta/DisabledPorts.hh b/include/sta/DisabledPorts.hh index 20ea6a18b..5628d85c6 100644 --- a/include/sta/DisabledPorts.hh +++ b/include/sta/DisabledPorts.hh @@ -111,4 +111,4 @@ sortByPathName(const DisabledInstancePortsMap *inst_map, LibertyPortPairSeq sortByName(const LibertyPortPairSet *set); -} // namespace +} // namespace sta diff --git a/include/sta/DispatchQueue.hh b/include/sta/DispatchQueue.hh index f28d94c5e..9baa0099a 100644 --- a/include/sta/DispatchQueue.hh +++ b/include/sta/DispatchQueue.hh @@ -18,10 +18,10 @@ namespace sta { class DispatchQueue { - typedef std::function fp_t; + using fp_t = std::function; public: - DispatchQueue(size_t thread_cnt); + DispatchQueue(size_t thread_count); ~DispatchQueue(); void setThreadCount(size_t thread_count); size_t getThreadCount() const; @@ -49,4 +49,4 @@ private: bool quit_ = false; }; -} // namespace +} // namespace sta diff --git a/include/sta/EnumNameMap.hh b/include/sta/EnumNameMap.hh index 9ae20acb3..6af151c35 100644 --- a/include/sta/EnumNameMap.hh +++ b/include/sta/EnumNameMap.hh @@ -34,7 +34,8 @@ template class EnumNameMap { public: - EnumNameMap(std::initializer_list> enum_names); + EnumNameMap(std::initializer_list> enum_names) noexcept; const std::string &find(ENUM key) const; ENUM find(std::string_view name, ENUM unknown_key) const; @@ -49,7 +50,7 @@ private: }; template -EnumNameMap::EnumNameMap(std::initializer_list> enum_names) : +EnumNameMap::EnumNameMap(std::initializer_list> enum_names) noexcept : enum_map_(enum_names) { for (const auto& [key, name] : enum_map_) @@ -97,4 +98,4 @@ EnumNameMap::find(std::string_view name, return unknown_key; } -} // namespace +} // namespace sta diff --git a/include/sta/EquivCells.hh b/include/sta/EquivCells.hh index 1227a5449..510ee50f7 100644 --- a/include/sta/EquivCells.hh +++ b/include/sta/EquivCells.hh @@ -87,4 +87,4 @@ bool equivCellSequentials(const LibertyCell *cell1, const LibertyCell *cell2); -} // namespace +} // namespace sta diff --git a/include/sta/Error.hh b/include/sta/Error.hh index 80fb1b0ae..7459e3157 100644 --- a/include/sta/Error.hh +++ b/include/sta/Error.hh @@ -36,7 +36,6 @@ class Exception : public std::exception { public: Exception(); - virtual ~Exception() {} const char *what() const noexcept override = 0; }; @@ -44,7 +43,7 @@ class ExceptionMsg : public Exception { public: ExceptionMsg(const std::string &msg, - const bool suppressed); + bool suppressed); const char *what() const noexcept override; bool suppressed() const { return suppressed_; } @@ -93,4 +92,4 @@ protected: #define criticalError(id, msg) \ Report::defaultReport()->fileCritical(id, __FILE__, __LINE__, msg) -} // namespace +} // namespace sta diff --git a/include/sta/ExceptionPath.hh b/include/sta/ExceptionPath.hh index bfe9c16df..07987e944 100644 --- a/include/sta/ExceptionPath.hh +++ b/include/sta/ExceptionPath.hh @@ -70,6 +70,7 @@ public: virtual bool isFilter() const { return false; } virtual ExceptionPathType type() const = 0; virtual std::string to_string(const Network *network) const; + virtual std::string_view typeString() const = 0; ExceptionFrom *from() const { return from_; } ExceptionThruSeq *thrus() const { return thrus_; } ExceptionTo *to() const { return to_; } @@ -135,7 +136,6 @@ public: virtual bool breakPath() const { return false; } protected: - virtual const char *typeString() const = 0; std::string fromThruToString(const Network *network) const; void makeStates(); @@ -172,7 +172,7 @@ public: bool own_pts) override; bool isFalse() const override { return true; } ExceptionPathType type() const override { return ExceptionPathType::false_path; } - const char *typeString() const override; + std::string_view typeString() const override; bool mergeable(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override; int typePriority() const override; @@ -188,7 +188,7 @@ public: bool own_pts); bool isLoop() const override { return true; } ExceptionPathType type() const override { return ExceptionPathType::loop; } - const char *typeString() const override; + std::string_view typeString() const override; bool mergeable(ExceptionPath *exception) const override; }; @@ -212,7 +212,7 @@ public: bool isPathDelay() const override { return true; } ExceptionPathType type() const override { return ExceptionPathType::path_delay; } std::string to_string(const Network *network) const override; - const char *typeString() const override; + std::string_view typeString() const override; bool mergeable(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override; float delay() const override { return delay_; } @@ -248,7 +248,7 @@ public: bool matches(const MinMax *min_max, bool exactly) const override; std::string to_string(const Network *network) const override; - const char *typeString() const override; + std::string_view typeString() const override; bool mergeable(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override; bool useEndClk() const override { return use_end_clk_; } @@ -279,7 +279,7 @@ public: bool own_pts) override; bool isFilter() const override { return true; } ExceptionPathType type() const override { return ExceptionPathType::filter; } - const char *typeString() const override; + std::string_view typeString() const override; bool mergeable(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override; bool resetMatch(ExceptionFrom *from, @@ -308,7 +308,7 @@ public: bool own_pts) override; bool isGroupPath() const override { return true; } ExceptionPathType type() const override { return ExceptionPathType::group_path; } - const char *typeString() const override; + std::string_view typeString() const override; bool mergeable(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override; int typePriority() const override; @@ -327,7 +327,7 @@ class ExceptionPt public: ExceptionPt(const RiseFallBoth *rf, bool own_pts); - virtual ~ExceptionPt() {}; + virtual ~ExceptionPt() = default; virtual bool isFrom() const { return false; } virtual bool isThru() const { return false; } virtual bool isTo() const { return false; } @@ -386,7 +386,7 @@ public: const RiseFallBoth *rf, bool own_pts, const Network *network); - ~ExceptionFromTo(); + ~ExceptionFromTo() override; PinSet *pins() override { return pins_; } bool hasPins() const; ClockSet *clks() override { return clks_; } @@ -512,7 +512,7 @@ public: const RiseFallBoth *rf, bool own_pts, const Network *network); - ~ExceptionThru(); + ~ExceptionThru() override; ExceptionThru *clone(const Network *network); std::string to_string(const Network *network) const override; bool isThru() const override { return true; } @@ -538,15 +538,6 @@ public: const Network *network) const; int typePriority() const override { return 2; } size_t objectCount() const override; - void connectPinAfter(PinSet *drvrs, - Network *network) override; - void deletePinBefore(const Pin *pin, - Network *network) override; - void deleteInstance(const Instance *inst, - const Network *network); - -protected: - void findHash(const Network *network); void addPin(const Pin *pin, const Network *network) override; void addEdge(const EdgePins &edge, @@ -556,6 +547,15 @@ protected: void addInstance(const Instance *inst, const Network *network) override; void addClock(Clock *) override {} + void connectPinAfter(PinSet *drvrs, + Network *network) override; + void deletePinBefore(const Pin *pin, + Network *network) override; + void deleteInstance(const Instance *inst, + const Network *network); + +protected: + void findHash(const Network *network); void deletePin(const Pin *pin, const Network *network); void deleteEdge(const EdgePins &edge); @@ -620,7 +620,6 @@ class ExpandedExceptionVisitor public: ExpandedExceptionVisitor(ExceptionPath *exception, const Network *network); - virtual ~ExpandedExceptionVisitor() {} void visitExpansions(); // From/thrus/to have a single exception point (pin/instance/net/clock). virtual void visit(ExceptionFrom *from, @@ -678,7 +677,7 @@ exceptionStateCmp(const ExceptionState *state1, class EmptyExpceptionPt : public Exception { public: - virtual const char *what() const noexcept; + const char *what() const noexcept override; }; class ExceptionPathLess @@ -698,4 +697,4 @@ checkFromThrusTo(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to); -} // namespace +} // namespace sta diff --git a/include/sta/FilterObjects.hh b/include/sta/FilterObjects.hh index dbeec3c5a..3de0dd844 100644 --- a/include/sta/FilterObjects.hh +++ b/include/sta/FilterObjects.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -28,6 +28,7 @@ #include #include +#include "GraphClass.hh" #include "NetworkClass.hh" #include "SdcClass.hh" #include "SearchClass.hh" @@ -93,4 +94,4 @@ StringSeq filterExprToPostfix(std::string_view expr, Report *report); -} // namespace +} // namespace sta diff --git a/include/sta/FuncExpr.hh b/include/sta/FuncExpr.hh index 00ff6dee7..93db8d681 100644 --- a/include/sta/FuncExpr.hh +++ b/include/sta/FuncExpr.hh @@ -105,4 +105,4 @@ private: FuncExpr * funcExprNot(FuncExpr *expr); -} // namespace +} // namespace sta diff --git a/include/sta/Fuzzy.hh b/include/sta/Fuzzy.hh index 5517190b7..29963b230 100644 --- a/include/sta/Fuzzy.hh +++ b/include/sta/Fuzzy.hh @@ -48,4 +48,4 @@ fuzzyGreaterEqual(float v1, bool fuzzyInf(float value); -} // namespace +} // namespace sta diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index a74759628..2d3024a77 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -61,7 +61,7 @@ public: Graph(StaState *sta, DcalcAPIndex ap_count); void makeGraph(); - ~Graph(); + ~Graph() override; // Number of arc delays and slews from sdf or delay calculation. void setDelayCount(DcalcAPIndex ap_count); @@ -291,8 +291,8 @@ protected: bool is_bidirect_drvr, bool is_reg_clk); void clear(); - Slew *slews() { return std::bit_cast(slews_); } - const Slew *slews() const { return std::bit_cast(slews_); } + Slew *slews() { return reinterpret_cast(slews_); } + const Slew *slews() const { return reinterpret_cast(slews_); } float *slewsFloat() { return slews_; } const float *slewsFloat() const { return slews_; } void setSlews(float *slews); @@ -491,4 +491,4 @@ makeVertexSet(StaState *sta) return VertexSet(VertexIdLess(sta->graphRef())); } -} // namespace +} // namespace sta diff --git a/include/sta/GraphClass.hh b/include/sta/GraphClass.hh index c56cea173..bb0309245 100644 --- a/include/sta/GraphClass.hh +++ b/include/sta/GraphClass.hh @@ -76,4 +76,4 @@ static constexpr int slew_annotated_bits = MinMax::index_count * RiseFall::index // Bit shifts used to mark vertices in a Bfs queue. enum class BfsIndex { dcalc, arrival, required, other, bits }; -} // namespace +} // namespace sta diff --git a/include/sta/GraphCmp.hh b/include/sta/GraphCmp.hh index bf67c9634..add7530a1 100644 --- a/include/sta/GraphCmp.hh +++ b/include/sta/GraphCmp.hh @@ -59,4 +59,4 @@ sortEdges(EdgeSeq *edges, Network *network, Graph *graph); -} // namespace +} // namespace sta diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 85d198c66..5d2ae509f 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -53,8 +53,8 @@ class GraphDelayCalc : public StaState { public: GraphDelayCalc(StaState *sta); - virtual ~GraphDelayCalc(); - virtual void copyState(const StaState *sta); + ~GraphDelayCalc() override; + void copyState(const StaState *sta) override; // Set the observer for edge delay changes. virtual void setObserver(DelayCalcObserver *observer); // Invalidate all delays/slews. @@ -323,8 +323,7 @@ protected: class DelayCalcObserver { public: - DelayCalcObserver() {} - virtual ~DelayCalcObserver() {} + virtual ~DelayCalcObserver() = default; virtual void delayChangedFrom(Vertex *vertex) = 0; virtual void delayChangedTo(Vertex *vertex) = 0; virtual void checkDelayChangedTo(Vertex *vertex) = 0; @@ -359,4 +358,4 @@ private: std::vector net_caps_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Hash.hh b/include/sta/Hash.hh index 6f87d56cf..ce68f03a5 100644 --- a/include/sta/Hash.hh +++ b/include/sta/Hash.hh @@ -67,4 +67,4 @@ hashString(std::string_view str); #define hashPtr(ptr) (reinterpret_cast(ptr) >> 2) #endif -} // namespace +} // namespace sta diff --git a/include/sta/HpinDrvrLoad.hh b/include/sta/HpinDrvrLoad.hh index 974156fc3..4b68cae82 100644 --- a/include/sta/HpinDrvrLoad.hh +++ b/include/sta/HpinDrvrLoad.hh @@ -47,8 +47,7 @@ public: class HpinDrvrLoadVisitor { public: - HpinDrvrLoadVisitor() {} - virtual ~HpinDrvrLoadVisitor() {} + virtual ~HpinDrvrLoadVisitor() = default; virtual void visit(HpinDrvrLoad *drvr_load) = 0; }; @@ -76,4 +75,4 @@ private: PinSet *hpins_to_load_; }; -} // namespace +} // namespace sta diff --git a/include/sta/InputDrive.hh b/include/sta/InputDrive.hh index 6eacc7e7d..a5dbdf2d8 100644 --- a/include/sta/InputDrive.hh +++ b/include/sta/InputDrive.hh @@ -116,4 +116,4 @@ private: const LibertyPort *to_port_; }; -} // namespace +} // namespace sta diff --git a/include/sta/InternalPower.hh b/include/sta/InternalPower.hh index 30d20ebbc..fb1c2d013 100644 --- a/include/sta/InternalPower.hh +++ b/include/sta/InternalPower.hh @@ -94,4 +94,4 @@ protected: InternalPowerModels models_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Iterator.hh b/include/sta/Iterator.hh index 83e799ebf..0c7e9ba30 100644 --- a/include/sta/Iterator.hh +++ b/include/sta/Iterator.hh @@ -35,7 +35,7 @@ template class Iterator { public: - virtual ~Iterator() {} + virtual ~Iterator() = default; virtual bool hasNext() = 0; virtual OBJ next() = 0; }; @@ -116,4 +116,4 @@ protected: SET_TYPE::const_iterator itr_; }; -} // namespace +} // namespace sta diff --git a/include/sta/LeakagePower.hh b/include/sta/LeakagePower.hh index 15f84db21..265e83f23 100644 --- a/include/sta/LeakagePower.hh +++ b/include/sta/LeakagePower.hh @@ -50,4 +50,4 @@ protected: float power_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index a6f8b5b35..204431273 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -80,7 +80,7 @@ public: private: std::string value_; - FuncExpr *cond_; + FuncExpr *cond_{nullptr}; std::string sdf_cond_; }; @@ -159,10 +159,10 @@ enum class PwrGndType { none, deepnwell, deeppwell}; enum class ScaleFactorPvt { process, volt, temp, unknown }; -constexpr int scale_factor_pvt_count = int(ScaleFactorPvt::unknown) + 1; +constexpr int scale_factor_pvt_count = static_cast(ScaleFactorPvt::unknown) + 1; enum class TableTemplateType { delay, power, output_current, capacitance, ocv }; -constexpr int table_template_type_count = int(TableTemplateType::ocv) + 1; +constexpr int table_template_type_count = static_cast(TableTemplateType::ocv) + 1; enum class LevelShifterType { HL, LH, HL_LH }; @@ -206,7 +206,7 @@ class LibertyLibrary : public ConcreteLibrary public: LibertyLibrary(std::string_view name, std::string_view filename); - virtual ~LibertyLibrary(); + ~LibertyLibrary() override; LibertyCell *findLibertyCell(std::string_view name) const; LibertyCellSeq findLibertyCellsMatching(PatternMatch *pattern); // Liberty cells that are buffers. @@ -269,7 +269,7 @@ public: void defaultIntrinsic(const RiseFall *rf, // Return values. - float &intrisic, + float &intrinsic, bool &exists) const; void setDefaultIntrinsic(const RiseFall *rf, float value); @@ -360,7 +360,7 @@ public: void setDefaultOcvDerate(OcvDerate *derate); OcvDerate *makeOcvDerate(std::string_view name); OcvDerate *findOcvDerate(std::string_view derate_name); - void addSupplyVoltage(std::string_view suppy_name, + void addSupplyVoltage(std::string_view supply_name, float voltage); bool supplyExists(std::string_view supply_name) const; void supplyVoltage(std::string_view supply_name, @@ -374,19 +374,19 @@ public: static void makeSceneMap(LibertyLibrary *lib, - int ap_index, + size_t lib_ap_index, Network *network, Report *report); static void makeSceneMap(LibertyCell *link_cell, - LibertyCell *map_cell, - int ap_index, + LibertyCell *scene_cell, + size_t lib_ap_index, Report *report); static void makeSceneMap(LibertyCell *cell1, LibertyCell *cell2, bool link, - int ap_index, + size_t lib_ap_index, Report *report); static void checkScenes(LibertyCell *cell, @@ -396,62 +396,66 @@ public: DriverWaveform *findDriverWaveform(std::string_view name); DriverWaveform *driverWaveformDefault() { return findDriverWaveform(""); } DriverWaveform *makeDriverWaveform(std::string_view name, - TablePtr waveforms); + const TablePtr &waveforms); protected: float degradeWireSlew(const TableModel *model, float in_slew, float wire_delay) const; - Units *units_; - DelayModelType delay_model_type_; + static constexpr float input_threshold_default_ = .5; + static constexpr float output_threshold_default_ = .5; + static constexpr float slew_lower_threshold_default_ = .2; + static constexpr float slew_upper_threshold_default_ = .8; + + Units *units_{nullptr}; + DelayModelType delay_model_type_{DelayModelType::table}; BusDclMap bus_dcls_; TableTemplateMap template_maps_[table_template_type_count]; - float nominal_process_; - float nominal_voltage_; - float nominal_temperature_; - ScaleFactors *scale_factors_; + float nominal_process_{0.0F}; + float nominal_voltage_{0.0F}; + float nominal_temperature_{0.0F}; + ScaleFactors *scale_factors_{nullptr}; ScaleFactorsMap scale_factors_map_; - TableModel *wire_slew_degradation_tbls_[RiseFall::index_count]; - float default_input_pin_cap_; - float default_output_pin_cap_; - float default_bidirect_pin_cap_; + TableModel *wire_slew_degradation_tbls_[RiseFall::index_count]{nullptr, nullptr}; + float default_input_pin_cap_{0.0F}; + float default_output_pin_cap_{0.0F}; + float default_bidirect_pin_cap_{0.0F}; RiseFallValues default_intrinsic_; RiseFallValues default_inout_pin_res_; RiseFallValues default_output_pin_res_; - float default_fanout_load_; - bool default_fanout_load_exists_; - float default_max_cap_; - bool default_max_cap_exists_; - float default_max_fanout_; - bool default_max_fanout_exists_; - float default_max_slew_; - bool default_max_slew_exists_; - float input_threshold_[RiseFall::index_count]; - float output_threshold_[RiseFall::index_count]; - float slew_lower_threshold_[RiseFall::index_count]; - float slew_upper_threshold_[RiseFall::index_count]; - float slew_derate_from_library_; + float default_fanout_load_{0.0F}; + bool default_fanout_load_exists_{false}; + float default_max_cap_{0.0F}; + bool default_max_cap_exists_{false}; + float default_max_fanout_{0.0F}; + bool default_max_fanout_exists_{false}; + float default_max_slew_{0.0F}; + bool default_max_slew_exists_{false}; + float input_threshold_[RiseFall::index_count]{input_threshold_default_, + input_threshold_default_}; + float output_threshold_[RiseFall::index_count]{output_threshold_default_, + output_threshold_default_}; + float slew_lower_threshold_[RiseFall::index_count]{slew_lower_threshold_default_, + slew_lower_threshold_default_}; + float slew_upper_threshold_[RiseFall::index_count]{slew_upper_threshold_default_, + slew_upper_threshold_default_}; + float slew_derate_from_library_{1.0F}; WireloadMap wireloads_; - const Wireload *default_wire_load_; - WireloadMode default_wire_load_mode_; - const WireloadSelection *default_wire_load_selection_; + const Wireload *default_wire_load_{nullptr}; + WireloadMode default_wire_load_mode_{WireloadMode::unknown}; + const WireloadSelection *default_wire_load_selection_{nullptr}; WireloadSelectionMap wire_load_selections_; OperatingConditionsMap operating_conditions_; - OperatingConditions *default_operating_conditions_; - float ocv_arc_depth_; - OcvDerate *default_ocv_derate_; + OperatingConditions *default_operating_conditions_{nullptr}; + float ocv_arc_depth_{0.0F}; + OcvDerate *default_ocv_derate_{nullptr}; OcvDerateMap ocv_derate_map_; SupplyVoltageMap supply_voltage_map_; - LibertyCellSeq *buffers_; - LibertyCellSeq *inverters_; + LibertyCellSeq *buffers_{nullptr}; + LibertyCellSeq *inverters_{nullptr}; DriverWaveformMap driver_waveform_map_; - static constexpr float input_threshold_default_ = .5; - static constexpr float output_threshold_default_ = .5; - static constexpr float slew_lower_threshold_default_ = .2; - static constexpr float slew_upper_threshold_default_ = .8; - private: friend class LibertyCell; friend class LibertyCellIterator; @@ -476,12 +480,11 @@ public: LibertyCell(LibertyLibrary *library, std::string_view name, std::string_view filename); - virtual ~LibertyCell(); + ~LibertyCell() override; LibertyLibrary *libertyLibrary() const { return liberty_library_; } LibertyLibrary *libertyLibrary() { return liberty_library_; } LibertyPort *findLibertyPort(std::string_view name) const; LibertyPortSeq findLibertyPortsMatching(PatternMatch *pattern) const; - bool hasInternalPorts() const { return has_internal_ports_; } ScaleFactors *scaleFactors() const { return scale_factors_; } void setScaleFactors(ScaleFactors *scale_factors); ModeDef *makeModeDef(std::string_view name); @@ -524,7 +527,7 @@ public: const LibertyPort *to) const; size_t timingArcSetCount() const; // Find a timing arc set equivalent to key. - TimingArcSet *findTimingArcSet(TimingArcSet *key) const; + TimingArcSet *findTimingArcSet(TimingArcSet *arc_set) const; TimingArcSet *findTimingArcSet(size_t index) const; bool hasTimingArcs(LibertyPort *port) const; @@ -552,7 +555,7 @@ public: // timing arcs. bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; } TestCell *testCell() const { return test_cell_; } - void latchEnable(const TimingArcSet *arc_set, + void latchEnable(const TimingArcSet *d_to_q_set, // Return values. const LibertyPort *&enable_port, const FuncExpr *&enable_func, @@ -560,7 +563,7 @@ public: const RiseFall *latchCheckEnableEdge(TimingArcSet *check_set); LibertyCell *sceneCell(const Scene *scene, const MinMax *min_max); - LibertyCell *sceneCell(int ap_index); + LibertyCell *sceneCell(size_t lib_ap_index); // AOCV float ocvArcDepth() const; @@ -604,7 +607,7 @@ public: void setTestCell(TestCell *test); void setHasInferedRegTimingArcs(bool infered); void setSceneCell(LibertyCell *scene_cell, - int ap_index); + size_t lib_ap_index); // Call after cell is finished being constructed. void finish(bool infer_latches, Report *report, @@ -623,9 +626,9 @@ public: void setFootprint(std::string_view footprint); const std::string &userFunctionClass() const { return user_function_class_; } void setUserFunctionClass(std::string_view user_function_class); + void addPort(ConcretePort *port) override; protected: - void addPort(ConcretePort *port); void setHasInternalPorts(bool has_internal); void setLibertyLibrary(LibertyLibrary *library); void makeLatchEnables(Report *report, @@ -661,35 +664,35 @@ protected: bool checkSceneCell(const Scene *scene, const MinMax *min_max) const; - LibertyLibrary *liberty_library_; - float area_; - bool dont_use_; - bool is_macro_; - bool is_memory_; - bool is_pad_; - bool is_clock_cell_; - bool is_level_shifter_; - LevelShifterType level_shifter_type_; - bool is_isolation_cell_; - bool always_on_; - SwitchCellType switch_cell_type_; - bool interface_timing_; - ClockGateType clock_gate_type_; + LibertyLibrary *liberty_library_{nullptr}; + float area_{0.0F}; + bool dont_use_{false}; + bool is_macro_{false}; + bool is_memory_{false}; + bool is_pad_{false}; + bool is_clock_cell_{false}; + bool is_level_shifter_{false}; + LevelShifterType level_shifter_type_{LevelShifterType::HL_LH}; + bool is_isolation_cell_{false}; + bool always_on_{false}; + SwitchCellType switch_cell_type_{SwitchCellType::fine_grain}; + bool interface_timing_{false}; + ClockGateType clock_gate_type_{ClockGateType::none}; TimingArcSetSeq timing_arc_sets_; TimingArcSetSet timing_arc_set_set_; LibertyPortPairTimingArcMap port_timing_arc_set_map_; - bool has_infered_reg_timing_arcs_; + bool has_infered_reg_timing_arcs_{false}; InternalPowerSeq internal_powers_; PortInternalPowerMap port_internal_powers_; LeakagePowerSeq leakage_powers_; SequentialSeq sequentials_; PortToSequentialMap port_to_seq_map_; - Statetable *statetable_; + Statetable *statetable_{nullptr}; BusDclMap bus_dcls_; ModeDefMap mode_defs_; - ScaleFactors *scale_factors_; + ScaleFactors *scale_factors_{nullptr}; ScaledCellMap scaled_cells_; - TestCell *test_cell_; + TestCell *test_cell_{nullptr}; // Latch D->Q to LatchEnable index. LatchEnableIndexMap latch_d_to_q_map_; // Latch EN->D setup to LatchEnable index. @@ -697,14 +700,14 @@ protected: LatchEnableSeq latch_enables_; // Ports that have latch D->Q timing arc sets from them. LibertyPortSet latch_data_ports_; - float ocv_arc_depth_; - OcvDerate *ocv_derate_; + float ocv_arc_depth_{0.0F}; + OcvDerate *ocv_derate_{nullptr}; OcvDerateMap ocv_derate_map_; std::vector scene_cells_; - float leakage_power_; - bool leakage_power_exists_; - bool has_internal_ports_; - std::atomic have_voltage_waveforms_; + float leakage_power_{0.0F}; + bool leakage_power_exists_{false}; + bool has_internal_ports_{false}; + std::atomic have_voltage_waveforms_{false}; std::mutex waveform_lock_; std::string footprint_; std::string user_function_class_; @@ -731,7 +734,7 @@ class LibertyCellPortBitIterator : public Iterator { public: LibertyCellPortBitIterator(const LibertyCell *cell); - virtual ~LibertyCellPortBitIterator(); + ~LibertyCellPortBitIterator() override; bool hasNext() override; LibertyPort *next() override; @@ -748,9 +751,7 @@ public: LibertyLibrary *libertyLibrary() const { return liberty_cell_->libertyLibrary(); } LibertyPort *findLibertyMember(int index) const; LibertyPort *findLibertyBusBit(int index) const; - LibertyPort *bundlePort() const; BusDcl *busDcl() const { return bus_dcl_; } - void setDirection(PortDirection *dir); //////////////////////////////////////////////////////////////// // pg_pin functions @@ -872,15 +873,15 @@ public: const RiseFall *pulseClkTrigger() const { return pulse_clk_trigger_; } // Rise for high, fall for low. const RiseFall *pulseClkSense() const { return pulse_clk_sense_; } - void setPulseClk(const RiseFall *rfigger, + void setPulseClk(const RiseFall *trigger, const RiseFall *sense); LibertyPort *scenePort(const Scene *scene, const MinMax *min_max); const LibertyPort *scenePort(const Scene *scene, const MinMax *min_max) const; - const LibertyPort *scenePort(int ap_index) const; + const LibertyPort *scenePort(size_t lib_ap_index) const; void setScenePort(LibertyPort *scene_port, - int ap_index); + size_t lib_ap_index); LibertyPort *relatedGroundPort() const { return related_ground_port_; } void setRelatedGroundPort(LibertyPort *related_ground_port); LibertyPort *relatedPowerPort() const { return related_power_port_; } @@ -913,7 +914,7 @@ protected: int to_index, bool is_bundle, ConcretePortSeq *members); - virtual ~LibertyPort(); + ~LibertyPort() override; void setMinPort(LibertyPort *min); void addScaledPort(OperatingConditions *op_cond, LibertyPort *scaled_port); @@ -927,48 +928,48 @@ protected: float, const MinMax *)> &setter); - LibertyPort *scenePort(int ap_index); + LibertyPort *scenePort(size_t lib_ap_index); - LibertyCell *liberty_cell_; - BusDcl *bus_dcl_; - PwrGndType pwr_gnd_type_; + LibertyCell *liberty_cell_{nullptr}; + BusDcl *bus_dcl_{nullptr}; + PwrGndType pwr_gnd_type_{PwrGndType::none}; std::string voltage_name_; - ScanSignalType scan_signal_type_; - FuncExpr *function_; - FuncExpr *tristate_enable_; - ScaledPortMap *scaled_ports_; + ScanSignalType scan_signal_type_{ScanSignalType::none}; + FuncExpr *function_{nullptr}; + FuncExpr *tristate_enable_{nullptr}; + ScaledPortMap *scaled_ports_{nullptr}; RiseFallMinMax capacitance_; MinMaxFloatValues slew_limit_; // inputs and outputs MinMaxFloatValues cap_limit_; // outputs - float fanout_load_; // inputs - bool fanout_load_exists_; + float fanout_load_{0.0F}; // inputs + bool fanout_load_exists_{false}; MinMaxFloatValues fanout_limit_; // outputs - float min_period_; - float min_pulse_width_[RiseFall::index_count]; - const RiseFall *pulse_clk_trigger_; - const RiseFall *pulse_clk_sense_; - LibertyPort *related_ground_port_; - LibertyPort *related_power_port_; + float min_period_{0.0F}; + float min_pulse_width_[RiseFall::index_count]{0.0F, 0.0F}; + const RiseFall *pulse_clk_trigger_{nullptr}; + const RiseFall *pulse_clk_sense_{nullptr}; + LibertyPort *related_ground_port_{nullptr}; + LibertyPort *related_power_port_{nullptr}; std::vector scene_ports_; - ReceiverModelPtr receiver_model_; - DriverWaveform *driver_waveform_[RiseFall::index_count]; - - unsigned int min_pulse_width_exists_:RiseFall::index_count; - bool min_period_exists_:1; - bool is_clk_:1; - bool is_reg_clk_:1; - bool is_reg_output_:1; - bool is_latch_data_: 1; - bool is_check_clk_:1; - bool is_clk_gate_clk_:1; - bool is_clk_gate_enable_:1; - bool is_clk_gate_out_:1; - bool is_pll_feedback_:1; - bool isolation_cell_data_:1; - bool isolation_cell_enable_:1; - bool level_shifter_data_:1; - bool is_switch_:1; - bool is_pad_:1; + ReceiverModelPtr receiver_model_{nullptr}; + DriverWaveform *driver_waveform_[RiseFall::index_count]{nullptr, nullptr}; + + bool min_pulse_width_exists_:RiseFall::index_count {false}; + bool min_period_exists_:1 {false}; + bool is_clk_:1 {false}; + bool is_reg_clk_:1 {false}; + bool is_reg_output_:1 {false}; + bool is_latch_data_: 1 {false}; + bool is_check_clk_:1 {false}; + bool is_clk_gate_clk_:1 {false}; + bool is_clk_gate_enable_:1 {false}; + bool is_clk_gate_out_:1 {false}; + bool is_pll_feedback_:1 {false}; + bool isolation_cell_data_:1 {false}; + bool isolation_cell_enable_:1 {false}; + bool level_shifter_data_:1 {false}; + bool is_switch_:1 {false}; + bool is_pad_:1 {false}; private: friend class LibertyLibrary; @@ -984,7 +985,7 @@ class LibertyPortMemberIterator : public Iterator { public: LibertyPortMemberIterator(const LibertyPort *port); - virtual ~LibertyPortMemberIterator(); + ~LibertyPortMemberIterator() override; bool hasNext() override; LibertyPort *next() override; @@ -999,7 +1000,7 @@ public: Pvt(float process, float voltage, float temperature); - virtual ~Pvt() {} + virtual ~Pvt() = default; float process() const { return process_; } void setProcess(float process); float voltage() const { return voltage_; } @@ -1023,7 +1024,7 @@ public: protected: std::string name_; - WireloadTree wire_load_tree_; + WireloadTree wire_load_tree_{WireloadTree::unknown}; }; class ScaleFactors @@ -1112,7 +1113,7 @@ public: protected: std::string name_; - TableTemplateType type_; + TableTemplateType type_{TableTemplateType::delay}; TableAxisPtr axis1_; TableAxisPtr axis2_; TableAxisPtr axis3_; @@ -1124,15 +1125,12 @@ public: TestCell(LibertyLibrary *library, std::string_view name, std::string_view filename); - -protected: }; class OcvDerate { public: OcvDerate(std::string_view name); - ~OcvDerate(); const std::string &name() const { return name_; } const Table *derateTable(const RiseFall *rf, const EarlyLate *early_late, @@ -1153,8 +1151,8 @@ portLibertyToSta(std::string_view port_name); const std::string & scanSignalTypeName(ScanSignalType scan_type); const std::string & -pwrGndTypeName(PwrGndType pwr_gnd_type); +pwrGndTypeName(PwrGndType pg_type); PwrGndType findPwrGndType(std::string_view pg_name); -} // namespace +} // namespace sta diff --git a/include/sta/LibertyClass.hh b/include/sta/LibertyClass.hh index af00c3a4f..17f547ad1 100644 --- a/include/sta/LibertyClass.hh +++ b/include/sta/LibertyClass.hh @@ -85,7 +85,7 @@ enum class ScaleFactorType : unsigned { wire_cap, wire_res, min_period, - // Liberty attributes have rise/fall suffix. + // Liberty attributes with rise/fall suffix. cell, hold, setup, @@ -95,13 +95,13 @@ enum class ScaleFactorType : unsigned { skew, leakage_power, internal_power, - // Liberty attributes have rise/fall prefix. + // Liberty attributes with rise/fall prefix. transition, - // Liberty attributes have low/high suffix (indexed as rise/fall). + // Liberty attributes with low/high suffix (indexed as rise/fall). min_pulse_width, unknown, }; -const int scale_factor_type_count = int(ScaleFactorType::unknown) + 1; +const int scale_factor_type_count = static_cast(ScaleFactorType::unknown) + 1; // Enough bits to hold a ScaleFactorType enum. const int scale_factor_bits = 4; @@ -116,7 +116,7 @@ enum class TimingSense { none, unknown }; -const int timing_sense_count = int(TimingSense::unknown) + 1; +const int timing_sense_count = static_cast(TimingSense::unknown) + 1; const int timing_sense_bit_count = 3; enum class TableAxisVariable { @@ -178,4 +178,4 @@ public: } }; -} // namespace +} // namespace sta diff --git a/include/sta/LibertyWriter.hh b/include/sta/LibertyWriter.hh index a58ce44ea..0178ad8e9 100644 --- a/include/sta/LibertyWriter.hh +++ b/include/sta/LibertyWriter.hh @@ -35,4 +35,4 @@ writeLiberty(LibertyLibrary *lib, const char *filename, StaState *sta); -} // namespace +} // namespace sta diff --git a/include/sta/LinearModel.hh b/include/sta/LinearModel.hh index 0ee16401e..d7e0218dd 100644 --- a/include/sta/LinearModel.hh +++ b/include/sta/LinearModel.hh @@ -55,10 +55,9 @@ public: PocvMode pocv_mode, int digits) const override; float driveResistance(const Pvt *pvt) const override; - -protected: void setIsScaled(bool is_scaled) override; +protected: float intrinsic_; float resistance_; }; @@ -82,11 +81,10 @@ public: const MinMax *min_max, PocvMode pocv_mode, int digits) const override; - -protected: void setIsScaled(bool is_scaled) override; +protected: float intrinsic_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Machine.hh b/include/sta/Machine.hh index b02e1e5f8..1df4d8281 100644 --- a/include/sta/Machine.hh +++ b/include/sta/Machine.hh @@ -26,11 +26,6 @@ // This header contains global os/port specific definitions. -// Pragma placeholder for non-gcc compilers. -#ifndef __GNUC__ - #define __attribute__(x) -#endif // __GNUC__ - #ifdef _MSC_VER // Microcruft Visual C++ // Obtuse warning codes enabled by pragma. @@ -48,6 +43,7 @@ // 4611 = setjmp used in C++ function // 4701 = variable used but not initialized #pragma warning( 3 : 4018 4032 4132 4189 4201 4222 4234 4505 4611 4701 ) + // Disable security warnings for posix functions. // _CRT_SECURE_NO_WARNINGS does not seem to work #pragma warning( disable : 4996 ) @@ -65,7 +61,7 @@ // Flex doesn't check for unistd.h. #define YY_NO_UNISTD_H namespace sta { - int vsnprint(char *str, size_t size, const char *fmt, va_list args); + int vsnprint(char *str, size_t size, const char *fmt, const va_list args); int vasprintf(char **str, const char *fmt, va_list args); } #else diff --git a/include/sta/MakeConcreteNetwork.hh b/include/sta/MakeConcreteNetwork.hh index 7f82834a9..47dd56b42 100644 --- a/include/sta/MakeConcreteNetwork.hh +++ b/include/sta/MakeConcreteNetwork.hh @@ -31,4 +31,4 @@ class NetworkReader; NetworkReader * makeConcreteNetwork(); -} // namespace +} // namespace sta diff --git a/include/sta/MinMax.hh b/include/sta/MinMax.hh index 24b911553..5d5c86866 100644 --- a/include/sta/MinMax.hh +++ b/include/sta/MinMax.hh @@ -52,12 +52,12 @@ public: static const MinMax *max() { return &max_; } static const EarlyLate *early() { return &min_; } static const EarlyLate *late() { return &max_; } - static int minIndex() { return min_.index_; } - static int earlyIndex() { return min_.index_; } - static int maxIndex() { return max_.index_; } - static int lateIndex() { return max_.index_; } + static size_t minIndex() { return min_.index_; } + static size_t earlyIndex() { return min_.index_; } + static size_t maxIndex() { return max_.index_; } + static size_t lateIndex() { return max_.index_; } const std::string &to_string() const { return name_; } - int index() const { return index_; } + size_t index() const { return index_; } float initValue() const { return init_value_; } int initValueInt() const { return init_value_int_; } // Max value1 > value2, Min value1 < value2. @@ -72,18 +72,18 @@ public: // for (auto min_max : MinMax::range()) {} static const std::array &range() { return range_; } // for (auto mm_index : MinMax::rangeIndex()) {} - static const std::array &rangeIndex() { return range_index_; } + static const std::array &rangeIndex() { return range_index_; } // Find MinMax from name. - static const MinMax *find(const char *min_max); + static const MinMax *find(std::string_view min_max); // Find MinMax from index. - static const MinMax *find(int index); - static const int index_max = 1; - static const int index_count = 2; - static const int index_bit_count = 1; + static const MinMax *find(size_t index); + static const size_t index_max = 1; + static const size_t index_count = 2; + static const size_t index_bit_count = 1; private: - MinMax(const char *name, - int index, + MinMax(std::string_view name, + size_t index, float init_value, int init_value_int, bool (*compare)(float value1, @@ -99,7 +99,7 @@ private: static const MinMax min_; static const MinMax max_; static const std::array range_; - static const std::array range_index_; + static const std::array range_index_; }; // Min/Max/All, where "All" means use both min and max. @@ -114,7 +114,7 @@ public: static const MinMaxAll *all() { return &all_; } static const MinMaxAll *minMax() { return &all_; } const std::string &to_string() const { return name_; } - int index() const { return index_; } + size_t index() const { return index_; } const MinMax *asMinMax() const; bool matches(const MinMax *min_max) const; bool matches(const MinMaxAll *min_max) const; @@ -122,22 +122,22 @@ public: // for (const auto min_max : min_max->range()) {} const std::vector &range() const { return range_; } // for (const auto mm_index : min_max->rangeIndex()) {} - const std::vector &rangeIndex() const { return range_index_; } + const std::vector &rangeIndex() const { return range_index_; } private: - MinMaxAll(const char *name, - int index, - std::vector range, - std::vector range_index); + MinMaxAll(std::string_view name, + size_t index, + const std::vector &range, + const std::vector &range_index); const std::string name_; - int index_; + size_t index_; const std::vector range_; - const std::vector range_index_; + const std::vector range_index_; static const MinMaxAll min_; static const MinMaxAll max_; static const MinMaxAll all_; }; -} // namespace +} // namespace sta diff --git a/include/sta/MinMaxValues.hh b/include/sta/MinMaxValues.hh index 7814c256c..6b2fcc9b8 100644 --- a/include/sta/MinMaxValues.hh +++ b/include/sta/MinMaxValues.hh @@ -200,4 +200,4 @@ private: using MinMaxFloatValues = MinMaxValues; using MinMaxIntValues = MinMaxValues; -} // namespace +} // namespace sta diff --git a/include/sta/Mode.hh b/include/sta/Mode.hh index 112226191..b3a2b9c04 100644 --- a/include/sta/Mode.hh +++ b/include/sta/Mode.hh @@ -39,21 +39,22 @@ class PathGroups; using PathGroupSeq = std::vector; // Sdc and dependent state. -class Mode : public StaState +class Mode { public: Mode(std::string_view name, size_t mode_index, StaState *sta); - virtual ~Mode(); - virtual void copyState(const StaState *sta); + ~Mode(); + void copyState(const StaState *sta); void clear(); const std::string &name() const { return name_; } size_t modeIndex() const { return mode_index_; } const SceneSeq &scenes() const { return scenes_; } - const SceneSet sceneSet() const; + SceneSet sceneSet() const; void addScene(Scene *scene); void removeScene(Scene *scene); + StaState *sta() const { return sta_; } Sdc *sdc() { return sdc_; } Sdc *sdc() const { return sdc_; } Sim *sim() { return sim_; } @@ -69,8 +70,8 @@ public: int endpoint_path_count, bool unique_pins, bool unique_edges, - float min_slack, - float max_slack, + float slack_min, + float slack_max, StringSeq &group_names, bool setup, bool hold, @@ -89,7 +90,8 @@ private: Sim *sim_; ClkNetwork *clk_network_; Genclks *genclks_; - PathGroups *path_groups_; + PathGroups *path_groups_{nullptr}; + StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Mutex.hh b/include/sta/Mutex.hh index a36685823..565e02722 100644 --- a/include/sta/Mutex.hh +++ b/include/sta/Mutex.hh @@ -29,6 +29,6 @@ namespace sta { // Hide a bit of the std verbosity. -using LockGuard = std::lock_guard; +using LockGuard = std::scoped_lock; -} // namespace +} // namespace sta diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 32dd25b3f..05eee68b0 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -94,7 +94,7 @@ class Network : public StaState { public: Network(); - virtual ~Network(); + ~Network() override; virtual void clear(); // Linking the hierarchy creates the instance/pin/net network hierarchy. @@ -501,7 +501,7 @@ class NetworkEdit : public Network { public: NetworkEdit(); - virtual bool isEditable() const { return true; } + bool isEditable() const override { return true; } virtual Instance *makeInstance(LibertyCell *cell, std::string_view name, Instance *parent) = 0; @@ -533,7 +533,6 @@ public: class NetworkReader : public NetworkEdit { public: - NetworkReader() {} // Called before reading a netlist to delete any previously linked network. virtual void readNetlistBefore() = 0; virtual void setLinkFunc(LinkNetworkFunc link) = 0; @@ -599,8 +598,7 @@ linkReaderNetwork(Cell *top_cell, class ConstantPinIterator { public: - ConstantPinIterator() {} - virtual ~ConstantPinIterator() {} + virtual ~ConstantPinIterator() = default; virtual bool hasNext() = 0; virtual void next(const Pin *&pin, LogicValue &value) = 0; @@ -613,9 +611,8 @@ public: NetworkConstantPinIterator(const Network *network, NetSet &zero_nets, NetSet &one_nets); - virtual ~NetworkConstantPinIterator() {} - virtual bool hasNext(); - virtual void next(const Pin *&pin, LogicValue &value); + bool hasNext() override; + void next(const Pin *&pin, LogicValue &value) override; private: void findConstantPins(NetSet &nets, @@ -631,8 +628,7 @@ private: class HierPinThruVisitor { public: - HierPinThruVisitor() {} - virtual ~HierPinThruVisitor() {} + virtual ~HierPinThruVisitor() = default; virtual void visit(const Pin *drvr, const Pin *load) = 0; }; @@ -640,7 +636,7 @@ public: class PinVisitor { public: - virtual ~PinVisitor() {} + virtual ~PinVisitor() = default; virtual void operator()(const Pin *pin) = 0; }; @@ -652,7 +648,7 @@ public: PinSeq &loads, PinSeq &drvrs, const Network *network); - virtual void operator()(const Pin *pin); + void operator()(const Pin *pin) override; protected: const Pin *drvr_pin_; @@ -675,4 +671,4 @@ visitDrvrLoadsThruNet(const Net *net, char logicValueString(LogicValue value); -} // namespace +} // namespace sta diff --git a/include/sta/NetworkClass.hh b/include/sta/NetworkClass.hh index 23f63a6a5..0e72ff241 100644 --- a/include/sta/NetworkClass.hh +++ b/include/sta/NetworkClass.hh @@ -173,4 +173,4 @@ public: NetSet(const Network *network); }; -} // namespace +} // namespace sta diff --git a/include/sta/NetworkCmp.hh b/include/sta/NetworkCmp.hh index ac5ffc5b9..019eb9b72 100644 --- a/include/sta/NetworkCmp.hh +++ b/include/sta/NetworkCmp.hh @@ -91,4 +91,4 @@ NetSeq sortByPathName(NetSet *set, const Network *network); -} // namespace +} // namespace sta diff --git a/include/sta/ObjectId.hh b/include/sta/ObjectId.hh index cd4558586..905f7b1b1 100644 --- a/include/sta/ObjectId.hh +++ b/include/sta/ObjectId.hh @@ -40,4 +40,4 @@ static constexpr BlockIdx block_idx_null = 0; static constexpr ObjectId object_id_null = 0; static constexpr ObjectIdx object_idx_null = 0; -} // Namespace +} // namespace sta diff --git a/include/sta/ObjectTable.hh b/include/sta/ObjectTable.hh index 4736b5e0f..b1568b936 100644 --- a/include/sta/ObjectTable.hh +++ b/include/sta/ObjectTable.hh @@ -50,7 +50,6 @@ template class ObjectTable { public: - ObjectTable(); ~ObjectTable(); TYPE *make(); void destroy(TYPE *object); @@ -70,20 +69,13 @@ private: void freePush(TYPE *object, ObjectId id); - size_t size_; + size_t size_{0}; // Object ID of next free object. - ObjectId free_; + ObjectId free_{object_id_null}; std::vector*> blocks_; static constexpr ObjectId idx_mask_ = block_object_count - 1; }; -template -ObjectTable::ObjectTable() : - size_(0), - free_(object_id_null) -{ -} - template ObjectTable::~ObjectTable() { @@ -213,4 +205,4 @@ TableBlock::TableBlock(BlockIdx block_idx, { } -} // Namespace +} // namespace sta diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index 8c010f07b..7b4bfccfc 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -52,7 +52,6 @@ class Parasitics : public StaState { public: Parasitics(StaState *sta); - virtual ~Parasitics() {} virtual const std::string &name() const = 0; virtual const std::string &filename() const = 0; virtual bool haveParasitics() = 0; @@ -304,4 +303,4 @@ private: const Network *network_; }; -} // namespace +} // namespace sta diff --git a/include/sta/ParasiticsClass.hh b/include/sta/ParasiticsClass.hh index d75ff05f3..590156b10 100644 --- a/include/sta/ParasiticsClass.hh +++ b/include/sta/ParasiticsClass.hh @@ -32,4 +32,4 @@ class ParasiticNode; class ParasiticResistor; class ParasiticCapacitor; -} // namespace +} // namespace sta diff --git a/include/sta/ParseBus.hh b/include/sta/ParseBus.hh index 9b40063a4..e6236fa8f 100644 --- a/include/sta/ParseBus.hh +++ b/include/sta/ParseBus.hh @@ -32,8 +32,8 @@ namespace sta { // Return true if name is a bus. bool isBusName(std::string_view name, - const char brkt_left, - const char brkt_right, + char brkt_left, + char brkt_right, char escape); // Parse name as a bus. @@ -45,8 +45,8 @@ isBusName(std::string_view name, // Caller must delete returned bus_name string. void parseBusName(std::string_view name, - const char brkt_left, - const char brkt_right, + char brkt_left, + char brkt_right, char escape, // Return values. bool &is_bus, @@ -68,8 +68,8 @@ parseBusName(std::string_view name, // Caller must delete returned bus_name string. void parseBusName(std::string_view name, - const char brkt_left, - const char brkt_right, + char brkt_left, + char brkt_right, char escape, // Return values. bool &is_bus, @@ -85,7 +85,7 @@ void parseBusName(std::string_view name, std::string_view brkts_left, std::string_view brkts_right, - const char escape, + char escape, // Return values. bool &is_bus, bool &is_range, @@ -97,8 +97,8 @@ parseBusName(std::string_view name, // Insert escapes before ch1 and ch2 in token. std::string escapeChars(std::string_view token, - const char ch1, - const char ch2, - const char escape); + char ch1, + char ch2, + char escape); -} // namespace +} // namespace sta diff --git a/include/sta/Path.hh b/include/sta/Path.hh index 7095a2695..81b37957e 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; + Slew slew(const StaState *sta) const; // This takes the same time as prevPath and prevArc combined. Path *prevPath() const; void setPrevPath(Path *prev_path); @@ -201,7 +201,6 @@ public: const RiseFall *rf, const MinMax *min_max, const StaState *sta); - virtual ~VertexPathIterator(); bool hasNext() override; Path *next() override; @@ -219,4 +218,4 @@ private: Path *next_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index 1fca8d5ab..2e2529220 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -69,7 +69,7 @@ public: }; virtual PathEnd *copy() const = 0; - virtual ~PathEnd() {} + virtual ~PathEnd() = default; void deletePath(); Path *path() { return path_; } const Path *path() const { return path_; } @@ -147,9 +147,8 @@ public: virtual TimingArc *checkArc() const { return nullptr; } // PathEndDataCheck data clock path. virtual const Path *dataClkPath() const { return nullptr; } - virtual int setupDefaultCycles() const { return 1; } virtual Delay clkSkew(const StaState *sta); - virtual bool ignoreClkLatency(const StaState * /* sta */) const { return false; } + [[nodiscard]] virtual bool ignoreClkLatency(const StaState *) const { return false; } static bool less(const PathEnd *path_end1, const PathEnd *path_end2, @@ -219,53 +218,55 @@ protected: static bool ignoreClkLatency(const Path *path, PathDelay *path_delay, const StaState *sta); + virtual int setupDefaultCycles() const { return 1; } + Path *path_; - PathGroup *path_group_; + PathGroup *path_group_{nullptr}; }; class PathEndUnconstrained : public PathEnd { public: PathEndUnconstrained(Path *path); - virtual Type type() const; - virtual const char *typeName() const; - virtual PathEnd *copy() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isUnconstrained() const; - virtual Required requiredTime(const StaState *sta) const; - virtual Required requiredTimeOffset(const StaState *sta) const; - virtual ArcDelay margin(const StaState *sta) const; - virtual Slack slack(const StaState *sta) const; - virtual Slack slackNoCrpr(const StaState *sta) const; - virtual float sourceClkOffset(const StaState *sta) const; + Type type() const override; + const char *typeName() const override; + PathEnd *copy() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isUnconstrained() const override; + Required requiredTime(const StaState *sta) const override; + Required requiredTimeOffset(const StaState *sta) const override; + ArcDelay margin(const StaState *sta) const override; + Slack slack(const StaState *sta) const override; + Slack slackNoCrpr(const StaState *sta) const override; + float sourceClkOffset(const StaState *sta) const override; }; class PathEndClkConstrained : public PathEnd { public: - virtual float sourceClkOffset(const StaState *sta) const; - virtual Delay sourceClkLatency(const StaState *sta) const; - virtual Delay sourceClkInsertionDelay(const StaState *sta) const; - virtual const Clock *targetClk(const StaState *sta) const; - virtual const ClockEdge *targetClkEdge(const StaState *sta) const; - virtual Path *targetClkPath(); - virtual const Path *targetClkPath() const; - virtual float targetClkTime(const StaState *sta) const; - virtual float targetClkOffset(const StaState *sta) const; - virtual Arrival targetClkArrival(const StaState *sta) const; - virtual Delay targetClkDelay(const StaState *sta) const; - virtual Delay targetClkInsertionDelay(const StaState *sta) const; - virtual float targetNonInterClkUncertainty(const StaState *sta) const; - virtual float interClkUncertainty(const StaState *sta) const; - virtual float targetClkUncertainty(const StaState *sta) const; - virtual Crpr crpr(const StaState *sta) const; - virtual Required requiredTime(const StaState *sta) const; - virtual Slack slack(const StaState *sta) const; - virtual Slack slackNoCrpr(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; - virtual void setPath(Path *path); + float sourceClkOffset(const StaState *sta) const override; + Delay sourceClkLatency(const StaState *sta) const override; + Delay sourceClkInsertionDelay(const StaState *sta) const override; + const Clock *targetClk(const StaState *sta) const override; + const ClockEdge *targetClkEdge(const StaState *sta) const override; + Path *targetClkPath() override; + const Path *targetClkPath() const override; + float targetClkTime(const StaState *sta) const override; + float targetClkOffset(const StaState *sta) const override; + Arrival targetClkArrival(const StaState *sta) const override; + Delay targetClkDelay(const StaState *sta) const override; + Delay targetClkInsertionDelay(const StaState *sta) const override; + float targetNonInterClkUncertainty(const StaState *sta) const override; + float interClkUncertainty(const StaState *sta) const override; + float targetClkUncertainty(const StaState *sta) const override; + Crpr crpr(const StaState *sta) const override; + Required requiredTime(const StaState *sta) const override; + Slack slack(const StaState *sta) const override; + Slack slackNoCrpr(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; + void setPath(Path *path) override; protected: PathEndClkConstrained(Path *path, @@ -280,16 +281,16 @@ protected: Path *clk_path_; mutable Crpr crpr_; - mutable bool crpr_valid_; + mutable bool crpr_valid_{false}; }; class PathEndClkConstrainedMcp : public PathEndClkConstrained { public: - virtual MultiCyclePath *multiCyclePath() const { return mcp_; } - virtual float targetClkMcpAdjustment(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + MultiCyclePath *multiCyclePath() const override { return mcp_; } + float targetClkMcpAdjustment(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; protected: PathEndClkConstrainedMcp(Path *path, @@ -316,23 +317,23 @@ public: Path *clk_path, MultiCyclePath *mcp, const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isCheck() const { return true; } - virtual ArcDelay margin(const StaState *sta) const; - virtual float macroClkTreeDelay(const StaState *sta) const; - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual TimingArc *checkArc() const { return check_arc_; } - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; - virtual Delay clkSkew(const StaState *sta); + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isCheck() const override { return true; } + ArcDelay margin(const StaState *sta) const override; + float macroClkTreeDelay(const StaState *sta) const override; + const TimingRole *checkRole(const StaState *sta) const override; + TimingArc *checkArc() const override { return check_arc_; } + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; + Delay clkSkew(const StaState *sta) override; protected: Delay sourceClkDelay(const StaState *sta) const; - virtual Required requiredTimeNoCrpr(const StaState *sta) const; + Required requiredTimeNoCrpr(const StaState *sta) const override; TimingArc *check_arc_; Edge *check_edge_; @@ -349,25 +350,25 @@ public: MultiCyclePath *mcp, PathDelay *path_delay, const StaState *sta); - virtual Type type() const; - virtual const char *typeName() const; - virtual float sourceClkOffset(const StaState *sta) const; - virtual bool isCheck() const { return false; } - virtual bool isLatchCheck() const { return true; } - virtual PathDelay *pathDelay() const { return path_delay_; } - virtual PathEnd *copy() const; + Type type() const override; + const char *typeName() const override; + float sourceClkOffset(const StaState *sta) const override; + bool isCheck() const override { return false; } + bool isLatchCheck() const override { return true; } + PathDelay *pathDelay() const override { return path_delay_; } + PathEnd *copy() const override; Path *latchDisable(); const Path *latchDisable() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual Required requiredTime(const StaState *sta) const; - virtual Arrival borrow(const StaState *sta) const; - virtual float targetClkTime(const StaState *sta) const; - virtual float targetClkOffset(const StaState *sta) const; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + const TimingRole *checkRole(const StaState *sta) const override; + Required requiredTime(const StaState *sta) const override; + Arrival borrow(const StaState *sta) const override; + float targetClkTime(const StaState *sta) const override; + float targetClkOffset(const StaState *sta) const override; Arrival targetClkWidth(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; void latchRequired(const StaState *sta, // Return values. Required &required, @@ -383,8 +384,8 @@ public: Crpr &open_crpr, Crpr &crpr_diff, Delay &max_borrow, - bool &borrow_limit_exists) const; - virtual bool ignoreClkLatency(const StaState *sta) const; + bool &borrow_limit_exists) const; + bool ignoreClkLatency(const StaState *sta) const override; protected: Path *disable_path_; @@ -404,23 +405,23 @@ public: Path *clk_path, MultiCyclePath *mcp, const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isOutputDelay() const { return true; } - virtual ArcDelay margin(const StaState *sta) const; - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual const ClockEdge *targetClkEdge(const StaState *sta) const; - virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const; - virtual Delay targetClkDelay(const StaState *sta) const; - virtual Delay targetClkInsertionDelay(const StaState *sta) const; - virtual Crpr crpr(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isOutputDelay() const override { return true; } + ArcDelay margin(const StaState *sta) const override; + const TimingRole *checkRole(const StaState *sta) const override; + const ClockEdge *targetClkEdge(const StaState *sta) const override; + Delay targetClkDelay(const StaState *sta) const override; + Delay targetClkInsertionDelay(const StaState *sta) const override; + Crpr crpr(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; protected: + Arrival targetClkArrivalNoCrpr(const StaState *sta) const override; Arrival tgtClkDelay(const ClockEdge *tgt_clk_edge, const TimingRole *check_role, const StaState *sta) const; @@ -444,16 +445,16 @@ public: MultiCyclePath *mcp, ArcDelay margin, const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isGatedClock() const { return true; } - virtual ArcDelay margin(const StaState *) const { return margin_; } - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isGatedClock() const override { return true; } + ArcDelay margin(const StaState *) const override { return margin_; } + const TimingRole *checkRole(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; protected: const TimingRole *check_role_; @@ -468,25 +469,25 @@ public: Path *data_clk_path, MultiCyclePath *mcp, const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isDataCheck() const { return true; } - virtual const ClockEdge *targetClkEdge(const StaState *sta) const; - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual ArcDelay margin(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; - virtual const Path *dataClkPath() const { return data_clk_path_; } + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isDataCheck() const override { return true; } + const ClockEdge *targetClkEdge(const StaState *sta) const override; + const TimingRole *checkRole(const StaState *sta) const override; + ArcDelay margin(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; + const Path *dataClkPath() const override { return data_clk_path_; } protected: Path *clkPath(Path *path, const StaState *sta); - Arrival requiredTimeNoCrpr(const StaState *sta) const; + Arrival requiredTimeNoCrpr(const StaState *sta) const override; // setup uses zero cycle default - virtual int setupDefaultCycles() const { return 0; } + int setupDefaultCycles() const override { return 0; } Path *data_clk_path_; DataCheck *check_; @@ -514,29 +515,29 @@ public: Path *path, OutputDelay *output_delay, const StaState *sta); - virtual PathEnd *copy() const; - virtual Type type() const; - virtual const char *typeName() const; - virtual void reportShort(const ReportPath *report) const; - virtual void reportFull(const ReportPath *report) const; - virtual bool isPathDelay() const { return true; } - virtual const TimingRole *checkRole(const StaState *sta) const; - virtual bool pathDelayMarginIsExternal() const; - virtual PathDelay *pathDelay() const { return path_delay_; } - virtual ArcDelay margin(const StaState *sta) const; - virtual float sourceClkOffset(const StaState *sta) const; - virtual const ClockEdge *targetClkEdge(const StaState *sta) const; - virtual float targetClkTime(const StaState *sta) const; - virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const; - virtual float targetClkOffset(const StaState *sta) const; - virtual TimingArc *checkArc() const { return check_arc_; } - virtual Required requiredTime(const StaState *sta) const; - virtual int exceptPathCmp(const PathEnd *path_end, - const StaState *sta) const; + PathEnd *copy() const override; + Type type() const override; + const char *typeName() const override; + void reportShort(const ReportPath *report) const override; + void reportFull(const ReportPath *report) const override; + bool isPathDelay() const override { return true; } + const TimingRole *checkRole(const StaState *sta) const override; + bool pathDelayMarginIsExternal() const override; + PathDelay *pathDelay() const override { return path_delay_; } + ArcDelay margin(const StaState *sta) const override; + float sourceClkOffset(const StaState *sta) const override; + const ClockEdge *targetClkEdge(const StaState *sta) const override; + float targetClkTime(const StaState *sta) const override; + float targetClkOffset(const StaState *sta) const override; + TimingArc *checkArc() const override { return check_arc_; } + Required requiredTime(const StaState *sta) const override; + int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const override; [[nodiscard]] bool hasOutputDelay() const { return output_delay_ != nullptr; } - virtual bool ignoreClkLatency(const StaState *sta) const; + bool ignoreClkLatency(const StaState *sta) const override; protected: + Arrival targetClkArrivalNoCrpr(const StaState *sta) const override; void findSrcClkArrival(const StaState *sta); PathDelay *path_delay_; @@ -590,4 +591,4 @@ protected: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PathExpanded.hh b/include/sta/PathExpanded.hh index ebb9d080e..b6c8ba2b7 100644 --- a/include/sta/PathExpanded.hh +++ b/include/sta/PathExpanded.hh @@ -78,4 +78,4 @@ protected: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index c7c6c5456..2d743fee6 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -52,20 +52,20 @@ class PathGroup public: // Path group that compares compare slacks. static PathGroup *makePathGroupArrival(std::string_view name, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, const MinMax *min_max, const StaState *sta); // Path group that compares arrival time, sorted by min_max. static PathGroup *makePathGroupSlack(std::string_view name, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, - float min_slack, - float max_slack, + float slack_min, + float slack_max, const StaState *sta); ~PathGroup(); const std::string &name() const { return name_; } @@ -77,19 +77,19 @@ public: // Predicate to determine if a PathEnd is worth saving. bool saveable(PathEnd *path_end); bool enumMinSlackUnderMin(PathEnd *path_end); - int maxPaths() const { return group_path_count_; } + size_t maxPaths() const { return group_path_count_; } // This does NOT delete the path ends. void clear(); - static int group_path_count_max; + static size_t group_path_count_max; protected: PathGroup(std::string_view name, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, - float min_slack, - float max_slack, + float slack_min, + float slack_max, bool cmp_slack, const MinMax *min_max, const StaState *sta); @@ -98,8 +98,8 @@ protected: void sort(); std::string name_; - int group_path_count_; - int endpoint_path_count_; + size_t group_path_count_; + size_t endpoint_path_count_; bool unique_pins_; bool unique_edges_; float slack_min_; @@ -116,8 +116,8 @@ protected: class PathGroups : public StaState { public: - PathGroups(int group_path_count, - int endpoint_path_count, + PathGroups(size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, @@ -131,7 +131,7 @@ public: bool clk_gating_hold, bool unconstrained, const Mode *mode); - ~PathGroups(); + ~PathGroups() override; // Use scene nullptr to make PathEnds for all scenes. // The PathEnds in the vector are owned by the PathGroups. void makePathEnds(ExceptionTo *to, @@ -155,8 +155,8 @@ public: protected: void makeGroupPathEnds(ExceptionTo *to, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, const SceneSeq &scenes, @@ -170,8 +170,8 @@ protected: const MinMaxAll *min_max, PathEndVisitor *visitor); void enumPathEnds(PathGroup *group, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, bool cmp_slack); @@ -180,8 +180,8 @@ protected: void pushUnconstrainedPathEnds(PathEndSeq &path_ends, const MinMaxAll *min_max); - void makeGroups(int group_path_count, - int endpoint_path_count, + void makeGroups(size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, @@ -199,8 +199,8 @@ protected: StringSeq pathGroupNames(); const Mode *mode_; - int group_path_count_; - int endpoint_path_count_; + size_t group_path_count_; + size_t endpoint_path_count_; bool unique_pins_; bool unique_edges_; float slack_min_; @@ -226,4 +226,4 @@ protected: static constexpr std::string_view unconstrained_group_name_ = "unconstrained"; }; -} // namespace +} // namespace sta diff --git a/include/sta/PatternMatch.hh b/include/sta/PatternMatch.hh index dc41363f8..0fbed7e97 100644 --- a/include/sta/PatternMatch.hh +++ b/include/sta/PatternMatch.hh @@ -77,8 +77,8 @@ class RegexpCompileError : public Exception { public: RegexpCompileError(std::string_view pattern); - virtual ~RegexpCompileError() noexcept {} - virtual const char *what() const noexcept; + ~RegexpCompileError() noexcept override = default; + const char *what() const noexcept override; private: std::string error_; @@ -98,4 +98,4 @@ patternMatchNoCase(std::string_view pattern, bool patternWildcards(std::string_view pattern); -} // namespace +} // namespace sta diff --git a/include/sta/PinPair.hh b/include/sta/PinPair.hh index 073212efa..69e759fec 100644 --- a/include/sta/PinPair.hh +++ b/include/sta/PinPair.hh @@ -68,4 +68,4 @@ public: const PinPair &pair2) const; }; -} // namespace +} // namespace sta diff --git a/include/sta/PocvMode.hh b/include/sta/PocvMode.hh index d75785ec5..d740234db 100644 --- a/include/sta/PocvMode.hh +++ b/include/sta/PocvMode.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -35,4 +35,4 @@ pocvModeName(PocvMode mode); PocvMode findPocvMode(std::string_view mode_name); -} // namespace +} // namespace sta diff --git a/include/sta/PortDelay.hh b/include/sta/PortDelay.hh index 78cb246d5..3657cd4c1 100644 --- a/include/sta/PortDelay.hh +++ b/include/sta/PortDelay.hh @@ -108,4 +108,4 @@ private: const Network *network_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PortDirection.hh b/include/sta/PortDirection.hh index 1d54271cc..72d4f074b 100644 --- a/include/sta/PortDirection.hh +++ b/include/sta/PortDirection.hh @@ -45,7 +45,7 @@ public: static PortDirection *unknown() { return unknown_; } static PortDirection *find(const char *dir_name); std::string_view name() const { return name_; } - int index() const { return index_; } + size_t index() const { return index_; } bool isInput() const { return this == input_; } // Input or bidirect. bool isAnyInput() const; @@ -66,10 +66,10 @@ public: private: PortDirection(const char *name, - int index); + size_t index); const char *name_; - int index_; + size_t index_; static PortDirection *input_; static PortDirection *output_; @@ -82,4 +82,4 @@ private: static PortDirection *unknown_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PortExtCap.hh b/include/sta/PortExtCap.hh index 3e186b691..d1c0d6943 100644 --- a/include/sta/PortExtCap.hh +++ b/include/sta/PortExtCap.hh @@ -76,4 +76,4 @@ private: FanoutValues fanout_; }; -} // namespace +} // namespace sta diff --git a/include/sta/PowerClass.hh b/include/sta/PowerClass.hh index b3f2a6dfd..a34256317 100644 --- a/include/sta/PowerClass.hh +++ b/include/sta/PowerClass.hh @@ -47,7 +47,7 @@ enum class PwrActivityOrigin class PwrActivity { public: - PwrActivity(); + PwrActivity() = default; PwrActivity(float density, float duty, PwrActivityOrigin origin); @@ -67,9 +67,9 @@ public: private: void check(); - float density_; // transitions / second - float duty_; // probability signal is high - PwrActivityOrigin origin_; + float density_{0.0}; // transitions / second + float duty_{0.0}; // probability signal is high + PwrActivityOrigin origin_{PwrActivityOrigin::unknown}; static constexpr float min_density = 1E-10; }; @@ -77,7 +77,7 @@ private: class PowerResult { public: - PowerResult(); + PowerResult() = default; void clear(); float internal() const { return internal_; } float switching() const { return switching_; } @@ -89,12 +89,12 @@ public: void incrLeakage(float pwr); private: - float internal_; - float switching_; - float leakage_; + float internal_{0.0}; + float switching_{0.0}; + float leakage_{0.0}; }; using InstPower = std::pair; using InstPowers = std::vector; -} // namespace +} // namespace sta diff --git a/include/sta/Property.hh b/include/sta/Property.hh index fd7344373..afb717657 100644 --- a/include/sta/Property.hh +++ b/include/sta/Property.hh @@ -47,7 +47,7 @@ template class PropertyRegistry { public: - typedef std::function PropertyHandler; + using PropertyHandler = std::function; void defineProperty(std::string_view property, PropertyHandler handler); PropertyValue getProperty(TYPE object, @@ -63,7 +63,6 @@ class Properties { public: Properties(Sta *sta); - virtual ~Properties() {} PropertyValue getProperty(const Library *lib, std::string_view property); @@ -100,25 +99,25 @@ public: // return PropertyValue("bar"); // }); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler); + PropertyRegistry::PropertyHandler &handler); protected: PropertyValue portSlew(const Port *port, @@ -201,9 +200,9 @@ public: PropertyValue(ConstPathSeq *value); PropertyValue(PwrActivity *value); // Copy constructor. - PropertyValue(const PropertyValue &props); + PropertyValue(const PropertyValue &value); // Move constructor. - PropertyValue(PropertyValue &&props) noexcept; + PropertyValue(PropertyValue &&value) noexcept; ~PropertyValue(); Type type() const { return type_; } const Unit *unit() const { return unit_; } @@ -259,4 +258,4 @@ private: const Unit *unit_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Report.hh b/include/sta/Report.hh index e8c39de3e..068a206ae 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -51,12 +51,11 @@ class Report { public: Report(); - virtual ~Report(); - + virtual ~Report() = default; virtual void reportLine(const std::string &line); virtual void reportBlankLine(); - // Print formatted line using std::format (C++20). + // Print formatted line using std::format. template void report(std::string_view fmt, Args &&...args) @@ -210,28 +209,13 @@ 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, - va_list args); - void printToBufferAppend(const char *fmt, - ...); - void printToBufferAppend(const char *fmt, - va_list args); - void printBufferLine(); void redirectStringPrint(const char *buffer, size_t length); - FILE *log_stream_; - FILE *redirect_stream_; - bool redirect_to_string_; + FILE *log_stream_{nullptr}; + FILE *redirect_stream_{nullptr}; + bool redirect_to_string_{false}; std::string redirect_string_; - // Buffer to support printf style arguments. - size_t buffer_size_; - char *buffer_; - // Length of string in buffer. - size_t buffer_length_; - std::mutex buffer_lock_; static Report *default_; std::set suppressed_msg_ids_; diff --git a/include/sta/ReportStd.hh b/include/sta/ReportStd.hh index c1743de82..b92821e84 100644 --- a/include/sta/ReportStd.hh +++ b/include/sta/ReportStd.hh @@ -33,4 +33,4 @@ class Report; Report * makeReportStd(); -} // namespace +} // namespace sta diff --git a/include/sta/ReportTcl.hh b/include/sta/ReportTcl.hh index 5f4a6cd0e..9f48364c7 100644 --- a/include/sta/ReportTcl.hh +++ b/include/sta/ReportTcl.hh @@ -43,7 +43,7 @@ class ReportTcl : public Report { public: ReportTcl(); - virtual ~ReportTcl(); + ~ReportTcl() override; void logBegin(std::string_view filename) override; void logEnd() override; void redirectFileBegin(std::string_view filename) override; @@ -68,13 +68,13 @@ private: const char *buffer, size_t length); - Tcl_Interp *interp_; + Tcl_Interp *interp_{nullptr}; // The original tcl channels. - Tcl_Channel tcl_stdout_; - Tcl_Channel tcl_stderr_; + Tcl_Channel tcl_stdout_{nullptr}; + Tcl_Channel tcl_stderr_{nullptr}; // Encapsulated channels that print on this object. - Tcl_Channel tcl_encap_stdout_; - Tcl_Channel tcl_encap_stderr_; + Tcl_Channel tcl_encap_stdout_{nullptr}; + Tcl_Channel tcl_encap_stderr_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/include/sta/RiseFallMinMax.hh b/include/sta/RiseFallMinMax.hh index 3f9bca811..b3a8abcf8 100644 --- a/include/sta/RiseFallMinMax.hh +++ b/include/sta/RiseFallMinMax.hh @@ -85,4 +85,4 @@ private: bool exists_[RiseFall::index_count][MinMax::index_count]; }; -} // namespace +} // namespace sta diff --git a/include/sta/RiseFallMinMaxDelay.hh b/include/sta/RiseFallMinMaxDelay.hh index 22b999c91..0fd3975a4 100644 --- a/include/sta/RiseFallMinMaxDelay.hh +++ b/include/sta/RiseFallMinMaxDelay.hh @@ -49,4 +49,4 @@ private: bool exists_[RiseFall::index_count][MinMax::index_count]; }; -} // namespace +} // namespace sta diff --git a/include/sta/RiseFallValues.hh b/include/sta/RiseFallValues.hh index e1198afec..5a49a25e3 100644 --- a/include/sta/RiseFallValues.hh +++ b/include/sta/RiseFallValues.hh @@ -49,4 +49,4 @@ private: bool exists_[RiseFall::index_count]; }; -} // namespace +} // namespace sta diff --git a/include/sta/Scene.hh b/include/sta/Scene.hh index 83e576684..a43a4cb4f 100644 --- a/include/sta/Scene.hh +++ b/include/sta/Scene.hh @@ -76,7 +76,7 @@ public: DcalcAPIndex checkClkSlewIndex(const MinMax *min_max) const; const LibertySeq &libertyLibraries(const MinMax *min_max) const; - int libertyIndex(const MinMax *min_max) const; + size_t libertyIndex(const MinMax *min_max) const; void addLiberty(LibertyLibrary *lib, const MinMax *min_max); @@ -95,4 +95,4 @@ protected: friend class Scenes; }; -} // namespace +} // namespace sta diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index eaf65e77b..cea05abff 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -123,13 +123,12 @@ private: class NetWireCaps : public MinMaxFloatValues { public: - NetWireCaps(); bool subtractPinCap(const MinMax *min_max); void setSubtractPinCap(bool subtrace_pin_cap, const MinMax *min_max); private: - bool subtract_pin_cap_[MinMax::index_count]; + bool subtract_pin_cap_[MinMax::index_count]{false, false}; }; using ClockNameMap = std::map>; @@ -202,7 +201,7 @@ class Sdc : public StaState public: Sdc(Mode *mode, StaState *sta); - ~Sdc(); + ~Sdc() override; Mode *mode() const { return mode_; } // Note that Search may reference a Filter exception removed by clear(). void clear(); @@ -317,13 +316,13 @@ public: bool &exists) const; void setSlewLimit(Clock *clk, const RiseFallBoth *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, float slew); bool haveClkSlewLimits() const; void slewLimit(const Clock *clk, const RiseFall *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, float &slew, bool &exists) const; @@ -500,12 +499,12 @@ public: Clock *to_clk, const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold); - ClockGroups *makeClockGroups(const std::string &name, + ClockGroups *makeClockGroups(std::string_view name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, - std::string comment); + std::string_view comment); void makeClockGroup(ClockGroups *clk_groups, ClockSet *clks); void removeClockGroups(const std::string &name); @@ -580,7 +579,7 @@ public: void setOutputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, - const RiseFall *clk_tr, + const RiseFall *clk_rf, const Pin *ref_pin, bool source_latency_included, bool network_latency_included, @@ -1113,7 +1112,7 @@ protected: ExceptionPathSet &matches); void expandExceptionExcluding(ExceptionPath *exception, ExceptionPath *excluding, - ExceptionPathSet &expanded_matches); + ExceptionPathSet &expansions); void recordException1(ExceptionPath *exception); void recordExceptionFirstPts(ExceptionPath *exception); void recordExceptionFirstFrom(ExceptionPath *exception); @@ -1288,7 +1287,7 @@ protected: InstancePvtMap instance_pvt_maps_[MinMax::index_count]; MinMaxFloatValues voltages_; NetVoltageMap net_voltage_map_; - DeratingFactorsGlobal *derating_factors_; + DeratingFactorsGlobal *derating_factors_{nullptr}; NetDeratingFactorsMap net_derating_factors_; InstDeratingFactorsMap inst_derating_factors_; CellDeratingFactorsMap cell_derating_factors_; @@ -1297,7 +1296,7 @@ protected: // which iterating over the name map can't provide. ClockSeq clocks_; // Clocks are assigned an index. - int clk_index_; + int clk_index_{0}; // Default clock used for unclocked input arrivals. Clock *default_arrival_clk_; ClockNameMap clock_name_map_; @@ -1320,7 +1319,7 @@ protected: // clks in the same set_clock_group set. ClockPairSet clk_group_same_; ClockSenseMap clk_sense_map_; - ClockGatingCheck *clk_gating_check_; + ClockGatingCheck *clk_gating_check_{nullptr}; ClockGatingCheckMap clk_gating_check_map_; InstanceClockGatingCheckMap inst_clk_gating_check_map_; PinClockGatingCheckMap pin_clk_gating_check_map_; @@ -1335,7 +1334,7 @@ protected: // Input delays on hierarchical pins are indexed by the load pins. InputDelaysPinMap input_delay_leaf_pin_map_; InputDelaysPinMap input_delay_internal_pin_map_; - int input_delay_index_; + int input_delay_index_{0}; OutputDelaySet output_delays_; OutputDelaysPinMap output_delay_pin_map_; @@ -1369,9 +1368,9 @@ protected: InstanceSet disabled_clk_gating_checks_inst_; PinSet disabled_clk_gating_checks_pin_; ExceptionPathSet exceptions_; - size_t exception_id_; // Unique ID for exceptions. + size_t exception_id_{0}; // Unique ID for exceptions. - bool have_thru_hpin_exceptions_; + bool have_thru_hpin_exceptions_{false}; // First pin/clock/instance/net/edge exception point to exception set map. PinExceptionsMap first_from_pin_exceptions_; ClockExceptionsMap first_from_clk_exceptions_; @@ -1404,7 +1403,7 @@ protected: // Filter exception to tag arrivals for // report_timing -from pin|inst -through. // -to is always nullptr. - FilterPath *filter_; + FilterPath *filter_{nullptr}; InputDriveMap input_drive_map_; // set_LogicValue::one/zero/dc @@ -1429,4 +1428,4 @@ private: friend class GroupPathIterator; }; -} // namespace +} // namespace sta diff --git a/include/sta/SdcClass.hh b/include/sta/SdcClass.hh index dc0563b53..a22fee1e6 100644 --- a/include/sta/SdcClass.hh +++ b/include/sta/SdcClass.hh @@ -133,4 +133,4 @@ constexpr int timing_derate_type_count = 3; enum class TimingDerateCellType { cell_delay, cell_check }; constexpr int timing_derate_cell_type_count = 2; -} // namespace +} // namespace sta diff --git a/include/sta/SdcCmdComment.hh b/include/sta/SdcCmdComment.hh index b1c70d0b0..65b6b6e66 100644 --- a/include/sta/SdcCmdComment.hh +++ b/include/sta/SdcCmdComment.hh @@ -32,20 +32,18 @@ namespace sta { class SdcCmdComment { public: - SdcCmdComment(); - SdcCmdComment(std::string comment); + SdcCmdComment() = default; SdcCmdComment(std::string_view comment); const std::string &comment() { return comment_; } const std::string &comment() const { return comment_; } - void setComment(std::string comment); void setComment(std::string_view comment); protected: // Destructor is protected to prevent deletion of a derived // class with a pointer to this base class. - ~SdcCmdComment(); + ~SdcCmdComment() = default; std::string comment_; }; -} // namespace +} // namespace sta diff --git a/include/sta/SdcNetwork.hh b/include/sta/SdcNetwork.hh index 84b0b3676..d23d18dab 100644 --- a/include/sta/SdcNetwork.hh +++ b/include/sta/SdcNetwork.hh @@ -290,4 +290,4 @@ std::string escapeBrackets(std::string_view name, const Network *network); -} // namespace +} // namespace sta diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 5e9c5ccba..ec1fbdbc9 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -76,8 +76,8 @@ class Search : public StaState { public: Search(StaState *sta); - virtual ~Search(); - virtual void copyState(const StaState *sta); + ~Search() override; + void copyState(const StaState *sta) override; // Reset to virgin state. void clear(); // When enabled, non-critical path arrivals are pruned to improve @@ -265,7 +265,7 @@ public: const Mode *mode, TagGroupBldr *tag_bldr); void setVertexArrivals(Vertex *vertex, - TagGroupBldr *group_bldr); + TagGroupBldr *tag_bldr); void tnsInvalid(Vertex *vertex); [[nodiscard]] bool arrivalsChanged(Vertex *vertex, TagGroupBldr *tag_bldr); @@ -314,7 +314,7 @@ public: Tag *findTag(Scene *scene, const RiseFall *rf, const MinMax *min_max, - const ClkInfo *tag_clk, + const ClkInfo *clk_info, bool is_clk, InputDelay *input_delay, bool is_segment_start, @@ -541,7 +541,7 @@ protected: void deletePaths(); // Delete with incremental tns/wns update. void deletePathsIncr(Vertex *vertex); - TagGroup *findTagGroup(TagGroupBldr *group_bldr); + TagGroup *findTagGroup(TagGroupBldr *tag_bldr); void deleteFilterTags(); void deleteFilterTagGroups(); void deleteFilterClkInfos(); @@ -582,9 +582,9 @@ protected: //////////////////////////////////////////////////////////////// // findPathEnds arg. - bool unconstrained_paths_; - bool crpr_path_pruning_enabled_; - bool crpr_approx_missing_requireds_; + bool unconstrained_paths_{false}; + bool crpr_path_pruning_enabled_{true}; + bool crpr_approx_missing_requireds_{true}; // Search predicates. SearchPred *search_thru_; @@ -592,9 +592,9 @@ protected: EvalPred *eval_pred_; // Some arrivals exist. - bool arrivals_exist_; + bool arrivals_exist_{false}; // Arrivals at start points have been initialized. - bool arrivals_seeded_; + bool arrivals_seeded_{false}; // Vertices with invalid arrival times to update and search from. VertexSet invalid_arrivals_; std::mutex invalid_arrivals_lock_; @@ -602,14 +602,14 @@ protected: ArrivalVisitor *arrival_visitor_; // Some requireds exist. - bool requireds_exist_; + bool requireds_exist_{false}; // Requireds have been seeded by searching arrivals to all endpoints. - bool requireds_seeded_; + bool requireds_seeded_{false}; // Vertices with invalid required times to update and search from. VertexSet invalid_requireds_; BfsBkwdIterator *required_iter_; - bool tns_exists_; + bool tns_exists_{false}; // Endpoint vertices with slacks that have changed since tns was found. VertexSet invalid_tns_; // Indexed by path_ap->index(). @@ -619,19 +619,19 @@ protected: std::mutex tns_lock_; // Indexed by path_ap->index(). - WorstSlacks *worst_slacks_; + WorstSlacks *worst_slacks_{nullptr}; // Use pointer to clk_info set so Tag.hh does not need to be included. ClkInfoSet *clk_info_set_; std::mutex clk_info_lock_; // Entries in tags_ may be missing where previous filter tags were deleted. - TagIndex tag_capacity_; + TagIndex tag_capacity_{128}; std::atomic tags_; // Use pointer to tag set so Tag.hh does not need to be included. TagSet *tag_set_; std::vector tags_prev_; - TagIndex tag_next_; + TagIndex tag_next_{0}; std::mutex tag_lock_; // Capacity of tag_groups_. @@ -639,7 +639,7 @@ protected: std::atomic tag_groups_; TagGroupSet *tag_group_set_; std::vector tag_groups_prev_; - TagGroupIndex tag_group_next_; + TagGroupIndex tag_group_next_{0}; // Holes in tag_groups_ left by deleting filter tag groups. std::vector tag_group_free_indices_; std::mutex tag_group_lock_; @@ -652,18 +652,18 @@ protected: std::mutex pending_clk_endpoints_lock_; VertexSet endpoints_; - bool endpoints_initialized_; + bool endpoints_initialized_{false}; VertexSet invalid_endpoints_; - bool have_filter_; - ExceptionFrom *filter_from_; - ExceptionThruSeq *filter_thrus_; - ExceptionTo *filter_to_; + bool have_filter_{false}; + ExceptionFrom *filter_from_{nullptr}; + ExceptionThruSeq *filter_thrus_{nullptr}; + ExceptionTo *filter_to_{nullptr}; VertexSet filtered_arrivals_; std::mutex filtered_arrivals_lock_; - bool found_downstream_clk_pins_; - bool postpone_latch_outputs_; + bool found_downstream_clk_pins_{false}; + bool postpone_latch_outputs_{false}; std::vector enum_paths_; VisitPathEnds *visit_path_ends_; @@ -691,12 +691,14 @@ public: using SearchPred::searchTo; protected: - bool search_thru_latches_; + bool search_thru_latches_{true}; }; // Class for visiting fanin/fanout paths of a vertex. // This used by forward/backward search to find arrival/required path times. -class PathVisitor : public VertexVisitor, public StaState +// NOLINTNEXTLINE(misc-multiple-inheritance) +class PathVisitor : public VertexVisitor, + public StaState { public: // Uses search->evalPred() for search predicate. @@ -704,9 +706,24 @@ public: PathVisitor(SearchPred *pred, bool make_tag_cache, const StaState *sta); - virtual ~PathVisitor(); + ~PathVisitor() override; virtual void visitFaninPaths(Vertex *to_vertex); virtual void visitFanoutPaths(Vertex *from_vertex); + // Return false to stop visiting. + virtual 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) = 0; protected: // Return false to stop visiting. @@ -738,21 +755,6 @@ protected: Vertex *to_vertex, const RiseFall *to_rf, const MinMax *min_max); - // Return false to stop visiting. - virtual 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) = 0; SearchPred *pred_; TagSet *tag_cache_; @@ -764,29 +766,29 @@ class ArrivalVisitor : public PathVisitor { public: ArrivalVisitor(const StaState *sta); - virtual ~ArrivalVisitor(); + ~ArrivalVisitor() override; // Initialize the visitor. void init(bool always_to_endpoints, bool clks_only, SearchPred *pred); - void copyState(const StaState *sta); - virtual void visit(Vertex *vertex); - virtual VertexVisitor *copy() const; + void copyState(const StaState *sta) override; + void visit(Vertex *vertex) override; + VertexVisitor *copy() const override; // Return false to stop visiting. - virtual 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); + 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; void setAlwaysToEndpoints(bool to_endpoints); TagGroupBldr *tagBldr() const { return tag_bldr_; } @@ -814,7 +816,6 @@ protected: class RequiredCmp { public: - RequiredCmp(); void requiredsInit(Vertex *vertex, const StaState *sta); void requiredSet(size_t path_index, @@ -827,8 +828,8 @@ public: Required required(size_t path_index); protected: - ArrivalSeq requireds_; - bool have_requireds_; + ArrivalSeq requireds_{10}; + bool have_requireds_{false}; }; // Visitor called during backward search to record a @@ -837,31 +838,31 @@ class RequiredVisitor : public PathVisitor { public: RequiredVisitor(const StaState *sta); - virtual ~RequiredVisitor(); - virtual VertexVisitor *copy() const; - virtual void visit(Vertex *vertex); + ~RequiredVisitor() override; + VertexVisitor *copy() const override; + void visit(Vertex *vertex) override; + // Return false to stop visiting. + 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; protected: RequiredVisitor(bool make_tag_cache, const StaState *sta); - // Return false to stop visiting. - virtual 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); RequiredCmp *required_cmp_; VisitPathEnds *visit_path_ends_; }; -} // namespace +} // namespace sta diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index bd5a639b1..47f29563b 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -129,4 +129,4 @@ static const int path_ap_index_bit_count = 8; // One path analysis point per scene min/max. static const int scene_count_max = (1 << path_ap_index_bit_count) / 2; -} // namespace +} // namespace sta diff --git a/include/sta/SearchPred.hh b/include/sta/SearchPred.hh index 0d688369a..ab6b8dd33 100644 --- a/include/sta/SearchPred.hh +++ b/include/sta/SearchPred.hh @@ -47,7 +47,7 @@ class SearchPred { public: SearchPred(const StaState *sta); - virtual ~SearchPred() {} + virtual ~SearchPred() = default; // Search is allowed from from_vertex. virtual bool searchFrom(const Vertex *from_vertex, const Mode *mode) const = 0; @@ -158,4 +158,4 @@ hasFanout(Vertex *vertex, const Graph *graph, const Mode *mode); -} // namespace +} // namespace sta diff --git a/include/sta/Sequential.hh b/include/sta/Sequential.hh index 89b3f5a9a..1b07075a6 100644 --- a/include/sta/Sequential.hh +++ b/include/sta/Sequential.hh @@ -141,4 +141,4 @@ private: StateInternalValues next_values_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 32d600931..9da0e8307 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -81,7 +81,7 @@ using SlowDrvrIterator = Iterator; using CheckError = StringSeq; using CheckErrorSeq = std::vector; enum class CmdNamespace { sta, sdc }; -using ParasiticsNameMap = std::map; +using ParasiticsNameMap = std::map>; // Path::slack/arrival/required function. using PathDelayFunc = std::function; using GraphLoopSeq = std::vector; @@ -103,7 +103,6 @@ deleteAllMemory(); class Sta : public StaState { public: - Sta(); // The Sta is a FACTORY for the components. // makeComponents calls the make{Component} virtual functions. // Ideally this would be called by the Sta constructor, but a @@ -114,7 +113,7 @@ public: // pointers to some components have changed. // This must be called after changing any of the StaState components. virtual void updateComponentsState(); - virtual ~Sta(); + ~Sta() override; // Singleton accessor used by tcl command interpreter. static Sta *sta(); @@ -232,7 +231,7 @@ public: Sdc *sdc); // Set net wire capacitance (set_load -wire net). void setNetWireCap(const Net *net, - bool subtract_pin_load, + bool subtract_pin_cap, const MinMaxAll *min_max, float cap, Sdc *sdc); @@ -312,7 +311,7 @@ public: Sdc *sdc); void setSlewLimit(Clock *clk, const RiseFallBoth *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, float slew, Sdc *sdc); @@ -385,7 +384,7 @@ public: const Mode *mode); void removePropagatedClock(Pin *pin, const Mode *mode); - void setClockSlew(Clock *clock, + void setClockSlew(Clock *clk, const RiseFallBoth *rf, const MinMaxAll *min_max, float slew, @@ -441,12 +440,12 @@ public: const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold, Sdc *sdc); - ClockGroups *makeClockGroups(const std::string &name, + ClockGroups *makeClockGroups(std::string_view name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, - std::string comment, + std::string_view comment, Sdc *sdc); void removeClockGroupsLogicallyExclusive(Sdc *sdc); void removeClockGroupsLogicallyExclusive(const std::string &name, @@ -700,7 +699,7 @@ public: InstanceSet findRegisterInstances(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode); PinSet findRegisterDataPins(ClockSet *clks, @@ -1151,9 +1150,9 @@ public: const SceneSeq &scenes, const MinMax *min_max); - const ArcDelay arcDelay(Edge *edge, - TimingArc *arc, - DcalcAPIndex ap_index); + ArcDelay arcDelay(Edge *edge, + TimingArc *arc, + DcalcAPIndex ap_index); // True if the timing arc has been back-annotated. bool arcDelayAnnotated(Edge *edge, TimingArc *arc, @@ -1187,8 +1186,8 @@ public: // Instances sorted by max driver pin slew. InstanceSeq slowDrivers(int count); - Parasitics *makeConcreteParasitics(std::string name, - std::string filename); + Parasitics *makeConcreteParasitics(std::string_view name, + std::string_view filename); // Annotate hierarchical "instance" with parasitics. // The parasitic analysis point is ap_name. // The parasitic memory footprint is much smaller if parasitic @@ -1518,23 +1517,23 @@ protected: bool report_variance, int digits, bool find_required, - PathDelayFunc get_path_delay); + const PathDelayFunc &get_path_delay); void reportDelaysWrtClks(Vertex *vertex, const Scene *scene, bool report_variance, int digits, bool find_required, - PathDelayFunc get_path_delay); + const PathDelayFunc &get_path_delay); void reportDelaysWrtClks(Vertex *vertex, const ClockEdge *clk_edge, const Scene *scene, bool report_variance, int digits, - PathDelayFunc get_path_delay); + const PathDelayFunc &get_path_delay); RiseFallMinMaxDelay findDelaysWrtClks(Vertex *vertex, const ClockEdge *clk_edge, const Scene *scene, - PathDelayFunc get_path_delay); + const PathDelayFunc &get_path_delay); std::string formatDelay(const RiseFall *rf, const MinMax *min_max, const RiseFallMinMaxDelay &delays, @@ -1608,30 +1607,30 @@ protected: Parasitics *parasitics); void deleteScenes(); - Scene *cmd_scene_; - CmdNamespace cmd_namespace_; - Instance *current_instance_; + Scene *cmd_scene_{nullptr}; + CmdNamespace cmd_namespace_{CmdNamespace::sdc}; + Instance *current_instance_{nullptr}; SceneNameMap scene_name_map_; ModeNameMap mode_name_map_; ParasiticsNameMap parasitics_name_map_; - VerilogReader *verilog_reader_; - CheckTiming *check_timing_; - CheckSlews *check_slews_; - CheckFanouts *check_fanouts_; - CheckCapacitances *check_capacitances_; - CheckMinPulseWidths *check_min_pulse_widths_; - CheckMinPeriods *check_min_periods_; - CheckMaxSkews *check_max_skews_; - ClkSkews *clk_skews_; - ReportPath *report_path_; - Power *power_; - Tcl_Interp *tcl_interp_; - bool update_genclks_; - EquivCells *equiv_cells_; - Properties properties_; + VerilogReader *verilog_reader_{nullptr}; + CheckTiming *check_timing_{nullptr}; + CheckSlews *check_slews_{nullptr}; + CheckFanouts *check_fanouts_{nullptr}; + CheckCapacitances *check_capacitances_{nullptr}; + CheckMinPulseWidths *check_min_pulse_widths_{nullptr}; + CheckMinPeriods *check_min_periods_{nullptr}; + CheckMaxSkews *check_max_skews_{nullptr}; + ClkSkews *clk_skews_{nullptr}; + ReportPath *report_path_{nullptr}; + Power *power_{nullptr}; + Tcl_Interp *tcl_interp_{nullptr}; + bool update_genclks_{false}; + EquivCells *equiv_cells_{nullptr}; + Properties properties_{this}; // Singleton sta used by tcl command interpreter. - static Sta *sta_; + inline static Sta *sta_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/include/sta/StaMain.hh b/include/sta/StaMain.hh index 6d818c41c..e588ab265 100644 --- a/include/sta/StaMain.hh +++ b/include/sta/StaMain.hh @@ -75,4 +75,4 @@ sourceTclFile(const char *filename, bool verbose, Tcl_Interp *interp); -} // namespace +} // namespace sta diff --git a/include/sta/StaState.hh b/include/sta/StaState.hh index 8f96e137c..c0da5998e 100644 --- a/include/sta/StaState.hh +++ b/include/sta/StaState.hh @@ -65,7 +65,7 @@ public: // Copy the state from sta. This is virtual so that a component // can notify sub-components. virtual void copyState(const StaState *sta); - virtual ~StaState() {} + virtual ~StaState() = default; Report *report() { return report_; } Report *report() const { return report_; } void setReport(Report *report); @@ -113,7 +113,7 @@ public: size_t scenePathCount() const; DcalcAPIndex dcalcAnalysisPtCount() const; - const SceneSet scenesSet(); + SceneSet scenesSet(); ModeSeq &modes() { return modes_; } const ModeSeq &modes() const { return modes_; } @@ -141,4 +141,4 @@ protected: DispatchQueue *dispatch_queue_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Stats.hh b/include/sta/Stats.hh index 6968c8fe7..79cd3ef41 100644 --- a/include/sta/Stats.hh +++ b/include/sta/Stats.hh @@ -40,12 +40,12 @@ public: void report(const char *step); private: - double elapsed_begin_; - double user_begin_; - double system_begin_; - size_t memory_begin_; + double elapsed_begin_{0.0}; + double user_begin_{0.0}; + double system_begin_{0.0}; + size_t memory_begin_{0}; Debug *debug_; Report *report_; }; -} // namespace +} // namespace sta diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index f9161a603..7d5fdbf04 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -25,11 +25,11 @@ #pragma once #include +#include #include #include #include #include -#include // for strncasecmp #include #include @@ -64,6 +64,13 @@ stringBeginEq(const char *str1, return strncmp(str1, str2, strlen(str2)) == 0; } +inline bool +charEqual(unsigned char c1, + unsigned char c2) +{ + return std::tolower(c1) == std::tolower(c2); +} + // Case insensitive compare the beginning of str1 to str2. inline bool stringBeginEqual(std::string_view str, @@ -71,7 +78,7 @@ stringBeginEqual(std::string_view str, { if (str.size() < prefix.size()) return false; - return strncasecmp(str.data(), prefix.data(), prefix.size()) == 0; + return std::ranges::equal(str.substr(0, prefix.size()), prefix, charEqual); } // Case insensitive compare. @@ -79,19 +86,7 @@ inline bool stringEqual(std::string_view s1, std::string_view s2) { - return std::ranges::equal(s1, s2, [](unsigned char c1, unsigned char c2) { - return std::tolower(c1) == std::tolower(c2); - }); -} - -void -stringDeleteCheck(const char *str); - -// Delete for strings allocated with new char[]. -inline void -stringDelete(const char *str) -{ - delete [] str; + return std::ranges::equal(s1, s2, charEqual); } std::pair @@ -114,4 +109,4 @@ StringSeq parseTokens(const std::string &text, std::string_view delims = " \t"); -} // namespace +} // namespace sta diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 3afce3352..46984d495 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -73,7 +73,6 @@ public: GateTableModel(LibertyCell *cell, TableModels *delay_models, TableModels *slew_models); - ~GateTableModel() override; void gateDelay(const Pvt *pvt, float in_slew, float load_cap, @@ -96,6 +95,7 @@ public: PocvMode pocv_mode, int digits) const override; float driveResistance(const Pvt *pvt) const override; + void setIsScaled(bool is_scaled) override; const TableModels *delayModels() const { return delay_models_.get(); } const TableModel *delayModel() const; @@ -112,10 +112,9 @@ protected: const Pvt *pvt, float &slew, float &cap) const; - void setIsScaled(bool is_scaled) override; float axisValue(const TableAxis *axis, - float load_cap, float in_slew, + float load_cap, float related_out_cap) const; float findValue(const Pvt *pvt, const TableModel *model, @@ -150,7 +149,6 @@ class CheckTableModel : public CheckTimingModel public: CheckTableModel(LibertyCell *cell, TableModels *check_models); - ~CheckTableModel() override; ArcDelay checkDelay(const Pvt *pvt, float from_slew, float to_slew, @@ -167,13 +165,13 @@ public: int digits) const override; const TableModels *checkModels() const { return check_models_.get(); } const TableModel *checkModel() const; + void setIsScaled(bool is_scaled) override; // Check the axes before making the model. // Return true if the model axes are supported. static bool checkAxes(const TableModel *table); protected: - void setIsScaled(bool is_scaled) override; float findValue(const Pvt *pvt, const TableModel *model, float from_slew, @@ -187,8 +185,8 @@ protected: float &axis_value2, float &axis_value3) const; float axisValue(const TableAxis *axis, - float load_cap, - float in_slew, + float from_slew, + float to_slew, float related_out_cap) const; std::string reportTableDelay(std::string_view result_name, const Pvt *pvt, @@ -247,19 +245,19 @@ public: TableAxisPtr axis1, TableAxisPtr axis2, TableAxisPtr axis3); - Table(Table &&table); + Table(Table &&table) noexcept; Table(const Table &table); - Table &operator=(Table &&table); + Table &operator=(Table &&table) noexcept; + void setIsScaled(bool is_scaled); void setScaleFactorType(ScaleFactorType type); int order() const { return order_; } const TableAxis *axis1() const { return axis1_.get(); } const TableAxis *axis2() const { return axis2_.get(); } const TableAxis *axis3() const { return axis3_.get(); } - const TableAxisPtr axis1ptr() const { return axis1_; } - const TableAxisPtr axis2ptr() const { return axis2_; } - const TableAxisPtr axis3ptr() const { return axis3_; } - void setIsScaled(bool is_scaled); + TableAxisPtr axis1ptr() const { return axis1_; } + TableAxisPtr axis2ptr() const { return axis2_; } + TableAxisPtr axis3ptr() const { return axis3_; } float value(size_t axis_idx1, size_t axis_idx2, @@ -440,7 +438,6 @@ protected: class ReceiverModel { public: - ~ReceiverModel(); void setCapacitanceModel(TableModel table_model, size_t segment, const RiseFall *rf); @@ -529,7 +526,7 @@ private: Table1Seq voltage_waveforms_; Table1Seq voltage_currents_; Table ref_times_; - float vdd_; + float vdd_{0.0F}; static constexpr size_t voltage_waveform_step_count_ = 100; }; @@ -546,4 +543,4 @@ private: TablePtr waveforms_; }; -} // namespace +} // namespace sta diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index acb4e164e..995b425f1 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -64,4 +64,4 @@ ArcDcalcArg arcDcalcArgTcl(Tcl_Obj *obj, Tcl_Interp *interp); -} // namespace +} // namespace sta diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 274f10e5e..d95538301 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -86,7 +86,7 @@ enum class TimingType { std::string_view to_string(TimingType type); TimingType -findTimingType(std::string_view string); +findTimingType(std::string_view type_name); bool timingTypeIsCheck(TimingType type); ScaleFactorType @@ -107,15 +107,15 @@ public: FuncExpr *cond() const { return cond_; } void setCond(FuncExpr *cond); const std::string &sdfCond() const { return sdf_cond_; } - void setSdfCond(std::string cond); + void setSdfCond(std::string_view cond); const std::string &sdfCondStart() const { return sdf_cond_start_; } - void setSdfCondStart(std::string cond); + void setSdfCondStart(std::string_view cond); const std::string &sdfCondEnd() const { return sdf_cond_end_; } - void setSdfCondEnd(std::string cond); + void setSdfCondEnd(std::string_view cond); const std::string &modeName() const { return mode_name_; } - void setModeName(std::string name); + void setModeName(std::string_view name); const std::string &modeValue() const { return mode_value_; } - void setModeValue(std::string value); + void setModeValue(std::string_view value); TimingModel *model(const RiseFall *rf) const; void setModel(const RiseFall *rf, TimingModel *model); @@ -260,9 +260,9 @@ public: GateTableModel *gateTableModel() const; GateTableModel *gateTableModel(const Scene *scene, const MinMax *min_max) const; - const TimingArc *sceneArc(int ap_index) const; + const TimingArc *sceneArc(size_t lib_ap_index) const; void setSceneArc(TimingArc *scene_arc, - int ap_index); + size_t lib_ap_index); float driveResistance() const; ArcDelay intrinsicDelay() const; @@ -281,7 +281,7 @@ protected: const Transition *to_rf_; unsigned index_; TimingModel *model_; - ScaledTimingModelMap *scaled_models_; + ScaledTimingModelMap *scaled_models_{nullptr}; std::vector scene_arcs_; private: @@ -290,4 +290,4 @@ private: friend class TimingArcSet; }; -} // namespace +} // namespace sta diff --git a/include/sta/TimingModel.hh b/include/sta/TimingModel.hh index 55e1623ec..36e25a36d 100644 --- a/include/sta/TimingModel.hh +++ b/include/sta/TimingModel.hh @@ -38,7 +38,7 @@ class TimingModel { public: TimingModel(LibertyCell *cell); - virtual ~TimingModel() {} + virtual ~TimingModel() = default; virtual void setIsScaled(bool is_scaled) = 0; protected: @@ -97,4 +97,4 @@ public: int digits) const = 0; }; -} // namespace +} // namespace sta diff --git a/include/sta/TimingRole.hh b/include/sta/TimingRole.hh index 23a075f02..f07d6ed6a 100644 --- a/include/sta/TimingRole.hh +++ b/include/sta/TimingRole.hh @@ -144,4 +144,4 @@ private: friend class TimingRoleLess; }; -} // namespace +} // namespace sta diff --git a/include/sta/Transition.hh b/include/sta/Transition.hh index 9d01abbc2..16ccb0255 100644 --- a/include/sta/Transition.hh +++ b/include/sta/Transition.hh @@ -47,43 +47,43 @@ public: // Singleton accessors. static const RiseFall *rise() { return &rise_; } static const RiseFall *fall() { return &fall_; } - static int riseIndex() { return rise_.sdf_triple_index_; } - static int fallIndex() { return fall_.sdf_triple_index_; } + static size_t riseIndex() { return rise_.sdf_triple_index_; } + static size_t fallIndex() { return fall_.sdf_triple_index_; } const std::string &to_string(bool use_short = false) const; const std::string &name() const { return name_; } const std::string &shortName() const { return short_name_; } - int index() const { return sdf_triple_index_; } + size_t index() const { return sdf_triple_index_; } const RiseFallBoth *asRiseFallBoth(); const RiseFallBoth *asRiseFallBoth() const; const Transition *asTransition() const; // Find transition corresponding to rf_str. - static const RiseFall *find(std::string_view rf_str); + static const RiseFall *find(std::string_view rf_name); // Find transition from index. - static const RiseFall *find(int index); + static const RiseFall *find(size_t index); const RiseFall *opposite() const; // for range support. // for (auto rf : RiseFall::range()) {} static const std::array &range() { return range_; } // for (auto rf_index : RiseFall::rangeIndex()) {} - static const std::array &rangeIndex() { return range_index_; } - static const int index_count = 2; - static const int index_max = (index_count - 1); - static const int index_bit_count = 1; + static const std::array &rangeIndex() { return range_index_; } + static const size_t index_count = 2; + static const size_t index_max = (index_count - 1); + static const size_t index_bit_count = 1; protected: RiseFall(std::string_view name, std::string_view short_name, - int sdf_triple_index); + size_t sdf_triple_index); const std::string name_; const std::string short_name_; - const int sdf_triple_index_; + const size_t sdf_triple_index_; static const RiseFall rise_; static const RiseFall fall_; static const std::array range_; - static const std::array range_index_; + static const std::array range_index_; }; // Rise/fall/risefall transition. @@ -97,35 +97,35 @@ public: const std::string &to_string(bool use_short = false) const; const std::string &name() const { return name_; } const std::string &shortName() const { return short_name_; } - int index() const { return sdf_triple_index_; } + size_t index() const { return sdf_triple_index_; } bool matches(const RiseFall *rf) const; bool matches(const Transition *tr) const; const RiseFall *asRiseFall() const { return as_rise_fall_; } // Find transition corresponding to string. - static const RiseFallBoth *find(std::string_view tr_str); + static const RiseFallBoth *find(std::string_view rf_name); // for (const auto rf : rf->range()) {} const std::vector &range() const { return range_; } // for (const auto rf_index : rf->rangeIndex()) {} - const std::vector &rangeIndex() const { return range_index_; } + const std::vector &rangeIndex() const { return range_index_; } - static const int index_count = 3; - static const int index_max = (index_count - 1); - static const int index_bit_count = 2; + static const size_t index_count = 3; + static const size_t index_max = (index_count - 1); + static const size_t index_bit_count = 2; protected: RiseFallBoth(std::string_view name, std::string_view short_name, - int sdf_triple_index, + size_t sdf_triple_index, const RiseFall *as_rise_fall, - std::vector range, - std::vector range_index); + const std::vector &range, + const std::vector &range_index); const std::string name_; const std::string short_name_; - const int sdf_triple_index_; + const size_t sdf_triple_index_; const RiseFall *as_rise_fall_; const std::vector range_; - const std::vector range_index_; + const std::vector range_index_; static const RiseFallBoth rise_; static const RiseFallBoth fall_; @@ -154,25 +154,25 @@ public: const std::string &to_string() const { return name_; } // As initial/final value pair. const std::string &asInitFinalString() const { return init_final_; } - int sdfTripleIndex() const { return sdf_triple_index_; } - int index() const { return sdf_triple_index_; } + size_t sdfTripleIndex() const { return sdf_triple_index_; } + size_t index() const { return sdf_triple_index_; } const RiseFall *asRiseFall() const { return as_rise_fall_; } const RiseFallBoth *asRiseFallBoth() const; bool matches(const Transition *tr) const; // Find transition corresponding to string. - static const Transition *find(std::string_view tr_str); - static int maxIndex() { return max_index_; } + static const Transition *find(std::string_view tr_name); + static size_t maxIndex() { return max_index_; } private: Transition(std::string_view name, std::string_view init_final, const RiseFall *as_rise_fall, - int sdf_triple_index); + size_t sdf_triple_index); const std::string name_; const std::string init_final_; const RiseFall *as_rise_fall_; - const int sdf_triple_index_; + const size_t sdf_triple_index_; static const Transition rise_; static const Transition fall_; @@ -187,12 +187,12 @@ private: static const Transition tr_XZ_; static const Transition tr_ZX_; static const Transition rise_fall_; - static const int index_count = 13; - static const int index_max = (index_count - 1); - static const int index_bit_count = 4; + static const size_t index_count = 13; + static const size_t index_max = (index_count - 1); + static const size_t index_bit_count = 4; static TransitionMap transition_map_; - static int max_index_; + static size_t max_index_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Units.hh b/include/sta/Units.hh index 0067fe75f..c3868f992 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -39,7 +39,7 @@ public: double staToUser(double value); // Convert from user interface units to sta units. double userToSta(double value); - void operator=(const Unit &unit); + Unit &operator=(const Unit &unit) = default; float scale() const { return scale_; } void setScale(float scale); // Mkmunpf abbreviation for scale. @@ -76,7 +76,7 @@ class Units public: Units(); Unit *find(std::string_view unit_name); - void operator=(const Units &units); + Units &operator=(const Units &units); Unit *timeUnit() { return &time_unit_; } const Unit *timeUnit() const { return &time_unit_; } Unit *capacitanceUnit() { return &capacitance_unit_; } @@ -105,4 +105,4 @@ private: Unit scalar_unit_; }; -} // namespace +} // namespace sta diff --git a/include/sta/Variables.hh b/include/sta/Variables.hh index 7e8e7a69c..a5bf2a1f7 100644 --- a/include/sta/Variables.hh +++ b/include/sta/Variables.hh @@ -97,4 +97,4 @@ private: float pocv_quantile_; }; -} // namespace +} // namespace sta diff --git a/include/sta/VectorMap.hh b/include/sta/VectorMap.hh index b78f76cfb..2273e3ab6 100644 --- a/include/sta/VectorMap.hh +++ b/include/sta/VectorMap.hh @@ -99,14 +99,14 @@ private: Compare comp_; // Helper to find insertion point using binary search - typename storage_type::iterator findInsertPos(const Key& key) { + storage_type::iterator findInsertPos(const Key& key) { return std::lower_bound(data_.begin(), data_.end(), key, [this](const auto& pair, const Key& k) { return comp_(pair.first, k); }); } - typename storage_type::const_iterator findInsertPos(const Key& key) const { + storage_type::const_iterator findInsertPos(const Key& key) const { return std::lower_bound(data_.begin(), data_.end(), key, [this](const auto& pair, const Key& k) { return comp_(pair.first, k); @@ -126,15 +126,12 @@ private: using reference = proxy_type; using pointer = proxy_type*; - IteratorAdapter() = default; explicit IteratorAdapter(BaseIter it) : it_(it) {} // Conversion constructor: allow conversion from non-const to const iterator template - IteratorAdapter(const IteratorAdapter& other, - typename std::enable_if< - std::is_convertible_v - >::type* = nullptr) + requires std::is_convertible_v + IteratorAdapter(const IteratorAdapter& other) : it_(other.base()) {} reference operator*() { @@ -166,11 +163,8 @@ private: // Assignment operator: allow assignment from non-const to const iterator template - typename std::enable_if< - std::is_convertible_v, - IteratorAdapter& - >::type - operator=(const IteratorAdapter& other) { + requires std::is_convertible_v + IteratorAdapter& operator=(const IteratorAdapter& other) { it_ = other.base(); return *this; } @@ -263,7 +257,6 @@ public: using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - VectorMap() = default; explicit VectorMap(const Compare& comp) : comp_(comp) {} template diff --git a/include/sta/VerilogNamespace.hh b/include/sta/VerilogNamespace.hh index 5b8741892..c004f6291 100644 --- a/include/sta/VerilogNamespace.hh +++ b/include/sta/VerilogNamespace.hh @@ -47,4 +47,4 @@ netVerilogToSta(std::string_view sta_name); std::string portVerilogToSta(std::string_view sta_name); -} // namespace +} // namespace sta diff --git a/include/sta/VerilogReader.hh b/include/sta/VerilogReader.hh index b28e0f23b..4a94269ce 100644 --- a/include/sta/VerilogReader.hh +++ b/include/sta/VerilogReader.hh @@ -104,12 +104,12 @@ public: ~VerilogReader(); bool read(std::string_view filename); - void makeModule(std::string &&module_name, + void makeModule(std::string_view module_name, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, int line); - void makeModule(std::string &&module_name, + void makeModule(std::string_view module_name, VerilogStmtSeq *port_dcls, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, @@ -122,7 +122,7 @@ public: VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, int line); - VerilogDclArg *makeDclArg(std::string &&net_name); + VerilogDclArg *makeDclArg(std::string_view net_name); VerilogDclArg*makeDclArg(VerilogAssign *assign); VerilogDclBus *makeDclBus(PortDirection *dir, int from_index, @@ -136,36 +136,36 @@ public: VerilogDclArgSeq *args, VerilogAttrStmtSeq *attr_stmts, int line); - VerilogInst *makeModuleInst(std::string &&module_name, - std::string &&inst_name, + VerilogInst *makeModuleInst(std::string_view module_name, + std::string_view inst_name, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, - const int line); + int line); VerilogAssign *makeAssign(VerilogNet *lhs, VerilogNet *rhs, int line); - VerilogNetScalar *makeNetScalar(std::string &&name); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string &&port_vname); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string &&port_name, - std::string &&net_name); - VerilogNetPortRef *makeNetNamedPortRefBitSelect(std::string &&port_name, - std::string &&bus_name, + VerilogNetScalar *makeNetScalar(std::string_view name); + VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string_view port_vname); + VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string_view port_name, + std::string_view net_name); + VerilogNetPortRef *makeNetNamedPortRefBitSelect(std::string_view port_name, + std::string_view bus_name, int index); - VerilogNetPortRef *makeNetNamedPortRefScalar(std::string &&port_name, + VerilogNetPortRef *makeNetNamedPortRefScalar(std::string_view port_name, VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefBit(std::string &&port_name, + VerilogNetPortRef *makeNetNamedPortRefBit(std::string_view port_name, int index, VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefPart(std::string &&port_name, + VerilogNetPortRef *makeNetNamedPortRefPart(std::string_view port_name, int from_index, int to_index, VerilogNet *net); VerilogNetConcat *makeNetConcat(VerilogNetSeq *nets); - VerilogNetConstant *makeNetConstant(std::string &&constant, + VerilogNetConstant *makeNetConstant(std::string_view constant, int line); - VerilogNetBitSelect *makeNetBitSelect(std::string &&name, + VerilogNetBitSelect *makeNetBitSelect(std::string_view name, int index); - VerilogNetPartSelect *makeNetPartSelect(std::string &&name, + VerilogNetPartSelect *makeNetPartSelect(std::string_view name, int from_index, int to_index); VerilogModule *module(Cell *cell); diff --git a/include/sta/VerilogWriter.hh b/include/sta/VerilogWriter.hh index 2867c7ea6..7e4cf6e06 100644 --- a/include/sta/VerilogWriter.hh +++ b/include/sta/VerilogWriter.hh @@ -34,4 +34,4 @@ writeVerilog(const char *filename, CellSeq *remove_cells, Network *network); -} // namespace +} // namespace sta diff --git a/include/sta/VertexId.hh b/include/sta/VertexId.hh index 864139bf0..8e6181b7b 100644 --- a/include/sta/VertexId.hh +++ b/include/sta/VertexId.hh @@ -34,4 +34,4 @@ using VertexId = ObjectId; static constexpr VertexId vertex_id_null = object_id_null; -} // namespace +} // namespace sta diff --git a/include/sta/VertexVisitor.hh b/include/sta/VertexVisitor.hh index 3b4a61763..c7c8e9812 100644 --- a/include/sta/VertexVisitor.hh +++ b/include/sta/VertexVisitor.hh @@ -33,12 +33,10 @@ namespace sta { class VertexVisitor { public: - VertexVisitor() {} - virtual ~VertexVisitor() {} + virtual ~VertexVisitor() = default; virtual VertexVisitor *copy() const = 0; virtual void visit(Vertex *vertex) = 0; void operator()(Vertex *vertex) { visit(vertex); } - virtual void levelFinished() {} }; // Collect visited pins into a PinSet. @@ -47,11 +45,11 @@ class VertexPinCollector : public VertexVisitor public: VertexPinCollector(PinSet &pins); const PinSet &pins() const { return pins_; } - void visit(Vertex *vertex); - virtual VertexVisitor *copy() const; + void visit(Vertex *vertex) override; + VertexVisitor *copy() const override; protected: PinSet &pins_; }; -} // namespace +} // namespace sta diff --git a/include/sta/VisitPathEnds.hh b/include/sta/VisitPathEnds.hh index 74ef04965..ad52fbf71 100644 --- a/include/sta/VisitPathEnds.hh +++ b/include/sta/VisitPathEnds.hh @@ -142,7 +142,7 @@ protected: class PathEndVisitor { public: - virtual ~PathEndVisitor() {} + virtual ~PathEndVisitor() = default; virtual PathEndVisitor *copy() const = 0; // Begin visiting the path ends for a vertex / path_index. virtual void vertexBegin(Vertex *) {} @@ -152,4 +152,4 @@ public: virtual void vertexEnd(Vertex *) {} }; -} // namespace +} // namespace sta diff --git a/include/sta/Wireload.hh b/include/sta/Wireload.hh index 6ca8b5360..c0dc90765 100644 --- a/include/sta/Wireload.hh +++ b/include/sta/Wireload.hh @@ -42,7 +42,7 @@ using WireloadForAreaSeq = std::vector; const char * wireloadTreeString(WireloadTree tree); WireloadTree -stringWireloadTree(std::string_view tree); +stringWireloadTree(std::string_view wire_load_type); const char * wireloadModeString(WireloadMode wire_load_mode); @@ -101,4 +101,4 @@ private: WireloadForAreaSeq wireloads_; }; -} // namespace +} // namespace sta diff --git a/liberty/EquivCells.cc b/liberty/EquivCells.cc index e4fbba446..3c40ca4d3 100644 --- a/liberty/EquivCells.cc +++ b/liberty/EquivCells.cc @@ -24,17 +24,16 @@ #include "EquivCells.hh" +#include + #include "ContainerHelpers.hh" -#include "Hash.hh" -#include "MinMax.hh" -#include "PortDirection.hh" -#include "Transition.hh" -#include "TimingRole.hh" #include "FuncExpr.hh" -#include "TimingArc.hh" +#include "Hash.hh" #include "Liberty.hh" -#include "TableModel.hh" +#include "LibertyClass.hh" +#include "PortDirection.hh" #include "Sequential.hh" +#include "TimingArc.hh" namespace sta { @@ -208,8 +207,8 @@ hashCellPorts(const LibertyCell *cell) static unsigned hashPort(const LibertyPort *port) { - return hashString(port->name().c_str()) * 3 - + port->direction()->index() * 5; + return hashString(port->name()) * 3U + + port->direction()->index() * 5U; } static unsigned @@ -235,8 +234,8 @@ hashSequential(const Sequential *seq) hash += hashPort(seq->outputInv()) * 11; hash += hashFuncExpr(seq->clear()) * 13; hash += hashFuncExpr(seq->preset()) * 17; - hash += int(seq->clearPresetOutput()) * 19; - hash += int(seq->clearPresetOutputInv()) * 23; + hash += static_cast(seq->clearPresetOutput()) * 19; + hash += static_cast(seq->clearPresetOutputInv()) * 23; return hash; } @@ -501,4 +500,4 @@ equivCellTimingArcSets(const LibertyCell *cell1, } } -} // namespace +} // namespace sta diff --git a/liberty/FuncExpr.cc b/liberty/FuncExpr.cc index b127cd278..fbab84523 100644 --- a/liberty/FuncExpr.cc +++ b/liberty/FuncExpr.cc @@ -24,9 +24,11 @@ #include "FuncExpr.hh" -#include "StringUtil.hh" +#include +#include + #include "Liberty.hh" -#include "Network.hh" +#include "LibertyClass.hh" namespace sta { @@ -233,7 +235,6 @@ std::string FuncExpr::to_string(bool with_parens, char op) const { - std::string right = right_->to_string(true); std::string result; if (with_parens) result += '('; @@ -422,4 +423,4 @@ FuncExpr::less(const FuncExpr *expr1, return (expr1 == nullptr && expr2 != nullptr); } -} // namespace +} // namespace sta diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index ed2cad244..97960ff55 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -25,8 +25,13 @@ #include "InternalPower.hh" #include +#include +#include +#include "Error.hh" #include "FuncExpr.hh" +#include "LibertyClass.hh" +#include "Transition.hh" #include "TableModel.hh" #include "Liberty.hh" #include "Units.hh" @@ -76,7 +81,7 @@ InternalPowerModel::InternalPowerModel() : } InternalPowerModel::InternalPowerModel(std::shared_ptr model) : - model_(model) + model_(std::move(model)) { } @@ -192,4 +197,4 @@ InternalPowerModel::checkAxis(const TableAxis *axis) || var == TableAxisVariable::related_out_total_output_net_capacitance; } -} // namespace +} // namespace sta diff --git a/liberty/LeakagePower.cc b/liberty/LeakagePower.cc index 150a2cf1a..5a3039250 100644 --- a/liberty/LeakagePower.cc +++ b/liberty/LeakagePower.cc @@ -25,7 +25,6 @@ #include "LeakagePower.hh" #include "FuncExpr.hh" -#include "TableModel.hh" #include "Liberty.hh" namespace sta { @@ -55,4 +54,4 @@ LeakagePower::~LeakagePower() delete when_; } -} // namespace +} // namespace sta diff --git a/liberty/LibExprLex.ll b/liberty/LibExprLex.ll index 69402d84f..0398225b2 100644 --- a/liberty/LibExprLex.ll +++ b/liberty/LibExprLex.ll @@ -40,7 +40,7 @@ using sta::FuncExpr; #undef YY_DECL #define YY_DECL \ int \ -sta::LibExprScanner::lex(sta::LibExprParse::semantic_type *const yylval) +sta::LibExprScanner::lex(sta::LibExprParse::semantic_type *yylval) typedef sta::LibExprParse::token token; diff --git a/liberty/LibExprReader.cc b/liberty/LibExprReader.cc index d74272234..149be5b8b 100644 --- a/liberty/LibExprReader.cc +++ b/liberty/LibExprReader.cc @@ -24,7 +24,6 @@ #include "FuncExpr.hh" -#include #include #include #include @@ -63,8 +62,7 @@ LibExprReader::LibExprReader(std::string_view func, func_(func), cell_(cell), error_msg_(error_msg), - report_(report), - result_(nullptr) + report_(report) { } diff --git a/liberty/LibExprReader.hh b/liberty/LibExprReader.hh index 6b0c8cbe6..542992989 100644 --- a/liberty/LibExprReader.hh +++ b/liberty/LibExprReader.hh @@ -38,4 +38,4 @@ parseFuncExpr(std::string_view func, std::string_view error_msg, Report *report); -} // namespace +} // namespace sta diff --git a/liberty/LibExprReaderPvt.hh b/liberty/LibExprReaderPvt.hh index b6b27bc83..9ef029e26 100644 --- a/liberty/LibExprReaderPvt.hh +++ b/liberty/LibExprReaderPvt.hh @@ -59,7 +59,7 @@ private: const LibertyCell *cell_; std::string_view error_msg_; Report *report_; - FuncExpr *result_; + FuncExpr *result_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/liberty/LibExprScanner.hh b/liberty/LibExprScanner.hh index f901be3d6..cb0cb0b48 100644 --- a/liberty/LibExprScanner.hh +++ b/liberty/LibExprScanner.hh @@ -43,9 +43,7 @@ class LibExprScanner : public LibExprFlexLexer { public: LibExprScanner(std::istringstream &stream); - virtual ~LibExprScanner() {} - - virtual int lex(LibExprParse::semantic_type *const yylval); + virtual int lex(LibExprParse::semantic_type *yylval); // YY_DECL defined in LibertyLex.ll // Method body created by flex in LibertyLex.cc @@ -57,4 +55,4 @@ private: std::string token_; }; -} // namespace +} // namespace sta diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 1817a7a28..bdccfcbd2 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -24,29 +24,39 @@ #include "Liberty.hh" +#include +#include +#include +#include +#include +#include + +#include "ConcreteLibrary.hh" #include "ContainerHelpers.hh" -#include "Format.hh" -#include "Mutex.hh" -#include "EnumNameMap.hh" -#include "Report.hh" #include "Debug.hh" +#include "Delay.hh" +#include "EnumNameMap.hh" #include "Error.hh" -#include "StringUtil.hh" -#include "PatternMatch.hh" -#include "Units.hh" -#include "Transition.hh" -#include "TimingRole.hh" +#include "Format.hh" #include "FuncExpr.hh" -#include "TableModel.hh" -#include "TimingArc.hh" #include "InternalPower.hh" -#include "LeakagePower.hh" -#include "Sequential.hh" -#include "Wireload.hh" -#include "EquivCells.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mutex.hh" #include "Network.hh" +#include "NetworkClass.hh" +#include "ObjectId.hh" +#include "PatternMatch.hh" #include "PortDirection.hh" +#include "Report.hh" #include "Scene.hh" +#include "Sequential.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" +#include "Wireload.hh" namespace sta { @@ -65,46 +75,13 @@ deleteLiberty() LibertyLibrary::LibertyLibrary(std::string_view name, std::string_view filename) : ConcreteLibrary(name, filename, true), - units_(new Units()), - delay_model_type_(DelayModelType::table), // default - nominal_process_(0.0), - nominal_voltage_(0.0), - nominal_temperature_(0.0), - scale_factors_(nullptr), - default_input_pin_cap_(0.0), - default_output_pin_cap_(0.0), - default_bidirect_pin_cap_(0.0), - default_fanout_load_(0.0), - default_fanout_load_exists_(false), - default_max_cap_(0.0), - default_max_cap_exists_(false), - default_max_fanout_(0.0), - default_max_fanout_exists_(false), - default_max_slew_(0.0), - default_max_slew_exists_(false), - slew_derate_from_library_(1.0), - default_wire_load_(nullptr), - default_wire_load_mode_(WireloadMode::unknown), - default_wire_load_selection_(nullptr), - default_operating_conditions_(nullptr), - ocv_arc_depth_(0.0), - default_ocv_derate_(nullptr), - buffers_(nullptr), - inverters_(nullptr) + units_(new Units()) { // Scalar templates are builtin. for (int i = 0; i != table_template_type_count; i++) { TableTemplateType type = static_cast(i); makeTableTemplate("scalar", type); } - - for (auto rf_index : RiseFall::rangeIndex()) { - wire_slew_degradation_tbls_[rf_index] = nullptr; - input_threshold_[rf_index] = input_threshold_default_; - output_threshold_[rf_index] = output_threshold_default_; - slew_lower_threshold_[rf_index] = slew_lower_threshold_default_; - slew_upper_threshold_[rf_index] = slew_upper_threshold_default_; - } } LibertyLibrary::~LibertyLibrary() @@ -204,10 +181,10 @@ LibertyLibrary::busDcls() const TableTemplate * LibertyLibrary::makeTableTemplate(std::string_view name, - TableTemplateType type) + TableTemplateType type) { std::string key(name); - auto [it, inserted] = template_maps_[int(type)].try_emplace(std::move(key), + auto [it, inserted] = template_maps_[static_cast(type)].try_emplace(std::move(key), std::string(name), type); return &it->second; @@ -217,15 +194,15 @@ TableTemplate * LibertyLibrary::findTableTemplate(std::string_view name, TableTemplateType type) { - return findStringValuePtr(template_maps_[int(type)], name); + return findStringValuePtr(template_maps_[static_cast(type)], name); } TableTemplateSeq LibertyLibrary::tableTemplates() const { TableTemplateSeq tbl_templates; - for (int type = 0; type < table_template_type_count; type++) { - for (auto &[key, tbl_template] : template_maps_[type]) + for (const auto & template_map : template_maps_) { + for (auto &[key, tbl_template] : template_map) tbl_templates.push_back(const_cast(&tbl_template)); } return tbl_templates; @@ -235,7 +212,7 @@ TableTemplateSeq LibertyLibrary::tableTemplates(TableTemplateType type) const { TableTemplateSeq tbl_templates; - for (auto &[key, tbl_template] : template_maps_[int(type)]) + for (auto &[key, tbl_template] : template_maps_[static_cast(type)]) tbl_templates.push_back(const_cast(&tbl_template)); return tbl_templates; } @@ -538,7 +515,7 @@ LibertyLibrary::defaultBidirectPinRes(const RiseFall *rf, float &res, bool &exists) const { - return default_inout_pin_res_.value(rf, res, exists); + default_inout_pin_res_.value(rf, res, exists); } void @@ -730,7 +707,7 @@ LibertyLibrary::makeScaledCell(std::string_view name, void LibertyLibrary::makeSceneMap(LibertyLibrary *lib, - int ap_index, + size_t lib_ap_index, Network *network, Report *report) { @@ -739,7 +716,7 @@ LibertyLibrary::makeSceneMap(LibertyLibrary *lib, LibertyCell *cell = cell_iter.next(); LibertyCell *link_cell = network->findLibertyCell(cell->name()); if (link_cell) - makeSceneMap(link_cell, cell, ap_index, report); + makeSceneMap(link_cell, cell, lib_ap_index, report); } } @@ -748,21 +725,21 @@ LibertyLibrary::makeSceneMap(LibertyLibrary *lib, void LibertyLibrary::makeSceneMap(LibertyCell *link_cell, LibertyCell *scene_cell, - int ap_index, + size_t lib_ap_index, Report *report) { - link_cell->setSceneCell(scene_cell, ap_index); - makeSceneMap(link_cell, scene_cell, true, ap_index, report); + link_cell->setSceneCell(scene_cell, lib_ap_index); + makeSceneMap(link_cell, scene_cell, true, lib_ap_index, report); // Check for brain damage in the other direction. - makeSceneMap(scene_cell, link_cell, false, ap_index, report); + makeSceneMap(scene_cell, link_cell, false, lib_ap_index, report); } void LibertyLibrary::makeSceneMap(LibertyCell *cell1, - LibertyCell *cell2, - bool link, - int ap_index, - Report *report) + LibertyCell *cell2, + bool link, + size_t lib_ap_index, + Report *report) { LibertyCellPortBitIterator port_iter1(cell1); while (port_iter1.hasNext()) { @@ -770,7 +747,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, LibertyPort *port2 = cell2->findLibertyPort(port1->name()); if (port2) { if (link) - port1->setScenePort(port2, ap_index); + port1->setScenePort(port2, lib_ap_index); } else report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.", @@ -794,7 +771,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, TimingArc *arc1 = *arc_itr1; TimingArc *arc2 = *arc_itr2; if (TimingArc::equiv(arc1, arc2)) - arc1->setSceneArc(arc2, ap_index); + arc1->setSceneArc(arc2, lib_ap_index); } } } @@ -905,7 +882,7 @@ LibertyLibrary::findDriverWaveform(std::string_view name) DriverWaveform * LibertyLibrary::makeDriverWaveform(std::string_view name, - TablePtr waveforms) + const TablePtr &waveforms) { std::string key(name); auto [it, inserted] = driver_waveform_map_.try_emplace(std::move(key), @@ -939,30 +916,7 @@ LibertyCell::LibertyCell(LibertyLibrary *library, std::string_view name, std::string_view filename) : ConcreteCell(name, filename, true, library), - liberty_library_(library), - area_(0.0), - dont_use_(false), - is_macro_(false), - is_memory_(false), - is_pad_(false), - is_clock_cell_(false), - is_level_shifter_(false), - level_shifter_type_(LevelShifterType::HL_LH), - is_isolation_cell_(false), - always_on_(false), - switch_cell_type_(SwitchCellType::fine_grain), - interface_timing_(false), - clock_gate_type_(ClockGateType::none), - has_infered_reg_timing_arcs_(false), - statetable_(nullptr), - scale_factors_(nullptr), - test_cell_(nullptr), - ocv_arc_depth_(0.0), - ocv_derate_(nullptr), - leakage_power_(0.0), - leakage_power_exists_(false), - has_internal_ports_(false), - have_voltage_waveforms_(false) + liberty_library_(library) { liberty_cell_ = this; } @@ -1248,7 +1202,7 @@ LibertyCell::makeTimingArcSet(LibertyPort *from, { size_t set_index = timing_arc_sets_.size(); TimingArcSet *arc_set = new TimingArcSet(this, from, to, related_out, role, - attrs, set_index); + std::move(attrs), set_index); timing_arc_sets_.push_back(arc_set); return arc_set; } @@ -1314,7 +1268,7 @@ LibertyCell::finish(bool infer_latches, void LibertyCell::findDefaultCondArcs() { - for (auto [port_pair, sets] : port_timing_arc_set_map_) { + for (auto &[port_pair, sets] : port_timing_arc_set_map_) { bool has_cond_arcs = false; for (auto set : sets) { if (set->cond()) { @@ -1323,7 +1277,7 @@ LibertyCell::findDefaultCondArcs() } } if (has_cond_arcs) { - for (auto set : sets) { + for (auto &set : sets) { if (!set->cond()) set->setIsCondDefault(true); } @@ -1566,21 +1520,21 @@ LibertyCell::sceneCell(const Scene *scene, } LibertyCell * -LibertyCell::sceneCell(int ap_index) +LibertyCell::sceneCell(size_t lib_ap_index) { if (scene_cells_.empty()) return this; - else if (ap_index < static_cast(scene_cells_.size())) - return scene_cells_[ap_index]; + else if (lib_ap_index < scene_cells_.size()) + return scene_cells_[lib_ap_index]; else return nullptr; } bool LibertyCell::checkSceneCell(const Scene *scene, - const MinMax *min_max) const + const MinMax *min_max) const { - unsigned lib_index = scene->libertyIndex(min_max); + size_t lib_index = scene->libertyIndex(min_max); return scene_cells_.empty() || (lib_index < scene_cells_.size() && scene_cells_[lib_index]); @@ -1588,11 +1542,11 @@ LibertyCell::checkSceneCell(const Scene *scene, void LibertyCell::setSceneCell(LibertyCell *scene_cell, - int ap_index) + size_t lib_ap_index) { - if (ap_index >= static_cast(scene_cells_.size())) - scene_cells_.resize(ap_index + 1); - scene_cells_[ap_index] = scene_cell; + if (lib_ap_index >= scene_cells_.size()) + scene_cells_.resize(lib_ap_index + 1); + scene_cells_[lib_ap_index] = scene_cell; } //////////////////////////////////////////////////////////////// @@ -1872,19 +1826,19 @@ LibertyCell::latchEnable(const TimingArcSet *d_to_q_set, // Return values. const LibertyPort *&enable_port, const FuncExpr *&enable_func, - const RiseFall *&enable_edge) const + const RiseFall *&enable_rf) const { auto it = latch_d_to_q_map_.find(d_to_q_set); if (it != latch_d_to_q_map_.end()) { const LatchEnable &latch_enable = latch_enables_[it->second]; enable_port = latch_enable.enable(); enable_func = latch_enable.enableFunc(); - enable_edge = latch_enable.enableEdge(); + enable_rf = latch_enable.enableEdge(); } else { enable_port = nullptr; enable_func = nullptr; - enable_edge = nullptr; + enable_rf = nullptr; } } @@ -1997,41 +1951,9 @@ LibertyPort::LibertyPort(LibertyCell *cell, ConcretePort(name, is_bus, from_index, to_index, is_bundle, members, cell), liberty_cell_(cell), - bus_dcl_(bus_dcl), - pwr_gnd_type_(PwrGndType::none), - scan_signal_type_(ScanSignalType::none), - function_(nullptr), - tristate_enable_(nullptr), - scaled_ports_(nullptr), - fanout_load_(0.0), - fanout_load_exists_(false), - min_period_(0.0), - pulse_clk_trigger_(nullptr), - pulse_clk_sense_(nullptr), - related_ground_port_(nullptr), - related_power_port_(nullptr), - receiver_model_(nullptr), - driver_waveform_{nullptr, nullptr}, - min_pulse_width_exists_(false), - min_period_exists_(false), - is_clk_(false), - is_reg_clk_(false), - is_reg_output_(false), - is_latch_data_(false), - is_check_clk_(false), - is_clk_gate_clk_(false), - is_clk_gate_enable_(false), - is_clk_gate_out_(false), - is_pll_feedback_(false), - isolation_cell_data_(false), - isolation_cell_enable_(false), - level_shifter_data_(false), - is_switch_(false), - is_pad_(false) + bus_dcl_(bus_dcl) { liberty_port_ = this; - min_pulse_width_[RiseFall::riseIndex()] = 0.0; - min_pulse_width_[RiseFall::fallIndex()] = 0.0; } LibertyPort::~LibertyPort() @@ -2041,14 +1963,6 @@ LibertyPort::~LibertyPort() delete scaled_ports_; } -void -LibertyPort::setDirection(PortDirection *dir) -{ - ConcretePort::setDirection(dir); - if (dir->isInternal()) - liberty_cell_->setHasInternalPorts(true); -} - void LibertyPort::setScanSignalType(ScanSignalType type) { @@ -2130,12 +2044,6 @@ LibertyPort::findLibertyBusBit(int index) const return static_cast(findBusBit(index)); } -LibertyPort * -LibertyPort::bundlePort() const -{ - return static_cast(bundle_port_); -} - void LibertyPort::setCapacitance(float cap) { @@ -2306,7 +2214,7 @@ LibertyPort::setFunction(FuncExpr *func) int bit_offset = 0; while (member_iter.hasNext()) { LibertyPort *port_bit = member_iter.next(); - FuncExpr *sub_expr = (func) ? func->bitSubExpr(bit_offset) : nullptr; + FuncExpr *sub_expr = func ? func->bitSubExpr(bit_offset) : nullptr; port_bit->setFunction(sub_expr); bit_offset++; } @@ -2321,8 +2229,9 @@ LibertyPort::setTristateEnable(FuncExpr *enable) LibertyPortMemberIterator member_iter(this); while (member_iter.hasNext()) { LibertyPort *port_bit = member_iter.next(); - FuncExpr *sub_expr = - (enable) ? enable->bitSubExpr(port_bit->busBitIndex()) : nullptr; + FuncExpr *sub_expr = enable + ? enable->bitSubExpr(port_bit->busBitIndex()) + : nullptr; port_bit->setTristateEnable(sub_expr); } } @@ -2351,7 +2260,7 @@ LibertyPort::capacitanceLimit(const MinMax *min_max, float &limit, bool &exists) const { - return cap_limit_.value(min_max, limit, exists); + cap_limit_.value(min_max, limit, exists); } void @@ -2385,7 +2294,7 @@ LibertyPort::fanoutLimit(const MinMax *min_max, float &limit, bool &exists) const { - return fanout_limit_.value(min_max, limit, exists); + fanout_limit_.value(min_max, limit, exists); } void @@ -2613,34 +2522,34 @@ LibertyPort::scenePort(const Scene *scene, } LibertyPort * -LibertyPort::scenePort(int ap_index) +LibertyPort::scenePort(size_t lib_ap_index) { if (scene_ports_.empty()) return this; - else if (ap_index < static_cast(scene_ports_.size())) - return scene_ports_[ap_index]; + else if (lib_ap_index < scene_ports_.size()) + return scene_ports_[lib_ap_index]; else return nullptr; } const LibertyPort * -LibertyPort::scenePort(int ap_index) const +LibertyPort::scenePort(size_t lib_ap_index) const { if (scene_ports_.empty()) return this; - else if (ap_index < static_cast(scene_ports_.size())) - return scene_ports_[ap_index]; + else if (lib_ap_index < scene_ports_.size()) + return scene_ports_[lib_ap_index]; else return nullptr; } void LibertyPort::setScenePort(LibertyPort *scene_port, - int ap_index) + size_t lib_ap_index) { - if (ap_index >= static_cast(scene_ports_.size())) - scene_ports_.resize(ap_index + 1); - scene_ports_[ap_index] = scene_port; + if (lib_ap_index >= scene_ports_.size()) + scene_ports_.resize(lib_ap_index + 1); + scene_ports_[lib_ap_index] = scene_port; } void @@ -2658,7 +2567,7 @@ LibertyPort::setRelatedPowerPort(LibertyPort *related_power_port) void LibertyPort::setReceiverModel(ReceiverModelPtr receiver_model) { - receiver_model_ = receiver_model; + receiver_model_ = std::move(receiver_model); } std::string @@ -2667,8 +2576,7 @@ portLibertyToSta(std::string_view port_name) constexpr char bus_brkt_left = '['; constexpr char bus_brkt_right = ']'; std::string sta_name; - for (size_t i = 0; i < port_name.size(); i++) { - char ch = port_name[i]; + for (char ch : port_name) { if (ch == bus_brkt_left || ch == bus_brkt_right) sta_name += '\\'; @@ -2866,8 +2774,7 @@ ModeDef::findValueDef(std::string_view value) const //////////////////////////////////////////////////////////////// ModeValueDef::ModeValueDef(std::string_view value) : - value_(value), - cond_(nullptr) + value_(value) { } @@ -2899,21 +2806,14 @@ ModeValueDef::setSdfCond(std::string sdf_cond) //////////////////////////////////////////////////////////////// TableTemplate::TableTemplate(std::string_view name) : - name_(name), - type_(TableTemplateType::delay), - axis1_(nullptr), - axis2_(nullptr), - axis3_(nullptr) + name_(name) { } TableTemplate::TableTemplate(std::string_view name, TableTemplateType type) : name_(name), - type_(type), - axis1_(nullptr), - axis2_(nullptr), - axis3_(nullptr) + type_(type) { } @@ -2922,10 +2822,9 @@ TableTemplate::TableTemplate(std::string_view name, TableAxisPtr axis2, TableAxisPtr axis3) : name_(name), - type_(TableTemplateType::delay), - axis1_(axis1), - axis2_(axis2), - axis3_(axis3) + axis1_(std::move(axis1)), + axis2_(std::move(axis2)), + axis3_(std::move(axis3)) { } @@ -2938,19 +2837,19 @@ TableTemplate::setName(std::string_view name) void TableTemplate::setAxis1(TableAxisPtr axis) { - axis1_ = axis; + axis1_ = std::move(axis); } void TableTemplate::setAxis2(TableAxisPtr axis) { - axis2_ = axis; + axis2_ = std::move(axis); } void TableTemplate::setAxis3(TableAxisPtr axis) { - axis3_ = axis; + axis3_ = std::move(axis); } //////////////////////////////////////////////////////////////// @@ -2984,9 +2883,7 @@ Pvt::setTemperature(float temp) OperatingConditions::OperatingConditions(std::string_view name) : Pvt(0.0, 0.0, 0.0), - name_(name), - // Default wireload tree. - wire_load_tree_(WireloadTree::unknown) + name_(name) { } @@ -3081,10 +2978,10 @@ scaleFactorPvtName(ScaleFactorPvt pvt) ScaleFactors::ScaleFactors(std::string_view name) : name_(name) { - for (int type = 0; type < scale_factor_type_count; type++) { - for (int pvt = 0; pvt < scale_factor_pvt_count; pvt++) { - for (auto rf_index : RiseFall::rangeIndex()) { - scales_[type][pvt][rf_index] = 0.0; + for (auto &scale : scales_) { + for (size_t pvt = 0; pvt < scale_factor_pvt_count; pvt++) { + for (size_t rf_index : RiseFall::rangeIndex()) { + scale[pvt][rf_index] = 0.0; } } } @@ -3096,7 +2993,7 @@ ScaleFactors::setScale(ScaleFactorType type, const RiseFall *rf, float scale) { - scales_[int(type)][int(pvt)][rf->index()] = scale; + scales_[static_cast(type)][static_cast(pvt)][rf->index()] = scale; } void @@ -3104,7 +3001,7 @@ ScaleFactors::setScale(ScaleFactorType type, ScaleFactorPvt pvt, float scale) { - scales_[int(type)][int(pvt)][0] = scale; + scales_[static_cast(type)][static_cast(pvt)][0] = scale; } float @@ -3112,7 +3009,7 @@ ScaleFactors::scale(ScaleFactorType type, ScaleFactorPvt pvt, const RiseFall *rf) { - return scales_[int(type)][int(pvt)][rf->index()]; + return scales_[static_cast(type)][static_cast(pvt)][rf->index()]; } float @@ -3120,14 +3017,14 @@ ScaleFactors::scale(ScaleFactorType type, ScaleFactorPvt pvt, int rf_index) { - return scales_[int(type)][int(pvt)][rf_index]; + return scales_[static_cast(type)][static_cast(pvt)][rf_index]; } float ScaleFactors::scale(ScaleFactorType type, ScaleFactorPvt pvt) { - return scales_[int(type)][int(pvt)][0]; + return scales_[static_cast(type)][static_cast(pvt)][0]; } void @@ -3135,13 +3032,13 @@ 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; + ScaleFactorPvt pvt = static_cast(pvt_index); line += sta::format("{:>10}", scaleFactorPvtName(pvt)); } report->reportLine(line); for (int type_index = 0; type_index < scale_factor_type_count; type_index++) { - ScaleFactorType type = (ScaleFactorType) type_index; + ScaleFactorType type = static_cast(type_index); std::string line = sta::format("{:>10}", scaleFactorTypeName(type)); for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { if (scaleFactorTypeRiseFallSuffix(type) @@ -3171,16 +3068,6 @@ TestCell::TestCell(LibertyLibrary *library, OcvDerate::OcvDerate(std::string_view name) : name_(name) -{ - for (auto el_index : EarlyLate::rangeIndex()) { - for (auto rf_index : RiseFall::rangeIndex()) { - derate_[rf_index][el_index][int(PathType::clk)] = nullptr; - derate_[rf_index][el_index][int(PathType::data)] = nullptr; - } - } -} - -OcvDerate::~OcvDerate() { } @@ -3189,7 +3076,7 @@ OcvDerate::derateTable(const RiseFall *rf, const EarlyLate *early_late, PathType path_type) { - return derate_[rf->index()][early_late->index()][int(path_type)].get(); + return derate_[rf->index()][early_late->index()][static_cast(path_type)].get(); } void @@ -3198,7 +3085,8 @@ OcvDerate::setDerateTable(const RiseFall *rf, const PathType path_type, TablePtr derate) { - derate_[rf->index()][early_late->index()][int(path_type)] = derate; + derate_[rf->index()][early_late->index()][static_cast(path_type)] + = std::move(derate); } -} // namespace +} // namespace sta diff --git a/liberty/Liberty.i b/liberty/Liberty.i index f0a9bc090..e1cfdc57f 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -317,7 +317,6 @@ bool is_bundle_member() { return self->isBundleMember(); } bool has_members() { return self->hasMembers(); } LibertyPortMemberIterator * member_iterator() { return new LibertyPortMemberIterator(self); } -LibertyPort *bundle_port() { return self->bundlePort(); } bool is_pwr_gnd() { return self->isPwrGnd(); } std::string diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index f296fdec8..c2571dd5e 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -24,14 +24,21 @@ #include "LibertyBuilder.hh" -#include "PortDirection.hh" -#include "TimingRole.hh" +#include +#include +#include + +#include "ConcreteLibrary.hh" #include "FuncExpr.hh" +#include "Liberty.hh" +#include "LibertyClass.hh" +#include "PortDirection.hh" +#include "Transition.hh" +#include "Sequential.hh" +#include "TableModel.hh" #include "TimingArc.hh" #include "TimingModel.hh" -#include "TableModel.hh" -#include "Sequential.hh" -#include "Liberty.hh" +#include "TimingRole.hh" namespace sta { @@ -114,10 +121,10 @@ LibertyBuilder::makeBusPortBit(ConcreteLibrary *library, LibertyPort * LibertyBuilder::makePort(LibertyCell *cell, - std::string bit_name, + std::string_view bit_name, int bit_index) { - LibertyPort *port = new LibertyPort(cell, std::move(bit_name), false, nullptr, + LibertyPort *port = new LibertyPort(cell, bit_name, false, nullptr, bit_index, bit_index, false, nullptr); return port; } @@ -141,8 +148,7 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, LibertyPort *related_out, - TimingArcAttrsPtr attrs, - int /* line */) + const TimingArcAttrsPtr &attrs) { FuncExpr *to_func = to_port->function(); Sequential *seq = (to_func && to_func->port()) @@ -300,7 +306,7 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, LibertyPort *to_port, bool to_rise, bool to_fall, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { FuncExpr *func = to_port->function(); FuncExpr *enable = to_port->tristateEnable(); @@ -384,7 +390,7 @@ LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, TimingSense sense, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, TimingRole::latchDtoQ(), @@ -414,7 +420,7 @@ LibertyBuilder::makeRegLatchArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, const RiseFall *from_rf, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { FuncExpr *to_func = to_port->function(); if (to_func) { @@ -453,7 +459,7 @@ LibertyBuilder::makeFromTransitionArcs(LibertyCell *cell, LibertyPort *related_out, const RiseFall *from_rf, const TimingRole *role, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, related_out, role, attrs); @@ -470,7 +476,7 @@ LibertyBuilder::makePresetClrArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, const RiseFall *to_rf, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { TimingArcSet *arc_set = nullptr; TimingModel *model = attrs->model(to_rf); @@ -506,7 +512,7 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, LibertyPort *to_port, bool to_rise, bool to_fall, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, TimingRole::tristateEnable(), @@ -577,7 +583,7 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, LibertyPort *to_port, bool to_rise, bool to_fall, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { TimingArcSet *arc_set = cell->makeTimingArcSet(from_port, to_port, nullptr, TimingRole::tristateDisable(), @@ -646,7 +652,7 @@ TimingArcSet * LibertyBuilder::makeClockTreePathArcs(LibertyCell *cell, LibertyPort *to_port, const TimingRole *role, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { TimingArcSet *arc_set = cell->makeTimingArcSet(nullptr, to_port, nullptr, role, attrs); @@ -680,7 +686,7 @@ LibertyBuilder::makeMinPulseWidthArcs(LibertyCell *cell, LibertyPort *to_port, LibertyPort *related_out, const TimingRole *role, - TimingArcAttrsPtr attrs) + const TimingArcAttrsPtr &attrs) { if (from_port == nullptr) from_port = to_port; @@ -715,4 +721,4 @@ LibertyBuilder::makeTimingArc(TimingArcSet *set, return new TimingArc(set, from_rf, to_rf, model); } -} // namespace +} // namespace sta diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh index 27a076777..ff4d89462 100644 --- a/liberty/LibertyBuilder.hh +++ b/liberty/LibertyBuilder.hh @@ -63,31 +63,30 @@ public: LibertyPort *from_port, LibertyPort *to_port, LibertyPort *related_out, - TimingArcAttrsPtr attrs, - int line); + const TimingArcAttrsPtr &attrs); TimingArcSet *makeFromTransitionArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, LibertyPort *related_out, const RiseFall *from_rf, const TimingRole *role, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); TimingArcSet *makeCombinationalArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, bool to_rise, bool to_fall, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); TimingArcSet *makeClockTreePathArcs(LibertyCell *cell, LibertyPort *to_port, const TimingRole *role, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); TimingArcSet *makeMinPulseWidthArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, LibertyPort *related_out, const TimingRole *role, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); protected: ConcretePort *makeBusPort(std::string_view name, @@ -102,7 +101,7 @@ protected: int to_index); // Bus port bit (internal to makeBusPortBits). LibertyPort *makePort(LibertyCell *cell, - std::string bit_name, + std::string_view bit_name, int bit_index); void makeBusPortBit(ConcreteLibrary *library, LibertyCell *cell, @@ -121,32 +120,32 @@ protected: LibertyPort *from_port, LibertyPort *to_port, TimingSense sense, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); TimingArcSet *makeRegLatchArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, const RiseFall *from_rf, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); TimingArcSet *makePresetClrArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, const RiseFall *to_rf, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); TimingArcSet *makeTristateEnableArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, bool to_rise, bool to_fall, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); TimingArcSet *makeTristateDisableArcs(LibertyCell *cell, LibertyPort *from_port, LibertyPort *to_port, bool to_rise, bool to_fall, - TimingArcAttrsPtr attrs); + const TimingArcAttrsPtr &attrs); Debug *debug_; Report *report_; }; -} // namespace +} // namespace sta diff --git a/liberty/LibertyExt.cc b/liberty/LibertyExt.cc deleted file mode 100644 index 68f1fe77a..000000000 --- a/liberty/LibertyExt.cc +++ /dev/null @@ -1,287 +0,0 @@ -// 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. - -// This file illustratates how to customize the liberty file reader to -// read attributes that are not used by the STA. In this example: -// * code is called at the beginning of a library definition -// * a string attribute named "thingy" is parsed - -#include -#include -#include "Machine.hh" -#include "StringUtil.hh" -#include "LibertyReader.hh" -#include "LibertyReaderPvt.hh" -#include "LibertyBuilder.hh" -#include "Network.hh" -#include "ConcreteNetwork.hh" -#include "Sta.hh" - -// Import symbols from sta package (this example is in the global package). -using sta::Report; -using sta::Debug; -using sta::Network; -using sta::LibertyReader; -using sta::LibertyGroup; -using sta::LibertySimpleAttr; -using sta::TimingGroup; -using sta::LibertyCell; -using sta::LibertyPort; -using sta::LibertyLibrary; -using sta::TimingArcSet; -using sta::LibertyBuilder; -using sta::TimingRole; -using sta::TimingArcAttrs; -using sta::Sta; -using sta::stringCopy; - -// LibertyCell with Bigco thingy variable. -class BigcoCell : public LibertyCell -{ -public: - BigcoCell(LibertyLibrary *library, const char *name, const char *filename); - void setThingy(const char *thingy); - TimingArcSet *makeTimingArcSet(LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs) override; - -protected: - const char *thingy_; -}; - -BigcoCell::BigcoCell(LibertyLibrary *library, const char *name, - const char *filename) : - LibertyCell(library, name, filename), - thingy_(0) -{ -} - -void -BigcoCell::setThingy(const char *thingy) -{ - thingy_ = thingy; -} - -TimingArcSet * -BigcoCell::makeTimingArcSet(LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs) -{ - size_t set_index = timing_arc_sets_.size(); - TimingArcSet *arc_set = new BigcoTimingArcSet(this, from, to, related_out, - role, attrs, set_index); - timing_arc_sets_.push_back(arc_set); - - if (role == TimingRole::regClkToQ() - || role == TimingRole::latchEnToQ()) { - from->setIsRegClk(true); - to->setIsRegOutput(true); - } - if (role->isTimingCheck()) - from->setIsCheckClk(true); - return arc_set; -} - -//////////////////////////////////////////////////////////////// - -class BigcoTimingGroup : public TimingGroup -{ -public: - BigcoTimingGroup(int line); - const char *frob() const { return frob_; } - void setFrob(const char *frob); - -protected: - const char *frob_; -}; - -BigcoTimingGroup::BigcoTimingGroup(int line) : - TimingGroup(line), - frob_(0) -{ -} - -void -BigcoTimingGroup::setFrob(const char *frob) -{ - frob_ = frob; -} - -//////////////////////////////////////////////////////////////// - -class BigcoTimingArcSet : public TimingArcSet -{ -public: - BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, LibertyPort *to, - LibertyPort *related_out, const TimingRole *role, - TimingArcAttrsPtr attrs, size_t index); - -protected: - const char *frob_; -}; - -BigcoTimingArcSet::BigcoTimingArcSet(LibertyCell *cell, LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - const TimingRole *role, - TimingArcAttrsPtr attrs, size_t index) : - TimingArcSet(cell, from, to, related_out, role, attrs, index) -{ - const char *frob = static_cast(attrs.get())->frob(); - if (frob) - frob_ = stringCopy(frob); -} - -//////////////////////////////////////////////////////////////// - -// Make Bigco objects instead of Liberty objects. -class BigcoLibertyBuilder : public LibertyBuilder -{ -public: - virtual LibertyCell *makeCell(LibertyLibrary *library, std::string_view name, - std::string_view filename); -}; - -LibertyCell * -BigcoLibertyBuilder::makeCell(LibertyLibrary *library, std::string_view name, - std::string_view filename) -{ - std::string name_str(name); - std::string filename_str(filename); - LibertyCell *cell = new BigcoCell(library, name_str.c_str(), filename_str.c_str()); - library->addCell(cell); - return cell; -} - -//////////////////////////////////////////////////////////////// - -// Liberty reader to parse Bigco attributes. -class BigcoLibertyReader : public LibertyReader -{ -public: - BigcoLibertyReader(LibertyBuilder *builder); - -protected: - virtual void visitAttr1(const LibertySimpleAttr *attr); - virtual void visitAttr2(const LibertySimpleAttr *attr); - virtual void beginLibrary(const LibertyGroup *group, - const LibertyGroup *library_group); - virtual TimingGroup *makeTimingGroup(int line); - virtual void beginCell(const LibertyGroup *group, - const LibertyGroup *library_group); -}; - -BigcoLibertyReader::BigcoLibertyReader(LibertyBuilder *builder) : - LibertyReader(builder) -{ -} - -bool -libertyCellRequired(const char *) -{ - // Predicate for cell names. - return true; -} - -// Prune cells from liberty file based on libertyCellRequired predicate. -void -BigcoLibertyReader::beginCell(const LibertyGroup *group, - const LibertyGroup *library_group) -{ - if (group->hasFirstParam() - && libertyCellRequired(group->firstParam().c_str())) - LibertyReader::beginCell(group, library_group); -} - -TimingGroup * -BigcoLibertyReader::makeTimingGroup(int line) -{ - return new BigcoTimingGroup(line); -} - -// Called at the beginning of a library group. -void -BigcoLibertyReader::beginLibrary(const LibertyGroup *group, - const LibertyGroup *library_group) -{ - LibertyReader::beginLibrary(group, library_group); - // Do Bigco stuff here. - printf("Bigco was here.\n"); -} - -void -BigcoLibertyReader::visitAttr1(const LibertySimpleAttr *attr) -{ - const char *thingy = getAttrString(attr); - if (thingy) { - printf("Bigco thingy attribute value is %s.\n", thingy); - if (cell_) - static_cast(cell_)->setThingy(thingy); - } -} - -void -BigcoLibertyReader::visitAttr2(const LibertySimpleAttr *attr) -{ - const char *frob = getAttrString(attr); - if (frob) { - if (timing_) - static_cast(timing_)->setFrob(frob); - } -} - -//////////////////////////////////////////////////////////////// - -// Define a BigcoSta class derived from the Sta class to install the -// new liberty reader/visitor in BigcoSta::makeLibertyReader. -class BigcoSta : public Sta -{ -public: - BigcoSta(); - -protected: - virtual LibertyLibrary *readLibertyFile(std::string_view filename, - bool infer_latches, - Network *network); -}; - -BigcoSta::BigcoSta() : - Sta() -{ -} - -// Replace Sta liberty file reader with Bigco's very own. -LibertyLibrary * -Sta::readLibertyFile(std::string_view filename, - bool infer_latches, - Network *network) -{ - BigcoLibertyBuilder builder; - BigcoLibertyReader reader(&builder); - return reader.readLibertyFile(filename, infer_latches, network); -} diff --git a/liberty/LibertyLex.ll b/liberty/LibertyLex.ll index 22edd6967..6aab5689c 100644 --- a/liberty/LibertyLex.ll +++ b/liberty/LibertyLex.ll @@ -34,7 +34,7 @@ #undef YY_DECL #define YY_DECL \ int \ -sta::LibertyScanner::lex(sta::LibertyParse::semantic_type *const yylval, \ +sta::LibertyScanner::lex(sta::LibertyParse::semantic_type *yylval, \ sta::LibertyParse::location_type *loc) // update location on matching diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc index 446ce30de..ee9ef6c63 100644 --- a/liberty/LibertyParser.cc +++ b/liberty/LibertyParser.cc @@ -24,17 +24,20 @@ #include "LibertyParser.hh" -#include #include +#include #include #include +#include +#include #include "ContainerHelpers.hh" -#include "Zlib.hh" -#include "Report.hh" #include "Error.hh" -#include "StringUtil.hh" +#include "LibertyParse.hh" #include "LibertyScanner.hh" +#include "Report.hh" +#include "StringUtil.hh" +#include "util/gzstream.hh" namespace sta { @@ -235,8 +238,7 @@ LibertyScanner::LibertyScanner(std::istream *stream, stream_(stream), filename_(filename), reader_(reader), - report_(report), - stream_prev_(nullptr) + report_(report) { } @@ -427,7 +429,7 @@ const LibertyGroup * LibertyGroup::findSubgroup(std::string_view type) const { const LibertyGroupSeq &groups = findSubgroups(type); - if (groups.size() >= 1) + if (!groups.empty()) return groups[0]; else return nullptr; @@ -453,7 +455,7 @@ const LibertyComplexAttr * LibertyGroup::findComplexAttr(std::string_view attr_name) const { const LibertyComplexAttrSeq &attrs = findComplexAttrs(attr_name); - if (attrs.size() >= 1) + if (!attrs.empty()) return attrs[0]; else return nullptr; @@ -518,7 +520,7 @@ LibertyGroup::findAttrInt(std::string_view attr_name, //////////////////////////////////////////////////////////////// LibertySimpleAttr::LibertySimpleAttr(std::string &&name, - const LibertyAttrValue value, + LibertyAttrValue value, int line) : name_(std::move(name)), line_(line), @@ -529,7 +531,7 @@ LibertySimpleAttr::LibertySimpleAttr(std::string &&name, //////////////////////////////////////////////////////////////// LibertyComplexAttr::LibertyComplexAttr(std::string &&name, - const LibertyAttrValueSeq values, + LibertyAttrValueSeq values, int line) : name_(std::move(name)), values_(std::move(values)), @@ -542,7 +544,7 @@ LibertyComplexAttr::~LibertyComplexAttr() { deleteContents(values_); } const LibertyAttrValue * LibertyComplexAttr::firstValue() const { - if (values_.size() > 0) + if (!values_.empty()) return values_[0]; else return nullptr; diff --git a/liberty/LibertyParser.hh b/liberty/LibertyParser.hh index de9ec60c1..607e9d716 100644 --- a/liberty/LibertyParser.hh +++ b/liberty/LibertyParser.hh @@ -103,7 +103,6 @@ private: class LibertyAttrValue { public: - LibertyAttrValue() {} LibertyAttrValue(float value); LibertyAttrValue(std::string &&value); bool isString() const; @@ -123,8 +122,8 @@ private: class LibertyGroup { public: - LibertyGroup(const std::string type, - const LibertyAttrValueSeq params, + LibertyGroup(std::string type, + LibertyAttrValueSeq params, int line); ~LibertyGroup(); void clear(); @@ -191,7 +190,7 @@ class LibertySimpleAttr { public: LibertySimpleAttr(std::string &&name, - const LibertyAttrValue value, + LibertyAttrValue value, int line); const std::string &name() const { return name_; } const LibertyAttrValue &value() const { return value_; }; @@ -210,7 +209,7 @@ class LibertyComplexAttr { public: LibertyComplexAttr(std::string &&name, - const LibertyAttrValueSeq values, + LibertyAttrValueSeq values, int line); ~LibertyComplexAttr(); const std::string &name() const { return name_; } @@ -269,8 +268,6 @@ private: class LibertyGroupVisitor { public: - LibertyGroupVisitor() {} - virtual ~LibertyGroupVisitor() {} virtual void begin(const LibertyGroup *group, LibertyGroup *parent_group) = 0; virtual void end(const LibertyGroup *group, @@ -284,4 +281,4 @@ void parseLibertyFile(std::string_view filename, LibertyGroupVisitor *library_visitor, Report *report); -} // namespace +} // namespace sta diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index a76b68934..991cf88e5 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -27,31 +27,40 @@ #include #include #include +#include +#include #include #include +#include +#include "ConcreteLibrary.hh" #include "ContainerHelpers.hh" -#include "Format.hh" -#include "Report.hh" #include "Debug.hh" #include "EnumNameMap.hh" -#include "Units.hh" -#include "Transition.hh" +#include "EquivCells.hh" +#include "Format.hh" #include "FuncExpr.hh" -#include "TimingArc.hh" -#include "TableModel.hh" -#include "LeakagePower.hh" #include "InternalPower.hh" -#include "LinearModel.hh" -#include "Wireload.hh" -#include "EquivCells.hh" #include "LibExprReader.hh" #include "Liberty.hh" #include "LibertyBuilder.hh" +#include "LibertyClass.hh" +#include "LibertyParser.hh" #include "LibertyReaderPvt.hh" -#include "PortDirection.hh" -#include "ParseBus.hh" +#include "LinearModel.hh" +#include "MinMax.hh" #include "Network.hh" +#include "NetworkClass.hh" +#include "ParseBus.hh" +#include "PortDirection.hh" +#include "Sequential.hh" +#include "StringUtil.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingModel.hh" +#include "Transition.hh" +#include "Units.hh" +#include "Wireload.hh" extern int LibertyParse_debug; @@ -73,14 +82,12 @@ readLibertyFile(std::string_view filename, LibertyReader::LibertyReader(std::string_view filename, bool infer_latches, Network *network) : - LibertyGroupVisitor(), filename_(filename), infer_latches_(infer_latches), report_(network->report()), debug_(network->debug()), network_(network), - builder_(debug_, report_), - library_(nullptr) + builder_(debug_, report_) { defineVisitors(); } @@ -386,7 +393,7 @@ LibertyReader::readUnit(std::string_view unit_attr_name, size_t mult_end = units.find_first_not_of("0123456789"); float mult = 1.0F; std::string scale_suffix; - if (mult_end != units.npos) { + if (mult_end != std::string::npos) { std::string unit_mult = units.substr(0, mult_end); scale_suffix = units.substr(mult_end); if (unit_mult == "1") @@ -698,11 +705,11 @@ LibertyReader::readScaleFactors(const LibertyGroup *scale_group, ScaleFactors *scale_factors) { // Skip unknown type. - for (int type_index = 0; type_index < scale_factor_type_count - 1; type_index++) { + for (size_t type_index = 0; type_index < scale_factor_type_count - 1; type_index++) { ScaleFactorType type = static_cast(type_index); const std::string &type_name = scaleFactorTypeName(type); // Skip unknown pvt. - for (int pvt_index = 0; pvt_index < scale_factor_pvt_count - 1; pvt_index++) { + for (size_t pvt_index = 0; pvt_index < scale_factor_pvt_count - 1; pvt_index++) { ScaleFactorPvt pvt = static_cast(pvt_index); const std::string pvt_name = scaleFactorPvtName(pvt); std::string attr_name; @@ -1605,8 +1612,7 @@ LibertyReader::readMinPulseWidth(LibertyCell *cell, } } if (timing_attrs) - builder_.makeTimingArcs(cell, port, port, nullptr, timing_attrs, - port_group->line()); + builder_.makeTimingArcs(cell, port, port, nullptr, timing_attrs); } } @@ -1758,27 +1764,27 @@ LibertyReader::seqPortNames(const LibertyGroup *group, bool &has_size, size_t &size) { - if (group->params().size() == 1) { + has_size = false; + const size_t param_count = group->params().size(); + if (param_count == 1) { // out_port out_name = group->firstParam(); size = 1; - has_size = false; } - if (group->params().size() == 2) { + else if (param_count == 2) { // out_port, out_port_inv out_name = group->firstParam(); out_inv_name = group->secondParam(); size = 1; - has_size = false; } - else if (group->params().size() == 3) { + else if (param_count == 3) { LibertyAttrValue *third_value = group->params()[2]; auto [size_flt, size_valid] = third_value->floatValue(); if (size_valid) { // out_port, out_port_inv, bus_size out_name = group->firstParam(); out_inv_name = group->secondParam(); - size = static_cast(size_flt); + size = static_cast(size_flt); has_size = true; } else { @@ -1837,7 +1843,7 @@ LibertyReader::readCellAttributes(LibertyCell *cell, } readScaleFactors(cell, cell_group); - readLeagageGrouops(cell, cell_group); + readLeakageGrouops(cell, cell_group); readStatetable(cell, cell_group); readModeDefs(cell, cell_group); } @@ -1915,8 +1921,8 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, { for (const LibertyGroup *timing_group : port_group->findSubgroups("timing")) { TimingArcAttrsPtr timing_attrs = std::make_shared(); - readTimingArcAttrs(cell, timing_group, timing_attrs); - makeTimingModels(cell, timing_group, timing_attrs); + readTimingArcAttrs(cell, timing_group, *timing_attrs); + makeTimingModels(cell, timing_group, *timing_attrs); LibertyPort *related_output_port = findLibertyPort(cell, timing_group, "related_output_pin"); @@ -1929,7 +1935,7 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, to_port->direction()->isInput()) warn(1209, timing_group, "combinational timing to an input port."); - if (related_port_names.size() || related_bus_names.size()) { + if (!related_port_names.empty() || !related_bus_names.empty()) { for (const std::string &from_port_name : related_port_names) { debugPrint(debug_, "liberty", 2, " timing {} -> {}", from_port_name, to_port->name()); @@ -1949,8 +1955,7 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, || timing_type == TimingType::max_clock_tree_path)) 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()); + makeTimingArcs(cell, to_port, related_output_port, timing_attrs); } } } @@ -1958,14 +1963,14 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, void LibertyReader::readTimingArcAttrs(LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs) + TimingArcAttrs &timing_attrs) { readTimingSense(timing_group, timing_attrs); readTimingType(timing_group, timing_attrs); readTimingWhen(cell, timing_group, timing_attrs); readTimingMode(timing_group, timing_attrs); readGroupAttrFloat("ocv_arc_depth", timing_group, - [timing_attrs](float v) { timing_attrs->setOcvArcDepth(v); }); + [&timing_attrs](float v) { timing_attrs.setOcvArcDepth(v); }); } void @@ -1983,17 +1988,17 @@ LibertyReader::readGroupAttrFloat(std::string_view attr_name, void LibertyReader::readTimingSense(const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs) + TimingArcAttrs &timing_attrs) { const LibertySimpleAttr *sense_attr = timing_group->findSimpleAttr("timing_sense"); if (sense_attr) { const std::string &sense_name = sense_attr->stringValue(); if (sense_name == "non_unate") - timing_attrs->setTimingSense(TimingSense::non_unate); + timing_attrs.setTimingSense(TimingSense::non_unate); else if (sense_name == "positive_unate") - timing_attrs->setTimingSense(TimingSense::positive_unate); + timing_attrs.setTimingSense(TimingSense::positive_unate); else if (sense_name == "negative_unate") - timing_attrs->setTimingSense(TimingSense::negative_unate); + timing_attrs.setTimingSense(TimingSense::negative_unate); else warn(1245, timing_group, "unknown timing_sense {}.", sense_name); } @@ -2001,7 +2006,7 @@ LibertyReader::readTimingSense(const LibertyGroup *timing_group, void LibertyReader::readTimingType(const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs) + TimingArcAttrs &timing_attrs) { TimingType type = TimingType::combinational; const LibertySimpleAttr *type_attr = timing_group->findSimpleAttr("timing_type"); @@ -2013,43 +2018,43 @@ LibertyReader::readTimingType(const LibertyGroup *timing_group, type = TimingType::combinational; } } - timing_attrs->setTimingType(type); + timing_attrs.setTimingType(type); } void LibertyReader::readTimingWhen(const LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs) + TimingArcAttrs &timing_attrs) { const LibertySimpleAttr *when_attr = timing_group->findSimpleAttr("when"); if (when_attr) { const std::string &when = when_attr->stringValue(); if (!when.empty()) { FuncExpr *when_expr = parseFunc(when, "when", cell, when_attr->line()); - timing_attrs->setCond(when_expr); + timing_attrs.setCond(when_expr); } } const LibertySimpleAttr *cond_attr = timing_group->findSimpleAttr("sdf_cond"); if (cond_attr) { const std::string &cond = cond_attr->stringValue(); - timing_attrs->setSdfCond(cond); + timing_attrs.setSdfCond(cond); } cond_attr = timing_group->findSimpleAttr("sdf_cond_start"); if (cond_attr) { const std::string &cond = cond_attr->stringValue(); - timing_attrs->setSdfCondStart(cond); + timing_attrs.setSdfCondStart(cond); } cond_attr = timing_group->findSimpleAttr("sdf_cond_end"); if (cond_attr) { const std::string &cond = cond_attr->stringValue(); - timing_attrs->setSdfCondEnd(cond); + timing_attrs.setSdfCondEnd(cond); } } void LibertyReader::readTimingMode(const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs) + TimingArcAttrs &timing_attrs) { const LibertyComplexAttrSeq &mode_attrs = timing_group->findComplexAttrs("mode"); if (!mode_attrs.empty()) { @@ -2058,13 +2063,13 @@ LibertyReader::readTimingMode(const LibertyGroup *timing_group, if (mode_values.size() == 2) { LibertyAttrValue *value = mode_values[0]; if (value->isString()) - timing_attrs->setModeName(value->stringValue()); + timing_attrs.setModeName(value->stringValue()); else warn(1248, mode_attr, "mode name is not a string."); value = mode_values[1]; if (value->isString()) - timing_attrs->setModeValue(value->stringValue()); + timing_attrs.setModeValue(value->stringValue()); else warn(1246, mode_attr, "mode value is not a string."); } @@ -2076,7 +2081,7 @@ LibertyReader::readTimingMode(const LibertyGroup *timing_group, void LibertyReader::makeTimingModels(LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs) + TimingArcAttrs &timing_attrs) { switch (cell->libertyLibrary()->delayModelType()) { case DelayModelType::cmos_linear: @@ -2096,7 +2101,7 @@ LibertyReader::makeTimingModels(LibertyCell *cell, void LibertyReader::makeLinearModels(LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs) + TimingArcAttrs &timing_attrs) { LibertyLibrary *library = cell->libertyLibrary(); for (const RiseFall *rf : RiseFall::range()) { @@ -2110,7 +2115,7 @@ LibertyReader::makeLinearModels(LibertyCell *cell, library->defaultIntrinsic(rf, intr, intr_exists); TimingModel *model = nullptr; if (intr_exists) { - if (timingTypeIsCheck(timing_attrs->timingType())) + if (timingTypeIsCheck(timing_attrs.timingType())) model = new CheckLinearModel(cell, intr); else { std::string res_attr_name = sta::format("{}_resistance", rf->to_string()); @@ -2124,7 +2129,7 @@ LibertyReader::makeLinearModels(LibertyCell *cell, res, res_exists); model = new GateLinearModel(cell, intr, res); } - timing_attrs->setModel(rf, model); + timing_attrs.setModel(rf, model); } } } @@ -2132,7 +2137,7 @@ LibertyReader::makeLinearModels(LibertyCell *cell, void LibertyReader::makeTableModels(LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs) + TimingArcAttrs &timing_attrs) { bool found_model = false; for (const RiseFall *rf : RiseFall::range()) { @@ -2169,11 +2174,11 @@ LibertyReader::makeTableModels(LibertyCell *cell, ReceiverModelPtr receiver_model = readReceiverCapacitance(timing_group, rf); OutputWaveforms *output_waveforms = readOutputWaveforms(timing_group, rf); - timing_attrs->setModel(rf, new GateTableModel(cell, delay_models, - slew_models, - receiver_model, - output_waveforms)); - TimingType timing_type = timing_attrs->timingType(); + timing_attrs.setModel(rf, new GateTableModel(cell, delay_models, + slew_models, + std::move(receiver_model), + output_waveforms)); + TimingType timing_type = timing_attrs.timingType(); if (isGateTimingType(timing_type)) { if (slew_model == nullptr) warn(1210, timing_group, "missing {}_transition.", rf->name()); @@ -2185,7 +2190,7 @@ LibertyReader::makeTableModels(LibertyCell *cell, std::string constraint_attr_name = sta::format("{}_constraint", rf->to_string()); ScaleFactorType scale_factor_type = - timingTypeScaleFactorType(timing_attrs->timingType()); + timingTypeScaleFactorType(timing_attrs.timingType()); TableModel *check_model = readTableModel(timing_group, constraint_attr_name, rf, TableTemplateType::delay, @@ -2199,7 +2204,7 @@ LibertyReader::makeTableModels(LibertyCell *cell, sta::format("ocv_mean_shift_{}_constraiint", rf->to_string()), sta::format("ocv_skewness_{}_constraiint", rf->to_string()), rf, check_models, CheckTableModel::checkAxes); - timing_attrs->setModel(rf, new CheckTableModel(cell, check_models)); + timing_attrs.setModel(rf, new CheckTableModel(cell, check_models)); found_model = true; } } @@ -2232,7 +2237,7 @@ LibertyReader::readTableModel(const LibertyGroup *timing_group, TableTemplateType template_type, float scale, ScaleFactorType scale_factor_type, - const std::function check_axes) + const std::function &check_axes) { const LibertyGroup *table_group = timing_group->findSubgroup(table_group_name); if (table_group) @@ -2249,7 +2254,7 @@ LibertyReader::readLvfModels(const LibertyGroup *timing_group, const std::string &skewness_group_name, const RiseFall *rf, TableModels *table_models, - const std::function check_axes) + const std::function &check_axes) { TableModelsEarlyLate sigmas = readEarlyLateTableModels(timing_group, @@ -2295,7 +2300,7 @@ LibertyReader::readEarlyLateTableModels(const LibertyGroup *timing_group, TableTemplateType template_type, float scale, ScaleFactorType scale_factor_type, - const std::function check_axes) + const std::function &check_axes) { TableModelsEarlyLate models{}; for (const LibertyGroup *table_group : timing_group->findSubgroups(table_group_name)){ @@ -2578,41 +2583,41 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, LibertyPort *to_port, LibertyPort *related_out_port, bool one_to_one, - TimingArcAttrsPtr timing_attrs, + const TimingArcAttrsPtr &timing_attrs, int timing_line) { PortNameBitIterator from_port_iter(cell, from_port_name, this, timing_line); if (from_port_iter.size() == 1 && !to_port->hasMembers()) { // one -> one - if (from_port_iter.hasNext()) { - LibertyPort *from_port = from_port_iter.next(); + if (from_port_iter.PortNameBitIterator::hasNext()) { + LibertyPort *from_port = from_port_iter.PortNameBitIterator::next(); if (from_port->direction()->isOutput()) warn(1212, timing_line, "timing group from output port."); builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, - timing_attrs, timing_line); + timing_attrs); } } else if (from_port_iter.size() > 1 && !to_port->hasMembers()) { // bus -> one - while (from_port_iter.hasNext()) { - LibertyPort *from_port = from_port_iter.next(); + while (from_port_iter.PortNameBitIterator::hasNext()) { + LibertyPort *from_port = from_port_iter.PortNameBitIterator::next(); if (from_port->direction()->isOutput()) warn(1213, timing_line, "timing group from output port."); builder_.makeTimingArcs(cell, from_port, to_port, related_out_port, - timing_attrs, timing_line); + timing_attrs); } } else if (from_port_iter.size() == 1 && to_port->hasMembers()) { // one -> bus - if (from_port_iter.hasNext()) { - LibertyPort *from_port = from_port_iter.next(); + if (from_port_iter.PortNameBitIterator::hasNext()) { + LibertyPort *from_port = from_port_iter.PortNameBitIterator::next(); if (from_port->direction()->isOutput()) 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(); builder_.makeTimingArcs(cell, from_port, to_port_bit, related_out_port, - timing_attrs, timing_line); + timing_attrs); } } } @@ -2631,33 +2636,31 @@ LibertyReader::makeTimingArcs(LibertyCell *cell, // align to/from iterators for one-to-one mapping while (from_size > to_size) { from_size--; - from_port_iter.next(); + from_port_iter.PortNameBitIterator::next(); } while (to_size > from_size) { to_size--; to_port_iter.next(); } // make timing arcs - while (from_port_iter.hasNext() && to_port_iter.hasNext()) { - LibertyPort *from_port_bit = from_port_iter.next(); + while (from_port_iter.PortNameBitIterator::hasNext() && to_port_iter.hasNext()) { + LibertyPort *from_port_bit = from_port_iter.PortNameBitIterator::next(); LibertyPort *to_port_bit = to_port_iter.next(); if (from_port_bit->direction()->isOutput()) 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); + related_out_port, timing_attrs); } } else { // cross product - while (from_port_iter.hasNext()) { - LibertyPort *from_port_bit = from_port_iter.next(); + while (from_port_iter.PortNameBitIterator::hasNext()) { + LibertyPort *from_port_bit = from_port_iter.PortNameBitIterator::next(); LibertyPortMemberIterator to_port_iter(to_port); while (to_port_iter.hasNext()) { LibertyPort *to_port_bit = to_port_iter.next(); builder_.makeTimingArcs(cell, from_port_bit, to_port_bit, - related_out_port, timing_attrs, - timing_line); + related_out_port, timing_attrs); } } } @@ -2668,28 +2671,25 @@ void LibertyReader::makeTimingArcs(LibertyCell *cell, LibertyPort *to_port, LibertyPort *related_out_port, - TimingArcAttrsPtr timing_attrs, - int timing_line) + const TimingArcAttrsPtr &timing_attrs) { if (to_port->hasMembers()) { LibertyPortMemberIterator bit_iter(to_port); while (bit_iter.hasNext()) { LibertyPort *to_port_bit = bit_iter.next(); builder_.makeTimingArcs(cell, nullptr, to_port_bit, - related_out_port, timing_attrs, - timing_line); + related_out_port, timing_attrs); } } else builder_.makeTimingArcs(cell, nullptr, to_port, - related_out_port, timing_attrs, - timing_line); + related_out_port, timing_attrs); } //////////////////////////////////////////////////////////////// void -LibertyReader::readLeagageGrouops(LibertyCell *cell, +LibertyReader::readLeakageGrouops(LibertyCell *cell, const LibertyGroup *cell_group) { for (const LibertyGroup *leak_group : cell_group->findSubgroups("leakage_power")) { @@ -3553,12 +3553,7 @@ PortNameBitIterator::PortNameBitIterator(LibertyCell *cell, int line) : cell_(cell), visitor_(visitor), - line_(line), - port_(nullptr), - bit_iterator_(nullptr), - range_bus_port_(nullptr), - range_name_next_(nullptr), - size_(0) + line_(line) { init(port_name); } @@ -3703,4 +3698,4 @@ OutputWaveform::releaseCurrents() return currents_.release(); } -} // namespace +} // namespace sta diff --git a/liberty/LibertyReader.hh b/liberty/LibertyReader.hh index 27fed80ae..030c42e82 100644 --- a/liberty/LibertyReader.hh +++ b/liberty/LibertyReader.hh @@ -36,4 +36,4 @@ readLibertyFile(std::string_view filename, bool infer_latches, Network *network); -} // namespace +} // namespace sta diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 28683aefe..cd4a16274 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -69,20 +69,22 @@ public: LibertyReader(std::string_view filename, bool infer_latches, Network *network); - virtual LibertyLibrary *readLibertyFile(std::string_view filename); + LibertyLibrary *readLibertyFile(std::string_view filename); LibertyLibrary *library() { return library_; } const LibertyLibrary *library() const { return library_; } - virtual void beginLibrary(const LibertyGroup *group, - LibertyGroup *library_group); - virtual void endLibrary(const LibertyGroup *group, - LibertyGroup *null_group); - virtual void visitAttr(const LibertySimpleAttr *attr); - virtual void visitAttr(const LibertyComplexAttr *attr); - virtual void visitVariable(LibertyVariable *var); - // Extension points for custom attributes (e.g. LibertyExt). - virtual void visitAttr1(const LibertySimpleAttr *) {} - virtual void visitAttr2(const LibertySimpleAttr *) {} + void beginLibrary(const LibertyGroup *group, + LibertyGroup *library_group); + void endLibrary(const LibertyGroup *group, + LibertyGroup *null_group); + void visitAttr(const LibertySimpleAttr *attr) override; + void visitAttr(const LibertyComplexAttr *attr) override; + void visitVariable(LibertyVariable *var) override; + + void begin(const LibertyGroup *group, + LibertyGroup *parent_group) override; + void end(const LibertyGroup *group, + LibertyGroup *parent_group) override; void endCell(const LibertyGroup *group, LibertyGroup *library_group); @@ -108,11 +110,6 @@ public: std::string_view name_attr); protected: - virtual void begin(const LibertyGroup *group, - LibertyGroup *library_group); - virtual void end(const LibertyGroup *group, - LibertyGroup *library_group); - // Library gruops. void makeLibrary(const LibertyGroup *library_group); void readLibraryAttributes(const LibertyGroup *library_group); @@ -146,7 +143,7 @@ protected: void readScaleFactors(const LibertyGroup *scale_group, ScaleFactors *scale_factors); void readOcvDerateFactors(LibertyCell *cell, - const LibertyGroup *library_group); + const LibertyGroup *parent_group); void readDefaultOcvDerateGroup(const LibertyGroup *library_group); void readNormalizedDriverWaveform(const LibertyGroup *library_group); void readSlewDegradations(const LibertyGroup *library_group); @@ -246,7 +243,7 @@ protected: TableTemplateType template_type, float scale, ScaleFactorType scale_factor_type, - const std::function check_axes); + const std::function &check_axes); TableModelsEarlyLate readEarlyLateTableModels(const LibertyGroup *timing_group, std::string_view table_group_name, @@ -254,7 +251,7 @@ protected: TableTemplateType template_type, float scale, ScaleFactorType scale_factor_type, - const std::function check_axes); + const std::function &check_axes); ReceiverModelPtr readReceiverCapacitance(const LibertyGroup *timing_group, const RiseFall *rf); void readReceiverCapacitance(const LibertyGroup *timing_group, @@ -280,13 +277,13 @@ protected: float scale); void makeTimingModels(LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs); + TimingArcAttrs &timing_attrs); void makeLinearModels(LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs); + TimingArcAttrs &timing_attrs); void makeTableModels(LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs); + TimingArcAttrs &timing_attrs); void readLvfModels(const LibertyGroup *timing_group, const std::string &sigma_group_name, const std::string &std_dev_group_name, @@ -294,7 +291,7 @@ protected: const std::string &skewness_group_name, const RiseFall *rf, TableModels *table_models, - const std::function check_axes); + const std::function &check_axes); TableAxisPtr makeTableAxis(const LibertyGroup *table_group, std::string_view index_attr_name, @@ -305,16 +302,16 @@ protected: float scale = 1.0F); void readTimingArcAttrs(LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs); + TimingArcAttrs &timing_attrs); void readTimingSense(const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs); + TimingArcAttrs &timing_attrs); void readTimingType(const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs); + TimingArcAttrs &timing_attrs); void readTimingWhen(const LibertyCell *cell, const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs); + TimingArcAttrs &timing_attrs); void readTimingMode(const LibertyGroup *timing_group, - TimingArcAttrsPtr timing_attrs); + TimingArcAttrs &timing_attrs); void makePortFuncs(LibertyCell *cell, const LibertyPortSeq &ports, const LibertyGroup *port_group); @@ -356,19 +353,18 @@ protected: LibertyPort *to_port, LibertyPort *related_out_port, bool one_to_one, - TimingArcAttrsPtr timing_attrs, + const TimingArcAttrsPtr &timing_attrs, int timing_line); void makeTimingArcs(LibertyCell *cell, LibertyPort *to_port, LibertyPort *related_out_port, - TimingArcAttrsPtr timing_attrs, - int timing_line); + const TimingArcAttrsPtr &timing_attrs); void readInternalPowerGroups(LibertyCell *cell, const LibertyPortSeq &ports, const LibertyGroup *port_group); - void readLeagageGrouops(LibertyCell *cell, - const LibertyGroup *port_group); + void readLeakageGrouops(LibertyCell *cell, + const LibertyGroup *cell_group); void readCellAttributes(LibertyCell *cell, const LibertyGroup *cell_group); @@ -524,7 +520,7 @@ protected: Network *network_; LibertyBuilder builder_; LibertyVariableMap var_map_; - LibertyLibrary *library_; + LibertyLibrary *library_{nullptr}; LibraryGroupVisitorMap group_begin_map_; LibraryGroupVisitorMap group_end_map_; @@ -554,7 +550,7 @@ public: std::string_view port_name, LibertyReader *visitor, int line); - ~PortNameBitIterator(); + ~PortNameBitIterator() override; bool hasNext() override; LibertyPort *next() override; unsigned size() const { return size_; } @@ -566,22 +562,22 @@ protected: LibertyCell *cell_; LibertyReader *visitor_; int line_; - LibertyPort *port_; - LibertyPortMemberIterator *bit_iterator_; - LibertyPort *range_bus_port_; + LibertyPort *port_{nullptr}; + LibertyPortMemberIterator *bit_iterator_{nullptr}; + LibertyPort *range_bus_port_{nullptr}; std::string range_bus_name_; - LibertyPort *range_name_next_; - int range_from_; - int range_to_; - int range_bit_; - unsigned size_; + LibertyPort *range_name_next_{nullptr}; + int range_from_{0}; + int range_to_{0}; + int range_bit_{0}; + unsigned size_{0}; }; class OutputWaveform { public: - OutputWaveform(float axis_value1, - float axis_value2, + OutputWaveform(float slew, + float cap, Table *currents, float reference_time); float slew() const { return slew_; } @@ -597,4 +593,4 @@ private: float reference_time_; }; -} // namespace +} // namespace sta diff --git a/liberty/LibertyScanner.hh b/liberty/LibertyScanner.hh index bb42f4c36..06c9ef5ef 100644 --- a/liberty/LibertyScanner.hh +++ b/liberty/LibertyScanner.hh @@ -48,9 +48,7 @@ public: std::string_view filename, LibertyParser *reader, Report *report); - virtual ~LibertyScanner() {} - - virtual int lex(LibertyParse::semantic_type *const yylval, + virtual int lex(LibertyParse::semantic_type *yylval, LibertyParse::location_type *yylloc); // YY_DECL defined in LibertyLex.ll // Method body created by flex in LibertyLex.cc @@ -71,7 +69,7 @@ private: // Previous lex state for include files. std::string filename_prev_; - std::istream *stream_prev_; + std::istream *stream_prev_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 79d4f7805..ab8b81776 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -24,11 +24,16 @@ #include "LibertyWriter.hh" -#include #include +#include #include +#include +#include "Error.hh" #include "Format.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Transition.hh" #include "Units.hh" #include "FuncExpr.hh" #include "PortDirection.hh" @@ -494,6 +499,8 @@ LibertyWriter::writeTableModel(const TableModel *model) case 3: report_->error(1342, "3 axis table models not supported."); break; + default: + break; } } diff --git a/liberty/LinearModel.cc b/liberty/LinearModel.cc index 4d96138e6..d64bfc276 100644 --- a/liberty/LinearModel.cc +++ b/liberty/LinearModel.cc @@ -24,6 +24,14 @@ #include "LinearModel.hh" +#include +#include + +#include "Delay.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "PocvMode.hh" +#include "TimingModel.hh" #include "Units.hh" #include "Liberty.hh" @@ -139,4 +147,4 @@ CheckLinearModel::setIsScaled(bool) { } -} // namespace +} // namespace sta diff --git a/liberty/Sequential.cc b/liberty/Sequential.cc index 5106f11cc..0fffdbe2a 100644 --- a/liberty/Sequential.cc +++ b/liberty/Sequential.cc @@ -25,6 +25,8 @@ #include "Sequential.hh" #include "FuncExpr.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" namespace sta { @@ -119,4 +121,4 @@ StatetableRow::StatetableRow(StateInputValues &input_values, { } -} // namespace +} // namespace sta diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 676d29afb..5cdd92f2e 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -24,14 +24,25 @@ #include "TableModel.hh" +#include #include +#include +#include #include +#include +#include "Delay.hh" #include "Error.hh" -#include "EnumNameMap.hh" #include "ContainerHelpers.hh" -#include "Units.hh" +#include "EnumNameMap.hh" +#include "Format.hh" +#include "LibertyClass.hh" #include "Liberty.hh" +#include "MinMax.hh" +#include "PocvMode.hh" +#include "TimingModel.hh" +#include "Transition.hh" +#include "Units.hh" namespace sta { @@ -61,7 +72,7 @@ GateTableModel::GateTableModel(LibertyCell *cell, GateTimingModel(cell), delay_models_(delay_models), slew_models_(slew_models), - receiver_model_(receiver_model), + receiver_model_(std::move(receiver_model)), output_waveforms_(output_waveforms) { } @@ -77,8 +88,6 @@ GateTableModel::GateTableModel(LibertyCell *cell, { } -GateTableModel::~GateTableModel() = default; - const TableModel * GateTableModel::delayModel() const { @@ -115,8 +124,7 @@ GateTableModel::gateDelay(const Pvt *pvt, if (slew_models_ && slew_models_->model()) { drvr_slew = findValue(pvt, slew_models_->model(), in_slew, load_cap, 0.0); // Clip negative slews to zero. - if (drvr_slew < 0.0) - drvr_slew = 0.0; + drvr_slew = std::max(drvr_slew, 0.0F); } else drvr_slew = 0.0; @@ -358,8 +366,7 @@ GateTableModel::maxCapSlew(float in_slew, slew = 0.0; } // Clip negative slews to zero. - if (slew < 0.0) - slew = 0.0; + slew = std::max(slew, 0.0F); } float @@ -410,8 +417,6 @@ GateTableModel::checkAxis(const TableAxis *axis) //////////////////////////////////////////////////////////////// -ReceiverModel::~ReceiverModel() = default; - void ReceiverModel::setCapacitanceModel(TableModel table_model, size_t segment, @@ -450,8 +455,6 @@ CheckTableModel::CheckTableModel(LibertyCell *cell, { } -CheckTableModel::~CheckTableModel() = default; - const TableModel * CheckTableModel::checkModel() const { @@ -760,9 +763,9 @@ TableModel::TableModel(TablePtr table, TableTemplate *tbl_template, ScaleFactorType scale_factor_type, const RiseFall *rf) : - table_(table), + table_(std::move(table)), tbl_template_(tbl_template), - scale_factor_type_(int(scale_factor_type)), + scale_factor_type_(static_cast(scale_factor_type)), rf_index_(rf->index()), is_scaled_(false) { @@ -783,7 +786,7 @@ TableModel::scaleFactorType() const void TableModel::setScaleFactorType(ScaleFactorType type) { - scale_factor_type_ = int(type); + scale_factor_type_ = static_cast(type); } void @@ -950,7 +953,7 @@ Table::Table(FloatSeq *values, order_(1), value_(0.0), values1_(std::move(*values)), - axis1_(axis1) + axis1_(std::move(axis1)) { delete values; } @@ -960,7 +963,7 @@ Table::Table(FloatSeq &&values, order_(1), value_(0.0), values1_(std::move(values)), - axis1_(axis1) + axis1_(std::move(axis1)) { } @@ -970,8 +973,8 @@ Table::Table(FloatTable &&values, order_(2), value_(0.0), values_table_(std::move(values)), - axis1_(axis1), - axis2_(axis2) + axis1_(std::move(axis1)), + axis2_(std::move(axis2)) { } @@ -982,36 +985,27 @@ Table::Table(FloatTable &&values, order_(3), value_(0.0), values_table_(std::move(values)), - axis1_(axis1), - axis2_(axis2), - axis3_(axis3) + axis1_(std::move(axis1)), + axis2_(std::move(axis2)), + axis3_(std::move(axis3)) { } -Table::Table(Table &&table) : +Table::Table(Table &&table) noexcept : order_(table.order_), value_(table.value_), values1_(std::move(table.values1_)), values_table_(std::move(table.values_table_)), - axis1_(table.axis1_), - axis2_(table.axis2_), - axis3_(table.axis3_) + axis1_(std::move(table.axis1_)), + axis2_(std::move(table.axis2_)), + axis3_(std::move(table.axis3_)) { } -Table::Table(const Table &table) : - order_(table.order_), - value_(table.value_), - values1_(table.values1_), - values_table_(table.values_table_), - axis1_(table.axis1_), - axis2_(table.axis2_), - axis3_(table.axis3_) -{ -} +Table::Table(const Table &table) = default; Table & -Table::operator=(Table &&table) +Table::operator=(Table &&table) noexcept { if (this != &table) { order_ = table.order_; @@ -1788,12 +1782,11 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, const RiseFall *rf, Table1Seq ¤t_waveforms, Table ref_times) : - slew_axis_(slew_axis), - cap_axis_(cap_axis), + slew_axis_(std::move(slew_axis)), + cap_axis_(std::move(cap_axis)), rf_(rf), current_waveforms_(current_waveforms), - ref_times_(std::move(ref_times)), - vdd_(0.0) + ref_times_(std::move(ref_times)) { } @@ -2211,7 +2204,7 @@ OutputWaveforms::finalResistance() DriverWaveform::DriverWaveform(std::string name, TablePtr waveforms) : name_(std::move(name)), - waveforms_(waveforms) + waveforms_(std::move(waveforms)) { } diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index a254b7cdc..ae99b043d 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -22,16 +22,27 @@ // // This notice may not be removed or altered from any source distribution. -#include "TimingModel.hh" +#include "TimingArc.hh" + +#include +#include +#include +#include +#include #include "ContainerHelpers.hh" +#include "Delay.hh" #include "EnumNameMap.hh" +#include "Error.hh" #include "FuncExpr.hh" -#include "TimingRole.hh" +#include "LibertyClass.hh" #include "Liberty.hh" -#include "TimingArc.hh" -#include "TableModel.hh" +#include "MinMax.hh" #include "Sdc.hh" +#include "TableModel.hh" +#include "TimingModel.hh" +#include "TimingRole.hh" +#include "Transition.hh" namespace sta { @@ -88,34 +99,34 @@ TimingArcAttrs::setCond(FuncExpr *cond) } void -TimingArcAttrs::setSdfCond(std::string cond) +TimingArcAttrs::setSdfCond(std::string_view cond) { - sdf_cond_ = std::move(cond); + sdf_cond_ = cond; sdf_cond_start_ = sdf_cond_end_ = sdf_cond_; } void -TimingArcAttrs::setSdfCondStart(std::string cond) +TimingArcAttrs::setSdfCondStart(std::string_view cond) { - sdf_cond_start_ = std::move(cond); + sdf_cond_start_ = cond; } void -TimingArcAttrs::setSdfCondEnd(std::string cond) +TimingArcAttrs::setSdfCondEnd(std::string_view cond) { - sdf_cond_end_ = std::move(cond); + sdf_cond_end_ = cond; } void -TimingArcAttrs::setModeName(std::string name) +TimingArcAttrs::setModeName(std::string_view name) { - mode_name_ = std::move(name); + mode_name_ = name; } void -TimingArcAttrs::setModeValue(std::string value) +TimingArcAttrs::setModeValue(std::string_view value) { - mode_value_ = std::move(value); + mode_value_ = value; } TimingModel * @@ -176,7 +187,7 @@ TimingArcSet::TimingArcSet(LibertyCell *, to_(to), related_out_(related_out), role_(role), - attrs_(attrs), + attrs_(std::move(attrs)), is_cond_default_(false), index_(index), from_arc1_{nullptr, nullptr}, @@ -191,7 +202,7 @@ TimingArcSet::TimingArcSet(const TimingRole *role, to_(nullptr), related_out_(nullptr), role_(role), - attrs_(attrs), + attrs_(std::move(attrs)), is_cond_default_(false), index_(0), from_arc1_{nullptr, nullptr}, @@ -235,7 +246,8 @@ TimingArcSet::addTimingArc(TimingArc *arc) { size_t arc_index = arcs_.size(); // Rise/fall to rise/fall. - if (arc_index > RiseFall::index_count * RiseFall::index_count) + if (arc_index > static_cast(RiseFall::index_count) + * RiseFall::index_count) criticalError(243, "timing arc max index exceeded\n"); arcs_.push_back(arc); @@ -531,8 +543,7 @@ TimingArc::TimingArc(TimingArcSet *set, set_(set), from_rf_(from_rf), to_rf_(to_rf), - model_(model), - scaled_models_(nullptr) + model_(model) { index_ = set->addTimingArc(this); } @@ -633,10 +644,10 @@ TimingArc::setIndex(size_t index) } const TimingArc * -TimingArc::sceneArc(int ap_index) const +TimingArc::sceneArc(size_t lib_ap_index) const { - if (ap_index < static_cast(scene_arcs_.size())) { - TimingArc *scene_arc = scene_arcs_[ap_index]; + if (lib_ap_index < scene_arcs_.size()) { + TimingArc *scene_arc = scene_arcs_[lib_ap_index]; if (scene_arc) return scene_arc; } @@ -645,11 +656,11 @@ TimingArc::sceneArc(int ap_index) const void TimingArc::setSceneArc(TimingArc *scene_arc, - int ap_index) + size_t lib_ap_index) { - if (ap_index >= static_cast(scene_arcs_.size())) - scene_arcs_.resize(ap_index + 1); - scene_arcs_[ap_index] = scene_arc; + if (lib_ap_index >= scene_arcs_.size()) + scene_arcs_.resize(lib_ap_index + 1); + scene_arcs_[lib_ap_index] = scene_arc; } //////////////////////////////////////////////////////////////// @@ -845,4 +856,4 @@ timingTypeScaleFactorType(TimingType type) return ScaleFactorType::unknown; } -} // namespace +} // namespace sta diff --git a/liberty/TimingModel.cc b/liberty/TimingModel.cc index 3acc7b95b..5e31059ac 100644 --- a/liberty/TimingModel.cc +++ b/liberty/TimingModel.cc @@ -24,6 +24,8 @@ #include "TimingModel.hh" +#include "LibertyClass.hh" + namespace sta { GateTimingModel::GateTimingModel(LibertyCell *cell) : @@ -36,4 +38,4 @@ CheckTimingModel::CheckTimingModel(LibertyCell *cell) : { } -} // namespace +} // namespace sta diff --git a/liberty/TimingRole.cc b/liberty/TimingRole.cc index ace8d7dec..672a6498a 100644 --- a/liberty/TimingRole.cc +++ b/liberty/TimingRole.cc @@ -24,6 +24,8 @@ #include "TimingRole.hh" +#include "MinMax.hh" + namespace sta { TimingRoleMap TimingRole::timing_roles_; @@ -184,4 +186,4 @@ TimingRole::less(const TimingRole *role1, return role1->index() < role2->index(); } -} // namespace +} // namespace sta diff --git a/liberty/Units.cc b/liberty/Units.cc index 6e5a4e34d..6d1c3f061 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -57,15 +57,6 @@ Unit::setScaleAbbrevSuffix() scale_abbrev_suffix_ = scaleAbbreviation() + suffix_; } -void -Unit::operator=(const Unit &unit) -{ - scale_ = unit.scale_; - suffix_ = unit.suffix_; - scale_abbrev_suffix_ = unit.scale_abbrev_suffix_; - digits_ = unit.digits_; -} - double Unit::staToUser(double value) { @@ -213,7 +204,7 @@ Units::find(std::string_view unit_name) return nullptr; } -void +Units & Units::operator=(const Units &units) { time_unit_ = *units.timeUnit(); @@ -224,6 +215,7 @@ Units::operator=(const Units &units) power_unit_ = *units.powerUnit(); distance_unit_ = *units.distanceUnit(); scalar_unit_ = *units.scalarUnit(); + return *this; } -} // namespace +} // namespace sta diff --git a/liberty/Wireload.cc b/liberty/Wireload.cc index 1c30df104..e416ebb33 100644 --- a/liberty/Wireload.cc +++ b/liberty/Wireload.cc @@ -25,8 +25,13 @@ #include "Wireload.hh" #include +#include +#include +#include +#include -#include "StringUtil.hh" +#include "ContainerHelpers.hh" +#include "LibertyClass.hh" #include "Liberty.hh" namespace sta { @@ -124,8 +129,7 @@ Wireload::findWireload(float fanout, if (fanout < fanout0) { // Extrapolate from lowest fanout entry. length = fanout_lengths_[0]->second - (fanout0 - fanout) * slope_; - if (length < 0) - length = 0; + length = std::max(length, 0.0F); } else if (fanout == fanout0) length = fanout_lengths_[0]->second; @@ -304,4 +308,4 @@ stringWireloadMode(std::string_view wire_load_mode) return WireloadMode::unknown; } -} // namespace +} // namespace sta diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index 3864239c7..3273cb694 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -351,8 +351,8 @@ BusPort::addBusBit(ConcretePort *port, } void -ConcreteCell::groupBusPorts(const char bus_brkt_left, - const char bus_brkt_right, +ConcreteCell::groupBusPorts(char bus_brkt_left, + char bus_brkt_right, std::function port_msb_first) { const char bus_brkts_left[2]{bus_brkt_left, '\0'}; @@ -625,4 +625,4 @@ ConcreteCellPortBitIterator::findNext() next_ = nullptr; } -} // namespace +} // namespace sta diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index 44cc8a0ac..dba9d2866 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -2125,4 +2125,4 @@ ConcreteBindingTbl::ensureBinding(Net *proto_net, return net; } -} // namespace +} // namespace sta diff --git a/network/HpinDrvrLoad.cc b/network/HpinDrvrLoad.cc index ab76900d4..032b0ff35 100644 --- a/network/HpinDrvrLoad.cc +++ b/network/HpinDrvrLoad.cc @@ -326,4 +326,4 @@ HpinDrvrLoadLess::operator()(const HpinDrvrLoad *drvr_load1, return load1 < load2; } -} // namespace +} // namespace sta diff --git a/network/Network.cc b/network/Network.cc index ca1f085dc..0252c801c 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -2040,4 +2040,4 @@ NetSet::NetSet(const Network *network) : { } -} // namespace +} // namespace sta diff --git a/network/NetworkCmp.cc b/network/NetworkCmp.cc index cda43af73..2d75bb01f 100644 --- a/network/NetworkCmp.cc +++ b/network/NetworkCmp.cc @@ -137,4 +137,4 @@ sortByPathName(NetSet *set, return nets; } -} // namespace +} // namespace sta diff --git a/network/ParseBus.cc b/network/ParseBus.cc index 7bbab7a07..16300d69b 100644 --- a/network/ParseBus.cc +++ b/network/ParseBus.cc @@ -190,4 +190,4 @@ escapeChars(std::string_view token, return escaped; } -} // namespace +} // namespace sta diff --git a/network/PortDirection.cc b/network/PortDirection.cc index 608eb2b01..90df7a355 100644 --- a/network/PortDirection.cc +++ b/network/PortDirection.cc @@ -76,7 +76,7 @@ PortDirection::destroy() } PortDirection::PortDirection(const char *name, - int index) : + size_t index) : name_(name), index_(index) { @@ -135,4 +135,4 @@ PortDirection::isPowerGround() const || this == well_; } -} // namespace +} // namespace sta diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index e36b0acd0..50f95fc0b 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -1271,4 +1271,4 @@ escapeBrackets(std::string_view name, return escapeChars(name, '[', ']', network->pathEscape()); } -} // namespace +} // namespace sta diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index 28f07dc0e..e00946882 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -219,4 +219,4 @@ verilogToSta(std::string_view verilog_name) return std::string(verilog_name); } -} // namespace +} // namespace sta diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index d85b4fa61..27d74bf6f 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -752,8 +752,8 @@ NetIdPairLess::operator()(const NetIdPair &net_id1, //////////////////////////////////////////////////////////////// -ConcreteParasitics::ConcreteParasitics(std::string name, - std::string filename, +ConcreteParasitics::ConcreteParasitics(std::string_view name, + std::string_view filename, StaState *sta) : Parasitics(sta), name_(name), @@ -1439,4 +1439,4 @@ ConcreteParasitics::unannotatedLoads(const Parasitic *parasitic, return cparasitic->unannotatedLoads(drvr_pin, this); } -} // namespace +} // namespace sta diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index a6486736c..e866e8e74 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -50,10 +50,10 @@ using ConcreteParasiticNetworkMap = std::mapsetCouplingCapFactor(coupling_cap_factor); } -SpefReader::~SpefReader() {} - bool SpefReader::read() { diff --git a/parasitics/SpefReader.hh b/parasitics/SpefReader.hh index d07e8a632..f1187e1de 100644 --- a/parasitics/SpefReader.hh +++ b/parasitics/SpefReader.hh @@ -55,4 +55,4 @@ readSpefFile(std::string_view filename, Parasitics *parasirics, StaState *sta); -} // namespace +} // namespace sta diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index 272d7ef1f..9ba21cde3 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -58,7 +58,7 @@ public: const MinMaxAll *min_max, Parasitics *parasitics, StaState *sta); - virtual ~SpefReader(); + virtual ~SpefReader() = default; bool read(); char divider() const { return divider_; } void setDivider(char divider); @@ -181,4 +181,4 @@ private: SpefTriple *c1_; }; -} // namespace +} // namespace sta diff --git a/parasitics/SpefScanner.hh b/parasitics/SpefScanner.hh index c87a43c00..58e64025f 100644 --- a/parasitics/SpefScanner.hh +++ b/parasitics/SpefScanner.hh @@ -46,8 +46,6 @@ public: std::string_view filename, SpefReader *reader, Report *report); - virtual ~SpefScanner() {} - virtual int lex(SpefParse::semantic_type *const yylval, SpefParse::location_type *yylloc); // YY_DECL defined in SpefLex.ll @@ -66,4 +64,4 @@ private: std::string token_; }; -} // namespace +} // namespace sta diff --git a/power/Power.cc b/power/Power.cc index 78d9adce2..114d802e8 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -26,38 +26,53 @@ #include // max #include // abs +#include +#include +#include +#include #include "cudd.h" + +#include "Bfs.hh" +#include "ClkNetwork.hh" +#include "Clock.hh" #include "ContainerHelpers.hh" -#include "Stats.hh" #include "Debug.hh" +#include "Delay.hh" #include "EnumNameMap.hh" +#include "Error.hh" +#include "FuncExpr.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "GraphDelayCalc.hh" #include "Hash.hh" -#include "MinMax.hh" -#include "Units.hh" -#include "Transition.hh" -#include "TimingRole.hh" -#include "Liberty.hh" #include "InternalPower.hh" #include "LeakagePower.hh" -#include "Sequential.hh" -#include "TimingArc.hh" -#include "FuncExpr.hh" -#include "PortDirection.hh" -#include "Network.hh" -#include "Clock.hh" -#include "Sdc.hh" +#include "Liberty.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" #include "Mode.hh" -#include "Graph.hh" -#include "GraphDelayCalc.hh" -#include "Scene.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "NetworkCmp.hh" #include "Path.hh" +#include "PortDirection.hh" +#include "PowerClass.hh" +#include "ReportPower.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "SdcClass.hh" +#include "Search.hh" +#include "SearchClass.hh" +#include "Sequential.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" +#include "VertexVisitor.hh" #include "search/Levelize.hh" #include "search/Sim.hh" -#include "Search.hh" -#include "Bfs.hh" -#include "ClkNetwork.hh" -#include "ReportPower.hh" // Related liberty not supported: // library @@ -90,16 +105,7 @@ static EnumNameMap pwr_activity_origin_map = { Power::Power(StaState *sta) : StaState(sta), - scene_(nullptr), - global_activity_(), - input_activity_(), // default set in ensureActivities. - seq_activity_map_(100, - SeqPinHash(network_), - SeqPinEqual()), - activities_valid_(false), - bdd_(sta), - instance_powers_(InstanceIdLess(network_)), - instance_powers_valid_(false) + bdd_(sta) { } @@ -399,7 +405,7 @@ Power::sortInstsByPower(const InstanceSeq &insts, InstPowers inst_pwrs; for (const Instance *inst : insts) { PowerResult inst_power = power(inst, scene); - inst_pwrs.push_back(std::make_pair(inst, inst_power)); + inst_pwrs.emplace_back(inst, inst_power); } // Sort by total power (descending) @@ -505,7 +511,7 @@ Power::highestInstPowers(size_t count, while (inst_iter->hasNext()) { Instance *inst = inst_iter->next(); PowerResult pwr = power(inst, scene); - inst_pwrs.push_back(std::make_pair(inst, pwr)); + inst_pwrs.emplace_back(inst, pwr); } delete inst_iter; @@ -564,14 +570,15 @@ ActivitySrchPred::searchTo(const Vertex *, //////////////////////////////////////////////////////////////// +// NOLINTNEXTLINE(misc-multiple-inheritance) class PropActivityVisitor : public VertexVisitor, StaState { public: PropActivityVisitor(Power *power, const Mode *mode, BfsFwdIterator *bfs); - virtual VertexVisitor *copy() const; - virtual void visit(Vertex *vertex); + VertexVisitor *copy() const override; + void visit(Vertex *vertex) override; InstanceSet &visitedRegs() { return visited_regs_; } void init(); float maxChange() const { return max_change_; } @@ -583,8 +590,8 @@ class PropActivityVisitor : public VertexVisitor, StaState static constexpr float change_tolerance_ = .01; InstanceSet visited_regs_; - float max_change_; - const Pin *max_change_pin_; + float max_change_{0.0}; + const Pin *max_change_pin_{nullptr}; BfsFwdIterator *bfs_; Power *power_; const Mode *mode_; @@ -595,8 +602,6 @@ PropActivityVisitor::PropActivityVisitor(Power *power, BfsFwdIterator *bfs) : StaState(power), visited_regs_(network_), - max_change_(0.0), - max_change_pin_(nullptr), bfs_(bfs), power_(power), mode_(mode) @@ -1445,28 +1450,15 @@ Power::findSwitchingPower(const Instance *inst, class LeakageSummary { public: - LeakageSummary(); - - bool cond_exists; - float cond_leakage; - float cond_duty_sum; - bool cond_true_exists; - float cond_true_leakage; - bool uncond_exists; - float uncond_leakage; + bool cond_exists{false}; + float cond_leakage{0.0}; + float cond_duty_sum{0.0}; + bool cond_true_exists{false}; + float cond_true_leakage{0.0}; + bool uncond_exists{false}; + float uncond_leakage{0.0}; }; -LeakageSummary::LeakageSummary() : - cond_exists(false), - cond_leakage(0.0), - cond_duty_sum(0.0), - cond_true_exists(false), - cond_true_leakage(0.0), - uncond_exists(false), - uncond_leakage(0.0) -{ -} - void Power::findLeakagePower(const Instance *inst, LibertyCell *cell, @@ -1804,13 +1796,6 @@ Power::powerInvalid() //////////////////////////////////////////////////////////////// -PowerResult::PowerResult() : - internal_(0.0), - switching_(0.0), - leakage_(0.0) -{ -} - void PowerResult::clear() { @@ -1863,13 +1848,6 @@ PwrActivity::PwrActivity(float density, check(); } -PwrActivity::PwrActivity() : - density_(0.0), - duty_(0.0), - origin_(PwrActivityOrigin::unknown) -{ -} - void PwrActivity::setDensity(float density) { diff --git a/power/Power.hh b/power/Power.hh index 724710473..9bb72e65c 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -107,20 +107,20 @@ public: PowerResult power(const Instance *inst, const Scene *scene); - void setGlobalActivity(float activity, + void setGlobalActivity(float density, float duty); void unsetGlobalActivity(); - void setInputActivity(float activity, + void setInputActivity(float density, float duty); void unsetInputActivity(); void setInputPortActivity(const Port *input_port, - float activity, + float density, float duty); void unsetInputPortActivity(const Port *input_port); PwrActivity pinActivity(const Pin *pin, const Scene *scene); void setUserActivity(const Pin *pin, - float activity, + float density, float duty, PwrActivityOrigin origin); void unsetUserActivity(const Pin *pin); @@ -255,7 +255,7 @@ protected: size_t pinCount(); private: - const Scene *scene_; + const Scene *scene_{nullptr}; // Port/pin activities set by set_pin_activity. // set_pin_activity -global PwrActivity global_activity_; @@ -265,15 +265,18 @@ private: PwrActivityMap user_activity_map_; // Propagated activities. PwrActivityMap activity_map_; - PwrSeqActivityMap seq_activity_map_; - bool activities_valid_; + PwrSeqActivityMap seq_activity_map_{100, + SeqPinHash(network_), + SeqPinEqual()}; + bool activities_valid_{false}; Bdd bdd_; - std::map instance_powers_; - bool instance_powers_valid_; + std::map instance_powers_{ + InstanceIdLess(network_)}; + bool instance_powers_valid_{false}; static constexpr int max_activity_passes_ = 50; friend class PropActivityVisitor; }; -} // namespace +} // namespace sta diff --git a/power/ReportPower.hh b/power/ReportPower.hh index 0486f8d79..4d905e885 100644 --- a/power/ReportPower.hh +++ b/power/ReportPower.hh @@ -90,4 +90,4 @@ private: int digits); }; -} // namespace +} // namespace sta diff --git a/power/SaifReader.hh b/power/SaifReader.hh index e91979145..8f408fd9b 100644 --- a/power/SaifReader.hh +++ b/power/SaifReader.hh @@ -33,4 +33,4 @@ readSaif(const char *filename, const char *scope, Sta *sta); -} // namespace +} // namespace sta diff --git a/power/SaifReaderPvt.hh b/power/SaifReaderPvt.hh index af8722a65..6443dfa9a 100644 --- a/power/SaifReaderPvt.hh +++ b/power/SaifReaderPvt.hh @@ -84,4 +84,4 @@ private: Power *power_; }; -} // namespace +} // namespace sta diff --git a/power/SaifScanner.hh b/power/SaifScanner.hh index 0fa4a8055..945ddc6a1 100644 --- a/power/SaifScanner.hh +++ b/power/SaifScanner.hh @@ -44,8 +44,6 @@ public: const std::string &filename, SaifReader *reader, Report *report); - virtual ~SaifScanner() {} - virtual int lex(SaifParse::semantic_type *const yylval, SaifParse::location_type *yylloc); // YY_DECL defined in SaifLex.ll @@ -63,4 +61,4 @@ private: std::string token_; }; -} // namespace +} // namespace sta diff --git a/power/VcdParse.hh b/power/VcdParse.hh index 69a3f7d02..827022223 100644 --- a/power/VcdParse.hh +++ b/power/VcdParse.hh @@ -97,7 +97,7 @@ private: class VcdReader { public: - virtual ~VcdReader() {} + virtual ~VcdReader() = default; virtual void setDate(std::string_view date) = 0; virtual void setComment(std::string_view comment) = 0; virtual void setVersion(std::string_view version) = 0; @@ -139,4 +139,4 @@ private: uint64_t bus_value_; }; -} // namespace +} // namespace sta diff --git a/power/VcdReader.hh b/power/VcdReader.hh index 205779a6a..e3b0bafcc 100644 --- a/power/VcdReader.hh +++ b/power/VcdReader.hh @@ -36,4 +36,4 @@ readVcdActivities(std::string_view filename, std::string_view mode_name, Sta *sta); -} // namespace +} // namespace sta diff --git a/sdc/Clock.cc b/sdc/Clock.cc index f2c916338..1772b60f6 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -244,7 +244,7 @@ Clock::removeSlew() void Clock::setSlewLimit(const RiseFallBoth *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, float slew) { @@ -253,7 +253,7 @@ Clock::setSlewLimit(const RiseFallBoth *rf, void Clock::slewLimit(const RiseFall *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, // Return values. float &slew, @@ -714,4 +714,4 @@ compare(const ClockSet *set1, return sta::compare(set1, set2, ClockIndexLess()); } -} // namespace +} // namespace sta diff --git a/sdc/ClockGatingCheck.cc b/sdc/ClockGatingCheck.cc index ca706cfec..96d134d32 100644 --- a/sdc/ClockGatingCheck.cc +++ b/sdc/ClockGatingCheck.cc @@ -37,4 +37,4 @@ ClockGatingCheck::setActiveValue(LogicValue value) active_value_ = value; } -} // namespace +} // namespace sta diff --git a/sdc/ClockGroups.cc b/sdc/ClockGroups.cc index 776b54a2d..279ae3c2e 100644 --- a/sdc/ClockGroups.cc +++ b/sdc/ClockGroups.cc @@ -29,13 +29,13 @@ namespace sta { -ClockGroups::ClockGroups(const std::string &name, +ClockGroups::ClockGroups(std::string_view name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, - std::string comment) : - SdcCmdComment(std::move(comment)), + std::string_view comment) : + SdcCmdComment(comment), name_(name), logically_exclusive_(logically_exclusive), physically_exclusive_(physically_exclusive), @@ -70,4 +70,4 @@ ClockGroups::removeClock(Clock *clk) } } -} // namespace +} // namespace sta diff --git a/sdc/ClockInsertion.cc b/sdc/ClockInsertion.cc index 6a0b05451..91a6c00a9 100644 --- a/sdc/ClockInsertion.cc +++ b/sdc/ClockInsertion.cc @@ -93,4 +93,4 @@ ClockInsertion::delays(const EarlyLate *early_late) return &delays_[early_late->index()]; } -} // namespace +} // namespace sta diff --git a/sdc/ClockLatency.cc b/sdc/ClockLatency.cc index 7912cd3f2..9ef63d265 100644 --- a/sdc/ClockLatency.cc +++ b/sdc/ClockLatency.cc @@ -87,4 +87,4 @@ ClockLatency::delays() return &delays_; } -} // namespace +} // namespace sta diff --git a/sdc/CycleAccting.cc b/sdc/CycleAccting.cc index a3baf7162..8f4b4b322 100644 --- a/sdc/CycleAccting.cc +++ b/sdc/CycleAccting.cc @@ -429,4 +429,4 @@ CycleAcctingEqual::operator()(const CycleAccting *acct1, && acct1->target() == acct2->target(); } -} // namespace +} // namespace sta diff --git a/sdc/DataCheck.cc b/sdc/DataCheck.cc index c3095188d..dbba24226 100644 --- a/sdc/DataCheck.cc +++ b/sdc/DataCheck.cc @@ -120,4 +120,4 @@ DataCheckLess::operator()(const DataCheck *check1, && clkCmp(clk1, clk2) < 0))); } -} // namespace +} // namespace sta diff --git a/sdc/DeratingFactors.cc b/sdc/DeratingFactors.cc index f187f20b3..f5534ceec 100644 --- a/sdc/DeratingFactors.cc +++ b/sdc/DeratingFactors.cc @@ -217,4 +217,4 @@ DeratingFactorsNet::DeratingFactorsNet() { } -} // namespace +} // namespace sta diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index e3cb2dd5c..69ad2b57d 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -524,7 +524,7 @@ PathDelay::to_string(const Network *network) const fromThruToString(network)); } -const char * +std::string_view PathDelay::typeString() const { return "Path"; @@ -604,7 +604,7 @@ FalsePath::tighterThan(ExceptionPath *) const return false; } -const char * +std::string_view FalsePath::typeString() const { return "False"; @@ -633,7 +633,7 @@ LoopPath::LoopPath(ExceptionThruSeq *thrus, { } -const char * +std::string_view LoopPath::typeString() const { return "Loop"; @@ -726,7 +726,7 @@ MultiCyclePath::to_string(const Network *network) const fromThruToString(network)); } -const char * +std::string_view MultiCyclePath::typeString() const { return "Multicycle"; @@ -759,7 +759,7 @@ FilterPath::FilterPath(ExceptionFrom *from, { } -const char * +std::string_view FilterPath::typeString() const { return "Filter"; @@ -831,7 +831,7 @@ GroupPath::~GroupPath() { } -const char * +std::string_view GroupPath::typeString() const { return "Group"; @@ -2465,4 +2465,4 @@ deletePinPairsThruHierPin(const Pin *hpin, visitDrvrLoadsThruHierPin(hpin, network, &visitor); } -} // namespace +} // namespace sta diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc index 45e59997a..cf3033778 100644 --- a/sdc/FilterObjects.cc +++ b/sdc/FilterObjects.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -24,19 +24,32 @@ #include "FilterObjects.hh" -#include #include #include +#include #include #include +#include #include #include +#include +#include +#include "Clock.hh" +#include "ContainerHelpers.hh" +#include "Format.hh" +#include "GraphClass.hh" +#include "GraphCmp.hh" +#include "Liberty.hh" +#include "LibertyClass.hh" #include "NetworkClass.hh" #include "Network.hh" +#include "SearchClass.hh" #include "StringUtil.hh" #include "Property.hh" +#include "PathEnd.hh" #include "PatternMatch.hh" +#include "SdcClass.hh" #include "Sta.hh" namespace sta { @@ -46,6 +59,7 @@ class FilterExpr public: struct Token { + virtual ~Token() = default; enum class Kind { skip = 0, predicate, @@ -72,6 +86,7 @@ class FilterExpr PredicateToken(std::string_view property, std::string_view op, std::string_view arg); + virtual ~PredicateToken() = default; const std::string &property() const { return property_; } const std::string &op() const { return op_; } const std::string &arg() const { return arg_; } @@ -142,7 +157,7 @@ FilterExpr::lex() }; std::vector> result; - const char* ptr = &raw_[0]; + const char* ptr = raw_.data(); bool match = false; while (*ptr != '\0') { match = false; @@ -197,7 +212,7 @@ FilterExpr::shuntingYard(std::vector> &infix) case Token::Kind::op_and: // The operators' enum values are ascending by precedence: // inv > and > or - while (operator_stack.size() + while (!operator_stack.empty() && operator_stack.top()->kind() > token->kind()) { output.push_back(std::move(operator_stack.top())); operator_stack.pop(); @@ -220,7 +235,7 @@ FilterExpr::shuntingYard(std::vector> &infix) case Token::Kind::op_rparen: if (operator_stack.empty()) report_->error(2601, "-filter extraneous )."); - while (operator_stack.size() + while (!operator_stack.empty() && operator_stack.top()->kind() != Token::Kind::op_lparen) { output.push_back(std::move(operator_stack.top())); operator_stack.pop(); @@ -236,7 +251,7 @@ FilterExpr::shuntingYard(std::vector> &infix) } } - while (operator_stack.size()) { + while (!operator_stack.empty()) { if (operator_stack.top()->kind() == Token::Kind::op_lparen) report_->error(2603, "-filter unmatched (."); output.push_back(std::move(operator_stack.top())); @@ -284,6 +299,7 @@ filterObjects(std::string_view property, template static std::vector filterObjects(std::string_view filter_expression, const std::vector *objects, + const std::function &object_less, Sta *sta) { Report *report = sta->report(); @@ -294,6 +310,8 @@ filterObjects(std::string_view filter_expression, std::set all; for (auto object: *objects) all.insert(object); + // Delete objects before parsing so errors to not leak them. + delete objects; FilterExpr filter(filter_expression, report); auto postfix = filter.postfix(); @@ -366,19 +384,19 @@ filterObjects(std::string_view filter_expression, case PropertyValue::Type::pin: case PropertyValue::Type::net: case PropertyValue::Type::clk: - is_defined = value.to_string(network) != ""; + is_defined = !value.to_string(network).empty(); break; case PropertyValue::Type::none: is_defined = false; break; case PropertyValue::Type::pins: - is_defined = value.pins()->size() > 0; + is_defined = !value.pins()->empty(); break; case PropertyValue::Type::clks: - is_defined = value.clocks()->size() > 0; + is_defined = !value.clocks()->empty(); break; case PropertyValue::Type::paths: - is_defined = value.paths()->size() > 0; + is_defined = !value.paths()->empty(); break; case PropertyValue::Type::pwr_activity: is_defined = value.pwrActivity().isSet(); @@ -400,7 +418,7 @@ filterObjects(std::string_view filter_expression, eval_stack.push(result); } } - if (eval_stack.size() == 0) + if (eval_stack.empty()) report->error(2607, "-filter expression is empty."); if (eval_stack.size() > 1) // huh? @@ -408,96 +426,139 @@ filterObjects(std::string_view filter_expression, auto result_set = eval_stack.top(); result.resize(result_set.size()); std::copy(result_set.begin(), result_set.end(), result.begin()); - std::map objects_i; - for (size_t i = 0; i < objects->size(); ++i) - objects_i[objects->at(i)] = i; - std::sort(result.begin(), result.end(), - [&](T* a, T* b) { - return objects_i[a] < objects_i[b]; - }); - delete objects; + sort(result, [object_less] (T *obj1, T *obj2) { + return object_less(obj1, obj2); + }); } return result; } PortSeq filterPorts(std::string_view filter_expression, - PortSeq *objects, + PortSeq *ports, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + Network *network = sta->network(); + return filterObjects(filter_expression, ports, + [network] (const Port *port1, + const Port *port2) { + return network->name(port1) < network->name(port2); + }, sta); } InstanceSeq filterInstances(std::string_view filter_expression, - InstanceSeq *objects, + InstanceSeq *insts, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + Network *network = sta->network(); + return filterObjects(filter_expression, insts, + [network] (const Instance *inst1, + const Instance *inst2) { + return network->name(inst1) < network->name(inst2); + }, sta); } PinSeq filterPins(std::string_view filter_expression, - PinSeq *objects, + PinSeq *pins, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + Network *network = sta->network(); + return filterObjects(filter_expression, pins, + [network] (const Pin *pin1, + const Pin *pin2) { + return network->pathName(pin1) < network->pathName(pin2); + }, sta); } NetSeq filterNets(std::string_view filter_expression, - NetSeq *objects, + NetSeq *nets, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + Network *network = sta->network(); + return filterObjects(filter_expression, nets, + [network] (const Net *net1, + const Net *net2) { + return network->pathName(net1) < network->pathName(net2); + }, sta); } ClockSeq filterClocks(std::string_view filter_expression, - ClockSeq *objects, + ClockSeq *clks, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + return filterObjects(filter_expression, clks, + [] (const Clock *clk1, + const Clock *clk2) { + return clk1->name() < clk2->name(); + }, sta); } LibertyCellSeq filterLibCells(std::string_view filter_expression, - LibertyCellSeq *objects, + LibertyCellSeq *cells, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + return filterObjects(filter_expression, cells, + [] (const LibertyCell *cell1, + const LibertyCell *cell2) { + return cell1->name() < cell2->name(); + }, sta); } LibertyPortSeq filterLibPins(std::string_view filter_expression, - LibertyPortSeq *objects, + LibertyPortSeq *ports, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + return filterObjects(filter_expression, ports, + [] (const LibertyPort *port1, + const LibertyPort *port2) { + return port1->name() < port2->name(); + }, sta); } LibertyLibrarySeq filterLibertyLibraries(std::string_view filter_expression, - LibertyLibrarySeq *objects, + LibertyLibrarySeq *libs, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + return filterObjects(filter_expression, libs, + [] (const LibertyLibrary *lib1, + const LibertyLibrary *lib2) { + return lib1->name() < lib2->name(); + }, sta); } EdgeSeq filterTimingArcs(std::string_view filter_expression, - EdgeSeq *objects, + EdgeSeq *edges, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + Network *network = sta->network(); + Graph *graph = sta->graph(); + EdgeLess edge_less(network, graph); + return filterObjects(filter_expression, edges, + [edge_less] (const Edge *edge1, + const Edge *edge2) { + return edge_less.operator()(edge1, edge2); + }, sta); } PathEndSeq filterPathEnds(std::string_view filter_expression, - PathEndSeq *objects, + PathEndSeq *ends, Sta *sta) { - return filterObjects(filter_expression, objects, sta); + PathEndLess end_less(true, sta); + return filterObjects(filter_expression, ends, + [end_less] (const PathEnd *end1, + const PathEnd *end2) { + return end_less.operator()(end1, end2); + }, sta); } StringSeq diff --git a/sdc/InputDrive.cc b/sdc/InputDrive.cc index 26b06e57a..1b558b96a 100644 --- a/sdc/InputDrive.cc +++ b/sdc/InputDrive.cc @@ -235,4 +235,4 @@ InputDriveCell::equal(const InputDriveCell *drive) const && to_port_ == drive->to_port_; } -} // namespace +} // namespace sta diff --git a/sdc/PinPair.cc b/sdc/PinPair.cc index 05eac4d12..7c90d92b2 100644 --- a/sdc/PinPair.cc +++ b/sdc/PinPair.cc @@ -77,4 +77,4 @@ PinPairSet::PinPairSet(const Network *network) : { } -} // namespace +} // namespace sta diff --git a/sdc/PortDelay.cc b/sdc/PortDelay.cc index db0c0263a..cd6dbba98 100644 --- a/sdc/PortDelay.cc +++ b/sdc/PortDelay.cc @@ -132,4 +132,4 @@ PortDelayLess::operator() (const PortDelay *delay1, return clkEdgeLess(delay1->clkEdge(), delay2->clkEdge()); } -} // namespace +} // namespace sta diff --git a/sdc/PortExtCap.cc b/sdc/PortExtCap.cc index 377c818f7..a17f353c3 100644 --- a/sdc/PortExtCap.cc +++ b/sdc/PortExtCap.cc @@ -90,4 +90,4 @@ PortExtCap::fanout(const MinMax *min_max, fanout_.value(min_max, fanout, exists); } -} // namespace +} // namespace sta diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 27d645697..08510aea8 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -25,40 +25,49 @@ #include "Sdc.hh" #include +#include #include +#include +#include +#include -#include "ContainerHelpers.hh" -#include "Stats.hh" -#include "Debug.hh" -#include "Mutex.hh" -#include "Report.hh" -#include "Variables.hh" -#include "PatternMatch.hh" -#include "MinMax.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "Transition.hh" -#include "PortDirection.hh" -#include "Network.hh" -#include "RiseFallMinMax.hh" #include "Clock.hh" -#include "ClockLatency.hh" +#include "ClockGatingCheck.hh" +#include "ClockGroups.hh" #include "ClockInsertion.hh" +#include "ClockLatency.hh" +#include "ContainerHelpers.hh" #include "CycleAccting.hh" -#include "PortDelay.hh" -#include "ExceptionPath.hh" -#include "PortExtCap.hh" -#include "DisabledPorts.hh" -#include "InputDrive.hh" #include "DataCheck.hh" -#include "ClockGatingCheck.hh" -#include "ClockGroups.hh" +#include "Debug.hh" #include "DeratingFactors.hh" +#include "DisabledPorts.hh" +#include "ExceptionPath.hh" +#include "Format.hh" +#include "Graph.hh" #include "HpinDrvrLoad.hh" -#include "search/Levelize.hh" +#include "InputDrive.hh" +#include "Liberty.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mutex.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "PatternMatch.hh" +#include "PinPair.hh" +#include "PortDelay.hh" +#include "PortDirection.hh" +#include "PortExtCap.hh" +#include "Report.hh" +#include "RiseFallMinMax.hh" #include "Scene.hh" -#include "Graph.hh" +#include "SdcClass.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Variables.hh" +#include "search/Levelize.hh" namespace sta { @@ -94,8 +103,6 @@ Sdc::Sdc(Mode *mode, StaState *sta) : StaState(sta), mode_(mode), - derating_factors_(nullptr), - clk_index_(0), clock_pin_map_(10, PinIdHash(network_)), clock_leaf_pin_map_(10, PinIdHash(network_)), clk_hpin_disables_(network_), @@ -105,14 +112,12 @@ Sdc::Sdc(Mode *mode, edge_clk_latency_map_(network_), clk_insertions_(network_), clk_sense_map_(network_), - clk_gating_check_(nullptr), cycle_acctings_(this), input_delay_pin_map_(PinIdLess(network_)), input_delay_ref_pin_map_(PinIdLess(network_)), input_delay_leaf_pin_map_(PinIdLess(network_)), input_delay_internal_pin_map_(PinIdLess(network_)), - input_delay_index_(0), output_delay_pin_map_(PinIdLess(network_)), output_delay_ref_pin_map_(PinIdLess(network_)), @@ -127,14 +132,11 @@ Sdc::Sdc(Mode *mode, disabled_wire_edges_(network_), disabled_clk_gating_checks_inst_(network_), disabled_clk_gating_checks_pin_(network_), - exception_id_(0), - have_thru_hpin_exceptions_(false), first_thru_edge_exceptions_(0, PinPairHash(network_), PinPairEqual()), path_delay_internal_from_(network_), path_delay_internal_from_break_(network_), path_delay_internal_to_(network_), - path_delay_internal_to_break_(network_), - filter_(nullptr) + path_delay_internal_to_break_(network_) { initVariables(); setWireload(nullptr, MinMaxAll::all()); @@ -150,7 +152,7 @@ Sdc::makeDefaultArrivalClock() waveform->push_back(0.0); waveform->push_back(0.0); default_arrival_clk_ = new Clock("input port clock", clk_index_++, network_); - default_arrival_clk_->initClk(0, false, 0.0, waveform, "", network_); + default_arrival_clk_->initClk(nullptr, false, 0.0, waveform, "", network_); } Sdc::~Sdc() @@ -760,7 +762,7 @@ Sdc::ensureInputDrive(const Port *port) void Sdc::setSlewLimit(Clock *clk, const RiseFallBoth *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, float slew) { @@ -777,7 +779,7 @@ Sdc::haveClkSlewLimits() const void Sdc::slewLimit(const Clock *clk, const RiseFall *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, float &slew, bool &exists) const @@ -971,7 +973,7 @@ Sdc::makeClock(std::string_view name, deleteClkPinMappings(clk); else { // Fresh clock definition. - clk = new Clock(std::move(name), clk_index_++, network_); + clk = new Clock(name, clk_index_++, network_); clk->setIsPropagated(variables_->propagateAllClocks()); clocks_.push_back(clk); // Use the copied name in the map. @@ -1041,8 +1043,8 @@ Sdc::deletePinClocks(Clock *defining_clk, { // Find all the clocks defined on pins to avoid finding the clock's // vertex pins multiple times. - ClockSet clks; if (pins) { + ClockSet clks; for (const Pin *pin : *pins) { ClockSet *pin_clks = findKey(clock_pin_map_, pin); if (pin_clks) { @@ -1050,19 +1052,19 @@ Sdc::deletePinClocks(Clock *defining_clk, clks.insert(clk); } } - } - for (Clock *clk : clks) { - deleteClkPinMappings(clk); - for (const Pin *pin : *pins) - clk->deletePin(pin); - if (clk != defining_clk) { - if (clk->pins().empty()) - removeClock(clk); - else { - clk->makeLeafPins(network_); - // One of the remaining clock pins may use a vertex pin that - // was deleted above. - makeClkPinMappings(clk); + for (Clock *clk : clks) { + deleteClkPinMappings(clk); + for (const Pin *pin : *pins) + clk->deletePin(pin); + if (clk != defining_clk) { + if (clk->pins().empty()) + removeClock(clk); + else { + clk->makeLeafPins(network_); + // One of the remaining clock pins may use a vertex pin that + // was deleted above. + makeClkPinMappings(clk); + } } } } @@ -1132,7 +1134,7 @@ Sdc::removeClock(Clock *clk) clearCycleAcctings(); deleteClkPinMappings(clk); - clocks_.erase(std::find(clocks_.begin(), clocks_.end(), clk)); + clocks_.erase(std::ranges::find(clocks_, clk)); clock_name_map_.erase(clk->name()); delete clk; } @@ -1287,9 +1289,9 @@ class FindClkHpinDisables : public HpinDrvrLoadVisitor Sdc *sdc); bool drvrLoadExists(const Pin *drvr, const Pin *load); + void visit(HpinDrvrLoad *drvr_load) override; protected: - void visit(HpinDrvrLoad *drvr_load) override; void makeClkHpinDisables(const Pin *clk_src, const Pin *drvr, const Pin *load); @@ -1303,7 +1305,6 @@ class FindClkHpinDisables : public HpinDrvrLoadVisitor FindClkHpinDisables::FindClkHpinDisables(Clock *clk, const Network *network, Sdc *sdc) : - HpinDrvrLoadVisitor(), clk_(clk), drvr_loads_(network), network_(network), @@ -1468,11 +1469,10 @@ class MakeClkLatencyEdge : public HierPinThruVisitor public: MakeClkLatencyEdge(ClockLatency *latency, EdgeClockLatencyMap &edge_clk_latency_map); - -private: void visit(const Pin *drvr, const Pin *load) override; +private: ClockLatency *latency_; EdgeClockLatencyMap &edge_clk_latency_map_; }; @@ -1941,12 +1941,12 @@ ClockInsertionkLess::operator()(const ClockInsertion *insert1, //////////////////////////////////////////////////////////////// ClockGroups * -Sdc::makeClockGroups(const std::string &name, +Sdc::makeClockGroups(std::string_view name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, - std::string comment) + std::string_view comment) { std::string group_name; if (name.empty()) @@ -3561,18 +3561,16 @@ class DisableEdgesThruHierPin : public HierPinThruVisitor public: DisableEdgesThruHierPin(PinPairSet *pairs, Graph *graph); - -protected: void visit(const Pin *drvr, const Pin *load) override; +protected: PinPairSet *pairs_; Graph *graph_; }; DisableEdgesThruHierPin::DisableEdgesThruHierPin(PinPairSet *pairs, Graph *graph) : - HierPinThruVisitor(), pairs_(pairs), graph_(graph) { @@ -3602,18 +3600,16 @@ class RemoveDisableEdgesThruHierPin : public HierPinThruVisitor public: RemoveDisableEdgesThruHierPin(PinPairSet *pairs, Graph *graph); - -protected: void visit(const Pin *drvr, const Pin *load) override; +protected: PinPairSet *pairs_; Graph *graph_; }; RemoveDisableEdgesThruHierPin::RemoveDisableEdgesThruHierPin(PinPairSet *pairs, Graph *graph) : - HierPinThruVisitor(), pairs_(pairs), graph_(graph) { @@ -4083,7 +4079,7 @@ void Sdc::clearGroupPathMap() { // GroupPath exceptions are deleted with other exceptions. - for (auto [name, groups] : group_path_map_) { + for (auto &[name, groups] : group_path_map_) { deleteContents(*groups); delete groups; } @@ -4396,17 +4392,17 @@ Sdc::deleteMatchingExceptions(ExceptionPath *exception) ExceptionPathSet matches; findMatchingExceptions(exception, matches); - ExceptionPathSet expanded_matches; + ExceptionPathSet expansions; for (ExceptionPath *match : matches) // Expand the matching exception into a set of exceptions that // that do not cover the new exception. Do not record them // to prevent merging with the match, which will be deleted. - expandExceptionExcluding(match, exception, expanded_matches); + expandExceptionExcluding(match, exception, expansions); for (ExceptionPath *match : matches) deleteException(match); - for (ExceptionPath *match : expanded_matches) + for (ExceptionPath *match : expansions) addException(match); } @@ -5783,11 +5779,6 @@ findLeafDriverPins(const Pin *pin, //////////////////////////////////////////////////////////////// -NetWireCaps::NetWireCaps() : - subtract_pin_cap_{false, false} -{ -} - bool NetWireCaps::subtractPinCap(const MinMax *min_max) { @@ -5801,4 +5792,4 @@ NetWireCaps::setSubtractPinCap(bool subtrace_pin_cap, subtract_pin_cap_[min_max->index()] = subtrace_pin_cap; } -} // namespace +} // namespace sta diff --git a/sdc/SdcCmdComment.cc b/sdc/SdcCmdComment.cc index 083812ad7..805bb1945 100644 --- a/sdc/SdcCmdComment.cc +++ b/sdc/SdcCmdComment.cc @@ -27,34 +27,15 @@ namespace sta { -SdcCmdComment::SdcCmdComment() -{ -} - -SdcCmdComment::SdcCmdComment(std::string comment) : - comment_(std::move(comment)) -{ -} - SdcCmdComment::SdcCmdComment(std::string_view comment) : comment_(comment) { } -SdcCmdComment::~SdcCmdComment() -{ -} - -void -SdcCmdComment::setComment(std::string comment) -{ - comment_ = std::move(comment); -} - void SdcCmdComment::setComment(std::string_view comment) { comment_ = comment; } -} // namespace +} // namespace sta diff --git a/sdc/Variables.cc b/sdc/Variables.cc index f877fc458..90534a802 100644 --- a/sdc/Variables.cc +++ b/sdc/Variables.cc @@ -136,4 +136,4 @@ Variables::setPocvQuantile(float quantile) pocv_quantile_ = quantile; } -} // namespace +} // namespace sta diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index 3af1de6a0..c812c1eaa 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -89,7 +89,7 @@ class WriteSdcObject { public: WriteSdcObject() {} - virtual ~WriteSdcObject() {} + virtual ~WriteSdcObject() = default; virtual void write() const = 0; }; @@ -2860,4 +2860,4 @@ WriteSdc::writeCmdComment(SdcCmdComment *cmd) const sta::print(stream_, " -comment {{{}}}", comment); } -} // namespace +} // namespace sta diff --git a/sdc/WriteSdc.hh b/sdc/WriteSdc.hh index 0e2b33c32..15b8c7251 100644 --- a/sdc/WriteSdc.hh +++ b/sdc/WriteSdc.hh @@ -46,4 +46,4 @@ writeSdc(const Sdc *sdc, bool gzip, bool no_timestamp); -} // namespace +} // namespace sta diff --git a/sdc/WriteSdcPvt.hh b/sdc/WriteSdcPvt.hh index 88600c77e..ad6b3c586 100644 --- a/sdc/WriteSdcPvt.hh +++ b/sdc/WriteSdcPvt.hh @@ -275,4 +275,4 @@ protected: gzFile stream_; }; -} // namespace +} // namespace sta diff --git a/sdf/ReportAnnotation.hh b/sdf/ReportAnnotation.hh index cc810464b..c9291ceec 100644 --- a/sdf/ReportAnnotation.hh +++ b/sdf/ReportAnnotation.hh @@ -56,4 +56,4 @@ reportAnnotatedCheck(const Scene *scene, bool report_constant_arcs, StaState *sta); -} // namespace +} // namespace sta diff --git a/sdf/SdfReader.hh b/sdf/SdfReader.hh index 47598d5d5..5bb2e8e58 100644 --- a/sdf/SdfReader.hh +++ b/sdf/SdfReader.hh @@ -63,4 +63,4 @@ readSdf(std::string_view filename, MinMaxAll *cond_use, StaState *sta); -} // namespace +} // namespace sta diff --git a/sdf/SdfReaderPvt.hh b/sdf/SdfReaderPvt.hh index 3b12d02e6..226e31519 100644 --- a/sdf/SdfReaderPvt.hh +++ b/sdf/SdfReaderPvt.hh @@ -230,4 +230,4 @@ private: static const int null_index_ = -1; }; -} // namespace +} // namespace sta diff --git a/sdf/SdfScanner.hh b/sdf/SdfScanner.hh index bcb0c6cd3..87fc85876 100644 --- a/sdf/SdfScanner.hh +++ b/sdf/SdfScanner.hh @@ -46,8 +46,6 @@ public: std::string_view filename, SdfReader *reader, Report *report); - virtual ~SdfScanner() {} - virtual int lex(SdfParse::semantic_type *const yylval, SdfParse::location_type *yylloc); // YY_DECL defined in SdfLex.ll @@ -65,4 +63,4 @@ private: std::string token_; }; -} // namespace +} // namespace sta diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 0d7500927..7607af785 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -824,4 +824,4 @@ SdfWriter::sdfPortName(const Pin *pin) return sdf_name; } -} // namespace +} // namespace sta diff --git a/sdf/SdfWriter.hh b/sdf/SdfWriter.hh index 98ca6f7dc..4cec60d92 100644 --- a/sdf/SdfWriter.hh +++ b/sdf/SdfWriter.hh @@ -42,4 +42,4 @@ writeSdf(std::string_view filename, bool no_version, StaState *sta); -} // namespace +} // namespace sta diff --git a/search/Bdd.cc b/search/Bdd.cc index 7d5bf55f6..0e1ff7262 100644 --- a/search/Bdd.cc +++ b/search/Bdd.cc @@ -24,10 +24,10 @@ #include "Bdd.hh" -#include "cudd.h" -#include "StaConfig.hh" -#include "Report.hh" #include "FuncExpr.hh" +#include "Report.hh" +#include "StaConfig.hh" +#include "cudd.h" namespace sta { @@ -57,6 +57,7 @@ Bdd::funcBdd(const FuncExpr *expr) case FuncExpr::Op::not_: left = funcBdd(expr->left()); if (left) + // NOLINTNEXTLINE(performance-no-int-to-ptr) result = Cudd_Not(left); break; case FuncExpr::Op::or_: @@ -161,4 +162,4 @@ Bdd::clearVarMap() bdd_var_idx_port_map_.clear(); } -} // namespace +} // namespace sta diff --git a/search/Bfs.cc b/search/Bfs.cc index d13f9700d..69b9c28b5 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -24,14 +24,14 @@ #include "Bfs.hh" -#include "Report.hh" #include "Debug.hh" -#include "Mutex.hh" #include "DispatchQueue.hh" -#include "Network.hh" #include "Graph.hh" -#include "Sdc.hh" #include "Levelize.hh" +#include "Mutex.hh" +#include "Network.hh" +#include "Report.hh" +#include "Sdc.hh" #include "SearchPred.hh" namespace sta { @@ -68,8 +68,6 @@ BfsIterator::ensureSize() } } -BfsIterator::~BfsIterator() {} - void BfsIterator::clear() { @@ -153,7 +151,6 @@ BfsIterator::visit(Level to_level, } } level_vertices.clear(); - visitor->levelFinished(); } return visit_count; } @@ -169,6 +166,7 @@ BfsIterator::visitParallel(Level to_level, visit_count = visit(to_level, visitor); else { std::vector visitors; + visitors.reserve(thread_count_); for (int k = 0; k < thread_count_; k++) visitors.push_back(visitor->copy()); while (levelLessOrEqual(first_level_, last_level_) @@ -208,7 +206,6 @@ BfsIterator::visitParallel(Level to_level, } dispatch_queue_->finishTasks(); } - visitor->levelFinished(); level_vertices.clear(); visit_count += vertex_count; } @@ -294,7 +291,7 @@ void BfsIterator::checkInQueue(Vertex *vertex) { Level level = vertex->level(); - if (static_cast(queue_.size()) > level) { + if (std::cmp_greater(queue_.size(), level)) { for (Vertex *v : queue_[level]) { if (v == vertex) { if (vertex->bfsInQueue(bfs_index_)) @@ -329,7 +326,7 @@ 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) { + if (vertex->bfsInQueue(bfs_index_) && std::cmp_greater(queue_.size(), level)) { debugPrint(debug_, "bfs", 2, "remove {}", vertex->to_string(this)); for (Vertex *&v : queue_[level]) { if (v == vertex) { diff --git a/search/CheckCapacitances.cc b/search/CheckCapacitances.cc index 41ccd9c21..cb6a7b47b 100644 --- a/search/CheckCapacitances.cc +++ b/search/CheckCapacitances.cc @@ -24,23 +24,25 @@ #include "CheckCapacitances.hh" +#include + +#include "ClkNetwork.hh" #include "ContainerHelpers.hh" #include "Fuzzy.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" +#include "InputDrive.hh" #include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" +#include "MinMax.hh" #include "Mode.hh" -#include "InputDrive.hh" -#include "GraphDelayCalc.hh" -#include "StaState.hh" -#include "Scene.hh" +#include "Network.hh" +#include "NetworkClass.hh" #include "PortDirection.hh" +#include "Scene.hh" +#include "Sdc.hh" #include "Sim.hh" -#include "Graph.hh" -#include "GraphDelayCalc.hh" -#include "ClkNetwork.hh" +#include "StaState.hh" #include "Transition.hh" -#include "BoundedHeap.hh" namespace sta { @@ -210,21 +212,21 @@ CheckCapacitances::findLimit(const Pin *pin, CapacitanceCheckSeq & CheckCapacitances::check(const Net *net, size_t max_count, - bool violations, + bool violators, const SceneSeq &scenes, const MinMax *min_max) { clear(); - if (violations) - return checkViolations(net, scenes, min_max); + if (violators) + return checkViolators(net, scenes, min_max); else return checkMaxCount(net, max_count, scenes, min_max); } CapacitanceCheckSeq & -CheckCapacitances::checkViolations(const Net *net, - const SceneSeq &scenes, - const MinMax *min_max) +CheckCapacitances::checkViolators(const Net *net, + const SceneSeq &scenes, + const MinMax *min_max) { const Network *network = sta_->network(); if (net) { @@ -366,4 +368,4 @@ CapacitanceCheck::CapacitanceCheck(const Pin *pin, { } -} // namespace +} // namespace sta diff --git a/search/CheckCapacitances.hh b/search/CheckCapacitances.hh index bcd720516..fc105dfaf 100644 --- a/search/CheckCapacitances.hh +++ b/search/CheckCapacitances.hh @@ -24,14 +24,16 @@ #pragma once +#include #include +#include "BoundedHeap.hh" #include "MinMax.hh" -#include "Transition.hh" #include "NetworkClass.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "StaState.hh" -#include "BoundedHeap.hh" +#include "Transition.hh" namespace sta { @@ -110,9 +112,9 @@ protected: const SceneSeq &scenes, const MinMax *min_max, CapacitanceCheckHeap &heap); - CapacitanceCheckSeq &checkViolations(const Net *net, - const SceneSeq &scenes, - const MinMax *min_max); + CapacitanceCheckSeq &checkViolators(const Net *net, + const SceneSeq &scenes, + const MinMax *min_max); CapacitanceCheckSeq &checkMaxCount(const Net *net, size_t max_count, const SceneSeq &scenes, @@ -124,5 +126,5 @@ protected: CapacitanceCheckSeq checks_; }; -} // namespace +} // namespace sta diff --git a/search/CheckFanouts.cc b/search/CheckFanouts.cc index 07e2f0af6..7b3dc2c11 100644 --- a/search/CheckFanouts.cc +++ b/search/CheckFanouts.cc @@ -24,18 +24,23 @@ #include "CheckFanouts.hh" +#include + +#include "ClkNetwork.hh" #include "ContainerHelpers.hh" #include "Fuzzy.hh" +#include "Graph.hh" +#include "InputDrive.hh" #include "Liberty.hh" +#include "MinMax.hh" +#include "Mode.hh" #include "Network.hh" +#include "NetworkClass.hh" +#include "PortDirection.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Mode.hh" -#include "InputDrive.hh" #include "Sim.hh" -#include "PortDirection.hh" -#include "Graph.hh" -#include "Search.hh" -#include "ClkNetwork.hh" +#include "Transition.hh" namespace sta { @@ -331,4 +336,4 @@ FanoutCheckSlackLess::operator()(const FanoutCheck &check1, && sta_->network()->pinLess(check1.pin(), check2.pin())); } -} // namespace +} // namespace sta diff --git a/search/CheckFanouts.hh b/search/CheckFanouts.hh index 1bcb647bf..a4b8e7abc 100644 --- a/search/CheckFanouts.hh +++ b/search/CheckFanouts.hh @@ -24,13 +24,16 @@ #pragma once +#include #include +#include "BoundedHeap.hh" #include "MinMax.hh" +#include "Mode.hh" #include "NetworkClass.hh" #include "SdcClass.hh" #include "Sta.hh" -#include "BoundedHeap.hh" +#include "StaState.hh" namespace sta { @@ -122,5 +125,5 @@ protected: FanoutCheckHeap heap_; }; -} // namespace +} // namespace sta diff --git a/search/CheckMaxSkews.cc b/search/CheckMaxSkews.cc index 20cb52243..663694b33 100644 --- a/search/CheckMaxSkews.cc +++ b/search/CheckMaxSkews.cc @@ -24,14 +24,19 @@ #include "CheckMaxSkews.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" +#include + +#include "ContainerHelpers.hh" +#include "Delay.hh" +#include "Graph.hh" #include "Liberty.hh" #include "Network.hh" -#include "Graph.hh" -#include "Clock.hh" +#include "NetworkClass.hh" #include "Path.hh" #include "Search.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" namespace sta { @@ -40,10 +45,6 @@ CheckMaxSkews::CheckMaxSkews(StaState *sta) : { } -CheckMaxSkews::~CheckMaxSkews() -{ -} - void CheckMaxSkews::clear() { @@ -208,4 +209,4 @@ MaxSkewSlackLess::operator()(const MaxSkewCheck &check1, && sta_->network()->pinLess(check1.clkPin(sta_), check2.clkPin(sta_))); } -} // namespace +} // namespace sta diff --git a/search/CheckMaxSkews.hh b/search/CheckMaxSkews.hh index dc64f0c00..2d23a9e7c 100644 --- a/search/CheckMaxSkews.hh +++ b/search/CheckMaxSkews.hh @@ -24,14 +24,17 @@ #pragma once +#include #include -#include "GraphClass.hh" #include "Delay.hh" -#include "StaState.hh" -#include "SearchClass.hh" +#include "GraphClass.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" #include "Path.hh" -#include "MinMax.hh" +#include "Scene.hh" +#include "SearchClass.hh" +#include "StaState.hh" namespace sta { @@ -66,7 +69,6 @@ class CheckMaxSkews { public: CheckMaxSkews(StaState *sta); - ~CheckMaxSkews(); void clear(); // Return max skew checks. // net=null check all nets @@ -95,4 +97,4 @@ protected: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/CheckMinPeriods.cc b/search/CheckMinPeriods.cc index 195187c55..ef32b9724 100644 --- a/search/CheckMinPeriods.cc +++ b/search/CheckMinPeriods.cc @@ -24,13 +24,21 @@ #include "CheckMinPeriods.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" +#include + #include "Clock.hh" +#include "ContainerHelpers.hh" +#include "Delay.hh" #include "Graph.hh" #include "GraphDelayCalc.hh" +#include "Liberty.hh" +#include "Mode.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "Sdc.hh" +#include "SdcClass.hh" #include "Search.hh" +#include "SearchPred.hh" namespace sta { @@ -205,4 +213,4 @@ MinPeriodSlackLess::operator()(const MinPeriodCheck &check1, check2.clk())))); } -} // namespace +} // namespace sta diff --git a/search/CheckMinPeriods.hh b/search/CheckMinPeriods.hh index 9a0caa13a..87596108a 100644 --- a/search/CheckMinPeriods.hh +++ b/search/CheckMinPeriods.hh @@ -24,13 +24,17 @@ #pragma once -#include "NetworkClass.hh" -#include "GraphClass.hh" +#include +#include + +#include "BoundedHeap.hh" #include "Delay.hh" +#include "GraphClass.hh" +#include "NetworkClass.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" -#include "BoundedHeap.hh" namespace sta { @@ -96,4 +100,4 @@ protected: StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index fc5b0c074..bd3ae5f8d 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -24,22 +24,28 @@ #include "CheckMinPulseWidths.hh" +#include +#include + +#include "ClkInfo.hh" +#include "Clock.hh" #include "ContainerHelpers.hh" #include "Debug.hh" -#include "TimingRole.hh" +#include "Delay.hh" +#include "Graph.hh" +#include "GraphClass.hh" #include "Liberty.hh" #include "Network.hh" -#include "Graph.hh" -#include "Clock.hh" -#include "Sdc.hh" -#include "GraphDelayCalc.hh" -#include "ClkInfo.hh" -#include "Tag.hh" +#include "NetworkClass.hh" #include "Path.hh" -#include "Scene.hh" -#include "SearchPred.hh" #include "PathEnd.hh" +#include "Scene.hh" +#include "Sdc.hh" #include "Search.hh" +#include "SearchClass.hh" +#include "SearchPred.hh" +#include "Tag.hh" +#include "TimingArc.hh" #include "search/Crpr.hh" namespace sta { @@ -130,7 +136,7 @@ CheckMinPulseWidths::checkVertex(Vertex *vertex, if (isClkEnd(path_vertex, mode) && path->isClock(search) && !path->tag(sta_)->clkInfo()->isGenClkSrcPath() - && scene_set.find(path->scene(sta_)) != scene_set.end() + && scene_set.contains(path->scene(sta_)) && path->minMax(sta_) == min_max) { float min_width; bool exists; @@ -386,4 +392,4 @@ MinPulseWidthSlackLess::operator()(const MinPulseWidthCheck &check1, < check2.openPath()->rfIndex(sta_)))); } -} // namespace +} // namespace sta diff --git a/search/CheckMinPulseWidths.hh b/search/CheckMinPulseWidths.hh index 27e2aff31..5cd503cf3 100644 --- a/search/CheckMinPulseWidths.hh +++ b/search/CheckMinPulseWidths.hh @@ -24,15 +24,19 @@ #pragma once +#include #include #include -#include +#include "BoundedHeap.hh" +#include "Delay.hh" +#include "GraphClass.hh" +#include "NetworkClass.hh" +#include "Path.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" -#include "Path.hh" -#include "BoundedHeap.hh" namespace sta { @@ -107,4 +111,4 @@ protected: StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/CheckSlews.cc b/search/CheckSlews.cc index 760aeee5d..5ba9436a5 100644 --- a/search/CheckSlews.cc +++ b/search/CheckSlews.cc @@ -24,21 +24,28 @@ #include "CheckSlews.hh" +#include + +#include "ClkNetwork.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" +#include "Delay.hh" #include "Fuzzy.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "InputDrive.hh" #include "Liberty.hh" -#include "Network.hh" -#include "Sdc.hh" +#include "MinMax.hh" #include "Mode.hh" -#include "InputDrive.hh" -#include "Graph.hh" -#include "GraphDelayCalc.hh" -#include "StaState.hh" -#include "Scene.hh" +#include "Network.hh" +#include "NetworkClass.hh" #include "Path.hh" #include "PortDirection.hh" -#include "Sim.hh" -#include "Search.hh" -#include "ClkNetwork.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "SdcClass.hh" +#include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -417,4 +424,4 @@ SlewCheckSlackLess::operator()(const SlewCheck &check1, && sta_->network()->pinLess(check1.pin(), check2.pin())); } -} // namespace +} // namespace sta diff --git a/search/CheckSlews.hh b/search/CheckSlews.hh index 6921c53de..ca43bd5e2 100644 --- a/search/CheckSlews.hh +++ b/search/CheckSlews.hh @@ -24,15 +24,19 @@ #pragma once +#include +#include + +#include "BoundedHeap.hh" +#include "Delay.hh" +#include "GraphClass.hh" +#include "LibertyClass.hh" #include "MinMax.hh" -#include "Transition.hh" #include "NetworkClass.hh" -#include "GraphClass.hh" -#include "Delay.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "StaState.hh" -#include "BoundedHeap.hh" -#include "Network.hh" +#include "Transition.hh" namespace sta { @@ -164,5 +168,5 @@ protected: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc index 9b83593f7..f64975e5a 100644 --- a/search/CheckTiming.cc +++ b/search/CheckTiming.cc @@ -24,33 +24,30 @@ #include "CheckTiming.hh" -#include "Error.hh" -#include "TimingRole.hh" +#include "ClkNetwork.hh" +#include "ExceptionPath.hh" +#include "Format.hh" +#include "Genclks.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "Levelize.hh" +#include "Mode.hh" #include "Network.hh" +#include "NetworkClass.hh" #include "NetworkCmp.hh" -#include "PortDirection.hh" -#include "Graph.hh" +#include "Path.hh" #include "PortDelay.hh" -#include "ExceptionPath.hh" +#include "PortDirection.hh" #include "Sdc.hh" -#include "Mode.hh" -#include "SearchPred.hh" -#include "Levelize.hh" -#include "Bfs.hh" -#include "Search.hh" -#include "Genclks.hh" -#include "Path.hh" +#include "SdcClass.hh" #include "Sim.hh" -#include "ClkNetwork.hh" +#include "StaState.hh" +#include "TimingRole.hh" namespace sta { CheckTiming::CheckTiming(StaState *sta) : - StaState(sta), - mode_(nullptr), - sdc_(nullptr), - sim_(nullptr), - clk_network_(nullptr) + StaState(sta) { } @@ -224,12 +221,12 @@ CheckTiming::checkLoops() last_edge = edge; } if (last_edge) { - error->push_back("| loop cut point"); + error->emplace_back("| loop cut point"); const Pin *pin = last_edge->to(graph_)->pin(); error->push_back(sdc_network_->pathName(pin)); // Separator between loops. - error->push_back("--------------------------------"); + error->emplace_back("--------------------------------"); } } } @@ -394,4 +391,4 @@ CheckTiming::pushClkErrors(const char *msg, } } -} // namespace +} // namespace sta diff --git a/search/CheckTiming.hh b/search/CheckTiming.hh index 94f845b52..ca7b98862 100644 --- a/search/CheckTiming.hh +++ b/search/CheckTiming.hh @@ -24,13 +24,15 @@ #pragma once +#include #include -#include "StringUtil.hh" -#include "NetworkClass.hh" #include "GraphClass.hh" +#include "Mode.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" #include "StaState.hh" +#include "StringUtil.hh" namespace sta { @@ -43,8 +45,8 @@ class CheckTiming : public StaState { public: CheckTiming(StaState *sta); - ~CheckTiming(); - CheckErrorSeq &check(const Mode *sdc, + ~CheckTiming() override; + CheckErrorSeq &check(const Mode *mode, bool no_input_delay, bool no_output_delay, bool reg_multiple_clks, @@ -62,7 +64,7 @@ protected: bool reg_no_clks); void checkUnconstrainedEndpoints(); bool hasClkedArrival(Vertex *vertex); - void checkNoOutputDelay(PinSet &ends); + void checkNoOutputDelay(PinSet &no_departure); void checkUnconstrainedOutputs(PinSet &unconstrained_ends); void checkUnconstrainedSetups(PinSet &unconstrained_ends); void checkLoops(); @@ -76,10 +78,10 @@ protected: ClockSet &clks); CheckErrorSeq errors_; - const Mode *mode_; - const Sdc *sdc_; - const Sim *sim_; - const ClkNetwork *clk_network_; + const Mode *mode_{nullptr}; + const Sdc *sdc_{nullptr}; + const Sim *sim_{nullptr}; + const ClkNetwork *clk_network_{nullptr}; }; -} // namespace +} // namespace sta diff --git a/search/ClkDelays.hh b/search/ClkDelays.hh index e8a157499..5c410a9e9 100644 --- a/search/ClkDelays.hh +++ b/search/ClkDelays.hh @@ -24,10 +24,11 @@ #pragma once +#include "Delay.hh" #include "MinMax.hh" +#include "Path.hh" #include "StaState.hh" #include "Transition.hh" -#include "Path.hh" namespace sta { @@ -41,7 +42,7 @@ public: // Return values. Delay &insertion, Delay &delay, - float &internal_latency, + float &lib_clk_delay, Delay &latency, Path &path, bool &exists) const; @@ -49,7 +50,7 @@ public: const RiseFall *end_rf, const MinMax *min_max, // Return values. - Delay &delay, + Delay &latency, bool &exists) const; static Delay latency(Path *clk_path, StaState *sta); @@ -76,4 +77,4 @@ private: bool exists_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count]; }; -} // namespace +} // namespace sta diff --git a/search/ClkInfo.cc b/search/ClkInfo.cc index e3e3a05a2..535267995 100644 --- a/search/ClkInfo.cc +++ b/search/ClkInfo.cc @@ -26,13 +26,13 @@ #include -#include "Units.hh" -#include "Network.hh" #include "Graph.hh" -#include "Sdc.hh" +#include "Network.hh" #include "Scene.hh" +#include "Sdc.hh" #include "Search.hh" #include "Tag.hh" +#include "Units.hh" namespace sta { @@ -67,10 +67,6 @@ ClkInfo::ClkInfo(Scene *scene, findHash(sta); } -ClkInfo::~ClkInfo() -{ -} - void ClkInfo::findHash(const StaState *sta) { @@ -377,4 +373,4 @@ ClkInfo::cmp(const ClkInfo *clk_info1, return 0; } -} // namespace +} // namespace sta diff --git a/search/ClkInfo.hh b/search/ClkInfo.hh index 5efe7cabc..a83529d5f 100644 --- a/search/ClkInfo.hh +++ b/search/ClkInfo.hh @@ -24,10 +24,19 @@ #pragma once -#include "Transition.hh" -#include "SearchClass.hh" -#include "Sdc.hh" +#include +#include + +#include "Clock.hh" +#include "Delay.hh" +#include "GraphClass.hh" +#include "MinMax.hh" +#include "NetworkClass.hh" #include "Path.hh" +#include "Sdc.hh" +#include "SearchClass.hh" +#include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -49,7 +58,6 @@ public: const MinMax *min_max, const Path *crpr_clk_path, const StaState *sta); - ~ClkInfo(); std::string to_string(const StaState *sta) const; Scene *scene() const { return scene_; } const MinMax *minMax() const; @@ -111,7 +119,6 @@ class ClkInfoLess { public: ClkInfoLess(const StaState *sta); - ~ClkInfoLess() {} bool operator()(const ClkInfo *clk_info1, const ClkInfo *clk_info2) const; @@ -136,4 +143,4 @@ protected: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc index bc2d75f9e..1e972abc4 100644 --- a/search/ClkLatency.cc +++ b/search/ClkLatency.cc @@ -26,18 +26,18 @@ #include +#include "ClkInfo.hh" +#include "Clock.hh" #include "ContainerHelpers.hh" -#include "Report.hh" #include "Debug.hh" -#include "Units.hh" +#include "Graph.hh" #include "Liberty.hh" #include "Network.hh" -#include "Clock.hh" -#include "Graph.hh" #include "Path.hh" -#include "StaState.hh" +#include "Report.hh" #include "Search.hh" -#include "ClkInfo.hh" +#include "StaState.hh" +#include "Units.hh" namespace sta { diff --git a/search/ClkLatency.hh b/search/ClkLatency.hh index a07a4b763..313ff8718 100644 --- a/search/ClkLatency.hh +++ b/search/ClkLatency.hh @@ -26,12 +26,11 @@ #include +#include "ClkDelays.hh" #include "SdcClass.hh" -#include "StaState.hh" -#include "Transition.hh" +#include "Scene.hh" #include "SearchClass.hh" -#include "Path.hh" -#include "ClkDelays.hh" +#include "StaState.hh" namespace sta { @@ -60,4 +59,4 @@ protected: int digits); }; -} // namespace +} // namespace sta diff --git a/search/ClkNetwork.cc b/search/ClkNetwork.cc index 5f757b893..93a294965 100644 --- a/search/ClkNetwork.cc +++ b/search/ClkNetwork.cc @@ -24,22 +24,21 @@ #include "ClkNetwork.hh" +#include "Bfs.hh" #include "Debug.hh" -#include "Network.hh" #include "Graph.hh" -#include "Bfs.hh" -#include "Sdc.hh" #include "Mode.hh" -#include "SearchPred.hh" +#include "Network.hh" +#include "Sdc.hh" #include "Search.hh" +#include "SearchPred.hh" namespace sta { ClkNetwork::ClkNetwork(Mode *mode, StaState *sta) : StaState(sta), - mode_(mode), - clk_pins_valid_(false) + mode_(mode) { } @@ -264,4 +263,4 @@ ClkNetwork::idealClkSlew(const Pin *pin, return 0.0; } -} // namespace +} // namespace sta diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 89eab1ea4..f44086695 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -24,35 +24,34 @@ #include "ClkSkew.hh" -#include // abs #include +#include // abs #include -#include #include +#include -#include "Fuzzy.hh" -#include "Report.hh" +#include "Bfs.hh" +#include "Crpr.hh" #include "Debug.hh" #include "DispatchQueue.hh" -#include "Units.hh" -#include "TimingArc.hh" +#include "Fuzzy.hh" +#include "Graph.hh" #include "Liberty.hh" #include "Network.hh" -#include "Graph.hh" -#include "Sdc.hh" -#include "Bfs.hh" #include "Path.hh" -#include "StaState.hh" -#include "SearchPred.hh" -#include "Search.hh" -#include "Crpr.hh" #include "PathEnd.hh" +#include "Report.hh" +#include "Sdc.hh" +#include "Search.hh" +#include "SearchPred.hh" +#include "StaState.hh" +#include "TimingArc.hh" +#include "Units.hh" namespace sta { ClkSkews::ClkSkews(StaState *sta) : StaState(sta), - include_internal_latency_(true), fanout_pred_(this) { } @@ -204,8 +203,8 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, dispatch_queue_->finishTasks(); // 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 & i : partial_skews) { + for (auto &[clk, partial_skew] : i) { auto itr = skews_.find(clk); if (itr == skews_.end()) { // Insert new entry using emplace with piecewise_construct @@ -410,15 +409,6 @@ ClkSkew::ClkSkew(const ClkSkew &clk_skew) skew_ = clk_skew.skew_; } -void -ClkSkew::operator=(const ClkSkew &clk_skew) -{ - src_path_ = clk_skew.src_path_; - tgt_path_ = clk_skew.tgt_path_; - include_internal_latency_ = clk_skew.include_internal_latency_; - skew_ = clk_skew.skew_; -} - Arrival ClkSkew::srcLatency(const StaState *sta) { diff --git a/search/ClkSkew.hh b/search/ClkSkew.hh index 1ce1cf43f..3a49ff3f2 100644 --- a/search/ClkSkew.hh +++ b/search/ClkSkew.hh @@ -25,15 +25,19 @@ #pragma once #include - #include +#include "Delay.hh" +#include "GraphClass.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Path.hh" +#include "Scene.hh" #include "SdcClass.hh" -#include "StaState.hh" -#include "Transition.hh" #include "SearchClass.hh" #include "SearchPred.hh" -#include "Path.hh" +#include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -49,7 +53,7 @@ public: bool include_internal_latency, StaState *sta); ClkSkew(const ClkSkew &clk_skew); - void operator=(const ClkSkew &clk_skew); + ClkSkew &operator=(const ClkSkew &clk_skew) = default; Path *srcPath() { return src_path_; } Path *tgtPath() { return tgt_path_; } Arrival srcLatency(const StaState *sta); @@ -128,9 +132,9 @@ protected: ConstClockSeq clks_; ConstClockSet clk_set_; SceneSet scenes_set_; - bool include_internal_latency_; + bool include_internal_latency_{true}; FanOutSrchPred fanout_pred_; ClkSkewMap skews_; }; -} // namespace +} // namespace sta diff --git a/search/Crpr.cc b/search/Crpr.cc index 3d1295bb1..105f3a5c3 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -27,20 +27,20 @@ #include #include // abs +#include "ClkInfo.hh" #include "Debug.hh" -#include "Network.hh" +#include "Genclks.hh" #include "Graph.hh" -#include "Sdc.hh" +#include "Mode.hh" +#include "Network.hh" #include "Path.hh" -#include "ClkInfo.hh" -#include "Tag.hh" -#include "TagGroup.hh" -#include "VisitPathEnds.hh" #include "PathEnd.hh" +#include "Sdc.hh" #include "Search.hh" -#include "Genclks.hh" +#include "Tag.hh" +#include "TagGroup.hh" #include "Variables.hh" -#include "Mode.hh" +#include "VisitPathEnds.hh" namespace sta { @@ -384,4 +384,4 @@ CheckCrpr::crprPossible(const Clock *clk1, || intersects(&clk1->pins(), &clk2->pins(), network_)); } -} // namespace +} // namespace sta diff --git a/search/Crpr.hh b/search/Crpr.hh index 5c7e6e4f1..64ac97b07 100644 --- a/search/Crpr.hh +++ b/search/Crpr.hh @@ -24,9 +24,13 @@ #pragma once +#include "Clock.hh" +#include "Delay.hh" +#include "NetworkClass.hh" +#include "Path.hh" #include "SdcClass.hh" -#include "StaState.hh" #include "SearchClass.hh" +#include "StaState.hh" namespace sta { @@ -41,7 +45,7 @@ public: // Find the maximum possible crpr (clock min/max delta delay) for path. Arrival maxCrpr(const ClkInfo *clk_info); // Timing check CRPR. - Crpr checkCrpr(const Path *src_clk_path, + Crpr checkCrpr(const Path *src_path, const Path *tgt_clk_path); void checkCrpr(const Path *src_path, const Path *tgt_clk_path, @@ -51,7 +55,7 @@ public: // Output delay CRPR. Crpr outputDelayCrpr(const Path *src_clk_path, const ClockEdge *tgt_clk_edge); - void outputDelayCrpr(const Path *src_clk_path, + void outputDelayCrpr(const Path *src_path, const ClockEdge *tgt_clk_edge, // Return values. Crpr &crpr, @@ -83,7 +87,7 @@ private: bool same_pin, // Return values. Crpr &crpr, - Pin *&common_pin); + Pin *&crpr_pin); Path *portClkPath(const ClockEdge *clk_edge, const Pin *clk_src_pin, const Scene *scene, @@ -93,4 +97,4 @@ private: float crprArrivalDiff(const Path *path); }; -} // namespace +} // namespace sta diff --git a/search/FindRegister.cc b/search/FindRegister.cc index 8c0f3969b..bafcf79cc 100644 --- a/search/FindRegister.cc +++ b/search/FindRegister.cc @@ -24,18 +24,18 @@ #include "FindRegister.hh" -#include "TimingRole.hh" +#include "Clock.hh" #include "FuncExpr.hh" -#include "TimingArc.hh" -#include "Sequential.hh" +#include "Graph.hh" #include "Liberty.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" #include "Sdc.hh" -#include "Mode.hh" -#include "Clock.hh" -#include "SearchPred.hh" #include "Search.hh" +#include "SearchPred.hh" +#include "Sequential.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" namespace sta { @@ -82,10 +82,10 @@ class FindRegVisitor : public StaState { public: FindRegVisitor(const StaState *sta); - virtual ~FindRegVisitor() {} + ~FindRegVisitor() override = default; void visitRegs(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode); @@ -93,7 +93,7 @@ class FindRegVisitor : public StaState void visitRegs(const Pin *clk_pin, TimingSense clk_sense, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches); virtual void visitReg(Instance *inst) = 0; virtual void visitSequential(Instance *inst, @@ -101,7 +101,7 @@ class FindRegVisitor : public StaState void visitFanoutRegs(Vertex *from_vertex, TimingSense from_sense, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, SearchPred &clk_pred, VertexSet &visited_vertices, @@ -111,7 +111,7 @@ class FindRegVisitor : public StaState LibertyCell *cell, TimingSense clk_sense, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, // Return values. bool &has_seqs, @@ -119,7 +119,7 @@ class FindRegVisitor : public StaState bool findInferedSequential(LibertyCell *cell, TimingSense clk_sense, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches); bool hasTimingCheck(LibertyCell *cell, LibertyPort *clk, @@ -135,7 +135,7 @@ FindRegVisitor::FindRegVisitor(const StaState *sta) : void FindRegVisitor::visitRegs(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode) { @@ -148,14 +148,14 @@ FindRegVisitor::visitRegs(ClockSet *clks, Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); visitFanoutRegs(vertex, TimingSense::positive_unate, - clk_rf, edge_triggered, + clk_rf, registers, latches, clk_pred, visited_vertices, mode); // Clocks defined on bidirect pins blow it out both ends. if (bidirect_drvr_vertex) visitFanoutRegs(bidirect_drvr_vertex, TimingSense::positive_unate, - clk_rf, edge_triggered, + clk_rf, registers, latches, clk_pred, visited_vertices, mode); } @@ -165,7 +165,7 @@ FindRegVisitor::visitRegs(ClockSet *clks, for (Vertex *vertex : graph_->regClkVertices()) { visitRegs(vertex->pin(), TimingSense::positive_unate, RiseFallBoth::riseFall(), - edge_triggered, latches); + registers, latches); } } } @@ -174,7 +174,7 @@ void FindRegVisitor::visitFanoutRegs(Vertex *from_vertex, TimingSense from_sense, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, SearchPred &clk_pred, VertexSet &visited_vertices, @@ -190,11 +190,11 @@ FindRegVisitor::visitFanoutRegs(Vertex *from_vertex, const Pin *to_pin = to_vertex->pin(); TimingSense to_sense = pathSenseThru(from_sense, edge->sense()); if (to_vertex->isRegClk()) - visitRegs(to_pin, to_sense, clk_rf, edge_triggered, latches); + visitRegs(to_pin, to_sense, clk_rf, registers, latches); // Even register clock pins can have combinational fanout arcs. if (clk_pred.searchThru(edge, mode) && clk_pred.searchTo(to_vertex, mode)) - visitFanoutRegs(to_vertex, to_sense, clk_rf, edge_triggered, latches, + visitFanoutRegs(to_vertex, to_sense, clk_rf, registers, latches, clk_pred, visited_vertices, mode); } } @@ -204,20 +204,20 @@ void FindRegVisitor::visitRegs(const Pin *clk_pin, TimingSense clk_sense, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches) { Instance *inst = network_->instance(clk_pin); LibertyCell *cell = network_->libertyCell(inst); - if (!edge_triggered || !latches + if (!registers || !latches || clk_rf != RiseFallBoth::riseFall()) { bool matches, has_seqs; findSequential(clk_pin, inst, cell, clk_sense, clk_rf, - edge_triggered, latches, + registers, latches, has_seqs, matches); if (!has_seqs) matches = findInferedSequential(cell, clk_sense, clk_rf, - edge_triggered, latches); + registers, latches); if (matches) visitReg(inst); } @@ -233,7 +233,7 @@ FindRegVisitor::findSequential(const Pin *clk_pin, LibertyCell *cell, TimingSense clk_sense, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, // Return values. bool &has_seqs, @@ -243,7 +243,7 @@ FindRegVisitor::findSequential(const Pin *clk_pin, matches = false; for (const Sequential &seq : cell->sequentials()) { has_seqs = true; - if ((seq.isRegister() && edge_triggered) + if ((seq.isRegister() && registers) || (seq.isLatch() && latches)) { if (clk_rf == RiseFallBoth::riseFall()) { visitSequential(inst, &seq); @@ -272,7 +272,7 @@ bool FindRegVisitor::findInferedSequential(LibertyCell *cell, TimingSense clk_sense, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches) { bool matches = false; @@ -288,7 +288,7 @@ FindRegVisitor::findInferedSequential(LibertyCell *cell, const TimingRole *role = arc_set->role(); if (tr_matches && ((role == TimingRole::regClkToQ() - && edge_triggered) + && registers) || (role == TimingRole::latchEnToQ() && latches))) { matches = true; @@ -317,7 +317,7 @@ class FindRegInstances : public FindRegVisitor FindRegInstances(const StaState *sta); InstanceSet findRegs(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode); @@ -338,11 +338,11 @@ FindRegInstances::FindRegInstances(const StaState *sta) : InstanceSet FindRegInstances::findRegs(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode) { - visitRegs(clks, clk_rf, edge_triggered, latches, mode); + visitRegs(clks, clk_rf, registers, latches, mode); return regs_; } @@ -361,13 +361,13 @@ FindRegInstances::visitReg(Instance *inst) InstanceSet findRegInstances(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta) { FindRegInstances find_regs(sta); - return find_regs.findRegs(clks, clk_rf, edge_triggered, latches, mode); + return find_regs.findRegs(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -378,14 +378,15 @@ class FindRegPins : public FindRegVisitor FindRegPins(const StaState *sta); PinSet findPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode); -protected: +private: void visitReg(Instance *inst) override; void visitSequential(Instance *inst, const Sequential *seq) override; +protected: virtual bool matchPin(Pin *pin); void visitExpr(FuncExpr *expr, Instance *inst, @@ -406,11 +407,11 @@ FindRegPins::FindRegPins(const StaState *sta) : PinSet FindRegPins::findPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode) { - visitRegs(clks, clk_rf, edge_triggered, latches, mode); + visitRegs(clks, clk_rf, registers, latches, mode); return pins_; } @@ -460,7 +461,7 @@ class FindRegDataPins : public FindRegPins public: FindRegDataPins(const StaState *sta); -private: +protected: bool matchPin(Pin *pin) override; FuncExpr *seqExpr1(const Sequential *seq) override; FuncExpr *seqExpr2(const Sequential *seq) override; @@ -511,13 +512,13 @@ hasMinPulseWidthCheck(LibertyPort *port) PinSet findRegDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta) { FindRegDataPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches, mode); + return find_regs.findPins(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -527,7 +528,7 @@ class FindRegClkPins : public FindRegPins public: FindRegClkPins(const StaState *sta); -private: +protected: bool matchPin(Pin *pin) override; FuncExpr *seqExpr1(const Sequential *seq) override; FuncExpr *seqExpr2(const Sequential *seq) override; @@ -569,13 +570,13 @@ FindRegClkPins::seqExpr2(const Sequential *) PinSet findRegClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta) { FindRegClkPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches, mode); + return find_regs.findPins(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -585,7 +586,7 @@ class FindRegAsyncPins : public FindRegPins public: FindRegAsyncPins(const StaState *sta); -private: +protected: bool matchPin(Pin *pin) override; FuncExpr *seqExpr1(const Sequential *seq) override { return seq->clear(); } FuncExpr *seqExpr2(const Sequential *seq) override { return seq->preset(); } @@ -612,13 +613,13 @@ FindRegAsyncPins::matchPin(Pin *pin) PinSet findRegAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta) { FindRegAsyncPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches, mode); + return find_regs.findPins(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -628,15 +629,17 @@ class FindRegOutputPins : public FindRegPins public: FindRegOutputPins(const StaState *sta); -private: +protected: bool matchPin(Pin *pin) override; + // Unused. + FuncExpr *seqExpr1(const Sequential *seq) override; + FuncExpr *seqExpr2(const Sequential *seq) override; + +private: void visitSequential(Instance *inst, const Sequential *seq) override; void visitOutput(LibertyPort *port, Instance *inst); - // Unused. - FuncExpr *seqExpr1(const Sequential *seq) override; - FuncExpr *seqExpr2(const Sequential *seq) override; }; FindRegOutputPins::FindRegOutputPins(const StaState *sta) : @@ -704,13 +707,13 @@ FindRegOutputPins::seqExpr2(const Sequential *) PinSet findRegOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta) { FindRegOutputPins find_regs(sta); - return find_regs.findPins(clks, clk_rf, edge_triggered, latches, mode); + return find_regs.findPins(clks, clk_rf, registers, latches, mode); } //////////////////////////////////////////////////////////////// @@ -722,7 +725,7 @@ initPathSenseThru1(TimingSense from, TimingSense thru, TimingSense to) { - path_sense_thru[int(from)][int(thru)] = to; + path_sense_thru[static_cast(from)][static_cast(thru)] = to; } void @@ -787,7 +790,7 @@ static TimingSense pathSenseThru(TimingSense from_sense, TimingSense thru_sense) { - return path_sense_thru[int(from_sense)][int(thru_sense)]; + return path_sense_thru[static_cast(from_sense)][static_cast(thru_sense)]; } -} // namespace +} // namespace sta diff --git a/search/FindRegister.hh b/search/FindRegister.hh index 65ec3d618..36f9cd894 100644 --- a/search/FindRegister.hh +++ b/search/FindRegister.hh @@ -25,44 +25,46 @@ #pragma once #include "LibertyClass.hh" +#include "Mode.hh" #include "NetworkClass.hh" #include "SdcClass.hh" #include "StaState.hh" +#include "Transition.hh" namespace sta { InstanceSet findRegInstances(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta); PinSet findRegDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta); PinSet findRegClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta); PinSet findRegAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta); PinSet findRegOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode, const StaState *sta); @@ -70,4 +72,4 @@ findRegOutputPins(ClockSet *clks, void initPathSenseThru(); -} // namespace +} // namespace sta diff --git a/search/GatedClk.cc b/search/GatedClk.cc index fdba28de2..11880d025 100644 --- a/search/GatedClk.cc +++ b/search/GatedClk.cc @@ -24,15 +24,15 @@ #include "GatedClk.hh" +#include "ClkNetwork.hh" #include "FuncExpr.hh" -#include "Liberty.hh" -#include "PortDirection.hh" -#include "Network.hh" #include "Graph.hh" +#include "Liberty.hh" #include "Mode.hh" +#include "Network.hh" +#include "PortDirection.hh" #include "Sdc.hh" #include "Search.hh" -#include "ClkNetwork.hh" namespace sta { @@ -259,4 +259,4 @@ GatedClk::gatedClkActiveTrans(LogicValue active_value, return leading_rf->opposite(); } -} // namespace +} // namespace sta diff --git a/search/GatedClk.hh b/search/GatedClk.hh index aeade0c5f..c6853dbc4 100644 --- a/search/GatedClk.hh +++ b/search/GatedClk.hh @@ -26,10 +26,14 @@ #include -#include "SdcClass.hh" #include "GraphClass.hh" -#include "SearchClass.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" #include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -64,4 +68,4 @@ protected: FuncExprSet &funcs) const; }; -} // namespace +} // namespace sta diff --git a/search/Genclks.cc b/search/Genclks.cc index 3dee07b99..48dbea192 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -26,25 +26,25 @@ #include +#include "Bfs.hh" +#include "Clock.hh" #include "ContainerHelpers.hh" -#include "Stats.hh" #include "Debug.hh" -#include "Report.hh" +#include "ExceptionPath.hh" +#include "Graph.hh" +#include "Levelize.hh" +#include "Mode.hh" #include "Network.hh" +#include "Path.hh" #include "PortDirection.hh" -#include "Graph.hh" +#include "Report.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Mode.hh" -#include "ExceptionPath.hh" -#include "Clock.hh" -#include "StaState.hh" +#include "Search.hh" #include "SearchPred.hh" -#include "Bfs.hh" +#include "StaState.hh" +#include "Stats.hh" #include "TagGroup.hh" -#include "Scene.hh" -#include "Levelize.hh" -#include "Path.hh" -#include "Search.hh" #include "Variables.hh" namespace sta { @@ -69,7 +69,7 @@ class GenclkInfo Level gclk_level_; VertexSet fanins_; EdgeSet fdbk_edges_; - bool found_latch_fdbk_edges_; + bool found_latch_fdbk_edges_{false}; FilterPath *src_filter_; }; @@ -80,7 +80,6 @@ GenclkInfo::GenclkInfo(Clock *gclk, gclk_(gclk), gclk_level_(gclk_level), fanins_(makeVertexSet(sta->graph())), - found_latch_fdbk_edges_(false), src_filter_(src_filter) { } @@ -99,7 +98,6 @@ Genclks::Genclks(const Mode *mode, StaState *sta) : StaState(sta), mode_(mode), - found_insertion_delays_(false), vertex_src_paths_map_(graph_) { } @@ -229,8 +227,9 @@ GenClkMasterSearchPred::GenClkMasterSearchPred(const StaState *sta) : bool GenClkMasterSearchPred::searchThruAllow(const TimingRole *role) const { - return (role->isWire() || role == TimingRole::combinational() - || role->regClkToQ()); + return role->isWire() + || role == TimingRole::combinational() + || role == TimingRole::regClkToQ(); } //////////////////////////////////////////////////////////////// @@ -339,8 +338,9 @@ GenClkFaninSrchPred::GenClkFaninSrchPred(Clock *gclk, bool GenClkFaninSrchPred::searchThruAllow(const TimingRole *role) const { - return (role == TimingRole::combinational() || role == TimingRole::wire() - || !combinational_); + return role == TimingRole::combinational() + || role == TimingRole::wire() + || !combinational_; } void @@ -682,6 +682,7 @@ bool GenClkArrivalSearchPred::searchTo(const Vertex *to_vertex, const Mode *mode) const { + // NOLINTNEXTLINE(bugprone-parent-virtual-call) return SearchPred0::searchTo(to_vertex, mode); } @@ -716,13 +717,11 @@ GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, BfsFwdIterator *insert_iter, GenclkInfo *genclk_info, const Mode *mode) : - ArrivalVisitor(mode), + ArrivalVisitor(mode->sta()), gclk_(gclk), insert_iter_(insert_iter), genclk_info_(genclk_info), - srch_pred_(gclk_, - genclk_info, - mode), + srch_pred_(gclk_, genclk_info, mode->sta()), mode_(mode), sdc_(mode->sdc()), genclks_(mode->genclks()) @@ -736,15 +735,11 @@ GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, bool always_to_endpoints, SearchPred *pred, const Mode *mode) : - ArrivalVisitor(always_to_endpoints, - pred, - mode), + ArrivalVisitor(always_to_endpoints, pred, this), gclk_(gclk), insert_iter_(insert_iter), genclk_info_(genclk_info), - srch_pred_(gclk, - genclk_info, - mode), + srch_pred_(gclk, genclk_info, this), mode_(mode), sdc_(mode->sdc()), genclks_(mode->genclks()) @@ -816,7 +811,7 @@ Genclks::copyGenClkSrcPaths(Vertex *vertex, void Genclks::clearSrcPaths() { - for (auto [vertex, paths] : vertex_src_paths_map_) { + for (const auto& [vertex, paths] : vertex_src_paths_map_) { for (const Path *path : paths) delete path; } @@ -888,11 +883,14 @@ 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 {} source pin {} missing paths from master clock {}.", - 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); } @@ -901,7 +899,7 @@ void Genclks::deleteGenclkSrcPaths(Clock *gclk) { GenclkInfo *genclk_info = genclkInfo(gclk); - GenClkInsertionSearchPred srch_pred(gclk, genclk_info, mode_); + GenClkInsertionSearchPred srch_pred(gclk, genclk_info, mode_->sta()); BfsFwdIterator insert_iter(BfsIndex::other, &srch_pred, this); FilterPath *src_filter = genclk_info->srcFilter(); seedSrcPins(gclk, src_filter, insert_iter); diff --git a/search/Genclks.hh b/search/Genclks.hh index 195e95385..67cc55de0 100644 --- a/search/Genclks.hh +++ b/search/Genclks.hh @@ -24,14 +24,20 @@ #pragma once +#include #include +#include +#include -#include "Transition.hh" -#include "NetworkClass.hh" +#include "Clock.hh" #include "Graph.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -59,7 +65,7 @@ class Genclks : public StaState public: Genclks(const Mode *mode, StaState *sta); - virtual ~Genclks(); + ~Genclks() override; void clear(); void ensureInsertionDelays(); VertexSet *fanins(const Clock *clk); @@ -137,10 +143,10 @@ private: void deleteGenclkSrcPaths(Clock *gclk); const Mode *mode_; - bool found_insertion_delays_; + bool found_insertion_delays_{false}; GenclkSrcPathMap genclk_src_paths_; GenclkInfoMap genclk_info_map_; VertexGenclkSrcPathsMap vertex_src_paths_map_; }; -} // namespace +} // namespace sta diff --git a/search/Latches.cc b/search/Latches.cc index c701e7fd4..f8d5b68ef 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -24,21 +24,21 @@ #include "Latches.hh" +#include "ClkInfo.hh" +#include "Crpr.hh" #include "Debug.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Graph.hh" #include "ExceptionPath.hh" -#include "Sdc.hh" +#include "Graph.hh" +#include "Liberty.hh" #include "Mode.hh" -#include "ClkInfo.hh" -#include "Tag.hh" -#include "Sim.hh" +#include "Network.hh" #include "PathEnd.hh" +#include "Sdc.hh" #include "Search.hh" -#include "Crpr.hh" +#include "Sim.hh" +#include "Tag.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" namespace sta { @@ -603,4 +603,4 @@ Latches::isLatchDtoQ(const Edge *edge, && latchDtoQState(edge, mode) == LatchEnableState::enabled; } -} // namespace +} // namespace sta diff --git a/search/Latches.hh b/search/Latches.hh index 33980a201..4d31e8d5a 100644 --- a/search/Latches.hh +++ b/search/Latches.hh @@ -24,10 +24,15 @@ #pragma once +#include "Delay.hh" #include "GraphClass.hh" -#include "SearchClass.hh" +#include "LibertyClass.hh" +#include "Mode.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" +#include "SearchClass.hh" #include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -108,4 +113,4 @@ protected: const ClockEdge *en_clk_edge); }; -} // namespace +} // namespace sta diff --git a/search/Levelize.cc b/search/Levelize.cc index 5302be0a0..2219a91d4 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -29,30 +29,25 @@ #include #include "ContainerHelpers.hh" -#include "Report.hh" #include "Debug.hh" -#include "Stats.hh" -#include "TimingRole.hh" -#include "PortDirection.hh" -#include "Network.hh" -#include "Sdc.hh" -#include "Mode.hh" #include "Graph.hh" #include "GraphCmp.hh" -#include "Variables.hh" #include "GraphDelayCalc.hh" +#include "Mode.hh" +#include "Network.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "Sdc.hh" +#include "Stats.hh" +#include "TimingRole.hh" +#include "Variables.hh" namespace sta { Levelize::Levelize(StaState *sta) : StaState(sta), - levelized_(false), - levels_valid_(false), - max_level_(0), - level_space_(10), roots_(makeVertexSet(sta)), - relevelize_from_(makeVertexSet(sta)), - observer_(nullptr) + relevelize_from_(makeVertexSet(sta)) { } @@ -106,7 +101,7 @@ Levelize::ensureLevelized() if (levelized_) relevelize(); else - levelize(); + findLevels(); } } @@ -114,7 +109,7 @@ Levelize::ensureLevelized() #define setOnPath(on_path) setVisited2(on_path) void -Levelize::levelize() +Levelize::findLevels() { Stats stats(debug_, report_); debugPrint(debug_, "levelize", 1, "levelize"); diff --git a/search/Levelize.hh b/search/Levelize.hh index 3a30d869b..a1b680e8f 100644 --- a/search/Levelize.hh +++ b/search/Levelize.hh @@ -25,11 +25,10 @@ #pragma once #include +#include +#include -#include "NetworkClass.hh" -#include "SdcClass.hh" #include "Graph.hh" -#include "SearchPred.hh" #include "StaState.hh" namespace sta { @@ -46,7 +45,7 @@ class Levelize : public StaState { public: Levelize(StaState *sta); - virtual ~Levelize(); + ~Levelize() override; // Space between initially assigned levels that is filled in by // incremental levelization. Set level space before levelization. void setLevelSpace(Level space); @@ -73,7 +72,7 @@ public: void setObserver(LevelizeObserver *observer); void checkLevels(); // Public for regression testing. - void levelize(); + void findLevels(); protected: void findRoots(); @@ -105,17 +104,17 @@ protected: void deleteLoops(); void reportPath(EdgeSeq &path) const; - bool levelized_; - bool levels_valid_; - Level max_level_; - Level level_space_; + bool levelized_{false}; + bool levels_valid_{false}; + Level max_level_{0}; + Level level_space_{10}; VertexSet roots_; VertexSet relevelize_from_; GraphLoopSeq loops_; EdgeSet loop_edges_; EdgeSet disabled_loop_edges_; EdgeSet latch_d_to_q_edges_; - LevelizeObserver *observer_; + LevelizeObserver *observer_{nullptr}; }; // Loops broken by levelization may not necessarily be combinational. @@ -137,10 +136,9 @@ private: class LevelizeObserver { public: - LevelizeObserver() {} - virtual ~LevelizeObserver() {} + virtual ~LevelizeObserver() = default; virtual void levelsChangedBefore() = 0; virtual void levelChangedBefore(Vertex *vertex) = 0; }; -} // namespace +} // namespace sta diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index d4fd82f09..7e61d19cc 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -26,29 +26,43 @@ #include "MakeTimingModelPvt.hh" #include -#include +#include #include +#include +#include +#include +#include +#include "ArcDelayCalc.hh" +#include "ClkDelays.hh" +#include "Clock.hh" +#include "ContainerHelpers.hh" #include "Debug.hh" -#include "Units.hh" -#include "Transition.hh" +#include "Delay.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "TimingArc.hh" -#include "TableModel.hh" -#include "liberty/LibertyBuilder.hh" +#include "LibertyClass.hh" #include "Network.hh" +#include "NetworkClass.hh" +#include "Path.hh" +#include "PathEnd.hh" #include "PortDirection.hh" +#include "RiseFallMinMax.hh" #include "Scene.hh" -#include "GraphDelayCalc.hh" #include "Sdc.hh" -#include "StaState.hh" -#include "Graph.hh" -#include "PathEnd.hh" +#include "SdcClass.hh" #include "Search.hh" #include "Sta.hh" +#include "StaState.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" #include "VisitPathEnds.hh" -#include "ArcDelayCalc.hh" -#include "ClkLatency.hh" +#include "liberty/LibertyBuilder.hh" namespace sta { @@ -73,13 +87,10 @@ MakeTimingModel::MakeTimingModel(std::string_view lib_name, cell_name_(cell_name), filename_(filename), scene_(scene), - cell_(nullptr), min_max_(MinMax::max()), lib_builder_(new LibertyBuilder(debug_, report_)), - tbl_template_index_(1), sdc_(scene->sdc()), - sdc_backup_(nullptr), sta_(sta) { scenes_.insert(scene_); @@ -232,20 +243,18 @@ class MakeEndTimingArcs : public PathEndVisitor public: MakeEndTimingArcs(Sta *sta); MakeEndTimingArcs(const MakeEndTimingArcs &) = default; - ~MakeEndTimingArcs() override {} PathEndVisitor *copy() const override; void visit(PathEnd *path_end) override; void setInputRf(const RiseFall *input_rf); const ClockEdgeDelays &margins() const { return margins_; } private: - const RiseFall *input_rf_; + const RiseFall *input_rf_{nullptr}; ClockEdgeDelays margins_; Sta *sta_; }; MakeEndTimingArcs::MakeEndTimingArcs(Sta *sta) : - input_rf_(nullptr), sta_(sta) { } @@ -673,8 +682,7 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, const FloatSeq &drvr_axis_values = drvr_load_axis->values(); FloatSeq *load_values = new FloatSeq; FloatSeq *slew_values = new FloatSeq; - for (size_t i = 0; i < drvr_axis_values.size(); i++) { - float load_cap = drvr_axis_values[i]; + for (float load_cap : drvr_axis_values) { // get slew from driver input pin float gate_delay, gate_slew; drvr_gate_model->gateDelay(pvt, in_slew1, load_cap, @@ -723,7 +731,7 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, TableTemplate * MakeTimingModel::ensureTableTemplate(const TableTemplate *drvr_template, - TableAxisPtr load_axis) + const TableAxisPtr &load_axis) { TableTemplate *model_template = findKey(template_map_, drvr_template); if (model_template == nullptr) { diff --git a/search/MakeTimingModel.hh b/search/MakeTimingModel.hh index 9056bce7c..201c65480 100644 --- a/search/MakeTimingModel.hh +++ b/search/MakeTimingModel.hh @@ -39,4 +39,4 @@ makeTimingModel(std::string_view lib_name, const Scene *scene, Sta *sta); -} // namespace +} // namespace sta diff --git a/search/MakeTimingModelPvt.hh b/search/MakeTimingModelPvt.hh index 07470e4e7..47dba1482 100644 --- a/search/MakeTimingModelPvt.hh +++ b/search/MakeTimingModelPvt.hh @@ -28,11 +28,14 @@ #include #include +#include "Delay.hh" #include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "RiseFallMinMax.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" -#include "RiseFallMinMax.hh" namespace sta { @@ -61,7 +64,7 @@ public: std::string_view filename, const Scene *scene, Sta *sta); - ~MakeTimingModel(); + ~MakeTimingModel() override; LibertyLibrary *makeTimingModel(); private: @@ -96,7 +99,7 @@ private: Delay delay, const RiseFall *rf); TableTemplate *ensureTableTemplate(const TableTemplate *drvr_template, - TableAxisPtr load_axis); + const TableAxisPtr &load_axis); const TableAxis *loadCapacitanceAxis(const TableModel *table); LibertyPort *modelPort(const Pin *pin); @@ -110,15 +113,15 @@ private: const Scene *scene_; SceneSet scenes_; LibertyLibrary *library_; - LibertyCell *cell_; + LibertyCell *cell_{nullptr}; const MinMax *min_max_; LibertyBuilder *lib_builder_; // Output driver table model template to model template. std::map template_map_; - int tbl_template_index_; + int tbl_template_index_{1}; Sdc *sdc_; - Sdc *sdc_backup_; + Sdc *sdc_backup_{nullptr}; Sta *sta_; }; -} // namespace +} // namespace sta diff --git a/search/Mode.cc b/search/Mode.cc index a73ea28ef..f822ff924 100644 --- a/search/Mode.cc +++ b/search/Mode.cc @@ -22,27 +22,28 @@ // // This notice may not be removed or altered from any source distribution. +#include + #include "Mode.hh" -#include "Sdc.hh" -#include "Sim.hh" #include "ClkNetwork.hh" #include "Genclks.hh" #include "PathGroup.hh" +#include "Sdc.hh" +#include "Sim.hh" namespace sta { Mode::Mode(std::string_view name, size_t mode_index, StaState *sta) : - StaState(sta), name_(name), mode_index_(mode_index), sdc_(new Sdc(this, sta)), sim_(new Sim(sta)), clk_network_(new ClkNetwork(this, sta)), genclks_(new Genclks(this, sta)), - path_groups_(nullptr) + sta_(sta) { } @@ -58,7 +59,7 @@ Mode::~Mode() void Mode::copyState(const StaState *sta) { - StaState::copyState(sta); + sta_->copyState(sta); sdc_->copyState(sta); sim_->copyState(sta); clk_network_->copyState(sta); @@ -84,10 +85,11 @@ void Mode::removeScene(Scene *scene) { // std iterators just plain suck - scenes_.erase(std::remove(scenes_.begin(), scenes_.end(), scene), scenes_.end()); + auto tail = std::ranges::remove(scenes_, scene); + scenes_.erase(tail.begin(), tail.end()); } -const SceneSet +SceneSet Mode::sceneSet() const { SceneSet scenes; @@ -143,4 +145,4 @@ Mode::pathGroups(const PathEnd *path_end) const return PathGroupSeq(); } -} // namespace +} // namespace sta diff --git a/search/Path.cc b/search/Path.cc index e7f78bde7..2dcc621e6 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -24,16 +24,27 @@ #include "Path.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Network.hh" -#include "Graph.hh" +#include +#include + #include "Clock.hh" -#include "Tag.hh" -#include "TagGroup.hh" -#include "Sdc.hh" +#include "Delay.hh" +#include "Format.hh" +#include "Graph.hh" +#include "GraphClass.hh" #include "Mode.hh" +#include "Network.hh" +#include "NetworkClass.hh" +#include "Sdc.hh" +#include "SdcClass.hh" #include "Search.hh" +#include "SearchClass.hh" +#include "StaState.hh" +#include "Tag.hh" +#include "TagGroup.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "VertexId.hh" namespace sta { @@ -326,7 +337,7 @@ Path::pathAnalysisPtIndex(const StaState *sta) const return scene(sta)->pathIndex(minMax(sta)); } -const Slew +Slew Path::slew(const StaState *sta) const { DcalcAPIndex slew_index = scene(sta)->dcalcAnalysisPtIndex(minMax(sta)); @@ -786,8 +797,6 @@ VertexPathIterator::findNext() next_ = nullptr; } -VertexPathIterator::~VertexPathIterator() {} - bool VertexPathIterator::hasNext() { diff --git a/search/PathEnd.cc b/search/PathEnd.cc index da7d0d774..819207521 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -24,33 +24,32 @@ #include "PathEnd.hh" +#include "ClkInfo.hh" +#include "Clock.hh" +#include "DataCheck.hh" #include "Debug.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" +#include "ExceptionPath.hh" +#include "Graph.hh" +#include "Latches.hh" #include "Liberty.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" -#include "Clock.hh" +#include "PathExpanded.hh" #include "PortDelay.hh" -#include "DataCheck.hh" +#include "ReportPath.hh" #include "Sdc.hh" -#include "Mode.hh" -#include "ExceptionPath.hh" -#include "ClkInfo.hh" -#include "Tag.hh" #include "Search.hh" -#include "ReportPath.hh" #include "Sim.hh" -#include "Latches.hh" #include "StaState.hh" -#include "PathExpanded.hh" +#include "Tag.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" #include "search/Crpr.hh" namespace sta { PathEnd::PathEnd(Path *path) : - path_(path), - path_group_(nullptr) + path_(path) { } @@ -419,6 +418,14 @@ PathEnd::checkInterClkUncertainty(const ClockEdge *src_clk_edge, exists = false; } +bool +PathEnd::ignoreClkLatency(const Path *path, + PathDelay *path_delay, + const StaState *sta) +{ + return path_delay->ignoreClkLatency() && !path->isClock(sta); +} + //////////////////////////////////////////////////////////////// void @@ -507,8 +514,7 @@ PathEndClkConstrained::PathEndClkConstrained(Path *path, Path *clk_path) : PathEnd(path), clk_path_(clk_path), - crpr_(0.0), - crpr_valid_(false) + crpr_(0.0) { } @@ -609,9 +615,7 @@ PathEndClkConstrained::targetClkTime(const StaState *sta) const Arrival PathEndClkConstrained::targetClkArrival(const StaState *sta) const { - return delaySum(targetClkArrivalNoCrpr(sta), - checkCrpr(sta), - sta); + return delaySum(targetClkArrivalNoCrpr(sta), checkCrpr(sta), sta); } Arrival @@ -1090,7 +1094,7 @@ PathEndLatchCheck::PathEndLatchCheck(Path *path, clk_path_ = enable_path; Search *search = sta->search(); // Same as PathEndPathDelay::findRequired. - if (path_delay_ && ignoreClkLatency(sta)) + if (path_delay_ && PathEnd::ignoreClkLatency(path_, path_delay_, sta)) src_clk_arrival_ = search->pathClkPathArrival(path_); } @@ -1266,7 +1270,7 @@ int PathEndLatchCheck::exceptPathCmp(const PathEnd *path_end, const StaState *sta) const { - int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); + int cmp = PathEndCheck::exceptPathCmp(path_end, sta); if (cmp == 0) { const PathEndLatchCheck *path_end2 = dynamic_cast(path_end); @@ -1765,7 +1769,7 @@ PathEndPathDelay::typeName() const void PathEndPathDelay::findSrcClkArrival(const StaState *sta) { - if (ignoreClkLatency(sta)) { + if (PathEnd::ignoreClkLatency(path_, path_delay_, sta)) { Search *search = sta->search(); src_clk_arrival_ = search->pathClkPathArrival(path_); } @@ -1851,14 +1855,6 @@ PathEnd::pathDelaySrcClkOffset(const Path *path, return offset; } -bool -PathEnd::ignoreClkLatency(const Path *path, - PathDelay *path_delay, - const StaState *sta) -{ - return path_delay->ignoreClkLatency() && !path->isClock(sta); -} - const ClockEdge * PathEndPathDelay::targetClkEdge(const StaState *sta) const { @@ -2119,4 +2115,4 @@ PathEndNoCrprLess::operator()(const PathEnd *path_end1, return cmp < 0; } -} // namespace +} // namespace sta diff --git a/search/PathEnum.cc b/search/PathEnum.cc index 437966a84..c285b8f99 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -24,20 +24,28 @@ #include "PathEnum.hh" +#include +#include +#include +#include + #include "Debug.hh" +#include "Delay.hh" #include "Error.hh" -#include "Fuzzy.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Network.hh" -#include "Sdc.hh" -#include "Mode.hh" #include "Graph.hh" -#include "Tag.hh" -#include "Search.hh" +#include "GraphClass.hh" #include "Latches.hh" -#include "PathEnd.hh" +#include "Mode.hh" +#include "Network.hh" #include "Path.hh" +#include "PathEnd.hh" +#include "Sdc.hh" +#include "Search.hh" +#include "SearchClass.hh" +#include "Tag.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "VertexVisitor.hh" namespace sta { @@ -118,10 +126,7 @@ PathEnum::PathEnum(size_t group_path_count, endpoint_path_count_(endpoint_path_count), unique_pins_(unique_pins), unique_edges_(unique_edges), - div_queue_(DiversionGreater(sta)), - div_count_(0), - inserts_pruned_(false), - next_(nullptr) + div_queue_(DiversionGreater(sta)) { } @@ -260,6 +265,15 @@ class PathEnumFaninVisitor : public PathVisitor Arrival &to_arrival, const MinMax *min_max) override; + void visit(Vertex *) override {} // Not used. + +protected: + bool visitEdge(const Pin *from_pin, + Vertex *from_vertex, + Edge *edge, + const Pin *to_pin, + Vertex *to_vertex) override; + private: void makeDivertedPathEnd(Path *after_div, Edge *div_edge, @@ -267,12 +281,6 @@ class PathEnumFaninVisitor : public PathVisitor // Return values. PathEnd *&div_end, Path *&after_div_copy); - bool visitEdge(const Pin *from_pin, - Vertex *from_vertex, - Edge *edge, - const Pin *to_pin, - Vertex *to_vertex) override; - void visit(Vertex *) override {} // Not used. void insertUniqueEdgeDiv(Diversion *div); void reportDiversion(const Edge *edge, const TimingArc *div_arc, @@ -286,7 +294,7 @@ class PathEnumFaninVisitor : public PathVisitor Slack path_end_slack_; Tag *before_div_tag_; - int before_div_rf_index_; + size_t before_div_rf_index_; Scene *scene_; const MinMax *min_max_; Arrival before_div_arrival_; @@ -445,7 +453,7 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *, bool tag_march = !Tag::matchNoCrpr(to_tag, before_div_tag_); bool crpr = !(!crpr_active_ - || visited_fanins_.find({from_vertex, arc}) == visited_fanins_.end()); + || !visited_fanins_.contains({from_vertex, arc})); debugPrint(debug_, "path_enum", 3, " pruned {}{}{}{}{} {} {}", unique_pins ? "unique_pins " : "", unique_edges ? "unique_edges " : "", same_arc ? "same_arc " : "", diff --git a/search/PathEnum.hh b/search/PathEnum.hh index 8fbab624a..bcf856fd6 100644 --- a/search/PathEnum.hh +++ b/search/PathEnum.hh @@ -24,14 +24,16 @@ #pragma once +#include #include #include -#include "ContainerHelpers.hh" +#include "Delay.hh" #include "Iterator.hh" -#include "StaState.hh" -#include "SearchClass.hh" +#include "LibertyClass.hh" #include "Path.hh" +#include "SearchClass.hh" +#include "StaState.hh" namespace sta { @@ -67,7 +69,7 @@ public: const StaState *sta); // Insert path ends that are enumerated in slack/arrival order. void insert(PathEnd *path_end); - virtual ~PathEnum(); + ~PathEnum() override; bool hasNext() override; PathEnd *next() override; @@ -85,7 +87,7 @@ private: Path *&after_div_copy); void updatePathHeadDelays(PathSeq &path, Path *after_div); - Arrival divSlack(Path *path, + Arrival divSlack(Path *before_div, Path *after_div, const Edge *div_edge, const TimingArc *div_arc); @@ -99,13 +101,13 @@ private: bool unique_pins_; bool unique_edges_; DiversionQueue div_queue_; - int div_count_; + int div_count_{0}; // Number of paths returned for each endpoint (limit to endpoint_path_count). VertexPathCountMap path_counts_; - bool inserts_pruned_; - PathEnd *next_; + bool inserts_pruned_{false}; + PathEnd *next_{nullptr}; friend class PathEnumFaninVisitor; }; -} // namespace +} // namespace sta diff --git a/search/PathExpanded.cc b/search/PathExpanded.cc index 1f9713a18..bfc082bcc 100644 --- a/search/PathExpanded.cc +++ b/search/PathExpanded.cc @@ -24,15 +24,15 @@ #include "PathExpanded.hh" -#include "TimingRole.hh" -#include "PortDirection.hh" -#include "Network.hh" #include "Clock.hh" -#include "Search.hh" -#include "Path.hh" -#include "Latches.hh" #include "Genclks.hh" +#include "Latches.hh" #include "Mode.hh" +#include "Network.hh" +#include "Path.hh" +#include "PortDirection.hh" +#include "Search.hh" +#include "TimingRole.hh" namespace sta { @@ -236,4 +236,4 @@ PathExpanded::latchPaths(// Return values. } } -} // namespace +} // namespace sta diff --git a/search/PathGroup.cc b/search/PathGroup.cc index e2e20a809..6750613ca 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -28,34 +28,35 @@ #include #include #include +#include #include "ContainerHelpers.hh" -#include "StringUtil.hh" -#include "Stats.hh" #include "Debug.hh" -#include "Mutex.hh" -#include "Fuzzy.hh" -#include "MinMax.hh" #include "DispatchQueue.hh" #include "ExceptionPath.hh" -#include "Sdc.hh" -#include "Mode.hh" +#include "Fuzzy.hh" #include "Graph.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Mutex.hh" #include "PathEnd.hh" -#include "Tag.hh" +#include "PathEnum.hh" #include "Scene.hh" +#include "Sdc.hh" #include "Search.hh" +#include "Stats.hh" +#include "StringUtil.hh" +#include "Tag.hh" #include "VisitPathEnds.hh" -#include "PathEnum.hh" namespace sta { -int PathGroup::group_path_count_max = std::numeric_limits::max(); +size_t PathGroup::group_path_count_max = std::numeric_limits::max(); PathGroup * PathGroup::makePathGroupSlack(std::string_view name, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, @@ -69,8 +70,8 @@ PathGroup::makePathGroupSlack(std::string_view name, PathGroup * PathGroup::makePathGroupArrival(std::string_view name, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, const MinMax *min_max, @@ -82,8 +83,8 @@ PathGroup::makePathGroupArrival(std::string_view name, } PathGroup::PathGroup(std::string_view name, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, @@ -180,7 +181,7 @@ PathGroup::insert(PathEnd *path_end) 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) + && path_ends_.size() > group_path_count_ * 2) prune(); } @@ -190,13 +191,12 @@ 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]; + for (PathEnd *path_end : path_ends_) { 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_)) { + if (end_count < group_path_count_ + && path_counts[vertex] < endpoint_path_count_) { path_ends_[end_count++] = path_end; path_counts[vertex]++; } @@ -225,7 +225,7 @@ PathGroup::pushEnds(PathEndSeq &path_ends) void PathGroup::ensureSortedMaxPaths() { - if (path_ends_.size() > static_cast(group_path_count_)) + if (path_ends_.size() > group_path_count_) prune(); else sort(); @@ -247,8 +247,8 @@ PathGroup::clear() //////////////////////////////////////////////////////////////// -PathGroups::PathGroups(int group_path_count, - int endpoint_path_count, +PathGroups::PathGroups(size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, @@ -262,7 +262,7 @@ PathGroups::PathGroups(int group_path_count, bool clk_gating_hold, bool unconstrained, const Mode *mode) : - StaState(mode), + StaState(mode->sta()), mode_(mode), group_path_count_(group_path_count), endpoint_path_count_(endpoint_path_count), @@ -286,8 +286,8 @@ PathGroups::PathGroups(int group_path_count, } void -PathGroups::makeGroups(int group_path_count, - int endpoint_path_count, +PathGroups::makeGroups(size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, @@ -299,7 +299,7 @@ PathGroups::makeGroups(int group_path_count, bool unconstrained, const MinMax *min_max) { - int mm_index = min_max->index(); + size_t mm_index = min_max->index(); if (setup_hold) { const Sdc *sdc = mode_->sdc(); for (const auto& [name, group] : sdc->groupPaths()) { @@ -425,7 +425,7 @@ PathGroups::pathGroups(const PathEnd *path_end) const PathGroup *path_group = nullptr; ExceptionPathSeq group_paths = search_->groupPathsTo(path_end); const MinMax *min_max = path_end->minMax(this); - int mm_index = min_max->index(); + size_t mm_index = min_max->index(); if (path_end->isUnconstrained()) path_group = unconstrained_[mm_index]; // GroupPaths have precedence. @@ -488,9 +488,9 @@ PathGroups::pathGroupNames(const PathEnd *path_end, // GroupPaths have precedence. for (ExceptionPath *group_path : group_paths) { if (group_path->isDefault()) - group_names.push_back(std::string(path_delay_group_name_)); + group_names.emplace_back(path_delay_group_name_); else - group_names.push_back(std::string(group_path->name())); + group_names.emplace_back(group_path->name()); } } else if (path_end->isCheck() || path_end->isLatchCheck()) { @@ -545,7 +545,7 @@ void PathGroups::pushEnds(PathEndSeq &path_ends) { for (const MinMax *min_max : MinMax::range()) { - int mm_index = min_max->index(); + size_t mm_index = min_max->index(); for (std::string &group_name : pathGroupNames()) { PathGroup *path_group = findPathGroup(group_name, min_max); if (path_group) @@ -592,7 +592,7 @@ PathGroups::pushUnconstrainedPathEnds(PathEndSeq &path_ends, { std::set groups; for (const MinMax *mm : min_max->range()) { - int mm_index = mm->index(); + size_t mm_index = mm->index(); PathGroup *group = unconstrained_[mm_index]; if (group // For multiple scene path APs use the same group. @@ -719,7 +719,7 @@ MakePathEnds1::vertexEnd(Vertex *) class MakePathEndsAll : public PathEndVisitor { public: - MakePathEndsAll(int endpoint_path_count, + MakePathEndsAll(size_t endpoint_path_count, PathGroups *path_groups); MakePathEndsAll(const MakePathEndsAll&) = default; ~MakePathEndsAll() override; @@ -731,7 +731,7 @@ class MakePathEndsAll : public PathEndVisitor void visitPathEnd(PathEnd *path_end, PathGroup *group); - int endpoint_path_count_; + size_t endpoint_path_count_; PathGroups *path_groups_; const StaState *sta_; PathGroupEndsMap ends_; @@ -739,7 +739,7 @@ class MakePathEndsAll : public PathEndVisitor PathEndNoCrprLess path_no_crpr_less_; }; -MakePathEndsAll::MakePathEndsAll(int endpoint_path_count, +MakePathEndsAll::MakePathEndsAll(size_t endpoint_path_count, PathGroups *path_groups) : endpoint_path_count_(endpoint_path_count), path_groups_(path_groups), @@ -789,7 +789,7 @@ MakePathEndsAll::vertexEnd(Vertex *) sort(ends, less_); PathEndNoCrprSet unique_ends(path_no_crpr_less_); auto end_iter = ends->begin(); - int n = 0; + size_t n = 0; while (end_iter != ends->end() && n < endpoint_path_count_) { PathEnd *path_end = *end_iter++; @@ -827,8 +827,8 @@ MakePathEndsAll::vertexEnd(Vertex *) void PathGroups::makeGroupPathEnds(ExceptionTo *to, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, const SceneSeq &scenes, @@ -843,7 +843,7 @@ PathGroups::makeGroupPathEnds(ExceptionTo *to, makeGroupPathEnds(to, scenes, min_max, &make_path_ends); for (const MinMax *path_min_max : MinMax::range()) { - int mm_index = path_min_max->index(); + size_t mm_index = path_min_max->index(); for (const Mode *mode : Scene::modes(scenes)) { const Sdc *sdc = mode->sdc(); for (const auto& [name, groups] : sdc->groupPaths()) { @@ -882,8 +882,8 @@ PathGroups::makeGroupPathEnds(ExceptionTo *to, void PathGroups::enumPathEnds(PathGroup *group, - int group_path_count, - int endpoint_path_count, + size_t group_path_count, + size_t endpoint_path_count, bool unique_pins, bool unique_edges, bool cmp_slack) @@ -900,7 +900,7 @@ PathGroups::enumPathEnds(PathGroup *group, group->clear(); // Parallel path enumeratation to find the endpoint_path_count/max path ends. - for (int n = 0; path_enum.hasNext() && n < group_path_count; n++) { + for (size_t n = 0; path_enum.hasNext() && n < group_path_count; n++) { PathEnd *end = path_enum.next(); if (group->saveable(end)) group->insert(end); @@ -954,7 +954,7 @@ class MakeEndpointPathEnds : public VertexVisitor const MinMaxAll *min_max, const StaState *sta); MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends); - ~MakeEndpointPathEnds(); + ~MakeEndpointPathEnds() override; VertexVisitor *copy() const override; void visit(Vertex *vertex) override; @@ -1032,4 +1032,4 @@ PathGroups::makeGroupPathEnds(VertexSet &endpoints, } } -} // namespace +} // namespace sta diff --git a/search/PocvMode.cc b/search/PocvMode.cc index 7e7b28a5f..46c262e52 100644 --- a/search/PocvMode.cc +++ b/search/PocvMode.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -45,4 +45,4 @@ findPocvMode(std::string_view mode_name) return pocv_mode_map.find(mode_name, PocvMode::scalar); } -} // namespace +} // namespace sta diff --git a/search/Property.cc b/search/Property.cc index 067298d79..322cf00ce 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -26,24 +26,25 @@ #include #include +#include +#include "Clock.hh" #include "Format.hh" -#include "StringUtil.hh" -#include "MinMax.hh" -#include "Transition.hh" -#include "Units.hh" -#include "TimingArc.hh" +#include "Graph.hh" #include "Liberty.hh" -#include "PortDirection.hh" +#include "MinMax.hh" #include "Network.hh" -#include "Graph.hh" -#include "Clock.hh" -#include "Scene.hh" +#include "Path.hh" #include "PathEnd.hh" #include "PathExpanded.hh" -#include "Path.hh" -#include "power/Power.hh" +#include "PortDirection.hh" +#include "Scene.hh" #include "Sta.hh" +#include "StringUtil.hh" +#include "TimingArc.hh" +#include "Transition.hh" +#include "Units.hh" +#include "power/Power.hh" namespace sta { @@ -52,8 +53,7 @@ class PropertyUnknown : public Exception public: PropertyUnknown(std::string_view type, std::string_view property); - virtual ~PropertyUnknown() {} - virtual const char *what() const noexcept; + const char *what() const noexcept override; private: std::string msg_; @@ -61,7 +61,6 @@ class PropertyUnknown : public Exception PropertyUnknown::PropertyUnknown(std::string_view type, std::string_view property) : - Exception(), msg_(sta::format("{} objects do not have a {} property.", type, property)) { } @@ -79,8 +78,7 @@ class PropertyTypeWrong : public Exception public: PropertyTypeWrong(const std::string &accessor, const std::string &type); - virtual ~PropertyTypeWrong() {} - virtual const char *what() const noexcept; + const char *what() const noexcept override; private: std::string msg_; @@ -88,7 +86,6 @@ class PropertyTypeWrong : public Exception PropertyTypeWrong::PropertyTypeWrong(const std::string &accessor, const std::string &type) : - Exception(), msg_(sta::format("property accessor {} is only valid for {} properties.", accessor, type)) { @@ -714,7 +711,7 @@ Properties::getProperty(const LibertyCell *cell, Network *network = sta_->cmdNetwork(); LibertyLibrary *lib = cell->libertyLibrary(); std::string lib_name = lib->name(); - std::string cell_name = cell->name(); + const std::string& cell_name = cell->name(); std::string full_name = lib_name + network->pathDivider() + cell_name; return PropertyValue(full_name); } @@ -893,14 +890,14 @@ Properties::getProperty(const LibertyPort *port, MinMax::max(), sta_); return delayPropertyValue(delay); } - else { + else { PropertyValue value = registry_liberty_port_.getProperty(port, property, "liberty_port", sta_); if (value.type() != PropertyValue::Type::none) return value; else throw PropertyUnknown("liberty port", property); - } + } } //////////////////////////////////////////////////////////////// @@ -1266,70 +1263,70 @@ Properties::capacitancePropertyValue(float cap) void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_library_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_liberty_library_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_cell_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_liberty_cell_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_port_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_liberty_port_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_instance_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_pin_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_net_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler handler) + PropertyRegistry::PropertyHandler &handler) { registry_clock_.defineProperty(property, handler); } @@ -1356,7 +1353,7 @@ void PropertyRegistry::defineProperty(std::string_view property, PropertyHandler handler) { - registry_[std::string(property)] = handler; + registry_[std::string(property)] = std::move(std::move(std::move(std::move(std::move(std::move(std::move(std::move(std::move(std::move(handler)))))))))); } -} // namespace +} // namespace sta diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 1ace1d08f..50d055749 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -22,46 +22,46 @@ // // This notice may not be removed or altered from any source distribution. +#include "ReportPath.hh" + #include // reverse #include #include -#include "ReportPath.hh" - +#include "ArcDelayCalc.hh" +#include "CheckMaxSkews.hh" +#include "CheckMinPeriods.hh" +#include "CheckMinPulseWidths.hh" +#include "ClkInfo.hh" #include "ContainerHelpers.hh" -#include "Format.hh" -#include "Report.hh" #include "Error.hh" -#include "StringUtil.hh" +#include "ExceptionPath.hh" +#include "Format.hh" #include "Fuzzy.hh" -#include "Units.hh" -#include "TimingRole.hh" -#include "Transition.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "PortDirection.hh" -#include "Network.hh" +#include "Genclks.hh" #include "Graph.hh" -#include "PortDelay.hh" -#include "ExceptionPath.hh" +#include "GraphDelayCalc.hh" #include "InputDrive.hh" -#include "Sdc.hh" +#include "Latches.hh" +#include "Liberty.hh" +#include "Mode.hh" +#include "Network.hh" #include "Parasitics.hh" -#include "ArcDelayCalc.hh" -#include "GraphDelayCalc.hh" -#include "ClkInfo.hh" -#include "Tag.hh" -#include "PathGroup.hh" -#include "CheckMinPulseWidths.hh" -#include "CheckMinPeriods.hh" -#include "CheckMaxSkews.hh" #include "Path.hh" -#include "Search.hh" #include "PathExpanded.hh" -#include "Latches.hh" +#include "PathGroup.hh" +#include "PortDelay.hh" +#include "PortDirection.hh" +#include "Report.hh" #include "Scene.hh" -#include "Mode.hh" -#include "Genclks.hh" +#include "Sdc.hh" +#include "Search.hh" +#include "StringUtil.hh" +#include "Tag.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" #include "Variables.hh" namespace sta { @@ -96,8 +96,7 @@ ReportField::ReportField(std::string_view name, } ReportField::~ReportField() -{ -} += default; void ReportField::setProperties(std::string_view title, @@ -125,11 +124,7 @@ ReportField::setEnabled(bool enabled) //////////////////////////////////////////////////////////////// ReportPath::ReportPath(StaState *sta) : - StaState(sta), - format_(ReportPathFormat::full), - no_split_(false), - start_end_pt_width_(80), - field_width_extra_(5) + StaState(sta) { makeFields(); setDigits(2); @@ -360,7 +355,6 @@ ReportPath::reportPathEndHeader() const void ReportPath::reportPathEndFooter() const { - std::string header; switch (format_) { case ReportPathFormat::full: case ReportPathFormat::full_clock: @@ -2466,7 +2460,7 @@ ReportPath::reportPathLine(const Path *path, void ReportPath::reportRequired(const PathEnd *end, - std::string margin_msg) const + const std::string& margin_msg) const { Required req_time = end->requiredTimeOffset(this); const EarlyLate *early_late = end->clkEarlyLate(this); @@ -2732,7 +2726,7 @@ ReportPath::reportPath6(const Path *path, Pin *pin = vertex->pin(); Arrival time = delaySum(path1->arrival(), time_offset, this); Delay incr = 0.0; - std::string_view line_case = ""; + std::string_view line_case; bool is_clk_start = path1->vertex(this) == clk_start; bool is_clk = path1->isClock(search_); Instance *inst = network_->instance(pin); @@ -3233,7 +3227,7 @@ ReportPath::reportLine(std::string_view what, else if (field == field_variation_) reportFieldBlank(field, line); else if (field == field_src_attr_) { - if (src_attr != "") + if (!src_attr.empty()) reportField(src_attr, field, line); else reportFieldBlank(field, line); @@ -3575,7 +3569,7 @@ hierPinsThruEdge(const Edge *edge, hierPinsAbove(drvr_pin, network, drvr_hpins); hierPinsAbove(load_pin, network, load_hpins); if (drvr_hpins.empty()) { - std::reverse(load_hpins.begin(), load_hpins.end()); + std::ranges::reverse(load_hpins); return load_hpins; } if (load_hpins.empty()) @@ -3634,4 +3628,4 @@ hierPinsAbove(const Net *net, } } -} // namespace +} // namespace sta diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 98b7dd232..b8ce652dc 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -24,16 +24,26 @@ #pragma once +#include #include #include #include -#include "StringUtil.hh" -#include "SearchClass.hh" -#include "PathEnd.hh" -#include "CheckMinPulseWidths.hh" -#include "CheckMinPeriods.hh" #include "CheckMaxSkews.hh" +#include "CheckMinPeriods.hh" +#include "CheckMinPulseWidths.hh" +#include "Clock.hh" +#include "Delay.hh" +#include "GraphClass.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "NetworkClass.hh" +#include "Path.hh" +#include "PathEnd.hh" +#include "Sdc.hh" +#include "SearchClass.hh" +#include "StringUtil.hh" namespace sta { @@ -47,7 +57,7 @@ class ReportPath : public StaState { public: ReportPath(StaState *sta); - virtual ~ReportPath(); + ~ReportPath() override; ReportPathFormat pathFormat() const { return format_; } void setPathFormat(ReportPathFormat format); void setReportFieldOrder(const StringSeq &field_names); @@ -195,7 +205,7 @@ protected: Arrival &borrow, Arrival &time_given_to_startpoint) const; void reportEndpoint(const PathEndDataCheck *end) const; - std::string_view clkNetworkDelayIdealProp(bool is_ideal) const; + std::string_view clkNetworkDelayIdealProp(bool is_prop) const; std::string checkRoleReason(const PathEnd *end) const; std::string checkRoleString(const PathEnd *end) const; @@ -286,13 +296,13 @@ protected: Arrival clk_time, const MinMax *min_max) const ; void reportRequired(const PathEnd *end, - std::string margin_msg) const ; + const std::string& margin_msg) const ; void reportSlack(const PathEnd *end) const ; void reportSlack(Slack slack) const ; void reportSpaceSlack(const PathEnd *end, - std::string &line) const ; + std::string &result) const ; void reportSpaceSlack(Slack slack, - std::string &line) const ; + std::string &result) const ; void reportSrcPathArrival(const PathEnd *end, const PathExpanded &expanded) const ; void reportPath(const PathEnd *end, @@ -383,38 +393,38 @@ protected: const EarlyLate *early_late) const; void reportDashLineTotal() const; void reportDescription(std::string_view what, - std::string &result) const; + std::string &line) const; void reportDescription(std::string_view what, bool first_field, bool last_field, - std::string &result) const; + std::string &line) const; void reportFieldTime(float value, ReportField *field, - std::string &result) const; + std::string &line) const; void reportSpaceFieldTime(float value, - std::string &result) const; + std::string &line) const; void reportSpaceFieldDelay(const Delay &value, const EarlyLate *early_late, - std::string &result) const; + std::string &line) const; void reportFieldDelayMinus(const Delay &value, const EarlyLate *early_late, const ReportField *field, - std::string &result) const; + std::string &line) const; void reportTotalDelay(const Delay &value, const EarlyLate *early_late, - std::string &result) const; + std::string &line) const; void reportFieldDelay(const Delay &value, const EarlyLate *early_late, const ReportField *field, - std::string &result) const; + std::string &line) const; void reportField(float value, const ReportField *field, - std::string &result) const; + std::string &line) const; void reportField(std::string_view value, const ReportField *field, - std::string &result) const; + std::string &line) const; void reportFieldBlank(const ReportField *field, - std::string &result) const; + std::string &line) const; void reportDashLine() const; void reportDashLine(int line_width) const; void reportBlankLine() const; @@ -472,15 +482,15 @@ protected: const MinMax *min_max) const; // Path options. - ReportPathFormat format_; + ReportPathFormat format_{ReportPathFormat::full}; ReportFieldSeq fields_; bool report_input_pin_; bool report_hier_pins_; bool report_net_; - bool no_split_; + bool no_split_{false}; int digits_; - size_t start_end_pt_width_; + size_t start_end_pt_width_{80}; ReportField *field_description_; ReportField *field_total_; @@ -496,7 +506,7 @@ protected: std::string plus_zero_; std::string minus_zero_; - int field_width_extra_; + int field_width_extra_{5}; static constexpr float field_blank_ = -1; static const float field_skip_; }; @@ -534,4 +544,4 @@ protected: std::string blank_; }; -} // namespace +} // namespace sta diff --git a/search/Scene.cc b/search/Scene.cc index 0bd27aa3a..404ac409c 100644 --- a/search/Scene.cc +++ b/search/Scene.cc @@ -25,9 +25,9 @@ #include "Scene.hh" #include "ContainerHelpers.hh" +#include "Mode.hh" #include "Parasitics.hh" #include "Sdc.hh" -#include "Mode.hh" namespace sta { @@ -134,7 +134,7 @@ Scene::libertyLibraries(const MinMax *min_max) const return liberty_[min_max->index()]; } -int +size_t Scene::libertyIndex(const MinMax *min_max) const { return index_ * MinMax::index_count + min_max->index(); @@ -184,4 +184,4 @@ Scene::modesSorted(const SceneSeq &scenes) return modes; } -} // namespace +} // namespace sta diff --git a/search/Search.cc b/search/Search.cc index 246d5dd6e..2fc617fa8 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -24,56 +24,61 @@ #include "Search.hh" +#include +#include #include +#include "Bfs.hh" +#include "ClkInfo.hh" +#include "Clock.hh" #include "ContainerHelpers.hh" -#include "Mutex.hh" -#include "Report.hh" +#include "Crpr.hh" +#include "DataCheck.hh" #include "Debug.hh" -#include "Stats.hh" +#include "Delay.hh" +#include "ExceptionPath.hh" #include "Fuzzy.hh" -#include "TimingRole.hh" -#include "FuncExpr.hh" -#include "TimingArc.hh" -#include "Sequential.hh" -#include "Units.hh" +#include "GatedClk.hh" +#include "Genclks.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "Latches.hh" +#include "Levelize.hh" #include "Liberty.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "Mode.hh" +#include "Mutex.hh" #include "Network.hh" -#include "PortDirection.hh" -#include "Graph.hh" -#include "GraphCmp.hh" +#include "NetworkClass.hh" +#include "Path.hh" +#include "PathEnd.hh" +#include "PathGroup.hh" #include "PortDelay.hh" -#include "Clock.hh" -#include "CycleAccting.hh" -#include "ExceptionPath.hh" -#include "DataCheck.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Mode.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" #include "SearchPred.hh" -#include "Levelize.hh" -#include "Bfs.hh" -#include "Sim.hh" -#include "Path.hh" -#include "ClkInfo.hh" +#include "Stats.hh" +#include "StringUtil.hh" #include "Tag.hh" #include "TagGroup.hh" -#include "PathEnd.hh" -#include "PathGroup.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Variables.hh" +#include "VertexVisitor.hh" #include "VisitPathEnds.hh" -#include "GatedClk.hh" #include "WorstSlack.hh" -#include "Latches.hh" -#include "Crpr.hh" -#include "Genclks.hh" -#include "Variables.hh" namespace sta { //////////////////////////////////////////////////////////////// EvalPred::EvalPred(const StaState *sta) : - SearchPred0(sta), - search_thru_latches_(true) + SearchPred0(sta) { } @@ -227,62 +232,40 @@ SearchAdj::searchTo(const Vertex * /* to_vertex */, Search::Search(StaState *sta) : StaState(sta), - unconstrained_paths_(false), - crpr_path_pruning_enabled_(true), - crpr_approx_missing_requireds_(true), search_thru_(new SearchThru(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_visitor_(new ArrivalVisitor(this)), - requireds_exist_(false), - requireds_seeded_(false), invalid_requireds_(makeVertexSet(this)), required_iter_(new BfsBkwdIterator(BfsIndex::required, search_adj_, this)), - tns_exists_(false), invalid_tns_(makeVertexSet(this)), - worst_slacks_(nullptr), 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))), - tag_next_(0), - tag_group_capacity_(tag_capacity_), tag_groups_(new TagGroup *[tag_group_capacity_]), tag_group_set_(new TagGroupSet(tag_group_capacity_)), - tag_group_next_(0), - pending_latch_outputs_(makeVertexSet(this)), pending_clk_endpoints_(makeVertexSet(this)), endpoints_(makeVertexSet(this)), - endpoints_initialized_(false), invalid_endpoints_(makeVertexSet(this)), - have_filter_(false), - filter_from_(nullptr), - filter_thrus_(nullptr), - filter_to_(nullptr), filtered_arrivals_(makeVertexSet(this)), - found_downstream_clk_pins_(false), - postpone_latch_outputs_(false), - visit_path_ends_(new VisitPathEnds(this)), gated_clk_(new GatedClk(this)), check_crpr_(new CheckCrpr(this)) @@ -714,18 +697,16 @@ class SeedFaninsThruHierPin : public HierPinThruVisitor public: SeedFaninsThruHierPin(Graph *graph, Search *search); - -protected: void visit(const Pin *drvr, const Pin *load) override; +protected: Graph *graph_; Search *search_; }; SeedFaninsThruHierPin::SeedFaninsThruHierPin(Graph *graph, Search *search) : - HierPinThruVisitor(), graph_(graph), search_(search) { @@ -1556,7 +1537,6 @@ Search::seedClkArrival(const Pin *pin, // Propagated pin overrides latency on clk. if (sdc->isPropagatedClock(pin)) { latency = 0.0; - latency_exists = false; is_propagated = true; } } @@ -2689,7 +2669,7 @@ Search::findTagGroup(TagGroupBldr *tag_bldr) 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 *)); + std::copy_n(tag_groups_.load(), tag_group_capacity_, tag_groups); tag_groups_prev_.push_back(tag_groups_); tag_groups_ = tag_groups; tag_group_capacity_ = tag_capacity; @@ -2952,7 +2932,7 @@ Search::findTag(Scene *scene, if (tag_next_ == tag_capacity_) { TagIndex tag_capacity = tag_capacity_ * 2; Tag **tags = new Tag *[tag_capacity]; - memcpy(tags, tags_, tag_capacity_ * sizeof(Tag *)); + std::copy_n(tags_.load(), tag_capacity_, tags); tags_prev_.push_back(tags_); tags_ = tags; tag_capacity_ = tag_capacity; @@ -3396,12 +3376,6 @@ Search::seedRequiredEnqueueFanin(Vertex *vertex) //////////////////////////////////////////////////////////////// -RequiredCmp::RequiredCmp() : - have_requireds_(false) -{ - requireds_.reserve(10); -} - void RequiredCmp::requiredsInit(Vertex *vertex, const StaState *sta) diff --git a/search/Search.i b/search/Search.i index f16176707..ae4e4c182 100644 --- a/search/Search.i +++ b/search/Search.i @@ -533,11 +533,11 @@ report_clk_latency(ConstClockSeq clks, void report_min_pulse_width_checks(const Net *net, size_t max_count, - bool violations, + bool violators, bool verbose, const SceneSeq scenes) { - return Sta::sta()->reportMinPulseWidthChecks(net, max_count, violations, + return Sta::sta()->reportMinPulseWidthChecks(net, max_count, violators, verbose, scenes); } @@ -546,11 +546,11 @@ report_min_pulse_width_checks(const Net *net, void report_min_period_checks(const Net *net, size_t max_count, - bool violations, + bool violators, bool verbose, const SceneSeq scenes) { - Sta::sta()->reportMinPeriodChecks(net, max_count, violations, verbose, scenes); + Sta::sta()->reportMinPeriodChecks(net, max_count, violators, verbose, scenes); } //////////////////////////////////////////////////////////////// @@ -1111,7 +1111,7 @@ void levelize() { Sta *sta = Sta::sta(); - sta->levelize()->levelize(); + sta->levelize()->findLevels(); } %} // inline diff --git a/search/SearchPred.cc b/search/SearchPred.cc index 803bcf544..772793742 100644 --- a/search/SearchPred.cc +++ b/search/SearchPred.cc @@ -24,17 +24,17 @@ #include "SearchPred.hh" -#include "TimingArc.hh" -#include "TimingRole.hh" +#include "Graph.hh" +#include "Latches.hh" +#include "Levelize.hh" #include "Liberty.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" #include "Sdc.hh" -#include "Mode.hh" -#include "Levelize.hh" #include "Search.hh" -#include "Latches.hh" #include "Sim.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" #include "Variables.hh" namespace sta { @@ -198,7 +198,7 @@ bool isClkEnd(Vertex *vertex, const Mode *mode) { - Graph *graph = mode->graph(); + Graph *graph = mode->sta()->graph(); ClkTreeSearchPred pred(graph); VertexOutEdgeIterator edge_iter(vertex, graph); while (edge_iter.hasNext()) { @@ -223,7 +223,7 @@ searchThru(const Edge *edge, const TimingArc *arc, const Mode *mode) { - const Graph *graph = mode->graph(); + const Graph *graph = mode->sta()->graph(); const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); const RiseFall *to_rf = arc->toEdge()->asRiseFall(); // Ignore transitions other than rise/fall. @@ -323,4 +323,4 @@ hasFanout(Vertex *vertex, return false; } -} // namespace +} // namespace sta diff --git a/search/Sim.cc b/search/Sim.cc index f2fd65319..58f9adeac 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -54,12 +54,7 @@ findDrvrPin(const Pin *pin, Sim::Sim(StaState *sta) : StaState(sta), - mode_(nullptr), - observer_(nullptr), - valid_(false), - incremental_(false), const_func_pins_(network_), - const_func_pins_valid_(false), invalid_insts_(network_), invalid_drvr_pins_(network_), invalid_load_pins_(network_), @@ -179,7 +174,7 @@ logicNot(LogicValue value) static LogicValue logic_not[5] = {LogicValue::one, LogicValue::zero, LogicValue::unknown, LogicValue::unknown, LogicValue::unknown}; - return logic_not[int(value)]; + return logic_not[static_cast(value)]; } void @@ -726,7 +721,7 @@ Sim::functionSense(const Instance *inst, const Pin *from_pin, const Pin *to_pin) { - if (isConstant((from_pin))) + if (isConstant(from_pin)) return TimingSense::none; else { LibertyPort *from_port = network_->libertyPort(from_pin); @@ -867,7 +862,7 @@ Sim::isDisabledMode(Edge *edge, { // Default values. is_disabled = false; - disable_cond = 0; + disable_cond = nullptr; TimingArcSet *arc_set = edge->timingArcSet(); const std::string &mode_name = arc_set->modeName(); const std::string &mode_value = arc_set->modeValue(); diff --git a/search/Sim.hh b/search/Sim.hh index 337f55466..5edd90f24 100644 --- a/search/Sim.hh +++ b/search/Sim.hh @@ -24,17 +24,18 @@ #pragma once -#include #include +#include #include #include -#include "StaConfig.hh" // CUDD -#include "NetworkClass.hh" +#include "Bdd.hh" #include "GraphClass.hh" +#include "LibertyClass.hh" +#include "Mode.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" #include "StaState.hh" -#include "Bdd.hh" namespace sta { @@ -51,8 +52,8 @@ class Sim : public StaState { public: Sim(StaState *sta); - virtual ~Sim(); - virtual void copyState(const StaState *sta); + ~Sim() override; + void copyState(const StaState *sta) override; void clear(); void setMode(Mode *mode); // Set the observer for simulation value changes. @@ -165,10 +166,10 @@ protected: DdNode *funcBddSim(const FuncExpr *expr, const Instance *inst); - Mode *mode_; - SimObserver *observer_; - bool valid_; - bool incremental_; + Mode *mode_{nullptr}; + SimObserver *observer_{nullptr}; + bool valid_{false}; + bool incremental_{false}; // Constants propagated by Sim.cc SimValueMap sim_value_map_; EdgeDisabledCondSet edge_disabled_cond_set_; @@ -176,7 +177,7 @@ protected: // Cache of pins that have constant functions (tie high and tie low // cell instances). PinSet const_func_pins_; - bool const_func_pins_valid_; + bool const_func_pins_valid_{false}; // Instances that require incremental constant propagation. InstanceSet invalid_insts_; // Driver pins waiting to propagate constant to loads. @@ -194,10 +195,9 @@ class SimObserver : public StaState { public: SimObserver(StaState *sta); - virtual ~SimObserver() {} virtual void valueChangeAfter(const Pin *pin) = 0; virtual void faninEdgesChangeAfter(const Pin *pin) = 0; virtual void fanoutEdgesChangeAfter(const Pin *pin) = 0; }; -} // namespace +} // namespace sta diff --git a/search/Sta.cc b/search/Sta.cc index 7a47319d2..0aca01e07 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -25,73 +25,90 @@ #include "Sta.hh" #include -#include +#include #include -#include "Machine.hh" -#include "Format.hh" +#include "ArcDelayCalc.hh" +#include "CheckCapacitances.hh" +#include "CheckFanouts.hh" +#include "CheckMaxSkews.hh" +#include "CheckMinPeriods.hh" +#include "CheckMinPulseWidths.hh" +#include "CheckSlews.hh" +#include "CheckTiming.hh" +#include "CircuitSim.hh" +#include "ClkInfo.hh" +#include "ClkLatency.hh" +#include "ClkNetwork.hh" +#include "ClkSkew.hh" #include "ContainerHelpers.hh" -#include "DispatchQueue.hh" -#include "ReportTcl.hh" #include "Debug.hh" -#include "Stats.hh" -#include "Fuzzy.hh" -#include "Units.hh" -#include "PatternMatch.hh" -#include "TimingArc.hh" -#include "FuncExpr.hh" +#include "Delay.hh" +#include "DelayCalc.hh" +#include "DelayNormal.hh" +#include "DelayScalar.hh" +#include "DelaySkewNormal.hh" +#include "DispatchQueue.hh" #include "EquivCells.hh" +#include "ExceptionPath.hh" +#include "FindRegister.hh" +#include "Format.hh" +#include "FuncExpr.hh" +#include "Fuzzy.hh" +#include "Genclks.hh" +#include "Graph.hh" +#include "GraphClass.hh" +#include "GraphCmp.hh" +#include "GraphDelayCalc.hh" +#include "Latches.hh" +#include "Levelize.hh" #include "Liberty.hh" -#include "liberty/LibertyReader.hh" +#include "LibertyClass.hh" #include "LibertyWriter.hh" -#include "SdcNetwork.hh" +#include "Machine.hh" #include "MakeConcreteNetwork.hh" -#include "PortDirection.hh" -#include "VerilogReader.hh" -#include "Graph.hh" -#include "GraphCmp.hh" -#include "Sdc.hh" +#include "MakeTimingModel.hh" +#include "MinMax.hh" #include "Mode.hh" -#include "Variables.hh" -#include "sdc/WriteSdc.hh" -#include "ExceptionPath.hh" +#include "Network.hh" +#include "NetworkClass.hh" #include "Parasitics.hh" -#include "parasitics/SpefReader.hh" -#include "parasitics/ReportParasiticAnnotation.hh" -#include "DelayCalc.hh" -#include "ArcDelayCalc.hh" -#include "GraphDelayCalc.hh" -#include "sdf/SdfReader.hh" -#include "sdf/SdfWriter.hh" -#include "Levelize.hh" -#include "Sim.hh" -#include "ClkInfo.hh" -#include "TagGroup.hh" -#include "Search.hh" -#include "Latches.hh" +#include "PathExpanded.hh" #include "PathGroup.hh" -#include "CheckTiming.hh" -#include "CheckSlews.hh" -#include "CheckFanouts.hh" -#include "CheckCapacitances.hh" -#include "CheckMinPulseWidths.hh" -#include "CheckMinPeriods.hh" -#include "CheckMaxSkews.hh" -#include "ClkSkew.hh" -#include "ClkLatency.hh" -#include "FindRegister.hh" +#include "PatternMatch.hh" +#include "PocvMode.hh" +#include "PortDirection.hh" +#include "PowerClass.hh" #include "ReportPath.hh" -#include "Genclks.hh" -#include "ClkNetwork.hh" -#include "power/Power.hh" +#include "ReportTcl.hh" +#include "RiseFallMinMaxDelay.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "SdcClass.hh" +#include "SdcNetwork.hh" +#include "Search.hh" +#include "SearchClass.hh" +#include "SearchPred.hh" +#include "Sim.hh" +#include "Stats.hh" +#include "StringUtil.hh" +#include "TagGroup.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Units.hh" +#include "Variables.hh" +#include "VerilogReader.hh" #include "VisitPathEnds.hh" -#include "PathExpanded.hh" -#include "MakeTimingModel.hh" +#include "Wireload.hh" +#include "liberty/LibertyReader.hh" #include "parasitics/ConcreteParasitics.hh" +#include "parasitics/ReportParasiticAnnotation.hh" +#include "parasitics/SpefReader.hh" +#include "power/Power.hh" +#include "sdc/WriteSdc.hh" +#include "sdf/SdfReader.hh" +#include "sdf/SdfWriter.hh" #include "spice/WritePathSpice.hh" -#include "DelayScalar.hh" -#include "DelayNormal.hh" -#include "DelaySkewNormal.hh" namespace sta { @@ -128,7 +145,6 @@ class StaDelayCalcObserver : public DelayCalcObserver }; StaDelayCalcObserver::StaDelayCalcObserver(Search *search) : - DelayCalcObserver(), search_(search) { } @@ -262,30 +278,6 @@ deleteAllMemory() //////////////////////////////////////////////////////////////// -// Singleton used by TCL commands. -Sta *Sta::sta_; - -Sta::Sta() : - StaState(), - cmd_scene_(nullptr), - current_instance_(nullptr), - verilog_reader_(nullptr), - check_timing_(nullptr), - check_slews_(nullptr), - check_fanouts_(nullptr), - check_capacitances_(nullptr), - check_min_pulse_widths_(nullptr), - check_min_periods_(nullptr), - check_max_skews_(nullptr), - clk_skews_(nullptr), - report_path_(nullptr), - power_(nullptr), - update_genclks_(false), - equiv_cells_(nullptr), - properties_(this) -{ -} - void Sta::makeComponents() { @@ -1077,7 +1069,7 @@ Sta::setWireloadSelection(WireloadSelection *selection, void Sta::setSlewLimit(Clock *clk, const RiseFallBoth *rf, - const PathClkOrData clk_data, + PathClkOrData clk_data, const MinMax *min_max, float slew, Sdc *sdc) @@ -1381,18 +1373,18 @@ Sta::removeClockUncertainty(Clock *from_clk, } ClockGroups * -Sta::makeClockGroups(const std::string &name, +Sta::makeClockGroups(std::string_view name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, - std::string comment, + std::string_view comment, Sdc *sdc) { ClockGroups *groups = sdc->makeClockGroups(name, logically_exclusive, physically_exclusive, asynchronous, allow_paths, - std::move(comment)); + comment); search_->requiredsInvalid(); return groups; } @@ -2482,9 +2474,9 @@ Sta::setClkThruTristateEnabled(bool enable) void Sta::makeDefaultScene() { - const char *name = "default"; + std::string name("default"); StringSeq scene_names; - scene_names.push_back(name); + scene_names.emplace_back(name); Parasitics *parasitics = makeConcreteParasitics(name, ""); Mode *mode = new Mode(name, 0, this); @@ -3199,8 +3191,8 @@ class EndpointPathEndVisitor : public PathEndVisitor EndpointPathEndVisitor(std::string_view path_group_name, const MinMax *min_max, const StaState *sta); - PathEndVisitor *copy() const; - void visit(PathEnd *path_end); + PathEndVisitor *copy() const override; + void visit(PathEnd *path_end) override; Slack slack() const { return slack_; } private: @@ -3304,7 +3296,7 @@ Sta::reportDelaysWrtClks(const Pin *pin, bool report_variance, int digits, bool find_required, - PathDelayFunc get_path_delay) + const PathDelayFunc &get_path_delay) { ensureGraph(); Vertex *vertex, *bidir_vertex; @@ -3323,7 +3315,7 @@ Sta::reportDelaysWrtClks(Vertex *vertex, bool report_variance, int digits, bool find_required, - PathDelayFunc get_path_delay) + const PathDelayFunc &get_path_delay) { if (find_required) findRequired(vertex); @@ -3349,7 +3341,7 @@ Sta::reportDelaysWrtClks(Vertex *vertex, const Scene *scene, bool report_variance, int digits, - PathDelayFunc get_path_delay) + const PathDelayFunc &get_path_delay) { RiseFallMinMaxDelay delays = findDelaysWrtClks(vertex, clk_edge, scene, get_path_delay); @@ -3373,7 +3365,7 @@ RiseFallMinMaxDelay Sta::findDelaysWrtClks(Vertex *vertex, const ClockEdge *clk_edge, const Scene *scene, - PathDelayFunc get_path_delay) + const PathDelayFunc &get_path_delay) { RiseFallMinMaxDelay delays; VertexPathIterator path_iter(vertex, scene, nullptr, nullptr, this); @@ -3425,7 +3417,7 @@ class MinPeriodEndVisitor : public PathEndVisitor const Clock *clk_; bool include_port_paths_; StaState *sta_; - float min_period_; + float min_period_{0.0}; }; MinPeriodEndVisitor::MinPeriodEndVisitor(const Clock *clk, @@ -3433,8 +3425,7 @@ MinPeriodEndVisitor::MinPeriodEndVisitor(const Clock *clk, StaState *sta) : clk_(clk), include_port_paths_(include_port_paths), - sta_(sta), - min_period_(0) + sta_(sta) { } @@ -3551,7 +3542,7 @@ Sta::worstSlack(const Scene *scene, Vertex *&worst_vertex) { searchPreamble(); - return search_->worstSlack(scene, min_max, worst_slack, worst_vertex); + search_->worstSlack(scene, min_max, worst_slack, worst_vertex); } //////////////////////////////////////////////////////////////// @@ -3612,7 +3603,7 @@ Sta::setIncrementalDelayTolerance(float tol) graph_delay_calc_->setIncrementalDelayTolerance(tol); } -const ArcDelay +ArcDelay Sta::arcDelay(Edge *edge, TimingArc *arc, DcalcAPIndex ap_index) @@ -4242,7 +4233,7 @@ void Sta::deleteParasitics() { Parasitics *parasitics_default = findParasitics("default"); - for (auto [name, parasitics] : parasitics_name_map_) { + for (auto &[name, parasitics] : parasitics_name_map_) { if (parasitics != parasitics_default) delete parasitics; } @@ -4258,11 +4249,11 @@ Sta::deleteParasitics() } Parasitics * -Sta::makeConcreteParasitics(std::string name, - std::string filename) +Sta::makeConcreteParasitics(std::string_view name, + std::string_view filename) { Parasitics *parasitics = new ConcreteParasitics(name, filename, this); - parasitics_name_map_[name] = parasitics; + parasitics_name_map_[std::string(name)] = parasitics; return parasitics; } @@ -5050,56 +5041,56 @@ Sta::clockDomains(const Pin *pin, InstanceSet Sta::findRegisterInstances(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegInstances(clks, clk_rf, edge_triggered, latches, mode, this); + return findRegInstances(clks, clk_rf, registers, latches, mode, this); } PinSet Sta::findRegisterDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegDataPins(clks, clk_rf, edge_triggered, latches, mode, this); + return findRegDataPins(clks, clk_rf, registers, latches, mode, this); } PinSet Sta::findRegisterClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegClkPins(clks, clk_rf, edge_triggered, latches, mode, this); + return findRegClkPins(clks, clk_rf, registers, latches, mode, this); } PinSet Sta::findRegisterAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegAsyncPins(clks, clk_rf, edge_triggered, latches, mode, this); + return findRegAsyncPins(clks, clk_rf, registers, latches, mode, this); } PinSet Sta::findRegisterOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, - bool edge_triggered, + bool registers, bool latches, const Mode *mode) { findRegisterPreamble(mode); - return findRegOutputPins(clks, clk_rf, edge_triggered, latches, mode, this); + return findRegOutputPins(clks, clk_rf, registers, latches, mode, this); } void @@ -5675,7 +5666,6 @@ Sta::checkFanout(const Pin *pin, float &slack) { FanoutCheck check = check_fanouts_->check(pin, mode, min_max); - pin = check.pin(); fanout = check.fanout(); limit = check.limit(); slack = check.slack(); @@ -5768,7 +5758,6 @@ Sta::checkCapacitance(const Pin *pin, const Scene *&scene) { CapacitanceCheck check = check_capacitances_->check(pin, scenes, min_max); - pin = check.pin(); capacitance = check.capacitance(); limit = check.limit(); slack = check.slack(); diff --git a/search/StaState.cc b/search/StaState.cc index 661c02c91..ea673b382 100644 --- a/search/StaState.cc +++ b/search/StaState.cc @@ -28,14 +28,14 @@ #include "ContainerHelpers.hh" #include "DispatchQueue.hh" -#include "Units.hh" -#include "Network.hh" -#include "Variables.hh" -#include "Sdc.hh" #include "Graph.hh" -#include "TimingArc.hh" #include "Mode.hh" +#include "Network.hh" #include "Scene.hh" +#include "Sdc.hh" +#include "TimingArc.hh" +#include "Units.hh" +#include "Variables.hh" namespace sta { @@ -160,11 +160,11 @@ StaState::dcalcAnalysisPtCount() const return MinMax::index_count * scenes_.size(); } -const SceneSet +SceneSet StaState::scenesSet() { return Scene::sceneSet(scenes_); } -} // namespace +} // namespace sta diff --git a/search/Tag.cc b/search/Tag.cc index 9e5113e4a..8dffce4a9 100644 --- a/search/Tag.cc +++ b/search/Tag.cc @@ -24,16 +24,16 @@ #include "Tag.hh" -#include "Report.hh" -#include "Network.hh" +#include "ClkInfo.hh" #include "Clock.hh" -#include "PortDelay.hh" #include "ExceptionPath.hh" -#include "Sdc.hh" #include "Graph.hh" +#include "Network.hh" +#include "PortDelay.hh" +#include "Report.hh" #include "Scene.hh" +#include "Sdc.hh" #include "Search.hh" -#include "ClkInfo.hh" namespace sta { @@ -53,8 +53,6 @@ Tag::Tag(Scene *scene, states_(states), index_(index), is_clk_(is_clk), - is_filter_(false), - is_loop_(false), is_segment_start_(is_segment_start), own_states_(own_states), rf_index_(rf->index()), @@ -681,4 +679,4 @@ TagMatchEqual::operator()(const Tag *tag1, return Tag::match(tag1, tag2, match_crpr_clk_pin_, sta_); } -} // namespace +} // namespace sta diff --git a/search/Tag.hh b/search/Tag.hh index b989c644e..006e88656 100644 --- a/search/Tag.hh +++ b/search/Tag.hh @@ -24,11 +24,15 @@ #pragma once -#include "Transition.hh" +#include +#include + +#include "NetworkClass.hh" +#include "Scene.hh" #include "SdcClass.hh" #include "SearchClass.hh" -#include "Path.hh" -#include "Scene.hh" +#include "StaState.hh" +#include "Transition.hh" namespace sta { @@ -71,7 +75,7 @@ public: const ClockEdge *clkEdge() const; const Clock *clock() const; const Pin *clkSrc() const; - int rfIndex() const { return rf_index_; } + size_t rfIndex() const { return rf_index_; } const RiseFall *transition() const; const MinMax *minMax() const; int minMaxIndex() const { return min_max_index_; } @@ -95,7 +99,7 @@ public: const StaState *sta); static int matchCmp(const Tag *tag1, const Tag *tag2, - bool match_clk_clk_pin, + bool match_crpr_clk_pin, const StaState *sta); static bool match(const Tag *tag1, const Tag *tag2, @@ -134,11 +138,11 @@ private: size_t match_hash_; TagIndex index_; bool is_clk_:1; - bool is_filter_:1; - bool is_loop_:1; - bool is_segment_start_:1; + bool is_filter_:1 {false}; + bool is_loop_:1 {false}; + bool is_segment_start_:1 {false}; // Indicates that states_ is owned by the tag. - bool own_states_:1; + bool own_states_:1 {false}; unsigned int rf_index_:RiseFall::index_bit_count; unsigned int min_max_index_:MinMax::index_bit_count; }; @@ -182,4 +186,4 @@ private: const StaState *sta_; }; -} // namespace +} // namespace sta diff --git a/search/TagGroup.cc b/search/TagGroup.cc index 85fc29a50..e7afc9c59 100644 --- a/search/TagGroup.cc +++ b/search/TagGroup.cc @@ -24,14 +24,14 @@ #include "TagGroup.hh" -#include "Report.hh" +#include "ClkInfo.hh" #include "Debug.hh" #include "Graph.hh" -#include "ClkInfo.hh" -#include "Tag.hh" +#include "Path.hh" +#include "Report.hh" #include "Scene.hh" #include "Search.hh" -#include "Path.hh" +#include "Tag.hh" namespace sta { @@ -152,11 +152,6 @@ TagGroupBldr::TagGroupBldr(bool match_crpr_clk_pin, path_index_map_(TagMatchLess(match_crpr_clk_pin, sta)), paths_(default_path_count_), - has_clk_tag_(false), - has_genclk_src_tag_(false), - has_filter_tag_(false), - has_loop_tag_(false), - has_propagated_clk_(false), sta_(sta) { } diff --git a/search/TagGroup.hh b/search/TagGroup.hh index 972b3e73e..2865186a3 100644 --- a/search/TagGroup.hh +++ b/search/TagGroup.hh @@ -25,11 +25,13 @@ #pragma once #include +#include -#include "MinMax.hh" -#include "Transition.hh" +#include "Delay.hh" #include "GraphClass.hh" +#include "LibertyClass.hh" #include "SearchClass.hh" +#include "StaState.hh" #include "Tag.hh" namespace sta { @@ -89,7 +91,7 @@ protected: class TagGroupHash { public: - size_t operator()(const TagGroup *tag) const; + size_t operator()(const TagGroup *group) const; }; class TagGroupEqual @@ -151,11 +153,11 @@ protected: int default_path_count_; PathIndexMap path_index_map_; std::vector paths_; - bool has_clk_tag_; - bool has_genclk_src_tag_; - bool has_filter_tag_; - bool has_loop_tag_; - bool has_propagated_clk_; + bool has_clk_tag_{false}; + bool has_genclk_src_tag_{false}; + bool has_filter_tag_{false}; + bool has_loop_tag_{false}; + bool has_propagated_clk_{false}; const StaState *sta_; }; @@ -163,4 +165,4 @@ void pathIndexMapReport(const PathIndexMap *path_index_map, const StaState *sta); -} // namespace +} // namespace sta diff --git a/search/VertexVisitor.cc b/search/VertexVisitor.cc index fc04c8926..c622c286a 100644 --- a/search/VertexVisitor.cc +++ b/search/VertexVisitor.cc @@ -47,4 +47,4 @@ VertexPinCollector::visit(Vertex *vertex) pins_.insert(vertex->pin()); } -} // namespace +} // namespace sta diff --git a/search/VisitPathEnds.cc b/search/VisitPathEnds.cc index 4a668d9bb..3a420aada 100644 --- a/search/VisitPathEnds.cc +++ b/search/VisitPathEnds.cc @@ -24,24 +24,24 @@ #include "VisitPathEnds.hh" +#include "ClkInfo.hh" #include "Debug.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "TimingArc.hh" #include "ExceptionPath.hh" -#include "PortDelay.hh" -#include "Sdc.hh" -#include "Mode.hh" +#include "GatedClk.hh" #include "Graph.hh" -#include "ClkInfo.hh" -#include "Tag.hh" +#include "Liberty.hh" +#include "Mode.hh" +#include "Network.hh" #include "Path.hh" #include "PathEnd.hh" +#include "PortDelay.hh" +#include "Scene.hh" +#include "Sdc.hh" #include "Search.hh" -#include "GatedClk.hh" #include "Sim.hh" +#include "Tag.hh" +#include "TimingArc.hh" #include "Variables.hh" -#include "Scene.hh" namespace sta { @@ -562,7 +562,7 @@ VisitPathEnds::visitDataCheckEnd1(DataCheck *check, if (sdc->sameClockGroup(src_clk, tgt_clk) && !sdc->clkStopPropagation(from_pin, tgt_clk) // False paths and path delays override. - && (exception == 0 + && (exception == nullptr || exception->isFilter() || exception->isGroupPath() || exception->isMultiCycle()) @@ -650,4 +650,4 @@ VisitPathEnds::exceptionTo(const Path *path, min_max, false, false, path->sdc(this)); } -} // namespace +} // namespace sta diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index a58efb64b..769319b9e 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -28,9 +28,9 @@ #include "ContainerHelpers.hh" #include "Debug.hh" -#include "Report.hh" -#include "Mutex.hh" #include "Graph.hh" +#include "Mutex.hh" +#include "Report.hh" #include "Scene.hh" #include "Search.hh" @@ -96,12 +96,9 @@ WorstSlacks::worstSlackNotifyBefore(Vertex *vertex) WorstSlack::WorstSlack(StaState *sta) : StaState(sta), slack_init_(MinMax::min()->initValue()), - worst_vertex_(nullptr), worst_slack_(slack_init_), slack_threshold_(slack_init_), - queue_(new VertexSet(VertexIdLess(graph_))), - min_queue_size_(10), - max_queue_size_(20) + queue_(new VertexSet(VertexIdLess(graph_))) { } @@ -110,12 +107,9 @@ WorstSlack::~WorstSlack() { delete queue_; } WorstSlack::WorstSlack(const WorstSlack &worst_slack) : StaState(worst_slack), slack_init_(MinMax::min()->initValue()), - worst_vertex_(nullptr), worst_slack_(slack_init_), slack_threshold_(slack_init_), - queue_(new VertexSet(VertexIdLess(graph_))), - min_queue_size_(10), - max_queue_size_(20) + queue_(new VertexSet(VertexIdLess(graph_))) { } @@ -168,7 +162,7 @@ WorstSlack::initQueue(PathAPIndex path_ap_index) setWorstSlack(vertex, slack); if (delayLessEqual(slack, slack_threshold_, this)) queue_->insert(vertex); - int queue_size = queue_->size(); + size_t queue_size = queue_->size(); if (queue_size >= max_queue_size_) sortQueue(path_ap_index); } @@ -181,7 +175,7 @@ WorstSlack::initQueue(PathAPIndex path_ap_index) void WorstSlack::sortQueue(PathAPIndex path_ap_index) { - if (queue_->size() > 0) { + if (!queue_->empty()) { debugPrint(debug_, "wns", 3, "sort queue"); VertexSeq vertices; @@ -191,8 +185,8 @@ WorstSlack::sortQueue(PathAPIndex path_ap_index) WnsSlackLess slack_less(path_ap_index, this); sort(vertices, slack_less); - int vertex_count = vertices.size(); - int threshold_index = std::min(min_queue_size_, vertex_count - 1); + size_t vertex_count = vertices.size(); + size_t 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 {}", diff --git a/search/WorstSlack.hh b/search/WorstSlack.hh index de3b03720..89141a98c 100644 --- a/search/WorstSlack.hh +++ b/search/WorstSlack.hh @@ -27,8 +27,9 @@ #include #include -#include "MinMax.hh" +#include "Delay.hh" #include "GraphClass.hh" +#include "MinMax.hh" #include "SearchClass.hh" #include "StaState.hh" @@ -79,7 +80,7 @@ class WorstSlack : public StaState { public: WorstSlack(StaState *sta); - ~WorstSlack(); + ~WorstSlack() override; WorstSlack(const WorstSlack &); void worstSlack(PathAPIndex path_ap_index, // Return values. @@ -102,16 +103,16 @@ protected: Slack slack_init_; // Vertex with the worst slack. // When nullptr the worst slack is unknown but in the queue. - Vertex *worst_vertex_; + Vertex *worst_vertex_{nullptr}; Slack worst_slack_; Slack slack_threshold_; // Vertices with slack < threshold_ VertexSet *queue_; // Queue is sorted and pruned to min_queue_size_ vertices when it // reaches max_queue_size_. - int min_queue_size_; - int max_queue_size_; + size_t min_queue_size_{10}; + size_t max_queue_size_{20}; std::mutex lock_; }; -} // namespace +} // namespace sta diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index 9b3ecb005..c414317d8 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -756,4 +756,4 @@ WritePathSpice::stageLibertyCell(Stage stage) return network_->libertyPort(pin)->libertyCell(); } -} // namespace +} // namespace sta diff --git a/spice/WritePathSpice.hh b/spice/WritePathSpice.hh index aa22e4fc7..fd574278c 100644 --- a/spice/WritePathSpice.hh +++ b/spice/WritePathSpice.hh @@ -50,4 +50,4 @@ writePathSpice(const Path *path, CircuitSim ckt_sim, StaState *sta); -} // namespace +} // namespace sta diff --git a/spice/Xyce.cc b/spice/Xyce.cc index 35bf668a6..2dfbe30f0 100644 --- a/spice/Xyce.cc +++ b/spice/Xyce.cc @@ -77,4 +77,4 @@ readXyceCsv(const char *csv_filename, throw FileNotReadable(csv_filename); } -} // namespace +} // namespace sta diff --git a/spice/Xyce.hh b/spice/Xyce.hh index 84cf4f343..9d3d09a3b 100644 --- a/spice/Xyce.hh +++ b/spice/Xyce.hh @@ -40,4 +40,4 @@ readXyceCsv(const char *csv_filename, StringSeq &titles, WaveformSeq &waveforms); -} // namespace +} // namespace sta diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index afbc74f58..26d5443d9 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -258,7 +258,7 @@ setPtrTclList(SET_TYPE *set, //////////////////////////////////////////////////////////////// -} // namespace +} // namespace sta using namespace sta; diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index bcd4f2640..913a437f5 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -204,4 +204,4 @@ arcDcalcArgTcl(Tcl_Obj *obj, return ArcDcalcArg(); } -} // namespace +} // namespace sta diff --git a/util/Debug.cc b/util/Debug.cc index 016dc1b51..6cb3f8992 100644 --- a/util/Debug.cc +++ b/util/Debug.cc @@ -32,9 +32,7 @@ namespace sta { bool debug_on = false; Debug::Debug(Report *report) : - report_(report), - debug_on_(false), - stats_level_(0) + report_(report) { } @@ -81,4 +79,4 @@ Debug::setLevel(std::string_view what, } } -} // namespace +} // namespace sta diff --git a/util/DispatchQueue.cc b/util/DispatchQueue.cc index 4e828c23a..5347a07ae 100644 --- a/util/DispatchQueue.cc +++ b/util/DispatchQueue.cc @@ -30,9 +30,9 @@ DispatchQueue::terminateThreads() cv_.notify_all(); // Wait for threads to finish before we exit - for(size_t i = 0; i < threads_.size(); i++) { - if (threads_[i].joinable()) { - threads_[i].join(); + for (auto &thread : threads_) { + if (thread.joinable()) { + thread.join(); } } quit_ = false; @@ -95,10 +95,10 @@ DispatchQueue::dispatch_thread_handler(size_t i) do { // Wait until we have data or a quit signal - cv_.wait(lock, [this] { return (q_.size() || quit_); } ); + cv_.wait(lock, [this] { return (!q_.empty() || quit_); } ); //after wait, we own the lock - if(!quit_ && q_.size()) { + if (!quit_ && !q_.empty()) { auto op = std::move(q_.front()); q_.pop(); @@ -112,4 +112,4 @@ DispatchQueue::dispatch_thread_handler(size_t i) } while (!quit_); } -} // namespace +} // namespace sta diff --git a/util/Error.cc b/util/Error.cc index 3278d0fe4..553bc29a8 100644 --- a/util/Error.cc +++ b/util/Error.cc @@ -24,22 +24,20 @@ #include "Error.hh" -#include #include +#include #include "Format.hh" #include "StringUtil.hh" namespace sta { -Exception::Exception() : - std::exception() +Exception::Exception() { } ExceptionMsg::ExceptionMsg(const std::string &msg, - const bool suppressed) : - Exception(), + bool suppressed) : msg_(msg), suppressed_(suppressed) { @@ -53,7 +51,6 @@ ExceptionMsg::what() const noexcept ExceptionLine::ExceptionLine(const std::string &filename, int line) : - Exception(), filename_(filename), line_(line) { @@ -81,4 +78,4 @@ FileNotWritable::what() const noexcept return msg_.c_str(); } -} // namespace +} // namespace sta diff --git a/util/Fuzzy.cc b/util/Fuzzy.cc index 0308bc9a9..b04a35773 100644 --- a/util/Fuzzy.cc +++ b/util/Fuzzy.cc @@ -89,4 +89,4 @@ fuzzyInf(float value) || fuzzyEqual(value, -INF); } -} // namespace +} // namespace sta diff --git a/util/Hash.cc b/util/Hash.cc index 49b497987..8c86b1c63 100644 --- a/util/Hash.cc +++ b/util/Hash.cc @@ -35,4 +35,4 @@ hashString(std::string_view str) return hash; } -} // namespace +} // namespace sta diff --git a/util/MachineApple.cc b/util/MachineApple.cc index 220df7725..e1de87f41 100644 --- a/util/MachineApple.cc +++ b/util/MachineApple.cc @@ -24,12 +24,12 @@ #include "Machine.hh" -#include -#include -#include -#include +#include +#include #include +#include #include +#include #include "StaConfig.hh" #include "StringUtil.hh" @@ -85,4 +85,4 @@ memoryUsage() return rusage.ru_maxrss; } -} // namespace +} // namespace sta diff --git a/util/MachineLinux.cc b/util/MachineLinux.cc index 7be581f63..ff7079ffb 100644 --- a/util/MachineLinux.cc +++ b/util/MachineLinux.cc @@ -24,16 +24,16 @@ #include "Machine.hh" -#include -#include -#include -#include +#include +#include #include +#include #include +#include +#include "Format.hh" #include "StaConfig.hh" #include "StringUtil.hh" -#include "Format.hh" namespace sta { @@ -105,4 +105,4 @@ memoryUsage() return memory; } -} // namespace +} // namespace sta diff --git a/util/MachineUnknown.cc b/util/MachineUnknown.cc index 0a28118b7..069e4a950 100644 --- a/util/MachineUnknown.cc +++ b/util/MachineUnknown.cc @@ -61,4 +61,4 @@ memoryUsage() return 0; } -} // namespace +} // namespace sta diff --git a/util/MachineWin32.cc b/util/MachineWin32.cc index 3fec862f1..b2def31d7 100644 --- a/util/MachineWin32.cc +++ b/util/MachineWin32.cc @@ -24,7 +24,7 @@ #include "Machine.hh" -#include +#include #include // GetSystemInfo namespace sta { @@ -35,7 +35,7 @@ int vsnprint(char *str, size_t size, const char *fmt, - va_list args) + const va_list args) { // Copy args before using them because consumption is destructive. va_list args_copy1; @@ -109,4 +109,4 @@ memoryUsage() return 0; } -} // namespace +} // namespace sta diff --git a/util/MinMax.cc b/util/MinMax.cc index 299d31576..4cb0179d6 100644 --- a/util/MinMax.cc +++ b/util/MinMax.cc @@ -52,10 +52,10 @@ compareMax(float value1, const MinMax MinMax::min_("min", 0, INF, std::numeric_limits::max(), compareMin); const MinMax MinMax::max_("max", 1, -INF, std::numeric_limits::min(), compareMax); const std::array MinMax::range_{&min_, &max_}; -const std::array MinMax::range_index_{min_.index(), max_.index()}; +const std::array MinMax::range_index_{min_.index(), max_.index()}; -MinMax::MinMax(const char *name, - int index, +MinMax::MinMax(std::string_view name, + size_t index, float init_value, int init_value_int, bool (*compare)(float value1, float value2)) : @@ -86,7 +86,7 @@ MinMax::opposite() const } const MinMax * -MinMax::find(const char *min_max) +MinMax::find(std::string_view min_max) { if (stringEqual(min_max, "min") || stringEqual(min_max, "early")) @@ -99,7 +99,7 @@ MinMax::find(const char *min_max) } const MinMax * -MinMax::find(int index) +MinMax::find(size_t index) { if (index == min_.index()) return &min_; @@ -133,10 +133,10 @@ const MinMaxAll MinMaxAll::max_("max", 1, {MinMax::max()}, {MinMax::max()->index const MinMaxAll MinMaxAll::all_("all", 2, {MinMax::min(), MinMax::max()}, {MinMax::min()->index(), MinMax::max()->index()}); -MinMaxAll::MinMaxAll(const char *name, - int index, - std::vector range, - std::vector range_index) : +MinMaxAll::MinMaxAll(std::string_view name, + size_t index, + const std::vector &range, + const std::vector &range_index) : name_(name), index_(index), range_(range), @@ -182,4 +182,4 @@ MinMaxAll::find(const char *min_max) return nullptr; } -} // namespace +} // namespace sta diff --git a/util/PatternMatch.cc b/util/PatternMatch.cc index c4f15237b..a2eb42d04 100644 --- a/util/PatternMatch.cc +++ b/util/PatternMatch.cc @@ -23,6 +23,7 @@ // This notice may not be removed or altered from any source distribution. #include "PatternMatch.hh" + #include #include @@ -114,15 +115,14 @@ PatternMatch::matchNoCase(std::string_view str) const if (regexp_) { std::string buf(str); const char *cstr = buf.c_str(); - return Tcl_RegExpExec(0, regexp_, cstr, cstr) == 1; + return Tcl_RegExpExec(nullptr, regexp_, cstr, cstr) == 1; } return patternMatchNoCase(pattern_, str, nocase_); } //////////////////////////////////////////////////////////////// -RegexpCompileError::RegexpCompileError(std::string_view pattern) : - Exception() +RegexpCompileError::RegexpCompileError(std::string_view pattern) { error_ = "TCL failed to compile regular expression '"; error_.append(pattern.data(), pattern.size()); @@ -205,4 +205,4 @@ patternWildcards(std::string_view pattern) return pattern.find_first_of("*?") != std::string_view::npos; } -} // namespace +} // namespace sta diff --git a/util/Report.cc b/util/Report.cc index ca6bc0771..b802738bc 100644 --- a/util/Report.cc +++ b/util/Report.cc @@ -29,26 +29,18 @@ #include // strlen #include "Error.hh" -#include "Machine.hh" #include "Format.hh" +#include "Machine.hh" namespace sta { Report *Report::default_ = nullptr; -Report::Report() : - log_stream_(nullptr), - redirect_stream_(nullptr), - redirect_to_string_(false), - buffer_size_(1000), - buffer_(new char[buffer_size_]), - buffer_length_(0) +Report::Report() { default_ = this; } -Report::~Report() { delete[] buffer_; } - size_t Report::printConsole(const char *buffer, size_t length) @@ -97,64 +89,6 @@ Report::reportLine(const std::string &line) //////////////////////////////////////////////////////////////// -void -Report::printToBuffer(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - printToBuffer(fmt, args); - va_end(args); -} - -void -Report::printToBuffer(const char *fmt, - va_list args) -{ - buffer_length_ = 0; - printToBufferAppend(fmt, args); -} - -void -Report::printToBufferAppend(const char *fmt, - ...) -{ - va_list args; - va_start(args, fmt); - printToBufferAppend(fmt, args); - va_end(args); -} - -void -Report::printToBufferAppend(const char *fmt, - va_list args) -{ - // 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); - 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_; - buffer_ = new_buffer; - length = vsnprint(buffer_ + buffer_length_, buffer_size_ - buffer_length_, fmt, - args_copy); - } - buffer_length_ += length; - va_end(args_copy); -} - -void -Report::printBufferLine() -{ - printLine(buffer_, buffer_length_); -} - -//////////////////////////////////////////////////////////////// - void reportThrowExceptionMsg(const std::string &msg, bool suppressed) diff --git a/util/ReportStd.cc b/util/ReportStd.cc index bcf73c2df..d8f499e80 100644 --- a/util/ReportStd.cc +++ b/util/ReportStd.cc @@ -24,8 +24,8 @@ #include "ReportStd.hh" -#include #include +#include #include "Report.hh" @@ -35,7 +35,7 @@ namespace sta { class ReportStd : public Report { public: - ReportStd(); + ReportStd() = default; protected: size_t printConsole(const char *buffer, size_t length) override; @@ -48,11 +48,6 @@ makeReportStd() return new ReportStd; } -ReportStd::ReportStd() : - Report() -{ -} - size_t ReportStd::printConsole(const char *buffer, size_t length) { @@ -65,4 +60,4 @@ ReportStd::printErrorConsole(const char *buffer, size_t length) return fwrite(buffer, sizeof(char), length, stderr); } -} // namespace +} // namespace sta diff --git a/util/ReportTcl.cc b/util/ReportTcl.cc index e782c23c7..d5fd53839 100644 --- a/util/ReportTcl.cc +++ b/util/ReportTcl.cc @@ -82,6 +82,7 @@ static int encapCloseProc(ClientData instanceData, Tcl_Interp *interp); static int encapSeekProc(ClientData instanceData, + // NOLINTNEXTLINE(google-runtime-int) // Tcl_DriverSeekProc offset long offset, int seekMode, int *errorCodePtr); @@ -90,43 +91,36 @@ encapSeekProc(ClientData instanceData, } // extern "C" Tcl_ChannelType tcl_encap_type_stdout = { - const_cast("file"), - TCL_CHANNEL_VERSION_5, + .typeName = "file", + .version = TCL_CHANNEL_VERSION_5, #if TCL_MAJOR_VERSION < 9 - encapCloseProc, + .closeProc = encapCloseProc, #else - nullptr, // closeProc unused + .closeProc = nullptr, // closeProc unused #endif - encapInputProc, - encapOutputProc, + .inputProc = encapInputProc, + .outputProc = encapOutputProc, #if TCL_MAJOR_VERSION < 9 - encapSeekProc, + .seekProc = encapSeekProc, #else - nullptr, // seekProc unused + .seekProc = nullptr, // seekProc unused #endif - encapSetOptionProc, - encapGetOptionProc, - encapWatchProc, - encapGetHandleProc, - encapClose2Proc, - encapBlockModeProc, - nullptr, // flushProc - nullptr, // handlerProc - nullptr, // wideSeekProc - nullptr, // threadActionProc - nullptr // truncateProc + .setOptionProc = encapSetOptionProc, + .getOptionProc = encapGetOptionProc, + .watchProc = encapWatchProc, + .getHandleProc = encapGetHandleProc, + .close2Proc = encapClose2Proc, + .blockModeProc = encapBlockModeProc, + .flushProc = nullptr, + .handlerProc = nullptr, + .wideSeekProc = nullptr, + .threadActionProc = nullptr, + .truncateProc = nullptr, }; //////////////////////////////////////////////////////////////// -ReportTcl::ReportTcl() : - Report(), interp_(nullptr), - tcl_stdout_(nullptr), - tcl_stderr_(nullptr), - tcl_encap_stdout_(nullptr), - tcl_encap_stderr_(nullptr) -{ -} +ReportTcl::ReportTcl() = default; ReportTcl::~ReportTcl() { @@ -324,6 +318,7 @@ encapCloseProc(ClientData instanceData, static int encapSeekProc(ClientData, + // NOLINTNEXTLINE(google-runtime-int) // Tcl_DriverSeekProc offset long, int, int *) diff --git a/util/RiseFallMinMax.cc b/util/RiseFallMinMax.cc index 0284e8832..6c2566f4d 100644 --- a/util/RiseFallMinMax.cc +++ b/util/RiseFallMinMax.cc @@ -34,8 +34,8 @@ RiseFallMinMax::RiseFallMinMax() void RiseFallMinMax::clear() { - for (int rf_index = 0; rf_indexvalues_[rf_index][mm_index]; exists_[rf_index][mm_index] = rfmm->exists_[rf_index][mm_index]; } @@ -154,8 +154,8 @@ RiseFallMinMax::setValue(const RiseFall *rf, void RiseFallMinMax::setValues(RiseFallMinMax *values) { - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { + for (size_t rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) { values_[rf_index][mm_index] = values->values_[rf_index][mm_index]; exists_[rf_index][mm_index] = values->exists_[rf_index][mm_index]; } @@ -206,8 +206,8 @@ RiseFallMinMax::maxValue(// Return values { max_value = MinMax::max()->initValue(); exists = false; - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { + for (size_t rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) { if (exists_[rf_index][mm_index]) { max_value = std::max(max_value, values_[rf_index][mm_index]); exists = true; @@ -219,8 +219,8 @@ RiseFallMinMax::maxValue(// Return values bool RiseFallMinMax::empty() const { - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { + for (size_t rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) { if (exists_[rf_index][mm_index]) return false; } @@ -258,8 +258,8 @@ RiseFallMinMax::mergeWith(RiseFallMinMax *rfmm) bool RiseFallMinMax::equal(const RiseFallMinMax *values) const { - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index = 0; mm_index < MinMax::index_count; mm_index++) { + for (size_t rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { + for (size_t mm_index = 0; mm_index < MinMax::index_count; mm_index++) { bool exists1 = exists_[rf_index][mm_index]; bool exists2 = values->exists_[rf_index][mm_index]; if (exists1 != exists2) @@ -284,8 +284,8 @@ RiseFallMinMax::isOneValue(float &value) const { if (exists_[0][0]) { value = values_[0][0]; - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { - for (int mm_index=0; mm_indexindex(); + size_t mm_index = min_max->index(); if (exists_[0][mm_index]) { value = values_[0][mm_index]; - for (int rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { + for (size_t rf_index = 0 ; rf_index < RiseFall::index_count ; rf_index++) { if (!exists_[rf_index][mm_index] || values_[rf_index][mm_index] != value) return false; @@ -316,4 +316,4 @@ RiseFallMinMax::isOneValue(const MinMax *min_max, return false; } -} // namespace +} // namespace sta diff --git a/util/RiseFallMinMaxDelay.cc b/util/RiseFallMinMaxDelay.cc index 70cac6090..9614d3b0e 100644 --- a/util/RiseFallMinMaxDelay.cc +++ b/util/RiseFallMinMaxDelay.cc @@ -28,8 +28,8 @@ namespace sta { RiseFallMinMaxDelay::RiseFallMinMaxDelay() { - for (int rf_index = 0; rf_indexindex()]; } -} // namespace +} // namespace sta diff --git a/util/Stats.cc b/util/Stats.cc index 07fb62a19..bda5b1101 100644 --- a/util/Stats.cc +++ b/util/Stats.cc @@ -24,19 +24,15 @@ #include "Stats.hh" +#include "Debug.hh" #include "Machine.hh" -#include "StringUtil.hh" #include "Report.hh" -#include "Debug.hh" +#include "StringUtil.hh" namespace sta { Stats::Stats(Debug *debug, Report *report) : - elapsed_begin_(0.0), - user_begin_(0.0), - system_begin_(0.0), - memory_begin_(0), debug_(debug), report_(report) { diff --git a/util/StringUtil.cc b/util/StringUtil.cc index c070cb6ec..808365cdf 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -1,5 +1,5 @@ // OpenSTA, Static Timing Analyzer -// Copyright (c) 2025, Parallax Software, Inc. +// 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 @@ -78,7 +78,7 @@ stringFloat(const std::string &str) void trimRight(std::string &str) { - str.erase(str.find_last_not_of(" ") + 1); + str.erase(str.find_last_not_of(' ') + 1); } StringSeq @@ -98,4 +98,4 @@ parseTokens(const std::string &text, return tokens; } -} // namespace +} // namespace sta diff --git a/util/Transition.cc b/util/Transition.cc index 4318366dc..25d872f96 100644 --- a/util/Transition.cc +++ b/util/Transition.cc @@ -33,11 +33,11 @@ namespace sta { const RiseFall RiseFall::rise_("rise", "^", 0); const RiseFall RiseFall::fall_("fall", "v", 1); const std::array RiseFall::range_{&rise_, &fall_}; -const std::array RiseFall::range_index_{rise_.index(), fall_.index()}; +const std::array RiseFall::range_index_{rise_.index(), fall_.index()}; RiseFall::RiseFall(std::string_view name, std::string_view short_name, - int sdf_triple_index) : + size_t sdf_triple_index) : name_(name), short_name_(short_name), sdf_triple_index_(sdf_triple_index) @@ -63,17 +63,17 @@ RiseFall::opposite() const } const RiseFall * -RiseFall::find(std::string_view rf_str) +RiseFall::find(std::string_view rf_name) { - if (rf_str == rise_.name() || rf_str == rise_.shortName()) + if (rf_name == rise_.name() || rf_name == rise_.shortName()) return &rise_; - if (rf_str == fall_.name() || rf_str == fall_.shortName()) + if (rf_name == fall_.name() || rf_name == fall_.shortName()) return &fall_; return nullptr; } const RiseFall * -RiseFall::find(int index) +RiseFall::find(size_t index) { if (index == rise_.index()) return &rise_; @@ -127,10 +127,10 @@ const RiseFallBoth RiseFallBoth::rise_fall_("rise_fall", "rf", 2, RiseFallBoth::RiseFallBoth(std::string_view name, std::string_view short_name, - int sdf_triple_index, + size_t sdf_triple_index, const RiseFall *as_rise_fall, - std::vector range, - std::vector range_index) : + const std::vector &range, + const std::vector &range_index) : name_(name), short_name_(short_name), sdf_triple_index_(sdf_triple_index), @@ -150,13 +150,13 @@ RiseFallBoth::to_string(bool use_short) const } const RiseFallBoth * -RiseFallBoth::find(std::string_view name) +RiseFallBoth::find(std::string_view rf_name) { - if (name == rise_.name()) + if (rf_name == rise_.name()) return &rise_; - if (name == fall_.name()) + if (rf_name == fall_.name()) return &fall_; - if (name == rise_fall_.name()) + if (rf_name == rise_fall_.name()) return &rise_fall_; return nullptr; } @@ -181,7 +181,7 @@ RiseFallBoth::matches(const Transition *tr) const //////////////////////////////////////////////////////////////// TransitionMap Transition::transition_map_; -int Transition::max_index_ = 0; +size_t Transition::max_index_ = 0; // Sdf triple order defined on Sdf 3.0 spec, pg 3-17. const Transition Transition::rise_{ "^", "01", RiseFall::rise(), 0}; @@ -196,12 +196,12 @@ const Transition Transition::tr_1X_{"1X", "1X", RiseFall::fall(), 8}; const Transition Transition::tr_X0_{"X0", "X0", RiseFall::fall(), 9}; const Transition Transition::tr_XZ_{"XZ", "XZ", nullptr, 10}; const Transition Transition::tr_ZX_{"ZX", "ZX", nullptr, 11}; -const Transition Transition::rise_fall_{"*", "**", nullptr, -1}; +const Transition Transition::rise_fall_{"*", "**", nullptr, 12}; Transition::Transition(std::string_view name, std::string_view init_final, const RiseFall *as_rise_fall, - int sdf_triple_index) : + size_t sdf_triple_index) : name_(name), init_final_(init_final), as_rise_fall_(as_rise_fall), @@ -219,9 +219,9 @@ Transition::matches(const Transition *tr) const } const Transition * -Transition::find(std::string_view tr_str) +Transition::find(std::string_view tr_name) { - return findStringKey(transition_map_, tr_str); + return findStringKey(transition_map_, tr_name); } const RiseFallBoth * @@ -230,4 +230,4 @@ Transition::asRiseFallBoth() const return reinterpret_cast(as_rise_fall_); } -} // namespace +} // namespace sta diff --git a/util/Util.i b/util/Util.i index c34ba4a8c..4379982fe 100644 --- a/util/Util.i +++ b/util/Util.i @@ -27,14 +27,14 @@ %{ +#include "Error.hh" +#include "Fuzzy.hh" +#include "Report.hh" #include "Sta.hh" #include "StaConfig.hh" // STA_VERSION #include "Stats.hh" -#include "Report.hh" -#include "Error.hh" -#include "Fuzzy.hh" -#include "Units.hh" #include "StringUtil.hh" +#include "Units.hh" using namespace sta; diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy index 9bd7f1621..c5a1c8cd8 100644 --- a/verilog/VerilogParse.yy +++ b/verilog/VerilogParse.yy @@ -25,7 +25,6 @@ %{ #include #include -#include #include "Report.hh" #include "PortDirection.hh" @@ -109,13 +108,13 @@ modules: module: attr_instance_seq MODULE ID ';' stmts ENDMODULE - { reader->makeModule(std::move($3), new sta::VerilogNetSeq,$5, $1, loc_line(@2));} + { reader->makeModule($3, new sta::VerilogNetSeq,$5, $1, loc_line(@2));} | attr_instance_seq MODULE ID '(' ')' ';' stmts ENDMODULE - { reader->makeModule(std::move($3), new sta::VerilogNetSeq,$7, $1, loc_line(@2));} + { reader->makeModule($3, new sta::VerilogNetSeq,$7, $1, loc_line(@2));} | attr_instance_seq MODULE ID '(' port_list ')' ';' stmts ENDMODULE - { reader->makeModule(std::move($3), $5, $8, $1, loc_line(@2)); } + { reader->makeModule($3, $5, $8, $1, loc_line(@2)); } | attr_instance_seq MODULE ID '(' port_dcls ')' ';' stmts ENDMODULE - { reader->makeModule(std::move($3), $5, $8, $1, loc_line(@2)); } + { reader->makeModule($3, $5, $8, $1, loc_line(@2)); } ; port_list: @@ -130,9 +129,9 @@ port_list: port: port_expr | '.' ID '(' ')' - { $$ = reader->makeNetNamedPortRefScalar(std::move($2), nullptr);} + { $$ = reader->makeNetNamedPortRefScalar($2, nullptr);} | '.' ID '(' port_expr ')' - { $$ = reader->makeNetNamedPortRefScalar(std::move($2), $4);} + { $$ = reader->makeNetNamedPortRefScalar($2, $4);} ; port_expr: @@ -332,9 +331,9 @@ dcl_args: dcl_arg: ID - { $$ = reader->makeDclArg(std::move($1)); } + { $$ = reader->makeDclArg($1); } | net_assignment - { $$ = reader->makeDclArg(std::move($1)); } + { $$ = reader->makeDclArg($1); } ; continuous_assign: @@ -363,9 +362,9 @@ net_assign_lhs: instance: attr_instance_seq ID ID '(' inst_pins ')' ';' - { $$ = reader->makeModuleInst(std::move($2), std::move($3), $5, $1, loc_line(@2)); } + { $$ = reader->makeModuleInst($2, $3, $5, $1, loc_line(@2)); } | attr_instance_seq ID parameter_values ID '(' inst_pins ')' ';' - { $$ = reader->makeModuleInst(std::move($2), std::move($4), $6, $1, loc_line(@2)); } + { $$ = reader->makeModuleInst($2, $4, $6, $1, loc_line(@2)); } ; parameter_values: @@ -411,23 +410,23 @@ inst_named_pins: inst_named_pin: // Scalar port. '.' ID '(' ')' - { $$ = reader->makeNetNamedPortRefScalarNet(std::move($2)); } + { $$ = reader->makeNetNamedPortRefScalarNet($2); } | '.' ID '(' ID ')' - { $$ = reader->makeNetNamedPortRefScalarNet(std::move($2), std::move($4)); } + { $$ = reader->makeNetNamedPortRefScalarNet($2, $4); } | '.' ID '(' ID '[' INT ']' ')' - { $$ = reader->makeNetNamedPortRefBitSelect(std::move($2), std::move($4), $6); } + { $$ = reader->makeNetNamedPortRefBitSelect($2, $4, $6); } | '.' ID '(' named_pin_net_expr ')' - { $$ = reader->makeNetNamedPortRefScalar(std::move($2), $4); } + { $$ = reader->makeNetNamedPortRefScalar($2, $4); } // Bus port bit select. | '.' ID '[' INT ']' '(' ')' - { $$ = reader->makeNetNamedPortRefBit(std::move($2), $4, nullptr); } + { $$ = reader->makeNetNamedPortRefBit($2, $4, nullptr); } | '.' ID '[' INT ']' '(' net_expr ')' - { $$ = reader->makeNetNamedPortRefBit(std::move($2), $4, $7); } + { $$ = reader->makeNetNamedPortRefBit($2, $4, $7); } // Bus port part select. | '.' ID '[' INT ':' INT ']' '(' ')' - { $$ = reader->makeNetNamedPortRefPart(std::move($2), $4, $6, nullptr); } + { $$ = reader->makeNetNamedPortRefPart($2, $4, $6, nullptr); } | '.' ID '[' INT ':' INT ']' '(' net_expr ')' - { $$ = reader->makeNetNamedPortRefPart(std::move($2), $4, $6, $9); } + { $$ = reader->makeNetNamedPortRefPart($2, $4, $6, $9); } ; named_pin_net_expr: @@ -444,22 +443,22 @@ net_named: net_scalar: ID - { $$ = reader->makeNetScalar(std::move($1)); } + { $$ = reader->makeNetScalar($1); } ; net_bit_select: ID '[' INT ']' - { $$ = reader->makeNetBitSelect(std::move($1), $3); } + { $$ = reader->makeNetBitSelect($1, $3); } ; net_part_select: ID '[' INT ':' INT ']' - { $$ = reader->makeNetPartSelect(std::move($1), $3, $5); } + { $$ = reader->makeNetPartSelect($1, $3, $5); } ; net_constant: CONSTANT - { $$ = reader->makeNetConstant(std::move($1), loc_line(@1)); } + { $$ = reader->makeNetConstant($1, loc_line(@1)); } ; net_expr_concat: @@ -507,16 +506,16 @@ attr_specs: attr_spec: ID - { $$ = new sta::VerilogAttrEntry(std::move($1), "1"); } + { $$ = new sta::VerilogAttrEntry($1, "1"); } | ID '=' attr_spec_value - { $$ = new sta::VerilogAttrEntry(std::move($1), std::move($3)); } + { $$ = new sta::VerilogAttrEntry($1, $3); } ; attr_spec_value: CONSTANT - { $$ = std::move($1); } + { $$ = $1; } | STRING - { $$ = std::move($1); } + { $$ = $1; } | INT { $$ = std::to_string($1); } ; diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 68f99b62e..d9f767474 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -47,7 +47,7 @@ namespace sta { using VerilogConstant10 = unsigned long long; static std::string -verilogBusBitName(const std::string &bus_name, +verilogBusBitName(std::string_view bus_name, int index); static int hierarchyLevel(Net *net, @@ -168,13 +168,13 @@ VerilogReader::module(Cell *cell) } void -VerilogReader::makeModule(std::string &&module_vname, +VerilogReader::makeModule(std::string_view module_vname, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, int line) { - const std::string module_name = moduleVerilogToSta(std::move(module_vname)); + const std::string module_name = moduleVerilogToSta(module_vname); Cell *cell = network_->findCell(library_, module_name); if (cell) { VerilogModule *module = module_map_[cell]; @@ -199,7 +199,7 @@ VerilogReader::makeModule(std::string &&module_vname, } void -VerilogReader::makeModule(std::string &&module_vname, +VerilogReader::makeModule(std::string_view module_vname, VerilogStmtSeq *port_dcls, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, @@ -219,7 +219,7 @@ VerilogReader::makeModule(std::string &&module_vname, } } delete port_dcls; - makeModule(std::move(module_vname), ports, stmts, attr_stmts, line); + makeModule(module_vname, ports, stmts, attr_stmts, line); } void @@ -371,9 +371,9 @@ VerilogReader::makeDclBus(PortDirection *dir, } VerilogDclArg * -VerilogReader::makeDclArg(std::string &&net_vname) +VerilogReader::makeDclArg(std::string_view net_vname) { - const std::string net_name = netVerilogToSta(std::move(net_vname)); + const std::string net_name = netVerilogToSta(net_vname); VerilogDclArg *dcl = new VerilogDclArg(net_name); return dcl; } @@ -385,36 +385,36 @@ VerilogReader::makeDclArg(VerilogAssign *assign) } VerilogNetPartSelect * -VerilogReader::makeNetPartSelect(std::string &&net_vname, +VerilogReader::makeNetPartSelect(std::string_view net_vname, int from_index, int to_index) { - const std::string net_name = netVerilogToSta(std::move(net_vname)); + const std::string net_name = netVerilogToSta(net_vname); VerilogNetPartSelect *select = new VerilogNetPartSelect(net_name, from_index, to_index); return select; } VerilogNetConstant * -VerilogReader::makeNetConstant(std::string &&constant, +VerilogReader::makeNetConstant(std::string_view constant, int line) { - return new VerilogNetConstant(std::move(constant), this, line); + return new VerilogNetConstant(constant, this, line); } VerilogNetScalar * -VerilogReader::makeNetScalar(std::string &&net_vname) +VerilogReader::makeNetScalar(std::string_view net_vname) { - const std::string net_name = netVerilogToSta(std::move(net_vname)); + const std::string net_name = netVerilogToSta(net_vname); VerilogNetScalar *scalar = new VerilogNetScalar(net_name); return scalar; } VerilogNetBitSelect * -VerilogReader::makeNetBitSelect(std::string &&net_vname, +VerilogReader::makeNetBitSelect(std::string_view net_vname, int index) { - const std::string net_name = netVerilogToSta(std::move(net_vname)); + const std::string net_name = netVerilogToSta(net_vname); VerilogNetBitSelect *select = new VerilogNetBitSelect(net_name, index); return select; } @@ -428,14 +428,14 @@ VerilogReader::makeAssign(VerilogNet *lhs, } VerilogInst * -VerilogReader::makeModuleInst(std::string &&module_vname, - std::string &&inst_vname, +VerilogReader::makeModuleInst(std::string_view module_vname, + std::string_view inst_vname, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, - const int line) + int line) { - const std::string module_name = moduleVerilogToSta(std::move(module_vname)); - const std::string inst_name = instanceVerilogToSta(std::move(inst_vname)); + const std::string module_name = moduleVerilogToSta(module_vname); + const std::string inst_name = instanceVerilogToSta(inst_vname); Cell *cell = network_->findAnyCell(module_name); LibertyCell *liberty_cell = nullptr; if (cell) @@ -443,7 +443,7 @@ VerilogReader::makeModuleInst(std::string &&module_vname, // 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)) { - const int port_count = liberty_cell->portBitCount(); + int port_count = liberty_cell->portBitCount(); StringSeq net_names(port_count); for (VerilogNet *vnet : *pins) { VerilogNetPortRefScalarNet *vpin = @@ -494,62 +494,62 @@ VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalarNet(std::string &&port_vname) +VerilogReader::makeNetNamedPortRefScalarNet(std::string_view port_vname) { - const std::string port_name = portVerilogToSta(std::move(port_vname)); + const std::string port_name = portVerilogToSta(port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalarNet(std::string &&port_vname, - std::string &&net_vname) +VerilogReader::makeNetNamedPortRefScalarNet(std::string_view port_vname, + std::string_view net_vname) { - const std::string port_name = portVerilogToSta(std::move(port_vname)); - const std::string net_name = netVerilogToSta(std::move(net_vname)); + const std::string port_name = portVerilogToSta(port_vname); + const std::string net_name = netVerilogToSta(net_vname); VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name, net_name); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefBitSelect(std::string &&port_vname, - std::string &&bus_vname, +VerilogReader::makeNetNamedPortRefBitSelect(std::string_view port_vname, + std::string_view bus_vname, int index) { - const std::string bus_name = portVerilogToSta(std::move(bus_vname)); + const std::string bus_name = portVerilogToSta(bus_vname); const std::string net_name = verilogBusBitName(bus_name, index); - const std::string port_name = portVerilogToSta(std::move(port_vname)); + const std::string port_name = portVerilogToSta(port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefScalarNet(port_name, net_name); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefScalar(std::string &&port_vname, +VerilogReader::makeNetNamedPortRefScalar(std::string_view port_vname, VerilogNet *net) { - const std::string port_name = portVerilogToSta(std::move(port_vname)); + const std::string port_name = portVerilogToSta(port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefScalar(port_name, net); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefBit(std::string &&port_vname, +VerilogReader::makeNetNamedPortRefBit(std::string_view port_vname, int index, VerilogNet *net) { - const std::string port_name = portVerilogToSta(std::move(port_vname)); + const std::string port_name = portVerilogToSta(port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefBit(port_name, index, net); return ref; } VerilogNetPortRef * -VerilogReader::makeNetNamedPortRefPart(std::string &&port_vname, +VerilogReader::makeNetNamedPortRefPart(std::string_view port_vname, int from_index, int to_index, VerilogNet *net) { - const std::string port_name = portVerilogToSta(std::move(port_vname)); + const std::string port_name = portVerilogToSta(port_vname); VerilogNetPortRef *ref = new VerilogNetPortRefPart(port_name, from_index, to_index, net); return ref; @@ -563,11 +563,11 @@ VerilogReader::makeNetConcat(VerilogNetSeq *nets) //////////////////////////////////////////////////////////////// -VerilogModule::VerilogModule(const std::string &name, +VerilogModule::VerilogModule(std::string_view name, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, - const std::string &filename, + std::string_view filename, int line, VerilogReader *reader) : VerilogStmt(line), @@ -679,9 +679,9 @@ VerilogStmt::VerilogStmt(int line) : { } -VerilogInst::VerilogInst(const std::string &inst_name, +VerilogInst::VerilogInst(std::string_view inst_name, VerilogAttrStmtSeq *attr_stmts, - const int line) : + int line) : VerilogStmt(line), inst_name_(inst_name), attr_stmts_(attr_stmts) @@ -700,8 +700,8 @@ VerilogInst::setInstanceName(const std::string &inst_name) inst_name_ = inst_name; } -VerilogModuleInst::VerilogModuleInst(const std::string &module_name, - const std::string &inst_name, +VerilogModuleInst::VerilogModuleInst(std::string_view module_name, + std::string_view inst_name, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, int line) : @@ -734,13 +734,11 @@ VerilogModuleInst::namedPins() } VerilogLibertyInst::VerilogLibertyInst(LibertyCell *cell, - const std::string &inst_name, + std::string_view inst_name, const StringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, - const int line) : - VerilogInst(inst_name, - attr_stmts, - line), + int line) : + VerilogInst(inst_name, attr_stmts, line), cell_(cell), net_names_(net_names) { @@ -795,10 +793,7 @@ 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) { @@ -810,10 +805,7 @@ 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) { @@ -825,7 +817,7 @@ VerilogDclBus::size() const return std::abs(to_index_ - from_index_) + 1; } -VerilogDclArg::VerilogDclArg(const std::string &net_name) : +VerilogDclArg::VerilogDclArg(std::string_view net_name) : net_name_(net_name), assign_(nullptr) { @@ -955,7 +947,7 @@ VerilogBusNetNameIterator::next() } static std::string -verilogBusBitName(const std::string &bus_name, +verilogBusBitName(std::string_view bus_name, int index) { return sta::format("{}[{}]", bus_name, index); @@ -1066,15 +1058,13 @@ VerilogNetConcatNameIterator::next() const std::string VerilogNetUnnamed::null_; -VerilogNetNamed::VerilogNetNamed(const std::string &name) : +VerilogNetNamed::VerilogNetNamed(std::string_view name) : VerilogNet(), name_(name) { } -VerilogNetNamed::~VerilogNetNamed() {} - -VerilogNetScalar::VerilogNetScalar(const std::string &name) : +VerilogNetScalar::VerilogNetScalar(std::string_view name) : VerilogNetNamed(name) { } @@ -1119,7 +1109,7 @@ VerilogNetScalar::nameIterator(VerilogModule *module, return verilogNetScalarNameIterator(name_, module); } -VerilogNetBitSelect::VerilogNetBitSelect(const std::string &name, +VerilogNetBitSelect::VerilogNetBitSelect(std::string_view name, int index) : VerilogNetNamed(verilogBusBitName(name, index)), @@ -1140,7 +1130,7 @@ VerilogNetBitSelect::nameIterator(VerilogModule *, return new VerilogOneNetNameIterator(name_); } -VerilogNetPartSelect::VerilogNetPartSelect(const std::string &name, +VerilogNetPartSelect::VerilogNetPartSelect(std::string_view name, int from_index, int to_index) : VerilogNetNamed(name), @@ -1165,7 +1155,7 @@ VerilogNetPartSelect::nameIterator(VerilogModule *, return new VerilogBusNetNameIterator(name_, from_index_, to_index_); } -VerilogNetConstant::VerilogNetConstant(std::string constant, +VerilogNetConstant::VerilogNetConstant(std::string_view constant, VerilogReader *reader, int line) { @@ -1173,13 +1163,13 @@ VerilogNetConstant::VerilogNetConstant(std::string constant, } void -VerilogNetConstant::parseConstant(const std::string &constant, +VerilogNetConstant::parseConstant(std::string_view constant, VerilogReader *reader, int line) { // Find constant size. size_t csize_end = constant.find('\''); - std::string csize = constant.substr(0, csize_end); + std::string csize(constant.substr(0, csize_end)); // Read the constant size. size_t size = std::stol(csize); @@ -1214,7 +1204,7 @@ VerilogNetConstant::parseConstant(const std::string &constant, } void -VerilogNetConstant::parseConstant(const std::string &constant, +VerilogNetConstant::parseConstant(std::string_view constant, size_t base_idx, int base, int digit_bit_count) @@ -1243,30 +1233,31 @@ VerilogNetConstant::parseConstant(const std::string &constant, } void -VerilogNetConstant::parseConstant10(const std::string &constant, - size_t base_idx, - VerilogReader *reader, - int line) +VerilogNetConstant::parseConstant10(std::string_view constant, + size_t base_idx, + VerilogReader *reader, + int line) { // Copy the constant skipping underscores. - std::string tmp; + std::string constant1; for (size_t i = base_idx + 1; i < constant.size(); i++) { char ch = constant.at(i); if (ch != '_') - tmp += ch; + constant1 += ch; } size_t size = value_->size(); - size_t length = tmp.size(); + size_t length = constant1.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 && constant1 > constant10_max)) reader->warn(1397, reader->filename(), line, "base 10 constant greater than {} not supported.", constant10_max); else { size_t *end = nullptr; - VerilogConstant10 value = std::stoull(tmp, end, 10); + VerilogConstant10 value = std::stoull(constant1, end, 10); VerilogConstant10 mask = 1; for (size_t bit = 0; bit < size; bit++) { (*value_)[bit] = (value & mask) != 0; @@ -1318,18 +1309,18 @@ VerilogNetConcat::nameIterator(VerilogModule *module, return new VerilogNetConcatNameIterator(nets_, module, reader); } -VerilogNetPortRef::VerilogNetPortRef(const std::string &name) : +VerilogNetPortRef::VerilogNetPortRef(std::string_view name) : VerilogNetScalar(name) { } -VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(const std::string &name) : +VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(std::string_view name) : VerilogNetPortRef(name) { } -VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(const std::string &name, - const std::string &net_name) : +VerilogNetPortRefScalarNet::VerilogNetPortRefScalarNet(std::string_view name, + std::string_view net_name) : VerilogNetPortRef(name), net_name_(net_name) { @@ -1356,7 +1347,7 @@ VerilogNetPortRefScalarNet::nameIterator(VerilogModule *module, return verilogNetScalarNameIterator(net_name_, module); } -VerilogNetPortRefScalar::VerilogNetPortRefScalar(const std::string &name, +VerilogNetPortRefScalar::VerilogNetPortRefScalar(std::string_view name, VerilogNet *net) : VerilogNetPortRef(name), net_(net) @@ -1384,7 +1375,7 @@ VerilogNetPortRefScalar::nameIterator(VerilogModule *module, return new VerilogNullNetNameIterator(); } -VerilogNetPortRefBit::VerilogNetPortRefBit(const std::string &name, +VerilogNetPortRefBit::VerilogNetPortRefBit(std::string_view name, int index, VerilogNet *net) : VerilogNetPortRefScalar(name, @@ -1394,13 +1385,11 @@ VerilogNetPortRefBit::VerilogNetPortRefBit(const std::string &name, { } -VerilogNetPortRefPart::VerilogNetPortRefPart(const std::string &name, +VerilogNetPortRefPart::VerilogNetPortRefPart(std::string_view name, int from_index, int to_index, VerilogNet *net) : - VerilogNetPortRefBit(name, - from_index, - net), + VerilogNetPortRefBit(name, from_index, net), to_index_(to_index) { } @@ -1411,25 +1400,13 @@ VerilogNetPortRefPart::name() const return name_; } -VerilogAttrEntry::VerilogAttrEntry(const std::string &key, - const std::string &value) : +VerilogAttrEntry::VerilogAttrEntry(std::string_view key, + std::string_view value) : key_(key), value_(value) { } -std::string -VerilogAttrEntry::key() -{ - return key_; -} - -std::string -VerilogAttrEntry::value() -{ - return value_; -} - VerilogAttrStmt::VerilogAttrStmt(VerilogAttrEntrySeq *attrs) : attrs_(attrs) { diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index fc5381f8b..e3569c619 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include @@ -39,7 +40,7 @@ class VerilogStmt { public: VerilogStmt(int line); - virtual ~VerilogStmt() {} + virtual ~VerilogStmt() = default; virtual bool isInstance() const { return false; } virtual bool isModuleInst() const { return false; } virtual bool isLibertyInst() const { return false; } @@ -54,11 +55,11 @@ private: class VerilogModule : public VerilogStmt { public: - VerilogModule(const std::string &name, + VerilogModule(std::string_view name, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, - const std::string &filename, + std::string_view filename, int line, VerilogReader *reader); ~VerilogModule() override; @@ -142,7 +143,7 @@ private: class VerilogDclArg { public: - VerilogDclArg(const std::string &net_name); + VerilogDclArg(std::string_view net_name); VerilogDclArg(VerilogAssign *assign); ~VerilogDclArg(); const std::string &netName(); @@ -174,7 +175,7 @@ private: class VerilogInst : public VerilogStmt { public: - VerilogInst(const std::string &inst_name, + VerilogInst(std::string_view inst_name, VerilogAttrStmtSeq *attr_stmts, const int line); ~VerilogInst() override; @@ -191,8 +192,8 @@ private: class VerilogModuleInst : public VerilogInst { public: - VerilogModuleInst(const std::string &module_name, - const std::string &inst_name, + VerilogModuleInst(std::string_view module_name, + std::string_view inst_name, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, const int line); @@ -215,7 +216,7 @@ class VerilogLibertyInst : public VerilogInst { public: VerilogLibertyInst(LibertyCell *cell, - const std::string &inst_name, + std::string_view inst_name, const StringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, const int line); @@ -232,8 +233,7 @@ private: class VerilogNet { public: - VerilogNet() {} - virtual ~VerilogNet() {} + virtual ~VerilogNet() = default; virtual bool isNamed() const = 0; virtual const std::string &name() const = 0; virtual bool isNamedPortRef() { return false; } @@ -257,8 +257,7 @@ private: class VerilogNetNamed : public VerilogNet { public: - VerilogNetNamed(const std::string &name); - ~VerilogNetNamed() override; + VerilogNetNamed(std::string_view name); bool isNamed() const override { return true; } virtual bool isScalar() const = 0; const std::string &name() const override { return name_; } @@ -271,7 +270,7 @@ protected: class VerilogNetScalar : public VerilogNetNamed { public: - VerilogNetScalar(const std::string &name); + VerilogNetScalar(std::string_view name); bool isScalar() const override { return true; } int size(VerilogModule *module) override; VerilogNetNameIterator *nameIterator(VerilogModule *module, @@ -281,7 +280,7 @@ public: class VerilogNetBitSelect : public VerilogNetNamed { public: - VerilogNetBitSelect(const std::string &name, + VerilogNetBitSelect(std::string_view name, int index); int index() { return index_; } bool isScalar() const override { return false; } @@ -295,7 +294,7 @@ private: class VerilogNetPartSelect : public VerilogNetNamed { public: - VerilogNetPartSelect(const std::string &name, + VerilogNetPartSelect(std::string_view name, int from_index, int to_index); bool isScalar() const override { return false; } @@ -313,7 +312,7 @@ private: class VerilogNetConstant : public VerilogNetUnnamed { public: - VerilogNetConstant(std::string constant, + VerilogNetConstant(std::string_view constant, VerilogReader *reader, int line); ~VerilogNetConstant() override; @@ -322,14 +321,14 @@ public: VerilogReader *reader) override; private: - void parseConstant(const std::string &constant, + void parseConstant(std::string_view constant, VerilogReader *reader, int line); - void parseConstant(const std::string &constant, + void parseConstant(std::string_view constant, size_t base_idx, int base, int digit_bit_count); - void parseConstant10(const std::string &constant, + void parseConstant10(std::string_view constant, size_t base_idx, VerilogReader *reader, int line); @@ -354,7 +353,7 @@ private: class VerilogNetPortRef : public VerilogNetScalar { public: - VerilogNetPortRef(const std::string &name); + VerilogNetPortRef(std::string_view name); bool isNamedPortRef() override { return true; } virtual bool hasNet() = 0; }; @@ -366,9 +365,9 @@ public: class VerilogNetPortRefScalarNet : public VerilogNetPortRef { public: - VerilogNetPortRefScalarNet(const std::string &name); - VerilogNetPortRefScalarNet(const std::string &name, - const std::string &net_name); + VerilogNetPortRefScalarNet(std::string_view name); + VerilogNetPortRefScalarNet(std::string_view name, + std::string_view net_name); bool isScalar() const override { return true; } bool isNamedPortRefScalarNet() const override { return true; } int size(VerilogModule *module) override; @@ -385,7 +384,7 @@ private: class VerilogNetPortRefScalar : public VerilogNetPortRef { public: - VerilogNetPortRefScalar(const std::string &name, + VerilogNetPortRefScalar(std::string_view name, VerilogNet *net); ~VerilogNetPortRefScalar() override; bool isScalar() const override { return true; } @@ -401,7 +400,7 @@ private: class VerilogNetPortRefBit : public VerilogNetPortRefScalar { public: - VerilogNetPortRefBit(const std::string &name, + VerilogNetPortRefBit(std::string_view name, int index, VerilogNet *net); const std::string &name() const override { return bit_name_; } @@ -413,7 +412,7 @@ private: class VerilogNetPortRefPart : public VerilogNetPortRefBit { public: - VerilogNetPortRefPart(const std::string &name, + VerilogNetPortRefPart(std::string_view name, int from_index, int to_index, VerilogNet *net); @@ -433,8 +432,8 @@ class VerilogAttrStmt { public: VerilogAttrStmt(VerilogAttrEntrySeq *attrs); - VerilogAttrEntrySeq *attrs(); virtual ~VerilogAttrStmt(); + VerilogAttrEntrySeq *attrs(); private: VerilogAttrEntrySeq *attrs_; @@ -443,15 +442,14 @@ private: class VerilogAttrEntry { public: - VerilogAttrEntry(const std::string &key, - const std::string &value); - virtual std::string key(); - virtual std::string value(); - virtual ~VerilogAttrEntry() = default; + VerilogAttrEntry(std::string_view key, + std::string_view value); + const std::string &key() const { return key_; } + const std::string &value() const { return value_; } private: std::string key_; std::string value_; }; -} // namespace +} // namespace sta diff --git a/verilog/VerilogScanner.hh b/verilog/VerilogScanner.hh index 3c0c5d613..92b0d395d 100644 --- a/verilog/VerilogScanner.hh +++ b/verilog/VerilogScanner.hh @@ -46,8 +46,6 @@ public: VerilogScanner(std::istream *stream, std::string_view filename, Report *report); - virtual ~VerilogScanner() {} - virtual int lex(VerilogParse::semantic_type *const yylval, VerilogParse::location_type *yylloc); // YY_DECL defined in VerilogLex.ll @@ -65,4 +63,4 @@ private: std::string token_; }; -} // namespace +} // namespace sta diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index 49ecedf31..954c560a4 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -515,4 +515,4 @@ VerilogWriter::findPortNCcount(const Instance *inst, return nc_count; } -} // namespace +} // namespace sta From 21848bcdd2dd36515c0db7b3047661f78514a8b9 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 15 Apr 2026 09:38:10 -0700 Subject: [PATCH 152/181] clang tidy Signed-off-by: James Cherry --- .clang-tidy | 7 +- dcalc/ArcDcalcWaveforms.cc | 12 +- dcalc/ArcDelayCalc.cc | 20 +-- dcalc/Arnoldi.hh | 12 +- dcalc/ArnoldiDelayCalc.cc | 171 ++++++++++++++---------- dcalc/ArnoldiReduce.cc | 30 ++--- dcalc/ArnoldiReduce.hh | 10 +- dcalc/CcsCeffDelayCalc.cc | 55 ++++---- dcalc/CcsCeffDelayCalc.hh | 26 ++-- dcalc/Delay.cc | 4 +- dcalc/DelayCalc.cc | 12 +- dcalc/DelayCalc.i | 4 +- dcalc/DelayCalcBase.cc | 12 +- dcalc/DelayCalcBase.hh | 2 +- dcalc/DelayNormal.cc | 4 +- dcalc/DelayScalar.cc | 2 +- dcalc/DelaySkewNormal.cc | 4 +- dcalc/DmpCeff.cc | 131 +++++++++--------- dcalc/DmpCeff.hh | 4 +- dcalc/DmpDelayCalc.cc | 35 +++-- dcalc/FindRoot.cc | 4 +- dcalc/FindRoot.hh | 4 +- dcalc/GraphDelayCalc.cc | 80 +++++------ dcalc/LumpedCapDelayCalc.cc | 12 +- dcalc/LumpedCapDelayCalc.hh | 2 +- dcalc/NetCaps.cc | 4 - dcalc/NetCaps.hh | 2 +- dcalc/ParallelDelayCalc.cc | 10 +- dcalc/ParallelDelayCalc.hh | 2 +- dcalc/PrimaDelayCalc.cc | 68 ++++------ dcalc/PrimaDelayCalc.hh | 33 ++--- graph/Graph.cc | 50 +++---- graph/Graph.i | 6 +- graph/GraphCmp.cc | 9 +- include/sta/ArcDelayCalc.hh | 14 +- include/sta/Bdd.hh | 2 +- include/sta/Bfs.hh | 4 +- include/sta/BoundedHeap.hh | 2 +- include/sta/ClkNetwork.hh | 4 +- include/sta/Clock.hh | 41 +++--- include/sta/ClockGatingCheck.hh | 5 +- include/sta/ClockGroups.hh | 2 +- include/sta/ClockInsertion.hh | 2 +- include/sta/ClockLatency.hh | 4 +- include/sta/ConcreteLibrary.hh | 30 ++--- include/sta/ConcreteNetwork.hh | 43 +++--- include/sta/CycleAccting.hh | 6 +- include/sta/DataCheck.hh | 4 +- include/sta/Debug.hh | 4 +- include/sta/Delay.hh | 4 +- include/sta/DeratingFactors.hh | 10 +- include/sta/DisabledPorts.hh | 13 +- include/sta/DispatchQueue.hh | 12 +- include/sta/Error.hh | 1 - include/sta/ExceptionPath.hh | 15 +-- include/sta/FilterObjects.hh | 20 +-- include/sta/FuncExpr.hh | 2 +- include/sta/Graph.hh | 22 +-- include/sta/GraphClass.hh | 6 +- include/sta/GraphCmp.hh | 2 +- include/sta/GraphDelayCalc.hh | 29 ++-- include/sta/Hash.hh | 2 +- include/sta/InputDrive.hh | 15 ++- include/sta/Liberty.hh | 18 +-- include/sta/LibertyClass.hh | 4 +- include/sta/MinMax.hh | 2 +- include/sta/MinMaxValues.hh | 2 +- include/sta/Network.hh | 20 +-- include/sta/NetworkClass.hh | 4 +- include/sta/Parasitics.hh | 20 +-- include/sta/Path.hh | 8 +- include/sta/PathEnd.hh | 4 +- include/sta/PathExpanded.hh | 4 +- include/sta/PathGroup.hh | 8 +- include/sta/PortDelay.hh | 6 +- include/sta/PortExtCap.hh | 7 +- include/sta/PowerClass.hh | 3 +- include/sta/Property.hh | 6 +- include/sta/Report.hh | 8 +- include/sta/RiseFallMinMaxDelay.hh | 2 +- include/sta/Scene.hh | 2 +- include/sta/Sdc.hh | 28 ++-- include/sta/SdcClass.hh | 11 +- include/sta/SdcNetwork.hh | 12 +- include/sta/Search.hh | 17 ++- include/sta/SearchClass.hh | 8 +- include/sta/SearchPred.hh | 2 +- include/sta/Sta.hh | 26 ++-- include/sta/StringUtil.hh | 2 +- include/sta/TableModel.hh | 4 +- include/sta/TclTypeHelpers.hh | 8 +- include/sta/TimingArc.hh | 4 +- include/sta/Variables.hh | 31 +++-- include/sta/VectorMap.hh | 4 +- include/sta/VerilogNamespace.hh | 8 +- include/sta/VerilogReader.hh | 34 ++--- include/sta/VertexVisitor.hh | 2 +- include/sta/VisitPathEnds.hh | 2 +- network/ConcreteLibrary.cc | 25 +--- network/ConcreteNetwork.cc | 134 +++++++++---------- network/HpinDrvrLoad.cc | 10 +- network/Network.cc | 46 ++----- network/Network.i | 4 +- network/NetworkCmp.cc | 2 +- network/ParseBus.cc | 4 +- network/SdcNetwork.cc | 12 +- network/VerilogNamespace.cc | 4 +- parasitics/ConcreteParasitics.cc | 71 +++++----- parasitics/ConcreteParasitics.hh | 15 ++- parasitics/ConcreteParasiticsPvt.hh | 77 ++++++----- parasitics/EstimateParasitics.cc | 35 +---- parasitics/EstimateParasitics.hh | 6 +- parasitics/Parasitics.cc | 15 +-- parasitics/ReduceParasitics.cc | 46 +++---- parasitics/ReportParasiticAnnotation.cc | 16 +-- parasitics/SpefParse.yy | 8 ++ parasitics/SpefReader.cc | 40 ++---- parasitics/SpefReader.hh | 4 +- parasitics/SpefReaderPvt.hh | 34 ++--- parasitics/SpefScanner.hh | 2 +- power/Power.cc | 4 +- power/Power.hh | 8 +- power/Power.i | 9 +- power/ReportPower.cc | 6 +- power/ReportPower.hh | 2 +- power/SaifParse.yy | 4 + power/SaifReader.cc | 17 +-- power/SaifReaderPvt.hh | 18 +-- power/SaifScanner.hh | 3 +- power/VcdParse.cc | 13 +- power/VcdParse.hh | 12 +- power/VcdReader.cc | 43 +++--- sdc/Clock.cc | 60 +++------ sdc/ClockGatingCheck.cc | 5 - sdc/CycleAccting.cc | 11 +- sdc/DataCheck.cc | 4 +- sdc/DeratingFactors.cc | 34 ++--- sdc/DisabledPorts.cc | 24 +--- sdc/ExceptionPath.cc | 42 +++--- sdc/FilterObjects.cc | 16 +-- sdc/InputDrive.cc | 20 +-- sdc/PortDelay.cc | 6 +- sdc/PortExtCap.cc | 5 - sdc/Sdc.cc | 10 +- sdc/Sdc.i | 10 +- sdc/SdcCmdComment.cc | 3 +- sdc/Variables.cc | 18 --- sdc/WriteSdc.cc | 74 +++++----- sdc/WriteSdcPvt.hh | 6 +- sdf/ReportAnnotation.cc | 20 +-- sdf/Sdf.i | 5 +- sdf/SdfParse.yy | 16 +++ sdf/SdfReader.cc | 38 +++--- sdf/SdfReaderPvt.hh | 30 ++--- sdf/SdfScanner.hh | 2 +- sdf/SdfWriter.cc | 23 ++-- search/CheckCapacitances.cc | 2 +- search/CheckFanouts.cc | 4 +- search/CheckSlews.cc | 2 +- search/Sta.cc | 2 +- spice/WritePathSpice.cc | 32 +++-- spice/WriteSpice.cc | 29 ++-- spice/WriteSpice.hh | 22 +-- util/Error.cc | 4 - util/FlexDisableRegister.hh | 1 - util/gzstream.hh | 2 +- verilog/Verilog.i | 2 +- verilog/VerilogParse.yy | 25 ++++ verilog/VerilogReader.cc | 35 +++-- verilog/VerilogReaderPvt.hh | 14 +- verilog/VerilogScanner.hh | 3 +- verilog/VerilogWriter.cc | 9 +- 172 files changed, 1363 insertions(+), 1550 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 19aed6568..c095fa1b9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -59,6 +59,7 @@ Checks: > -modernize-use-transparent-functors, misc-*, -misc-const-correctness, + -misc-multiple-inheritance, -misc-no-recursion, -misc-non-private-member-variables-in-classes, -misc-redundant-expression, @@ -72,6 +73,8 @@ Checks: > # Excludes system and third-party paths such as /opt/local/include. HeaderFilterRegex: '.*/(app|cmake|dcalc|graph|liberty|network|parasitics|power|sdc|sdf|search|spice|tcl|util|verilog|include/sta|build/include/sta)/.*' -# Bison-generated parser headers (build/{Liberty,Verilog,...}Parse.hh); gzstream (e.g. util/gzstream.hh). -ExcludeHeaderFilterRegex: '.*/(gzstream\.h(?:h)?|(?:Liberty|Verilog|Sdf|Spef|Saif|LibExpr)Parse\.hh)$' +# util/gzstream.hh +# util/FlexDisableRegister.hh +# Bison-generated parser headers (build/{Liberty,Verilog,...}Parse.hh) +ExcludeHeaderFilterRegex: '(.*/)?(gzstream\.hh|FlexDisableRegister\.hh|(Liberty|Verilog|Sdf|Spef|Saif|LibExpr)Parse\.hh)$' FormatStyle: none diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index 6de6bdb63..184607e6a 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -22,16 +22,16 @@ // // This notice may not be removed or altered from any source distribution. -#include - #include "ArcDcalcWaveforms.hh" -#include "Report.hh" -#include "Liberty.hh" -#include "Network.hh" -#include "Graph.hh" +#include + #include "ArcDelayCalc.hh" +#include "Graph.hh" #include "GraphDelayCalc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Report.hh" namespace sta { diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index 38000cd09..b615ca477 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -27,12 +27,12 @@ #include #include -#include "StringUtil.hh" -#include "Units.hh" +#include "Graph.hh" #include "Liberty.hh" -#include "TimingArc.hh" #include "Network.hh" -#include "Graph.hh" +#include "StringUtil.hh" +#include "TimingArc.hh" +#include "Units.hh" namespace sta { @@ -165,17 +165,7 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin, { } -ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) : - in_pin_(arg.in_pin_), - drvr_pin_(arg.drvr_pin_), - edge_(arg.edge_), - arc_(arg.arc_), - in_slew_(arg.in_slew_), - load_cap_(arg.load_cap_), - parasitic_(arg.parasitic_), - input_delay_(arg.input_delay_) -{ -} +ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) = default; const RiseFall * ArcDcalcArg::inEdge() const diff --git a/dcalc/Arnoldi.hh b/dcalc/Arnoldi.hh index 73fc26b29..ccfbb0027 100644 --- a/dcalc/Arnoldi.hh +++ b/dcalc/Arnoldi.hh @@ -45,7 +45,7 @@ class arnoldi1 public: arnoldi1() { order=0; n=0; d=nullptr; e=nullptr; U=nullptr; ctot=0.0; sqc=0.0; } ~arnoldi1(); - double elmore(int term_index); + double elmore(int k); // // calculate poles/residues for given rdrive @@ -70,12 +70,12 @@ class rcmodel : public ConcreteParasitic, { public: rcmodel(); - virtual ~rcmodel(); - virtual float capacitance() const; - virtual PinSet unannotatedLoads(const Pin *drvr_pin, - const Parasitics *parasitics) const; + ~rcmodel() override; + float capacitance() const override; + PinSet unannotatedLoads(const Pin *drvr_pin, + const Parasitics *parasitics) const override; - const Pin **pinV; // [n] + const Pin **pinV{nullptr}; // [n] }; struct timing_table diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index efacb54a7..502a73eb7 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -28,30 +28,34 @@ #include "ArnoldiDelayCalc.hh" -#include +#include #include // abs +#include +#include +#include -#include "Report.hh" +#include "ArcDelayCalc.hh" +#include "Arnoldi.hh" +#include "ArnoldiReduce.hh" #include "Debug.hh" -#include "Units.hh" +#include "DelayCalc.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "TimingModel.hh" -#include "TimingArc.hh" -#include "TableModel.hh" -#include "PortDirection.hh" +#include "LumpedCapDelayCalc.hh" #include "Network.hh" -#include "Graph.hh" #include "Parasitics.hh" +#include "PortDirection.hh" +#include "Report.hh" #include "Sdc.hh" -#include "DelayCalc.hh" -#include "ArcDelayCalc.hh" -#include "LumpedCapDelayCalc.hh" -#include "GraphDelayCalc.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingModel.hh" +#include "Units.hh" #include "Variables.hh" -#include "Arnoldi.hh" -#include "ArnoldiReduce.hh" namespace sta { +// NOLINTBEGIN(modernize-avoid-c-style-cast) // wireload8 is n^2 // do not delete arnoldi parasitics @@ -76,7 +80,11 @@ delay_work_get_residues(delay_work *D, int term_index); static bool -tridiagEV(int n,double *d,double *e,double *p,double **v); +tridiagEV(int n, + const double *din, + const double *ein, + double *d, + double **v); ////////////////////////////////////////////////////////////// @@ -229,7 +237,7 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc double *c_x1, double *c_y1); - rcmodel *rcmodel_; + rcmodel *rcmodel_{nullptr}; int _pinNmax; double *_delayV; double *_slewV; @@ -246,7 +254,6 @@ makeArnoldiDelayCalc(StaState *sta) ArnoldiDelayCalc::ArnoldiDelayCalc(StaState *sta) : LumpedCapDelayCalc(sta), - rcmodel_(nullptr), reduce_(new ArnoldiReduce(sta)), delay_work_(delay_work_create()) { @@ -362,7 +369,7 @@ ArnoldiDelayCalc::inputPortDelay(const Pin *, for (int j=1;jelmore(j); - double wire_delay = 0.6931472*elmore; + double wire_delay = std::numbers::ln2 * elmore; double load_slew = in_slew + c_log*elmore/slew_derate; _delayV[j] = wire_delay; _slewV[j] = load_slew; @@ -487,7 +494,7 @@ ArnoldiDelayCalc::reportGateDelay(const Pin *drvr_pin, arnoldi1::~arnoldi1() { free(d); - free(U); + free(reinterpret_cast(U)); } double @@ -506,13 +513,16 @@ delay_work_create() int j; delay_work *D = (delay_work*)malloc(sizeof(delay_work)); D->nmax = 256; - D->resi = (double**)malloc(D->nmax*sizeof(double*)); - D->resi[0] = (double*)malloc(D->nmax*32*sizeof(double)); - for (j=1;jnmax;j++) D->resi[j] = D->resi[0] + j*32; - D->v[0] = (double*)malloc(32*32*sizeof(double)); - for (j=1;j<32;j++) D->v[j] = D->v[0] + j*32; - D->w[0] = (double*)malloc(32*D->nmax*sizeof(double)); - for (j=1;j<32;j++) D->w[j] = D->w[0] + j*D->nmax; + D->resi = (double**)malloc(static_cast(D->nmax) * sizeof(double *)); + D->resi[0] = (double*)malloc(static_cast(D->nmax) * 32u * sizeof(double)); + for (j=1;jnmax;j++) + D->resi[j] = D->resi[0] + static_cast(j) * 32; + D->v[0] = (double*)malloc(static_cast(32) * 32u * sizeof(double)); + for (j=1;j<32;j++) + D->v[j] = D->v[0] + static_cast(j) * 32; + D->w[0] = (double*)malloc(32u * static_cast(D->nmax) * sizeof(double)); + for (j=1;j<32;j++) + D->w[j] = D->w[0] + static_cast(j) * D->nmax; D->lo_thresh = 0.0; D->hi_thresh = 0.0; D->slew_derate = 0.0; @@ -535,7 +545,7 @@ static void delay_work_destroy(delay_work *D) { free(D->resi[0]); - free(D->resi); + free(reinterpret_cast(D->resi)); free(D->v[0]); free(D->w[0]); free(D); @@ -547,15 +557,17 @@ delay_work_alloc(delay_work *D,int n) if (n<=D->nmax) return; free(D->w[0]); free(D->resi[0]); - free(D->resi); + free(reinterpret_cast(D->resi)); D->nmax *= 2; - if (n > D->nmax) D->nmax = n; + D->nmax = std::max(n, D->nmax); int j; - D->resi = (double**)malloc(D->nmax*sizeof(double*)); - D->resi[0] = (double*)malloc(D->nmax*32*sizeof(double)); - for (j=1;jnmax;j++) D->resi[j] = D->resi[0] + j*32; - D->w[0] = (double*)malloc(32*D->nmax*sizeof(double)); - for (j=1;j<32;j++) D->w[j] = D->w[0] + j*D->nmax; + D->resi = (double**)malloc(static_cast(D->nmax) * sizeof(double *)); + D->resi[0] = (double*)malloc(static_cast(D->nmax) * 32u * sizeof(double)); + for (j=1;jnmax;j++) + D->resi[j] = D->resi[0] + static_cast(j) * 32; + D->w[0] = (double*)malloc(32u * static_cast(D->nmax) * sizeof(double)); + for (j=1;j<32;j++) + D->w[j] = D->w[0] + static_cast(j) * D->nmax; } void @@ -622,8 +634,7 @@ void arnoldi1::calculate_poles_res(delay_work *D, d[0] = dsave; for (h=0;h= 0.0) + if ((((rts-xh)*df-f)*((rts-x_lo)*df-f) >= 0.0) || (std::abs(2.0*f) > std::abs(dxold*df))) { dxold = dx; - dx = 0.5*(xh-xl); + dx = 0.5*(xh-x_lo); if (flast*f >0.0) { // 2 successive bisections in same direction, // accelerate - if (f<0.0) dx = 0.9348*(xh-xl); - else dx = 0.0625*(xh-xl); + if (f<0.0) dx = 0.9348*(xh-x_lo); + else dx = 0.0625*(xh-x_lo); } flast = f; - rts = xl+dx; - if (xl == rts) { + rts = x_lo+dx; + if (x_lo == rts) { return rts; } } else { @@ -849,13 +881,13 @@ solve_t_bracketed(double s,int order,double *p,double *rr, } get_dv(rts,s,order,p,rr,&f,&df); f -= val; if (f<0.0) - xl = rts; + x_lo = rts; else xh = rts; } if (std::abs(f)<1e-6) // 1uV return rts; - return 0.5*(xl+xh); + return 0.5*(x_lo+xh); } void @@ -949,8 +981,7 @@ ArnoldiDelayCalc::pr_solve3(double s, for (h=1;h0.3 && rr[h]>rr[0]) { h0 = h; break; } } double p0 = p[h0]; - if (p0>10e+9) // 1/10ns - p0=10e+9; + p0 = std::min(p0, 10e+9); // 1/10ns cap double ps,vs,ta,va; vs = 0.0; for (h=0;hvmid) { - tmin5 = tmin8 = ta; vmin5 = tmin8 = va; + tmin5 = ta; + tmin8 = ta; + vmin5 = va; ta += 0.7/p0; pr_get_v(ta,s,order,p,rr,&va); } @@ -1064,18 +1097,14 @@ ArnoldiDelayCalc::pr_solve3(double s, pr_get_v(ta,s,order,p,rr,&va); } tmax2 = ta; vmax2 = va; - if (va < vmid) { - tmax5 = ta; vmax5 = va; - } else while (va > vmid) { + while (va > vmid) { tmin5 = tmin8 = ta; vmin5 = vmin8 = va; ta += 1.0/p0; pr_get_v(ta,s,order,p,rr,&va); } tmax5 = ta; vmax5 = va; - if (va < vlo) { - tmax8 = ta; vmax8 = va; - } else while (va > vlo) { + while (va > vlo) { tmin8 = ta; vmin8 = va; ta += 1.0/p0; @@ -1248,11 +1277,12 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, if (x <= x1) { y = y1 - 0.5*(x-x1); - if (y>1.0) y=1.0; + y = std::min(y, 1.0); } else { y = y1 - (x-x1)*(0.5 + 8*(x-x1)); - if (y0.0 && r<100e+3)) // 100khom @@ -1436,9 +1465,6 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, units_->timeUnit()->asString(tlox-thix)); } ceff = ctot; - 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); // calculate s,r,mod -> t50_srmod, // then t50_srmod+t50_sy-t50_sr @@ -1488,4 +1514,5 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, } } +// NOLINTEND(modernize-avoid-c-style-cast) } // namespace sta diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc index 046e8e8d6..236c01a60 100644 --- a/dcalc/ArnoldiReduce.cc +++ b/dcalc/ArnoldiReduce.cc @@ -28,19 +28,22 @@ #include "ArnoldiReduce.hh" +#include + +#include "Arnoldi.hh" #include "Debug.hh" +#include "Format.hh" #include "MinMax.hh" -#include "Sdc.hh" #include "Network.hh" +#include "Sdc.hh" #include "Units.hh" -#include "Arnoldi.hh" -#include "Format.hh" #include "parasitics/ConcreteParasiticsPvt.hh" namespace sta { +// This is legacy C-style code. +// NOLINTBEGIN(modernize-avoid-c-style-cast, bugprone-multi-level-implicit-pointer-conversion, bugprone-implicit-widening-of-multiplication-result) -rcmodel::rcmodel() : - pinV(nullptr) +rcmodel::rcmodel() { } @@ -87,11 +90,7 @@ const int ArnoldiReduce::ts_point_count_incr_ = 1024; const int ArnoldiReduce::ts_edge_count_incr_ = 1024; ArnoldiReduce::ArnoldiReduce(StaState *sta) : - StaState(sta), - ts_pointNmax(1024), - ts_edgeNmax(1024), - termNmax(256), - dNmax(8) + StaState(sta) { ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point)); ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int)); @@ -351,14 +350,14 @@ ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv) ts_point *p0 = ts_pointV; ts_point *pend = p0 + ts_pointN; for (p = p0; p != pend; p++) - p->visited = 0; + p->visited = false; ts_edge *e; ts_edge **stackV = ts_stackV; int stackN = 1; stackV[0] = e = pdrv->eV[0]; ts_orient(pdrv, e); - pdrv->visited = 1; + pdrv->visited = true; pdrv->in_edge = nullptr; pdrv->ts = 0; ts_ordV[0] = pdrv - p0; @@ -376,7 +375,7 @@ ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv) } else { // try to descend - q->visited = 1; + q->visited = true; q->ts = ts_ordN++; ts_pordV[q->ts] = q; ts_ordV[q->ts] = q - p0; @@ -531,9 +530,7 @@ ArnoldiReduce::makeRcmodelFromTs() u0 = _u0; u1 = _u1; double sum, e1; - order = max_order; - if (n < order) - order = n; + order = std::min(n, max_order); par[0] = -1; r[0] = 0.0; @@ -682,4 +679,5 @@ ArnoldiReduce::makeRcmodelFromW() return mod; } +// NOLINTEND(modernize-avoid-c-style-cast, bugprone-multi-level-implicit-pointer-conversion, bugprone-implicit-widening-of-multiplication-result) } // namespace sta diff --git a/dcalc/ArnoldiReduce.hh b/dcalc/ArnoldiReduce.hh index 0ee70c841..16232d5cc 100644 --- a/dcalc/ArnoldiReduce.hh +++ b/dcalc/ArnoldiReduce.hh @@ -52,7 +52,7 @@ class ArnoldiReduce : public StaState { public: ArnoldiReduce(StaState *sta); - ~ArnoldiReduce(); + ~ArnoldiReduce() override; rcmodel *reduceToArnoldi(Parasitic *parasitic, const Pin *drvr_pin, float coupling_cap_factor, @@ -86,11 +86,11 @@ protected: // rcWork ts_point *ts_pointV; int ts_pointN; - int ts_pointNmax; + int ts_pointNmax{1024}; static const int ts_point_count_incr_; ts_edge *ts_edgeV; int ts_edgeN; - int ts_edgeNmax; + int ts_edgeNmax{1024}; static const int ts_edge_count_incr_; ts_edge **ts_eV; ts_edge **ts_stackV; @@ -98,14 +98,14 @@ protected: ts_point **ts_pordV; int ts_ordN; - int termNmax; + int termNmax{256}; int termN; ts_point *pterm0; const Pin **pinV; // fixed order, offset from pterm0 int *termV; // from drv-ordered to fixed order int *outV; // from drv-ordered to ts_pordV - int dNmax; + int dNmax{8}; double *d; double *e; double *U0; diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index fec6f0326..848ac227d 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -24,19 +24,20 @@ #include "CcsCeffDelayCalc.hh" +#include #include #include "Debug.hh" -#include "Units.hh" +#include "DmpDelayCalc.hh" +#include "FindRoot.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "TimingArc.hh" #include "Network.hh" -#include "Graph.hh" -#include "Scene.hh" #include "Parasitics.hh" -#include "GraphDelayCalc.hh" -#include "DmpDelayCalc.hh" -#include "FindRoot.hh" +#include "Scene.hh" +#include "TimingArc.hh" +#include "Units.hh" namespace sta { @@ -53,10 +54,6 @@ makeCcsCeffDelayCalc(StaState *sta) CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) : LumpedCapDelayCalc(sta), - output_waveforms_(nullptr), - // Includes the Vh:Vdd region. - region_count_(0), - vl_fail_(false), watch_pin_values_(network_), capacitance_unit_(units_->capacitanceUnit()), table_dcalc_(makeDmpCeffElmoreDelayCalc(sta)) @@ -176,8 +173,8 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, } for (size_t i = 0; i < region_count_; i++) { - double v1 = region_volts_[i]; - double v2 = region_volts_[i + 1]; + double seg_v1 = region_volts_[i]; + double seg_v2 = region_volts_[i + 1]; double t1 = region_times_[i]; double t2 = region_times_[i + 1]; @@ -186,11 +183,11 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, // for the charge on c1 from previous segments so it does not // work well. double c1_v1, c1_v2, ignore; - vl(t1, rpi_ * c1_, c1_v1, ignore); - vl(t2, rpi_ * c1_, c1_v2, ignore); - double q1 = v1 * c2_ + c1_v1 * c1_; - double q2 = v2 * c2_ + c1_v2 * c1_; - double ceff = (q2 - q1) / (v2 - v1); + vLoad(t1, rpi_ * c1_, c1_v1, ignore); + vLoad(t2, rpi_ * c1_, c1_v2, ignore); + double q1 = seg_v1 * c2_ + c1_v1 * c1_; + double q2 = seg_v2 * c2_ + c1_v2 * c1_; + double ceff = (q2 - q1) / (seg_v2 - seg_v1); debugPrint(debug_, "ccs_dcalc", 2, "ceff {}", capacitance_unit_->asString(ceff)); @@ -297,7 +294,7 @@ CcsCeffDelayCalc::initRegions(const LibertyLibrary *drvr_library, report_->error(1701, "unsupported ccs region count."); break; } - fill(region_ceff_.begin(), region_ceff_.end(), c2_ + c1_); + std::ranges::fill(region_ceff_, c2_ + c1_); } void @@ -402,11 +399,11 @@ rampElmoreV(double t, // Elmore (one pole) response to 2 segment ramps [0, vth] slew1, [vth, vdd] slew2. void -CcsCeffDelayCalc::vl(double t, - double elmore, - // Return values. - double &vl, - double &dvl_dt) +CcsCeffDelayCalc::vLoad(double t, + double elmore, + // Return values. + double &vl, + double &dvl_dt) { vl = 0.0; dvl_dt = 0.0; @@ -431,11 +428,11 @@ CcsCeffDelayCalc::vl(double t, // for debugging double -CcsCeffDelayCalc::vl(double t, - double elmore) +CcsCeffDelayCalc::vLoad(double t, + double elmore) { double vl1, dvl_dt; - vl(t, elmore, vl1, dvl_dt); + vLoad(t, elmore, vl1, dvl_dt); return vl1; } @@ -447,7 +444,7 @@ CcsCeffDelayCalc::findVlTime(double v, double t_final = region_ramp_times_[region_count_]; auto [time, failed] = findRoot([&](double t, double &y, double &dy) { - vl(t, elmore, y, dy); + vLoad(t, elmore, y, dy); y -= v; }, t_init, t_final + elmore * 3.0, .001, 20); @@ -539,7 +536,7 @@ CcsCeffDelayCalc::loadWaveform(const Pin *load_pin) load_times->push_back(t); double ignore; - vl(t, elmore, v, ignore); + vLoad(t, elmore, v, ignore); double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v; load_volts->push_back(v1); } diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 81fb24506..07a659b90 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -24,8 +24,8 @@ #pragma once -#include "LumpedCapDelayCalc.hh" #include "ArcDcalcWaveforms.hh" +#include "LumpedCapDelayCalc.hh" namespace sta { @@ -39,7 +39,7 @@ class CcsCeffDelayCalc : public LumpedCapDelayCalc, { public: CcsCeffDelayCalc(StaState *sta); - virtual ~CcsCeffDelayCalc(); + ~CcsCeffDelayCalc() override; ArcDelayCalc *copy() override; std::string_view name() const override { return "ccs_ceff"; } bool reduceSupported() const override { return true; } @@ -68,7 +68,7 @@ public: Waveform watchWaveform(const Pin *pin) override; protected: - typedef std::vector Region; + using Region = std::vector; void gateDelaySlew(const LibertyLibrary *drvr_library, // Return values. @@ -106,13 +106,13 @@ protected: const Pin *load_pin, const Scene *scene, const MinMax *min_max); - void vl(double t, - double elmore, - // Return values. - double &vl, - double &dvl_dt); - double vl(double t, - double elmore); + void vLoad(double t, + double elmore, + // Return values. + double &vl, + double &dvl_dt); + double vLoad(double t, + double elmore); void fail(std::string_view reason); const Pin *drvr_pin_; @@ -122,7 +122,7 @@ protected: Parasitics *parasitics_; const Parasitic *parasitic_; - OutputWaveforms *output_waveforms_; + OutputWaveforms *output_waveforms_{nullptr}; double ref_time_; float vdd_; float vth_; @@ -133,7 +133,7 @@ protected: float rpi_; float c1_; - size_t region_count_; + size_t region_count_{0}; size_t region_vl_idx_; size_t region_vth_idx_; size_t region_vh_idx_; @@ -146,7 +146,7 @@ protected: Region region_time_offsets_; Region region_ramp_times_; Region region_ramp_slopes_; - bool vl_fail_; + bool vl_fail_{false}; // Waveform recording. WatchPinValuesMap watch_pin_values_; diff --git a/dcalc/Delay.cc b/dcalc/Delay.cc index bdec1906e..295cd4536 100644 --- a/dcalc/Delay.cc +++ b/dcalc/Delay.cc @@ -26,10 +26,10 @@ #include -#include "StaConfig.hh" #include "Fuzzy.hh" -#include "Units.hh" +#include "StaConfig.hh" #include "StaState.hh" +#include "Units.hh" #include "Variables.hh" namespace sta { diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index 3ce3cbc92..2b8fc8261 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -27,18 +27,18 @@ #include #include -#include "ContainerHelpers.hh" -#include "StringUtil.hh" -#include "UnitDelayCalc.hh" -#include "LumpedCapDelayCalc.hh" -#include "DmpDelayCalc.hh" #include "ArnoldiDelayCalc.hh" #include "CcsCeffDelayCalc.hh" +#include "ContainerHelpers.hh" +#include "DmpDelayCalc.hh" +#include "LumpedCapDelayCalc.hh" #include "PrimaDelayCalc.hh" +#include "StringUtil.hh" +#include "UnitDelayCalc.hh" namespace sta { -typedef std::map> DelayCalcMap; +using DelayCalcMap = std::map>; static DelayCalcMap delay_calcs; diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index 992ecadea..9aaa3f3fb 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -28,11 +28,11 @@ %{ -#include "DelayCalc.hh" #include "ArcDelayCalc.hh" +#include "DelayCalc.hh" +#include "Sta.hh" #include "dcalc/ArcDcalcWaveforms.hh" #include "dcalc/PrimaDelayCalc.hh" -#include "Sta.hh" %} diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index c41eb94f8..214aec7d3 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -24,16 +24,16 @@ #include "DelayCalcBase.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "TimingArc.hh" -#include "TimingModel.hh" -#include "TableModel.hh" #include "Network.hh" #include "Parasitics.hh" -#include "Graph.hh" -#include "Sdc.hh" #include "Scene.hh" -#include "GraphDelayCalc.hh" +#include "Sdc.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingModel.hh" #include "Variables.hh" namespace sta { diff --git a/dcalc/DelayCalcBase.hh b/dcalc/DelayCalcBase.hh index 2cfdc6504..a2d4666be 100644 --- a/dcalc/DelayCalcBase.hh +++ b/dcalc/DelayCalcBase.hh @@ -72,7 +72,7 @@ protected: void thresholdAdjust(const Pin *load_pin, const LibertyLibrary *drvr_library, const RiseFall *rf, - double &load_delay, + double &wire_delay, double &load_slew); // Helper function for input ports driving dspf parasitic. void dspfWireDelaySlew(const Pin *load_pin, diff --git a/dcalc/DelayNormal.cc b/dcalc/DelayNormal.cc index 2120bded0..c73de0696 100644 --- a/dcalc/DelayNormal.cc +++ b/dcalc/DelayNormal.cc @@ -27,10 +27,10 @@ #include // sqrt #include "Error.hh" -#include "Fuzzy.hh" -#include "Units.hh" #include "Format.hh" +#include "Fuzzy.hh" #include "StaState.hh" +#include "Units.hh" #include "Variables.hh" namespace sta { diff --git a/dcalc/DelayScalar.cc b/dcalc/DelayScalar.cc index 73f217b4e..0ec8543ee 100644 --- a/dcalc/DelayScalar.cc +++ b/dcalc/DelayScalar.cc @@ -27,8 +27,8 @@ #include "DelayScalar.hh" #include "Fuzzy.hh" -#include "Units.hh" #include "StaState.hh" +#include "Units.hh" namespace sta { diff --git a/dcalc/DelaySkewNormal.cc b/dcalc/DelaySkewNormal.cc index b8c9c8e53..7e492a036 100644 --- a/dcalc/DelaySkewNormal.cc +++ b/dcalc/DelaySkewNormal.cc @@ -27,10 +27,10 @@ #include // sqrt #include "Error.hh" -#include "Fuzzy.hh" -#include "Units.hh" #include "Format.hh" +#include "Fuzzy.hh" #include "StaState.hh" +#include "Units.hh" #include "Variables.hh" namespace sta { diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 108bf7989..7ce3e9031 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -40,17 +40,17 @@ #include #include -#include "Format.hh" -#include "Report.hh" +#include "ArcDelayCalc.hh" #include "Debug.hh" -#include "Units.hh" -#include "TimingArc.hh" -#include "TableModel.hh" +#include "FindRoot.hh" +#include "Format.hh" #include "Liberty.hh" -#include "Sdc.hh" #include "Parasitics.hh" -#include "ArcDelayCalc.hh" -#include "FindRoot.hh" +#include "Report.hh" +#include "Sdc.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "Units.hh" #include "Variables.hh" namespace sta { @@ -72,7 +72,7 @@ class DmpError : public Exception { public: DmpError(std::string_view what); - virtual const char *what() const noexcept { return what_.c_str(); } + const char *what() const noexcept override { return what_.c_str(); } private: std::string what_; @@ -127,7 +127,7 @@ class DmpAlg : public StaState void newtonRaphson(); // Find driver parameters t0, delta_t, Ceff. void findDriverParams(double ceff); - std::pair gateCapDelaySlew(double cl); + std::pair gateCapDelaySlew(double ceff); std::tuple gateDelays(double ceff); // Partial derivatives of y(t) jacobian (dydt0, dyddt, dydcl). std::tuple dy(double t, @@ -143,12 +143,12 @@ class DmpAlg : public StaState void showJacobian(); std::pair findDriverDelaySlew(); double findVoCrossing(double vth, - double lower_bound, - double upper_bound); + double t_lower, + double t_upper); void showVo(); double findVlCrossing(double vth, - double lower_bound, - double upper_bound); + double t_lower, + double t_upper); void showVl(); void fail(std::string_view reason); @@ -177,9 +177,9 @@ class DmpAlg : public StaState const Pvt *pvt_; const GateTableModel *gate_model_; double in_slew_; - double c2_; - double rpi_; - double c1_; + double c2_{0.0}; + double rpi_{0.0}; + double c1_{0.0}; double rd_; // Logic threshold (percentage of supply voltage). @@ -232,9 +232,6 @@ class DmpAlg : public StaState DmpAlg::DmpAlg(int nr_order, StaState *sta) : StaState(sta), - c2_(0.0), - rpi_(0.0), - c1_(0.0), nr_order_(nr_order) { } @@ -465,8 +462,13 @@ DmpAlg::showVo() { report_->report(" t vo(t)"); double ub = voCrossingUpperBound(); - for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) + const double step = dt_ / 10.0; + for (int i = 0;; ++i) { + double t = t0_ + step * i; + if (!(t < t0_ + ub)) + break; report_->report(" {:g} {:g}", t, Vo(t).first); + } } std::pair @@ -567,8 +569,14 @@ DmpAlg::showVl() { report_->report(" t vl(t)"); double ub = vlCrossingUpperBound(); - for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) + const double step = ub / 10.0; + const double t_end = t0_ + ub * 2.0; + for (int i = 0;; ++i) { + double t = t0_ + step * i; + if (!(t < t_end)) + break; report_->report(" {:g} {:g}", t, Vl(t).first); + } } void @@ -605,9 +613,9 @@ class DmpCap : public DmpAlg std::pair loadDelaySlew(const Pin *, double elmore) override; void evalDmpEqns() override; - double voCrossingUpperBound() override; -private: +protected: + double voCrossingUpperBound() override; std::pair V0(double t) override; std::pair Vl0(double t) override; }; @@ -702,7 +710,11 @@ class DmpPi : public DmpAlg double c1) override; std::pair gateDelaySlew() override; void evalDmpEqns() override; + +protected: double voCrossingUpperBound() override; + std::pair V0(double t) override; + std::pair Vl0(double t) override; private: void findDriverParamsPi(); @@ -710,39 +722,26 @@ class DmpPi : public DmpAlg double dt, double ceff_time, double ceff); - std::pair V0(double t) override; - std::pair Vl0(double t) override; // Poles/zero. - double p1_; - double p2_; - double z1_; + double p1_{0.0}; + double p2_{0.0}; + double z1_{0.0}; // Residues. - double k0_; - double k1_; - double k2_; - double k3_; - double k4_; + double k0_{0.0}; + double k1_{0.0}; + double k2_{0.0}; + double k3_{0.0}; + double k4_{0.0}; // Ipi coefficients. - double A_; - double B_; - double D_; + double A_{0.0}; + double B_{0.0}; + double D_{0.0}; }; DmpPi::DmpPi(StaState *sta) : DmpAlg(3, - sta), - p1_(0.0), - p2_(0.0), - z1_(0.0), - k0_(0.0), - k1_(0.0), - k2_(0.0), - k3_(0.0), - k4_(0.0), - A_(0.0), - B_(0.0), - D_(0.0) + sta) { } @@ -846,8 +845,7 @@ DmpPi::evalDmpEqns() throw DmpError("eqn eval failed: slew = 0"); double ceff_time = slew / (vh_ - vl_); - if (ceff_time > 1.4 * dt) - ceff_time = 1.4 * dt; + ceff_time = std::min(ceff_time, 1.4 * dt); if (dt <= 0.0) throw DmpError("eqn eval failed: dt < 0"); @@ -949,6 +947,8 @@ class DmpOnePole : public DmpAlg public: DmpOnePole(StaState *sta); void evalDmpEqns() override; + +protected: double voCrossingUpperBound() override; }; @@ -1018,29 +1018,24 @@ class DmpZeroC2 : public DmpOnePole double c1) override; std::pair gateDelaySlew() override; -private: +protected: std::pair V0(double t) override; std::pair Vl0(double t) override; double voCrossingUpperBound() override; +private: // Pole/zero. - double p1_; - double z1_; + double p1_{0.0}; + double z1_{0.0}; // Residues. - double k0_; - double k1_; - double k2_; - double k3_; + double k0_{0.0}; + double k1_{0.0}; + double k2_{0.0}; + double k3_{0.0}; }; DmpZeroC2::DmpZeroC2(StaState *sta) : - DmpOnePole(sta), - p1_(0.0), - z1_(0.0), - k0_(0.0), - k1_(0.0), - k2_(0.0), - k3_(0.0) + DmpOnePole(sta) { } @@ -1172,8 +1167,7 @@ DmpAlg::luDecomp() double big = 0.0; for (int j = 0; j < size; j++) { double temp = std::abs(fjac_[i][j]); - if (temp > big) - big = temp; + big = std::max(temp, big); } if (big == 0.0) throw DmpError("LU decomposition: no non-zero row element"); @@ -1268,8 +1262,7 @@ DmpCeffDelayCalc::DmpCeffDelayCalc(StaState *sta) : LumpedCapDelayCalc(sta), dmp_cap_(new DmpCap(sta)), dmp_pi_(new DmpPi(sta)), - dmp_zero_c2_(new DmpZeroC2(sta)), - dmp_alg_(nullptr) + dmp_zero_c2_(new DmpZeroC2(sta)) { } diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index 15f446404..1ca6209e9 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -44,7 +44,7 @@ class DmpCeffDelayCalc : public LumpedCapDelayCalc { public: DmpCeffDelayCalc(StaState *sta); - virtual ~DmpCeffDelayCalc(); + ~DmpCeffDelayCalc() override; bool reduceSupported() const override { return true; } ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, @@ -98,7 +98,7 @@ private: DmpCap *dmp_cap_; DmpPi *dmp_pi_; DmpZeroC2 *dmp_zero_c2_; - DmpAlg *dmp_alg_; + DmpAlg *dmp_alg_{nullptr}; }; } // namespace sta diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index c9c63f5a1..f94d20863 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -24,15 +24,15 @@ #include "DmpDelayCalc.hh" -#include "TableModel.hh" -#include "TimingArc.hh" +#include "DmpCeff.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Sdc.hh" #include "Parasitics.hh" -#include "GraphDelayCalc.hh" -#include "DmpCeff.hh" +#include "PortDirection.hh" +#include "Sdc.hh" +#include "TableModel.hh" +#include "TimingArc.hh" namespace sta { @@ -164,7 +164,7 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc const Scene *scene, const MinMax *min_max) override; -private: +protected: void loadDelaySlew(const Pin *load_pin, double drvr_slew, const RiseFall *rf, @@ -173,6 +173,8 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc // Return values. double &wire_delay, double &load_slew) override; + +private: void loadDelay(double drvr_slew, Parasitic *pole_residue, double p1, @@ -190,11 +192,11 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc double tt, double y_tt); - bool parasitic_is_pole_residue_; - float vth_; - float vl_; - float vh_; - float slew_derate_; + bool parasitic_is_pole_residue_{false}; + float vth_{0.0F}; + float vl_{0.0F}; + float vh_{0.0F}; + float slew_derate_{0.0F}; }; ArcDelayCalc * @@ -204,12 +206,7 @@ makeDmpCeffTwoPoleDelayCalc(StaState *sta) } DmpCeffTwoPoleDelayCalc::DmpCeffTwoPoleDelayCalc(StaState *sta) : - DmpCeffDelayCalc(sta), - parasitic_is_pole_residue_(false), - vth_(0.0), - vl_(0.0), - vh_(0.0), - slew_derate_(0.0) + DmpCeffDelayCalc(sta) { } @@ -334,7 +331,7 @@ DmpCeffTwoPoleDelayCalc::loadDelaySlew(const Pin *load_pin, // Should handle PiElmore parasitic. wire_delay = 0.0; load_slew = drvr_slew; - Parasitic *pole_residue = 0; + Parasitic *pole_residue = nullptr; if (parasitic_is_pole_residue_) pole_residue = parasitics_->findPoleResidue(parasitic, load_pin); if (pole_residue) { diff --git a/dcalc/FindRoot.cc b/dcalc/FindRoot.cc index 97e1c62db..58e8dde8e 100644 --- a/dcalc/FindRoot.cc +++ b/dcalc/FindRoot.cc @@ -29,7 +29,7 @@ namespace sta { std::pair -findRoot(FindRootFunc func, +findRoot(const FindRootFunc &func, double x1, double x2, double x_tol, @@ -42,7 +42,7 @@ findRoot(FindRootFunc func, } std::pair -findRoot(FindRootFunc func, +findRoot(const FindRootFunc &func, double x1, double y1, double x2, diff --git a/dcalc/FindRoot.hh b/dcalc/FindRoot.hh index d0c1921ff..61daac86e 100644 --- a/dcalc/FindRoot.hh +++ b/dcalc/FindRoot.hh @@ -36,7 +36,7 @@ using FindRootFunc = const std::function -findRoot(FindRootFunc func, +findRoot(const FindRootFunc &func, double x1, double x2, double x_tol, @@ -44,7 +44,7 @@ findRoot(FindRootFunc func, // first: root estimate; second: true if the search failed. std::pair -findRoot(FindRootFunc func, +findRoot(const FindRootFunc &func, double x1, double y1, double x2, diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index eddd32353..741353bf9 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -24,35 +24,35 @@ #include "GraphDelayCalc.hh" -#include #include +#include #include #include +#include "ArcDelayCalc.hh" +#include "Bfs.hh" +#include "ClkNetwork.hh" #include "ContainerHelpers.hh" #include "Debug.hh" -#include "Stats.hh" +#include "Graph.hh" +#include "InputDrive.hh" +#include "Liberty.hh" #include "MinMax.hh" +#include "Mode.hh" #include "Mutex.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "PortDirection.hh" +#include "NetCaps.hh" #include "Network.hh" -#include "InputDrive.hh" -#include "Sdc.hh" -#include "Mode.hh" -#include "Graph.hh" #include "Parasitics.hh" -#include "search/Levelize.hh" +#include "PortDirection.hh" +#include "Sdc.hh" #include "Scene.hh" #include "SearchPred.hh" -#include "Bfs.hh" -#include "ArcDelayCalc.hh" -#include "NetCaps.hh" -#include "ClkNetwork.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" #include "Variables.hh" #include "search/Latches.hh" +#include "search/Levelize.hh" namespace sta { @@ -143,15 +143,10 @@ DcalcNonLatchPred::searchThru(Edge *edge, GraphDelayCalc::GraphDelayCalc(StaState *sta) : StaState(sta), - observer_(nullptr), - delays_seeded_(false), - incremental_(false), - delays_exist_(false), invalid_delays_(makeVertexSet(this)), search_pred_(new DcalcPred(sta)), search_non_latch_pred_(new DcalcNonLatchPred(sta)), - iter_(new BfsFwdIterator(BfsIndex::dcalc, search_non_latch_pred_, sta)), - incremental_delay_tolerance_(0.0) + iter_(new BfsFwdIterator(BfsIndex::dcalc, search_non_latch_pred_, sta)) { } @@ -297,9 +292,9 @@ class FindVertexDelays : public VertexVisitor { public: FindVertexDelays(GraphDelayCalc *graph_delay_calc1); - virtual ~FindVertexDelays(); - virtual void visit(Vertex *vertex); - virtual VertexVisitor *copy() const; + ~FindVertexDelays() override; + void visit(Vertex *vertex) override; + VertexVisitor *copy() const override; protected: GraphDelayCalc *graph_delay_calc_; @@ -419,7 +414,7 @@ GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex, if (drive) { const LibertyCell *drvr_cell; const LibertyPort *from_port, *to_port; - float *from_slews; + const DriveCellSlews *from_slews; drive->driveCell(rf, min_max, drvr_cell, from_port, from_slews, to_port); if (drvr_cell) { @@ -562,7 +557,7 @@ LibertyPort * GraphDelayCalc::driveCellDefaultFromPort(const LibertyCell *cell, const LibertyPort *to_port) { - LibertyPort *from_port = 0; + LibertyPort *from_port = nullptr; int from_port_index = 0; for (TimingArcSet *arc_set : cell->timingArcSetsTo(to_port)) { LibertyPort *set_from_port = arc_set->from(); @@ -599,7 +594,7 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, Vertex *drvr_vertex, const RiseFall *rf, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews *from_slews, const LibertyPort *to_port, const Scene *scene, const MinMax *min_max, @@ -611,7 +606,7 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, for (TimingArcSet *arc_set : drvr_cell->timingArcSets(from_port, to_port)) { for (TimingArc *arc : arc_set->arcs()) { if (arc->toEdge()->asRiseFall() == rf) { - float from_slew = from_slews[arc->fromEdge()->index()]; + float from_slew = (*from_slews)[arc->fromEdge()->index()]; findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, scene, min_max, arc_delay_calc); } @@ -1270,19 +1265,19 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, bool load_changed = false; 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); - graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew); + // Copy the driver slew to the load if it is annotated. + 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); - if (!merge + } + else { + 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); + graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); load_changed = true; } - } + } } if (!graph_->wireDelayAnnotated(wire_edge, drvr_rf, ap_index)) { // Multiple timing arcs with the same output transition @@ -1594,9 +1589,9 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, const RiseFall *to_rf = arc->toEdge()->asRiseFall(); if (from_rf && to_rf) { const LibertyPort *related_out_port = arc_set->relatedOut(); - const Pin *related_out_pin = 0; + const Pin *related_out_pin = nullptr; if (related_out_port) - related_out_pin = network_->findPin(inst, related_out_port); + related_out_pin = network_->findPin(inst, related_out_port); for (Scene *scene : scenes_) { for (const MinMax *min_max : MinMax::range()) { @@ -1677,7 +1672,7 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, const RiseFall *to_rf = arc->toEdge()->asRiseFall(); if (from_rf && to_rf) { const LibertyPort *related_out_port = arc_set->relatedOut(); - const Pin *related_out_pin = 0; + const Pin *related_out_pin = nullptr; if (related_out_port) related_out_pin = network_->findPin(inst, related_out_port); float related_out_cap = 0.0; @@ -1767,11 +1762,6 @@ GraphDelayCalc::minPeriod(const Pin *pin, //////////////////////////////////////////////////////////////// -MultiDrvrNet::MultiDrvrNet() : - dcalc_drvr_(nullptr) -{ -} - void MultiDrvrNet::netCaps(const RiseFall *drvr_rf, const Scene *scene, diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 936fc77a5..b245c1d66 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -27,15 +27,15 @@ #include // isnan #include "Debug.hh" -#include "Units.hh" -#include "TimingArc.hh" -#include "TimingModel.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Sdc.hh" #include "Parasitics.hh" -#include "GraphDelayCalc.hh" +#include "PortDirection.hh" +#include "Sdc.hh" +#include "TimingArc.hh" +#include "TimingModel.hh" +#include "Units.hh" #include "Variables.hh" namespace sta { diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index 7c7643da8..d9b24d74b 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -61,7 +61,7 @@ public: const LoadPinIndexMap &load_pin_index_map, const Scene *scene, const MinMax *min_max) override; - std::string reportGateDelay(const Pin *drvr_pin, + std::string reportGateDelay(const Pin *check_pin, const TimingArc *arc, const Slew &in_slew, float load_cap, diff --git a/dcalc/NetCaps.cc b/dcalc/NetCaps.cc index 80a7c8018..d1eaa45b4 100644 --- a/dcalc/NetCaps.cc +++ b/dcalc/NetCaps.cc @@ -26,10 +26,6 @@ namespace sta { -NetCaps::NetCaps() -{ -} - NetCaps::NetCaps(float pin_cap, float wire_cap, float fanout, diff --git a/dcalc/NetCaps.hh b/dcalc/NetCaps.hh index 7c09cc66a..a968ddda1 100644 --- a/dcalc/NetCaps.hh +++ b/dcalc/NetCaps.hh @@ -30,7 +30,7 @@ namespace sta { class NetCaps { public: - NetCaps(); + NetCaps() = default; NetCaps(float pin_cap, float wire_cap, float fanout, diff --git a/dcalc/ParallelDelayCalc.cc b/dcalc/ParallelDelayCalc.cc index 1cb7c94e7..8a47956ce 100644 --- a/dcalc/ParallelDelayCalc.cc +++ b/dcalc/ParallelDelayCalc.cc @@ -24,13 +24,13 @@ #include "ParallelDelayCalc.hh" -#include "TimingArc.hh" -#include "Scene.hh" -#include "Network.hh" #include "Graph.hh" -#include "Sdc.hh" -#include "Liberty.hh" #include "GraphDelayCalc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "TimingArc.hh" namespace sta { diff --git a/dcalc/ParallelDelayCalc.hh b/dcalc/ParallelDelayCalc.hh index 46d3f826b..f20f4b741 100644 --- a/dcalc/ParallelDelayCalc.hh +++ b/dcalc/ParallelDelayCalc.hh @@ -24,8 +24,8 @@ #pragma once -#include #include +#include #include "DelayCalcBase.hh" diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index 93388a10c..a38da6ac8 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -24,32 +24,27 @@ #include "PrimaDelayCalc.hh" +#include +#include #include // abs #include #include "Debug.hh" -#include "Units.hh" -#include "TimingArc.hh" +#include "DmpDelayCalc.hh" +#include "Format.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Sdc.hh" -#include "Scene.hh" -#include "Graph.hh" #include "Parasitics.hh" -#include "GraphDelayCalc.hh" -#include "DmpDelayCalc.hh" -#include "Format.hh" - -#include -#include +#include "PortDirection.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "TimingArc.hh" +#include "Units.hh" namespace sta { -using Eigen::ColPivHouseholderQR; -using Eigen::HouseholderQR; -using Eigen::SparseLU; - // Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998 // McGraw-Hill, Inc. New York, NY. @@ -61,17 +56,7 @@ makePrimaDelayCalc(StaState *sta) PrimaDelayCalc::PrimaDelayCalc(StaState *sta) : DelayCalcBase(sta), - dcalc_args_(nullptr), - scene_(nullptr), - min_max_(nullptr), - parasitics_(nullptr), - parasitic_network_(nullptr), - load_pin_index_map_(nullptr), pin_node_map_(network_), - prima_order_(3), - make_waveforms_(false), - waveform_drvr_pin_(nullptr), - waveform_load_pin_(nullptr), watch_pin_values_(network_), table_dcalc_(makeDmpCeffElmoreDelayCalc(sta)) { @@ -79,20 +64,18 @@ PrimaDelayCalc::PrimaDelayCalc(StaState *sta) : PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) : DelayCalcBase(dcalc), - dcalc_args_(nullptr), - load_pin_index_map_(nullptr), pin_node_map_(network_), node_index_map_(dcalc.node_index_map_), prima_order_(dcalc.prima_order_), - make_waveforms_(false), - waveform_drvr_pin_(nullptr), - waveform_load_pin_(nullptr), watch_pin_values_(network_), table_dcalc_(makeDmpCeffElmoreDelayCalc(this)) { } -PrimaDelayCalc::~PrimaDelayCalc() { delete table_dcalc_; } +PrimaDelayCalc::~PrimaDelayCalc() +{ + delete table_dcalc_; +} ArcDelayCalc * PrimaDelayCalc::copy() @@ -359,7 +342,7 @@ PrimaDelayCalc::simulate1(const MatrixSd &G, const Eigen::MatrixXd &B, const Eigen::VectorXd &x_init, const Eigen::MatrixXd &x_to_v, - const size_t order) + size_t order) { Eigen::VectorXd x(order); Eigen::VectorXd x_prev(order); @@ -379,7 +362,7 @@ PrimaDelayCalc::simulate1(const MatrixSd &G, MatrixSd A(order, order); A = G + (2.0 / time_step_) * C; A.makeCompressed(); - SparseLU A_solver; + Eigen::SparseLU A_solver; A_solver.compute(A); // Initial time depends on ceff which impact delay, so use a sim step @@ -403,7 +386,10 @@ PrimaDelayCalc::simulate1(const MatrixSd &G, if (make_waveforms_) recordWaveformStep(time_begin); - for (double time = time_begin; time <= time_end; time += time_step_) { + for (size_t step = 0;; ++step) { + const double time = time_begin + step * time_step_; + if (time > time_end) + break; setPortCurrents(); rhs = B * u_ + (1.0 / time_step_) * C * (3.0 * x_prev - x_prev2); x = A_solver.solve(rhs); @@ -850,7 +836,7 @@ PrimaDelayCalc::primaReduce() { G_.makeCompressed(); // Step 3: solve G*R = B for R - SparseLU G_solver(G_); + Eigen::SparseLU G_solver(G_); if (G_solver.info() != Eigen::Success) report_->error(1752, "G matrix is singular."); Eigen::MatrixXd R(order_, port_count_); @@ -1038,7 +1024,7 @@ PinSeq PrimaDelayCalc::watchPins() const { PinSeq pins; - for (auto pin_values : watch_pin_values_) { + for (const auto &pin_values : watch_pin_values_) { const Pin *pin = pin_values.first; pins.push_back(pin); } @@ -1117,10 +1103,8 @@ void PrimaDelayCalc::reportMatrix(Eigen::VectorXd &matrix) { std::string line = "| "; - for (Eigen::Index i = 0; i < matrix.rows(); i++) { - std::string entry = + for (Eigen::Index i = 0; i < matrix.rows(); i++) line += sta::format("{:10.3e}", matrix.coeff(i)) + " "; - } line += "|"; report_->reportLine(line); } @@ -1129,8 +1113,8 @@ void PrimaDelayCalc::reportVector(std::vector &matrix) { std::string line = "| "; - for (size_t i = 0; i < matrix.size(); i++) - line += sta::format("{:10.3e}", matrix[i]) + " "; + for (const double &entry : matrix) + line += sta::format("{:10.3e}", entry) + " "; line += "|"; report_->reportLine(line); } diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index a925efdff..4d5b13469 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -24,13 +24,14 @@ #pragma once -#include -#include #include #include +#include +#include +#include -#include "LumpedCapDelayCalc.hh" #include "ArcDcalcWaveforms.hh" +#include "LumpedCapDelayCalc.hh" #include "Parasitics.hh" namespace sta { @@ -57,7 +58,7 @@ class PrimaDelayCalc : public DelayCalcBase, public: PrimaDelayCalc(StaState *sta); PrimaDelayCalc(const PrimaDelayCalc &dcalc); - ~PrimaDelayCalc(); + ~PrimaDelayCalc() override; ArcDelayCalc *copy() override; void copyState(const StaState *sta) override; std::string_view name() const override { return "prima"; } @@ -123,7 +124,7 @@ protected: const Eigen::MatrixXd &B, const Eigen::VectorXd &x_init, const Eigen::MatrixXd &x_to_v, - const size_t order); + size_t order); double maxTime(); double timeStep(); float driverResistance(); @@ -178,15 +179,15 @@ protected: void reportMatrix(Eigen::VectorXd &matrix); void reportVector(std::vector &matrix); - ArcDcalcArgSeq *dcalc_args_; + ArcDcalcArgSeq *dcalc_args_{nullptr}; size_t drvr_count_; float load_cap_; - const Scene *scene_; - const MinMax *min_max_; - Parasitics *parasitics_; - const Parasitic *parasitic_network_; + const Scene *scene_{nullptr}; + const MinMax *min_max_{nullptr}; + Parasitics *parasitics_{nullptr}; + const Parasitic *parasitic_network_{nullptr}; const RiseFall *drvr_rf_; - const LoadPinIndexMap *load_pin_index_map_; + const LoadPinIndexMap *load_pin_index_map_{nullptr}; PinNodeMap pin_node_map_; // Parasitic pin -> array index NodeIndexMap node_index_map_; // Parasitic node -> array index @@ -210,7 +211,7 @@ protected: Eigen::VectorXd u_; // Prima reduced MNA eqns - size_t prima_order_; + size_t prima_order_{3}; Eigen::MatrixXd Vq_; MatrixSd Gq_; MatrixSd Cq_; @@ -231,9 +232,9 @@ protected: double time_step_prev_; // Waveform recording. - bool make_waveforms_; - const Pin *waveform_drvr_pin_; - const Pin *waveform_load_pin_; + bool make_waveforms_{false}; + const Pin *waveform_drvr_pin_{nullptr}; + const Pin *waveform_load_pin_{nullptr}; FloatSeq drvr_voltages_; FloatSeq load_voltages_; WatchPinValuesMap watch_pin_values_; @@ -248,7 +249,7 @@ protected: static constexpr size_t threshold_vth = 1; static constexpr size_t threshold_vh = 2; static constexpr size_t measure_threshold_count_ = 3; - typedef std::array ThresholdTimes; + using ThresholdTimes = std::array; // Vl Vth Vh ThresholdTimes measure_thresholds_; // Indexed by node number. diff --git a/graph/Graph.cc b/graph/Graph.cc index fa92605ce..74996842e 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -26,16 +26,16 @@ #include "ContainerHelpers.hh" #include "Debug.hh" -#include "Stats.hh" +#include "FuncExpr.hh" +#include "Liberty.hh" #include "MinMax.hh" #include "Mutex.hh" -#include "Transition.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "FuncExpr.hh" +#include "PortDirection.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Transition.hh" #include "Variables.hh" namespace sta { @@ -49,8 +49,6 @@ namespace sta { Graph::Graph(StaState *sta, DcalcAPIndex ap_count) : StaState(sta), - vertices_(nullptr), - edges_(nullptr), ap_count_(ap_count), period_check_annotations_(network_), reg_clk_vertices_(makeVertexSet(this)) @@ -342,11 +340,10 @@ class MakeEdgesThruHierPin : public HierPinThruVisitor { public: MakeEdgesThruHierPin(Graph *graph); - -private: void visit(const Pin *drvr, const Pin *load) override; +private: Graph *graph_; }; @@ -585,7 +582,7 @@ Graph::slew(const Vertex *vertex, 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); + const Slew *slews = reinterpret_cast(slews_flt); return slews[slew_index]; } else @@ -598,7 +595,7 @@ Graph::slew(const Vertex *vertex, { const float *slews_flt = vertex->slewsFloat(); if (variables_->pocvEnabled()) { - const Slew *slews = std::bit_cast(slews_flt); + const Slew *slews = reinterpret_cast(slews_flt); return slews[index]; } else @@ -678,7 +675,7 @@ Graph::arcDelay(const Edge *edge, { size_t index = arc->index() * ap_count_ + ap_index; if (variables_->pocvEnabled()) { - ArcDelay *delays = std::bit_cast(edge->arcDelays()); + const ArcDelay *delays = reinterpret_cast(edge->arcDelays()); return delays[index]; } else { @@ -695,7 +692,7 @@ Graph::setArcDelay(Edge *edge, { size_t index = arc->index() * ap_count_ + ap_index; if (variables_->pocvEnabled()) { - ArcDelay *delays = std::bit_cast(edge->arcDelays()); + ArcDelay *delays = reinterpret_cast(edge->arcDelays()); delays[index] = delay; } else { @@ -711,7 +708,7 @@ Graph::wireArcDelay(const Edge *edge, { size_t index = rf->index() * ap_count_ + ap_index; if (variables_->pocvEnabled()) { - ArcDelay *delays = std::bit_cast(edge->arcDelays()); + const ArcDelay *delays = reinterpret_cast(edge->arcDelays()); return delays[index]; } else { @@ -728,7 +725,7 @@ Graph::setWireArcDelay(Edge *edge, { size_t index = rf->index() * ap_count_ + ap_index; if (variables_->pocvEnabled()) { - ArcDelay *delays = std::bit_cast(edge->arcDelays()); + ArcDelay *delays = reinterpret_cast(edge->arcDelays()); delays[index] = delay; } else { @@ -753,7 +750,7 @@ Graph::setArcDelayAnnotated(Edge *edge, DcalcAPIndex ap_index, bool annotated) { - return edge->setArcDelayAnnotated(arc, ap_index, ap_count_, annotated); + edge->setArcDelayAnnotated(arc, ap_index, ap_count_, annotated); } bool @@ -774,7 +771,7 @@ Graph::setWireDelayAnnotated(Edge *edge, { int arc_index = TimingArcSet::wireArcIndex(rf); TimingArc *arc = TimingArcSet::wireTimingArcSet()->findTimingArc(arc_index); - return edge->setArcDelayAnnotated(arc, ap_index, ap_count_, annotated); + edge->setArcDelayAnnotated(arc, ap_index, ap_count_, annotated); } void @@ -819,7 +816,7 @@ Graph::initSlews(Vertex *vertex) { size_t slew_count = slewCount(); if (variables_->pocvEnabled()) { - float *slews = std::bit_cast(new Slew[slew_count]{}); + float *slews = reinterpret_cast(new Slew[slew_count]{}); vertex->setSlews(slews); } else { @@ -840,7 +837,7 @@ Graph::initArcDelays(Edge *edge) size_t arc_count = edge->timingArcSet()->arcCount(); size_t delay_count = arc_count * ap_count_; if (variables_->pocvEnabled()) { - float *delays = std::bit_cast(new ArcDelay[delay_count]{}); + float *delays = reinterpret_cast(new ArcDelay[delay_count]{}); edge->setArcDelays(delays); } else { @@ -1187,7 +1184,7 @@ Vertex::setHasDownstreamClkPin(bool has_clk_pin) bool Vertex::bfsInQueue(BfsIndex index) const { - return (bfs_in_queue_ >> unsigned(index)) & 1; + return (bfs_in_queue_ >> static_cast(index)) & 1; } void @@ -1195,9 +1192,9 @@ Vertex::setBfsInQueue(BfsIndex index, bool value) { if (value) - bfs_in_queue_ |= 1 << int(index); + bfs_in_queue_ |= 1 << static_cast(index); else - bfs_in_queue_ &= ~(1 << int(index)); + bfs_in_queue_ &= ~(1 << static_cast(index)); } //////////////////////////////////////////////////////////////// @@ -1400,10 +1397,7 @@ VertexIterator::VertexIterator(Graph *graph) : graph_(graph), network_(graph->network()), top_inst_(network_->topInstance()), - inst_iter_(network_->leafInstanceIterator()), - pin_iter_(nullptr), - vertex_(nullptr), - bidir_vertex_(nullptr) + inst_iter_(network_->leafInstanceIterator()) { if (inst_iter_) findNext(); diff --git a/graph/Graph.i b/graph/Graph.i index 371e929c4..7b31ebc0e 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -25,16 +25,16 @@ %module graph %{ -#include "Graph.hh" +#include "Clock.hh" #include "FuncExpr.hh" -#include "TimingRole.hh" +#include "Graph.hh" #include "Liberty.hh" #include "Network.hh" -#include "Clock.hh" #include "Scene.hh" #include "Search.hh" #include "Sdc.hh" #include "Sta.hh" +#include "TimingRole.hh" using namespace sta; diff --git a/graph/GraphCmp.cc b/graph/GraphCmp.cc index 3c3a5dfc9..8ad693dfd 100644 --- a/graph/GraphCmp.cc +++ b/graph/GraphCmp.cc @@ -22,12 +22,13 @@ // // This notice may not be removed or altered from any source distribution. +#include "GraphCmp.hh" + #include "ContainerHelpers.hh" -#include "StringUtil.hh" +#include "Graph.hh" #include "Network.hh" #include "NetworkCmp.hh" -#include "Graph.hh" -#include "GraphCmp.hh" +#include "StringUtil.hh" namespace sta { @@ -73,4 +74,4 @@ sortEdges(EdgeSeq *edges, sort(edges, EdgeLess(network, graph)); } -} +} // namespace sta diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index b21d524cc..eacad4454 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -24,20 +24,20 @@ #pragma once +#include #include #include #include -#include -#include "MinMax.hh" +#include "Delay.hh" +#include "GraphClass.hh" #include "LibertyClass.hh" -#include "TimingArc.hh" -#include "TableModel.hh" +#include "MinMax.hh" #include "NetworkClass.hh" -#include "GraphClass.hh" -#include "Delay.hh" #include "ParasiticsClass.hh" #include "StaState.hh" +#include "TableModel.hh" +#include "TimingArc.hh" namespace sta { @@ -70,7 +70,7 @@ public: const Pin *drvr_pin, Edge *edge, const TimingArc *arc, - float in_delay); + float input_delay); const Pin *inPin() const { return in_pin_; } const RiseFall *inEdge() const; const Pin *drvrPin() const { return drvr_pin_; } diff --git a/include/sta/Bdd.hh b/include/sta/Bdd.hh index 0cc101bfe..834696fcb 100644 --- a/include/sta/Bdd.hh +++ b/include/sta/Bdd.hh @@ -26,8 +26,8 @@ #include -#include "StaState.hh" #include "LibertyClass.hh" +#include "StaState.hh" struct DdNode; struct DdManager; diff --git a/include/sta/Bfs.hh b/include/sta/Bfs.hh index 10c90f175..5c11ab1ce 100644 --- a/include/sta/Bfs.hh +++ b/include/sta/Bfs.hh @@ -27,10 +27,10 @@ #include #include -#include "Iterator.hh" #include "GraphClass.hh" -#include "VertexVisitor.hh" +#include "Iterator.hh" #include "StaState.hh" +#include "VertexVisitor.hh" namespace sta { diff --git a/include/sta/BoundedHeap.hh b/include/sta/BoundedHeap.hh index ff0ee39d6..6ae23ce06 100644 --- a/include/sta/BoundedHeap.hh +++ b/include/sta/BoundedHeap.hh @@ -24,10 +24,10 @@ #pragma once -#include #include #include #include +#include namespace sta { diff --git a/include/sta/ClkNetwork.hh b/include/sta/ClkNetwork.hh index 1d25b57c8..493446c8d 100644 --- a/include/sta/ClkNetwork.hh +++ b/include/sta/ClkNetwork.hh @@ -26,10 +26,10 @@ #include -#include "StaState.hh" -#include "NetworkClass.hh" #include "GraphClass.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" +#include "StaState.hh" namespace sta { diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index aae3e58b8..48cfb88d2 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -27,11 +27,11 @@ #include #include +#include "GraphClass.hh" #include "MinMax.hh" #include "RiseFallMinMax.hh" #include "SdcClass.hh" #include "SdcCmdComment.hh" -#include "GraphClass.hh" namespace sta { @@ -170,31 +170,31 @@ protected: std::string name_; PinSet pins_; - bool add_to_pins_; + bool add_to_pins_{false}; // Hierarchical pins in pins_ become driver pins through the pin. PinSet leaf_pins_; - float period_; - FloatSeq *waveform_; - bool waveform_valid_; + float period_{0.0}; + FloatSeq *waveform_{nullptr}; + bool waveform_valid_{false}; const int index_; - ClockEdge **clk_edges_; - bool is_propagated_; + ClockEdge **clk_edges_{nullptr}; + bool is_propagated_{false}; RiseFallMinMax slews_; RiseFallMinMax slew_limits_[path_clk_or_data_count]; - ClockUncertainties *uncertainties_; - bool is_generated_; + ClockUncertainties *uncertainties_{nullptr}; + bool is_generated_{false}; // Generated clock variables. - Pin *src_pin_; - Clock *master_clk_; + Pin *src_pin_{nullptr}; + Clock *master_clk_{nullptr}; // True if the master clock is infered rather than specified by command. - bool master_clk_infered_; - int divide_by_; - int multiply_by_; - float duty_cycle_; - bool invert_; - bool combinational_; - IntSeq *edges_; - FloatSeq *edge_shifts_; + bool master_clk_infered_{false}; + int divide_by_{0}; + int multiply_by_{0}; + float duty_cycle_{0}; + bool invert_{false}; + bool combinational_{false}; + IntSeq *edges_{nullptr}; + FloatSeq *edge_shifts_{nullptr}; private: friend class Sdc; @@ -205,7 +205,6 @@ class ClockEdge { public: Clock *clock() const { return clock_; } - ~ClockEdge(); const RiseFall *transition() const { return rf_; } float time() const { return time_; } const std::string &name() const { return name_; } @@ -223,7 +222,7 @@ private: Clock *clock_; const RiseFall *rf_; std::string name_; - float time_; + float time_{0.0}; int index_; }; diff --git a/include/sta/ClockGatingCheck.hh b/include/sta/ClockGatingCheck.hh index dad7bdeb1..f460472f8 100644 --- a/include/sta/ClockGatingCheck.hh +++ b/include/sta/ClockGatingCheck.hh @@ -24,22 +24,21 @@ #pragma once -#include "SdcClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" namespace sta { class ClockGatingCheck { public: - ClockGatingCheck(); RiseFallMinMax *margins() { return &margins_; } void setActiveValue(LogicValue value); LogicValue activeValue() const { return active_value_; } private: RiseFallMinMax margins_; - LogicValue active_value_; + LogicValue active_value_{LogicValue::unknown}; }; } // namespace sta diff --git a/include/sta/ClockGroups.hh b/include/sta/ClockGroups.hh index f725b6e9c..7a3540b10 100644 --- a/include/sta/ClockGroups.hh +++ b/include/sta/ClockGroups.hh @@ -26,8 +26,8 @@ #include -#include "SdcCmdComment.hh" #include "SdcClass.hh" +#include "SdcCmdComment.hh" namespace sta { diff --git a/include/sta/ClockInsertion.hh b/include/sta/ClockInsertion.hh index 256e7dbef..a33423b38 100644 --- a/include/sta/ClockInsertion.hh +++ b/include/sta/ClockInsertion.hh @@ -26,8 +26,8 @@ #include "MinMax.hh" #include "NetworkClass.hh" -#include "SdcClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" #include "Transition.hh" namespace sta { diff --git a/include/sta/ClockLatency.hh b/include/sta/ClockLatency.hh index 87ddb3b7e..54c4dff2c 100644 --- a/include/sta/ClockLatency.hh +++ b/include/sta/ClockLatency.hh @@ -26,9 +26,9 @@ #include "MinMax.hh" #include "NetworkClass.hh" -#include "Transition.hh" -#include "SdcClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" +#include "Transition.hh" namespace sta { diff --git a/include/sta/ConcreteLibrary.hh b/include/sta/ConcreteLibrary.hh index 50182a1ec..75018056e 100644 --- a/include/sta/ConcreteLibrary.hh +++ b/include/sta/ConcreteLibrary.hh @@ -25,12 +25,12 @@ #pragma once #include +#include #include #include -#include -#include "StringUtil.hh" #include "NetworkClass.hh" +#include "StringUtil.hh" // The classes defined in this file are a contrete implementation of // the library API. They can be used by a reader to construct classes @@ -84,8 +84,8 @@ protected: ObjectId id_; std::string filename_; bool is_liberty_; - char bus_brkt_left_; - char bus_brkt_right_; + char bus_brkt_left_{'['}; + char bus_brkt_right_{']'}; ConcreteCellMap cell_map_; private: @@ -129,7 +129,7 @@ public: // Group previously defined bus bit ports together. void groupBusPorts(char bus_brkt_left, char bus_brkt_right, - std::function port_msb_first); + const std::function &port_msb_first); size_t portCount() const; void setName(std::string_view name); virtual void addPort(ConcretePort *port); @@ -149,7 +149,7 @@ protected: int from_index, int to_index); // Bus port bit (internal to makeBusPortBits). - ConcretePort *makePort(std::string bit_name, + ConcretePort *makePort(std::string_view bit_name, int bit_index); void makeBusPortBit(ConcretePort *bus_port, std::string_view bus_name, @@ -160,14 +160,14 @@ protected: // Filename is optional. std::string filename_; ConcreteLibrary *library_; - LibertyCell *liberty_cell_; + LibertyCell *liberty_cell_{nullptr}; // External application cell. - void *ext_cell_; + void *ext_cell_{nullptr}; // Non-bus and bus ports (but no expanded bus bit ports). ConcretePortSeq ports_; ConcretePortMap port_map_; // Port bit count (expanded buses). - int port_bit_count_; + int port_bit_count_{0}; bool is_leaf_; AttributeMap attribute_map_; @@ -242,10 +242,10 @@ protected: ObjectId id_; ConcreteCell *cell_; PortDirection *direction_; - LibertyPort *liberty_port_; + LibertyPort *liberty_port_{nullptr}; // External application port. - void *ext_port_; - int pin_index_; + void *ext_port_{nullptr}; + int pin_index_{-1}; bool is_bundle_; bool is_bus_; int from_index_; @@ -253,7 +253,7 @@ protected: // Expanded bus bit ports (ordered by from_index_ to to_index_) // or bundle member ports. ConcretePortSeq *member_ports_; - ConcretePort *bundle_port_; + ConcretePort *bundle_port_{nullptr}; private: friend class ConcreteCell; @@ -271,8 +271,8 @@ private: const ConcretePortSeq &ports_; ConcretePortSeq::const_iterator port_iter_; - ConcretePortMemberIterator *member_iter_; - ConcretePort *next_; + ConcretePortMemberIterator *member_iter_{nullptr}; + ConcretePort *next_{nullptr}; }; } // namespace sta diff --git a/include/sta/ConcreteNetwork.hh b/include/sta/ConcreteNetwork.hh index 592a65a6d..593c444c3 100644 --- a/include/sta/ConcreteNetwork.hh +++ b/include/sta/ConcreteNetwork.hh @@ -25,14 +25,14 @@ #pragma once #include -#include -#include #include #include +#include +#include -#include "StringUtil.hh" -#include "Network.hh" #include "LibertyClass.hh" +#include "Network.hh" +#include "StringUtil.hh" namespace sta { @@ -263,6 +263,9 @@ public: using Network::isLeaf; protected: + void clearImpl(); + void deleteCellNetworkViewsImpl(); + void deleteInstanceImpl(Instance *inst); void addLibrary(ConcreteLibrary *library); void setName(std::string_view name); void clearConstantNets(); @@ -280,8 +283,8 @@ protected: // Cell lookup search order sequence. ConcreteLibrarySeq library_seq_; ConcreteLibraryMap library_map_; - Instance *top_instance_; - NetSet constant_nets_[2]; // LogicValue::zero/one + Instance *top_instance_{nullptr}; + NetSet constant_nets_[2]{NetSet(this), NetSet(this)}; // LogicValue::zero/one LinkNetworkFunc link_func_; CellNetworkViewMap cell_network_view_map_; static ObjectId object_id_; @@ -293,7 +296,7 @@ private: class ConcreteInstance { public: - std::string_view name() const { return name_; } + const std::string &name() const { return name_; } ObjectId id() const { return id_; } Cell *cell() const; ConcreteInstance *parent() const { return parent_; } @@ -332,8 +335,8 @@ protected: ConcreteInstance *parent_; // Array of pins indexed by pin->port->index(). ConcretePinSeq pins_; - ConcreteInstanceChildMap *children_; - ConcreteInstanceNetMap *nets_; + ConcreteInstanceChildMap *children_{nullptr}; + ConcreteInstanceNetMap *nets_{nullptr}; AttributeMap attribute_map_; private: @@ -344,7 +347,7 @@ private: class ConcretePin { public: - std::string_view name() const; + const std::string &name() const; ConcreteInstance *instance() const { return instance_; } ConcreteNet *net() const { return net_; } ConcretePort *port() const { return port_; } @@ -362,12 +365,12 @@ protected: ConcreteInstance *instance_; ConcretePort *port_; ConcreteNet *net_; - ConcreteTerm *term_; + ConcreteTerm *term_{nullptr}; ObjectId id_; // Doubly linked list of net pins. - ConcretePin *net_next_; - ConcretePin *net_prev_; - VertexId vertex_id_; + ConcretePin *net_next_{nullptr}; + ConcretePin *net_prev_{nullptr}; + VertexId vertex_id_{vertex_id_null}; private: friend class ConcreteNetwork; @@ -378,7 +381,7 @@ private: class ConcreteTerm { public: - std::string_view name() const; + const std::string &name() const; ObjectId id() const { return id_; } ConcreteNet *net() const { return net_; } ConcretePin *pin() const { return pin_; } @@ -392,7 +395,7 @@ protected: ConcreteNet *net_; ObjectId id_; // Linked list of net terms. - ConcreteTerm *net_next_; + ConcreteTerm *net_next_{nullptr}; private: friend class ConcreteNetwork; @@ -403,7 +406,7 @@ private: class ConcreteNet { public: - std::string_view name() const { return name_; } + const std::string &name() const { return name_; } ObjectId id() const { return id_; } ConcreteInstance *instance() const { return instance_; } void addPin(ConcretePin *pin); @@ -420,12 +423,12 @@ protected: ObjectId id_; ConcreteInstance *instance_; // Pointer to head of linked list of pins. - ConcretePin *pins_; + ConcretePin *pins_{nullptr}; // Pointer to head of linked list of terminals. // These terminals correspond to the pins attached to the instance that // contains this net in the hierarchy level above. - ConcreteTerm *terms_; - ConcreteNet *merged_into_; + ConcreteTerm *terms_{nullptr}; + ConcreteNet *merged_into_{nullptr}; friend class ConcreteNetwork; friend class ConcreteNetTermIterator; diff --git a/include/sta/CycleAccting.hh b/include/sta/CycleAccting.hh index ca4aa3b81..8eb369070 100644 --- a/include/sta/CycleAccting.hh +++ b/include/sta/CycleAccting.hh @@ -27,9 +27,9 @@ #include #include "MinMax.hh" -#include "TimingRole.hh" -#include "StaState.hh" #include "SdcClass.hh" +#include "StaState.hh" +#include "TimingRole.hh" namespace sta { @@ -127,7 +127,7 @@ private: int src_cycle_[TimingRole::index_max + 1]; // Target clock cycle offset. int tgt_cycle_[TimingRole::index_max + 1]; - bool max_cycles_exceeded_; + bool max_cycles_exceeded_{false}; }; } // namespace sta diff --git a/include/sta/DataCheck.hh b/include/sta/DataCheck.hh index e96e067c2..e14c6331e 100644 --- a/include/sta/DataCheck.hh +++ b/include/sta/DataCheck.hh @@ -24,12 +24,12 @@ #pragma once -#include "MinMax.hh" #include "LibertyClass.hh" +#include "MinMax.hh" #include "NetworkClass.hh" #include "NetworkCmp.hh" -#include "SdcClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" namespace sta { diff --git a/include/sta/Debug.hh b/include/sta/Debug.hh index 0bc4be1d5..876009a40 100644 --- a/include/sta/Debug.hh +++ b/include/sta/Debug.hh @@ -24,10 +24,10 @@ #pragma once -#include -#include #include #include +#include +#include #include "Format.hh" #include "Report.hh" diff --git a/include/sta/Delay.hh b/include/sta/Delay.hh index 983e68891..6a6984bdf 100644 --- a/include/sta/Delay.hh +++ b/include/sta/Delay.hh @@ -27,8 +27,8 @@ #include #include -#include "StaConfig.hh" #include "MinMax.hh" +#include "StaConfig.hh" namespace sta { @@ -78,7 +78,7 @@ class DelayDbl { public: DelayDbl() noexcept; - DelayDbl(double value) noexcept; + DelayDbl(double mean) noexcept; double mean() const { return values_[0]; } void setMean(double mean); double meanShift() const { return values_[1]; } diff --git a/include/sta/DeratingFactors.hh b/include/sta/DeratingFactors.hh index a068a6da7..005318302 100644 --- a/include/sta/DeratingFactors.hh +++ b/include/sta/DeratingFactors.hh @@ -24,10 +24,10 @@ #pragma once -#include "MinMax.hh" #include "LibertyClass.hh" -#include "SdcClass.hh" +#include "MinMax.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" namespace sta { @@ -111,10 +111,4 @@ private: DeratingFactors factors_[timing_derate_cell_type_count]; }; -class DeratingFactorsNet : public DeratingFactors -{ -public: - DeratingFactorsNet(); -}; - } // namespace sta diff --git a/include/sta/DisabledPorts.hh b/include/sta/DisabledPorts.hh index 5628d85c6..2352b8a3e 100644 --- a/include/sta/DisabledPorts.hh +++ b/include/sta/DisabledPorts.hh @@ -27,8 +27,8 @@ #include #include -#include "NetworkClass.hh" #include "LibertyClass.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" namespace sta { @@ -46,7 +46,6 @@ using TimingArcSetSet = std::set; class DisabledPorts { public: - DisabledPorts(); ~DisabledPorts(); void setDisabledAll(); void removeDisabledAll(); @@ -67,10 +66,10 @@ public: [[nodiscard]] bool all() const { return all_; } private: - bool all_; - LibertyPortSet *from_; - LibertyPortSet *to_; - LibertyPortPairSet *from_to_; + bool all_{false}; + LibertyPortSet *from_{nullptr}; + LibertyPortSet *to_{nullptr}; + LibertyPortPairSet *from_to_{nullptr}; }; // set_disable_timing cell [-from] [-to] @@ -89,7 +88,7 @@ public: private: LibertyCell *cell_; - TimingArcSetSet *arc_sets_; + TimingArcSetSet *arc_sets_{nullptr}; }; // set_disable_timing instance [-from] [-to] diff --git a/include/sta/DispatchQueue.hh b/include/sta/DispatchQueue.hh index 9baa0099a..ceda2e78f 100644 --- a/include/sta/DispatchQueue.hh +++ b/include/sta/DispatchQueue.hh @@ -5,14 +5,14 @@ #pragma once -#include -#include -#include +#include +#include #include -#include +#include #include -#include -#include +#include +#include +#include namespace sta { diff --git a/include/sta/Error.hh b/include/sta/Error.hh index 7459e3157..5754a99b0 100644 --- a/include/sta/Error.hh +++ b/include/sta/Error.hh @@ -35,7 +35,6 @@ namespace sta { class Exception : public std::exception { public: - Exception(); const char *what() const noexcept override = 0; }; diff --git a/include/sta/ExceptionPath.hh b/include/sta/ExceptionPath.hh index 07987e944..c67270ae3 100644 --- a/include/sta/ExceptionPath.hh +++ b/include/sta/ExceptionPath.hh @@ -29,8 +29,8 @@ #include #include "Error.hh" -#include "SdcCmdComment.hh" #include "SdcClass.hh" +#include "SdcCmdComment.hh" namespace sta { @@ -145,7 +145,7 @@ protected: const MinMaxAll *min_max_; bool own_pts_; int priority_; - size_t id_; // Unique ID assigned by Sdc. + size_t id_{0}; // Unique ID assigned by Sdc. ExceptionState *states_; }; @@ -301,7 +301,6 @@ public: ExceptionTo *to, bool own_pts, std::string_view comment); - ~GroupPath() override; ExceptionPath *clone(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, @@ -367,7 +366,7 @@ protected: bool own_pts_; // Hash is cached because there may be many objects to speed up // exception merging. - size_t hash_; + size_t hash_{0}; // Maximum number of objects for to_string() to show. static const int to_string_max_objects_; @@ -583,7 +582,7 @@ protected: // Leaf/port pins. PinSet *pins_; // Graph edges that traverse thru hierarchical pins. - EdgePinsSet *edges_; + EdgePinsSet *edges_{nullptr}; NetSet *nets_; InstanceSet *insts_; }; @@ -602,9 +601,9 @@ public: private: const ExceptionPath *exception_; - bool from_done_; + bool from_done_{false}; ExceptionThruSeq::iterator thru_iter_; - bool to_done_; + bool to_done_{false}; }; // Visitor for exception point sets expanded into single object paths. @@ -665,7 +664,7 @@ public: private: ExceptionPath *exception_; ExceptionThru *next_thru_; - ExceptionState *next_state_; + ExceptionState *next_state_{nullptr}; int index_; }; diff --git a/include/sta/FilterObjects.hh b/include/sta/FilterObjects.hh index 3de0dd844..71bea6c0b 100644 --- a/include/sta/FilterObjects.hh +++ b/include/sta/FilterObjects.hh @@ -41,52 +41,52 @@ class Report; PortSeq filterPorts(std::string_view filter_expression, - PortSeq *objects, + PortSeq *ports, Sta *sta); InstanceSeq filterInstances(std::string_view filter_expression, - InstanceSeq *objects, + InstanceSeq *insts, Sta *sta); PinSeq filterPins(std::string_view filter_expression, - PinSeq *objects, + PinSeq *pins, Sta *sta); NetSeq filterNets(std::string_view filter_expression, - NetSeq *objects, + NetSeq *nets, Sta *sta); ClockSeq filterClocks(std::string_view filter_expression, - ClockSeq *objects, + ClockSeq *clks, Sta *sta); LibertyCellSeq filterLibCells(std::string_view filter_expression, - LibertyCellSeq *objects, + LibertyCellSeq *cells, Sta *sta); LibertyPortSeq filterLibPins(std::string_view filter_expression, - LibertyPortSeq *objects, + LibertyPortSeq *ports, Sta *sta); LibertyLibrarySeq filterLibertyLibraries(std::string_view filter_expression, - LibertyLibrarySeq *objects, + LibertyLibrarySeq *libs, Sta *sta); EdgeSeq filterTimingArcs(std::string_view filter_expression, - EdgeSeq *objects, + EdgeSeq *edges, Sta *sta); PathEndSeq filterPathEnds(std::string_view filter_expression, - PathEndSeq *objects, + PathEndSeq *ends, Sta *sta); // For FilterExpr unit tests. diff --git a/include/sta/FuncExpr.hh b/include/sta/FuncExpr.hh index 93db8d681..72f7bf96a 100644 --- a/include/sta/FuncExpr.hh +++ b/include/sta/FuncExpr.hh @@ -26,8 +26,8 @@ #include -#include "NetworkClass.hh" #include "LibertyClass.hh" +#include "NetworkClass.hh" namespace sta { diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 2d3024a77..233d363cf 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -24,19 +24,19 @@ #pragma once -#include #include #include +#include +#include "Delay.hh" +#include "GraphClass.hh" #include "Iterator.hh" -#include "ObjectTable.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "Delay.hh" -#include "GraphClass.hh" -#include "VertexId.hh" +#include "ObjectTable.hh" #include "Path.hh" #include "StaState.hh" +#include "VertexId.hh" namespace sta { @@ -102,7 +102,7 @@ public: const Slew &slew); // Edge functions. - Edge *edge(EdgeId edge_index) const; + Edge *edge(EdgeId edge_id) const; EdgeId id(const Edge *edge) const; Edge *makeEdge(Vertex *from, Vertex *to, @@ -211,8 +211,8 @@ protected: void initArcDelays(Edge *edge); void removeDelayAnnotated(Edge *edge); - VertexTable *vertices_; - EdgeTable *edges_; + VertexTable *vertices_{nullptr}; + EdgeTable *edges_{nullptr}; // Bidirect pins are split into two vertices: // load/sink (top level output, instance pin input) vertex in pin_vertex_map // driver/source (top level input, instance pin output) vertex @@ -436,9 +436,9 @@ private: Network *network_; Instance *top_inst_; LeafInstanceIterator *inst_iter_; - InstancePinIterator *pin_iter_; - Vertex *vertex_; - Vertex *bidir_vertex_; + InstancePinIterator *pin_iter_{nullptr}; + Vertex *vertex_{nullptr}; + Vertex *bidir_vertex_{nullptr}; }; class VertexInEdgeIterator : public VertexEdgeIterator diff --git a/include/sta/GraphClass.hh b/include/sta/GraphClass.hh index bb0309245..cdf7539b8 100644 --- a/include/sta/GraphClass.hh +++ b/include/sta/GraphClass.hh @@ -25,13 +25,13 @@ #pragma once #include -#include #include +#include -#include "ObjectId.hh" +#include "Delay.hh" #include "MinMax.hh" +#include "ObjectId.hh" #include "Transition.hh" -#include "Delay.hh" namespace sta { diff --git a/include/sta/GraphCmp.hh b/include/sta/GraphCmp.hh index add7530a1..9c714d9c8 100644 --- a/include/sta/GraphCmp.hh +++ b/include/sta/GraphCmp.hh @@ -24,9 +24,9 @@ #pragma once +#include "GraphClass.hh" #include "NetworkClass.hh" #include "NetworkCmp.hh" -#include "GraphClass.hh" namespace sta { diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 5d2ae509f..caf309b5d 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -25,16 +25,16 @@ #pragma once #include -#include #include #include +#include -#include "NetworkClass.hh" +#include "ArcDelayCalc.hh" #include "GraphClass.hh" -#include "SearchClass.hh" +#include "NetworkClass.hh" #include "SdcClass.hh" +#include "SearchClass.hh" #include "StaState.hh" -#include "ArcDelayCalc.hh" namespace sta { @@ -104,7 +104,7 @@ public: float &pin_cap, float &wire_cap, float &fanout, - bool &has_set_load) const; + bool &has_net_load) const; void parasiticLoad(const Pin *drvr_pin, const RiseFall *rf, const Scene *scene, @@ -171,7 +171,7 @@ protected: Vertex *drvr_vertex, const RiseFall *rf, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews *from_slews, const LibertyPort *to_port, const Scene *scene, const MinMax *min_max, @@ -191,7 +191,7 @@ protected: ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map); MultiDrvrNet *multiDrvrNet(const Vertex *drvr_vertex) const; - MultiDrvrNet *findMultiDrvrNet(Vertex *drvr_pin); + MultiDrvrNet *findMultiDrvrNet(Vertex *drvr_vertex); MultiDrvrNet *makeMultiDrvrNet(Vertex *drvr_vertex); bool hasMultiDrvrs(Vertex *drvr_vertex); Vertex *firstLoad(Vertex *drvr_vertex); @@ -235,7 +235,7 @@ protected: ArcDelayCalc *arc_delay_calc, bool propagate); DrvrLoadSlews loadSlews(LoadPinIndexMap &load_pin_index_map); - bool loadSlewsChanged(DrvrLoadSlews &prev_load_slews, + bool loadSlewsChanged(DrvrLoadSlews &load_slews_prev, LoadPinIndexMap &load_pin_index_map); void enqueueTimingChecksEdges(Vertex *vertex); bool annotateDelaysSlews(Edge *edge, @@ -294,10 +294,10 @@ protected: bool &has_net_load) const; // Observer for edge delay changes. - DelayCalcObserver *observer_; - bool delays_seeded_; - bool incremental_; - bool delays_exist_; + DelayCalcObserver *observer_{nullptr}; + bool delays_seeded_{false}; + bool incremental_{false}; + bool delays_exist_{false}; // Vertices with invalid -to delays. VertexSet invalid_delays_; // Timing check edges with invalid delays. @@ -313,7 +313,7 @@ protected: std::mutex multi_drvr_lock_; // Percentage (0.0:1.0) change in delay that causes downstream // delays to be recomputed during incremental delay calculation. - float incremental_delay_tolerance_; + float incremental_delay_tolerance_{0.0}; friend class FindVertexDelays; friend class MultiDrvrNet; @@ -334,7 +334,6 @@ public: class MultiDrvrNet { public: - MultiDrvrNet(); VertexSeq &drvrs() { return drvrs_; } const VertexSeq &drvrs() const { return drvrs_; } bool parallelGates(const Network *network) const; @@ -352,7 +351,7 @@ public: private: // Driver that triggers delay calculation for all the drivers on the net. - Vertex *dcalc_drvr_; + Vertex *dcalc_drvr_{nullptr}; VertexSeq drvrs_; // [drvr_rf->index][dcalc_ap->index] std::vector net_caps_; diff --git a/include/sta/Hash.hh b/include/sta/Hash.hh index ce68f03a5..5cfd423df 100644 --- a/include/sta/Hash.hh +++ b/include/sta/Hash.hh @@ -24,8 +24,8 @@ #pragma once -#include #include +#include #include namespace sta { diff --git a/include/sta/InputDrive.hh b/include/sta/InputDrive.hh index a5dbdf2d8..30bdd5f30 100644 --- a/include/sta/InputDrive.hh +++ b/include/sta/InputDrive.hh @@ -24,10 +24,11 @@ #pragma once -#include "MinMax.hh" #include "LibertyClass.hh" +#include "MinMax.hh" #include "NetworkClass.hh" #include "RiseFallMinMax.hh" +#include "SdcClass.hh" namespace sta { @@ -58,7 +59,7 @@ public: void setDriveCell(const LibertyLibrary *library, const LibertyCell *cell, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews &from_slews, const LibertyPort *to_port, const RiseFallBoth *rf, const MinMaxAll *min_max); @@ -67,7 +68,7 @@ public: // Return values. const LibertyCell *&cell, const LibertyPort *&from_port, - float *&from_slews, + const DriveCellSlews *&from_slews, const LibertyPort *&to_port) const; InputDriveCell *driveCell(const RiseFall *rf, const MinMax *min_max) const; @@ -94,7 +95,7 @@ public: InputDriveCell(const LibertyLibrary *library, const LibertyCell *cell, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews &from_slews, const LibertyPort *to_port); const LibertyLibrary *library() const { return library_; } void setLibrary(const LibertyLibrary *library); @@ -102,8 +103,8 @@ public: void setCell(const LibertyCell *cell); const LibertyPort *fromPort() const { return from_port_; } void setFromPort(const LibertyPort *from_port); - float *fromSlews() { return from_slews_; } - void setFromSlews(float *from_slews); + const DriveCellSlews &fromSlews() const { return from_slews_; } + void setFromSlews(const DriveCellSlews &from_slews); const LibertyPort *toPort() const { return to_port_; } void setToPort(const LibertyPort *to_port); bool equal(const InputDriveCell *drive) const; @@ -112,7 +113,7 @@ private: const LibertyLibrary *library_; const LibertyCell *cell_; const LibertyPort *from_port_; - float from_slews_[RiseFall::index_count]; + DriveCellSlews from_slews_; const LibertyPort *to_port_; }; diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 204431273..5e5fef753 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -24,27 +24,27 @@ #pragma once -#include -#include #include #include -#include #include +#include +#include +#include #include #include #include -#include "ContainerHelpers.hh" -#include "MinMax.hh" -#include "RiseFallMinMax.hh" #include "ConcreteLibrary.hh" -#include "RiseFallValues.hh" -#include "MinMaxValues.hh" -#include "Transition.hh" +#include "ContainerHelpers.hh" #include "Delay.hh" #include "InternalPower.hh" #include "LeakagePower.hh" #include "LibertyClass.hh" +#include "MinMax.hh" +#include "MinMaxValues.hh" +#include "RiseFallMinMax.hh" +#include "RiseFallValues.hh" +#include "Transition.hh" namespace sta { diff --git a/include/sta/LibertyClass.hh b/include/sta/LibertyClass.hh index 17f547ad1..4687cbbb5 100644 --- a/include/sta/LibertyClass.hh +++ b/include/sta/LibertyClass.hh @@ -24,10 +24,10 @@ #pragma once -#include -#include #include +#include #include +#include namespace sta { diff --git a/include/sta/MinMax.hh b/include/sta/MinMax.hh index 5d5c86866..a23eda8e6 100644 --- a/include/sta/MinMax.hh +++ b/include/sta/MinMax.hh @@ -25,8 +25,8 @@ #pragma once #include -#include #include +#include #include "Iterator.hh" diff --git a/include/sta/MinMaxValues.hh b/include/sta/MinMaxValues.hh index 6b2fcc9b8..8fb8eeea4 100644 --- a/include/sta/MinMaxValues.hh +++ b/include/sta/MinMaxValues.hh @@ -24,8 +24,8 @@ #pragma once -#include "MinMax.hh" #include "Error.hh" +#include "MinMax.hh" namespace sta { diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 05eee68b0..70580e90e 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -29,11 +29,11 @@ #include #include -#include "StringUtil.hh" #include "LibertyClass.hh" -#include "VertexId.hh" #include "NetworkClass.hh" #include "StaState.hh" +#include "StringUtil.hh" +#include "VertexId.hh" namespace sta { @@ -93,7 +93,7 @@ using NetDrvrPinsMap = std::map; class Network : public StaState { public: - Network(); + Network() = default; ~Network() override; virtual void clear(); @@ -446,7 +446,7 @@ protected: void findInstancesMatching1(const Instance *context, size_t context_name_length, const PatternMatch *pattern, - InstanceSeq &insts) const; + InstanceSeq &matches) const; void findInstancesHierMatching1(const Instance *instance, const PatternMatch *pattern, InstanceSeq &matches) const; @@ -475,7 +475,7 @@ protected: const PatternMatch *pattern, // Return value. PinSeq &matches) const; - void findInstPinsHierMatching(const Instance *parent, + void findInstPinsHierMatching(const Instance *instance, const PatternMatch *pattern, // Return value. PinSeq &matches) const; @@ -490,9 +490,9 @@ protected: // nets may be connected across hierarchy levels. void clearNetDrvrPinMap(); - LibertyLibrary *default_liberty_; - char divider_; - char escape_; + LibertyLibrary *default_liberty_{nullptr}; + char divider_{'/'}; + char escape_{'\\'}; NetDrvrPinsMap net_drvr_pin_map_; }; @@ -500,7 +500,7 @@ protected: class NetworkEdit : public Network { public: - NetworkEdit(); + NetworkEdit() = default; bool isEditable() const override { return true; } virtual Instance *makeInstance(LibertyCell *cell, std::string_view name, @@ -619,7 +619,7 @@ private: PinSet &pins); const Network *network_; - PinSet constant_pins_[2]; + PinSet constant_pins_[2]{PinSet(network_), PinSet(network_)}; LogicValue value_; PinSet::iterator pin_iter_; }; diff --git a/include/sta/NetworkClass.hh b/include/sta/NetworkClass.hh index 0e72ff241..e32ce51a8 100644 --- a/include/sta/NetworkClass.hh +++ b/include/sta/NetworkClass.hh @@ -25,11 +25,11 @@ #pragma once #include -#include -#include #include #include +#include #include +#include #include "Iterator.hh" diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index 7b4bfccfc..c6706f90f 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -28,11 +28,11 @@ #include #include -#include "StaState.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "SdcClass.hh" #include "ParasiticsClass.hh" +#include "SdcClass.hh" +#include "StaState.hh" namespace sta { @@ -164,12 +164,12 @@ public: // Parasitic network component builders. virtual ParasiticNode *findParasiticNode(Parasitic *parasitic, const Net *net, - int id, + uint32_t id, const Network *network) const = 0; // Make a subnode of the parasitic network net. virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, const Net *net, - int id, + uint32_t id, const Network *network) = 0; // Find the parasitic node connected to pin. virtual ParasiticNode *findParasiticNode(const Parasitic *parasitic, @@ -195,11 +195,11 @@ public: // Coupling capacitor between parasitic nodes on a net. virtual void makeCapacitor(Parasitic *parasitic, - size_t id, + uint32_t id, float cap, ParasiticNode *node1, ParasiticNode *node2) = 0; - virtual size_t id(const ParasiticCapacitor *capacitor) const = 0; + virtual uint32_t id(const ParasiticCapacitor *capacitor) const = 0; virtual float value(const ParasiticCapacitor *capacitor) const = 0; virtual ParasiticNode *node1(const ParasiticCapacitor *capacitor) const = 0; virtual ParasiticNode *node2(const ParasiticCapacitor *capacitor) const = 0; @@ -207,15 +207,15 @@ public: ParasiticNode *node) const; virtual void makeResistor(Parasitic *parasitic, - size_t id, + uint32_t id, float res, ParasiticNode *node1, ParasiticNode *node2) = 0; - virtual size_t id(const ParasiticResistor *resistor) const = 0; + virtual uint32_t id(const ParasiticResistor *resistor) const = 0; virtual float value(const ParasiticResistor *resistor) const = 0; virtual ParasiticNode *node1(const ParasiticResistor *resistor) const = 0; virtual ParasiticNode *node2(const ParasiticResistor *resistor) const = 0; - virtual ParasiticNode *otherNode(const ParasiticResistor *capacitor, + virtual ParasiticNode *otherNode(const ParasiticResistor *resistor, ParasiticNode *node) const; // Iteration over resistors connected to a nodes. @@ -287,7 +287,7 @@ protected: const Net *findParasiticNet(const Pin *pin) const; - float coupling_cap_factor_; + float coupling_cap_factor_ {1.0}; }; class ParasiticNodeLess diff --git a/include/sta/Path.hh b/include/sta/Path.hh index 81b37957e..d04f9827f 100644 --- a/include/sta/Path.hh +++ b/include/sta/Path.hh @@ -24,14 +24,14 @@ #pragma once +#include "Delay.hh" +#include "GraphClass.hh" #include "MinMax.hh" #include "NetworkClass.hh" #include "SdcClass.hh" -#include "Transition.hh" -#include "GraphClass.hh" -#include "Delay.hh" -#include "StaState.hh" #include "SearchClass.hh" +#include "StaState.hh" +#include "Transition.hh" namespace sta { diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index 2e2529220..be54fa735 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -26,11 +26,11 @@ #include -#include "LibertyClass.hh" #include "GraphClass.hh" +#include "LibertyClass.hh" +#include "Path.hh" #include "SdcClass.hh" #include "SearchClass.hh" -#include "Path.hh" #include "StaState.hh" namespace sta { diff --git a/include/sta/PathExpanded.hh b/include/sta/PathExpanded.hh index b6c8ba2b7..2a54e2a83 100644 --- a/include/sta/PathExpanded.hh +++ b/include/sta/PathExpanded.hh @@ -24,11 +24,11 @@ #pragma once -#include "TimingArc.hh" #include "GraphClass.hh" -#include "SearchClass.hh" #include "Path.hh" +#include "SearchClass.hh" #include "StaState.hh" +#include "TimingArc.hh" namespace sta { diff --git a/include/sta/PathGroup.hh b/include/sta/PathGroup.hh index 2d743fee6..df1800168 100644 --- a/include/sta/PathGroup.hh +++ b/include/sta/PathGroup.hh @@ -24,17 +24,17 @@ #pragma once +#include +#include #include #include #include -#include -#include +#include "PathEnd.hh" #include "SdcClass.hh" -#include "StaState.hh" #include "SearchClass.hh" +#include "StaState.hh" #include "StringUtil.hh" -#include "PathEnd.hh" namespace sta { diff --git a/include/sta/PortDelay.hh b/include/sta/PortDelay.hh index 3657cd4c1..ab71151a7 100644 --- a/include/sta/PortDelay.hh +++ b/include/sta/PortDelay.hh @@ -59,9 +59,9 @@ protected: const Pin *pin_; const ClockEdge *clk_edge_; - bool source_latency_included_; - bool network_latency_included_; - const Pin *ref_pin_; + bool source_latency_included_{false}; + bool network_latency_included_{false}; + const Pin *ref_pin_{nullptr}; RiseFallMinMax delays_; PinSet leaf_pins_; }; diff --git a/include/sta/PortExtCap.hh b/include/sta/PortExtCap.hh index d1c0d6943..189ac33e8 100644 --- a/include/sta/PortExtCap.hh +++ b/include/sta/PortExtCap.hh @@ -25,10 +25,10 @@ #pragma once #include "MinMax.hh" -#include "Transition.hh" -#include "RiseFallMinMax.hh" #include "MinMaxValues.hh" #include "NetworkClass.hh" +#include "RiseFallMinMax.hh" +#include "Transition.hh" namespace sta { @@ -38,7 +38,6 @@ using FanoutValues = MinMaxIntValues; class PortExtCap { public: - PortExtCap(); const Port *port() { return port_; } void pinCap(const RiseFall *rf, const MinMax *min_max, @@ -70,7 +69,7 @@ public: const FanoutValues *fanout() const { return &fanout_; } private: - const Port *port_; + const Port *port_{nullptr}; RiseFallMinMax pin_cap_; RiseFallMinMax wire_cap_; FanoutValues fanout_; diff --git a/include/sta/PowerClass.hh b/include/sta/PowerClass.hh index a34256317..0794fc4c5 100644 --- a/include/sta/PowerClass.hh +++ b/include/sta/PowerClass.hh @@ -24,12 +24,13 @@ #pragma once -#include #include +#include namespace sta { class Power; +class Instance; enum class PwrActivityOrigin { diff --git a/include/sta/Property.hh b/include/sta/Property.hh index afb717657..e5834b612 100644 --- a/include/sta/Property.hh +++ b/include/sta/Property.hh @@ -24,16 +24,16 @@ #pragma once +#include #include #include #include -#include #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "SearchClass.hh" -#include "SdcClass.hh" #include "PowerClass.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" namespace sta { diff --git a/include/sta/Report.hh b/include/sta/Report.hh index 068a206ae..c835a8eef 100644 --- a/include/sta/Report.hh +++ b/include/sta/Report.hh @@ -24,15 +24,15 @@ #pragma once -#include #include -#include -#include #include #include +#include +#include +#include -#include "Machine.hh" // __attribute__ #include "Format.hh" +#include "Machine.hh" // __attribute__ struct Tcl_Interp; diff --git a/include/sta/RiseFallMinMaxDelay.hh b/include/sta/RiseFallMinMaxDelay.hh index 0fd3975a4..50c803aad 100644 --- a/include/sta/RiseFallMinMaxDelay.hh +++ b/include/sta/RiseFallMinMaxDelay.hh @@ -24,9 +24,9 @@ #pragma once +#include "Delay.hh" #include "MinMax.hh" #include "Transition.hh" -#include "Delay.hh" namespace sta { diff --git a/include/sta/Scene.hh b/include/sta/Scene.hh index a43a4cb4f..4807136fb 100644 --- a/include/sta/Scene.hh +++ b/include/sta/Scene.hh @@ -24,9 +24,9 @@ #pragma once +#include #include #include -#include #include "GraphClass.hh" #include "SearchClass.hh" diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index cea05abff..4d3f010d6 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -24,25 +24,25 @@ #pragma once -#include #include -#include #include #include +#include +#include -#include "StringUtil.hh" -#include "MinMax.hh" -#include "StaState.hh" -#include "NetworkClass.hh" -#include "LibertyClass.hh" -#include "GraphClass.hh" -#include "SdcClass.hh" -#include "RiseFallValues.hh" #include "Clock.hh" -#include "DataCheck.hh" #include "CycleAccting.hh" +#include "DataCheck.hh" #include "ExceptionPath.hh" +#include "GraphClass.hh" +#include "LibertyClass.hh" +#include "MinMax.hh" +#include "NetworkClass.hh" #include "PinPair.hh" +#include "RiseFallValues.hh" +#include "SdcClass.hh" +#include "StaState.hh" +#include "StringUtil.hh" namespace sta { @@ -54,7 +54,7 @@ class DisabledPorts; class GraphLoop; class DeratingFactors; class DeratingFactorsGlobal; -class DeratingFactorsNet; +class DeratingFactors; class DeratingFactorsCell; class PatternMatch; class FindNetCaps; @@ -174,7 +174,7 @@ using InstancePvtMap = std::map; using PinMinPulseWidthMap = std::map; using ClockMinPulseWidthMap = std::map; using InstMinPulseWidthMap = std::map; -using NetDeratingFactorsMap = std::map; +using NetDeratingFactorsMap = std::map; using InstDeratingFactorsMap = std::map; using CellDeratingFactorsMap = std::map; using ClockGroupsSet = std::set; @@ -279,7 +279,7 @@ public: const LibertyCell *cell, const Port *port, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews &from_slews, const LibertyPort *to_port, const RiseFallBoth *rf, const MinMaxAll *min_max); diff --git a/include/sta/SdcClass.hh b/include/sta/SdcClass.hh index a22fee1e6..62c24efe6 100644 --- a/include/sta/SdcClass.hh +++ b/include/sta/SdcClass.hh @@ -29,9 +29,10 @@ #include #include "LibertyClass.hh" -#include "NetworkClass.hh" #include "MinMaxValues.hh" +#include "NetworkClass.hh" #include "PinPair.hh" +#include "Transition.hh" namespace sta { @@ -126,11 +127,13 @@ using ExceptionStateSet = std::set; // Constraint applies to clock or data paths. enum class PathClkOrData { clk, data }; -const int path_clk_or_data_count = 2; +const size_t path_clk_or_data_count = 2; enum class TimingDerateType { cell_delay, cell_check, net_delay }; -constexpr int timing_derate_type_count = 3; +constexpr size_t timing_derate_type_count = 3; enum class TimingDerateCellType { cell_delay, cell_check }; -constexpr int timing_derate_cell_type_count = 2; +constexpr size_t timing_derate_cell_type_count = 2; + +using DriveCellSlews = std::array; } // namespace sta diff --git a/include/sta/SdcNetwork.hh b/include/sta/SdcNetwork.hh index d23d18dab..ea4c83045 100644 --- a/include/sta/SdcNetwork.hh +++ b/include/sta/SdcNetwork.hh @@ -217,15 +217,15 @@ public: InstanceSeq findInstancesMatching(const Instance *context, const PatternMatch *pattern) const override; Net *findNet(std::string_view path_name) const override; - Net *findNetRelative(const Instance *instance, - std::string_view net_name) const override; + Net *findNetRelative(const Instance *inst, + std::string_view path_name) const override; Net *findNet(const Instance *instance, std::string_view net_name) const override; NetSeq findNetsMatching(const Instance *parent, const PatternMatch *pattern) const override; void findInstNetsMatching(const Instance *instance, const PatternMatch *pattern, - NetSeq &nets) const override; + NetSeq &matches) const override; Instance *findChild(const Instance *parent, std::string_view name) const override; Pin *findPin(std::string_view path_name) const override; @@ -266,9 +266,9 @@ protected: std::string &path_tail) const; bool visitMatches(const Instance *parent, const PatternMatch *pattern, - std::function - visit_tail) const; + const std::function + &visit_tail) const; bool visitPinTail(const Instance *instance, const PatternMatch *tail, PinSeq &matches) const; diff --git a/include/sta/Search.hh b/include/sta/Search.hh index ec1fbdbc9..47fb62477 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -24,23 +24,23 @@ #pragma once -#include #include +#include #include -#include "MinMax.hh" -#include "Transition.hh" +#include "Delay.hh" +#include "GraphClass.hh" #include "LibertyClass.hh" +#include "MinMax.hh" #include "NetworkClass.hh" -#include "GraphClass.hh" -#include "Delay.hh" +#include "Path.hh" #include "SdcClass.hh" -#include "StaState.hh" #include "SearchClass.hh" #include "SearchPred.hh" -#include "VertexVisitor.hh" -#include "Path.hh" +#include "StaState.hh" #include "StringUtil.hh" +#include "Transition.hh" +#include "VertexVisitor.hh" namespace sta { @@ -696,7 +696,6 @@ protected: // Class for visiting fanin/fanout paths of a vertex. // This used by forward/backward search to find arrival/required path times. -// NOLINTNEXTLINE(misc-multiple-inheritance) class PathVisitor : public VertexVisitor, public StaState { diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index 47f29563b..9e3205b9f 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -25,14 +25,14 @@ #pragma once #include -#include #include +#include -#include "VectorMap.hh" -#include "MinMaxValues.hh" #include "Delay.hh" -#include "NetworkClass.hh" #include "GraphClass.hh" +#include "MinMaxValues.hh" +#include "NetworkClass.hh" +#include "VectorMap.hh" namespace sta { diff --git a/include/sta/SearchPred.hh b/include/sta/SearchPred.hh index ab6b8dd33..9bcdf9b89 100644 --- a/include/sta/SearchPred.hh +++ b/include/sta/SearchPred.hh @@ -24,9 +24,9 @@ #pragma once -#include "NetworkClass.hh" #include "GraphClass.hh" #include "LibertyClass.hh" +#include "NetworkClass.hh" #include "StaState.hh" namespace sta { diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 9da0e8307..a8e80c552 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -24,27 +24,27 @@ #pragma once -#include +#include #include #include -#include +#include -#include "StringUtil.hh" +#include "ArcDelayCalc.hh" +#include "CircuitSim.hh" +#include "GraphClass.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "SdcClass.hh" -#include "Scene.hh" -#include "GraphClass.hh" #include "ParasiticsClass.hh" -#include "StaState.hh" -#include "VertexVisitor.hh" -#include "SearchClass.hh" #include "PowerClass.hh" -#include "ArcDelayCalc.hh" -#include "CircuitSim.hh" -#include "Variables.hh" #include "Property.hh" #include "RiseFallMinMaxDelay.hh" +#include "Scene.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "StaState.hh" +#include "StringUtil.hh" +#include "Variables.hh" +#include "VertexVisitor.hh" struct Tcl_Interp; @@ -267,7 +267,7 @@ public: const LibertyCell *cell, const Port *port, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews &from_slews, const LibertyPort *to_port, const RiseFallBoth *rf, const MinMaxAll *min_max, diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index 7d5fdbf04..35a8cda92 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -28,10 +28,10 @@ #include #include #include +#include #include #include #include -#include #include "Machine.hh" // __attribute__ diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 46984d495..40855a75c 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -30,10 +30,10 @@ #include #include -#include "MinMax.hh" -#include "Transition.hh" #include "LibertyClass.hh" +#include "MinMax.hh" #include "TimingModel.hh" +#include "Transition.hh" #include "Variables.hh" namespace sta { diff --git a/include/sta/TclTypeHelpers.hh b/include/sta/TclTypeHelpers.hh index 995b425f1..628ee099b 100644 --- a/include/sta/TclTypeHelpers.hh +++ b/include/sta/TclTypeHelpers.hh @@ -30,17 +30,17 @@ namespace sta { #if TCL_MAJOR_VERSION < 9 - typedef int Tcl_Size; + using Tcl_Size = int ; #endif StringSeq -tclListStringSeq(Tcl_Obj *const source, +tclListStringSeq(Tcl_Obj *source, Tcl_Interp *interp); StringSeq * -tclListStringSeqPtr(Tcl_Obj *const source, +tclListStringSeqPtr(Tcl_Obj *source, Tcl_Interp *interp); StringSet * -tclListStringSet(Tcl_Obj *const source, +tclListStringSet(Tcl_Obj *source, Tcl_Interp *interp); void diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index d95538301..4f9d54454 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -24,14 +24,14 @@ #pragma once +#include #include #include #include -#include -#include "Transition.hh" #include "Delay.hh" #include "LibertyClass.hh" +#include "Transition.hh" namespace sta { diff --git a/include/sta/Variables.hh b/include/sta/Variables.hh index a5bf2a1f7..90c9fad20 100644 --- a/include/sta/Variables.hh +++ b/include/sta/Variables.hh @@ -34,7 +34,6 @@ enum class CrprMode { same_pin, same_transition }; class Variables { public: - Variables(); // TCL variable sta_crpr_enabled. bool crprEnabled() const { return crpr_enabled_; } void setCrprEnabled(bool enabled); @@ -78,23 +77,23 @@ public: PocvMode pocvMode() const { return pocv_mode_; } void setPocvMode(PocvMode mode); float pocvQuantile() const { return pocv_quantile_; } - void setPocvQuantile(float quartile); + void setPocvQuantile(float quantile); private: - bool crpr_enabled_; - CrprMode crpr_mode_; - bool propagate_gated_clock_enable_; - bool preset_clr_arcs_enabled_; - bool cond_default_arcs_enabled_; - bool bidirect_inst_paths_enabled_; - bool recovery_removal_checks_enabled_; - bool gated_clk_checks_enabled_; - bool clk_thru_tristate_enabled_; - bool dynamic_loop_breaking_; - bool propagate_all_clks_; - bool use_default_arrival_clock_; - PocvMode pocv_mode_; - float pocv_quantile_; + bool crpr_enabled_{true}; + CrprMode crpr_mode_{CrprMode::same_pin}; + bool propagate_gated_clock_enable_{true}; + bool preset_clr_arcs_enabled_{false}; + bool cond_default_arcs_enabled_{true}; + bool bidirect_inst_paths_enabled_{false}; + bool recovery_removal_checks_enabled_{true}; + bool gated_clk_checks_enabled_{true}; + bool clk_thru_tristate_enabled_{false}; + bool dynamic_loop_breaking_{false}; + bool propagate_all_clks_{false}; + bool use_default_arrival_clock_{false}; + PocvMode pocv_mode_{PocvMode::scalar}; + float pocv_quantile_{3.0}; }; } // namespace sta diff --git a/include/sta/VectorMap.hh b/include/sta/VectorMap.hh index 2273e3ab6..5fa6990cc 100644 --- a/include/sta/VectorMap.hh +++ b/include/sta/VectorMap.hh @@ -24,12 +24,12 @@ #pragma once -#include #include -#include #include #include #include +#include +#include namespace sta { diff --git a/include/sta/VerilogNamespace.hh b/include/sta/VerilogNamespace.hh index c004f6291..441b6ba24 100644 --- a/include/sta/VerilogNamespace.hh +++ b/include/sta/VerilogNamespace.hh @@ -39,12 +39,12 @@ std::string portVerilogName(std::string_view sta_name); std::string -moduleVerilogToSta(std::string_view sta_name); +moduleVerilogToSta(std::string_view module_name); std::string -instanceVerilogToSta(std::string_view sta_name); +instanceVerilogToSta(std::string_view inst_name); std::string -netVerilogToSta(std::string_view sta_name); +netVerilogToSta(std::string_view net_name); std::string -portVerilogToSta(std::string_view sta_name); +portVerilogToSta(std::string_view port_name); } // namespace sta diff --git a/include/sta/VerilogReader.hh b/include/sta/VerilogReader.hh index 4a94269ce..8d88e9e76 100644 --- a/include/sta/VerilogReader.hh +++ b/include/sta/VerilogReader.hh @@ -24,15 +24,15 @@ #pragma once +#include #include #include #include -#include #include "Format.hh" +#include "NetworkClass.hh" #include "Report.hh" #include "StringUtil.hh" -#include "NetworkClass.hh" namespace sta { @@ -104,12 +104,12 @@ public: ~VerilogReader(); bool read(std::string_view filename); - void makeModule(std::string_view module_name, + void makeModule(std::string_view module_vname, VerilogNetSeq *ports, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, int line); - void makeModule(std::string_view module_name, + void makeModule(std::string_view module_vname, VerilogStmtSeq *port_dcls, VerilogStmtSeq *stmts, VerilogAttrStmtSeq *attr_stmts, @@ -122,8 +122,8 @@ public: VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, int line); - VerilogDclArg *makeDclArg(std::string_view net_name); - VerilogDclArg*makeDclArg(VerilogAssign *assign); + VerilogDclArg *makeDclArg(std::string_view net_vname); + VerilogDclArg *makeDclArg(VerilogAssign *assign); VerilogDclBus *makeDclBus(PortDirection *dir, int from_index, int to_index, @@ -136,8 +136,8 @@ public: VerilogDclArgSeq *args, VerilogAttrStmtSeq *attr_stmts, int line); - VerilogInst *makeModuleInst(std::string_view module_name, - std::string_view inst_name, + VerilogInst *makeModuleInst(std::string_view module_vname, + std::string_view inst_vname, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, int line); @@ -146,17 +146,17 @@ public: int line); VerilogNetScalar *makeNetScalar(std::string_view name); VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string_view port_vname); - VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string_view port_name, - std::string_view net_name); - VerilogNetPortRef *makeNetNamedPortRefBitSelect(std::string_view port_name, - std::string_view bus_name, + VerilogNetPortRef *makeNetNamedPortRefScalarNet(std::string_view port_vname, + std::string_view net_vname); + VerilogNetPortRef *makeNetNamedPortRefBitSelect(std::string_view port_vname, + std::string_view bus_vname, int index); - VerilogNetPortRef *makeNetNamedPortRefScalar(std::string_view port_name, + VerilogNetPortRef *makeNetNamedPortRefScalar(std::string_view port_vname, VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefBit(std::string_view port_name, + VerilogNetPortRef *makeNetNamedPortRefBit(std::string_view port_vname, int index, VerilogNet *net); - VerilogNetPortRef *makeNetNamedPortRefPart(std::string_view port_name, + VerilogNetPortRef *makeNetNamedPortRefPart(std::string_view port_vname, int from_index, int to_index, VerilogNet *net); @@ -306,8 +306,8 @@ protected: Debug *debug_; NetworkReader *network_; - Library *library_; - int black_box_index_; + Library *library_{nullptr}; + int black_box_index_{0}; VerilogModuleMap module_map_; VerilogErrorSeq link_errors_; const std::string zero_net_name_; diff --git a/include/sta/VertexVisitor.hh b/include/sta/VertexVisitor.hh index c7c8e9812..e7aa7d363 100644 --- a/include/sta/VertexVisitor.hh +++ b/include/sta/VertexVisitor.hh @@ -24,8 +24,8 @@ #pragma once -#include "NetworkClass.hh" #include "GraphClass.hh" +#include "NetworkClass.hh" namespace sta { diff --git a/include/sta/VisitPathEnds.hh b/include/sta/VisitPathEnds.hh index ad52fbf71..8ac723f51 100644 --- a/include/sta/VisitPathEnds.hh +++ b/include/sta/VisitPathEnds.hh @@ -24,8 +24,8 @@ #pragma once -#include "SdcClass.hh" #include "GraphClass.hh" +#include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index 3273cb694..77b335a1d 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -28,11 +28,11 @@ #include #include +#include "ConcreteNetwork.hh" #include "ContainerHelpers.hh" +#include "ParseBus.hh" #include "PatternMatch.hh" #include "PortDirection.hh" -#include "ParseBus.hh" -#include "ConcreteNetwork.hh" namespace sta { @@ -44,9 +44,7 @@ ConcreteLibrary::ConcreteLibrary(std::string_view name, name_(name), id_(ConcreteNetwork::nextObjectId()), filename_(filename), - is_liberty_(is_liberty), - bus_brkt_left_('['), - bus_brkt_right_(']') + is_liberty_(is_liberty) { } @@ -125,9 +123,6 @@ ConcreteCell::ConcreteCell(std::string_view name, id_(ConcreteNetwork::nextObjectId()), filename_(filename), library_(library), - liberty_cell_(nullptr), - ext_cell_(nullptr), - port_bit_count_(0), is_leaf_(is_leaf) { } @@ -234,7 +229,7 @@ ConcreteCell::makeBusPortBit(ConcretePort *bus_port, } ConcretePort * -ConcreteCell::makePort(std::string bit_name, +ConcreteCell::makePort(std::string_view bit_name, int bit_index) { ConcretePort *port = new ConcretePort(bit_name, false, @@ -353,7 +348,7 @@ BusPort::addBusBit(ConcretePort *port, void ConcreteCell::groupBusPorts(char bus_brkt_left, char bus_brkt_right, - std::function port_msb_first) + const std::function &port_msb_first) { const char bus_brkts_left[2]{bus_brkt_left, '\0'}; const char bus_brkts_right[2]{bus_brkt_right, '\0'}; @@ -415,15 +410,11 @@ ConcretePort::ConcretePort(std::string_view name, id_(ConcreteNetwork::nextObjectId()), cell_(cell), direction_(PortDirection::unknown()), - liberty_port_(nullptr), - ext_port_(nullptr), - pin_index_(-1), is_bundle_(is_bundle), is_bus_(is_bus), from_index_(from_index), to_index_(to_index), - member_ports_(member_ports), - bundle_port_(nullptr) + member_ports_(member_ports) { } @@ -575,9 +566,7 @@ ConcretePort::memberIterator() const ConcreteCellPortBitIterator::ConcreteCellPortBitIterator(const ConcreteCell* cell) : ports_(cell->ports_), - port_iter_(ports_.begin()), - member_iter_(nullptr), - next_(nullptr) + port_iter_(ports_.begin()) { findNext(); } diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc index dba9d2866..df99ff9d7 100644 --- a/network/ConcreteNetwork.cc +++ b/network/ConcreteNetwork.cc @@ -24,15 +24,16 @@ #include "ConcreteNetwork.hh" +#include #include #include -#include "PatternMatch.hh" -#include "Report.hh" -#include "Liberty.hh" -#include "PortDirection.hh" #include "ConcreteLibrary.hh" +#include "Liberty.hh" #include "Network.hh" +#include "PatternMatch.hh" +#include "PortDirection.hh" +#include "Report.hh" namespace sta { @@ -105,13 +106,12 @@ class ConcreteInstanceNetIterator : public InstanceNetIterator ConcreteInstanceNetMap *nets_; ConcreteInstanceNetMap::iterator iter_; - ConcreteNet *next_; + ConcreteNet *next_{nullptr}; }; ConcreteInstanceNetIterator:: ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets): - nets_(nets), - next_(nullptr) + nets_(nets) { if (nets) { iter_ = nets->begin(); @@ -161,7 +161,7 @@ class ConcreteInstancePinIterator : public InstancePinIterator const ConcretePinSeq &pins_; int pin_count_; - int pin_index_; + int pin_index_{0}; ConcretePin *next_; }; @@ -169,8 +169,7 @@ ConcreteInstancePinIterator:: ConcreteInstancePinIterator(const ConcreteInstance *inst, int pin_count) : pins_(inst->pins_), - pin_count_(pin_count), - pin_index_(0) + pin_count_(pin_count) { findNext(); } @@ -271,24 +270,31 @@ ObjectId ConcreteNetwork::object_id_ = 0; ConcreteNetwork::ConcreteNetwork() : NetworkReader(), - top_instance_(nullptr), - constant_nets_{NetSet(this), NetSet(this)}, link_func_(nullptr) { } ConcreteNetwork::~ConcreteNetwork() { - clear(); + // Cannot call virtual functions in destructor. + clearImpl(); } void -ConcreteNetwork::clear() +ConcreteNetwork::clearImpl() { - deleteTopInstance(); - deleteCellNetworkViews(); + if (top_instance_) + deleteInstanceImpl(top_instance_); + top_instance_ = nullptr; + deleteCellNetworkViewsImpl(); deleteContents(library_seq_); library_map_.clear(); +} + +void +ConcreteNetwork::clear() +{ + clearImpl(); Network::clear(); } @@ -303,6 +309,12 @@ ConcreteNetwork::deleteTopInstance() void ConcreteNetwork::deleteCellNetworkViews() +{ + deleteCellNetworkViewsImpl(); +} + +void +ConcreteNetwork::deleteCellNetworkViewsImpl() { for (auto [cell, view] : cell_network_view_map_) { if (view) @@ -361,7 +373,6 @@ class ConcreteLibertyLibraryIterator : public Iterator { public: ConcreteLibertyLibraryIterator(const ConcreteNetwork *network); - virtual ~ConcreteLibertyLibraryIterator(); bool hasNext() override; LibertyLibrary *next() override; @@ -370,22 +381,17 @@ class ConcreteLibertyLibraryIterator : public Iterator const ConcreteLibrarySeq &libs_; ConcreteLibrarySeq::const_iterator iter_; - LibertyLibrary *next_; + LibertyLibrary *next_{nullptr}; }; ConcreteLibertyLibraryIterator:: ConcreteLibertyLibraryIterator(const ConcreteNetwork *network): libs_(network->library_seq_), - iter_(libs_.begin()), - next_(nullptr) + iter_(libs_.begin()) { findNext(); } -ConcreteLibertyLibraryIterator::~ConcreteLibertyLibraryIterator() -{ -} - bool ConcreteLibertyLibraryIterator::hasNext() { @@ -462,7 +468,7 @@ ConcreteNetwork::deleteLibrary(Library *library) { ConcreteLibrary *clib = reinterpret_cast(library); library_map_.erase(clib->name()); - library_seq_.erase(std::find(library_seq_.begin(), library_seq_.end(), clib)); + library_seq_.erase(std::ranges::find(library_seq_, clib)); delete clib; } @@ -721,7 +727,7 @@ class ConcreteCellPortIterator1 : public CellPortIterator { public: ConcreteCellPortIterator1(const ConcreteCell *cell); - ~ConcreteCellPortIterator1(); + ~ConcreteCellPortIterator1() override; bool hasNext() override { return iter_->hasNext(); } Port *next() override; @@ -758,7 +764,7 @@ class ConcreteCellPortBitIterator1 : public CellPortIterator { public: ConcreteCellPortBitIterator1(const ConcreteCell *cell); - ~ConcreteCellPortBitIterator1(); + ~ConcreteCellPortBitIterator1() override; bool hasNext() override { return iter_->hasNext(); } Port *next() override; @@ -905,19 +911,18 @@ class ConcretePortMemberIterator1 : public PortMemberIterator { public: ConcretePortMemberIterator1(const ConcretePort *port); - ~ConcretePortMemberIterator1(); + ~ConcretePortMemberIterator1() override; bool hasNext() override; Port *next() override; private: ConcretePortMemberIterator *iter_; - ConcretePort *next_; + ConcretePort *next_{nullptr}; }; ConcretePortMemberIterator1::ConcretePortMemberIterator1(const ConcretePort * port) : - iter_(port->memberIterator()), - next_(nullptr) + iter_(port->memberIterator()) { } @@ -1051,7 +1056,7 @@ ConcreteNetwork::findInstNetsMatching(const Instance *instance, { const ConcreteInstance *inst = reinterpret_cast(instance); - return inst->findNetsMatching(pattern, matches); + inst->findNetsMatching(pattern, matches); } //////////////////////////////////////////////////////////////// @@ -1191,13 +1196,13 @@ ConcreteNetwork::instance(const Net *net) const bool ConcreteNetwork::isPower(const Net *net) const { - return constant_nets_[int(LogicValue::one)].contains(const_cast(net)); + return constant_nets_[static_cast(LogicValue::one)].contains(const_cast(net)); } bool ConcreteNetwork::isGround(const Net *net) const { - return constant_nets_[int(LogicValue::zero)].contains(const_cast(net)); + return constant_nets_[static_cast(LogicValue::zero)].contains(const_cast(net)); } NetPinIterator * @@ -1308,6 +1313,12 @@ ConcreteNetwork::replaceCell(Instance *inst, void ConcreteNetwork::deleteInstance(Instance *inst) +{ + deleteInstanceImpl(inst); +} + +void +ConcreteNetwork::deleteInstanceImpl(Instance *inst) { ConcreteInstance *cinst = reinterpret_cast(inst); ConcreteInstanceNetMap *nets = cinst->nets_; @@ -1504,8 +1515,7 @@ ConcreteNetwork::deletePin(Pin *pin) ConcreteNet *cnet = cpin->net(); if (cnet) disconnectNetPin(cnet, cpin); - ConcreteInstance *cinst = - reinterpret_cast(cpin->instance()); + ConcreteInstance *cinst = cpin->instance(); if (cinst) cinst->deletePin(cpin); delete cpin; @@ -1533,16 +1543,15 @@ ConcreteNetwork::deleteNet(Net *net) pin->net_ = nullptr; } - constant_nets_[int(LogicValue::zero)].erase(net); - constant_nets_[int(LogicValue::one)].erase(net); + constant_nets_[static_cast(LogicValue::zero)].erase(net); + constant_nets_[static_cast(LogicValue::one)].erase(net); PinSet *drvrs = findKey(net_drvr_pin_map_, net); if (drvrs) { delete drvrs; net_drvr_pin_map_.erase(net); } - ConcreteInstance *cinst = - reinterpret_cast(cnet->instance()); + ConcreteInstance *cinst = cnet->instance(); cinst->deleteNet(cnet); delete cnet; } @@ -1550,8 +1559,8 @@ ConcreteNetwork::deleteNet(Net *net) void ConcreteNetwork::clearConstantNets() { - constant_nets_[int(LogicValue::zero)].clear(); - constant_nets_[int(LogicValue::one)].clear(); + constant_nets_[static_cast(LogicValue::zero)].clear(); + constant_nets_[static_cast(LogicValue::one)].clear(); } void @@ -1560,15 +1569,15 @@ ConcreteNetwork::addConstantNet(Net *net, { if (value == LogicValue::zero || value == LogicValue::one) - constant_nets_[int(value)].insert(net); + constant_nets_[static_cast(value)].insert(net); } ConstantPinIterator * ConcreteNetwork::constantPinIterator() { return new NetworkConstantPinIterator(this, - constant_nets_[int(LogicValue::zero)], - constant_nets_[int(LogicValue::one)]); + constant_nets_[static_cast(LogicValue::zero)], + constant_nets_[static_cast(LogicValue::one)]); } //////////////////////////////////////////////////////////////// @@ -1617,9 +1626,7 @@ ConcreteInstance::ConcreteInstance(std::string_view name, name_(name), id_(ConcreteNetwork::nextObjectId()), cell_(cell), - parent_(parent), - children_(nullptr), - nets_(nullptr) + parent_(parent) { initPins(); } @@ -1738,13 +1745,13 @@ ConcreteInstance::addChild(ConcreteInstance *child) { if (children_ == nullptr) children_ = new ConcreteInstanceChildMap; - (*children_)[child->name().data()] = child; + (*children_)[child->name()] = child; } void ConcreteInstance::deleteChild(ConcreteInstance *child) { - children_->erase(child->name().data()); + children_->erase(child->name()); } void @@ -1769,7 +1776,7 @@ ConcreteInstance::addNet(ConcreteNet *net) { if (nets_ == nullptr) nets_ = new ConcreteInstanceNetMap; - (*nets_)[net->name().data()] = net; + (*nets_)[net->name()] = net; } void @@ -1778,13 +1785,13 @@ ConcreteInstance::addNet(std::string_view, { if (nets_ == nullptr) nets_ = new ConcreteInstanceNetMap; - (*nets_)[net->name().data()] = net; + (*nets_)[net->name()] = net; } void ConcreteInstance::deleteNet(ConcreteNet *net) { - nets_->erase(net->name().data()); + nets_->erase(net->name()); } void @@ -1801,15 +1808,11 @@ ConcretePin::ConcretePin(ConcreteInstance *instance, instance_(instance), port_(port), net_(net), - term_(nullptr), - id_(ConcreteNetwork::nextObjectId()), - net_next_(nullptr), - net_prev_(nullptr), - vertex_id_(vertex_id_null) + id_(ConcreteNetwork::nextObjectId()) { } -std::string_view +const std::string& ConcretePin::name() const { return port_->name(); @@ -1823,12 +1826,11 @@ ConcretePin::setVertexId(VertexId id) //////////////////////////////////////////////////////////////// -std::string_view +const std::string & ConcreteTerm::name() const { ConcretePin *cpin = reinterpret_cast(pin_); - const ConcretePort *cport = - reinterpret_cast(cpin->port()); + const ConcretePort *cport = cpin->port(); return cport->name(); } @@ -1836,8 +1838,7 @@ ConcreteTerm::ConcreteTerm(ConcretePin *pin, ConcreteNet *net) : pin_(pin), net_(net), - id_(ConcreteNetwork::nextObjectId()), - net_next_(nullptr) + id_(ConcreteNetwork::nextObjectId()) { } @@ -1847,10 +1848,7 @@ ConcreteNet::ConcreteNet(std::string_view name, ConcreteInstance *instance) : name_(name), id_(ConcreteNetwork::nextObjectId()), - instance_(instance), - pins_(nullptr), - terms_(nullptr), - merged_into_(nullptr) + instance_(instance) { } diff --git a/network/HpinDrvrLoad.cc b/network/HpinDrvrLoad.cc index 032b0ff35..8aaf57b4a 100644 --- a/network/HpinDrvrLoad.cc +++ b/network/HpinDrvrLoad.cc @@ -32,7 +32,7 @@ namespace sta { -typedef std::set HpinDrvrLoads; +using HpinDrvrLoads = std::set; static void visitPinsAboveNet2(const Pin *hpin, @@ -52,8 +52,8 @@ visitPinsBelowNet2(const Pin *hpin, PinSet *hpin_path, const Network *network); static void -visitHpinDrvrLoads(HpinDrvrLoads drvrs, - HpinDrvrLoads loads, +visitHpinDrvrLoads(HpinDrvrLoads &drvrs, + HpinDrvrLoads &loads, HpinDrvrLoadVisitor *visitor); void @@ -249,8 +249,8 @@ visitPinsBelowNet2(const Pin *hpin, } static void -visitHpinDrvrLoads(HpinDrvrLoads drvrs, - HpinDrvrLoads loads, +visitHpinDrvrLoads(HpinDrvrLoads &drvrs, + HpinDrvrLoads &loads, HpinDrvrLoadVisitor *visitor) { for (HpinDrvrLoad *drvr : drvrs) { diff --git a/network/Network.cc b/network/Network.cc index 0252c801c..d096d842a 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -29,22 +29,15 @@ #include #include "ContainerHelpers.hh" -#include "StringUtil.hh" -#include "PatternMatch.hh" #include "Liberty.hh" +#include "ParseBus.hh" +#include "PatternMatch.hh" #include "PortDirection.hh" #include "Scene.hh" -#include "ParseBus.hh" +#include "StringUtil.hh" namespace sta { -Network::Network() : - default_liberty_(nullptr), - divider_('/'), - escape_('\\') -{ -} - Network::~Network() { deleteContents(net_drvr_pin_map_); @@ -264,7 +257,7 @@ Network::pathName(const Instance *instance) const InstanceSeq inst_path; path(instance, inst_path); std::string path_name; - while (inst_path.size()) { + while (!inst_path.empty()) { const Instance *inst = inst_path.back(); path_name += name(inst); inst_path.pop_back(); @@ -1184,7 +1177,7 @@ Network::setPathEscape(char escape) //////////////////////////////////////////////////////////////// -typedef std::vector InstanceChildIteratorSeq; +using InstanceChildIteratorSeq = std::vector; class LeafInstanceIterator1 : public LeafInstanceIterator { @@ -1200,15 +1193,14 @@ class LeafInstanceIterator1 : public LeafInstanceIterator const Network *network_; InstanceChildIteratorSeq pending_child_iters_; InstanceChildIterator *child_iter_; - Instance *next_; + Instance *next_{nullptr}; }; LeafInstanceIterator1::LeafInstanceIterator1(const Instance *inst, const Network *network) : network_(network), - child_iter_(network->childIterator(inst)), - next_(nullptr) + child_iter_(network->childIterator(inst)) { pending_child_iters_.reserve(8); nextInst(); @@ -1632,7 +1624,6 @@ Network::pathNameLast(std::string_view path_name, char divider = pathDivider(); size_t div_pos = path_name.rfind(divider); - size_t path_end = path_name.size(); while (div_pos > 0) { if (div_pos == std::string_view::npos) return; @@ -1642,27 +1633,18 @@ Network::pathNameLast(std::string_view path_name, last = path_name.substr(div_pos + 1); return; } - path_end = div_pos - 1; - div_pos = path_name.rfind(divider, path_end); + div_pos = path_name.rfind(divider, div_pos - 1); } } //////////////////////////////////////////////////////////////// -NetworkEdit::NetworkEdit() : - Network() -{ -} - -//////////////////////////////////////////////////////////////// - NetworkConstantPinIterator:: NetworkConstantPinIterator(const Network *network, NetSet &zero_nets, NetSet &one_nets) : ConstantPinIterator(), - network_(network), - constant_pins_{PinSet(network), PinSet(network)} + network_(network) { findConstantPins(zero_nets, constant_pins_[0]); findConstantPins(one_nets, constant_pins_[1]); @@ -1687,7 +1669,7 @@ NetworkConstantPinIterator::findConstantPins(NetSet &nets, bool NetworkConstantPinIterator::hasNext() { - if (pin_iter_ != constant_pins_[(int)value_].end()) + if (pin_iter_ != constant_pins_[static_cast(value_)].end()) return true; else if (value_ == LogicValue::zero) { value_ = LogicValue::one; @@ -1818,8 +1800,8 @@ visitPinsBelowNet1(const Pin *hpin, } static void -visitDrvrLoads(PinSet drvrs, - PinSet loads, +visitDrvrLoads(PinSet &drvrs, + PinSet &loads, HierPinThruVisitor *visitor) { for (const Pin *drvr : drvrs) { @@ -1921,8 +1903,8 @@ visitDrvrLoadsThruNet(const Net *net, char logicValueString(LogicValue value) { - static char str[] = "01X^v"; - return str[int(value)]; + static char names[] = "01X^v"; + return names[static_cast(value)]; } //////////////////////////////////////////////////////////////// diff --git a/network/Network.i b/network/Network.i index 6af7c8a36..f36a91a04 100644 --- a/network/Network.i +++ b/network/Network.i @@ -28,8 +28,10 @@ %{ #include "Network.hh" -#include "StringUtil.hh" + #include + +#include "StringUtil.hh" %} //////////////////////////////////////////////////////////////// diff --git a/network/NetworkCmp.cc b/network/NetworkCmp.cc index 2d75bb01f..2d0db2dbf 100644 --- a/network/NetworkCmp.cc +++ b/network/NetworkCmp.cc @@ -26,9 +26,9 @@ #include -#include "StringUtil.hh" #include "Liberty.hh" #include "Network.hh" +#include "StringUtil.hh" namespace sta { diff --git a/network/ParseBus.cc b/network/ParseBus.cc index 16300d69b..8205dead7 100644 --- a/network/ParseBus.cc +++ b/network/ParseBus.cc @@ -88,7 +88,7 @@ parseBusName(std::string_view name, size_t left = name.rfind(brkt_left_ch); if (left != std::string_view::npos) { is_bus = true; - bus_name.append(name.data(), left); + bus_name.append(name.substr(0, left)); // Simple bus subscript. index = std::stoi(std::string(name.substr(left + 1))); } @@ -142,7 +142,7 @@ parseBusName(std::string_view name, size_t left = name.rfind(brkt_left_ch); if (left != std::string_view::npos) { is_bus = true; - bus_name.append(name.data(), left); + bus_name.append(name.substr(0, left)); // Check for bus range. size_t range = name.find(':', left); if (range != std::string_view::npos) { diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc index 50f95fc0b..0542f95aa 100644 --- a/network/SdcNetwork.cc +++ b/network/SdcNetwork.cc @@ -24,14 +24,13 @@ #include "SdcNetwork.hh" -#include "StringUtil.hh" -#include "PatternMatch.hh" #include "ParseBus.hh" +#include "PatternMatch.hh" +#include "StringUtil.hh" namespace sta { NetworkNameAdapter::NetworkNameAdapter(Network *network) : - NetworkEdit(), network_(network), network_edit_(dynamic_cast(network)) { @@ -822,9 +821,8 @@ SdcNetwork::findInstancesMatching1(const Instance *context, InstanceSeq &matches) const { visitMatches(context, pattern, - [&](const Instance *instance, - const PatternMatch *tail) - { + [&] (const Instance *instance, + const PatternMatch *tail) { size_t match_count = matches.size(); network_->findChildrenMatching(instance, tail, matches); return matches.size() != match_count; @@ -1193,7 +1191,7 @@ SdcNetwork::visitMatches(const Instance *parent, const PatternMatch *pattern, const std::function - visit_tail) const + &visit_tail) const { int divider_count, path_length; scanPath(pattern->pattern(), divider_count, path_length); diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc index e00946882..ed63cd3e6 100644 --- a/network/VerilogNamespace.cc +++ b/network/VerilogNamespace.cc @@ -26,8 +26,8 @@ #include -#include "StringUtil.hh" #include "ParseBus.hh" +#include "StringUtil.hh" namespace sta { @@ -38,7 +38,7 @@ staToVerilog(std::string_view sta_name); static std::string staToVerilog2(std::string_view sta_name); static std::string -verilogToSta(const std::string_view verilog_name); +verilogToSta(std::string_view verilog_name); std::string cellVerilogName(std::string_view sta_name) diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 27d74bf6f..e015d5e24 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -26,18 +26,18 @@ #include // max -#include "Report.hh" +#include "ConcreteParasiticsPvt.hh" #include "Debug.hh" #include "Error.hh" -#include "Mutex.hh" +#include "Liberty.hh" #include "MinMax.hh" +#include "Mutex.hh" #include "Network.hh" -#include "Wireload.hh" -#include "Liberty.hh" -#include "Sdc.hh" #include "Parasitics.hh" -#include "ConcreteParasiticsPvt.hh" +#include "Report.hh" #include "Scene.hh" +#include "Sdc.hh" +#include "Wireload.hh" // Multiple inheritance is used to share elmore and pi model base // classes, but care is taken to make sure there are no loops in the @@ -45,10 +45,6 @@ namespace sta { -ConcreteParasitic::~ConcreteParasitic() -{ -} - bool ConcreteParasitic::isPiElmore() const { @@ -138,8 +134,7 @@ ConcretePi::ConcretePi(float c2, float c1) : c2_(c2), rpi_(rpi), - c1_(c1), - is_reduced_(false) + c1_(c1) { } @@ -257,12 +252,6 @@ ConcretePiElmore::unannotatedLoads(const Pin *drvr_pin, //////////////////////////////////////////////////////////////// -ConcretePoleResidue::ConcretePoleResidue() : - poles_(nullptr), - residues_(nullptr) -{ -} - ConcretePoleResidue::~ConcretePoleResidue() { delete poles_; @@ -380,7 +369,7 @@ ConcretePiPoleResidue::unannotatedLoads(const Pin *drvr_pin, //////////////////////////////////////////////////////////////// ConcreteParasiticNode::ConcreteParasiticNode(const Net *net, - int id, + uint32_t id, bool is_external) : is_net_(true), is_external_(is_external), @@ -449,7 +438,7 @@ ConcreteParasiticNode::net(const Network *network) const //////////////////////////////////////////////////////////////// -ConcreteParasiticDevice::ConcreteParasiticDevice(size_t id, +ConcreteParasiticDevice::ConcreteParasiticDevice(uint32_t id, float value, ConcreteParasiticNode *node1, ConcreteParasiticNode *node2) : @@ -470,7 +459,7 @@ ConcreteParasiticDevice::replaceNode(ConcreteParasiticNode *from_node, node2_ = to_node; } -ConcreteParasiticResistor::ConcreteParasiticResistor(size_t id, +ConcreteParasiticResistor::ConcreteParasiticResistor(uint32_t id, float value, ConcreteParasiticNode *node1, ConcreteParasiticNode *node2) : @@ -478,7 +467,7 @@ ConcreteParasiticResistor::ConcreteParasiticResistor(size_t id, { } -ConcreteParasiticCapacitor::ConcreteParasiticCapacitor(size_t id, +ConcreteParasiticCapacitor::ConcreteParasiticCapacitor(uint32_t id, float value, ConcreteParasiticNode *node1, ConcreteParasiticNode *node2) : @@ -494,15 +483,15 @@ ConcreteParasiticNetwork::ConcreteParasiticNetwork(const Net *net, net_(net), sub_nodes_(network), pin_nodes_(network), - max_node_id_(0), includes_pin_caps_(includes_pin_caps) { } -ConcreteParasiticNetwork::ConcreteParasiticNetwork(ConcreteParasiticNetwork &¶sitic): +ConcreteParasiticNetwork::ConcreteParasiticNetwork(ConcreteParasiticNetwork &¶sitic) + noexcept : net_(parasitic.net_), - sub_nodes_(parasitic.sub_nodes_), - pin_nodes_(parasitic.pin_nodes_), + sub_nodes_(std::move(parasitic.sub_nodes_)), + pin_nodes_(std::move(parasitic.pin_nodes_)), max_node_id_(parasitic.max_node_id_), includes_pin_caps_(parasitic.includes_pin_caps_) { @@ -586,7 +575,7 @@ ConcreteParasiticNetwork::capacitance() const ConcreteParasiticNode * ConcreteParasiticNetwork::findParasiticNode(const Net *net, - int id, + uint32_t id, const Network *) const { NetIdPair net_id(net, id); @@ -609,7 +598,7 @@ ConcreteParasiticNetwork::findParasiticNode(const Pin *pin) const ConcreteParasiticNode * ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, - int id, + uint32_t id, const Network *network) { ConcreteParasiticNode *node; @@ -620,7 +609,7 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, node = new ConcreteParasiticNode(net, id, network->highestNetAbove(net1) != net_); sub_nodes_[net_id] = node; if (net == net_) - max_node_id_ = std::max((int) max_node_id_, id); + max_node_id_ = std::max(max_node_id_, id); } else node = id_node->second; @@ -763,7 +752,7 @@ ConcreteParasitics::ConcreteParasitics(std::string_view name, ConcreteParasitics::~ConcreteParasitics() { - deleteParasitics(); + deleteParasiticsImpl(); } bool @@ -781,6 +770,12 @@ ConcreteParasitics::clear() void ConcreteParasitics::deleteParasitics() +{ + deleteParasiticsImpl(); +} + +void +ConcreteParasitics::deleteParasiticsImpl() { for (auto &[drvr, parasitics] : drvr_parasitic_map_) { for (size_t i = 0; i < min_max_rise_fall_count; i++) @@ -1209,7 +1204,7 @@ ConcreteParasitics::includesPinCaps(const Parasitic *parasitic) const ParasiticNode * ConcreteParasitics::findParasiticNode(Parasitic *parasitic, const Net *net, - int id, + uint32_t id, const Network *network) const { const ConcreteParasiticNetwork *cparasitic = @@ -1220,7 +1215,7 @@ ConcreteParasitics::findParasiticNode(Parasitic *parasitic, ParasiticNode * ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic, const Net *net, - int id, + uint32_t id, const Network *network) { ConcreteParasiticNetwork *cparasitic = @@ -1257,7 +1252,7 @@ ConcreteParasitics::incrCap(ParasiticNode *node, void ConcreteParasitics::makeCapacitor(Parasitic *parasitic, - size_t index, + uint32_t id, float cap, ParasiticNode *node1, ParasiticNode *node2) @@ -1265,7 +1260,7 @@ ConcreteParasitics::makeCapacitor(Parasitic *parasitic, ConcreteParasiticNode *cnode1 = static_cast(node1); ConcreteParasiticNode *cnode2 = static_cast(node2); ConcreteParasiticCapacitor *capacitor = - new ConcreteParasiticCapacitor(index, cap, cnode1, cnode2); + new ConcreteParasiticCapacitor(id, cap, cnode1, cnode2); ConcreteParasiticNetwork *cparasitic = static_cast(parasitic); cparasitic->addCapacitor(capacitor); @@ -1273,14 +1268,14 @@ ConcreteParasitics::makeCapacitor(Parasitic *parasitic, void ConcreteParasitics::makeResistor(Parasitic *parasitic, - size_t index, + uint32_t id, float res, ParasiticNode *node1, ParasiticNode *node2) { ConcreteParasiticNode *cnode1 = static_cast(node1); ConcreteParasiticNode *cnode2 = static_cast(node2); - ParasiticResistor *resistor = new ConcreteParasiticResistor(index, res, + ParasiticResistor *resistor = new ConcreteParasiticResistor(id, res, cnode1, cnode2); ConcreteParasiticNetwork *cparasitic = static_cast(parasitic); @@ -1363,7 +1358,7 @@ ConcreteParasitics::isExternal(const ParasiticNode *node) const //////////////////////////////////////////////////////////////// -size_t +uint32_t ConcreteParasitics::id(const ParasiticResistor *resistor) const { const ConcreteParasiticResistor *cresistor = @@ -1397,7 +1392,7 @@ ConcreteParasitics::node2(const ParasiticResistor *resistor) const //////////////////////////////////////////////////////////////// -size_t +uint32_t ConcreteParasitics::id(const ParasiticCapacitor *capacitor) const { const ConcreteParasiticCapacitor *ccapacitor = diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index e866e8e74..936609c17 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -50,10 +50,10 @@ using ConcreteParasiticNetworkMap = std::map + #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Sdc.hh" #include "Parasitics.hh" +#include "PortDirection.hh" +#include "Sdc.hh" +#include "Wireload.hh" namespace sta { @@ -205,8 +207,7 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, else { c1 = static_cast(y2 * y2 / y3); c2 = static_cast(y1 - y2 * y2 / y3); - if (c2 < 0.0) - c2 = 0.0; + c2 = std::max(c2, 0.0f); rpi = static_cast(-y3 * y3 / (y2 * y2 * y2)); } elmore_res = static_cast(res_fanout); @@ -215,28 +216,4 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, } } -#if 0 -static void -selectWireload(Network *network) -{ - // Look for a default wireload selection group. - WireloadSelection *selection; - float area = instanceArea(network->topInstance(), network); - Wireload *wireload = selection->findWireload(area); -} - -static float -instanceArea(Instance *inst, - Network *network) -{ - float area = 0.0; - LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); - while (network->hasNext(inst_iter)) { - Instance *leaf = network->next(inst_iter); - area += network->cell(leaf)->area(); - } - return area; -} -#endif - } // namespace sta diff --git a/parasitics/EstimateParasitics.hh b/parasitics/EstimateParasitics.hh index 97e6fa62c..f762198f1 100644 --- a/parasitics/EstimateParasitics.hh +++ b/parasitics/EstimateParasitics.hh @@ -24,11 +24,11 @@ #pragma once -#include "StaState.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "SdcClass.hh" #include "ParasiticsClass.hh" +#include "SdcClass.hh" +#include "StaState.hh" namespace sta { @@ -57,8 +57,8 @@ public: protected: void estimatePiElmoreBest(const Pin *drvr_pin, - float net_pin_cap, float wireload_cap, + float net_pin_cap, const RiseFall *rf, const Scene *scene, const MinMax *min_max, diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index 69187f959..086ad9b45 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -24,23 +24,22 @@ #include "Parasitics.hh" -#include "Error.hh" #include "Debug.hh" -#include "Units.hh" +#include "Error.hh" +#include "EstimateParasitics.hh" #include "Liberty.hh" -#include "Wireload.hh" #include "Network.hh" #include "PortDirection.hh" -#include "Sdc.hh" -#include "Scene.hh" #include "ReduceParasitics.hh" -#include "EstimateParasitics.hh" +#include "Scene.hh" +#include "Sdc.hh" +#include "Units.hh" +#include "Wireload.hh" namespace sta { Parasitics::Parasitics(StaState *sta) : - StaState(sta), - coupling_cap_factor_(1.0) + StaState(sta) { } diff --git a/parasitics/ReduceParasitics.cc b/parasitics/ReduceParasitics.cc index 0f56860fd..231cdebae 100644 --- a/parasitics/ReduceParasitics.cc +++ b/parasitics/ReduceParasitics.cc @@ -28,21 +28,21 @@ #include #include -#include "Error.hh" #include "Debug.hh" -#include "MinMax.hh" +#include "Error.hh" #include "Liberty.hh" +#include "MinMax.hh" #include "Network.hh" -#include "Sdc.hh" -#include "Scene.hh" #include "Parasitics.hh" +#include "Scene.hh" +#include "Sdc.hh" namespace sta { -typedef std::map ParasiticNodeValueMap; -typedef std::map ResistorCurrentMap; -typedef std::set ParasiticResistorSet; -typedef std::set ParasiticNodeSet; +using ParasiticNodeValueMap = std::map; +using ResistorCurrentMap = std::map; +using ParasiticResistorSet = std::set; +using ParasiticNodeSet = std::set; class ReduceToPi : public StaState { @@ -82,26 +82,21 @@ class ReduceToPi : public StaState Parasitics *parasitics_; bool includes_pin_caps_; - float coupling_cap_multiplier_; - const RiseFall *rf_; - const Scene *scene_; - const MinMax *min_max_; + float coupling_cap_multiplier_ {1.0}; + const RiseFall *rf_ {nullptr}; + const Scene *scene_ {nullptr}; + const MinMax *min_max_ {nullptr}; ParasiticNodeResistorMap resistor_map_; ParasiticNodeCapacitorMap capacitor_map_; ParasiticNodeSet visited_nodes_; ParasiticNodeValueMap node_values_; ParasiticResistorSet loop_resistors_; - bool pin_caps_one_value_; + bool pin_caps_one_value_ {true}; }; ReduceToPi::ReduceToPi(StaState *sta) : - StaState(sta), - coupling_cap_multiplier_(1.0), - rf_(nullptr), - scene_(nullptr), - min_max_(nullptr), - pin_caps_one_value_(true) + StaState(sta) { } @@ -340,7 +335,7 @@ ReduceToPiElmore::makePiElmore(const Parasitic *parasitic_network, Parasitic *pi_elmore = parasitics_->makePiElmore(drvr_pin, rf, min_max, c2, rpi, c1); parasitics_->setIsReducedParasiticNetwork(pi_elmore, true); - reduceElmoreDfs(drvr_pin, drvr_node, 0, 0.0, pi_elmore); + reduceElmoreDfs(drvr_pin, drvr_node, nullptr, 0.0, pi_elmore); return pi_elmore; } @@ -383,7 +378,7 @@ class ReduceToPiPoleResidue2 : public ReduceToPi { public: ReduceToPiPoleResidue2(StaState *sta); - ~ReduceToPiPoleResidue2(); + ~ReduceToPiPoleResidue2() override; void findPolesResidues(const Parasitic *parasitic_network, Parasitic *pi_pole_residue, const Pin *drvr_pin, @@ -424,12 +419,11 @@ class ReduceToPiPoleResidue2 : public ReduceToPi // Resistor/capacitor currents. ResistorCurrentMap currents_; - ParasiticNodeValueMap *moments_; + ParasiticNodeValueMap *moments_ {nullptr}; }; ReduceToPiPoleResidue2::ReduceToPiPoleResidue2(StaState *sta) : - ReduceToPi(sta), - moments_(nullptr) + ReduceToPi(sta) { } @@ -526,10 +520,10 @@ ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, // current thru the resistors. Thus, there is no point in doing a // pass to find the zero'th moments. for (int moment_index = 1; moment_index < moment_count; moment_index++) { - double rd_i = findBranchCurrents(drvr_pin, drvr_node, 0, moment_index); + double rd_i = findBranchCurrents(drvr_pin, drvr_node, nullptr, moment_index); double rd_volt = rd_i * rd; setMoment(drvr_node, 0.0, moment_index); - findMoments(drvr_pin, drvr_node, -rd_volt, 0, moment_index); + findMoments(drvr_pin, drvr_node, -rd_volt, nullptr, moment_index); } } diff --git a/parasitics/ReportParasiticAnnotation.cc b/parasitics/ReportParasiticAnnotation.cc index f91db926c..f9651e577 100644 --- a/parasitics/ReportParasiticAnnotation.cc +++ b/parasitics/ReportParasiticAnnotation.cc @@ -24,15 +24,15 @@ #include "ReportParasiticAnnotation.hh" +#include "ArcDelayCalc.hh" #include "ContainerHelpers.hh" -#include "Report.hh" +#include "Graph.hh" #include "Network.hh" #include "NetworkCmp.hh" +#include "Parasitics.hh" #include "PortDirection.hh" -#include "Graph.hh" +#include "Report.hh" #include "Scene.hh" -#include "Parasitics.hh" -#include "ArcDelayCalc.hh" namespace sta { @@ -43,7 +43,7 @@ class ReportParasiticAnnotation : public StaState bool report_unannotated, const Scene *scene, StaState *sta); - void report(); + void reportAnnotation(); private: void reportAnnotationCounts(); @@ -67,7 +67,7 @@ reportParasiticAnnotation(Parasitics *parasitics, { ReportParasiticAnnotation report_annotation(parasitics, report_unannotated, scene, sta); - report_annotation.report(); + report_annotation.reportAnnotation(); } ReportParasiticAnnotation::ReportParasiticAnnotation(Parasitics *parasitics, @@ -83,7 +83,7 @@ ReportParasiticAnnotation::ReportParasiticAnnotation(Parasitics *parasitics, } void -ReportParasiticAnnotation::report() +ReportParasiticAnnotation::reportAnnotation() { findCounts(); reportAnnotationCounts(); @@ -132,7 +132,7 @@ ReportParasiticAnnotation::findCounts() arc_delay_calc_->findParasitic(pin, RiseFall::rise(), scene_, min_max_); if (parasitic) { PinSet unannotated_loads = parasitics_->unannotatedLoads(parasitic, pin); - if (unannotated_loads.size() > 0) + if (!unannotated_loads.empty()) partially_annotated_.push_back(pin); } else diff --git a/parasitics/SpefParse.yy b/parasitics/SpefParse.yy index e527d781c..fed603c07 100755 --- a/parasitics/SpefParse.yy +++ b/parasitics/SpefParse.yy @@ -47,10 +47,18 @@ sta::SpefParse::error(const location_type &loc, %code requires { #include +#include "StringUtil.hh" namespace sta { // Bison's C++ variant skeleton cannot use void as a semantic type. struct SpefParseVoid {}; +class SpefReader; +class SpefScanner; +class Net; +class Pin; +class PortDirection; +class SpefRspfPi; +class SpefTriple; } } diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 30613dd5a..051416730 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -28,21 +28,21 @@ #include #include -#include "Zlib.hh" -#include "Stats.hh" -#include "Report.hh" +#include "ArcDelayCalc.hh" #include "Debug.hh" -#include "StringUtil.hh" -#include "Transition.hh" #include "Liberty.hh" #include "Network.hh" -#include "PortDirection.hh" -#include "Sdc.hh" #include "Parasitics.hh" +#include "PortDirection.hh" +#include "Report.hh" #include "Scene.hh" -#include "ArcDelayCalc.hh" -#include "SpefReaderPvt.hh" +#include "Sdc.hh" #include "SpefNamespace.hh" +#include "SpefReaderPvt.hh" +#include "Stats.hh" +#include "StringUtil.hh" +#include "Transition.hh" +#include "Zlib.hh" #include "parasitics/SpefScanner.hh" namespace sta { @@ -84,19 +84,7 @@ SpefReader::SpefReader(std::string_view filename, reduce_(reduce), scene_(scene), min_max_(min_max), - // defaults - divider_('\0'), - delimiter_('\0'), - bus_brkt_left_('\0'), - bus_brkt_right_('\0'), - net_(nullptr), - triple_index_(0), - time_scale_(1.0), - cap_scale_(1.0), - res_scale_(1.0), - induct_scale_(1.0), - parasitics_(parasitics), - parasitic_(nullptr) + parasitics_(parasitics) { parasitics->setCouplingCapFactor(coupling_cap_factor); } @@ -452,7 +440,7 @@ SpefReader::findParasiticNode(std::string_view name, if (net) { // : if (isDigits(name2)) { - int id = std::stoi(name2); + uint32_t id = std::stoi(name2); if (local_only && !network_->isConnected(net, net_)) warn(1653, "{}{}{} not connected to net {}.", name1, delimiter_, name2, network_->pathName(net_)); @@ -487,7 +475,7 @@ SpefReader::findParasiticNode(std::string_view name, } void -SpefReader::makeCapacitor(int, +SpefReader::makeCapacitor(uint32_t, std::string_view node_name, SpefTriple *cap) { @@ -500,7 +488,7 @@ SpefReader::makeCapacitor(int, } void -SpefReader::makeCapacitor(int id, +SpefReader::makeCapacitor(uint32_t id, std::string_view node_name1, std::string_view node_name2, SpefTriple *cap) @@ -523,7 +511,7 @@ SpefReader::makeCapacitor(int id, } void -SpefReader::makeResistor(int id, +SpefReader::makeResistor(uint32_t id, std::string_view node_name1, std::string_view node_name2, SpefTriple *res) diff --git a/parasitics/SpefReader.hh b/parasitics/SpefReader.hh index f1187e1de..4708fc1c4 100644 --- a/parasitics/SpefReader.hh +++ b/parasitics/SpefReader.hh @@ -27,9 +27,9 @@ #include #include -#include "Zlib.hh" #include "MinMax.hh" #include "ParasiticsClass.hh" +#include "Zlib.hh" namespace sta { @@ -52,7 +52,7 @@ readSpefFile(std::string_view filename, bool reduce, const Scene *scene, const MinMaxAll *min_max, - Parasitics *parasirics, + Parasitics *parasitics, StaState *sta); } // namespace sta diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index 9ba21cde3..4b1b90ffe 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -28,11 +28,11 @@ #include #include -#include "Zlib.hh" -#include "StringUtil.hh" #include "NetworkClass.hh" #include "ParasiticsClass.hh" #include "StaState.hh" +#include "StringUtil.hh" +#include "Zlib.hh" namespace sta { @@ -58,7 +58,7 @@ public: const MinMaxAll *min_max, Parasitics *parasitics, StaState *sta); - virtual ~SpefReader() = default; + ~SpefReader() override = default; bool read(); char divider() const { return divider_; } void setDivider(char divider); @@ -94,14 +94,14 @@ public: void dspfBegin(Net *net, SpefTriple *total_cap); void dspfFinish(); - void makeCapacitor(int id, + void makeCapacitor(uint32_t id, std::string_view node_name, SpefTriple *cap); - void makeCapacitor(int id, + void makeCapacitor(uint32_t id, std::string_view node_name1, std::string_view node_name2, SpefTriple *cap); - void makeResistor(int id, + void makeResistor(uint32_t id, std::string_view node_name1, std::string_view node_name2, SpefTriple *res); @@ -134,21 +134,21 @@ private: const Scene *scene_; const MinMaxAll *min_max_; // Normally no need to keep device names. - char divider_; - char delimiter_; - char bus_brkt_left_; - char bus_brkt_right_; - Net *net_; + char divider_{'\0'}; + char delimiter_{'\0'}; + char bus_brkt_left_{'\0'}; + char bus_brkt_right_{'\0'}; + Net *net_{nullptr}; - int triple_index_; - float time_scale_; - float cap_scale_; - float res_scale_; - float induct_scale_; + int triple_index_{0}; + float time_scale_{1.0}; + float cap_scale_{1.0}; + float res_scale_{1.0}; + float induct_scale_{1.0}; SpefNameMap name_map_; StringSeq design_flow_; Parasitics *parasitics_; - Parasitic *parasitic_; + Parasitic *parasitic_{nullptr}; }; class SpefTriple diff --git a/parasitics/SpefScanner.hh b/parasitics/SpefScanner.hh index 58e64025f..d9c21b862 100644 --- a/parasitics/SpefScanner.hh +++ b/parasitics/SpefScanner.hh @@ -46,7 +46,7 @@ public: std::string_view filename, SpefReader *reader, Report *report); - virtual int lex(SpefParse::semantic_type *const yylval, + virtual int lex(SpefParse::semantic_type *yylval, SpefParse::location_type *yylloc); // YY_DECL defined in SpefLex.ll // Method body created by flex in SpefLex.cc diff --git a/power/Power.cc b/power/Power.cc index 114d802e8..da1b5e08b 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -31,8 +31,6 @@ #include #include -#include "cudd.h" - #include "Bfs.hh" #include "ClkNetwork.hh" #include "Clock.hh" @@ -71,6 +69,7 @@ #include "Transition.hh" #include "Units.hh" #include "VertexVisitor.hh" +#include "cudd.h" #include "search/Levelize.hh" #include "search/Sim.hh" @@ -570,7 +569,6 @@ ActivitySrchPred::searchTo(const Vertex *, //////////////////////////////////////////////////////////////// -// NOLINTNEXTLINE(misc-multiple-inheritance) class PropActivityVisitor : public VertexVisitor, StaState { public: diff --git a/power/Power.hh b/power/Power.hh index 9bb72e65c..8a19f3090 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -24,16 +24,16 @@ #pragma once -#include #include #include +#include -#include "StaConfig.hh" // CUDD +#include "Bdd.hh" #include "Network.hh" -#include "SdcClass.hh" #include "PowerClass.hh" +#include "SdcClass.hh" +#include "StaConfig.hh" // CUDD #include "StaState.hh" -#include "Bdd.hh" struct DdNode; struct DdManager; diff --git a/power/Power.i b/power/Power.i index bc8186685..52d77af79 100644 --- a/power/Power.i +++ b/power/Power.i @@ -25,12 +25,13 @@ %module power %{ -#include "Sta.hh" -#include "Sdc.hh" -#include "Mode.hh" #include "power/Power.hh" -#include "power/VcdReader.hh" + +#include "Mode.hh" +#include "Sdc.hh" +#include "Sta.hh" #include "power/SaifReader.hh" +#include "power/VcdReader.hh" using namespace sta; diff --git a/power/ReportPower.cc b/power/ReportPower.cc index 00407ff6d..16de910ae 100644 --- a/power/ReportPower.cc +++ b/power/ReportPower.cc @@ -24,12 +24,12 @@ #include "ReportPower.hh" -#include #include +#include -#include "Report.hh" -#include "Network.hh" #include "Format.hh" +#include "Network.hh" +#include "Report.hh" namespace sta { diff --git a/power/ReportPower.hh b/power/ReportPower.hh index 4d905e885..105a9a00a 100644 --- a/power/ReportPower.hh +++ b/power/ReportPower.hh @@ -26,9 +26,9 @@ #include -#include "StaState.hh" #include "NetworkClass.hh" #include "PowerClass.hh" +#include "StaState.hh" namespace sta { diff --git a/power/SaifParse.yy b/power/SaifParse.yy index 9413cbced..a447a0cb6 100644 --- a/power/SaifParse.yy +++ b/power/SaifParse.yy @@ -60,6 +60,10 @@ sta::SaifParse::error(const location_type &loc, %define api.parser.class {SaifParse} %define api.value.type variant +%code requires { +#include "power/SaifReaderPvt.hh" +} + // expected shift/reduce conflicts %expect 2 diff --git a/power/SaifReader.cc b/power/SaifReader.cc index 98e21e76f..b05b6c121 100644 --- a/power/SaifReader.cc +++ b/power/SaifReader.cc @@ -29,18 +29,18 @@ #include #include -#include "Error.hh" #include "Debug.hh" -#include "Stats.hh" -#include "Report.hh" +#include "Error.hh" +#include "Liberty.hh" #include "Network.hh" #include "PortDirection.hh" -#include "Liberty.hh" -#include "Sdc.hh" #include "Power.hh" +#include "Report.hh" +#include "Sdc.hh" +#include "Sta.hh" +#include "Stats.hh" #include "power/SaifReaderPvt.hh" #include "power/SaifScanner.hh" -#include "Sta.hh" namespace sta { @@ -60,11 +60,6 @@ SaifReader::SaifReader(const char *filename, StaState(sta), filename_(filename), scope_(scope), - divider_('/'), - escape_('\\'), - timescale_(1.0E-9F), // default units of ns - duration_(0.0), - in_scope_level_(0), power_(sta->power()) { } diff --git a/power/SaifReaderPvt.hh b/power/SaifReaderPvt.hh index 6443dfa9a..243e14dc6 100644 --- a/power/SaifReaderPvt.hh +++ b/power/SaifReaderPvt.hh @@ -24,15 +24,15 @@ #pragma once +#include #include -#include -#include #include -#include +#include +#include -#include "Zlib.hh" #include "NetworkClass.hh" #include "StaState.hh" +#include "Zlib.hh" // Header for SaifReader.cc to communicate with SaifLex.cc, SaifParse.cc @@ -72,13 +72,13 @@ private: const char *filename_; const char *scope_; // Divider delimited scope to begin annotation. - char divider_; - char escape_; - double timescale_; - int64_t duration_; + char divider_ = '/'; + char escape_ = '\\'; + double timescale_ = 1.0E-9; // default units of ns + int64_t duration_ = 0; std::vector saif_scope_; // Scope during parsing. - size_t in_scope_level_; + size_t in_scope_level_ = 0; std::vector path_; // Path within scope. std::set annotated_pins_; Power *power_; diff --git a/power/SaifScanner.hh b/power/SaifScanner.hh index 945ddc6a1..9ca6867f9 100644 --- a/power/SaifScanner.hh +++ b/power/SaifScanner.hh @@ -36,6 +36,7 @@ namespace sta { class Report; +class SaifReader; class SaifScanner : public SaifFlexLexer { @@ -44,7 +45,7 @@ public: const std::string &filename, SaifReader *reader, Report *report); - virtual int lex(SaifParse::semantic_type *const yylval, + virtual int lex(SaifParse::semantic_type *yylval, SaifParse::location_type *yylloc); // YY_DECL defined in SaifLex.ll // Method body created by flex in SaifLex.cc diff --git a/power/VcdParse.cc b/power/VcdParse.cc index ea0d71a7f..2a8414276 100644 --- a/power/VcdParse.cc +++ b/power/VcdParse.cc @@ -28,10 +28,10 @@ #include #include -#include "Stats.hh" -#include "Report.hh" -#include "Error.hh" #include "EnumNameMap.hh" +#include "Error.hh" +#include "Report.hh" +#include "Stats.hh" namespace sta { @@ -106,11 +106,6 @@ VcdParse::read(const char *filename, VcdParse::VcdParse(Report *report, Debug *debug) : - reader_(nullptr), - file_line_(0), - stmt_line_(0), - time_(0), - prev_time_(0), report_(report), debug_(debug) { @@ -237,7 +232,7 @@ VcdParse::parseVarValues() 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()); + std::ranges::reverse(bus_value); reader_->varAppendBusValue(id, time_, bus_value); } } diff --git a/power/VcdParse.hh b/power/VcdParse.hh index 827022223..66f5d28fd 100644 --- a/power/VcdParse.hh +++ b/power/VcdParse.hh @@ -28,8 +28,8 @@ #include #include -#include "Zlib.hh" #include "StaState.hh" +#include "Zlib.hh" namespace sta { @@ -78,15 +78,15 @@ private: std::string readStmtString(); std::vector readStmtTokens(); - VcdReader *reader_; + VcdReader *reader_ = nullptr; gzFile stream_; std::string token_; const char *filename_; - int file_line_; - int stmt_line_; + int file_line_ = 0; + int stmt_line_ = 0; - VcdTime time_; - VcdTime prev_time_; + VcdTime time_ = 0; + VcdTime prev_time_ = 0; VcdScope scope_; Report *report_; diff --git a/power/VcdReader.cc b/power/VcdReader.cc index ee6e60371..663730229 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -25,21 +25,21 @@ #include "VcdReader.hh" #include -#include +#include #include #include -#include "VcdParse.hh" #include "Debug.hh" -#include "Network.hh" #include "Liberty.hh" -#include "PortDirection.hh" -#include "VerilogNamespace.hh" -#include "ParseBus.hh" -#include "Sdc.hh" #include "Mode.hh" +#include "Network.hh" +#include "ParseBus.hh" +#include "PortDirection.hh" #include "Power.hh" +#include "Sdc.hh" #include "Sta.hh" +#include "VcdParse.hh" +#include "VerilogNamespace.hh" namespace sta { @@ -60,17 +60,13 @@ class VcdCount private: PinSeq pins_; - VcdTime prev_time_; - char prev_value_; - VcdTime high_time_; - double transition_count_; + VcdTime prev_time_ = -1; + char prev_value_ = '\0'; + VcdTime high_time_ = 0; + double transition_count_ = 0; }; -VcdCount::VcdCount() : - prev_time_(-1), - prev_value_('\0'), - high_time_(0), - transition_count_(0) +VcdCount::VcdCount() { } @@ -157,9 +153,9 @@ class VcdCountReader : public VcdReader const std::string scope_; - double time_scale_; - VcdTime time_min_; - VcdTime time_max_; + double time_scale_ = 1.0; + VcdTime time_min_ = 0; + VcdTime time_max_ = 0; VcdIdCountsMap vcd_count_map_; const Network *sdc_network_; @@ -172,9 +168,6 @@ VcdCountReader::VcdCountReader(std::string_view scope, Report *report, Debug *debug) : scope_(scope), - time_scale_(1.0), - time_min_(0), - time_max_(0), sdc_network_(sdc_network), report_(report), debug_(debug) @@ -299,16 +292,14 @@ VcdCountReader::varAppendValue(const std::string &id, if (itr != vcd_count_map_.end()) { VcdCounts &vcd_counts = itr->second; if (debug_->check("read_vcd", 3)) { - for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) { - VcdCount &vcd_count = vcd_counts[bit_idx]; + for (auto &vcd_count : vcd_counts) { for (const Pin *pin : vcd_count.pins()) { debugPrint(debug_, "read_vcd", 3, "{} time {} value {}", sdc_network_->pathName(pin), time, value); } } } - for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) { - VcdCount &vcd_count = vcd_counts[bit_idx]; + for (auto &vcd_count : vcd_counts) { vcd_count.incrCounts(time, value); } } diff --git a/sdc/Clock.cc b/sdc/Clock.cc index 1772b60f6..d38e384b3 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -25,17 +25,18 @@ #include "Clock.hh" #include +#include #include "ContainerHelpers.hh" #include "Error.hh" #include "Format.hh" -#include "StringUtil.hh" +#include "Graph.hh" #include "MinMax.hh" -#include "Transition.hh" -#include "TimingRole.hh" #include "Network.hh" -#include "Graph.hh" #include "Sdc.hh" +#include "StringUtil.hh" +#include "TimingRole.hh" +#include "Transition.hh" namespace sta { @@ -47,26 +48,8 @@ Clock::Clock(std::string_view name, const Network *network) : name_(name), pins_(network), - add_to_pins_(false), leaf_pins_(network), - period_(0.0), - waveform_(nullptr), - waveform_valid_(false), - index_(index), - clk_edges_(nullptr), - is_propagated_(false), - uncertainties_(nullptr), - is_generated_(false), - src_pin_(nullptr), - master_clk_(nullptr), - master_clk_infered_(false), - divide_by_(0), - multiply_by_(0), - duty_cycle_(0), - invert_(false), - combinational_(false), - edges_(nullptr), - edge_shifts_(nullptr) + index_(index) { makeClkEdges(); } @@ -87,7 +70,7 @@ Clock::initClk(PinSet *pins, waveform_valid_ = true; period_ = period; setClkEdgeTimes(); - setComment(std::move(comment)); + setComment(comment); } bool @@ -248,7 +231,7 @@ Clock::setSlewLimit(const RiseFallBoth *rf, const MinMax *min_max, float slew) { - slew_limits_[int(clk_data)].setValue(rf, min_max, slew); + slew_limits_[static_cast(clk_data)].setValue(rf, min_max, slew); } void @@ -259,7 +242,7 @@ Clock::slewLimit(const RiseFall *rf, float &slew, bool &exists) const { - slew_limits_[int(clk_data)].value(rf, min_max, slew, exists); + slew_limits_[static_cast(clk_data)].value(rf, min_max, slew, exists); } void @@ -343,7 +326,7 @@ Clock::initGeneratedClk(PinSet *pins, invert_ = invert; combinational_ = combinational; is_propagated_ = is_propagated; - setComment(std::move(comment)); + setComment(comment); delete edges_; if (edges @@ -451,25 +434,29 @@ Clock::generateEdgesClk(const Clock *src_clk) if (edges_->size() == 3) { const FloatSeq *src_wave = src_clk->waveform(); size_t src_size = src_wave->size(); + int src_size_int = static_cast(src_size); float src_period = src_clk->period(); int edge0_1 = (*edges_)[0] - 1; - float rise = (*src_wave)[edge0_1 % src_size] - + (edge0_1 / src_size) * src_period; + div_t edge0_div = std::div(edge0_1, src_size_int); + float rise = (*src_wave)[edge0_div.rem] + + static_cast(edge0_div.quot) * src_period; if (edge_shifts_) rise += (*edge_shifts_)[0]; waveform_->push_back(rise); int edge1_1 = (*edges_)[1] - 1; - float fall = (*src_wave)[edge1_1 % src_size] - + (edge1_1 / src_size) * src_period; + div_t edge1_div = std::div(edge1_1, src_size_int); + float fall = (*src_wave)[edge1_div.rem] + + static_cast(edge1_div.quot) * src_period; if (edge_shifts_) fall += (*edge_shifts_)[1]; waveform_->push_back(fall); int edge2_1 = (*edges_)[2] - 1; - period_ = (*src_wave)[edge2_1 % src_size] - + (edge2_1 / src_size) * src_period - rise; + div_t edge2_div = std::div(edge2_1, src_size_int); + period_ = (*src_wave)[edge2_div.rem] + + static_cast(edge2_div.quot) * src_period - rise; if (edge_shifts_) period_ += (*edge_shifts_)[2]; } @@ -522,7 +509,7 @@ Clock::isDivideByOneCombinational() const return combinational_ && divide_by_ == 1 && multiply_by_ == 0 - && edge_shifts_ == 0; + && edge_shifts_ == nullptr; } //////////////////////////////////////////////////////////////// @@ -532,15 +519,10 @@ ClockEdge::ClockEdge(Clock *clock, clock_(clock), rf_(rf), name_(sta::format("{} {}", clock_->name(), rf_->shortName())), - time_(0.0), index_(clock_->index() * RiseFall::index_count + rf_->index()) { } -ClockEdge::~ClockEdge() -{ -} - void ClockEdge::setTime(float time) { diff --git a/sdc/ClockGatingCheck.cc b/sdc/ClockGatingCheck.cc index 96d134d32..a8cb57635 100644 --- a/sdc/ClockGatingCheck.cc +++ b/sdc/ClockGatingCheck.cc @@ -26,11 +26,6 @@ namespace sta { -ClockGatingCheck::ClockGatingCheck() : - active_value_(LogicValue::unknown) -{ -} - void ClockGatingCheck::setActiveValue(LogicValue value) { diff --git a/sdc/CycleAccting.cc b/sdc/CycleAccting.cc index 8f4b4b322..8c0ca1eb7 100644 --- a/sdc/CycleAccting.cc +++ b/sdc/CycleAccting.cc @@ -24,16 +24,16 @@ #include "CycleAccting.hh" -#include // ceil #include // max +#include // ceil +#include "Clock.hh" #include "ContainerHelpers.hh" #include "Debug.hh" #include "Fuzzy.hh" -#include "Units.hh" -#include "TimingRole.hh" -#include "Clock.hh" #include "Sdc.hh" +#include "TimingRole.hh" +#include "Units.hh" namespace sta { @@ -110,8 +110,7 @@ CycleAcctings::reportClkToClkMaxCycleWarnings(Report *report) CycleAccting::CycleAccting(const ClockEdge *src, const ClockEdge *tgt) : src_(src), - tgt_(tgt), - max_cycles_exceeded_(false) + tgt_(tgt) { for (int i = 0; i <= TimingRole::index_max; i++) { delay_[i] = MinMax::min()->initValue(); diff --git a/sdc/DataCheck.cc b/sdc/DataCheck.cc index dbba24226..a538c6a05 100644 --- a/sdc/DataCheck.cc +++ b/sdc/DataCheck.cc @@ -46,8 +46,8 @@ DataCheck::margin(const RiseFall *from_rf, float &margin, bool &exists) const { - return margins_[from_rf->index()].value(to_rf, setup_hold, - margin, exists); + margins_[from_rf->index()].value(to_rf, setup_hold, + margin, exists); } void diff --git a/sdc/DeratingFactors.cc b/sdc/DeratingFactors.cc index f5534ceec..8b13bf2c1 100644 --- a/sdc/DeratingFactors.cc +++ b/sdc/DeratingFactors.cc @@ -26,16 +26,16 @@ namespace sta { -inline int +inline size_t index(TimingDerateType type) { - return int(type); + return static_cast(type); } -inline int +inline size_t index(TimingDerateCellType type) { - return int(type); + return static_cast(type); } DeratingFactors::DeratingFactors() @@ -49,8 +49,8 @@ DeratingFactors::setFactor(PathClkOrData clk_data, const EarlyLate *early_late, float factor) { - for (auto rf1 : rf->range()) - factors_[int(clk_data)].setValue(rf1, early_late, factor); + for (const RiseFall *rf1 : rf->range()) + factors_[static_cast(clk_data)].setValue(rf1, early_late, factor); } void @@ -60,14 +60,14 @@ DeratingFactors::factor(PathClkOrData clk_data, float &factor, bool &exists) const { - factors_[int(clk_data)].value(rf, early_late, factor, exists); + factors_[static_cast(clk_data)].value(rf, early_late, factor, exists); } void DeratingFactors::clear() { - for (int clk_data = 0; clk_data < path_clk_or_data_count;clk_data++) - factors_[int(clk_data)].clear(); + for (RiseFallMinMax &factors : factors_) + factors.clear(); } void @@ -91,7 +91,7 @@ DeratingFactors::isOneValue(PathClkOrData clk_data, bool &is_one_value, float &value) const { - is_one_value = factors_[int(clk_data)].isOneValue(early_late, value); + is_one_value = factors_[static_cast(clk_data)].isOneValue(early_late, value); } bool @@ -143,8 +143,8 @@ DeratingFactorsGlobal::factor(TimingDerateCellType type, void DeratingFactorsGlobal::clear() { - for (int type = 0; type < timing_derate_type_count; type++) - factors_[type].clear(); + for (DeratingFactors &factors : factors_) + factors.clear(); } DeratingFactors * @@ -184,8 +184,8 @@ DeratingFactorsCell::factor(TimingDerateCellType type, void DeratingFactorsCell::clear() { - for (int type = 0; type < timing_derate_cell_type_count; type++) - factors_[type].clear(); + for (DeratingFactors &factors : factors_) + factors.clear(); } DeratingFactors * @@ -211,10 +211,4 @@ DeratingFactorsCell::isOneValue(const EarlyLate *early_late, value = value1; } -//////////////////////////////////////////////////////////////// - -DeratingFactorsNet::DeratingFactorsNet() -{ -} - } // namespace sta diff --git a/sdc/DisabledPorts.cc b/sdc/DisabledPorts.cc index 9c3fa9219..311a2439b 100644 --- a/sdc/DisabledPorts.cc +++ b/sdc/DisabledPorts.cc @@ -26,21 +26,13 @@ #include -#include "StringUtil.hh" -#include "TimingRole.hh" #include "Liberty.hh" #include "Network.hh" +#include "StringUtil.hh" +#include "TimingRole.hh" namespace sta { -DisabledPorts::DisabledPorts() : - all_(false), - from_(nullptr), - to_(nullptr), - from_to_(nullptr) -{ -} - DisabledPorts::~DisabledPorts() { delete from_; @@ -122,9 +114,7 @@ DisabledPorts::isDisabled(LibertyPort *from, //////////////////////////////////////////////////////////////// DisabledCellPorts::DisabledCellPorts(LibertyCell *cell) : - DisabledPorts(), - cell_(cell), - arc_sets_(nullptr) + cell_(cell) { } @@ -159,15 +149,10 @@ DisabledCellPorts::isDisabled(TimingArcSet *arc_set) const class DisabledCellPortsLess { public: - DisabledCellPortsLess(); bool operator()(const DisabledCellPorts *disable1, const DisabledCellPorts *disable2); }; -DisabledCellPortsLess::DisabledCellPortsLess() -{ -} - bool DisabledCellPortsLess::operator()(const DisabledCellPorts *disable1, const DisabledCellPorts *disable2) @@ -190,7 +175,6 @@ sortByName(const DisabledCellPortsMap *cell_map) //////////////////////////////////////////////////////////////// DisabledInstancePorts::DisabledInstancePorts(Instance *inst) : - DisabledPorts(), inst_(inst) { } @@ -263,4 +247,4 @@ sortByName(const LibertyPortPairSet *set) return pairs; } -} +} // namespace sta diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 69ad2b57d..151c98ad2 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -26,16 +26,16 @@ #include -#include "Format.hh" +#include "Clock.hh" #include "ContainerHelpers.hh" +#include "Format.hh" #include "MinMax.hh" -#include "TimingRole.hh" -#include "Units.hh" -#include "Transition.hh" -#include "PortDirection.hh" #include "Network.hh" #include "NetworkCmp.hh" -#include "Clock.hh" +#include "PortDirection.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "Units.hh" namespace sta { @@ -99,8 +99,7 @@ ExceptionPath::ExceptionPath(ExceptionFrom *from, to_(to), min_max_(min_max), own_pts_(own_pts), - priority_(priority), - id_(0) + priority_(priority) { makeStates(); } @@ -827,10 +826,6 @@ GroupPath::GroupPath(std::string_view name, { } -GroupPath::~GroupPath() -{ -} - std::string_view GroupPath::typeString() const { @@ -882,8 +877,7 @@ const int ExceptionPt::to_string_max_objects_ = 20; ExceptionPt::ExceptionPt(const RiseFallBoth *rf, bool own_pts) : rf_(rf), - own_pts_(own_pts), - hash_(0) + own_pts_(own_pts) { } @@ -922,7 +916,7 @@ ExceptionFromTo::ExceptionFromTo(PinSet *pins, delete insts_; insts_ = nullptr; } - findHash(network); + ExceptionFromTo::findHash(network); } ExceptionFromTo::~ExceptionFromTo() @@ -1484,7 +1478,6 @@ ExceptionThru::ExceptionThru(PinSet *pins, const Network *network) : ExceptionPt(rf, own_pts), pins_(pins), - edges_(nullptr), nets_(nets), insts_(insts) { @@ -2103,9 +2096,7 @@ ExceptionThru::deletePinBefore(const Pin *pin, //////////////////////////////////////////////////////////////// ExceptionPtIterator::ExceptionPtIterator(const ExceptionPath *exception) : - exception_(exception), - from_done_(false), - to_done_(false) + exception_(exception) { if (exception->thrus()) thru_iter_ = exception->thrus()->begin(); @@ -2177,7 +2168,7 @@ ExpandedExceptionVisitor::visitExpansions() } } else - expandThrus(0); + expandThrus(nullptr); } void @@ -2282,7 +2273,6 @@ ExceptionState::ExceptionState(ExceptionPath *exception, int index) : exception_(exception), next_thru_(next_thru), - next_state_(nullptr), index_(index) { } @@ -2385,11 +2375,10 @@ class InsertPinPairsThru : public HierPinThruVisitor public: InsertPinPairsThru(PinPairSet *pairs, const Network *network); + void visit(const Pin *drvr, + const Pin *load) override; protected: - virtual void visit(const Pin *drvr, - const Pin *load); - PinPairSet *pairs_; const Network *network_; }; @@ -2432,11 +2421,10 @@ class DeletePinPairsThru : public HierPinThruVisitor public: DeletePinPairsThru(PinPairSet *pairs, const Network *network); + void visit(const Pin *drvr, + const Pin *load) override; protected: - virtual void visit(const Pin *drvr, - const Pin *load); - PinPairSet *pairs_; const Network *network_; }; diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc index cf3033778..509e48df8 100644 --- a/sdc/FilterObjects.cc +++ b/sdc/FilterObjects.cc @@ -24,12 +24,12 @@ #include "FilterObjects.hh" +#include +#include #include #include -#include -#include -#include #include +#include #include #include #include @@ -42,15 +42,15 @@ #include "GraphCmp.hh" #include "Liberty.hh" #include "LibertyClass.hh" -#include "NetworkClass.hh" #include "Network.hh" -#include "SearchClass.hh" -#include "StringUtil.hh" -#include "Property.hh" +#include "NetworkClass.hh" #include "PathEnd.hh" #include "PatternMatch.hh" +#include "Property.hh" #include "SdcClass.hh" +#include "SearchClass.hh" #include "Sta.hh" +#include "StringUtil.hh" namespace sta { @@ -86,7 +86,7 @@ class FilterExpr PredicateToken(std::string_view property, std::string_view op, std::string_view arg); - virtual ~PredicateToken() = default; + ~PredicateToken() override = default; const std::string &property() const { return property_; } const std::string &op() const { return op_; } const std::string &arg() const { return arg_; } diff --git a/sdc/InputDrive.cc b/sdc/InputDrive.cc index 1b558b96a..8f93a7f8e 100644 --- a/sdc/InputDrive.cc +++ b/sdc/InputDrive.cc @@ -24,6 +24,8 @@ #include "InputDrive.hh" +#include "SdcClass.hh" + namespace sta { InputDrive::InputDrive() @@ -90,7 +92,7 @@ void InputDrive::setDriveCell(const LibertyLibrary *library, const LibertyCell *cell, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews &from_slews, const LibertyPort *to_port, const RiseFallBoth *rf, const MinMaxAll *min_max) @@ -120,20 +122,21 @@ InputDrive::driveCell(const RiseFall *rf, // Return values. const LibertyCell *&cell, const LibertyPort *&from_port, - float *&from_slews, + const DriveCellSlews *&from_slews, const LibertyPort *&to_port) const { InputDriveCell *drive = drive_cells_[rf->index()][min_max->index()]; if (drive) { cell = drive->cell(); from_port = drive->fromPort(); - from_slews = drive->fromSlews(); + from_slews = &drive->fromSlews(); to_port = drive->toPort(); } else { cell = nullptr; from_port = nullptr; - from_slews = nullptr; + static constexpr DriveCellSlews slews_zero{0.0, 0.0}; + from_slews = &slews_zero; to_port = nullptr; } } @@ -182,14 +185,14 @@ InputDrive::slew(const RiseFall *rf, InputDriveCell::InputDriveCell(const LibertyLibrary *library, const LibertyCell *cell, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews &from_slews, const LibertyPort *to_port) : library_(library), cell_(cell), from_port_(from_port), + from_slews_(from_slews), to_port_(to_port) { - setFromSlews(from_slews); } void @@ -217,10 +220,9 @@ InputDriveCell::setToPort(const LibertyPort *to_port) } void -InputDriveCell::setFromSlews(float *from_slews) +InputDriveCell::setFromSlews(const DriveCellSlews &from_slews) { - for (auto rf_index : RiseFall::rangeIndex()) - from_slews_[rf_index] = from_slews[rf_index]; + from_slews_ = from_slews; } bool diff --git a/sdc/PortDelay.cc b/sdc/PortDelay.cc index cd6dbba98..94d3bbb20 100644 --- a/sdc/PortDelay.cc +++ b/sdc/PortDelay.cc @@ -24,8 +24,8 @@ #include "PortDelay.hh" -#include "Sdc.hh" #include "Network.hh" +#include "Sdc.hh" namespace sta { @@ -34,10 +34,6 @@ PortDelay::PortDelay(const Pin *pin, const Network *network) : pin_(pin), clk_edge_(clk_edge), - source_latency_included_(false), - network_latency_included_(false), - ref_pin_(nullptr), - delays_(), leaf_pins_(network) { } diff --git a/sdc/PortExtCap.cc b/sdc/PortExtCap.cc index a17f353c3..9802628c9 100644 --- a/sdc/PortExtCap.cc +++ b/sdc/PortExtCap.cc @@ -26,11 +26,6 @@ namespace sta { -PortExtCap::PortExtCap() : - port_(nullptr) -{ -} - void PortExtCap::pinCap(const RiseFall *rf, const MinMax *min_max, diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 08510aea8..ed8b54acd 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -579,9 +579,9 @@ Sdc::setTimingDerate(const Net *net, const EarlyLate *early_late, float derate) { - DeratingFactorsNet *factors = findKey(net_derating_factors_, net); + DeratingFactors *factors = findKey(net_derating_factors_, net); if (factors == nullptr) { - factors = new DeratingFactorsNet; + factors = new DeratingFactors; net_derating_factors_[net] = factors; } factors->setFactor(clk_data, rf, early_late, derate); @@ -665,7 +665,7 @@ Sdc::timingDerateNet(const Pin *pin, const EarlyLate *early_late) const { const Net *net = network_->net(pin); - DeratingFactorsNet *factors = findKey(net_derating_factors_, net); + DeratingFactors *factors = findKey(net_derating_factors_, net); if (factors) { float factor; bool exists; @@ -719,7 +719,7 @@ Sdc::setDriveCell(const LibertyLibrary *library, const LibertyCell *cell, const Port *port, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews &from_slews, const LibertyPort *to_port, const RiseFallBoth *rf, const MinMaxAll *min_max) @@ -1960,7 +1960,7 @@ Sdc::makeClockGroups(std::string_view name, ClockGroups *groups = new ClockGroups(group_name, logically_exclusive, physically_exclusive, asynchronous, allow_paths, - std::move(comment)); + comment); clk_groups_name_map_[groups->name()] = groups; return groups; } diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 601077040..996424b3a 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -27,15 +27,17 @@ %include "std_string.i" %{ +#include "Sdc.hh" + #include -#include "Sdc.hh" -#include "Wireload.hh" #include "Clock.hh" +#include "FilterObjects.hh" #include "PortDelay.hh" #include "Property.hh" -#include "FilterObjects.hh" +#include "SdcClass.hh" #include "Sta.hh" +#include "Wireload.hh" using namespace sta; @@ -1076,7 +1078,7 @@ set_drive_cell_cmd(LibertyLibrary *library, { Sta *sta = Sta::sta(); Sdc *sdc = sta->cmdSdc(); - float from_slews[RiseFall::index_count]; + DriveCellSlews from_slews; from_slews[RiseFall::riseIndex()] = from_slew_rise; from_slews[RiseFall::fallIndex()] = from_slew_fall; sta->setDriveCell(library, cell, port, from_port, from_slews, diff --git a/sdc/SdcCmdComment.cc b/sdc/SdcCmdComment.cc index 805bb1945..adbf6e4a8 100644 --- a/sdc/SdcCmdComment.cc +++ b/sdc/SdcCmdComment.cc @@ -22,9 +22,10 @@ // // This notice may not be removed or altered from any source distribution. -#include "StringUtil.hh" #include "SdcCmdComment.hh" +#include "StringUtil.hh" + namespace sta { SdcCmdComment::SdcCmdComment(std::string_view comment) : diff --git a/sdc/Variables.cc b/sdc/Variables.cc index 90534a802..81650eb4a 100644 --- a/sdc/Variables.cc +++ b/sdc/Variables.cc @@ -26,24 +26,6 @@ namespace sta { -Variables::Variables() : - crpr_enabled_(true), - crpr_mode_(CrprMode::same_pin), - propagate_gated_clock_enable_(true), - preset_clr_arcs_enabled_(false), - cond_default_arcs_enabled_(true), - bidirect_inst_paths_enabled_(false), - recovery_removal_checks_enabled_(true), - gated_clk_checks_enabled_(true), - clk_thru_tristate_enabled_(false), - dynamic_loop_breaking_(false), - propagate_all_clks_(false), - use_default_arrival_clock_(false), - pocv_mode_(PocvMode::scalar), - pocv_quantile_(3.0) -{ -} - void Variables::setCrprEnabled(bool enabled) { diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index c812c1eaa..8f6d8caa2 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -26,47 +26,47 @@ #include #include -#include #include #include #include +#include +#include "ClockGroups.hh" +#include "ClockInsertion.hh" +#include "ClockLatency.hh" #include "ContainerHelpers.hh" -#include "Format.hh" -#include "Zlib.hh" -#include "Report.hh" +#include "DataCheck.hh" +#include "DeratingFactors.hh" +#include "DisabledPorts.hh" #include "Error.hh" -#include "Units.hh" -#include "Transition.hh" +#include "ExceptionPath.hh" +#include "Format.hh" +#include "Fuzzy.hh" +#include "Graph.hh" +#include "GraphCmp.hh" +#include "InputDrive.hh" #include "Liberty.hh" -#include "Wireload.hh" #include "Network.hh" -#include "PortDirection.hh" #include "NetworkCmp.hh" -#include "Graph.hh" -#include "GraphCmp.hh" -#include "RiseFallValues.hh" #include "PortDelay.hh" -#include "ExceptionPath.hh" +#include "PortDirection.hh" #include "PortExtCap.hh" -#include "DisabledPorts.hh" -#include "ClockGroups.hh" -#include "ClockInsertion.hh" -#include "ClockLatency.hh" -#include "InputDrive.hh" -#include "DataCheck.hh" -#include "DeratingFactors.hh" +#include "Report.hh" +#include "RiseFallValues.hh" +#include "Scene.hh" #include "Sdc.hh" -#include "Fuzzy.hh" #include "StaState.hh" -#include "Scene.hh" +#include "Transition.hh" +#include "Units.hh" #include "Variables.hh" +#include "Wireload.hh" #include "WriteSdcPvt.hh" +#include "Zlib.hh" namespace sta { -typedef std::set ClockSenseSet; -typedef std::vector ClockSenseSeq; +using ClockSenseSet = std::set; +using ClockSenseSeq = std::vector; static std::string_view transRiseFallFlag(const RiseFall *rf); @@ -88,7 +88,7 @@ timingDerateTypeKeyword(TimingDerateType type); class WriteSdcObject { public: - WriteSdcObject() {} + WriteSdcObject() = default; virtual ~WriteSdcObject() = default; virtual void write() const = 0; }; @@ -98,7 +98,7 @@ class WriteGetPort : public WriteSdcObject public: WriteGetPort(const Port *port, const WriteSdc *writer); - virtual void write() const; + void write() const override; private: const Port *port_; @@ -125,7 +125,7 @@ class WriteGetPinAndClkKey : public WriteSdcObject bool map_hpin_to_drvr, const Clock *clk, const WriteSdc *writer); - virtual void write() const; + void write() const override; private: const Pin *pin_; @@ -159,7 +159,7 @@ class WriteGetPin : public WriteSdcObject WriteGetPin(const Pin *pin, bool map_hpin_to_drvr, const WriteSdc *writer); - virtual void write() const; + void write() const override; private: const Pin *pin_; @@ -187,7 +187,7 @@ class WriteGetNet : public WriteSdcObject public: WriteGetNet(const Net *net, const WriteSdc *writer); - virtual void write() const; + void write() const override; private: const Net *net_; @@ -212,7 +212,7 @@ class WriteGetInstance : public WriteSdcObject public: WriteGetInstance(const Instance *inst, const WriteSdc *writer); - virtual void write() const; + void write() const override; private: const Instance *inst_; @@ -237,7 +237,7 @@ class WriteGetLibCell : public WriteSdcObject public: WriteGetLibCell(const LibertyCell *cell, const WriteSdc *writer); - virtual void write() const; + void write() const override; private: const LibertyCell *cell_; @@ -262,7 +262,7 @@ class WriteGetClock : public WriteSdcObject public: WriteGetClock(const Clock *clk, const WriteSdc *writer); - virtual void write() const; + void write() const override; private: const Clock *clk_; @@ -321,10 +321,6 @@ WriteSdc::WriteSdc(const Sdc *sdc, { } -WriteSdc::~WriteSdc() -{ -} - void WriteSdc::write(std::string_view filename, bool gzip) @@ -1666,7 +1662,7 @@ WriteSdc::writeDrivingCell(Port *port, const LibertyCell *cell = drive_cell->cell(); const LibertyPort *from_port = drive_cell->fromPort(); const LibertyPort *to_port = drive_cell->toPort(); - float *from_slews = drive_cell->fromSlews(); + const DriveCellSlews &from_slews = drive_cell->fromSlews(); const LibertyLibrary *lib = drive_cell->library(); sta::print(stream_, "set_driving_cell"); if (rf) @@ -1888,7 +1884,7 @@ WriteSdc::writeDerating(DeratingFactorsGlobal *factors) const } } else { - for (int type_index = 0; + for (size_t type_index = 0; type_index < timing_derate_type_count; type_index++) { TimingDerateType type = static_cast(type_index); @@ -1935,7 +1931,7 @@ WriteSdc::writeDerating(DeratingFactors *factors, } } else { - for (int clk_data_index = 0; + for (size_t clk_data_index = 0; clk_data_index < path_clk_or_data_count; clk_data_index++) { PathClkOrData clk_data = static_cast(clk_data_index); @@ -2855,7 +2851,7 @@ setupHoldFlag(const MinMax *min_max) void WriteSdc::writeCmdComment(SdcCmdComment *cmd) const { - const std::string comment = cmd->comment(); + const std::string &comment = cmd->comment(); if (!comment.empty()) sta::print(stream_, " -comment {{{}}}", comment); } diff --git a/sdc/WriteSdcPvt.hh b/sdc/WriteSdcPvt.hh index ad6b3c586..94a1a5912 100644 --- a/sdc/WriteSdcPvt.hh +++ b/sdc/WriteSdcPvt.hh @@ -27,10 +27,10 @@ #include #include -#include "Zlib.hh" -#include "NetworkClass.hh" #include "GraphClass.hh" +#include "NetworkClass.hh" #include "Sdc.hh" +#include "Zlib.hh" namespace sta { @@ -46,7 +46,7 @@ public: bool native, int digits, bool no_timestamp); - virtual ~WriteSdc(); + ~WriteSdc() override = default; void write(std::string_view filename, bool gzip); diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc index dce57ee1e..cca76e65e 100644 --- a/sdf/ReportAnnotation.cc +++ b/sdf/ReportAnnotation.cc @@ -26,15 +26,15 @@ #include -#include "StringUtil.hh" -#include "Report.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "Network.hh" #include "Graph.hh" #include "GraphCmp.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Report.hh" #include "Sdc.hh" +#include "StringUtil.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" namespace sta { @@ -126,16 +126,16 @@ void reportAnnotatedDelay(const Scene *scene, bool report_cells, bool report_nets, - bool from_in_ports, - bool to_out_ports, + bool report_in_ports, + bool report_out_ports, int max_lines, bool report_annotated, bool report_unannotated, bool report_constant_arcs, StaState *sta) { - ReportAnnotated report(scene, report_cells, report_nets, from_in_ports, - to_out_ports, max_lines, report_annotated, + ReportAnnotated report(scene, report_cells, report_nets, report_in_ports, + report_out_ports, max_lines, report_annotated, report_unannotated, report_constant_arcs, sta); report.reportDelayAnnotation(); } diff --git a/sdf/Sdf.i b/sdf/Sdf.i index 89b1fdd28..271e9f60f 100644 --- a/sdf/Sdf.i +++ b/sdf/Sdf.i @@ -26,10 +26,11 @@ %{ #include -#include "sdf/ReportAnnotation.hh" -#include "sdf/SdfWriter.hh" + #include "Search.hh" #include "Sta.hh" +#include "sdf/ReportAnnotation.hh" +#include "sdf/SdfWriter.hh" using sta::Sta; using sta::AnalysisType; diff --git a/sdf/SdfParse.yy b/sdf/SdfParse.yy index 7570f5df7..68e05c57c 100644 --- a/sdf/SdfParse.yy +++ b/sdf/SdfParse.yy @@ -44,6 +44,22 @@ sta::SdfParse::error(const location_type &loc, } %} +%code requires { +#include +#include +#include "StringUtil.hh" + +namespace sta { +class SdfScanner; +class SdfReader; +class Transition; +class SdfPortSpec; +class SdfTriple; + +using SdfTripleSeq = std::vector; +} +} + %require "3.2" %skeleton "lalr1.cc" %debug diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 6a5bfd0b0..007de2ba8 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -24,24 +24,24 @@ #include "sdf/SdfReader.hh" -#include #include +#include #include #include #include "ContainerHelpers.hh" -#include "Zlib.hh" -#include "Error.hh" #include "Debug.hh" -#include "Stats.hh" -#include "Report.hh" +#include "Error.hh" +#include "Graph.hh" #include "MinMax.hh" -#include "TimingArc.hh" #include "Network.hh" -#include "SdcNetwork.hh" -#include "Graph.hh" +#include "Report.hh" #include "Scene.hh" #include "Sdc.hh" +#include "SdcNetwork.hh" +#include "Stats.hh" +#include "TimingArc.hh" +#include "Zlib.hh" #include "sdf/SdfReaderPvt.hh" #include "sdf/SdfScanner.hh" @@ -107,20 +107,12 @@ SdfReader::SdfReader(std::string_view filename, StaState(sta), filename_(filename), path_(path), - triple_min_index_(0), - triple_max_index_(2), arc_delay_min_index_(arc_min_index), arc_delay_max_index_(arc_max_index), analysis_type_(analysis_type), unescaped_dividers_(unescaped_dividers), is_incremental_only_(is_incremental_only), - cond_use_(cond_use), - divider_('/'), - escape_('\\'), - instance_(nullptr), - in_timing_check_(false), - in_incremental_(false), - timescale_(1.0E-9F) // default units of ns + cond_use_(cond_use) { if (unescaped_dividers) network_ = makeSdcNetwork(network_); @@ -747,7 +739,7 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge, void SdfReader::setEdgeArcDelaysCondUse(Edge *edge, TimingArc *arc, - float *value, + const float *value, int triple_index, int arc_delay_index, const MinMax *min_max) @@ -819,11 +811,11 @@ SdfReader::makeCondPortSpec(std::string_view cond_port) // Search from end to find port name because condition may contain spaces. std::string cond_port1(cond_port); trimRight(cond_port1); - auto port_idx = cond_port1.find_last_of(" "); - if (port_idx != cond_port1.npos) { + auto port_idx = cond_port1.find_last_of(' '); + if (port_idx != std::string::npos) { std::string port1 = cond_port1.substr(port_idx + 1); - size_t cond_end = cond_port1.find_last_not_of(" ", port_idx); - if (cond_end != cond_port1.npos) { + size_t cond_end = cond_port1.find_last_not_of(' ', port_idx); + if (cond_end != std::string::npos) { std::string cond1 = cond_port1.substr(0, cond_end + 1); return new SdfPortSpec(Transition::riseFall(), port1, cond1); } @@ -847,7 +839,7 @@ SdfReader::deleteTripleSeq(SdfTripleSeq *triples) SdfTriple * SdfReader::makeTriple() { - return new SdfTriple(0, 0, 0); + return new SdfTriple(nullptr, nullptr, nullptr); } SdfTriple * diff --git a/sdf/SdfReaderPvt.hh b/sdf/SdfReaderPvt.hh index 226e31519..3a53e391d 100644 --- a/sdf/SdfReaderPvt.hh +++ b/sdf/SdfReaderPvt.hh @@ -28,14 +28,14 @@ #include #include -#include "TimingRole.hh" -#include "Transition.hh" +#include "GraphClass.hh" #include "LibertyClass.hh" #include "NetworkClass.hh" -#include "GraphClass.hh" #include "Report.hh" #include "SdcClass.hh" #include "StaState.hh" +#include "TimingRole.hh" +#include "Transition.hh" namespace sta { @@ -58,7 +58,7 @@ public: bool is_incremental_only, MinMaxAll *cond_use, StaState *sta); - ~SdfReader(); + ~SdfReader() override; bool read(); void setDivider(char divider); @@ -80,7 +80,7 @@ public: SdfTriple *triple); void setEdgeArcDelaysCondUse(Edge *edge, TimingArc *arc, - float *value, + const float *value, int triple_index, int arc_delay_index, const MinMax *min_max); @@ -126,7 +126,7 @@ public: void port(std::string_view o_pin_name, SdfTripleSeq *triples); void device(SdfTripleSeq *triples); - void device(std::string_view to_pin_name, + void device(std::string_view to_port_name, SdfTripleSeq *triples); SdfTriple *makeTriple(); @@ -151,7 +151,7 @@ public: void setInTimingCheck(bool in); bool inIncremental() const { return in_incremental_; } void setInIncremental(bool incr); - std::string makeBusName(std::string_view bus_name, + std::string makeBusName(std::string_view base_name, int index); std::string_view filename() const { return filename_; } int sdfLine() const; @@ -209,8 +209,8 @@ private: SdfScanner *scanner_; std::string_view path_; // Which values to pull out of the sdf triples. - int triple_min_index_; - int triple_max_index_; + int triple_min_index_{0}; + int triple_max_index_{2}; // Which arc delay value to deposit the sdf values into. int arc_delay_min_index_; int arc_delay_max_index_; @@ -219,13 +219,13 @@ private: bool is_incremental_only_; MinMaxAll *cond_use_; - char divider_; - char escape_; - Instance *instance_; + char divider_{'/'}; + char escape_{'\\'}; + Instance *instance_{nullptr}; std::string cell_name_; - bool in_timing_check_; - bool in_incremental_; - float timescale_; + bool in_timing_check_{false}; + bool in_incremental_{false}; + float timescale_{1.0E-9F}; // default units of ns static const int null_index_ = -1; }; diff --git a/sdf/SdfScanner.hh b/sdf/SdfScanner.hh index 87fc85876..8d0d5b43f 100644 --- a/sdf/SdfScanner.hh +++ b/sdf/SdfScanner.hh @@ -46,7 +46,7 @@ public: std::string_view filename, SdfReader *reader, Report *report); - virtual int lex(SdfParse::semantic_type *const yylval, + virtual int lex(SdfParse::semantic_type *yylval, SdfParse::location_type *yylloc); // YY_DECL defined in SdfLex.ll // Method body created by flex in SdfLex.cc diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 7607af785..79750e88e 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -30,21 +30,21 @@ #include #include "Format.hh" -#include "Zlib.hh" -#include "StaConfig.hh" // STA_VERSION #include "Fuzzy.hh" -#include "StringUtil.hh" -#include "Units.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" +#include "Graph.hh" +#include "GraphDelayCalc.hh" #include "Liberty.hh" -#include "Sdc.hh" #include "MinMaxValues.hh" #include "Network.hh" -#include "Graph.hh" -#include "GraphDelayCalc.hh" -#include "StaState.hh" #include "Scene.hh" +#include "Sdc.hh" +#include "StaConfig.hh" // STA_VERSION +#include "StaState.hh" +#include "StringUtil.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Units.hh" +#include "Zlib.hh" namespace sta { @@ -118,7 +118,7 @@ class SdfWriter : public StaState bool include_typ_; float timescale_; - char sdf_escape_; + char sdf_escape_{'\\'}; char network_escape_; int digits_; @@ -146,7 +146,6 @@ writeSdf(std::string_view filename, SdfWriter::SdfWriter(StaState *sta) : StaState(sta), - sdf_escape_('\\'), network_escape_(network_->pathEscape()) { } diff --git a/search/CheckCapacitances.cc b/search/CheckCapacitances.cc index cb6a7b47b..24983ba5b 100644 --- a/search/CheckCapacitances.cc +++ b/search/CheckCapacitances.cc @@ -160,7 +160,7 @@ CheckCapacitances::findLimit(const Pin *pin, for (auto rf : RiseFall::range()) { const LibertyCell *cell; const LibertyPort *from_port; - float *from_slews; + const DriveCellSlews *from_slews; const LibertyPort *to_port; drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); if (to_port) { diff --git a/search/CheckFanouts.cc b/search/CheckFanouts.cc index 7b3dc2c11..b5ffae22b 100644 --- a/search/CheckFanouts.cc +++ b/search/CheckFanouts.cc @@ -111,10 +111,10 @@ CheckFanouts::findLimit(const Pin *pin, } InputDrive *drive = sdc->findInputDrive(port); if (drive) { - for (auto rf : RiseFall::range()) { + for (const RiseFall *rf : RiseFall::range()) { const LibertyCell *cell; const LibertyPort *from_port; - float *from_slews; + const DriveCellSlews *from_slews; const LibertyPort *to_port; drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); if (to_port) { diff --git a/search/CheckSlews.cc b/search/CheckSlews.cc index 5ba9436a5..748cc336f 100644 --- a/search/CheckSlews.cc +++ b/search/CheckSlews.cc @@ -297,7 +297,7 @@ CheckSlews::findLimit(const Pin *pin, for (auto rf : RiseFall::range()) { const LibertyCell *cell; const LibertyPort *from_port; - float *from_slews; + const DriveCellSlews *from_slews; const LibertyPort *to_port; drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port); if (to_port) { diff --git a/search/Sta.cc b/search/Sta.cc index 0aca01e07..c6230d42e 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -956,7 +956,7 @@ Sta::setDriveCell(const LibertyLibrary *library, const LibertyCell *cell, const Port *port, const LibertyPort *from_port, - float *from_slews, + const DriveCellSlews &from_slews, const LibertyPort *to_port, const RiseFallBoth *rf, const MinMaxAll *min_max, diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index c414317d8..842b2620b 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -24,35 +24,36 @@ #include "WritePathSpice.hh" +#include #include #include #include #include "Debug.hh" #include "Error.hh" -#include "Report.hh" #include "Format.hh" -#include "StringUtil.hh" #include "FuncExpr.hh" -#include "Units.hh" -#include "Sequential.hh" +#include "Graph.hh" #include "Liberty.hh" -#include "TimingArc.hh" -#include "TableModel.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "Graph.hh" -#include "Sdc.hh" #include "Parasitics.hh" #include "Path.hh" #include "PathExpanded.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "Sdc.hh" +#include "Sequential.hh" #include "StaState.hh" -#include "search/Sim.hh" +#include "StringUtil.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "Units.hh" #include "WriteSpice.hh" +#include "search/Sim.hh" namespace sta { -typedef int Stage; +using Stage = int; //////////////////////////////////////////////////////////////// @@ -138,7 +139,7 @@ class WritePathSpice : public WriteSpice const Path *path_; PathExpanded path_expanded_; // Input clock waveform cycles. - int clk_cycle_count_; + int clk_cycle_count_{3}; InstanceSet written_insts_; @@ -184,7 +185,6 @@ WritePathSpice::WritePathSpice(const Path *path, path->scene(sta), path->minMax(sta), sta), path_(path), path_expanded_(sta), - clk_cycle_count_(3), written_insts_(network_) { initPowerGnd(); @@ -269,13 +269,11 @@ WritePathSpice::pathMaxTime() Edge *edge = edge_iter.next(); Vertex *load = edge->to(graph_); float load_slew = railToRailSlew(findSlew(load, rf, nullptr), rf); - if (load_slew > path_max_slew) - path_max_slew = load_slew; + path_max_slew = std::max(load_slew, path_max_slew); } } float path_max_time = delayAsFloat(path->arrival()) + path_max_slew * 2.0; - if (path_max_time > max_time) - max_time = path_max_time; + max_time = std::max(path_max_time, max_time); } return max_time; } diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 1823080c4..6d3f9395f 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -30,26 +30,25 @@ #include #include -#include "cudd.h" - +#include "Bdd.hh" +#include "Clock.hh" #include "ContainerHelpers.hh" #include "Debug.hh" -#include "Units.hh" -#include "TableModel.hh" -#include "TimingRole.hh" #include "FuncExpr.hh" -#include "Sequential.hh" -#include "PortDirection.hh" -#include "TimingArc.hh" +#include "Graph.hh" #include "Liberty.hh" +#include "Mode.hh" #include "Network.hh" -#include "Graph.hh" -#include "search/Sim.hh" -#include "Clock.hh" #include "Path.hh" -#include "Bdd.hh" +#include "PortDirection.hh" #include "Sdc.hh" -#include "Mode.hh" +#include "Sequential.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Units.hh" +#include "cudd.h" +#include "search/Sim.hh" namespace sta { @@ -78,10 +77,6 @@ WriteSpice::WriteSpice(std::string_view spice_filename, scene_(scene), min_max_(min_max), default_library_(network_->defaultLibertyLibrary()), - short_ckt_resistance_(.0001), - cap_index_(1), - res_index_(1), - volt_index_(1), bdd_(sta), parasitics_(scene->parasitics(min_max)) { diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index 2ac35b338..5230b72be 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -25,19 +25,19 @@ #pragma once #include +#include #include #include -#include #include +#include "Bdd.hh" +#include "CircuitSim.hh" #include "Format.hh" -#include "StaState.hh" -#include "StringUtil.hh" -#include "Liberty.hh" #include "GraphClass.hh" +#include "Liberty.hh" #include "Parasitics.hh" -#include "Bdd.hh" -#include "CircuitSim.hh" +#include "StaState.hh" +#include "StringUtil.hh" namespace sta { @@ -96,7 +96,7 @@ protected: const NetSet &coupling_nets); void writeParasiticNetwork(const Pin *drvr_pin, const Parasitic *parasitic, - const NetSet &aggressor_nets); + const NetSet &coupling_nets); void writePiElmore(const Pin *drvr_pin, const Parasitic *parasitic); void writeNullParasitic(const Pin *drvr_pin); @@ -181,12 +181,12 @@ protected: float gnd_voltage_; float max_time_; // Resistance to use to simulate a short circuit between spice nodes. - float short_ckt_resistance_; + float short_ckt_resistance_{0.0001F}; // Input clock waveform cycles. // Sequential device numbers. - int cap_index_; - int res_index_; - int volt_index_; + int cap_index_{1}; + int res_index_{1}; + int volt_index_{1}; CellSpicePortNames cell_spice_port_names_; Bdd bdd_; Parasitics *parasitics_; diff --git a/util/Error.cc b/util/Error.cc index 553bc29a8..2ca81a1de 100644 --- a/util/Error.cc +++ b/util/Error.cc @@ -32,10 +32,6 @@ namespace sta { -Exception::Exception() -{ -} - ExceptionMsg::ExceptionMsg(const std::string &msg, bool suppressed) : msg_(msg), diff --git a/util/FlexDisableRegister.hh b/util/FlexDisableRegister.hh index 64983ba71..52943946e 100644 --- a/util/FlexDisableRegister.hh +++ b/util/FlexDisableRegister.hh @@ -3,4 +3,3 @@ #if !(YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 6) && __cplusplus > 199711L #define register #endif - diff --git a/util/gzstream.hh b/util/gzstream.hh index 26e853528..26b16174d 100644 --- a/util/gzstream.hh +++ b/util/gzstream.hh @@ -211,4 +211,4 @@ public: } }; -} // namespace GZSTREAM_NAMESPACE +} // namespace gzstream diff --git a/verilog/Verilog.i b/verilog/Verilog.i index 3a60be304..82647cffa 100644 --- a/verilog/Verilog.i +++ b/verilog/Verilog.i @@ -25,8 +25,8 @@ %module verilog %{ -#include "VerilogWriter.hh" #include "Sta.hh" +#include "VerilogWriter.hh" %} %inline %{ diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy index c5a1c8cd8..3103c3be0 100644 --- a/verilog/VerilogParse.yy +++ b/verilog/VerilogParse.yy @@ -49,6 +49,31 @@ sta::VerilogParse::error(const location_type &loc, %} +%code requires { +#include +#include + +namespace sta { +class PortDirection; +class VerilogAssign; +class VerilogAttr; +class VerilogAttrEntry; +class VerilogAttrStmt; +class VerilogDclArg; +class VerilogNet; +class VerilogReader; +class VerilogScanner; +class VerilogStmt; + +using VerilogAttrEntrySeq = std::vector; +using VerilogAttrSeq = std::vector; +using VerilogAttrStmtSeq = std::vector; +using VerilogDclArgSeq = std::vector; +using VerilogNetSeq = std::vector; +using VerilogStmtSeq = std::vector; +} +} + %require "3.2" %skeleton "lalr1.cc" %debug diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index d9f767474..263acb4a9 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -24,27 +24,28 @@ #include "VerilogReader.hh" +#include #include #include #include #include "ContainerHelpers.hh" -#include "Zlib.hh" #include "Debug.hh" -#include "Report.hh" #include "Error.hh" -#include "Stats.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" -#include "VerilogNamespace.hh" +#include "PortDirection.hh" +#include "Report.hh" +#include "Stats.hh" #include "StringUtil.hh" +#include "VerilogNamespace.hh" +#include "Zlib.hh" #include "verilog/VerilogReaderPvt.hh" #include "verilog/VerilogScanner.hh" namespace sta { -using VerilogConstant10 = unsigned long long; +using VerilogConstant10 = std::uint64_t; static std::string verilogBusBitName(std::string_view bus_name, @@ -111,8 +112,6 @@ VerilogReader::VerilogReader(NetworkReader *network) : report_(network->report()), debug_(network->debug()), network_(network), - library_(nullptr), - black_box_index_(0), zero_net_name_("zero_"), one_net_name_("one_") { @@ -476,7 +475,7 @@ bool VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, VerilogNetSeq *pins) { - if (pins && pins->size() > 0 && (*pins)[0]->isNamedPortRef()) { + if (pins && !pins->empty() && (*pins)[0]->isNamedPortRef()) { for (VerilogNet *vpin : *pins) { std::string_view port_name = vpin->name(); LibertyPort *port = liberty_cell->findLibertyPort(port_name); @@ -724,13 +723,13 @@ VerilogModuleInst::~VerilogModuleInst() bool VerilogModuleInst::hasPins() { - return pins_ && pins_->size() > 0; + return pins_ && !pins_->empty(); } bool VerilogModuleInst::namedPins() { - return pins_ && pins_->size() > 0 && (*pins_)[0]->isNamedPortRef(); + return pins_ && !pins_->empty() && (*pins_)[0]->isNamedPortRef(); } VerilogLibertyInst::VerilogLibertyInst(LibertyCell *cell, @@ -879,12 +878,11 @@ class VerilogOneNetNameIterator : public VerilogNetNameIterator protected: std::string name_; - bool has_next_; + bool has_next_{true}; }; VerilogOneNetNameIterator::VerilogOneNetNameIterator(const std::string &name) : - name_(name), - has_next_(true) + name_(name) { } @@ -904,7 +902,7 @@ VerilogOneNetNameIterator::next() class VerilogBusNetNameIterator : public VerilogNetNameIterator { public: - VerilogBusNetNameIterator(const std::string bus_name, + VerilogBusNetNameIterator(std::string_view bus_name, int from_index, int to_index); bool hasNext() override; @@ -918,7 +916,7 @@ class VerilogBusNetNameIterator : public VerilogNetNameIterator std::string bit_name_; }; -VerilogBusNetNameIterator::VerilogBusNetNameIterator(const std::string bus_name, +VerilogBusNetNameIterator::VerilogBusNetNameIterator(std::string_view bus_name, int from_index, int to_index) : bus_name_(bus_name), @@ -1007,7 +1005,7 @@ class VerilogNetConcatNameIterator : public VerilogNetNameIterator VerilogReader *reader_; VerilogNetSeq *nets_; VerilogNetSeq::iterator net_iter_; - VerilogNetNameIterator *net_name_iter_; + VerilogNetNameIterator *net_name_iter_{nullptr}; }; VerilogNetConcatNameIterator::VerilogNetConcatNameIterator(VerilogNetSeq *nets, @@ -1016,8 +1014,7 @@ VerilogNetConcatNameIterator::VerilogNetConcatNameIterator(VerilogNetSeq *nets, module_(module), reader_(reader), nets_(nets), - net_iter_(nets->begin()), - net_name_iter_(nullptr) + net_iter_(nets->begin()) { if (net_iter_ != nets_->end()) { VerilogNet *net = *net_iter_++; diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh index e3569c619..c425a3878 100644 --- a/verilog/VerilogReaderPvt.hh +++ b/verilog/VerilogReaderPvt.hh @@ -24,12 +24,13 @@ #pragma once +#include #include #include #include -#include #include "StringUtil.hh" +#include "VerilogReader.hh" namespace sta { @@ -98,10 +99,10 @@ public: VerilogDclArg *arg, VerilogAttrStmtSeq *attr_stmts, int line); - virtual ~VerilogDcl(); + ~VerilogDcl() override; const std::string &portName(); virtual bool isBus() const { return false; } - virtual bool isDeclaration() const { return true; } + bool isDeclaration() const override { return true; } VerilogDclArgSeq *args() const { return args_; } void appendArg(VerilogDclArg *arg); PortDirection *direction() const { return dir_; } @@ -177,7 +178,7 @@ class VerilogInst : public VerilogStmt public: VerilogInst(std::string_view inst_name, VerilogAttrStmtSeq *attr_stmts, - const int line); + int line); ~VerilogInst() override; bool isInstance() const override { return true; } const std::string &instanceName() const { return inst_name_; } @@ -196,7 +197,7 @@ public: std::string_view inst_name, VerilogNetSeq *pins, VerilogAttrStmtSeq *attr_stmts, - const int line); + int line); ~VerilogModuleInst() override; bool isModuleInst() const override { return true; } const std::string &moduleName() const { return module_name_; } @@ -219,7 +220,7 @@ public: std::string_view inst_name, const StringSeq &net_names, VerilogAttrStmtSeq *attr_stmts, - const int line); + int line); bool isLibertyInst() const override { return true; } LibertyCell *cell() const { return cell_; } const StringSeq &netNames() const { return net_names_; } @@ -246,7 +247,6 @@ public: class VerilogNetUnnamed : public VerilogNet { public: - VerilogNetUnnamed() {} bool isNamed() const override { return false; } const std::string &name() const override { return null_; } diff --git a/verilog/VerilogScanner.hh b/verilog/VerilogScanner.hh index 92b0d395d..092c72857 100644 --- a/verilog/VerilogScanner.hh +++ b/verilog/VerilogScanner.hh @@ -39,6 +39,7 @@ namespace sta { class Report; +class VerilogReader; class VerilogScanner : public VerilogFlexLexer { @@ -46,7 +47,7 @@ public: VerilogScanner(std::istream *stream, std::string_view filename, Report *report); - virtual int lex(VerilogParse::semantic_type *const yylval, + virtual int lex(VerilogParse::semantic_type *yylval, VerilogParse::location_type *yylloc); // YY_DECL defined in VerilogLex.ll // Method body created by flex in VerilogLex.cc diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index 954c560a4..4f5e5fc66 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -33,11 +33,11 @@ #include "Error.hh" #include "Format.hh" #include "Liberty.hh" -#include "PortDirection.hh" #include "Network.hh" #include "NetworkCmp.hh" -#include "VerilogNamespace.hh" #include "ParseBus.hh" +#include "PortDirection.hh" +#include "VerilogNamespace.hh" namespace sta { @@ -84,7 +84,7 @@ class VerilogWriter CellSet remove_cells_; FILE *stream_; Network *network_; - int unconnected_net_index_; + int unconnected_net_index_{1}; }; void @@ -115,8 +115,7 @@ VerilogWriter::VerilogWriter(const char *filename, include_pwr_gnd_(include_pwr_gnd), remove_cells_(network), stream_(stream), - network_(network), - unconnected_net_index_(1) + network_(network) { if (remove_cells) { for(Cell *lib_cell : *remove_cells) From 134cf2cab701309c1a01c14cb7272c3d24d6101f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 15 Apr 2026 18:21:47 -0700 Subject: [PATCH 153/181] tidy Signed-off-by: James Cherry --- include/sta/Liberty.hh | 18 +++++++------- liberty/Liberty.cc | 9 ++++--- power/VcdReader.cc | 5 ---- tcl/TclTypeHelpers.cc | 48 +++++-------------------------------- util/Machine.cc | 33 ------------------------- util/RiseFallMinMax.cc | 11 ++++----- util/RiseFallMinMaxDelay.cc | 14 +++++------ util/Util.i | 17 ------------- 8 files changed, 30 insertions(+), 125 deletions(-) delete mode 100644 util/Machine.cc diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 5e5fef753..204431273 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -24,27 +24,27 @@ #pragma once -#include -#include -#include #include #include +#include +#include #include +#include #include #include #include -#include "ConcreteLibrary.hh" #include "ContainerHelpers.hh" -#include "Delay.hh" -#include "InternalPower.hh" -#include "LeakagePower.hh" -#include "LibertyClass.hh" #include "MinMax.hh" -#include "MinMaxValues.hh" #include "RiseFallMinMax.hh" +#include "ConcreteLibrary.hh" #include "RiseFallValues.hh" +#include "MinMaxValues.hh" #include "Transition.hh" +#include "Delay.hh" +#include "InternalPower.hh" +#include "LeakagePower.hh" +#include "LibertyClass.hh" namespace sta { diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index bdccfcbd2..8deb4a1dd 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -2978,11 +2978,10 @@ scaleFactorPvtName(ScaleFactorPvt pvt) ScaleFactors::ScaleFactors(std::string_view name) : name_(name) { - for (auto &scale : scales_) { - for (size_t pvt = 0; pvt < scale_factor_pvt_count; pvt++) { - for (size_t rf_index : RiseFall::rangeIndex()) { - scale[pvt][rf_index] = 0.0; - } + for (auto &type_factors : scales_) { + for (auto pvt_factors : type_factors) { + for (size_t rf_index : RiseFall::rangeIndex()) + pvt_factors[rf_index] = 0.0; } } } diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 663730229..a300c90f5 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -48,7 +48,6 @@ namespace sta { class VcdCount { public: - VcdCount(); double transitionCount() const { return transition_count_; } VcdTime highTime(VcdTime time_max) const; void incrCounts(VcdTime time, @@ -66,10 +65,6 @@ class VcdCount double transition_count_ = 0; }; -VcdCount::VcdCount() -{ -} - void VcdCount::addPin(const Pin *pin) { diff --git a/tcl/TclTypeHelpers.cc b/tcl/TclTypeHelpers.cc index 913a437f5..d9a50a41b 100644 --- a/tcl/TclTypeHelpers.cc +++ b/tcl/TclTypeHelpers.cc @@ -41,8 +41,8 @@ tclListStringSeq(Tcl_Obj *const source, if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { for (int i = 0; i < argc; i++) { Tcl_Size length; - const char *str = Tcl_GetStringFromObj(argv[i], &length); - seq.push_back(str); + const char *arg = Tcl_GetStringFromObj(argv[i], &length); + seq.emplace_back(arg); } } return seq; @@ -59,8 +59,8 @@ tclListStringSeqPtr(Tcl_Obj *const source, StringSeq *seq = new StringSeq; for (int i = 0; i < argc; i++) { Tcl_Size length; - const char *str = Tcl_GetStringFromObj(argv[i], &length); - seq->push_back(str); + const char *arg = Tcl_GetStringFromObj(argv[i], &length); + seq->emplace_back(arg); } return seq; } @@ -79,8 +79,8 @@ tclListStringSet(Tcl_Obj *const source, StringSet *set = new StringSet; for (int i = 0; i < argc; i++) { Tcl_Size length; - const char *str = Tcl_GetStringFromObj(argv[i], &length); - set->insert(str); + const char *arg = Tcl_GetStringFromObj(argv[i], &length); + set->insert(arg); } return set; } @@ -102,42 +102,6 @@ tclArgError(Tcl_Interp *interp, } } -void -objectListNext(const char *list, - const char *type, - // Return values. - bool &type_match, - const char *&next) -{ - // Default return values (failure). - type_match = false; - next = nullptr; - // _hexaddress_p_type - const char *s = list; - char ch = *s++; - if (ch == '_') { - while (*s && isxdigit(*s)) - s++; - if ((s - list - 1) == sizeof(void*) * 2 - && *s && *s++ == '_' - && *s && *s++ == 'p' - && *s && *s++ == '_') { - const char *t = type; - while (*s && *s != ' ') { - if (*s != *t) - return; - s++; - t++; - } - type_match = true; - if (*s) - next = s + 1; - else - next = nullptr; - } - } -} - Tcl_Obj * tclArcDcalcArg(ArcDcalcArg &gate, Tcl_Interp *interp) diff --git a/util/Machine.cc b/util/Machine.cc deleted file mode 100644 index 07e141829..000000000 --- a/util/Machine.cc +++ /dev/null @@ -1,33 +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. - -#if defined(_WIN32) - #include "MachineWin32.cc" -#elif defined(__APPLE__) - #include "MachineApple.cc" -#elif defined(__linux__) - #include "MachineLinux.cc" -#else - #include "MachineUnknown.cc" -#endif diff --git a/util/RiseFallMinMax.cc b/util/RiseFallMinMax.cc index 6c2566f4d..774fd5608 100644 --- a/util/RiseFallMinMax.cc +++ b/util/RiseFallMinMax.cc @@ -34,10 +34,9 @@ RiseFallMinMax::RiseFallMinMax() void RiseFallMinMax::clear() { - for (size_t rf_index = 0; rf_index Date: Wed, 15 Apr 2026 19:23:39 -0700 Subject: [PATCH 154/181] Machine.cc Signed-off-by: James Cherry --- CMakeLists.txt | 11 ++++++++++- util/MachineWin32.cc | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 33bc1edec..cf46f8449 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,7 +222,6 @@ set(STA_SOURCE util/Error.cc util/Fuzzy.cc util/Hash.cc - util/Machine.cc util/MinMax.cc util/PatternMatch.cc util/Report.cc @@ -239,6 +238,16 @@ set(STA_SOURCE verilog/VerilogWriter.cc ) +if(APPLE) + list(APPEND STA_SOURCE util/MachineApple.cc) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + list(APPEND STA_SOURCE util/MachineLinux.cc) +elseif(WIN32) + list(APPEND STA_SOURCE util/MachineWin32.cc) +else() + list(APPEND STA_SOURCE util/MachineUnknown.cc) +endif() + # Source files. set(STA_TCL_FILES tcl/Init.tcl diff --git a/util/MachineWin32.cc b/util/MachineWin32.cc index b2def31d7..2c2c36bf2 100644 --- a/util/MachineWin32.cc +++ b/util/MachineWin32.cc @@ -24,6 +24,7 @@ #include "Machine.hh" +#include #include #include // GetSystemInfo From 49d0ba2cfc87e3fd6750373b7fc22b71eb030b7a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 16 Apr 2026 09:59:29 -0700 Subject: [PATCH 155/181] github workflow retention Signed-off-by: James Cherry --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74255543d..65c53abb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,7 @@ jobs: name: artifact path: | build/install/* + retention-days: 1 - name: Upload Test Result uses: actions/upload-artifact@v7 From 12c59878eed722fa8a7639a50be1c2371f00d696 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 16 Apr 2026 15:13:28 -0700 Subject: [PATCH 156/181] prima non-ccs report dcalc resolves #418 Signed-off-by: James Cherry --- dcalc/PrimaDelayCalc.cc | 8 ++++++-- parasitics/ConcreteParasitics.cc | 10 ++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index a38da6ac8..86456936f 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -987,10 +987,14 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic); bool arg_fail = checkArgs(dcalc_args, scene, min_max); - if (arg_fail) + if (arg_fail) { + const RiseFall *rf = arc->toEdge()->asRiseFall(); + const Parasitic *reduced = table_dcalc_->findParasitic(drvr_pin, rf, + scene, min_max); return table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, - parasitic, load_pin_index_map, scene, + reduced, load_pin_index_map, scene, min_max, digits); + } else { GateTimingModel *model = arc->gateModel(scene, min_max); // Delay calc to find ceff. diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index e015d5e24..6ab2bcbb0 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -975,7 +975,10 @@ ConcreteParasitics::piModel(const Parasitic *parasitic, float &c1) const { const ConcreteParasitic *cparasitic = static_cast(parasitic); - cparasitic->piModel(c2, rpi, c1); + if (cparasitic->isPiModel()) + cparasitic->piModel(c2, rpi, c1); + else + criticalError(2700, "piModel called on non-PiElmore parasitic."); } void @@ -985,7 +988,10 @@ ConcreteParasitics::setPiModel(Parasitic *parasitic, float c1) { ConcreteParasitic *cparasitic = static_cast(parasitic); - cparasitic->setPiModel(c2, rpi, c1); + if (cparasitic->isPiModel()) + cparasitic->setPiModel(c2, rpi, c1); + else + criticalError(2701, "setPiModel called on non-PiElmore parasitic."); } //////////////////////////////////////////////////////////////// From 6e7ec45bc8e43bcde35f8ddf9f69f3744a55519c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 16 Apr 2026 15:46:32 -0700 Subject: [PATCH 157/181] rm extra swig module dcls Signed-off-by: James Cherry --- dcalc/DelayCalc.i | 2 -- graph/Graph.i | 2 -- liberty/Liberty.i | 2 -- network/Network.i | 2 -- network/NetworkEdit.i | 2 -- parasitics/Parasitics.i | 2 -- power/Power.i | 2 -- sdc/Sdc.i | 2 -- sdf/Sdf.i | 2 -- search/Property.i | 2 -- search/Search.i | 2 -- search/Search.tcl | 19 ++++++++++--------- spice/WriteSpice.i | 2 -- util/Util.i | 18 +----------------- verilog/Verilog.i | 2 -- 15 files changed, 11 insertions(+), 52 deletions(-) diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index 9aaa3f3fb..69e23059a 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module dcalc - %include %{ diff --git a/graph/Graph.i b/graph/Graph.i index 7b31ebc0e..d6fb17697 100644 --- a/graph/Graph.i +++ b/graph/Graph.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module graph - %{ #include "Clock.hh" #include "FuncExpr.hh" diff --git a/liberty/Liberty.i b/liberty/Liberty.i index e1cfdc57f..f431536f3 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module liberty - %{ #include "PatternMatch.hh" #include "PortDirection.hh" diff --git a/network/Network.i b/network/Network.i index f36a91a04..30cfff61c 100644 --- a/network/Network.i +++ b/network/Network.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module network - %include %{ diff --git a/network/NetworkEdit.i b/network/NetworkEdit.i index d52c2e1df..7c515f577 100644 --- a/network/NetworkEdit.i +++ b/network/NetworkEdit.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module NetworkEdit - %{ using sta::Cell; using sta::Instance; diff --git a/parasitics/Parasitics.i b/parasitics/Parasitics.i index 440945ecf..ab794949e 100644 --- a/parasitics/Parasitics.i +++ b/parasitics/Parasitics.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module parasitics - %{ #include "Sta.hh" diff --git a/power/Power.i b/power/Power.i index 52d77af79..8c2c42e36 100644 --- a/power/Power.i +++ b/power/Power.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module power - %{ #include "power/Power.hh" diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 996424b3a..1ed858068 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module sdc - %include "std_string.i" %{ diff --git a/sdf/Sdf.i b/sdf/Sdf.i index 271e9f60f..8cb3a6f1e 100644 --- a/sdf/Sdf.i +++ b/sdf/Sdf.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module sdf - %{ #include diff --git a/search/Property.i b/search/Property.i index d6d2425b7..01c8b4c11 100644 --- a/search/Property.i +++ b/search/Property.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module properties - %{ #include "Property.hh" diff --git a/search/Search.i b/search/Search.i index ae4e4c182..7a89abe86 100644 --- a/search/Search.i +++ b/search/Search.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module search - %include "std_string.i" %{ diff --git a/search/Search.tcl b/search/Search.tcl index 73ef64b37..dcc8f9dc9 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -366,6 +366,7 @@ define_cmd_args "report_check_types" \ [> filename] [>> filename]} proc_redirect report_check_types { + global sta::float_inf sta::group_path_count_max variable path_options parse_key_args "report_check_types" args \ @@ -484,13 +485,13 @@ proc_redirect report_check_types { set path_min_max "min" } if { $violators } { - set group_path_count $sta::group_path_count_max - set slack_min [expr -$sta::float_inf] + set group_path_count $group_path_count_max + set slack_min [expr -$float_inf] set slack_max 0.0 } else { set group_path_count 1 - set slack_min [expr -$sta::float_inf] - set slack_max $sta::float_inf + set slack_min [expr -$float_inf] + set slack_max $float_inf } set path_ends [find_path_ends "NULL" {} "NULL" 0 \ @@ -971,7 +972,7 @@ proc_redirect report_clock_min_period { set include_port_paths [info exists flags(-include_port_paths)] foreach clk $clks { - set min_period [sta::find_clk_min_period $clk $include_port_paths] + set min_period [find_clk_min_period $clk $include_port_paths] if { $min_period == 0.0 } { set min_period 0 set fmax "INF" @@ -979,7 +980,7 @@ proc_redirect report_clock_min_period { # max frequency in MHz set fmax [expr 1.0e-6 / $min_period] } - report_line "[get_name $clk] period_min = [sta::format_time $min_period 2] fmax = [format %.2f $fmax]" + report_line "[get_name $clk] period_min = [format_time $min_period 2] fmax = [format %.2f $fmax]" } } @@ -1023,17 +1024,17 @@ proc unset_disable_inferred_clock_gating_cmd { objects } { # max slew slack / limit proc max_slew_check_slack_limit {} { - return [expr "[sta::max_slew_check_slack] / [sta::max_slew_check_limit]"] + return [expr "[max_slew_check_slack] / [max_slew_check_limit]"] } # max cap slack / limit proc max_capacitance_check_slack_limit {} { - return [expr [sta::max_capacitance_check_slack] / [sta::max_capacitance_check_limit]] + return [expr [max_capacitance_check_slack] / [max_capacitance_check_limit]] } # max fanout slack / limit proc max_fanout_check_slack_limit {} { - return [expr [sta::max_fanout_check_slack] / [sta::max_fanout_check_limit]] + return [expr [max_fanout_check_slack] / [max_fanout_check_limit]] } ################################################################ diff --git a/spice/WriteSpice.i b/spice/WriteSpice.i index a6d5dbec3..65bdda1fc 100644 --- a/spice/WriteSpice.i +++ b/spice/WriteSpice.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module write_gate_spice - %{ #include "spice/WritePathSpice.hh" diff --git a/util/Util.i b/util/Util.i index 5d1430542..cb08647a4 100644 --- a/util/Util.i +++ b/util/Util.i @@ -22,7 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module util %include %{ @@ -40,16 +39,9 @@ using namespace sta; %} -//////////////////////////////////////////////////////////////// -// -// Empty class definitions to make swig happy. -// Private constructor/destructor so swig doesn't emit them. -// -//////////////////////////////////////////////////////////////// - %inline %{ -float float_inf = INF; +const float float_inf = INF; const char * version() @@ -544,11 +536,3 @@ fuzzy_equal(float value1, } %} // inline - -//////////////////////////////////////////////////////////////// -// -// Object Methods -// -//////////////////////////////////////////////////////////////// - - diff --git a/verilog/Verilog.i b/verilog/Verilog.i index 82647cffa..75ecef335 100644 --- a/verilog/Verilog.i +++ b/verilog/Verilog.i @@ -22,8 +22,6 @@ // // This notice may not be removed or altered from any source distribution. -%module verilog - %{ #include "Sta.hh" #include "VerilogWriter.hh" From 6bcf7b81561b5d2a71c652e3828f28d68b396d9d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 16 Apr 2026 16:12:59 -0700 Subject: [PATCH 158/181] rm unnecessary sta:: in tcl Signed-off-by: James Cherry --- power/Power.tcl | 8 ++++---- tcl/Splash.tcl | 2 +- tcl/Sta.tcl | 2 +- test/get_filter.tcl | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/power/Power.tcl b/power/Power.tcl index 1c624e5be..71cf563cd 100644 --- a/power/Power.tcl +++ b/power/Power.tcl @@ -135,7 +135,7 @@ proc set_power_activity { args } { sta_error 307 "-activity requires a clock to be defined" } } - set density [expr $activity / [clock_min_period [sta::cmd_mode]]] + set density [expr $activity / [clock_min_period [cmd_mode_name]]] } if { [info exists keys(-density)] } { @@ -165,7 +165,7 @@ proc set_power_activity { args } { set ports [get_ports_error "input_ports" $keys(-input_ports)] foreach port $ports { if { [get_property $port "direction"] == "input" } { - if { [is_clock_src [sta::get_port_pin $port]] } { + if { [is_clock_src [get_port_pin $port]] } { sta_warn 310 "activity cannot be set on clock ports." } else { set_power_input_port_activity $port $density $duty @@ -206,7 +206,7 @@ proc unset_power_activity { args } { set ports [get_ports_error "input_ports" $keys(-input_ports)] foreach port $ports { if { [get_property $port "direction"] == "input" } { - if { [is_clock_src [sta::get_port_pin $port]] } { + if { [is_clock_src [get_port_pin $port]] } { sta_warn 303 "activity cannot be set on clock ports." } else { unset_power_input_port_activity $port @@ -255,7 +255,7 @@ proc read_vcd { args } { if { [info exists keys(-scope)] } { set scope $keys(-scope) } - set mode_name [sta::cmd_mode] + set mode_name [cmd_mode_name] if { [info exists keys(-mode)] } { set mode_name $keys(-mode) } diff --git a/tcl/Splash.tcl b/tcl/Splash.tcl index 701fcf16d..3dcf1838a 100644 --- a/tcl/Splash.tcl +++ b/tcl/Splash.tcl @@ -33,7 +33,7 @@ namespace eval sta { define_cmd_args show_splash {} proc show_splash {} { - report_line "OpenSTA [sta::version] [string range [sta::git_sha1] 0 9] Copyright (c) 2026, Parallax Software, Inc. + report_line "OpenSTA [version] [string range [git_sha1] 0 9] Copyright (c) 2026, Parallax Software, Inc. License GPLv3: GNU GPL version 3 This is free software, and you are free to change and redistribute it diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index 8796df34b..2db64f5e4 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -47,7 +47,7 @@ proc define_scene { args } { if { [info exists keys(-mode)] } { set mode_name $keys(-mode) } else { - set mode_name [sta::cmd_mode_name] + set mode_name [cmd_mode_name] } set liberty_min_files {} diff --git a/test/get_filter.tcl b/test/get_filter.tcl index b373e9440..4aa3abc43 100644 --- a/test/get_filter.tcl +++ b/test/get_filter.tcl @@ -70,4 +70,3 @@ report_object_full_names [get_clocks -filter is_virtual==true *] report_object_full_names [get_cells -filter {name =~ .1} *] puts [sta::filter_expr_to_postfix "direction == input && name =~ clk* && is_clock"] - From e629909aafae64fb358aff672c0c71e3004ffecb Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 16 Apr 2026 16:37:36 -0700 Subject: [PATCH 159/181] report_check_types $422 Signed-off-by: James Cherry --- cmake/FindTCL.cmake | 2 ++ search/Search.tcl | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/FindTCL.cmake b/cmake/FindTCL.cmake index 17cb3a625..7df9b71d3 100644 --- a/cmake/FindTCL.cmake +++ b/cmake/FindTCL.cmake @@ -23,6 +23,7 @@ # searching OSX system directories before unix directories. set(TCL_POSSIBLE_NAMES + #tcl90 tcl9.0 tcl87 tcl8.7 tcl86 tcl8.6 tcl85 tcl8.5 @@ -32,6 +33,7 @@ set(TCL_POSSIBLE_NAMES if (NOT TCL_LIB_PATHS) if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(TCL_LIB_PATHS + #/opt/homebrew/Cellar/tcl-tk/9.0.3/lib /opt/homebrew/Cellar/tcl-tk@8/8.6.17/lib /opt/homebrew/Cellar/tcl-tk@8/8.6.16/lib /opt/homebrew/opt/tcl-tk/lib /usr/local/lib) diff --git a/search/Search.tcl b/search/Search.tcl index dcc8f9dc9..113942924 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -366,7 +366,8 @@ define_cmd_args "report_check_types" \ [> filename] [>> filename]} proc_redirect report_check_types { - global sta::float_inf sta::group_path_count_max + variable float_inf + variable group_path_count_max variable path_options parse_key_args "report_check_types" args \ From 7838986dc4206ee0307353afb88993a95275b333 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 16 Apr 2026 18:42:26 -0700 Subject: [PATCH 160/181] Clock embed sequence members Signed-off-by: James Cherry --- include/sta/Clock.hh | 27 +++++----- include/sta/Sdc.hh | 12 ++--- include/sta/Sta.hh | 10 ++-- power/Power.cc | 6 +-- sdc/Clock.cc | 124 +++++++++++++++++-------------------------- sdc/Sdc.cc | 58 ++++++++++---------- sdc/Sdc.i | 20 +++---- sdc/WriteSdc.cc | 24 ++++----- sdc/WriteSdcPvt.hh | 4 +- search/Genclks.cc | 2 +- search/Sta.cc | 10 ++-- tcl/StaTclTypes.i | 31 ++++++++--- 12 files changed, 157 insertions(+), 171 deletions(-) diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index 48cfb88d2..8acc1e458 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -57,8 +57,7 @@ public: const Pin *defaultPin() const; bool addToPins() const { return add_to_pins_; } void setAddToPins(bool add_to_pins); - FloatSeq *waveform() { return waveform_; } - const FloatSeq *waveform() const { return waveform_; } + const FloatSeq &waveform() const { return waveform_; } ClockEdge *edge(const RiseFall *rf) const; int index() const { return index_; } bool isPropagated() const { return is_propagated_; } @@ -120,8 +119,8 @@ public: int multiplyBy() const { return multiply_by_; } float dutyCycle() const { return duty_cycle_; } bool invert() const { return invert_; } - IntSeq *edges() const { return edges_; } - FloatSeq *edgeShifts() const { return edge_shifts_; } + const IntSeq &edges() const { return edges_; } + const FloatSeq &edgeShifts() const { return edge_shifts_; } const RiseFall *masterClkEdgeTr(const RiseFall *rf) const; bool combinational() const { return combinational_; } bool isDivideByOneCombinational() const; @@ -138,13 +137,13 @@ protected: Clock(std::string_view name, int index, const Network *network); - void initClk(PinSet *pins, + void initClk(const PinSet &pins, bool add_to_pins, float period, - FloatSeq *waveform, + const FloatSeq &waveform, std::string_view comment, const Network *network); - void initGeneratedClk(PinSet *pins, + void initGeneratedClk(const PinSet &pins, bool add_to_pins, Pin *src_pin, Clock *master_clk, @@ -153,12 +152,12 @@ protected: float duty_cycle, bool invert, bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, + const IntSeq &edges, + const FloatSeq &edge_shifts, bool is_propagated, std::string_view comment, const Network *network); - void setPins(PinSet *pins, + void setPins(const PinSet &pins, const Network *network); void setMasterClk(Clock *master); void makeClkEdges(); @@ -174,10 +173,10 @@ protected: // Hierarchical pins in pins_ become driver pins through the pin. PinSet leaf_pins_; float period_{0.0}; - FloatSeq *waveform_{nullptr}; + FloatSeq waveform_; bool waveform_valid_{false}; const int index_; - ClockEdge **clk_edges_{nullptr}; + std::array clk_edges_; bool is_propagated_{false}; RiseFallMinMax slews_; RiseFallMinMax slew_limits_[path_clk_or_data_count]; @@ -193,8 +192,8 @@ protected: float duty_cycle_{0}; bool invert_{false}; bool combinational_{false}; - IntSeq *edges_{nullptr}; - FloatSeq *edge_shifts_{nullptr}; + IntSeq edges_; + FloatSeq edge_shifts_; private: friend class Sdc; diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index 4d3f010d6..3f7cd8e44 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -378,14 +378,14 @@ public: void setMaxArea(float area); float maxArea() const; Clock *makeClock(std::string_view name, - PinSet *pins, + const PinSet &pins, bool add_to_pins, float period, - FloatSeq *waveform, + const FloatSeq &waveform, std::string_view comment); // edges size must be 3. Clock *makeGeneratedClock(std::string_view name, - PinSet *pins, + const PinSet &pins, bool add_to_pins, Pin *src_pin, Clock *master_clk, @@ -394,8 +394,8 @@ public: float duty_cycle, bool invert, bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, + const IntSeq &edges, + const FloatSeq &dge_shifts, std::string_view comment); // Invalidate all generated clock waveforms. void invalidateGeneratedClks() const; @@ -1071,7 +1071,7 @@ protected: void deleteClkPinMappings(Clock *clk); void makeClkPinMappings(Clock *clk); void deletePinClocks(Clock *defining_clk, - PinSet *pins); + const PinSet &pins); void makeDefaultArrivalClock(); InputDrive *ensureInputDrive(const Port *port); ExceptionPath *findMergeMatch(ExceptionPath *exception); diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index a8e80c552..7be5c95e1 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -347,15 +347,15 @@ public: Sdc *sdc); void makeClock(std::string_view name, - PinSet *pins, + const PinSet &pins, bool add_to_pins, float period, - FloatSeq *waveform, + const FloatSeq &waveform, std::string_view comment, const Mode *mode); // edges size must be 3. void makeGeneratedClock(std::string_view name, - PinSet *pins, + const PinSet &pins, bool add_to_pins, Pin *src_pin, Clock *master_clk, @@ -364,8 +364,8 @@ public: float duty_cycle, bool invert, bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, + const IntSeq &edges, + const FloatSeq &edge_shifts, std::string_view comment, const Mode *mode); void removeClock(Clock *clk, diff --git a/power/Power.cc b/power/Power.cc index da1b5e08b..f5333f62e 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -1584,9 +1584,9 @@ Power::clockDuty(const Clock *clk) return clockDuty(master); } else { - const FloatSeq *waveform = clk->waveform(); - float rise_time = (*waveform)[0]; - float fall_time = (*waveform)[1]; + const FloatSeq &waveform = clk->waveform(); + float rise_time = waveform[0]; + float fall_time = waveform[1]; float duty = (fall_time - rise_time) / clk->period(); return duty; } diff --git a/sdc/Clock.cc b/sdc/Clock.cc index d38e384b3..c9d4d11a2 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -55,17 +55,16 @@ Clock::Clock(std::string_view name, } void -Clock::initClk(PinSet *pins, +Clock::initClk(const PinSet &pins, bool add_to_pins, float period, - FloatSeq *waveform, + const FloatSeq &waveform, std::string_view comment, const Network *network) { is_generated_ = false; setPins(pins, network); add_to_pins_ = add_to_pins; - delete waveform_; waveform_ = waveform; waveform_valid_ = true; period_ = period; @@ -80,12 +79,10 @@ Clock::isVirtual() const } void -Clock::setPins(PinSet *pins, +Clock::setPins(const PinSet &pins, const Network *network) { - if (pins) - pins_ = *pins; - delete pins; + pins_ = pins; makeLeafPins(network); } @@ -107,23 +104,15 @@ Clock::setMasterClk(Clock *master) void Clock::makeClkEdges() { - clk_edges_ = new ClockEdge*[RiseFall::index_count]; - for (auto rf : RiseFall::range()) { + for (const RiseFall *rf : RiseFall::range()) { clk_edges_[rf->index()] = new ClockEdge(this, rf); } } Clock::~Clock() { - if (clk_edges_) { - delete clk_edges_[RiseFall::riseIndex()]; - delete clk_edges_[RiseFall::fallIndex()]; - delete [] clk_edges_; - } - delete waveform_; - delete edges_; - delete edge_shifts_; - delete uncertainties_; + for (size_t rf_index : RiseFall::rangeIndex()) + delete clk_edges_[rf_index]; } void @@ -155,7 +144,7 @@ Clock::setClkEdgeTimes() void Clock::setClkEdgeTime(const RiseFall *rf) { - float time = (rf == RiseFall::rise()) ? (*waveform_)[0]:(*waveform_)[1]; + float time = waveform_[rf->index()]; clk_edges_[rf->index()]->setTime(time); } @@ -298,7 +287,7 @@ Clock::waveformInvalid() //////////////////////////////////////////////////////////////// void -Clock::initGeneratedClk(PinSet *pins, +Clock::initGeneratedClk(const PinSet &pins, bool add_to_pins, Pin *src_pin, Clock *master_clk, @@ -307,8 +296,8 @@ Clock::initGeneratedClk(PinSet *pins, float duty_cycle, bool invert, bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, + const IntSeq &edges, + const FloatSeq &edge_shifts, bool is_propagated, std::string_view comment, const Network *network) @@ -328,20 +317,7 @@ Clock::initGeneratedClk(PinSet *pins, is_propagated_ = is_propagated; setComment(comment); - delete edges_; - if (edges - && edges->empty()) { - delete edges; - edges = nullptr; - } edges_ = edges; - - delete edge_shifts_; - if (edge_shifts - && edge_shifts->empty()) { - delete edge_shifts; - edge_shifts = nullptr; - } edge_shifts_ = edge_shifts; } @@ -371,40 +347,36 @@ Clock::isGeneratedWithPropagatedMaster() const void Clock::generate(const Clock *src_clk) { - if (waveform_ == nullptr) - waveform_ = new FloatSeq; - else - waveform_->clear(); - + waveform_.clear(); if (divide_by_ == 1.0) { period_ = src_clk->period(); - const FloatSeq *src_wave = src_clk->waveform(); - waveform_->push_back((*src_wave)[0]); - waveform_->push_back((*src_wave)[1]); + const FloatSeq &src_wave = src_clk->waveform(); + waveform_.push_back(src_wave[0]); + waveform_.push_back(src_wave[1]); } else if (divide_by_ > 1) { if (isPowerOfTwo(divide_by_)) { period_ = src_clk->period() * divide_by_; - const FloatSeq *src_wave = src_clk->waveform(); - float rise = (*src_wave)[0]; - waveform_->push_back(rise); - waveform_->push_back(rise + period_ / 2); + const FloatSeq &src_wave = src_clk->waveform(); + float rise = src_wave[0]; + waveform_.push_back(rise); + waveform_.push_back(rise + period_ / 2); } else generateScaledClk(src_clk, static_cast(divide_by_)); } else if (multiply_by_ >= 1) generateScaledClk(src_clk, 1.0F / multiply_by_); - else if (edges_) + else if (!edges_.empty()) generateEdgesClk(src_clk); if (invert_) { - float first_time = (*waveform_)[0]; + float first_time = waveform_[0]; float offset = (first_time >= period_) ? period_ : 0.0F; - size_t edge_count = waveform_->size(); + size_t edge_count = waveform_.size(); for (size_t i = 0; i < edge_count - 1; i++) - (*waveform_)[i] = (*waveform_)[i + 1] - offset; - (*waveform_)[edge_count - 1] = first_time - offset + period_; + waveform_[i] = waveform_[i + 1] - offset; + waveform_[edge_count - 1] = first_time - offset + period_; } setClkEdgeTimes(); waveform_valid_ = true; @@ -416,13 +388,13 @@ Clock::generateScaledClk(const Clock *src_clk, { period_ = src_clk->period() * scale; if (duty_cycle_ != 0.0) { - float rise = (*src_clk->waveform())[0] * scale; - waveform_->push_back(rise); - waveform_->push_back(rise + period_ * duty_cycle_ / 100.0F); + float rise = src_clk->waveform()[0] * scale; + waveform_.push_back(rise); + waveform_.push_back(rise + period_ * duty_cycle_ / 100.0F); } else { - for (float time : *src_clk->waveform()) - waveform_->push_back(time * scale); + for (float time : src_clk->waveform()) + waveform_.push_back(time * scale); } } @@ -431,34 +403,34 @@ Clock::generateEdgesClk(const Clock *src_clk) { // The create_generated_clock tcl cmd and Sta::makeClock // enforce this restriction. - if (edges_->size() == 3) { - const FloatSeq *src_wave = src_clk->waveform(); - size_t src_size = src_wave->size(); + if (edges_.size() == 3) { + const FloatSeq &src_wave = src_clk->waveform(); + size_t src_size = src_wave.size(); int src_size_int = static_cast(src_size); float src_period = src_clk->period(); - int edge0_1 = (*edges_)[0] - 1; + int edge0_1 = edges_[0] - 1; div_t edge0_div = std::div(edge0_1, src_size_int); - float rise = (*src_wave)[edge0_div.rem] + float rise = src_wave[edge0_div.rem] + static_cast(edge0_div.quot) * src_period; - if (edge_shifts_) - rise += (*edge_shifts_)[0]; - waveform_->push_back(rise); + if (!edge_shifts_.empty()) + rise += edge_shifts_[0]; + waveform_.push_back(rise); - int edge1_1 = (*edges_)[1] - 1; + int edge1_1 = edges_[1] - 1; div_t edge1_div = std::div(edge1_1, src_size_int); - float fall = (*src_wave)[edge1_div.rem] + float fall = src_wave[edge1_div.rem] + static_cast(edge1_div.quot) * src_period; - if (edge_shifts_) - fall += (*edge_shifts_)[1]; - waveform_->push_back(fall); + if (!edge_shifts_.empty()) + fall += edge_shifts_[1]; + waveform_.push_back(fall); - int edge2_1 = (*edges_)[2] - 1; + int edge2_1 = edges_[2] - 1; div_t edge2_div = std::div(edge2_1, src_size_int); - period_ = (*src_wave)[edge2_div.rem] + period_ = src_wave[edge2_div.rem] + static_cast(edge2_div.quot) * src_period - rise; - if (edge_shifts_) - period_ += (*edge_shifts_)[2]; + if (!edge_shifts_.empty()) + period_ += edge_shifts_[2]; } else criticalError(244, "generated clock edges size is not three."); @@ -474,7 +446,7 @@ const RiseFall * Clock::masterClkEdgeTr(const RiseFall *rf) const { int edge_index = (rf == RiseFall::rise()) ? 0 : 1; - return ((*edges_)[edge_index] - 1) % 2 + return (edges_[edge_index] - 1) % 2 ? RiseFall::fall() : RiseFall::rise(); } @@ -509,7 +481,7 @@ Clock::isDivideByOneCombinational() const return combinational_ && divide_by_ == 1 && multiply_by_ == 0 - && edge_shifts_ == nullptr; + && edge_shifts_.empty(); } //////////////////////////////////////////////////////////////// diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index ed8b54acd..fb9b9c750 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -148,9 +148,9 @@ Sdc::Sdc(Mode *mode, void Sdc::makeDefaultArrivalClock() { - FloatSeq *waveform = new FloatSeq; - waveform->push_back(0.0); - waveform->push_back(0.0); + FloatSeq waveform; + waveform.push_back(0.0); + waveform.push_back(0.0); default_arrival_clk_ = new Clock("input port clock", clk_index_++, network_); default_arrival_clk_->initClk(nullptr, false, 0.0, waveform, "", network_); } @@ -959,10 +959,10 @@ Sdc::maxArea() const Clock * Sdc::makeClock(std::string_view name, - PinSet *pins, + const PinSet &pins, bool add_to_pins, float period, - FloatSeq *waveform, + const FloatSeq &waveform, std::string_view comment) { Clock *clk = findStringKey(clock_name_map_, name); @@ -989,7 +989,7 @@ Sdc::makeClock(std::string_view name, Clock * Sdc::makeGeneratedClock(std::string_view name, - PinSet *pins, + const PinSet &pins, bool add_to_pins, Pin *src_pin, Clock *master_clk, @@ -998,8 +998,8 @@ Sdc::makeGeneratedClock(std::string_view name, float duty_cycle, bool invert, bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, + const IntSeq &edges, + const FloatSeq &edge_shifts, std::string_view comment) { Clock *clk = findStringKey(clock_name_map_, name); @@ -1039,32 +1039,30 @@ Sdc::invalidateGeneratedClks() const // is not the clock being defined and has no pins it is removed. void Sdc::deletePinClocks(Clock *defining_clk, - PinSet *pins) + const PinSet &pins) { // Find all the clocks defined on pins to avoid finding the clock's // vertex pins multiple times. - if (pins) { - ClockSet clks; - for (const Pin *pin : *pins) { - ClockSet *pin_clks = findKey(clock_pin_map_, pin); - if (pin_clks) { - for (Clock *clk : *pin_clks) - clks.insert(clk); - } + ClockSet clks; + for (const Pin *pin : pins) { + ClockSet *pin_clks = findKey(clock_pin_map_, pin); + if (pin_clks) { + for (Clock *clk : *pin_clks) + clks.insert(clk); } - for (Clock *clk : clks) { - deleteClkPinMappings(clk); - for (const Pin *pin : *pins) - clk->deletePin(pin); - if (clk != defining_clk) { - if (clk->pins().empty()) - removeClock(clk); - else { - clk->makeLeafPins(network_); - // One of the remaining clock pins may use a vertex pin that - // was deleted above. - makeClkPinMappings(clk); - } + } + for (Clock *clk : clks) { + deleteClkPinMappings(clk); + for (const Pin *pin : pins) + clk->deletePin(pin); + if (clk != defining_clk) { + if (clk->pins().empty()) + removeClock(clk); + else { + clk->makeLeafPins(network_); + // One of the remaining clock pins may use a vertex pin that + // was deleted above. + makeClkPinMappings(clk); } } } diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 1ed858068..0a02cb07b 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -292,21 +292,21 @@ set_net_resistance(Net *net, void make_clock(std::string name, - PinSet *pins, + PinSet pins, bool add_to_pins, float period, - FloatSeq *waveform, + FloatSeq waveform, std::string comment) { Sta *sta = Sta::sta(); const Mode *mode = sta->cmdMode(); - sta->makeClock(name.c_str(), pins, add_to_pins, period, waveform, - std::move(comment), mode); + sta->makeClock(name, pins, add_to_pins, period, waveform, + comment, mode); } void make_generated_clock(std::string name, - PinSet *pins, + PinSet pins, bool add_to_pins, Pin *src_pin, Clock *master_clk, @@ -315,17 +315,17 @@ make_generated_clock(std::string name, float duty_cycle, bool invert, bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, + IntSeq edges, + FloatSeq edge_shifts, std::string comment) { Sta *sta = Sta::sta(); const Mode *mode = sta->cmdMode(); - sta->makeGeneratedClock(name.c_str(), pins, add_to_pins, + sta->makeGeneratedClock(name, pins, add_to_pins, src_pin, master_clk, divide_by, multiply_by, duty_cycle, invert, combinational, edges, edge_shifts, - std::move(comment), mode); + comment, mode); } void @@ -1673,7 +1673,7 @@ pin_is_constrained(const Pin *pin) %extend Clock { float period() { return self->period(); } -FloatSeq *waveform() { return self->waveform(); } +FloatSeq waveform() { return self->waveform(); } float time(RiseFall *rf) { return self->edge(rf)->time(); } bool is_generated() { return self->isGenerated(); } bool waveform_valid() { return self->waveformValid(); } diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index 8f6d8caa2..1733a050f 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -419,10 +419,10 @@ WriteSdc::writeClock(Clock *clk) const 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))) { + const FloatSeq &waveform = clk->waveform(); + if (!(waveform.size() == 2 + && waveform[0] == 0.0 + && fuzzyEqual(waveform[1], period / 2.0))) { sta::print(stream_, " -waveform "); writeFloatSeq(waveform, scaleTime(1.0)); } @@ -461,12 +461,12 @@ WriteSdc::writeGeneratedClock(Clock *clk) const } if (clk->invert()) sta::print(stream_, " -invert"); - IntSeq *edges = clk->edges(); - if (edges && !edges->empty()) { + const IntSeq &edges = clk->edges(); + if (!edges.empty()) { sta::print(stream_, " -edges "); writeIntSeq(edges); - FloatSeq *edge_shifts = clk->edgeShifts(); - if (edge_shifts && !edge_shifts->empty()) { + const FloatSeq &edge_shifts = clk->edgeShifts(); + if (!edge_shifts.empty()) { sta::print(stream_, " -edge_shift "); writeFloatSeq(edge_shifts, scaleTime(1.0)); } @@ -2762,12 +2762,12 @@ WriteSdc::writeResistance(float res) const } void -WriteSdc::writeFloatSeq(FloatSeq *floats, +WriteSdc::writeFloatSeq(const FloatSeq &floats, float scale) const { sta::print(stream_, "{{"); bool first = true; - for (float flt : *floats) { + for (float flt : floats) { if (!first) sta::print(stream_, " "); writeFloat(flt * scale); @@ -2777,11 +2777,11 @@ WriteSdc::writeFloatSeq(FloatSeq *floats, } void -WriteSdc::writeIntSeq(IntSeq *ints) const +WriteSdc::writeIntSeq(const IntSeq &ints) const { sta::print(stream_, "{{"); bool first = true; - for (int i : *ints) { + for (int i : ints) { if (!first) sta::print(stream_, " "); sta::print(stream_, "{}", i); diff --git a/sdc/WriteSdcPvt.hh b/sdc/WriteSdcPvt.hh index 94a1a5912..b7bc93385 100644 --- a/sdc/WriteSdcPvt.hh +++ b/sdc/WriteSdcPvt.hh @@ -72,9 +72,9 @@ public: void writeClock(Clock *clk) const; void writeGeneratedClock(Clock *clk) const; void writeClockPins(const Clock *clk) const; - void writeFloatSeq(FloatSeq *floats, + void writeFloatSeq(const FloatSeq &floats, float scale) const; - void writeIntSeq(IntSeq *ints) const; + void writeIntSeq(const IntSeq &ints) const; void writeClockSlews(const Clock *clk) const; void writeClockUncertainty(const Clock *clk) const; void writeClockUncertainty(const Clock *clk, diff --git a/search/Genclks.cc b/search/Genclks.cc index 48dbea192..2123ea1bb 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -833,7 +833,7 @@ Genclks::recordSrcPaths(Clock *gclk) bool divide_by_1 = gclk->isDivideByOneCombinational(); bool invert = gclk->invert(); - bool has_edges = gclk->edges() != nullptr; + bool has_edges = !gclk->edges().empty(); for (const Pin *gclk_pin : gclk->leafPins()) { std::vector &src_paths = genclk_src_paths_[ClockPinPair(gclk, gclk_pin)]; diff --git a/search/Sta.cc b/search/Sta.cc index c6230d42e..de546ca2f 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -1149,10 +1149,10 @@ Sta::setMaxArea(float area, void Sta::makeClock(std::string_view name, - PinSet *pins, + const PinSet &pins, bool add_to_pins, float period, - FloatSeq *waveform, + const FloatSeq &waveform, std::string_view comment, const Mode *mode) { @@ -1165,7 +1165,7 @@ Sta::makeClock(std::string_view name, void Sta::makeGeneratedClock(std::string_view name, - PinSet *pins, + const PinSet &pins, bool add_to_pins, Pin *src_pin, Clock *master_clk, @@ -1174,8 +1174,8 @@ Sta::makeGeneratedClock(std::string_view name, float duty_cycle, bool invert, bool combinational, - IntSeq *edges, - FloatSeq *edge_shifts, + const IntSeq &edges, + const FloatSeq &edge_shifts, std::string_view comment, const Mode *mode) { diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 26d5443d9..65a6b0f38 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -751,8 +751,28 @@ using namespace sta; Tcl_SetObjResult(interp, list); } +%typemap(in) FloatSeq { + Tcl_Size argc; + Tcl_Obj **argv; + FloatSeq floats; + + if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK) { + for (int i = 0; i < argc; i++) { + char *arg = Tcl_GetString(argv[i]); + double value; + if (Tcl_GetDouble(interp, arg, &value) == TCL_OK) + floats.push_back(static_cast(value)); + else { + tclArgError(interp, 2175, "{} is not a floating point number.", arg); + return TCL_ERROR; + } + } + } + $1 = floats; +} + %typemap(out) FloatSeq { - FloatSeq &floats = $1; + const FloatSeq &floats = $1; Tcl_Obj *list = Tcl_NewListObj(0, nullptr); for (float f : floats) { Tcl_Obj *obj = Tcl_NewDoubleObj(f); @@ -761,21 +781,18 @@ using namespace sta; Tcl_SetObjResult(interp, list); } -%typemap(in) IntSeq* { +%typemap(in) IntSeq { Tcl_Size argc; Tcl_Obj **argv; - IntSeq *ints = nullptr; + IntSeq ints; if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK) { - if (argc) - ints = new IntSeq; for (int i = 0; i < argc; i++) { char *arg = Tcl_GetString(argv[i]); int value; if (Tcl_GetInt(interp, arg, &value) == TCL_OK) - ints->push_back(value); + ints.push_back(value); else { - delete ints; tclArgError(interp, 2158, "{} is not an integer.", arg); return TCL_ERROR; } From 53a1552ab05aa10c8fc2b3bdea434a27daedddc3 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 17 Apr 2026 13:52:44 -0700 Subject: [PATCH 161/181] timing_arc full_name Signed-off-by: James Cherry --- search/Property.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/search/Property.cc b/search/Property.cc index 322cf00ce..bef9ebb00 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -42,6 +42,7 @@ #include "Sta.hh" #include "StringUtil.hh" #include "TimingArc.hh" +#include "TimingRole.hh" #include "Transition.hh" #include "Units.hh" #include "power/Power.hh" @@ -1152,10 +1153,11 @@ Properties::getProperty(TimingArcSet *arc_set, if (arc_set->isWire()) return PropertyValue("wire"); else { - std::string name = sta::format("{} {} -> {}", + std::string name = sta::format("{} {} -> {} {}", arc_set->libertyCell()->name(), arc_set->from()->name(), - arc_set->to()->name()); + arc_set->to()->name(), + arc_set->role()->to_string()); return PropertyValue(name); } } From 48febcf78552ffedb97816a556ca2226f4879a48 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 17 Apr 2026 13:53:01 -0700 Subject: [PATCH 162/181] stringFloat Signed-off-by: James Cherry --- util/StringUtil.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/StringUtil.cc b/util/StringUtil.cc index 808365cdf..a6edc3f46 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -67,8 +67,9 @@ stringFloat(const std::string &str) return {0.0, false}; #else char *ptr; + errno = 0; value = strtof(str.data(), &ptr); - if (!errno || *ptr != '\0') + if (errno != 0 || *ptr != '\0') return {0.0, false}; else return {value, true}; From 668cfb26afbc0c4760ae733a32990a182b335bde Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 18 Apr 2026 08:45:04 -0700 Subject: [PATCH 163/181] makeSceneMap rm dup warnings Signed-off-by: James Cherry --- include/sta/Liberty.hh | 12 +++------ include/sta/Sta.hh | 5 +++- liberty/Liberty.cc | 52 ++++++++++++++++++------------------- search/Sta.cc | 58 ++++++++++++++++++++---------------------- 4 files changed, 61 insertions(+), 66 deletions(-) diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 204431273..461166a1d 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -374,19 +374,15 @@ public: static void makeSceneMap(LibertyLibrary *lib, - size_t lib_ap_index, + Scene *scene, + const MinMaxAll *min_max, Network *network, Report *report); static void makeSceneMap(LibertyCell *link_cell, LibertyCell *scene_cell, - size_t lib_ap_index, - Report *report); - static void - makeSceneMap(LibertyCell *cell1, - LibertyCell *cell2, - bool link, - size_t lib_ap_index, + Scene *scene, + const MinMaxAll *min_max, Report *report); static void checkScenes(LibertyCell *cell, diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 7be5c95e1..0d78ebbad 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -154,7 +154,7 @@ public: // tmp public void readLibertyAfter(LibertyLibrary *liberty, Scene *scene, - const MinMax *min_max); + const MinMaxAll *min_max); bool readVerilog(std::string_view filename); // Network readers call this to notify the Sta to delete any previously // linked network. @@ -1597,6 +1597,9 @@ protected: void updateSceneLiberty(Scene *scene, const StringSeq &liberty_min_files, const StringSeq &liberty_max_files); + void updateSceneLiberty(Scene *scene, + const StringSeq &liberty_files, + const MinMaxAll *min_max); Scene *makeScene(const std::string &name, Mode *mode, diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 8deb4a1dd..8d7a59e0f 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -707,7 +707,8 @@ LibertyLibrary::makeScaledCell(std::string_view name, void LibertyLibrary::makeSceneMap(LibertyLibrary *lib, - size_t lib_ap_index, + Scene *scene, + const MinMaxAll *min_max, Network *network, Report *report) { @@ -716,38 +717,33 @@ LibertyLibrary::makeSceneMap(LibertyLibrary *lib, LibertyCell *cell = cell_iter.next(); LibertyCell *link_cell = network->findLibertyCell(cell->name()); if (link_cell) - makeSceneMap(link_cell, cell, lib_ap_index, report); + makeSceneMap(link_cell, cell, scene, min_max, report); } } // Map a cell linked in the network to the corresponding liberty cell // to use for delay calculation at a scene. -void -LibertyLibrary::makeSceneMap(LibertyCell *link_cell, - LibertyCell *scene_cell, - size_t lib_ap_index, - Report *report) -{ - link_cell->setSceneCell(scene_cell, lib_ap_index); - makeSceneMap(link_cell, scene_cell, true, lib_ap_index, report); - // Check for brain damage in the other direction. - makeSceneMap(scene_cell, link_cell, false, lib_ap_index, report); -} - void LibertyLibrary::makeSceneMap(LibertyCell *cell1, LibertyCell *cell2, - bool link, - size_t lib_ap_index, + Scene *scene, + const MinMaxAll *min_max, Report *report) { + for (const MinMax *mm : min_max->range()) { + size_t lib_ap_index = scene->libertyIndex(mm); + cell1->setSceneCell(cell2, lib_ap_index); + } + LibertyCellPortBitIterator port_iter1(cell1); while (port_iter1.hasNext()) { LibertyPort *port1 = port_iter1.next(); LibertyPort *port2 = cell2->findLibertyPort(port1->name()); if (port2) { - if (link) + for (const MinMax *mm : min_max->range()) { + size_t lib_ap_index = scene->libertyIndex(mm); port1->setScenePort(port2, lib_ap_index); + } } else report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.", @@ -761,17 +757,19 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, for (TimingArcSet *arc_set1 : cell1->timing_arc_sets_) { TimingArcSet *arc_set2 = cell2->findTimingArcSet(arc_set1); if (arc_set2) { - if (link) { - const TimingArcSeq &arcs1 = arc_set1->arcs(); - const TimingArcSeq &arcs2 = arc_set2->arcs(); - auto arc_itr1 = arcs1.begin(), arc_itr2 = arcs2.begin(); - for (; - arc_itr1 != arcs1.end() && arc_itr2 != arcs2.end(); - arc_itr1++, arc_itr2++) { - TimingArc *arc1 = *arc_itr1; - TimingArc *arc2 = *arc_itr2; - if (TimingArc::equiv(arc1, arc2)) + const TimingArcSeq &arcs1 = arc_set1->arcs(); + const TimingArcSeq &arcs2 = arc_set2->arcs(); + auto arc_itr1 = arcs1.begin(), arc_itr2 = arcs2.begin(); + for (; + arc_itr1 != arcs1.end() && arc_itr2 != arcs2.end(); + arc_itr1++, arc_itr2++) { + TimingArc *arc1 = *arc_itr1; + TimingArc *arc2 = *arc_itr2; + if (TimingArc::equiv(arc1, arc2)) { + for (const MinMax *mm : min_max->range()) { + size_t lib_ap_index = scene->libertyIndex(mm); arc1->setSceneArc(arc2, lib_ap_index); + } } } } diff --git a/search/Sta.cc b/search/Sta.cc index de546ca2f..c2cd5446c 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -713,12 +713,7 @@ Sta::readLibertyFile(std::string_view filename, if (liberty) { // Don't map liberty cells if they are redefined by reading another // library with the same cell names. - if (min_max == MinMaxAll::all()) { - readLibertyAfter(liberty, scene, MinMax::min()); - readLibertyAfter(liberty, scene, MinMax::max()); - } - else - readLibertyAfter(liberty, scene, min_max->asMinMax()); + readLibertyAfter(liberty, scene, min_max); network_->readLibertyAfter(liberty); } return liberty; @@ -727,11 +722,11 @@ Sta::readLibertyFile(std::string_view filename, void Sta::readLibertyAfter(LibertyLibrary *liberty, Scene *scene, - const MinMax *min_max) + const MinMaxAll *min_max) { - scene->addLiberty(liberty, min_max); - LibertyLibrary::makeSceneMap(liberty, scene->libertyIndex(min_max), network_, - report_); + for (const MinMax *mm : min_max->range()) + scene->addLiberty(liberty, mm); + LibertyLibrary::makeSceneMap(liberty, scene, min_max, network_, report_); } bool @@ -2624,22 +2619,27 @@ Sta::updateSceneLiberty(Scene *scene, const StringSeq &liberty_min_files, const StringSeq &liberty_max_files) { - StringSet warned_files; - for (const MinMax *min_max : MinMax::range()) { - 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); - if (lib == nullptr) - lib = network_->findLibertyFilename(lib_file); - if (lib) - LibertyLibrary::makeSceneMap(lib, scene->libertyIndex(min_max), network_, - report_); - else if (!warned_files.contains(lib_file)) { - report_->warn(1555, "liberty name/filename {} not found.", lib_file); - warned_files.insert(lib_file); - } - } + if (liberty_min_files == liberty_max_files) + updateSceneLiberty(scene, liberty_max_files, MinMaxAll::minMax()); + else { + updateSceneLiberty(scene, liberty_min_files, MinMaxAll::min()); + updateSceneLiberty(scene, liberty_max_files, MinMaxAll::max()); + } +} + +void +Sta::updateSceneLiberty(Scene *scene, + const StringSeq &liberty_files, + const MinMaxAll *min_max) +{ + for (const std::string &lib_file : liberty_files) { + LibertyLibrary *lib = network_->findLiberty(lib_file); + if (lib == nullptr) + lib = network_->findLibertyFilename(lib_file); + if (lib) + LibertyLibrary::makeSceneMap(lib, scene, min_max, network_, report_); + else + report_->warn(1555, "liberty name/filename {} not found.", lib_file); } } @@ -2650,10 +2650,8 @@ Sta::updateLibertyScenes() LibertyLibraryIterator *iter = network_->libertyLibraryIterator(); 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, MinMaxAll::minMax(), + network_, report_); } } } From 6c9af4a5fa6fef796f360ec33b6fdfc9a7e10e83 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 18 Apr 2026 11:39:03 -0700 Subject: [PATCH 164/181] LibertyPort::less Signed-off-by: James Cherry --- include/sta/TimingArc.hh | 2 +- liberty/Liberty.cc | 3 ++- liberty/TimingArc.cc | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 4f9d54454..b2f562308 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -146,7 +146,7 @@ class TimingArcSet public: ~TimingArcSet(); - std::string to_string(); + std::string to_string() const; LibertyCell *libertyCell() const; LibertyPort *from() const { return from_; } LibertyPort *to() const { return to_; } diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 8d7a59e0f..97682e1e0 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -2380,12 +2380,13 @@ LibertyPort::equiv(const LibertyPort *port1, && port1->pwr_gnd_type_ == port2->pwr_gnd_type_); } +// Note port1 and port2 may be from different cells (timingArcSetLess). bool LibertyPort::less(const LibertyPort *port1, const LibertyPort *port2) { if (port1 && port2) - return port1->pinIndex() < port2->pinIndex(); + return port1->name() < port2->name(); else return port1 == nullptr && port2 != nullptr; } diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index ae99b043d..dd1694bdb 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -212,7 +212,7 @@ TimingArcSet::TimingArcSet(const TimingRole *role, } std::string -TimingArcSet::to_string() +TimingArcSet::to_string() const { std::string str = from_->name(); str += " -> "; From c671b266fe4702a4cbbdf6771783099a3246fcae Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 20 Apr 2026 15:02:24 -0700 Subject: [PATCH 165/181] tidy Signed-off-by: James Cherry --- liberty/Liberty.cc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 97682e1e0..b63e56fa1 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -724,21 +724,21 @@ LibertyLibrary::makeSceneMap(LibertyLibrary *lib, // Map a cell linked in the network to the corresponding liberty cell // to use for delay calculation at a scene. void -LibertyLibrary::makeSceneMap(LibertyCell *cell1, - LibertyCell *cell2, +LibertyLibrary::makeSceneMap(LibertyCell *link_cell, + LibertyCell *scene_cell, Scene *scene, const MinMaxAll *min_max, Report *report) { for (const MinMax *mm : min_max->range()) { size_t lib_ap_index = scene->libertyIndex(mm); - cell1->setSceneCell(cell2, lib_ap_index); + link_cell->setSceneCell(scene_cell, lib_ap_index); } - LibertyCellPortBitIterator port_iter1(cell1); + LibertyCellPortBitIterator port_iter1(link_cell); while (port_iter1.hasNext()) { LibertyPort *port1 = port_iter1.next(); - LibertyPort *port2 = cell2->findLibertyPort(port1->name()); + LibertyPort *port2 = scene_cell->findLibertyPort(port1->name()); if (port2) { for (const MinMax *mm : min_max->range()) { size_t lib_ap_index = scene->libertyIndex(mm); @@ -747,15 +747,15 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, } else report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.", - cell1->library()->name(), - cell1->name(), + link_cell->library()->name(), + link_cell->name(), port1->name(), - cell2->library()->name(), - cell2->name()); + scene_cell->library()->name(), + scene_cell->name()); } - for (TimingArcSet *arc_set1 : cell1->timing_arc_sets_) { - TimingArcSet *arc_set2 = cell2->findTimingArcSet(arc_set1); + for (TimingArcSet *arc_set1 : link_cell->timing_arc_sets_) { + TimingArcSet *arc_set2 = scene_cell->findTimingArcSet(arc_set1); if (arc_set2) { const TimingArcSeq &arcs1 = arc_set1->arcs(); const TimingArcSeq &arcs2 = arc_set2->arcs(); @@ -775,13 +775,13 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1, } else report->warn(1111, "cell {}/{} {} -> {} timing group {} not found in cell {}/{}.", - cell1->library()->name(), - cell1->name(), + link_cell->library()->name(), + link_cell->name(), arc_set1->from() ? arc_set1->from()->name() : "", arc_set1->to()->name(), arc_set1->role()->to_string(), - cell2->library()->name(), - cell2->name()); + scene_cell->library()->name(), + scene_cell->name()); } } From daeb8503f9cf9fb3ab732c2abfb70ba45fe57f00 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 21 Apr 2026 10:54:40 -0700 Subject: [PATCH 166/181] cursor Signed-off-by: James Cherry --- .cursor/rules/opensta-tests.mdc | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .cursor/rules/opensta-tests.mdc diff --git a/.cursor/rules/opensta-tests.mdc b/.cursor/rules/opensta-tests.mdc new file mode 100644 index 000000000..c49f375ed --- /dev/null +++ b/.cursor/rules/opensta-tests.mdc @@ -0,0 +1,13 @@ +--- +description: Where OpenSTA regression tests live (pvt/test vs public test/) +alwaysApply: true +--- + +# OpenSTA tests and regression + +- **Primary suite:** Most regression tests, Tcl drivers, and golden `.ok` files live under **`pvt/test/`** (private / separate repo, often opened as a second workspace root alongside `master`). +- **Public subset:** **`test/`** at the OpenSTA repo root holds a smaller set of tests shipped with the public tree (e.g. `test/regression`, `*.tcl`, `*.ok` there). + +When searching for a test name, regression lists, or updating goldens, **check `pvt/test` first**, then `test/`. + +Typical driver: `pvt/test/regression` (or `test/regression` for the public list). Built `sta` binary is usually `master/build/sta` relative to the main checkout. From d8c56f1d19814d3b5ac63876982798a9a4d64b53 Mon Sep 17 00:00:00 2001 From: Drew Lewis Date: Tue, 21 Apr 2026 17:59:38 -0400 Subject: [PATCH 167/181] Make defineProperty take handler by const& and clean up std::moves (#427) Signed-off-by: Drew Lewis --- include/sta/Property.hh | 20 ++++++++++---------- search/Property.cc | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/sta/Property.hh b/include/sta/Property.hh index e5834b612..adeee3ccb 100644 --- a/include/sta/Property.hh +++ b/include/sta/Property.hh @@ -99,25 +99,25 @@ public: // return PropertyValue("bar"); // }); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); void defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler); + const PropertyRegistry::PropertyHandler &handler); protected: PropertyValue portSlew(const Port *port, diff --git a/search/Property.cc b/search/Property.cc index bef9ebb00..fe848d5e1 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -1265,70 +1265,70 @@ Properties::capacitancePropertyValue(float cap) void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_library_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_liberty_library_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_cell_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_liberty_cell_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_port_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_liberty_port_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_instance_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_pin_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_net_.defineProperty(property, handler); } void Properties::defineProperty(std::string_view property, - PropertyRegistry::PropertyHandler &handler) + const PropertyRegistry::PropertyHandler &handler) { registry_clock_.defineProperty(property, handler); } @@ -1355,7 +1355,7 @@ void PropertyRegistry::defineProperty(std::string_view property, PropertyHandler handler) { - registry_[std::string(property)] = std::move(std::move(std::move(std::move(std::move(std::move(std::move(std::move(std::move(std::move(handler)))))))))); + registry_[std::string(property)] = std::move(handler); } } // namespace sta From ee5021dffcc65a9fe22e420ee15655e76362b03a Mon Sep 17 00:00:00 2001 From: Drew Lewis Date: Tue, 21 Apr 2026 18:02:29 -0400 Subject: [PATCH 168/181] Add missing headers and to StringUtil.cc for strtof fallback (#426) Signed-off-by: Drew Lewis --- util/StringUtil.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/StringUtil.cc b/util/StringUtil.cc index a6edc3f46..88ad97641 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -26,7 +26,9 @@ #include #include +#include #include +#include #include #include From c8b9ffb22da36bcf18a8c4704163ae0b6a032108 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 21 Apr 2026 12:39:06 -0700 Subject: [PATCH 169/181] tidy Signed-off-by: James Cherry --- .clang-tidy | 4 +++- dcalc/Arnoldi.hh | 1 - dcalc/ArnoldiReduce.cc | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index c095fa1b9..6d1a84c20 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -76,5 +76,7 @@ HeaderFilterRegex: '.*/(app|cmake|dcalc|graph|liberty|network|parasitics|power|s # util/gzstream.hh # util/FlexDisableRegister.hh # Bison-generated parser headers (build/{Liberty,Verilog,...}Parse.hh) -ExcludeHeaderFilterRegex: '(.*/)?(gzstream\.hh|FlexDisableRegister\.hh|(Liberty|Verilog|Sdf|Spef|Saif|LibExpr)Parse\.hh)$' +# Homebrew and MacPorts third-party headers. +ExcludeHeaderFilterRegex: '(^/opt/(homebrew|local)/.*)|((.*/)?(gzstream\.hh|FlexDisableRegister\.hh|(Liberty|Verilog|Sdf|Spef|Saif|LibExpr)Parse\.hh)$)' +SystemHeaders: false FormatStyle: none diff --git a/dcalc/Arnoldi.hh b/dcalc/Arnoldi.hh index ccfbb0027..ac5775651 100644 --- a/dcalc/Arnoldi.hh +++ b/dcalc/Arnoldi.hh @@ -69,7 +69,6 @@ class rcmodel : public ConcreteParasitic, public arnoldi1 { public: - rcmodel(); ~rcmodel() override; float capacitance() const override; PinSet unannotatedLoads(const Pin *drvr_pin, diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc index 236c01a60..c9940ae44 100644 --- a/dcalc/ArnoldiReduce.cc +++ b/dcalc/ArnoldiReduce.cc @@ -43,12 +43,11 @@ namespace sta { // This is legacy C-style code. // NOLINTBEGIN(modernize-avoid-c-style-cast, bugprone-multi-level-implicit-pointer-conversion, bugprone-implicit-widening-of-multiplication-result) -rcmodel::rcmodel() +rcmodel::~rcmodel() { + free(pinV); } -rcmodel::~rcmodel() { free(pinV); } - float rcmodel::capacitance() const { From f9643fbf45cdcd675ffbf0088e7e146fc2b6b567 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 22 Apr 2026 11:13:25 -0700 Subject: [PATCH 170/181] set_pocv_mode warn if no lvf libs Signed-off-by: James Cherry --- include/sta/Sta.hh | 1 + network/Network.cc | 10 +++++----- search/Search.i | 6 +++--- search/Sta.cc | 26 ++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 0d78ebbad..1b74fb289 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1609,6 +1609,7 @@ protected: Mode *mode, Parasitics *parasitics); void deleteScenes(); + void checkLibrarayPocv(); Scene *cmd_scene_{nullptr}; CmdNamespace cmd_namespace_{CmdNamespace::sdc}; diff --git a/network/Network.cc b/network/Network.cc index d096d842a..3bf22cf67 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -134,16 +134,16 @@ Network::readLibertyAfter(LibertyLibrary *) LibertyCell * Network::findLibertyCell(std::string_view name) const { - LibertyLibraryIterator *iter = libertyLibraryIterator(); - while (iter->hasNext()) { - LibertyLibrary *lib = iter->next(); + LibertyLibraryIterator *lib_iter = libertyLibraryIterator(); + while (lib_iter->hasNext()) { + LibertyLibrary *lib = lib_iter->next(); LibertyCell *cell = lib->findLibertyCell(name); if (cell) { - delete iter; + delete lib_iter; return cell; } } - delete iter; + delete lib_iter; return nullptr; } diff --git a/search/Search.i b/search/Search.i index 7a89abe86..0764ae689 100644 --- a/search/Search.i +++ b/search/Search.i @@ -237,7 +237,7 @@ endpoint_slack(const Pin *pin, sta->ensureLibLinked(); if (!path_group_name.empty() && !sta->isGroupPathName(path_group_name, sta->cmdSdc())) { - sta->report()->error(1577, "{} is not a known path group name.", + sta->report()->error(1590, "{} is not a known path group name.", path_group_name); return INF; } @@ -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 {}", field_name); + sta->report()->warn(1591, "unknown report path field {}", field_name); } void @@ -961,7 +961,7 @@ set_crpr_mode(std::string mode) else if (stringEqual(mode, "same_transition")) sta->setCrprMode(CrprMode::same_transition); else - sta->report()->error(1573, "unknown common clk pessimism mode."); + sta->report()->error(1592, "unknown common clk pessimism mode."); } const std::string & diff --git a/search/Sta.cc b/search/Sta.cc index c2cd5446c..2d85c0275 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2281,9 +2281,11 @@ Sta::setPocvMode(PocvMode mode) delay_ops_ = new DelayOpsScalar(); break; case PocvMode::normal: + checkLibrarayPocv(); delay_ops_ = new DelayOpsNormal(); break; case PocvMode::skew_normal: + checkLibrarayPocv(); delay_ops_ = new DelayOpsSkewNormal(); break; } @@ -2292,6 +2294,30 @@ Sta::setPocvMode(PocvMode mode) } } +void +Sta::checkLibrarayPocv() +{ + LibertyLibraryIterator *lib_iter = network_->libertyLibraryIterator(); + while (lib_iter->hasNext()) { + LibertyLibrary *lib = lib_iter->next(); + LibertyCellIterator cell_iter(lib); + while (cell_iter.hasNext()) { + LibertyCell *cell = cell_iter.next(); + for (const TimingArcSet *arc_set : cell->timingArcSets()) { + for (const TimingArc *arc : arc_set->arcs()) { + GateTableModel *gate_model = arc->gateTableModel(); + if (gate_model) { + const TableModels *models = gate_model->delayModels(); + if (models->sigma(EarlyLate::early()) != nullptr) + return; + } + } + } + } + } + report_->warn(1578, "No liberty POCV/LVF models found."); +} + float Sta::pocvQuantile() { From 1edf4395472bdd19159891ca785597394cc6e34e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 22 Apr 2026 11:13:45 -0700 Subject: [PATCH 171/181] spef allow illegal unquoted !'s Signed-off-by: James Cherry --- parasitics/SpefLex.ll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parasitics/SpefLex.ll b/parasitics/SpefLex.ll index 58975b840..0cb0dda48 100644 --- a/parasitics/SpefLex.ll +++ b/parasitics/SpefLex.ll @@ -73,11 +73,11 @@ FLOAT {DECIMAL}|{FRACTION}|{EXP} HCHAR "."|"/"|"|"|":" PREFIX_BUS_DELIM "["|"{"|"("|"<" SUFFIX_BUS_DELIM "]"|"}"|")"|">" -SPECIAL_CHAR "!"|"#"|"$"|"%"|"&"|"`"|"("|")"|"*"|"+"|","|"-"|"."|"/"|":"|";"|"<"|"="|">"|"?"|"@"|"["|"\\"|"]"|"^"|"'"|"{"|"|"|"}"|"~" +SPECIAL_CHAR "#"|"$"|"%"|"&"|"`"|"("|")"|"*"|"+"|","|"-"|"."|"/"|":"|";"|"<"|"="|">"|"?"|"@"|"["|"\\"|"]"|"^"|"'"|"{"|"|"|"}"|"~" ESCAPED_CHAR_SET {SPECIAL_CHAR}|\" ESCAPED_CHAR \\{ESCAPED_CHAR_SET} IDENT_ACHAR {ESCAPED_CHAR}|{ALPHA}|"_" -IDENT_CHAR {IDENT_ACHAR}|{DIGIT} +IDENT_CHAR {IDENT_ACHAR}|{DIGIT}|"!" ID {IDENT_ACHAR}{IDENT_CHAR}* BUS_SUB {DIGIT}|{ALPHA}|"_" BIT_IDENT {ID}({PREFIX_BUS_DELIM}{BUS_SUB}+{SUFFIX_BUS_DELIM})+ From f361dd65656d7cba561d454745b34be2fca4d1b0 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 23 Apr 2026 09:56:45 -0700 Subject: [PATCH 172/181] spice args Signed-off-by: James Cherry --- spice/WritePathSpice.cc | 2 +- spice/WriteSpice.hh | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spice/WritePathSpice.cc b/spice/WritePathSpice.cc index 842b2620b..1b90af87b 100644 --- a/spice/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -193,7 +193,7 @@ WritePathSpice::WritePathSpice(const Path *path, void WritePathSpice::writeSpice() { - spice_stream_.open(std::string(spice_filename_)); + spice_stream_.open(spice_filename_); if (spice_stream_.is_open()) { path_expanded_.expand(path_, true); // Find subckt port names as a side-effect of writeSubckts. diff --git a/spice/WriteSpice.hh b/spice/WriteSpice.hh index 5230b72be..a1fd64a3a 100644 --- a/spice/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -165,12 +165,12 @@ protected: std::string replaceFileExt(std::string_view filename, std::string_view ext); - const std::string_view spice_filename_; - const std::string_view subckt_filename_; - const std::string_view lib_subckt_filename_; - const std::string_view model_filename_; - const std::string_view power_name_; - const std::string_view gnd_name_; + const std::string spice_filename_; + const std::string subckt_filename_; + const std::string lib_subckt_filename_; + const std::string model_filename_; + const std::string power_name_; + const std::string gnd_name_; CircuitSim ckt_sim_; const Scene *scene_; const MinMax *min_max_; From a1c307713994379649f7673b8d51415b88cf64c2 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 23 Apr 2026 18:12:40 -0700 Subject: [PATCH 173/181] message ids Signed-off-by: James Cherry --- dcalc/LumpedCapDelayCalc.cc | 6 ++++-- search/MakeTimingModel.cc | 2 +- search/TagGroup.cc | 2 +- verilog/VerilogReader.cc | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index b245c1d66..41ef42502 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -142,8 +142,10 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, 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(in_slew.mean())) - report_->error(1350, "gate delay input variable is NaN"); + if (std::isnan(load_cap)) + report_->error(1350, "gate delay load cap is NaN"); + if (std::isnan(in_slew.mean())) + report_->error(1351, "gate delay input slew is NaN"); const Pvt *pvt = pinPvt(drvr_pin, scene, min_max); model->gateDelay(pvt, in_slew1, load_cap, gate_delay, drvr_slew); diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 7e61d19cc..6ca5f935d 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -231,7 +231,7 @@ MakeTimingModel::checkClock(Clock *clk) { for (const Pin *pin : clk->leafPins()) { if (!network_->isTopLevelPort(pin)) - report_->warn(1355, "clock {} pin {} is inside model block.", clk->name(), + report_->warn(1380, "clock {} pin {} is inside model block.", clk->name(), network_->pathName(pin)); } } diff --git a/search/TagGroup.cc b/search/TagGroup.cc index e7afc9c59..6fbe4d933 100644 --- a/search/TagGroup.cc +++ b/search/TagGroup.cc @@ -311,7 +311,7 @@ TagGroupBldr::copyPaths(TagGroup *tag_group, if (exists2) paths[path_index2] = paths_[path_index1]; else - sta_->report()->critical(1351, "tag group missing tag"); + sta_->report()->critical(1360, "tag group missing tag"); } } diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 263acb4a9..5c6cd0f91 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -1490,12 +1490,12 @@ VerilogReader::linkNetwork(std::string_view top_cell_name, return top_instance; } else { - report_->error(1398, "{} is not a verilog module.", top_cell_name); + report_->error(1390, "{} is not a verilog module.", top_cell_name); return nullptr; } } else { - report_->error(1399, "{} is not a verilog module.", top_cell_name); + report_->error(1391, "{} is not a verilog module.", top_cell_name); return nullptr; } } From d4c13bb7cdfcea212ee45d7051228dd1f5b0cd81 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 24 Apr 2026 11:54:28 -0700 Subject: [PATCH 174/181] delays wrt clks refactor Signed-off-by: James Cherry --- include/sta/Search.hh | 5 +++++ include/sta/SearchClass.hh | 7 +++++++ include/sta/Sta.hh | 14 +++---------- search/Search.cc | 34 ++++++++++++++++++++++++++++++++ search/Sta.cc | 40 ++++++++------------------------------ 5 files changed, 57 insertions(+), 43 deletions(-) diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 47fb62477..58506e1fc 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -409,6 +409,11 @@ public: void saveEnumPath(Path *path); bool isSrchRoot(Vertex *vertex, const Mode *mode) const; + DelaysWrtClks arrivalsWrtClks(Vertex *vertex, + const Scene *scene); + DelaysWrtClks delaysWrtClks(Vertex *vertex, + const Scene *scene, + const PathDelayFunc &get_path_delay); protected: void initVars(); diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index 9e3205b9f..c47ccf1bc 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -24,14 +24,18 @@ #pragma once +#include #include #include #include #include "Delay.hh" #include "GraphClass.hh" +#include "LibertyClass.hh" #include "MinMaxValues.hh" #include "NetworkClass.hh" +#include "RiseFallMinMaxDelay.hh" +#include "SdcClass.hh" #include "VectorMap.hh" namespace sta { @@ -111,6 +115,9 @@ using SlackSeq = std::vector; using Crpr = Delay; using PathSeq = std::vector; using ConstPathSeq = std::vector; +// Path::slack/arrival/required function. +using PathDelayFunc = std::function; +using DelaysWrtClks = std::map; enum class ReportPathFormat { full, full_clock, diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 1b74fb289..7817b1c27 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -24,7 +24,7 @@ #pragma once -#include +#include #include #include #include @@ -82,8 +82,6 @@ using CheckError = StringSeq; using CheckErrorSeq = std::vector; enum class CmdNamespace { sta, sdc }; using ParasiticsNameMap = std::map>; -// Path::slack/arrival/required function. -using PathDelayFunc = std::function; using GraphLoopSeq = std::vector; // Initialize sta functions that are not part of the Sta class. @@ -1524,16 +1522,10 @@ protected: int digits, bool find_required, const PathDelayFunc &get_path_delay); - void reportDelaysWrtClks(Vertex *vertex, - const ClockEdge *clk_edge, - const Scene *scene, + void reportDelaysWrtClks(const ClockEdge *clk_edge, bool report_variance, int digits, - const PathDelayFunc &get_path_delay); - RiseFallMinMaxDelay findDelaysWrtClks(Vertex *vertex, - const ClockEdge *clk_edge, - const Scene *scene, - const PathDelayFunc &get_path_delay); + DelaysWrtClks &clk_delays); std::string formatDelay(const RiseFall *rf, const MinMax *min_max, const RiseFallMinMaxDelay &delays, diff --git a/search/Search.cc b/search/Search.cc index 2fc617fa8..d0d6b0caf 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -57,6 +57,7 @@ #include "PortDelay.hh" #include "PortDirection.hh" #include "Report.hh" +#include "RiseFallMinMaxDelay.hh" #include "Scene.hh" #include "Sdc.hh" #include "SdcClass.hh" @@ -3997,4 +3998,37 @@ Search::wnsSlack(Vertex *vertex, return slacks[path_ap_index]; } +//////////////////////////////////////////////////////////////// + +DelaysWrtClks +Search::arrivalsWrtClks(Vertex *vertex, + const Scene *scene) +{ + return delaysWrtClks(vertex, scene, + [] (const Path *path) { + return path->arrival(); + }); +} + +DelaysWrtClks +Search::delaysWrtClks(Vertex *vertex, + const Scene *scene, + const PathDelayFunc &get_path_delay) +{ + DelaysWrtClks delays_wrt_clks; + VertexPathIterator path_iter(vertex, scene, nullptr, nullptr, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + Delay delay = get_path_delay(path); + if (!delayInf(delay, this)) { + const RiseFall *rf = path->transition(this); + const MinMax *min_max = path->minMax(this); + const ClockEdge *clk_edge = path->clkEdge(this); + RiseFallMinMaxDelay &delays = delays_wrt_clks[clk_edge]; + delays.mergeValue(rf, min_max, delay, this); + } + } + return delays_wrt_clks; +} + } // namespace sta diff --git a/search/Sta.cc b/search/Sta.cc index 2d85c0275..dc6bfc04f 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -26,6 +26,7 @@ #include #include +#include #include #include "ArcDelayCalc.hh" @@ -3346,29 +3347,25 @@ Sta::reportDelaysWrtClks(Vertex *vertex, else search_->findArrivals(vertex->level()); const Sdc *sdc = scene->sdc(); - reportDelaysWrtClks(vertex, nullptr, scene, report_variance, digits, get_path_delay); + DelaysWrtClks clk_delays = search_->delaysWrtClks(vertex, scene, get_path_delay); + reportDelaysWrtClks(nullptr, report_variance, digits, clk_delays); const ClockEdge *default_clk_edge = sdc->defaultArrivalClock()->edge(RiseFall::rise()); - reportDelaysWrtClks(vertex, default_clk_edge, scene, report_variance, - digits, get_path_delay); + reportDelaysWrtClks(default_clk_edge, report_variance, digits, clk_delays); for (const Clock *clk : sdc->sortedClocks()) { for (const RiseFall *rf : RiseFall::range()) { const ClockEdge *clk_edge = clk->edge(rf); - reportDelaysWrtClks(vertex, clk_edge, scene, report_variance, digits, - get_path_delay); + reportDelaysWrtClks(clk_edge, report_variance, digits, clk_delays); } } } void -Sta::reportDelaysWrtClks(Vertex *vertex, - const ClockEdge *clk_edge, - const Scene *scene, +Sta::reportDelaysWrtClks(const ClockEdge *clk_edge, bool report_variance, int digits, - const PathDelayFunc &get_path_delay) + DelaysWrtClks &clk_delays) { - RiseFallMinMaxDelay delays = - findDelaysWrtClks(vertex, clk_edge, scene, get_path_delay); + const RiseFallMinMaxDelay &delays = clk_delays[clk_edge]; if (!delays.empty()) { std::string clk_name; if (clk_edge) @@ -3385,27 +3382,6 @@ Sta::reportDelaysWrtClks(Vertex *vertex, } } -RiseFallMinMaxDelay -Sta::findDelaysWrtClks(Vertex *vertex, - const ClockEdge *clk_edge, - const Scene *scene, - const PathDelayFunc &get_path_delay) -{ - RiseFallMinMaxDelay delays; - VertexPathIterator path_iter(vertex, scene, nullptr, nullptr, this); - while (path_iter.hasNext()) { - Path *path = path_iter.next(); - Delay delay = get_path_delay(path); - const RiseFall *rf = path->transition(this); - const MinMax *min_max = path->minMax(this); - const ClockEdge *path_clk_edge = path->clkEdge(this); - if (path_clk_edge == clk_edge - && !delayInf(delay, this)) - delays.mergeValue(rf, min_max, delay, this); - } - return delays; -} - std::string Sta::formatDelay(const RiseFall *rf, const MinMax *min_max, From 7a236c38b77bc7155797df3fbc1ef724660b1b04 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 25 Apr 2026 10:35:42 -0700 Subject: [PATCH 175/181] flatten Clock::uncertainties Signed-off-by: James Cherry --- include/sta/Clock.hh | 4 ++-- include/sta/MinMaxValues.hh | 5 +++++ sdc/Clock.cc | 23 ++++------------------- search/PathEnd.cc | 2 +- search/Search.cc | 6 +++--- 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index 8acc1e458..5a633ef0e 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -90,7 +90,7 @@ public: // Return values. float &slew, bool &exists) const; - ClockUncertainties *uncertainties() const { return uncertainties_; } + const ClockUncertainties &uncertainties() const { return uncertainties_; } void uncertainty(const SetupHold *setup_hold, // Return values. float &uncertainty, @@ -180,7 +180,7 @@ protected: bool is_propagated_{false}; RiseFallMinMax slews_; RiseFallMinMax slew_limits_[path_clk_or_data_count]; - ClockUncertainties *uncertainties_{nullptr}; + ClockUncertainties uncertainties_; bool is_generated_{false}; // Generated clock variables. Pin *src_pin_{nullptr}; diff --git a/include/sta/MinMaxValues.hh b/include/sta/MinMaxValues.hh index 8fb8eeea4..c365d6130 100644 --- a/include/sta/MinMaxValues.hh +++ b/include/sta/MinMaxValues.hh @@ -169,6 +169,11 @@ public: static int cmp(const MinMaxValues *values1, const MinMaxValues *values2) { + if (!values1->exists_[MinMax::minIndex()] + && !values2->exists_[MinMax::minIndex()] + && !values1->exists_[MinMax::maxIndex()] + && !values2->exists_[MinMax::maxIndex()]) + return 0; if (!values1->exists_[MinMax::minIndex()] && values2->exists_[MinMax::minIndex()]) return -1; diff --git a/sdc/Clock.cc b/sdc/Clock.cc index c9d4d11a2..21cd4d8d2 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -240,42 +240,27 @@ Clock::uncertainty(const SetupHold *setup_hold, float &uncertainty, bool &exists) const { - if (uncertainties_) - uncertainties_->value(setup_hold, uncertainty, exists); - else { - uncertainty = 0.0F; - exists = false; - } + uncertainties_.value(setup_hold, uncertainty, exists); } void Clock::setUncertainty(const SetupHoldAll *setup_hold, float uncertainty) { - if (uncertainties_ == nullptr) - uncertainties_ = new ClockUncertainties; - uncertainties_->setValue(setup_hold, uncertainty); + uncertainties_.setValue(setup_hold, uncertainty); } void Clock::setUncertainty(const SetupHold *setup_hold, float uncertainty) { - if (uncertainties_ == nullptr) - uncertainties_ = new ClockUncertainties; - uncertainties_->setValue(setup_hold, uncertainty); + uncertainties_.setValue(setup_hold, uncertainty); } void Clock::removeUncertainty(const SetupHoldAll *setup_hold) { - if (uncertainties_) { - uncertainties_->removeValue(setup_hold); - if (uncertainties_->empty()) { - delete uncertainties_; - uncertainties_ = nullptr; - } - } + uncertainties_.removeValue(setup_hold); } void diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 819207521..e917d89f6 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -379,7 +379,7 @@ PathEnd::checkTgtClkUncertainty(const Path *tgt_clk_path, if (tgt_clk_path && tgt_clk_path->isClock(sta)) uncertainties = tgt_clk_path->clkInfo(sta)->uncertainties(); else if (tgt_clk_edge) - uncertainties = tgt_clk_edge->clock()->uncertainties(); + uncertainties = &tgt_clk_edge->clock()->uncertainties(); float uncertainty = 0.0; if (uncertainties) { bool exists; diff --git a/search/Search.cc b/search/Search.cc index d0d6b0caf..081daac80 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1547,7 +1547,7 @@ Search::seedClkArrival(const Pin *pin, const ClockUncertainties *uncertainties = sdc->clockUncertainties(pin); if (uncertainties == nullptr) - uncertainties = clk->uncertainties(); + uncertainties = &clk->uncertainties(); // Propagate liberty "pulse_clock" transition to transitive fanout. LibertyPort *port = network_->libertyPort(pin); const RiseFall *pulse_clk_sense = (port ? port->pulseClkSense() : nullptr); @@ -1914,13 +1914,13 @@ Search::inputDelayTag(const Pin *pin, const Pin *clk_pin = nullptr; const RiseFall *clk_rf = nullptr; bool is_propagated = false; - ClockUncertainties *clk_uncertainties = nullptr; + const ClockUncertainties *clk_uncertainties = nullptr; if (clk_edge) { clk = clk_edge->clock(); clk_rf = clk_edge->transition(); clk_pin = clk->defaultPin(); is_propagated = clk->isPropagated(); - clk_uncertainties = clk->uncertainties(); + clk_uncertainties = &clk->uncertainties(); } Sdc *sdc = scene->sdc(); From 24ada4b22fee4814252b93495c8e4343f0e6a29e Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 25 Apr 2026 10:40:14 -0700 Subject: [PATCH 176/181] Scene::liberty_ use array Signed-off-by: James Cherry --- include/sta/Scene.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sta/Scene.hh b/include/sta/Scene.hh index 4807136fb..2e2041d39 100644 --- a/include/sta/Scene.hh +++ b/include/sta/Scene.hh @@ -89,7 +89,7 @@ protected: std::string name_; size_t index_; Mode *mode_; - LibertySeq liberty_[MinMax::index_count]; + std::array liberty_; std::array parasitics_; friend class Scenes; From f3f608596a5323a3e144b9891f238aefe4b17d70 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 25 Apr 2026 13:46:45 -0700 Subject: [PATCH 177/181] leak Signed-off-by: James Cherry --- search/Sta.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/search/Sta.cc b/search/Sta.cc index dc6bfc04f..9ccdc8418 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2309,13 +2309,16 @@ Sta::checkLibrarayPocv() GateTableModel *gate_model = arc->gateTableModel(); if (gate_model) { const TableModels *models = gate_model->delayModels(); - if (models->sigma(EarlyLate::early()) != nullptr) + if (models->sigma(EarlyLate::early()) != nullptr) { + delete lib_iter; return; + } } } } } } + delete lib_iter; report_->warn(1578, "No liberty POCV/LVF models found."); } From 1eb2e9b11d74989bb6e548f5a5d80446fc6d21f1 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 25 Apr 2026 14:45:49 -0700 Subject: [PATCH 178/181] untabify Signed-off-by: James Cherry --- dcalc/DelayNormal.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dcalc/DelayNormal.cc b/dcalc/DelayNormal.cc index c73de0696..de26bfd96 100644 --- a/dcalc/DelayNormal.cc +++ b/dcalc/DelayNormal.cc @@ -121,7 +121,7 @@ DelayOpsNormal::greater(const Delay &delay1, const StaState *sta) const { return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); + delayAsFloat(delay2, EarlyLate::late(), sta)); } bool @@ -130,7 +130,7 @@ DelayOpsNormal::greaterEqual(const Delay &delay1, const StaState *sta) const { return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta), - delayAsFloat(delay2, EarlyLate::late(), sta)); + delayAsFloat(delay2, EarlyLate::late(), sta)); } Delay @@ -138,7 +138,7 @@ DelayOpsNormal::sum(const Delay &delay1, const Delay &delay2) const { return Delay(delay1.mean() + delay2.mean(), - delay1.stdDev2() + delay2.stdDev2()); + delay1.stdDev2() + delay2.stdDev2()); } Delay @@ -146,7 +146,7 @@ DelayOpsNormal::sum(const Delay &delay1, float delay2) const { return Delay(delay1.mean() + delay2, - delay1.stdDev2()); + delay1.stdDev2()); } Delay From 0129c8e9556a655218a40cfafbab95e67d08652c Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Fri, 22 May 2026 20:18:51 +0300 Subject: [PATCH 179/181] chore: clean up remnant printf-style debugPrints --- power/Power.cc | 2 +- power/VcdReader.cc | 4 ++-- sdc/Sdc.cc | 10 +++++----- verilog/VerilogReader.cc | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/power/Power.cc b/power/Power.cc index 55868f54e..c60439412 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -1362,7 +1362,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port, } float port_internal = weight * energy * to_activity.density(); float avg_arc_internal = energy * to_activity.density(); - debugPrint(debug_, "power", 2, "%3s -> %-3s %6s %.3f %.3f %.3f %9.2e %9.2e %s", + debugPrint(debug_, "power", 2, "{:>3} -> {:<3} {:>6} {:.3f} {:.3f} {:.3f} {:9.2e} {:9.2e} {}", from_scene_port ? from_scene_port->name() : "-" , to_port->name(), when ? when->to_string().c_str() : "", diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 463f3f68d..98297ea66 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -219,14 +219,14 @@ VcdCountReader::setTimeUnit(std::string_view , void VcdCountReader::setTimeMin(VcdTime time) { - debugPrint(debug_, "read_vcd", 1, "setTimeMin called with time %" PRIu64, time); + debugPrint(debug_, "read_vcd", 1, "setTimeMin called with time {}", time); time_min_ = time; } void VcdCountReader::setTimeMax(VcdTime time) { - debugPrint(debug_, "read_vcd", 1, "setTimeMax called with time %" PRIu64, time); + debugPrint(debug_, "read_vcd", 1, "setTimeMax called with time {}", time); time_max_ = time; } diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 8c7eaa3e2..6bf69fd1e 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -1019,8 +1019,8 @@ void Sdc::createLibertyGeneratedClocks(Clock *clk) { Pin *pin = network_->findPin(pinName.c_str()); if (pin && clkNetworkPins.count(pin)) { - debugPrint(debug_, "libgenclk", 1, "Found generated clock pin %s " - "in liberty cell %s at path %s", + debugPrint(debug_, "libgenclk", 1, "Found generated clock pin {} " + "in liberty cell {} at path {}", pinName, cell->name(), network_->pathName(pin)); // Search liberty cell for the corresponding generated clock @@ -1046,8 +1046,8 @@ void Sdc::createLibertyGeneratedClocks(Clock *clk) { generatedClock->clockPin() ); - debugPrint(debug_, "libgenclk", 1, "Creating generated clock %s " - "from clock %s in instance %s", + debugPrint(debug_, "libgenclk", 1, "Creating generated clock {} " + "from clock {} in instance {}", generatedClockName, clk->name(), instPath); // Find the output pin, for nested generated clocks @@ -1128,7 +1128,7 @@ Sdc::makeClock(std::string_view name, clkHpinDisablesInvalid(); if (network_->generatedClockPinsToCellMap().size() > 0) { debugPrint(debug_, "libgenclk", 1, "Creating liberty-defined generated clocks " - "for clock %s by searching %lu liberty-defined generated clock pins", + "for clock {} by searching {} liberty-defined generated clock pins", name, network_->generatedClockPinsToCellMap().size()); createLibertyGeneratedClocks(clk); } diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 4c3bd828e..8de4aaba2 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -1561,8 +1561,8 @@ VerilogReader::makeGeneratedClocks(LibertyCell *lib_cell, Instance *inst) // containing the generated clock definition network_->addGeneratedClockPinToCell(pinPath.c_str(), lib_cell); - debugPrint(debug_, "libgenclk", 1, "Adding generated clock pin %s " - "to liberty cell %s for instance %s", + debugPrint(debug_, "libgenclk", 1, "Adding generated clock pin {} " + "to liberty cell {} for instance {}", pinPath, lib_cell->name(), inst_path); } } From 0a8180df0547cb631fc78e415834df24d438200e Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 25 May 2026 21:01:36 +0300 Subject: [PATCH 180/181] fix: filter boolean property normalization + address review comments, fix silisizer compilation issue --- include/sta/FindObjects.hh | 2 +- include/sta/Sta.hh | 2 +- sdc/FilterObjects.cc | 22 ++++++++++++++++------ search/ReportPath.cc | 23 ++++++++++++++++++----- search/ReportPath.hh | 2 ++ search/Search.i | 3 +-- search/Sta.cc | 3 +-- test/get_property_flags.ok | 6 ++++++ test/get_property_flags.tcl | 8 ++++++-- 9 files changed, 52 insertions(+), 19 deletions(-) diff --git a/include/sta/FindObjects.hh b/include/sta/FindObjects.hh index e99000b7e..4b6675073 100644 --- a/include/sta/FindObjects.hh +++ b/include/sta/FindObjects.hh @@ -39,7 +39,7 @@ find_objects_complete(SEQ_TYPE *collection, { collection = collection ? new SEQ_TYPE(*collection) : new SEQ_TYPE(); auto sta = Sta::sta(); - for (const std::string pattern: patterns) { + for (const std::string &pattern: patterns) { PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); auto result = get_pattern(&matcher); if (result.size() == 0) { diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 2e98e20d9..97b752f9f 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -767,7 +767,7 @@ public: const Mode *mode); // Registers whose clock pin is in the fanout of an ICG cell - InstanceSeq clockGatedRegisters(const Mode *mode); + InstanceSeq clockGatedRegisters(); bool isClkGatedRegister(const Instance *inst); // The set of clocks that arrive at vertex in the clock network. diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc index 0962f943a..472d41b47 100644 --- a/sdc/FilterObjects.cc +++ b/sdc/FilterObjects.cc @@ -280,12 +280,22 @@ filterObjects(std::string_view property, for (T *object : all) { PropertyValue value = properties.getProperty(object, property); std::string prop = value.to_string(network); - if (value.type() == PropertyValue::Type::bool_ - && sta->booleanPropsAsInt()) { - if (stringEqual(pattern, "true")) - pattern = "1"; - else if (stringEqual(pattern, "false")) - pattern = "0"; + + // normalize boolean props + if (value.type() == PropertyValue::Type::bool_) { + if (sta->booleanPropsAsInt()) { + if (stringEqual(pattern, "true")) { + pattern = "1"; + } else if (stringEqual(pattern, "false")) { + pattern = "0"; + } + } else { + if (stringEqual(pattern, "1")) { + pattern = "true"; + } else if (stringEqual(pattern, "0")) { + pattern = "false"; + } + } } if ((exact_match && prop == pattern) || (not_match && prop != pattern) diff --git a/search/ReportPath.cc b/search/ReportPath.cc index df0e8f08f..1f9849fb1 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -407,7 +407,11 @@ ReportPath::reportPathEnds(const PathEndSeq *ends) const end_iter = ends->begin(); - // Reiterate to maintain order; also filter delays + // Second filtering pass: create final list with order preserved, optionally + // also filtered by delay + PathEndSeq filtered; + filtered.reserve(qualified_endpoints.size()); + std::set found_delays; while (end_iter != end_iter_end) { PathEnd *end = *end_iter++; @@ -416,11 +420,20 @@ ReportPath::reportPathEnds(const PathEndSeq *ends) const (!qualified_endpoints.size() || qualified_endpoints.count(end)) && (!dedup_same_delay_ || !found_delays.count(end_delay)) ) { - reportPathEnd(end, prev_end, end_iter == end_iter_end); - found_delays.insert(end_delay); - prev_end = end; + filtered.push_back(end); + found_delays.insert(end_delay); + prev_end = end; } - } + } + + // Report final list + end_iter = filtered.begin(); + end_iter_end = filtered.end(); + prev_end = nullptr; + while (end_iter != end_iter_end) { + PathEnd *end = *end_iter++; + reportPathEnd(end, prev_end, end_iter == end_iter_end); + } } else { if (format_ != ReportPathFormat::json) diff --git a/search/ReportPath.hh b/search/ReportPath.hh index 482955154..6d7c6d6f5 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -86,6 +86,8 @@ public: // Format report_path_endpoint only: // Previous path end is used to: // - detect path group changes so headers are reported by group. + // Last is used to: + // - If set to true, a comma is not emitted after in JSON mode. void reportPathEnd(const PathEnd *end, const PathEnd *prev_end, bool last) const; diff --git a/search/Search.i b/search/Search.i index 330a4613d..2ec870afa 100644 --- a/search/Search.i +++ b/search/Search.i @@ -953,8 +953,7 @@ InstanceSeq clock_gated_registers() { Sta *sta = Sta::sta(); - const Mode *mode = sta->cmdMode(); - return sta->clockGatedRegisters(mode); + return sta->clockGatedRegisters(); } //////////////////////////////////////////////////////////////// diff --git a/search/Sta.cc b/search/Sta.cc index fa6d36b03..e84ca5c50 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -5592,8 +5592,7 @@ pinInstances(PinSet &pins, return insts; } -InstanceSeq Sta::clockGatedRegisters(const Mode *mode) { - Network* network = this->network(); +InstanceSeq Sta::clockGatedRegisters() { InstanceSeq result; // Find all leaf registers diff --git a/test/get_property_flags.ok b/test/get_property_flags.ok index 82317f70b..69223045a 100644 --- a/test/get_property_flags.ok +++ b/test/get_property_flags.ok @@ -50,9 +50,15 @@ get_ports get_ports 2 TEST 3 get_clocks +clk get_clocks 2 +vclk get_lib_cells +asap7_small/BUFx2_ASAP7_75t_R get_lib_cells 2 +asap7_small/AND2x2_ASAP7_75t_R +asap7_small/BUFx2_ASAP7_75t_R +asap7_small/DFFHQx4_ASAP7_75t_R get_pins get_pins 2 get_ports diff --git a/test/get_property_flags.tcl b/test/get_property_flags.tcl index d57d20033..eea4ebfef 100644 --- a/test/get_property_flags.tcl +++ b/test/get_property_flags.tcl @@ -26,7 +26,9 @@ puts "get_ports 2" report_object_full_names [get_ports -filter direction==output *] # Default property settings (sta_boolean_props_as_int=1, sta_direction_props_short=0) -# Incompatible property values used: will return empty lists +# Incompatible property values used: +# - booleans: starting OpenSTA 3.0, will be translated to 1/0 automatically +# - directions: return empty lists puts "TEST 2" puts "get_clocks" report_object_full_names [get_clocks -filter is_virtual==false *] @@ -46,7 +48,9 @@ puts "get_ports 2" report_object_full_names [get_ports -filter direction==out *] # Non-default property settings (sta_boolean_props_as_int=0, sta_direction_props_short=1) -# Incompatible property values used: will return empty lists +# Incompatible property values used: +# - booleans: starting OpenSTA 3.0, will be translated to true/false automatically +# - directions: return empty lists set sta_boolean_props_as_int 0 set sta_direction_props_short 1 puts "TEST 3" From 2989368ea34b7f7f8f3f3acf4d8be81baa1a755b Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Tue, 26 May 2026 05:07:03 +0300 Subject: [PATCH 181/181] ci: bump to macOS 15 to fix broken test --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f3180c552..1d35ca1e1 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -20,7 +20,7 @@ jobs: docker run --entrypoint /bin/bash --tty --rm sta-${{ matrix.os }} -c "cd /OpenSTA/test && (./regression || (cat results/diffs && exit 1)) || (./regression -collections || (cat results/diffs && exit 1))" macos: - runs-on: [macos-14] + runs-on: [macos-15] timeout-minutes: 5 steps: - uses: actions/checkout@v4

k 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

bn;6VKLY#jMZB2pw< z-1Q~=@qBgcvLj=|#iJ15!U1*e=X7b~^Tg*fkzxwfOR}`HmsfIKfiN^GeavGBvm~O) znF&}^XylH)#~<~JYHhu#AQ7d=@;cXR^>A9_iF%m6;jy{I?Y!;JJGxYjPm;BIt^-}i z6QFE3A2@p26z!U9iz!xnssrl+KbIl@@aMMXJ+fp^ci9_3g zCE4nr$_%Gj`6Qd7u#`dIOb{OyleHC=H+b=+gK2sW54f`WV2=hVWuMbFhUKNTA*S=k z9^oec%GXQ#RadpQo`xx$hhx6ht;ws&{-eom7ezE=x9o|leT!>hiCG+0nO*B>s;yQ} z#Y$&?su7EtWgRWl0fX9W!<;I4lX|U%*Gi;X-Pp?Q_nJDViydH^d3~0-3DzP$Db{3K z7>Ao*4!YEwv))O*InN+l^JhnTa_O|Hj-84O%JKp0dO9Z@d#%g3*H)Gz^n&k_I}B56 z4vpNocahz)n)N!@#xASIN=xe++!|5s_h0EYIxR%!=WEL?P_h}Ml4E% zfj;6It!NkwI4Wf7^f8rsHYTPPwDoaI%E0VQXA9W1<(hL$vIF)<5BJOP&XY;wG&?+| z8P~_AOa!PXB$*bFqY%%i%yn}UIwGUPDcgeNWvhz(q&lXU z7r!DUqcR&2p|D?T@ETd7_kVo(D(D#a_jjoOiDPoGv2p#i<1aK5nuC$+ze{6E{;QPX zFB+3h*umEHpF)PeK{7em3E8>WKatFTNsIh%BboncR3z7bL^7E^0knV3=VM`I{jVep z$#S1jk%;1txr3ALNI`kv$WTONq>{AH#btb78Y56EF((rt+UqUC+mtcBTlnzbUUAX` zRqVQ`{O?10`$^ZPu@UwiAF+qg?DOJEkjoYpn2Xvka4<%Qe6q%%p4Zw$@+GcyG;t^d zVj@DY>aH+H7`6g0&*XT0Q!^NQH}YPB@y|$;D=0&Q*w1bke;k!XWGYv$h5okB)*!mk zxZ>wQj2=x^yE8O&jr+R2f+Omcj7nTzMGR{5UR#MLQCItJ>r0td$SS3*1+G3tc9ZR? zL*(G;AJ=VG_LTY*nTuqh+Pwe7ca#4V>${T;{xse#MxVtoPGKJAdr13BbxO-Hi}5lM zBVbDUVMf{XK_Lp`1LbBf;YH?QW>$s%y+z=K#`s|demq9NFXIjEJ;pS^tqjaS9z6m7 za#TSktk;KNO$sCOITG)T_H~PigHv|y77ksi=Irlg@KcfUZ{N>CDhFkQ$dzYq$DN6 zK&(ctbcWKu@~@%1fj8UuUzUi%3t2TY~Fn0$VnD^g$ z-x3JevJK|b>!~#i9I9O`tph#xUyt>soo#O=&Scfavd69juv&2vMkoT35;EN-Cjg6iCljTc}`Il|9m9bd28sa9o zQD=hm<|Vu14*wpjr8LcVko1h&OU;GXU~ki}>3#h-i*SHtk3(n<>o3kt4K*zs4witP zwo5Wv4X~B^%d_K)C%OYM5OV^v^E|!UJQjnzTln;C2uV*cAmn>P0+JRSjfzkzYm<68 z2d12P4BJ4uWyD7T|2&f9zLh{p6u%@`WPY|}Qrj`r(kSH{WLz4)%(Vh$e)x8>Zc#tt z6LRhqgcSLL5q{(qm-nO)X2il4hWBg4iw0U{3Z zzJ8_i2DlfR22d@yd;AyqVjRc1k$2Q zc*Y!}_|e~(yh{+uip9|PIIDNpuT=hEy;8hlY+`RhYxFDQK9MEMqwa~se1wxPmJ?jG0g&pJu1AET5)1|>nDoD-9Sg`%h zMwcr;6!FUOt$xVZ_u3DL6g1T%Ma2@{{#8V=8JPYDpEuq=0CIWf$Z{?~ByMa+MK=s} zkBFfl;g-+=|%F7b`M7=^QJXp0tba9Va=^3~nO&miqv~U9q4Fd0qfYNsagD$> zYIFuu)~R$^{*213&--W^ueCbr~KL4ig`> z=cHDo2y7bU5-{1ZK8zv!Qs;Z>&R=Qfqae|9I|!DIs4k*E!l8+RfyUkM2k?HKxog$_u>lE@nbD~AXPmx%Fum$Q2 zqyIS+hz~A--3nVsx{M;f7LQdJy82Rgyl{p8MC2<~ay*8)2_7pBz%;X$2tDuf8ArM^~zKr_7r;9t>hab}W`pt2> zHe}LitUE#98FsS9mF>cnZ#0>B3t5(QFDcj$ zl5iv?`1-vWy({+BxR<qec|R;%nBp}?Eau~SY(;>4 z(JxP!7jey)6m{VR{ZhhFC>AYW$Cix++$@jOD0>hzuGUQ0*_=J|P_9qm+@UDMyoAK@Sf6VC{CIzd?1Z$vpXu1)ufe4Zy(qbSBa zfq=Li%CiW2qDcP6Q^?5H#K@h}1~qmS8)f|VC11bN5o#&qD0WclF{`Gm$8OqMBvGJbjIEm{MSazt}SJ$uo< z>JRKDVz(vHWr@LRtr(M`spRx!y0n!=2W$0GRZLR%-K34wzZLTZ)GDv33zw^Uellux z`~Z34sp=6Ta}B?R;p~gJB|qT92d8AKbNeM7$x6jmxF!dDw#lw)>=Fv`uk*04<`1BT zJ%t;`PK4p=$qO0gif|0)wlRlOp3PRQRE%!#Bu_o7HOCGOJ*`UUfHv=>g^;VW&O5_ z3FVL5&VA6mn+g<0;%NuituS@4sU6olwNllrZ1;&k#nIbmZk!rv>e)NK80x3JY0Tx*f%y{fx`&-HeLrBWvd1Icvxj?&TG z$ao>2`icOpkY$uA$@{N4)eB$GXZuVi6`QI^4K|Vm+-9>8SC-b{-`KvLPDE|v_$xMWW>EoII2nUhEFQb;1ASr4FD!JHhj-|JX3dWR!O zt*Y10e_#kAT;)Ig*~>okj3Zxv#xSJ4O}VWLKS%K$zeM=`n+YT7XfL}c-o>+nG~l$% zh1Z#@^B%#t8XGLQLVA^Eu?f7 zbbcswN4s+RcLyS`g^8&1&(j|kjoipKO8{u3F#${axYlL5`z1R4)$faU93yr*?Po>G zHGYRrZJZd=x$A@_^PCbY5L?mmUiV%`>XwY*!?v#qLwTKKq~BF;poz40QATl%9ysW8XkeX`U@No6_Fio@3UmnZDdVE_vnCUhQx- z8;V3T@ZiZ{3#BS_$*Bq+@a@)afJnOfreKX#1+RoWH$K!NEC((X3;8``1Tx69Ry2cN$cfQg6NTI)!!#3Y$pLW+v~+@4vY6sP@#OB!@^1e%Y9I>+h#! zfzpse9(s3UJkIf{mQ1W&m+Sd27B$*C4H2qYe4WJbU5oTKRA12cgGMu{N-NecK}w^( zCiOxd_ep}-!%GU=yEX8={)Kzyu74-eJup3x zKd8Nym9N^;dr(y+{j>mmT$#m%Nk$&Q)iiIiNUQZ^Xilkh))q=PU7m0=8(;dvrZY32 zKMge`x)t5A*m{!BT3a|wLax>LP#VfJ+-ioU$d>h_oY)s~opZ!oUdfnRx}s_`k!iC$ zuV|7DVso@d{FFVpA_-HinZ!_53zaa4TsMvf28w7zg||BQfR94!K==2VBlPsaUfa)1 zISzL^&fAkwmN=N&xPm8>QWe|AnLls-tY@6b3OH}&wq_7Mq~GgBS@qgcyjR#;u$20ug#yO zL0QaGr~7ZWj{u8^apFs)t9T<~nry|PE#hwu-TtmWf%CwXjLDWYiSAyt7o{W|0hcgGTdB zH)0lJOO`(#x=vpBJjLD8?A@92d@4ZK=J0RZfxX>e`4eIu92ZyOWFKO!S*=xV0MrCp z%cNk3%pNn0)`pi2y&!xaa85=(mv5Pk&75#gJh0n6B1yVVs{X2`W4M(*>aguC2XsFf z|Fil+Od$G>&4uH2RJBvgdKo2_=TpG_y5h0=-i^^*?sNf4iw6Sr<{};3E1Y&pwnhDf$Y?$t&^=rmBwC?!=`Y72+|g>JIkI zD$SGP4ZDuc)`6(&nQI~^(t!(8R7|Hc2}&B+v`(JoEYAPP6bKo@GBw1HJfLeQE>ML9GXMqjlSq}00M@QG|yK}PQl<<-G9Xs)Eud1^z znK~ss=)k^VZ~C!_K?gmgmCkIR5 z`y5_u@9&Qhc#%1um2GMy-gDUI@wDU25NZbZUO_$wLv4#8RB8B1^3VF`|M5!x1O290 z)MiShB@wcO7Dab9r1Q1#K7B|#su&^z4dFpTXD)=mTTR72a@B;HJ1Atk}h*Pe2 z&p1@CP3(6s+}be(nqrvwWaZAMu4PH90PrQ{ts-k-bkbMChtAQrZE>91Pu4!qp>6Q& z=#v@%;@Bu)>>IX;relEg-;-?yE7mg4V+3ys$dRvS`#!`4k0`%`9v)sU9UKAz&9~%P z-;y-=pJv8k+QkukFZ+XnBoJ^7U#7xR-c#<#ISn=GvWb#;l^Us`gc@hatVZ_fvkxS{ zyXTM$x zaJ*%=rzIFu4bsv|f)Kd=k*7ID?CO-HVook8O)Y=3_wdk(Pv`UF@!5WRgDWGg`&#@U zL_*Bq?LjBQyvLGGNB8l?Wd{0oKwf>#PgcXO{>-)7c){4jX{v zy4tC0QfC$I7hSAz`pYh(#kuIrm7F_I#WFvUZlYs;Xg;K~0Ez*t1iA^5g6!j0G|Z9-y?K3Dxk+&p2= zm6NPZjulG%ki{6nNkdHkh4aSVl%gXDJn}?PM(MzVO2PK|ok1UO<&IRY=k(0V8AD5I zZQ{+R53B0D^S*dIBPoI3KTzw!zFP>wshrcW{xlgSFN&rW*s|9k3Az^8s;y(kWtYY} zMKq6M+AL+liV6AEa?d5O$`k?~G|HHQiXMFlH%lvsuMPy`+%?Z_PC<9Ll;cmVId9G& zc}mhZ{!m!V39W7}Ql5P!KNE~mHy{>G*XwRx{|8q6PEhZL_Ov{meMnp!zJhR|f=)_k z{H~O+!Ng~XBIAwF(q5An-~0GC(X}`8eUg|B*8P*Eto!zG68pKwx_RA%Y86g+Rt>?f1Cxw-f68d*2%_G|B# z^b$E0?^}2B&Fn|X`8Tf!W&|%ezXSTC^SX#z|4fYRDkpE;sxchL3ag92b)l`HM75gx zMT_k;xn?yDZ$h3hJ$*HbEwZ{4Jli?7VfVIPV)upF-6i=4 z1A!^^eO!F4*D~x!$q#Y$!F?d?Fny7AR5&7>6QaQ(tU|2?78JLRWX>&15rsT;nJ2TA zebI!Zi;8-bXbpgEu{<$#O%%%n|2@OdC0(v$G_H~)5$kP4(H>ua#Dvv_lbby?Y2>Gg z$VH7N7gt!uFICezmL#mK)+~gqi%DOm+gjN;iUmvL5mfdBiz{@{ku!eggQ<1` zQ*u*>JfH}w-5gKEj*1Xb6u4JuZ)TTx#|a93)hB=dYdT zkKIUv`@uc_3AY%P`Ks}OxO|nT(vzUsx>KeXRvly@anV@m@>TcwxVA&K&<9ao%5}Z{ zoG8+z(EF{9barS<(!iGGV*ym!TeeVZH_=(3Yf%k}D99>2p^a-%g(P+tsa1o>HXC!AXj0%(B!UrU8K7!F?hixObEIInReF^RP?D%Or}6?4 z0E$HB3f-HL0=KP;5~Bb!nj$cSX0vgOZ4k+@DJ7?WeT$UHO_ z9pzY!$1sSs%M|JSvTo>W1r+`o>w?8Q4K*jJNpqSYN@7{*5`yhS{sQjwWKy?t4f#ie zH!7GAFX6Ss;q@wgeA1PjYVzf2(p1P)lH0kifO_BLY`UGNN&hoMFavE{E1_b+oLoOi z(hj6>N;Mr*vKIX?>yS*n{GCm0Y)e;2=j_*g*6Xq=-If!1Up?;EkYC36o(5#HOgVJ? z)oBeB>AOIN8VHM@*E>!5&p90E12}L4It5WZjcc}z8tLJBugKR2#*e*!NN5H`$Twfz zQRJ=@>hXwJo$X6@>%=@uxGr?Nyw@Mtmd}QZYkJ9UBE~%rn#b2$C&v_ENJJF;JS;!< zeVE+0hvc2^xW9#}^@aVA!lEi0S)x`>yZUycQItX!kt&9RyhJ7$IxFfEOsCYKV!ZpU zix7C{?UTF(VmSNK!tLJxlvI^)4Y)M?)-{vl`Yx(KY>^^4KSJ?L7Jr*LmPbAP;JK$?;EH9czo2K>$t7E?mD@9qmildup{`u zq==(&-p!*LIz*Z+G)Frt{UkZ(`1K??s(1=tL@7GlhoJp-aA*802x)URl22(?yY$@Pn)WKULNP?zbncx8vjJL(T(dh53a z(6r;rZ6LfYm|qFpoI=PfVYrvH)@<$Kb98l*2}%&K4cdCoo@?(p>K3vW))u15Q+j7f zr)6`qShv}?m4MLL>#;YqI+ewXWVY$-I_{F|bX~~^D-S&4wh?&TECG6DUorhg%@HzV zb*_2-BJl>N;!h6`;sK};P39zh1V?N>&Ek^ z9a2OrC~%nk5OAnk+rR@x*m#NdT++CL0i8FmC3Ti^MK2C9tGsLHYag+~ozSGy0~+T0 zOUx3Z{4(!HkledJkvnsv1ExJi$F&1y`Yty6Sl`5I@!@)K_Rf>!P2y|A1x#KYOEE*p zG&1O@mQ>KelW?n(zp~rS5O-eQH%t-eJ{oLOmS?=lxn z5WhHBFT3xv)A17Y(MY-eM6;(Hyj0xl9;Ils30#L*5SS>9L@Jd^-30~lmRwAy8K9kr z+(3F|ylV5?3{t2@AI~3HOB%sLKv60>qFoERj`l)t~9E7fQZJVU_h&WY$K+Z zoLN&)YEaQ9-)G>f{+jj9q7?D%Xq703CQ#?@KruOGj)`p|6Oph;A3eyq2qI4%vElbs z>Z_nCVXPWmd`;j^F*i>sx0DIvaLx=iq=^~@JB}~m?gX_JsnmDuT6v2vR+;vYHqma9 zsv&J|Nmlh%Q>2;T>T)q~HEZtd4{q?cG)UTU412ZwSv+YmJ-}p)9hd|cMMNHUCX=z! zfc@bptbNF?Zy=M#M~5ulVn@3uup|)v9LCGJqj5N0kdx6+`0-KrQxF4^ZQ>W?+nmqL zSp;5@XVGT@Pw@B3L^t#k(Z{0!VxNCC5brO@QCqDIBVLYarLPQYrgR^G+x-vyeEyTi zS>QGNE&0#Wy@2u&r<>ey;Y^?1jC+HZ-)_lYtA_(3Dvdd&L^?y#2YSXeh|TV0j#GA$ z^@Gem-8Z-5?7}q7?YfvYQ8EnT@7!q_MDY0#&HL$0DSpj57eWWbM>Y>{OKV79{?uvx zy++ch)w%gh^$hp?2uESu2NS%vLI>+YgWB)>1(0#)mFRh{yh~!(mt|OB<3)d!b?haz zLrHfx>;;0oGs-#hUUyQ<$sqLA7xRd1XD?u2)DtT{F_yDFOxT%u8u-1ydcQl0U+R*} zp$oZ^cN!(JB+e;6n9?r#7F0IPKJpfGpw!PvA2bFv`WBl}dThTNHg29_uN&PlWYi09 z(Q=W~fq0Uw-DpM>)seRORX!JkNbEiFc1qiuF7B3?vqgN`ra`|L5PM6!($bNek+s>< zyvMsHtux|9w}jJ5v)2tte{MYu%zSI*81`!Edr-}_czcz|m3Xlch`C)Uw>SDMf#lCZ zFTm%|_Zd7Y{?%vvwbftev&1oWnQQLIfgkhA?K%;g#%b6xe-`;Xh9TGVTk*1-Roo*b zg65E0^Fyn{ZcYZ_Y%cR}55MaG-Ne%-0*7w73a0F=4hgrGBDd$c?JRy%+cCEl2~Sqj z6h=LpugzlLNo|Vs9xO=A8MnCUlthd4KAdJMyR1R+@?BjUSKJByd`kTzrTgpZ{2uT zcgdA_tF_8Kx^0?{yshcYz8=kd<=E$~Hkk41rC&OC(jR8@rFcaoaY7p16OP4)rVVkM zjdlfb733+yUjfbQHm6VTO#HkHu+;7pWwoLV1-zCBBGdGM+w=LJlcAl|R&+-`rSoU| z&8<+B<)=E4JKvI@U#eD$Y3i+VvmwM~IS+psDA|e*k*owg#*1YL+sazdIbH+$OSbcb z{<}WGg5$%og7AUDy#1;eU+c9Oo5|@eyX62r2jO`zupvI~ZDu{;er?((b~oLm5>95y z7@AhBE-S;Bq}{|0-EwQhvYX-7An;SZuQRP9J&;Xbr=qYSe!h|3=*)6inUc^OwC+22 zTJ1%ELes?0Kl5;_J&cIr?JAdie&D|nj9tHLGJMvU1q9GKpKuNTly!%TUz((wAM{vS zrW-&6a%IHas3bBY=;AHE%=@)}FdX_9KBnK=Y>&Lz7}HU7A1SkBo~>g5*?i`)8?%NF z=ZD5}u4I-1Z0qPWo3m!RLoPc54xj)Swca+q<-UG>jMWd{br~d>?Ktut4>bj3o%y6+ zNeHqZL`o|f9K)Mz^{Sh`7xxrf$XHY}w-;A87gu+b*B1L_D}PD`no7>I-S{?tCNEtq z)ydV;El_u@WiM;E)i`7YPVKQlS7)~twN~RKNl&y4o=9H=x3THrZQ|MZl<6X7(K6K@ zdv7Jqbm^d@SOe9w)AH*M)0f}?ddpOJHXn8V(mGx9A$F7N>)zTrsik}XzFJ6o2dd*^ zW$uPj8Lfr6^{s_pOKW`Rmu}9z_E?iuUD+J%tp>%k+FfmSa@EQ8v?rQz#k96M&2=Vz zE|03Ey;xm33rjV$b{BRI109qLlji6UJl<(=0G-sW3-v`vi-xx19*L6^MeVgto@Em- z>QYztR-@n8wpL==6F89$weyjP=>$|J5vTB-|o(pI?qXXila{E)#8rda5t@B%N)y=}*(m z`Xw!j>e1yx0GalD!*9wPFZO-}IIV-1@%W3o>NbtR)Y5QrXpyVCKrq|Vap+6Rt@$i+~^W~EY6oA&hJCZs-0@fWU6`kAgyA(bIoN9cC ztiL6^OxHF0%-cvRR!3})iIoO`FO<;3tVtECV-}%alNeI22fd4 zS|@v?vM$E+5m(%luV~)TTSKTLq>$f2$A((%&vv2C#+9uw^1lRY^MllzL>nV3zEC*Q zEA&%bV!OCdf=SYpE$aVDj+2m8=XIMfl~ z;L}0}6fpemkeKG@rFf}FF>#mEILH(fLv?26>qVjHH{koakqqdVAB`?%B$J4`h7zCl zt~a4@^(?%;BMb)#K(O5<{KV=v1NMc}q8iTN6Pm zb!#|mO6hL%c-Oa|?OVckeDf1WXqaUAqY7(!fT#5e4H&LEt-+ z=wObd`W&iDIV?Iis*|+-Gq0DR!(X@6U$^qFhiVl;4??KkN)wD3bSTbul@(YY#&fa! z4mD*=IPSr$nvEhPA}z;4qnepQDyXG0*pn$)7LAqk@ey4SaTlc#rb3*zZk!j@uzaHO z>WY0Xq67uA(?psy5bmMlx-oDITPwt4Lm6jTNf8mop%NIZT~d#V`8cz~aeKnMqdRaG zWWSX6Y&wn?jk?@-V)@U@6#+4G$CP|X>!^2d;$zec$1f|Z)XzREF}S5Oa~N0B=Vh0( zMr9CwIq`xJ^C_D4LfpYEm?udMM{19Zc=*D>odFcK&J7du7=!P@ocq)9EYpjeCN8LI z<_kwB3Pf;Sf9U3EO9V=b6Qk}pl`-a(*1+nC{le3C=!WDFMowbxC|#VMY58J3WI6Dl zbF!cd8>Yqw8p4wZB8b$h7#s;o13EjBDph>+2u@EMdqdxYE+d90{YX43&^JVJ%7hCPM7fRo`A9F+d#FP)dZ z9RAXjvG-ou#l!#+R`h77%yeGZHE3*A;f#AUd?;3ka0@ccX0$cI)xY80drx~reQehN zi88`(KiYz(LQtk7IQ;rmc6-p3q%6l%4xvn6His$OmAD86DKGN)AW~lRA2sgWgv8~@ zFrr+frLZUpaW-cW6w*Ix2}Zvh#JORl`^X9jQ9j~Rt|I*ZRZA?A{8i%?5(|rkMnk5N zsESrZDk2p+e>P}H)Doz~MTkgYk(39K!lM7Eag`$fs+~iFAt8{UiBaPf5eo^0g+d4a zYD6hJ=Es!!bpMF^NYJnMRRJBL7iy8pHlo7w3En zLrG&GIsg}zCp-X;$Q2xi3jON|o+~I31(FM?z#D<%t5|yn)`GgMUnZtIYGZLEp4b`X zSiJM&>Bw*ck!UC)lCWqbiEtz&5pifFqQw8|zalaPMuLdKIu3s!{zn~;xf8!06Gimy zRsLF@5xymi-Qu}4&(9pTPkrfBn(i9D+h7cI1{g-`PsCRHnK{?K1-((5> z8IrNv@c~|E4gAW*xyh(;l048-R5S{>syU7^KV+3k1`BxcG9HA0GuH?|mW`nU%UD^Z zH%Wm})<`RYGP_J^CN!g;W2tcy0N{;cuMi;C!wXdfo*aQZ3=RbTn?9l)l(t)F_IPh$ zhHU?P5kqcD%#Wi%t}w~)A+DH2UYu#I^SZ^Pp~NA3u>&mKj`Bo&*e(U5+XUl-lZF>A z(r<$oUy?^1r&&og!d&!SG_uE?9b;fSY2`Eay0P?--RlJ#M~og(N#7mg6QquI1y@(N z!&v*NN>-gz)on=~R`_tF%vbbAOIj0$(DN+`3#uuMj8!p(!}zg?;E0vrE?6#nHVH^x zXDl^c6WY^^kS2huL#AO>9t407d}xi%p)_yugrgzD-0;r}AUzB|881957!&+w{wsU{ zKa2MU^y*8&9*=9H!3(>ejDFH`u8$osn|x2@8=DtT7v_)19wzKd5t|xa3CwInlGw*8 z5u5ibyS%w#?kj#?4~!a(OFbjCoh}<@Kb6c4GO>W|tRIxkUrJB76-*rVD#Qltw+Z+?=QW^uyN!bE{69wUFr<67BOele|$+O00!HO;-yu ze+GvCK2BDw?4u`LHu%LTIi4p}12>m91fI?Yn?hOLNtgDv4io}y-%R2L4JF-nmtcgX zA1j0Yyq731tYvpk)>P(s(Rbw}XTBhUut;YI$(Mx>i^fErjb8(^Irwh&0NJXjB6knT z#)52%iNsS`Y7SM6!BZVV=^hx?PHWQRs63%f6chBCw!J@JGX?~E_5DDMDD zka2q=rz=k?YkyUutio48sY{NtLz=;r1(#wvYl5i5cYNzaRWFR4iE4F4t=mY|P124d zcC)DqzCB>Sm6UTeAPj*a+O$thb|fbKM-#Mza6f)x#f8H3tb=m@!@ys=`qk9Z`x3y= z5OB72P-``^h`QEel}EGKuC4Ud;UeyX#?xTolnsb!XAgJpty3++;ZvP9aKfNl^aQcQ zyQ#wm$*@RkgXt?|R|~J8QNHY%Qyn6L_8Tq@Bd!(rstNZys-j!eh0LbT z&_u1Nk$P1V%^HHBx-9O=&u{2EdR4GXef%7e^=vT$N2zzP8obRB?%^y+GhbbfNF2~G62|m%cJvsFn`78B-P^%!QUt!Yd z%VE4S8tjZ3P?dlT{bu`zT>yQBRrTmc!^RNGyKH*sg^a&9{r$=L`MJ4PeIL$u`qcIo zdR7xAYvM^xRTVcxV42(^6lgup;)!*5aq-Hr1eqiPPP~)<;QL%b)tE+x>^D|0W;2Db z{KC|`qIeU}V26>=OU_O%-Ts0Z*IPw%cAvgGShUn(N`4J2xN^_vm2Hc3;?%h_Of{67 zq}7xjiU5TZ!3V*2!OHNr>PA1!ldZA%dB*{M5bxJblyJc|3dqKQn8>OFlw{D}Z{T}z*3mlqxE@?oAgMZcf!-fO zsGSec%`{8?znAa-(+!h}iIer;F(gck|6Mywk$*5GLe5r3w*M0ykyHIAJo4#P`EPk% zpXTSECYYb_2s7g+)A7j-{HKXtT>pgy=6@FH^$)@Qzl!kxqXi}l(|^s6O#a1>h&}p- zWtC@aGLkx4>S2+{KyNJ|9!E&A-bMJT!dA( z7u+7`@|HL?QrYq?Ur{^0HpYn1I_(0i$mf->nc_cmi)E_}#%`?p5coxZMsTirdZK?x ztcL#R;Ch4rThSW-3m>7M^0MA^;SrRraC{(m9LgN*>@H(H!u( zg!>Z8bBs$&7bFoxdAQn7$nsHKP#{=LFT6&#zVu5o6C|z%glmtytNh%4+xH!L_wdmk zf17k-^Yjp)!FWFO)8x=&JjdeL(E0QBZR1!`Y=&bcFKhqrN9_O6NAvFo>hIBz&r$d{ zG=zzf@Gsv?_Rm-5Pw&isdT9RDv#_ubaxgRh&$Ykmzt;ZQf38FS>;Avi{|5c|$FJi5 z^xkA*{A!SX8?}?HU$fj1+?m6Bibb z;r#(L!Z;9xPLqVT;@J%P4rPxr2QrWhi*kcid*gHUzG4e+(1)T*Xt>n_d7n28wj?Nf zzY+nC^z_@yDW%dv6wT>pDS^RJ$KzOBv3!jA8^2I`hbTY7W7`KkmD>A;@|MgU2mTOa zJ^r|av^D+sNn}bL%Crru~2X@3KA=nZCa$bdx*Hm*Z`UNNmhG+6xx*Lrtih~Glsz8$YUORR}42+s<;fw(r1y~8?o98T0?XIuX&#@?T6no9CA z2X@6m;LkfwQYY3`gdn21(g1fJ?H`jo$HKfGi9C|>iS1)wqvt#?B2)S8VF;Fd3 z{W439YW!1?{w&2%`zqQ zHp1zN-C0j^tVf3LLb!!s`p>tk7>%Hpm%;9IGkS~Ly)v=5_%N75j>TdLxo z8rA76KAXeX=#$@OrD9^h-< zJvR6D5p$>I!{0Y;sdd@ix;Z0T=)0S~;QG34rETf*TAwzSaS5hFlYB;isGjDnzMSwq3@R=O}kWLCCeiNGq8Beppz=d7bRv2xq7 zi8rsNW$0i?wJgq(sme)-AN)dsAe(rJJhsFUNcC}dd}+XLZT~&&LyQ0DY!T`SGd)Gjp4kTc404?qr6BeZG?n z%MI;4M`R#U8Fw%S7vO+t?Nf~w;tu0LV2k65kO1GJj<^9+~a?(|dgBdu&4!7^$(^Uc69-)WyW&bMc^8Fj+_*klH64RjV9GG|d5 zYBK_99U^3&Jw_elwdTMV7Z!@A8Ei>PYc`>IHg95PY-8^IADq2&a42CPwilcvC$??d z$%$>-wr$(CZQHhO+ji1??R5Ii*SGVv{ePd`*_q$Y&hB&F*WEP*)Ldm_4RnQbiJ6S4tSRgE5uNR~1}CW-F(OQ&|4+`Wi}wnfRxJ#2TOnUjuZ>G4{3U z^?m*?R6VnLcR!HfU+j#bWsx3|;DV0H<#rbcb(Y;Eq+0}55vS#ZrBwp!$mF$it@~C1R?PW-1Cxv9+5#Ja_Rzkup(e-Tr)+$cls66Z#K`O$#py{Fc=OoNLxE?PmxhCW4sr z8>`}A!Wju)vJZ!iM@<$S-05AxCSMFEzws3nb(_V)wTS+K7u81P8w?6M!yudjAAYjWP(2J!{@{6+hh)at6OvfrIwmjL&Cft%Nl+POhH*Xcpp-7Yl~ek52kXpIEeLa zVWD=&mKB(4ajJWu3rIU}rdXLZRCUPSg-1B05-=VkLccZSC8PHDi{HnT`D^PouM{mk zYd1nC>?qC75+XN3M>t(3B)paw=ZmJgH;AkUl@Y*R#Ky{da>^JOIv%K+mW!kx=t|;8 z52MH6gl4QAgux72KzPRnh#8{0LcqZl2^OsXry!Ib0L`ClZ~6E}jg{5`CrqR^>bbWx z{NFAOxt6YFtFmQh2-`TQ%8G7A#2-usU$b3Qky;~|)G-FIKXD@>`qbOyqM1eElPd9V z;&$57$H=?+tG;?40IS>ZeS^U2nZ|#`xUZ}Za^EHN$x(@t_?~hecw4y&lLO!5g&>f8 z1cl9S{9hopDJdGwCoz^Q>2LIMbT}De*zp^@UNBxRE@=IefeTdFU|Rr;2r_bo)SH^6 zFlS**XUutKDfCsZZco+l*HYCgmPkk|u&ND#9!?{Yp*r05$r7)lbl1` zPus7K8&<9l73;n{udsV!EzbNKC~NhecGB)EyOo;hM}vhMYr-Ljuk2kMF!qR9MZT2k zg`(VlZsD7I`wEK~ONpwbWt0&Sl$2G1C{fM)2GkTxq)B1^WU3PLSj~&&{mbmD>XxIi z+wRzE=py}KESSaH^5`rzZg*-z{F(QCg=oe4UR@2rXHKx#P`Co7D!FVop- zw)a7CC9B0^PfJ1J-~AhzRaN&(6UiRu0fZp??-4UGmv~0xr>~6H*riMIvq(%rvuBUy ziN5-4su2Uo6~+tLG|RpeLPF+pLPFw3Msj+}1jXW%E9nu?W6SqMl$0u}x|u>r5CCg zxz6-8GE5_Wz?(z+-QwYCP4$^=A;?&dDhG;ED(KMo8-cLEHUW_=X;@PfyhthU>EYG& zl<7KRaJg{4rbCbF%Y5Efza^#Jhi$8AO&9iPBQN_fBh~^q6=rb7Wx4SpW?r1G3y-+Q zLA%L&SwR<5h)?(RGc@hUKr``#gA6O79{)}fFNfC8Fi`q(#_5W;DfRseW-Z)lCC27? zF@1C*{C*FsY^is?-W)T&{=8ai2pyOK_ClVO@xP(xV!%rzR_JvhuLHvG8-Hpy6u3_$ zLOB?GJPpAlLA5_ANQrzMphb@|pSFLnwl0y>(QKMeokn-jrj3c1niP$?iHL?<8-O#B zzTHtAEI{`cD-*1TIv+b$9O<~-a;I7Ed0R9rP9FHFPqM^vq#50? zo!Ka1(R~QIK0QX!f%v|fh*{L1Q>n3~$&`dW=(*jMso|DbpiO&Fzc2; zMR+1(M&2YvmKUuQOgDcQu@8~1#uE!5We;fUCbmAihpx$3#w`ll_a%w0a(lwDl-!)g z%AsFsJ6$ax(Af$R?}D8z;&u3DwTSqp=tTs!-GyA{ZOF zbP;ao)9z@LCUNOvuXR(9g!X<27jE`+bFDfPTuUjr-Cs4LPY~I*@}kLI&R)CU{p9E& z$h9_2?bvR~Gf~Ia#y`MtkWaTjt)Bho>hqY<8E@(x{g#a7HbE#w(>lXClcPYnUWYnu zc5p6-Gmac@K$>@Gi1-={Z=q&Tk3o(_6V0}WdFW~r$?gi`R7HN27E2ZS4r$A+pw19d z=j~I{3m?y@)&;|W>weR?ueSWNI}pRv6nxWWWxSLn;z2-~eQ}Pt$!#-+JMbLY!M*Zy zL&tvd(u|~3ltwaRnXT1P(eSw4;mGjO*?jz&7K~23^t`D=y3VJK#q*B*TMrS8Wd+~3 z{wGAjLbjNW93~}lk4-p;q6#D(+^lL70vtUavI1|9Yz=7sPc$n|>sPcr7QHa`SAFpC z6xcigttF11k*K-#AGjdgCLr*>*GI}lutAE5g3+aNN%hX5MU=4mhsd^GwF_8ANdAw>TKlbmD{<2a zd1!|BZ^kno`EimUMY}3_r0kGr%Yw30dbq!+(lI<~vr1XN&CvSDYMZntYtzorg5o1y zlcoal%2Mmf_=WiEp^0oHbG_Zi?asgati>hC+|*NX_O*rY^CDJE*1)L(G9F;0OkE3W z!c^`^RhbBD3hu)=KJZXeK5VT&{&V0_=x3ij;OO-e3>7!>`{HFzrdtacx_iE#j!-V` z>MW$$y93(C+xk%ABq4F9zcgV3$eRNkxqtwlS*vKqxm8{t@$o#+(^eja_%y7ApI8Wr zS3kC!Vib}H&0(d`KEm@KZUUqSOlv4agJ3Nzo|;y4G8|%@2m?h3dt41lvEn~ZMCg*O zoly3S3*UQ1E-XWX_GW7fcLz2OjKYEGwq|7o3G84@&AgY}&`gpIoYPY}&#gXvoVg(q=|WLEmPV#6c(fl4IBW+|N>DfkCLc;9RReg76xU^sk- zHD!W;MK6sQ)h&(uLU;Yo1_$68&fM+Z?=YfcgvbtgvY0X}h2L!ucCRvQEr3ao;OnZ$ z@?}b$;Zg^R0|hA{R{q7xLnDJL<&O03M1*USk>VB1A&cFDV`2%#iHK2(`j%4TgYNkj znV}u#5%VOdRG%#z29F&L8wd6J5_dH+F^<+h3XW!*C~xUeLYjS}T0$j@prjRD-xWSM zU~1id4>^Ep`>}kX3WNMZRf%P2ck%Tk37FwS$!NR$Hr;7Df+|@C`}w$CZyud{SEfMO z(K5moshTA8>;5^|Z7la1`xh<#S{kNX$wKF((1&Ow@Mep4WXgFW3EeY{AW4fuLrlO= z=^&lxmX)>1VQQenQdGD26PlC;iD{P;^-xQ!BB)aP*WVbZC5a*h9vT}r?s-+5t|Q(h z)@(1%LFd~VZL|f|BRk>Ut@>V%3#TTNO`PuDI9^r@GyF0KZ;RJA1Z0tT-^il0g9OUM z&FzK;b}^L)AVvOFZ=a7RavCHm@nc=CUgR(P1~GG*?lkR*T`(q!0^g%3kLpqh5)lac zd*VvO!)URd{%gbIX+#Kv`M3)dF#)qmo{hJ6SO;G`7d|9MhKnYK=RXGaj~bicAO?Bb+0QZBVUn{Hpd6{v?JxsHFjWi@TF6|es!5)6*eS5>#N6-6G!CZ|{W6HzfrFrchb z!2=I+6GwKs>_I1goSHvwf3{n^et~+bJX*ioIaeBaEC$zvF$ziX+GCfd~t zR0n0^&fk5-qPA6FAY^)#$WGL#+Hq#4dY5cT zM{Aic8<^@nCt#vql2$t0Y?G~bpuTBx+BvstxOi+-kBcl*+(&-m>{sv~z|DBE65S^^Ibun-P=>es&@)Udl)^w->SYk)tAvj;s#IKO zo?3X4FM;g!u3L5=4DaESoC<7UoIXpoB`uO|ex8AAUaCXeVxA0}?8ys2+!MffG$ zjDIx@e(ZJNW%=CvVg2cinMaD*MWCS29X0}Ip<5~v*O&&SCO_N;qvIn++VP1k<7ooP z_RW_m;03lPq9KYF_ZP_GfSR#8)z2Xxw_oMtlE_}{X5kFPc!kEH@N-BEd z#89|Ey*PXMRNmn@tv)S^Mv&sXjzXG>kZBBg-LUL7(fZc@bX?HOAP7Ir(}H{y8&(O2 z5;KN&EN;6zwp}U8|xdls$6XcFX7dts|71$CGyM4#8Jty zY!WKnI2IxORrj8?Eba^O`~2vwPltg11!p|~2mnM_rBB71#S=b6uM^z*fMXHz?_B|* zEVy!RkgSO%U-Vpst;|;VHZ~X78*SvbA0>XW#4HjCxTi>POJuZsyIJkazhgUa!$Q)# znlv#<&W4OP>fb$47#`F%&+04>!QoMo2@WHfaqfhQqpSOlqmm;Pqy*%q@T2e$vqPMJ6yi(aBhIMc?FrmT42_U3x=i#20a&AYs$7)gC;)8h`j?=dm~y zf3?^<7H|?=k_=*1(6vuF@Z^AI?O0t9by#;eJ>vO2d+=z!uvTq<1G~Q+L~Jlcj3*;M z6xlPLG=1QuiOiz51mXr4VULv4wm|CG559j$ z^W#%5n7fs_Yi+t#20S+PSadexwUoC8^{H?x(>sl zaRKQ9|ILRh+TPpoyDyo_l$$FpOM7zx{^!f0G}kRJI(KCQ@^cf%rK4s86;xg8I=2IO z5w!imDX>(X7t^zME5qah@*P+k&iSk}_y(TupIi~X&kKf4sQ!z4S2i4d7+CZ=bcU~8 z%Z?8wyL<`HUw!v}EjPr&>&KU1y^0(ckAJq#vZ*}xH>Z`Nbn*t*%Cxtg~M7JE*BHHPtcdGiZf0Nk?@mn$Msk}z|ME4{Q( zzTTI3Sih{xy@9lPq7G3V0P{Q0o~-xiO4bc*HHE6F4KMR(h%z6ffCg;X1Tb+BSexD@ zXesa;zFds}U-%M8!#|=?7$)fLp;9!hS$MwFifaHxi9^lA^Q(xuJ-`9{nMr)n30ywp@_7Y88%mj2etGZA*B@A8$c*y#?qvD|4fu zo)bNRE!-_z-dqID-}2C9mbBw6&2dx!7ejjXjTWrFlFzIvlG|YR_O6#$JOgsAVV22H z?p9;X2XhkNmS{z2=W{Q+u`ZH6cgtttN>tzWDY%g?&!`<-k|8Gec@l5zIMwrB-4LlF6HY!|+55?7XI-k% zSx5Cn?w3d`3K14ZeHs8r2P8HH6czElFJqOC%z+_yzb8(E{H}R{Zv#>cKrA3)qmh6e zUBx@{+rE3f$bcI&NH+DiT;ydG;l0Q0C}jTzLlV9Gq)R}VlXQO|7qofoW0O1rm(x04 zV660i3hK?{t8AGgTfCcViX9&=e*~Y|wA}o&9yEDJ5u+HPIzj7?dI06I>I$H9u6&$8QcbjutJ7F0uq-;YqmTR>i9Ri#EqNgZsm=`D`Y~ zI9)MkKT?LfIYSZGV9%hV@9`J$SxB3>(dl+fL#EM~xJ-XSfh1utqXstLv(pijBkNVaR5dp7p8n=gC&GQ_BnMVFY^wELtSmVPKVD5QmbX zELst#%{`FIZ)(-iH5uGdpChwxBgD>MmqXTq!nNog`FKnqPGE9P02fOi0!A6fFxwS8 zhwHWv@ObdlDAO&lW%c4jdMM4dbga=Q`1xh7JLuV|aVEf@O^ah*Wu3 z%_Ubu3i7it(ppd#Xzj(eYm!z|iLT61_BWTx%cx?_zU$%09nj3>cAS-auGyla87j?Y zkW>eRGyS*LR|lG7&%5`xcRq*S{1^F|^OLl<$)2?HUi32dH@4-TbLG1Ax^`_Aa&tw( zp2RFO|XP;O9x0WG-OvUYApJ1St#nioCF$E-R?-Q*0CpCT}UmS8oP3^>;7 z0#a9NDqE;q(=s&mdZ2eYscvK{R$3+daoP_J5-0KJ+a-$)?R!<9Xh!yc!yYi@0g=(# zqe-b?ieRGW)j76nnNFsg`|S9kvP?2i1MolOp<~ozj$^0_0e?#j^kAgAdw{bMPa~|~ zRrR*Rz{o;c;WVmTzQ%u*XQ9^~JgOTwFf6lThCfYVRGKTJR-~hg!lUUWh$)qsD?L@P zC6)6w$sVa})wCC4D3Md>U6+SH5sq58rT|Xxj%o=+WZ2uEhaeY3oDb*umhNno!RaK)Z3Y+O7juGFlV~&&Z5xhEnkw1oq|i1q4_`7XVc}f(R1W>&Ck4 zLqOwa&$C4XFvWizvYiS0I~&WEBam{6q$|P5h6ND@Nf2f$jeF%#XePPRAKH(9=Vx>p zJ4zSm0DvHdXcij^dMN~P8`%KTn3RXrD34%eRBA(v2UG=@BsN&kQD`8lu%F;R>jN}F zx8(HSv5@~$UdzVF&hS5u_&?;e^mPAk<+URJRbKl)_3*O)CxZ0b`1?P4cW*5(Zc(?1cFvT&sNmP%*533cjf;xf%IPtw!iqv|IJ{_&iG#pp#KSe zXvP00fdmALZ%q`Kh$@fbRcglL+e;)w0m^O|*Hcpy@@ueF9WjXULX+v_x`b)QK2TX% z5y4q%Yl=Fs@Nr5bwJhbc zA42AB2i>u(y?ANh@%V6Hh1T6_z*$a20kL$umh+$pJ)<;YH=2%%(X$!-(kAn^aqEq% zDwk>@SN0KHBcRk!aT!$bPYfa{#R$I6>bt6lQj9(8VrKlUYTZUE-bme*WDlDSS=$%Y zuK^)b%oAK2qNZ2a71A4Nx|eb33xRe~NGae@Sou z<68gA_5YQU`Y(dq|NSZWpC~E%|0c+#|6dAn8JJj@nEvx&S?YB2R2o`i<7PDJ`sluC zYm-2zzafgX5dGWnmjDnNL3GuSG*GBcZmlSkKms1Q5y)TMmke-F{SSN*GF3i-dX?!} zyvjvHGsMf`$7CBTzUsQn&*{mRmvfl-JV8J|0fMUOzQjDe}l3C1$nelMMMaQN07{N+vwCA8^@&euK*@bfwX zrzl^E!|$LwU>Y%mO#IMZNK$^`U`38N)fKxOPUDfqL7L|hUwiHlkL=#>m0?emKXpeI zfx`3jFEgb&Y#)qIH6Vf3J+ReZj29+M&+mJE4Ypm#_%bmr-Oht%!oExWKYYvzu zIcmD8uq!8U`X>C?!LVNecl0tf-~;xZcoc~gO~Mrmn32Cm5%UJl%->SIiw7`y|FS$= zqV(kKIZ8%KPEuCdp4{5f*1|>?HMg8@cXT$d z2Yie(AePX)1R#2tSpl`qDKkSoxtckXj9x-o^WG4 z$W5&Xfvku<4$#{_jg1fJiQ(e2_KrwN{52MGA}0@7V7u3UnINu&J$Bz*Y-2ACq_Sw8 zwD}3zq2DzjT&A62&Nd?=OHs=O2lAZB-JwHLA+r=>v6BFuHRcvfIp3MzDZSxZoTSez zum3H)u@l&?L<#qsfLjmbFnX*VWodB!Y?1>K=aXRzW5+BPnB@I*-MV29mSF4zWZb%U z`VEc-6h>(cuNAV*;zKX>ngFB+DFcDPx8mYNr|gd0fqrtvOAE)T*ZWct>Cg|PcBBeT-mJP^dRs%cq^XGQ zIN_ZATYKZK$A@i4oDzB&*uWMrnMHmiG|r`Eh1p}!3>)by0pa0;*}Hm$Lyy)OF&VY~ z^!Nn%#?T?gl0$?X!;>=PS`&zl+hI^hgPx>K-C^9R^J4F}I_W+X=pUK?W0v%|Fq)nMGa=d$^T- zg&Jtt7jvcXhQJM;v0$LtLDc;t2M1gS&x$Ta5{q6K)G4EmhMF)9r9aHzvG;)Dkli+} zX~e!fsJ@EfjHf;@t=rOT0@e|+HPbzjWbf6cw~4I@*%GcXAbB`#fAaS6*7WK9ZSjqX zPXPsQ3~u4eS;A}V<2AsfYe%7*6Jnen)(RoRi|L2CVaKHoJ-_6u0u-rEDqbmMA$n8zm}GpsX*^cjx4(RZBW#%eX03N;p8fK$#plzt@3EfbLug z`;upS2LoqkJU9Rt)NuYnmrHT+r4()FJf!@awb|?`CMDy|#u7asIl+6LA=woD!aD5f z+g>qyZ9)h8vE{p%;?!ouz@`G^^+c(U{(Bh)9fxAz!cZ0177qaSwc<*l{Iklnv!}hu z=OJ-v>7XLFLx3L7kD&ooiHg23AG;;r`$?jBbx%-3v4tZ*X{jn&H_0;wTxo^15vHfA zxqTe@Z=@ebDTW-Rf#Gwy_?;kZ1*ShVY|F@O;2lzVPepT58SaAk@Ip5fdR4QoQiauB z%JW5vmh4kH4y%S(sgKve`8ThB2pAE~$U?PUQpzDV~T8rT} z|BMmf$5+?`F0S}u&r7?wwaX_X8!XP86o8}%1k9g*8efPTR>V@5{+?TBed^;&`my>Py~117bURo=0e=Jl;*+tWdyLPkcY; zH*mFdt9L7y7Uv1fo}xe4`FKW-EtS1rG_7hRP}tKrnfz}XnpvGN>lGe~z0AbWLv2f& z>8!4FV&`pja_fV#YurqANi-@;3AaSJ6~hSJ{ju zZB9uuo(IOiDsh0hBqg5dq^!`Dy*!^?3;$mhXGJ|J#}!kH{Lnu ztS8Y-lQyErGgBm+NbRP9GaVKHM`a!v5Z--hhAXwRS|LEyb#%Jco6@K22Aw#Cd8$H* zDc9pR0c9JpTcA>Gn*IROq#gU|0?c9vn5hF0Y!S;{mEP_sw3dGz@Cfk_4!`mrS%JZOCaIA=g7LQ`Uvi6*huf~`94%7 z_P2cP$hi(eBjLQ=kb8Q_K*Ru9+N6+sj{Kgv;0F<6|{ z`Ki!$LgsJy_XSbU1!(-rmq&ceh1h)u;Flf4*vLLNVcenV9N=}?AZ?9zD_cWh z1{D5o#o3{`@9$_pB&#lWsZDrp~fn#=^>sC#R z*a&2`kA(eL1-pZ=P71>$XPDpu4G6lq(p&7JhM>K3$CjiaUpT?Y^cJcf*n6;nmCP`l z8AWq8G997&dJ*8Pc0`kC_$8f0KtLX`+t|NQhTI8Ts0|@31gdMt%SQz;8n|IGp*gyC zgDGL?-Ovz|<<^(nL`?$u9q-*zhhzU3@;?_dB__4W>t)#c>_;Q@TC(=&Hl)b;MDl zE4T2wD6?XT&QxY%+#c@}L~>rCy#ZBn-hgLA_>8h}>j0h6dQN5X;q+MKw^7o~91 zn7U%4perQ9>G(krhW(-Oq=94SD`5yLL--r-^(Ia8V#DHc0m>zqQBVYbZ)jV;`54m= ziYV3};)zTLFNb;p-Tt*L3Nwc*rfa3J$A>^`V3_A#O0~t{pmn6?YR4h04ev{-onWXa z#desva_AQ`Y(&E69$e0)Q%9jh6y%$@u*{dTSP&WvWy#0fZ53cvD}o75l$M#zBdVlE zx?TN;q0QSlfvqn&UOIKmRG79wzkMV5&`Vw{$VQonA{;H+h;0G$heUBak?i+#sjz3> zj+nc$z3!Y+QIE2wlni%$PLFO~ysl5xoN8){&#Bl`Le5UkJA2CfBI0aJ|1So_A!{)& z5jgc0E+raciIHl};=Kt54_31GVRqhvx7}5D;JV@-;1q5Ej4pwjPk3IsgbPTmGH6WR+{{1M>13)NLwM5f=+dPreftBY+InXxQ+OJ?uTH zTmQW=x4!jp#-Hg*6622T0l(8VCGnD7M*^qx{5!Ec0=ky__WWE`9ezCUTN^TFBDR~x4rIELS}kN@2v(#~zJAd&gfi=1 zF&a$r4+um=kiJ9uLrnjm#E8<`-qfEjgN+o+ojTW}2iK?3Om13kg}eSr;Q1IGpYWUR z&%QF3ZMFv%a++Hhl5E@$>r~YUMxRec9O!mE%GIJi-4wP&Oh){EINFJ@NQGXaL3}XU z#)CL8ps9oQQ!Q>u*oAZ;86j@r6ey)dle zLim-@)ayjw{cQ^)w(9u@lPFbkCiEkV^bM)Ug8@o4bfk9~lVU3&`y$~F-LL^ERw)7H z3Bd`%_9>$v@=h9+*^BbpaFu{D8nW({<*~J6T?<~bUw#?TYEJm9C$i-rz+|E_feym* z4S}Hp@X?u&m!jrhg|}G|0qOzdS`a0d?beWLIJ7GP+Y+@cJP`F*1Mn~oR5!p9#)}!h z=KhHa0rKiax_d$yG!HI_3^-wm2$o3Z^;|{(B`l2B*T5?ir1!@CZ?pTApJeuS|Coev?z2=_q zW8J~1_|>qt8F<9yKr0NuO$RFF7JUw5&e;4=+}kda_5KCOSl(3~{Ct#1jr)K9PT|9) zy4zaTLS)?A#e&oE8DHISCVNR!p|T9d{PRkWB(B(pObz3C9=MtZXa{P5fOCbqOb6V; zfD}osElw@dQ>!W^RS)9Jiw;$cLQBkF2t4x?gefS)Rtb1-?Fo6$wP$VIcl~5L!eepN zAS>snWPyAVETva(8o8hFVCxXMnXmDLnw-hYC;j_?A@DpMWm8d6t;#)hAdA5zu9X~{G}M+x zWua`0aWW}qL73jX7rGIbwIJZ8c}7-*fZFqzwb#y7b{acGg>Ta3FIS>sBno{tz-Pl2 znJ-NSMPUVS)@Uv@nt(72kD6+gwT{Mn+77mCBvPtOKFONeweLtGfzQ9%Y*{Rd; z5HndD@9y)atUk)J?!Fy#046w!*}5AG-SZj2sq9mme_nan={hTsE%*dF2Ihl-vtyQ@=}SkRby&}*-uy?&SE!uT|7s>*5~ zUd|dD<+Es*K4Z(=E98Um#IYlHIz~7W$UB(1yajlcovp+p?0BKp%n~E5lc{d zKWAr$dm5Uoj3!Bj5x;cA#Bs9*!<3p_mU%fE404btnNWctqvSK|7EVI1dwsKEX+21g zvdo`E>`~|UOJ$G2WmXIn0y&fFWz@MBp*SiL>FaOnEQo3Y{$YtH0J%1rsbUy;Gjb!$ zr|i-jCJ~fpe71x@YJ3y6+$WG&0;)fB3y`0*G}w4C@aLMw9snOsbQE5qjBgCd~Rfx+?;HtAuwL@kAj`~IBmZODpx{ShIiP*E^|f>n|EK3AfXgLKRH z&(m`Hbk0S&#GhGQ4EMG7SY;(TYo4^mShdopec*8HTIuVR(1d$k*c;$&9g=_9pT_sM z>qC+)OU@oowiS?FwHlwUj-FgV%s`ylj9zqdyTOE3Gq)Y1-~jK>jYAARJjhEJUd9_e z^8|#s^6DtQQ564<=cf1P7hBO|(vEL0^7GJd6dl*cK*IIvV)CUz%E0-PE#XZ&nYcIT z3ZSdTpUhm9O6j(~GWl!dnlLz4pulRR9M})KBHlDvfko#m zJr3^?`DoE{SWnlM0%|Nky4B($Ez;tK@kNVE*mHA^rOdvia0^mk;-(Pj=z~22`UG#i z1c_4p?b9o?#?+>7hXcb!CvZ(=6A;s@6+HWr~7Bmzzk9QG3l|3 zNUJ|$)zS<^dvK^`m}fXfj8QDK+oXXh9NG_OJ|Afj>EfxgbwM~kf5&R}9w#FApb4ji zLP&zZ*`%C7A_rMiVvgOvj1oxXqTNceUrMwT+g3J##&6@CKa+7XI1bXEe;NYB*>oVZ z?oKQx=G-t8Gy)=_n-aO+Gcz(P64Z-dMhJ@U{&-sUXF#M}KvrDMBSPP0`y<@3)@&jl zp54eQ!uv&gLhB=%$+FGn5yp6D-EYqROa=~@yW#Gd|EBkqRE#tHW0YG%eG=$Vauiui z07_IDRt4HM*>Mp;dcC}vCEYdlNYC@+?17x+!QB?F#AY8cDe9Dl0xc-%z#1tBswLb6{&@vbKv6iCwS^8M=(T+w`|Z9;Y4} zNkGmdo7NC5lz%OHv(w7Ytit+lH9{CTq3K@(c?lv!Bt<#Hp@$2EaeY&!iCqB#@B~}q zP_QDvaspr@<1tbit;t@T1X|!Ws%NOn+JN9u-tbTg;1ESNNL_qrh?==9z9u1Id5zPK zfZFTNAgoB@DPvfpxb&P6^=~zm+TY43&B!_xveegu%Z_JQocqZ!R%?xbD3zbk5A3<4 zZMQt9iPd_>o-NxAjI*&G(VvGwL@4xdS}OGC)SjE>pf|U?N$nsPY)^&vFPACg2b>%_ zu9=*KEP0l<*QA%_f1f;+;rB(A4-UsroKX4HsO#tx+h@HQR~>^}&c?B(uKkmA#@h!- zOgIhW#I2evUW1`&f9ct{#zUv}f@O8s;8HsZOW?1_sJqliN&zR?vMvSYQD;sYb8G&pLBqD($n})~wfsFlXhda@&_E|Jo)L7S? z^R$$|0GL+g4LFVTDq30D>-{(UoOiP}X)seTjxJF$ko8F-)QcWM)ze+;+X2*E6S`O; zylURzD+bOuP*WwlRKaeK;<>cdA%0-ctvvkMb@?8!)}F)f@9oc5U~uZlj2l&BmpBGY z8`jt(P0--$YevUsY1wIV1lbHY)Q$mw&uzJ9;QU61D`P>Cg@$yYZMlt8d&h1C5Gh)k zEul-?8Wx`1*4(RWl+8J!yi%vKskkV}42WV|=ry4e`Y6N70;34WAkw-Z z!gxR z*9ARdGCe}V`& zOvWY3GI5RWpIobD@zh!JOVJ_$MIEaP^IG})j|#x3n-iK{&gq#^COL`F8{4kH*o2@4 zu5F}!`eH1#KSx7`r06B<>&xiX#lLU)Zi4Hc!0Q`tpz}4Yt%pcx^dVBiVAK zQ}pFg(%E`!yzzvvU(;muvyy=5FM+$`6BpC*n(j_`jm~>603u75iuwecUEHe^*GUyq zYGo@0uo(iCgsqUsC;5>$RYUm;R2zByO1}rhz{*Bd?F72PcX_ktfb;@0BN2ABz^Ff- zE?|)&S6dHdrBb;@6{kn$ZhBM~e~y$8#W6A?pVim#Ei<_+Z1+Yzk}Gu>UDx7{8RuVS zx@%jD?L_6SPt7`v)&mCZH~N+<>Uf-epgS}Guc@zTp%klhl_Mq+Z4wS9Gy2Kzq>jjw z%;V{#EET1dm?0wujgJVH81$ON#WvzGi~-e0=?e;$$wa|5f+to*ZV9Psbm-W@pn*0F3l-(^FkpC3)D-%qUCHyZc(G!^_&3 z2x9??(3i}STljMTf9TLRki>&ZEk|HSB!y*TfM)C)@oC8fgX9aJ)CnQQ6`*UVdBRno zrujZr|J3t+kwldirW-~cZ0cF7YO_A9+a6g|fH*UN(NwPTX z_gq8@U3&A(4T3c}2NyNDCLY_KLE)I<2$LT)l$jWoBSLn^Nmd8{V$x5c9!KPh2}tgu zqy6jDm7`=f6EDHmn2%l3Yxp6;SIN$-J+gPMphO4T=4UwYFbHm;PUd;<nN8I2?NtER|7@ z6&OX53uA@hAMG`duJ7@YYm8MuKVom^QI z6s@}N0@Xy*0jWMlQHS%<9522 z+)?e=ztPB4S>{>~9{foNYt!X@wvNMB2pYR$q%=;jnW_u}2r@cm8W6Z&sh%n)-Z;tM zFqd7Ex(71KrL5Rt`x-lun&34^o@WCRzVLlz84W(rAJJRxdgrB3}fbWMhgY+xOI)Oi^co?a+gD0uWV?vAeX=IHF z?9;Q@n%nM2m#J6(DKvl%*zR9yS1APgT) znBmD_{;wIPbWIgK3R0wF^Gi%ZrC{fOWW@@}hEoLHOh3T8m4_>bPqXH-`GqF-#NSEW zaaes?Zms#3SYT{;nyZN7;18i;1j+CYyjg7l-DLK`mvXj}e`H`lbfQ<4s6Wbe?z&9XB$3(wO3Ukl*g9Zuda;4GK_;d_1D?OK}iuUQ{ER* z0umyJ9-bfS-h^UdZFUghBZfa)%P^%l$t@%%M!AFW1Y^Z^G5D_Mi{xt>Bc=5Z1Q*BZ zvAEb_Mo@Isk8$!&J`1J`hKL&)iWYwgmCqa3--Hv!N|)ZENC49;#WnCtD-u%`9vG{Z z4KAw;*7$$KVutY&*-Y{fe8i>j`hsqL+c`1HM*M(NsvYHW(ot?1ct!FIg{-5qs$EyT zRQdWNQH22Mb8;L4vgB6tdbwG`T~U$p3H5Bqz!0nnihiDAHH*ChF_eUr87hY)fym3^ zTJ6LxpRF`tnir`jwmAYcqj|PSX&qh{5qGsEusw=?mJZN0#yd-HHeM$H=|KiMiKd`4 zfaWo8L~U}|sAsB22Og+9$gH&cdQ%l#VKt-Wz@ifgldE8#P*;I44ZY7)0jZam{P;Aj@A6jwAy?y zz*ugmKaO5oykEcj_ctEIU1G&zxZfW(9v;9r+{Ig?{Xl(T}&=3ft)ko|bfoc^0GUqeEfg_nW86OwQp6(L_ z(UC5U0&gP>P-Vm?QYtuDnK8?yt=-LkfX1k{vTeX1(z4&24qixmpa%9^_e_Vo(p;{% z6|h-~MG>@s<8i#uO}sw(k-?}D${o`4^POm`$j&0maPe-*8A=Ju9CAb)9Xi+1>t2#X zFz1x9x9i|`8l4q?jdvehr!EIC&Fe^uq1veVhy1&J_;6vm@_IknRN$e)PGUsy-?eZt z7Vc?(41VOyj407x(%ZZ}AnkFtJszW$sr*N@U{3jpc~)OjA~;3wKu@z5=4-{~Z(&Ql z_eb(CGa8F|OBkL%J$#>?Zld^PL}1>F_OWV-I-D705fHoLxfP(fXmy6TySQQ7;Sub4 zMFX9TG8Ow=Q*e-(Ji@@wa!+hc^f!04N7N%|0A2FdodM5tTZPMb7+mD0Z*7zZ?Aq+C zYVht}5)SCaWRWtojUq>Hs~K}JQ-&Pfc_TOVoHirpWwb>Y+LUQo07y4xdNp$z<`W9L%g*O!*7om#&Ns@8~D9fW`9MNKOhsZ|y(W3JGkABoWI z!eHJ)@Fn)K2ue0iWyZ0_NNoXGyf6^sA52#1h#0|SwZa>iXR1Nzrh?a z+9=Gj*E;h2#Fhx9h-)}{khX#DseA=xrN0^K%X3-_5fk@%X=N?#rn7aU>Z37l`Hc&a zX35UbmffhIhy4jA6`i#=z}37eQIN%PV4wVBn1AZ@%B;hSzL>}_r^V?gfk?nlFC6Is z4gwDZFGt%cE%SB#Jp}a(7Dm^`A;rRzhyv;e;|jw?TqinO1&V_8p=F(BF|%4&HxaeQ1ce@*wCe9PBu8AX?AU z`^~?;m946&v2APKCw+8p@Rm0=bFY56P;-C(Znwk2G8XCGHGU1EhkF)2-$%%?uQnA` zy0OX5-(j_N(beOXyjGjOXQ*fSHcctn^oA>T%&V!oE4S9AZ2Qr6P|-A?wBpmv3CoC9 z#hV|T8NJW>&ei>`TD!iFJdpT;^Y8_&1^i74<4T;rXw-VEZ+8k0U1VK&puV71PEvMM zoqU|gyt1WX->-x(?+05-pLr8g;B)kx*xL--^JB+)b~V@T z-vaJ7*Yo4;D&F05qwUD7&`_FZ%IyueC_A`v49rfLomstYe}?5m5xuhqIKCL98)hxD zOxYBDc+}M1eaqIIJGfA9Wt#Z?#1A*;KanZXatOJk`Bm|Gq2jzRCZ9A`8br;Ha9R~J zEnsiS{H1T4{1PT`oHiJzwmWItq&Q9qf}p^n-KuuJqs5OeSi~o^D5AW6jEy}1D6iKG z<&W8YGK+gsAu;X6p6tJS3SwQTql>)zPX9mIj5dBz5d;k z^tB7F$Jfse4#}C5WZ2fDq3bcT_VXgim$w>C1bqZgKTA629grj1Yj^0$u0uQ8VvYE3 z&(hp6!6NgUVoU3;z$&ScC+avZr!ESt(n$7{ToYJQaHB0qGEBoY#?>Tbt=Qd(#;9Zi8Tq!+`?}ihP*0F53nv_@ z&){tpI~>f@uw~O2nV&qQ>z0e9ayM$exU-8h@#Uf;anI&lkt8{2s-|65HU69$Iz>{r zVB@2Tlj9FsEZDulS7y0EwTfn`ZkRD3w>q`*A+V*x^Rwt1t5v6&8gX1c z)^pxEwQCz5x);r8aI9`sA1mk(KIxeCxHQqIz`&$aGeH_s!g43$B-_2l$B7&7TI+4t zy-;auXB*EQ*|X}(T?>1S{N`KebNHSOt~!~b_AWt{v}o3zx#t?Lh#~u)qG9>_6YPv`UVQoL$E5ueR~KzRe@1cI z6|YGbG|D6kuXj9MaDL~L^(LW@mn-lj&ZKl*EeL;3HRAi=_xRgav#fpdX#DQaUZ}=|W+hf=!k#FDP!+(X~kqPc0RsdKw9zHlE+a-EhD#VE?qYT`KQ0 zbB?5k6GnATOAlFhut?;A{>N#p6T+sxob_>v&$gA`Gk8C$6~>-?@5Sd6o**=pYai!g zPRG$4OXBGl+7`GyEn&(Q)G#dA?@+Oo^9hJE=~2@SomR`jGc7Bl=HDv zuA8d6=GK3;%6RPk)JwPHVneZax+NvSXUWn!;WPVqYzp~Y11J6Zw!nGv>(0Gbwmgb% zIkvH7sk6zZkzN<~O@8w@cS6g7;?-|^qHNtA2-@Ap)~6e$rdaHmmcgZR+;ZoP&ry?p z<&2EIp6rssp&l%5?{a;@wZ=lX%2=iEd)ItVCVTEDkINU|t)8f$e|3x?L3rX6%C=Lp zZ%wp`9Cv(Fd#Hf+)tf}-qujvM9UDbl`b{h^BOf1UPMd!M~>K*yTfqfq?6yo zxxU?;IeH^U@x*WMPLDahB<3Rb=(h_en?F5JwYgUNjZBZ~o~EO#D|SvWZ8rPZLOC#yq6gt(!6<_N|Dl- zobtN_C*_}WDcemW9-lC?IG1$(^_kbH!A3pXes0W^*L>`h*|Tf$k=5r&FPVYA)#_b8iw*aF8#77ox^(?h8<}zw zM;F;?RdL6*Ei%}Cty1Vr$%^^M1Ad&IxwL^cWz>!1wU${qX1DL3^QT-oK)&(z%Jn-_ zc}&#v18#Rc*ic&?rJI`Wap3-g@|_QL>PCG9RYW|Db{pxP`}pK$n9+n7C;so59KqTj z%+ka^YJX0?cUw>>K;qQOMHXUY&-t6TWfdm-S4p`qRjGX>ZDo5V=bjf~oA7txH4nAU zjNN%u$o1J}(!&)_dsdHCxbkvm=e$zeT`yWsej*Ch?kS(8^p-Tqx6{9+-OzyV$gZz` zt@m4rHv=y1wumv!vTF4qth}@O{LbL;ufYo{%qI%@2S&YqD<`;XTFM44+evb{N20{W zj}g3>K9e`*lOSck)T0Q!hq1(G4LTDuYwv=iBJSlsOy7A6k+S?!>zZy>-mvpul-_fH z>XP*vN_dwoX*B#ei!fsJ?kYv@CWUitPHnL_=e^(N75eUWqZOZ8$Wd zeq??j-_G(yE7u6ONR*l$Qhulu_cLPpa@{RIxiaPv?&yrRGuK^Sdnw}ez6*Ij4_#YZ zWX{nPUDJM|?CQn+!3&P;4NlJUliA{=*FukO8%$8pXuf3jOXPs^&DYZJ7wPid{t?N)DYHR%a=mzz z>d^ufwdt=o%=ra7?ff6O1vX4qda9Ck>+IPD3O93QaWGt#Z!V_DT&*9Slp47BoJz6H{Y5p0uXx!)U+OsqUj0! ziya;@N$&Kzhm#JID?&8{=Z-3j-(Gh;cIDMluIGxJDRy1wN6p*0)o)sJP4fqtc5VUZ z^W3QgQ`SH1eA%Yk=(DDB$^Dc=g(mY2IUg*FleolfvLa*aygk!|9vm?mD?LT7!q+e4 zV13%15@**am8Azy1X%vOPiS(=*?KtU+FFYh%D*i4d^N}-M}1o7vV`|X#XGa$$jw`G zpXNnhh|KH-Xg624YlxEuo8NU9RcdM1TF7CcGy5{De;;l5WqY8}?30ef?{_{%qTIUEN2% z$0@X5{+4t2N5^)h%4@Ag-+R3jEE=dvmYp36bvkc?Q+uf00&{CPj$aZ|w~QWpH|G^k z&Vzk{7Lz9I0H;;P>1K&tR@+f`gO>bsHYx0Ohc z?!WP%JYMXk(LSG3Kmd>i()0Lx6Ak`eT6C7o-Tc1B!TguaJ$(~tt zwU@_)kH0tS1FvA+n)lUB=I=Qzt4sKTmTY0nnm2C~GaWf{*{Iygk;laXe|AfX3 zX^G~WzpkxpnW5m1#upu<6Hf;k`GnDt`> zc4&8eGCO)V+CZb@#g2Qzlb%~XGhX9fo_OI>=rI%R)jNBJ?Ac^Y2J7vIV*Zil*_Kgy!C9ZdUOL{t!|b z|GhY*$XitKvc|z*W6VcrQT<(Bl+)($-&q*1yz8Q4_}FMs^Zj-Emetyx{+hkf!o#Y2 zzT*q;y6q8n?6iqdw^j1}Ur#7FN=YtKOH`TE5l|}WH0B-0O99LK`X7W!C(tT_M%Aqw z>utT^JW-$};*M~gK;0q1pbw%(!W_T$*;Qd6W0&xu=K)9r{@m>0YE?h}1K z+M`>ZlE-{C9z7gOd8u<^LGrpXzmm`Mk}hrP=pwF(Ue@74COdH6usp4qlrzbw#U}9R z-UlP+s=nT#`D{_*LAkPrjijaCNwc&49|UZ;9jc(^Gx9jmVa>|3=9%RNH_YZM+`aCo z^35)Fy4uyotlD?F8$OJ!yL$M#Qd&N3-TeI8Y`?4v-`8s_meDElic}oS?VI9eciTEJ z>P=iljBHQu>%?1~-={blC{(I(s(-U~ihkeRY~7;dX{EMy)~vKmHnLmvv&NU4hg$X8ox~>ISrQ{B#9X#ME3_o3X4-n! z6OG}8-HJTdoh!CgotWA^ci&Ej>6hkdY8^M3F1%{~$<=dqBzHy63O0)3;+qjCL-FRj zI-W91Tq8=N_^R5bFKQh(qat}1 z9l7$^rbE4{zB!`wY+&ZcUDV_Qa$kJRMJu`u_&bxpNueJOIsk!DhlEF%g9`BucGmFMi9wr|*cK-Dwk;wtPW2W9E zXJ!`Xzs|QV`nj=jQbcu%(7W?0$W@9Cz&TNx1kiYUMzT->%(~`);f_W7wA{$>to9H!^o!b*b(#lSxycBQF+~ead zl%kr@RmU}DqPVxyyqNE&4_kJNulg<)uVm;Q*>qapao5}Zk9NLH-Nz|Zkl#M8rzvb< zSGw6cQX%&)A418z8naQ;%_5J?c%>3Fx207z@c4^4J)Msu1y-+&*%-d|`omGJ%}$0_ z<$||QUXYcPxi>LHyQC+=c5d735c$L{SMFGC5Z<4(@knZ5p~Oi2ZK38Vn@@y1oFD7f zcrN|pGK5;5{NVnFQs&`bI(7>(c z;a`h<$6nk$XGe|vrSus&Tw_k}DO?_>&7%rVS9g5~o&R);$MO3KXI6wAPPyw4@q$CW zCu--d@Kd9vFXD17bpKZ2va0l)sbiei_eP3y{FU^H`JCzArgt3B`kXg8d&!Mlg| z^fUGR=at+!rcKvmG!BiFef1zcDB;WDPJYX1fvo1Xu05Nrq;^|1A8bm9JF@)7hmqS) zF9G+N*G@}4$=^D<;a9pux=Hneqo306tv0@|XKg}U_<_$_65LhUHfBot(T8VdN1U)ehT3};l#WM*97;16y-CA)E9Jo6Q-Rg2y5B>eEP+QHZhjx97KyR>J+;L2qnFk zJ9XvE#h2REpBA{qwG%hmnDa=7M6Vv(lKFCq`(E3VJ{d+8R#QgC>7*Ds^G^9pyISs7 zd0~=H*4qbi*J@W1_KrE3C8D#a!q<&2cE3{XN!`iWquy_xMRn(E37ffm?|9N}zVWB) z4Ud<2v}h4(M_4U5zx{qPg|Aqde@fN(DHBp9sCMTkHzY+)e5QHomUPWX5No%~ET3Ys z?D{3g$)g*JH%6Tk4VYt@=TVuxrR(VZ>%OWV(s-B7dy~DS?VHl(=E`{{?gh5ggdmjy z&SsIQy9yp}&vZQD-?Aq-Ks)HuJDwT6fel70zHHp|z$d}P_}b&G?vn~7f@bf_+f)2e zQux>mjafIMiX%0q$m)?amW0T1jgB^cy!LXVK=BmCSu;d$9vfjQBN1`gSG?B ztp*GDdhlNFYZtl|?D#%3WJf4h_NcEvqJ4t6tv8Be)T=d(pAuKZR|a*z7Sf{jKJnHv z9^bxUp#tyT*6a#H`Q2MXl5};XGz8nrO*R?ki4?rrmSaa3DnWHI#&x6Xf5X1nYA?7UF2R^@bbc}J<7ay z44g8iettQEclp!lOR7iK)Jfjjf2_vSgWAy$etc`pgzLMuJ36hI&^7YLg2*j;EhA#G zD`bNTeE4f}rp3&!cUkaKCc^QmdbLc+*p?=%WY;rqM)|pYx2qd{eYXIwkk*|P->tS( zsYb=^rpamL(lc*+&AW4~qh#Mj5ajI;Y2%ct z_`GS`s}^M-r8j9UpLA5wt>;nR#V%3#^F@u<8k(;vI}>s4nsbn|$YTfRE*F{lv%b?i zXg|9;JcOnRy(XU$$|pP^yOH?Y$F?eOr30^Zfyh^x7e7tZROSJ-cp|pPeWQVv(4SQrGrpSvG z3H&U({fVfTzVOa?#niM1g~E0RMqj5Y&nlYH<4$Qxuui%FWhGW*k`;2rKd#rcA>^;G~(Oz-OydveSURyz)lEz<>MN-7x$WHy! zZd-{{q)!H1j_hpIeG@j(&@93EzQessdJ8C4;c3K86T+VO*67%I?6p4mvJRYK%{qCK zmKaE*Y4%z;lqJ2cZ>GB3{_;bAmroC;dihd&No>aZ{IB4;vTpnCVpww#~A0+;?=T>ZL ze8ZuS;EAypiMCJmwsd~I^n*0FAbQu~H7Z`8OYaudJv^x7BCVMld_%>{<#o9^$?-u* zwXEJ{hpL5J@}<6#=26#bpYd87zooZSOXaPsWAO2O>&7qU?N4uJwVtXP(W~EkJC*c_ zkTogx=u7zwucR%$Juh!3Mhl-_ez;PeU*yQfc1O`|+v?@{PwO4od!q2=&r{~T;i#gmUpt(g&hu(youJeFgONhX!Hbu~d$<<5T7M&GCt4MH?Vy$BfUBM( zQp^n=j<~)y_=c!uS;WDh`t-=`(%d_nZ>`agsIWPcxK%lV5}jO}V<(fbCzt%OTdwZp zVHuy^1fRzDn=igtaqX*s{rn|LcM^ML2nSyCJLI}bD;!9(?pk90Gv2Y+o_Dop;+1J` zh4})!ves^{A{HZiK3+OmzF~aFW`%>>Epm?*2(K`CFS&jSrPx68>bq&0N#bKyjqLr} zlJ~WCnaRw2zxVk?Au=B%Hc?dS!_)02>}e?$N@>;at#4L(TePDnqWqQMchm3VK1XvI z2(^`8{G5Dyr&Wz9N3^g@%(2(h$H`VTAx@H2A8lHGWD8}6_~ov;;5EmECq>BVQO5Bi zl}|!5Z(rP&=u+Zz=i=<(i3?jv7MwL_wjP%aagWI_j#=HpbA>0y%~RHCez3rz?Y2{A zY_akAX8(*LN7J*Lnlo+l8l~Q7f0Kz>H?}glEZsJL zjFkJzcW&REW1U2UdBR$3$Rjjc_i;Ku+FV=nwB3m^W09w}od2XEzf+DoWRyCodB(e6mhv76w;6M^I^*>-VfR3#CvmSmXB1|)@=MNCNmn74 zY!Fxy@QRc3(K5=_+Ks&2J3fZLXnki^v9seygM`buo`FhO6IC|O26J8Y)x)yBeo|H3o)d=%%Gx!4IT<*xI&e=FBU(z%$ zz`$A!y8v7j{cVt9#jB^Fr)2G;c5Drqie{241akJS@ zeCvx;i*yvYA-HMQ15lGOY4=**3)sXQX))i-;2U)LMwTC)s$oCBE%!K15OY;Zs=C%0G8`O5M-f zPoEdwKKsDQE5J>(N%h;i`pxF_h%K={PkvjzULP&ei~XeDsbV?q$ft&CJ$Xke(Eq?G4qSZ zt{B&u0o7G6Ot$nKIP0Gi(tGdCrqEtcZn@Qib&-hvi zcTlxN4t&)L36?G!7kO0kU94!>dybt-e7c2Zw`T-&UB9u|TxzUA z;LArntIDoBRu?suXV!kH_B^-SxwOpf8TY5)jog0sR(z`TPmIrTZm3A=$j>v`OgZ#T zYqjluFWM%x)IL9L+3|eR$vnAFuTA}g&Nl72Y`^k+ z+lS!%dxp=tJKa}$-~VL!>ufkUvY4Vdse66pv7o$&Z;ut~moz3zd{yW;ym-m}vnRK% zm93lha~Hv*+~xDQTpT$-3Q~leV)AEy?TH5oE-VuXMSxFH1ZE6@G)Y#b}vmJw3{P$YMXY; zj@IKB1XR|K2Y=e}RrE(#w}@X@H^*i^=#7oC<9_54W8Z{Wm59nEb+4?wE9xm4+P!`4 zkDG1PSwTUP&&oRt#P^>byKTh^-bo@kfmKT$g`Byn5E_&uqWJUOCskh=MV*=iTlKUb zjobx|pH$yIdK4l#PifR+F8>Hm)9sr%ckud;X_>rJG?(Z0#fJC1Hx{3ZgLbf7r7nQ4PY&tP^_W>PVk=z{}d^My>^~ZkM+SeuG zL=@L+wkIDHIrY>gpemt2pzv^)LQ>mL0{0`QO6xPE@sG|lR^|sje-rej@{MTL9NI$G z&i5a$oMPc>EUU7}Q&Ck}p!=hsd!pp~?vsK(L;w2klIr_Ag+hVvpF;heOAb_Yp+5)x z9r_)97rLXW?|10lD(HLqJyp=(`}*X9`h=fD^+#_XdWPOM{X6Jc8X4Sy1@6THe}nsn z`vmRN@5X|EmnZeVD=$xj{|5gZya)Y+{(-i*=(IrnkmShly;@KoaNm7=lfe5>n?#77 zKAH$LP{{^*AM^wLgWiW}g?oT}>yCU)zq&p<>uSb@44+3$SCksgUT<#xsJ+Bq=@qTZ zWBlEo?%>X@-r(km)a0x4b#|M~npR8+e7`Sl(+Qqg9Rh0duWOpj?s852Bymh%17 zl5f3Rdc;T0s+mzzb@-Ck&!1(ZLT{OwomNoV=C9OwTF-3y&DGvr2jAzuRFOBM7A$@( zaqU{E7GdkL_`{v|L_W+)B~N?`uDojPns!vBWyHPgZ3cUe9FE`(rv_?raM?Y$G(%=b z{jbjA*zGMX;W4T+E8kVVYuYNaQ{GqKX5&TucNJ8fcR_OI@AJIY9=PM7rj-z8pk|$u zR%NnI?$qUQ4X;ttMz>e_j+u9R-c&QUR?V^s|fJE>*Vm(&ULL3hySz?_8=?G;>p2=5_Ar)pOrnzqwOy z!o!8z_pS`hocL{NY*p+yLd-giZKDpxC30ok9|)IvC((L$(t@9FzWjQ&IsA-3c%9jr zQ27^mZe6Yt!nRknXFE*$AicnC4E*R-z`{YJ!HUQm zXR6|}vR0M-m@j(RoNTT5{@UByfmaVHZEw=hjJ1+nJ;B1*%=OXag_ri|l~CRjMdBB_ z7N1{4DpE9=veO{wDk|I6elz|ytaOLp%j~|^=xo?}P+?#A_BoWmb-ngQyJM-Ex8`pQ?Yo6TqwiORN zQZi0%hAIDD?Kx9kDFoTHydFER*h}2PcA0+ki!9peqg2PZV+W7Do007(Xny{Kz;&-A zmk9Htqb869rAi)*P(Af7h4W~f^8#tUoi*0tcg+tRyGcA%aM13igzP+u?BYif(NEor zYS%ly^s6LSuRXXjA=4|U>w;*z;Rnb@zW=)u5%} z^K;4CZwY?w^PYW}qjfSmyz=1F&#S!swAUxuZx2d6o8@Y)S=|xY?rKYXXSh#M@!W@G z(mV;JtWBC{v}vAe>e}yDyz*QX?_C}Bj*E0R*2$wpQX)fOv2m={L0j(T!i|d4xV<80 zudG!1;#yXrdHK*wKj)khZPM2n4w{?YSJv(iXvl1~@RsP{c4$b{xfE+y_vV&X)V^=j z;4A4dx*|%wGe~v!3F7xB*q(YAs4yp6Y`de`z8Z<9^!nGSX?oKGwH))jH+N=zNrY+hX8=pFMnVL_<%EQ~KZ}00(&DguTA|=t_Vzrz1i=bI`6PvS(hyqt$CJ4rI8#t_7 zy#91)iqrRE9s+OHms1N1de`0)zvU-+tL3D&sT8Te$|(F7$GVv|{L5~&COjzFN|JP( zB_VzC$<(;f&%zE=6`qsKkmR^{+*M8LJK>%~LU=aWVac^AM<)_0?B7Xcl$7i|=BhS+ z=ibYLi3ggU^@2OS1vl-R@d{*GP{?2 zf7c0DHRqS|_Z-OKhc2Y+tS_dipZ}z+nW#CYY4p9h3Ab{W3$8B?TYv7+`}||vJs;c+ z##n9Qwr?q}b3Q-TYV;=k9g))cQN&w!FCOQnwJAs*ii_iYLR4XtS^N6?y9}JurO1-_f7O{rKvL#W;qmU3pyAE#9kIW$#tTcyHx1=dm1Gc63{e{ zdv4WH34ep|@5M$1ic#h7yQHEe)wMtR&n{K|{zT5mVP)t;a2u-c4W4@rQ%-IiCtffk zD>vy;PXbXZJt&O$So_VgM9oz{rzX%6j;W|6YPbI6m6~wr7tMasqR1WRTQYsmNJo&w z8uzv7E^G5yw(Rm8m$gay^F5rs78r(=J*pHkc154eDRE1)GMN{|9y`AGquJT^en0C^zca(0DlGR-qsnK;$yAN;r zem{HZ+=YZohkW7%_gn2Lj4%2Sv2JSi6a)M0cX3Y~?p`xK$T68?vVEX^+KRKD_tr)b zi$271rtt~e)wc$oEX*`A`7rm=NLs-)Q=U*eJLf1}cf&Otrt#z_r-QnuM!ovr|K{pU zo~5m0PncX3SDn&%ZT_Ck5{ge;Lyr^5Ntdc+b@cO0R(;$4jN{h@O4aDhxr;98i>c3u zQ~E4e>N1i~?~tzFl^c6!`Hk0!y05FuGdk*>L5`S$w#n3H&Zu*DQv+02&XgASIC)uP z&6^kRefiTpH|MAy+w(C@Pw&}1iR{cB2bXFDSAF9>%riD){AbmB74Ln2IAs{q-g2fL z8fl=GGzMHozvA4pRUFU97GzGCF;U}fCD)`2oRJx$FK^45GrQmW+jq=47sn z*ePLb_4J-hM6}2Lh(v9L(xWQeDQyuBJSHJv>Z6`|#+oBd@KB0iTY=tojzC%NJw5B% zqW0yy3*0qZ)HCZyMDnd7EAjPN)wSXieQ0B z8O!lScIK*=XOyIL@0-2AUElj&`E2`CFH+X$UfVrnZB3U;&DBEZx!o?=>TI04O4nId z_`}oQpY8|WWaqriGtU-o@*KVR#8&QEC9^{vOiLUWR5@>5bFCVf&8wORLi%U){b`)a z#m=3h!*jcOHeAbk`}1wb+jr|c8yzinyQQw(?7J>E&7~IH;1Z&2W~%IKb&2pj{_VcE z>D!t2+Htw! zx!$&0_dg3ixtR3xNpG(9&({q-$(?p(fssqfqH{%cx_W~G>S|Q{H!M{W{U(y|%{RYo z&*RS0Lh8vpm$AH#a<6`@3GN+J9RBNhwX(t9iARnGKMhQ8?g+cKSSHV>JO0|@-A4j0 z`gFx^cP-)il@LrVyB5hSTDWAyk>)Lx8(Pik(rY)LmhtQTR-gWG!;0XbtoB!rrpHWl zyZ=kFv1Q>V?@ynLAC}4V^en4W$3<%Y}dVSbz?SDe0mRxL_1_u&YP`!y~4 zgXhzUcT}T;-A)$7efJ3QP|$g(Ml88|aHfpe!Sl~E-&~amD@$1Kr9a}8qS=R#nUn3M z9=trOd1ys`dtULwMvD912S4^XS*}(lKDe8broMMh;L?Kmhck=racWPMD@x*B6xJ;G zSWC?EzPItxq^c{=cFpJ*A@uR9$JDa5BUrC)>6LRyL|H02vNYJrxoSvc$-vsx+Sl3L z1HQGJWpqgnlsa8RrjW!4G*Fh4K%kM?MVG+eNT~1!)ieHvV|{ROAQnz55l%hp9!JLZY$U$B)?xA@T` zuyD)v&R60IsnTtxp>fw&OP6~oNjM8OBgZ|RV zS_BY_gc&lF(SqWVvQUr*d?~m;GIl8WfBEO$7*yg7;y<3}p>{ZYQIPd{|fMpIoP@!g_x2UiXpbv0_ z7y3YNnM!Y&inI*kF~-(5J~Bp-7km5J`XX%jK=EaDAQHfmj~Ltvihq~}sH(T3qKt8% zC&c^T2!J&KAZ*=F0KzMZ0(q4E3>gKWQg}ZFAdCfMi=u#r3X~5S1pr(7DL@PuMFD|n z!(k}Y0Kq-o4hTq84ow3I1?WR2253j4p9T_a+-Mr)u^TT#AqNVY26@?0hCbhmjf9=e3UN8F#HWUJfQ)Si~uc) z2ss#EhnxsBNP;*w#v&-_VRp#0K|tdq1sUp4lAy{ChiibH*xxommBso7byx-r0var- zOhbme+`pYTuo*YeXelp;9lL4pH$!fiXt<=wVbuYqc*B9&1TV3Y{s||Q3NNtGN}+y=&?nYmi3IX+NP~n%QP3xoG?3A5$fbcsQ6d4>0D7@bB!U!~VNePh zMM0lTN+Au)T$qGLQ6hISGxTpieBA6XifU(=gZu z8byh6@BqSKq7>q=NCSlgU#k@Rra}lL4JS=wC{cD9G*;8aEJpA96C#C`zJ|v4I7WJiK)< z>fq14l!Tq7*cW zf1PU5OL7!OLkR!rrMMFKpQqU+0`os?OawHf^hMEj28bv{$Slo~! z4^O5B8bv{$SWl6Y8qUWNZPwdbp zCl4dqkV^xNqM%PEPoWJhHxP=o4!jAmemcYy*v= zpiiuAPzb{kC8nZLl!64h(5p5OBp!}w4^%0je?<@a#KISt0}jU;RvwL_U=D~4NK(jh zP^fgM9abKVqM%PE6P1I9nIR_wjiMAeP#=PE_C=u#Oa6vD8bv{$Owypr4aWjO9*v@a zbL`MYkslu0K%*$=lSvx#!?MW*)i3z%u<}fsTof8cOMp4iw z7QU#%vp}GsQ55uvT_AuZENXX2-NO$YA0UvxVHj^8_$UIr4J4yy4gW@h-$tK80I#ET zio7l>W9;nfYOkoJsih?=OD4(6Qov7&M&CaI_^Ap0fizvBEcm-R_)P2j4pel2zXQ)u z!3PjJmZgI4;CG>);5{mMk4jR8j|2Hyz#AemX6CCwaR@Ov0!O)P0eDZJRVRrkOhp`SERX{Tbh@#2^!XTyqgMeOH3d9hdkpY;OgZ>Ww z!OQ|7K=0!)NTkS0Lx{r6pn~yk_O5_GY0FbUvt>G z5MTv`@I%51ARQ6`qz?+vF)RW|$B-dH?DvTZ(ivLk;LnIYA)|-H(l|_f z==*2$w;A%i!UR00rp_B7+L)yH6Jbn>%Y#2_$)GjDI3#OxG2&cF_Al z?6d=!41|*~S$$p!n;+eD=q3mKPM0Q;P9kzf$B-^g3XOnuHF)(0&YK|^!r*vJTSq{k zsM$Y4F|i*qksw1qWWo(&AhVA?3S{ztUI^pie;Bl3jYR!CA&TzjiAu%g2|>bsBY{y8 z6(s{aLBlhSk&q!r1x5mC0Csq2gn~E$z(99w3I%xv<_aF+*icKBCrLw_8z`4X#N1CH zEH_31p|AdsK$vLof3S7atsN3ST@~Ob^b9007g291UYGdiYWQQlN{(sC2Zmt zX~PE3C~fE~JgOj7L(-s+1AUGNNsbJChRgyo74QWDZQwJ+3G@!c4d4KU_sH6abJsCZFcSm0*F*!1k0C@57^|rTm<5E-!@U*sdvG*0T zv3GQKR}yc|&KDPRwo?)}mD88i_f)rca@Gm*wl@k|ZfqOmVoS3VS5f9t3|JiC=II7{ z5esm0b@y2upd@Z>=V4>N7<`7lmJwIx5>xcHb69Msq1E>c_)SUN$=BC&v5d@y4I894 zkfc4l9c2hKPTN!&-dpGD{s|-P!0Mi3nv$I|7;Nk6N?W=6<>FElpGD2<2 zc-lEA%KZKl=oWqwNYEalp^pk9doScSgeX^MWuh!mZV`dFh$v@FkY7w7E(X8KQWwk0 zD#}1)f+vu-;os=5!#(vk4N>F^&3929sk-_yb_DUc*wxzIQAs>tk)6GRwV$i6xH34y zYwhZ49Ux|8tSV;bVe1ER3p2#3dwQdA_rH6D`nLs^;OpUyqJU0oALoFPW!7%sjI+9v zy|;HDtLEu%z|Er?M4m+{5b6as4Agh5lnQjxuT^&+Y1q1?Z9Uu|WdV(n=o$sTE(Yhg z?fh)*y_NNxZM^MQI5;@l+KUn8q~)c7>cu2gRSng3C}b%`=-uBB!)k`V(a0E&47ONv_F!u0pRW|i@_OM=l(ik|3!Q1KJQWr5$*DhZj2>~*?pr9Z_fkIRanz?U!o`?idR>$7j&e`1&A_R^dgAaow z1nHK**exn@&!SsvI7|!m1#cUo`_i?51Ln*&WEkKnzr+1bI-5tr5jY zkQiYiR;VpAlv!c39;$)}2k*rI?F`a_Tj<+yU_p0Z-aQZ(NJldegSQdU-QjDjPf)OS zG5n$ShMBuHUZK7e>W+o15RNg8F%^GfEi5<{22H)L1zAjvrLbVrWEK``w~m==lx@Mn zLQX26`x}(BkghS-fZFgT;Vp4Ne$l>oxrnWO5f> z3whu}th^IGxxj32xNd?nI7EU>{(^6CY$b@~Lt}V`Q^wiyjAw(e7%;7&%g-`fG+lzo zQERkK=vn{-0N}_Ovlw3Xu**oA_%Muas8`b zhD?41WJmwjX0NRk@1HCzwtmm!m9 z0U44Z$YNoKeKm=h>=0^$E!`*?!h;8#Fvl>mxnmrGAzWiy z!yS401;*qvKw$EqL_JG^Au~oMf#G@vN?`D4fh5HYUSMoT3pkYqJKmxboBo>&VL@PJ zi0eE3GK9r~=1A893}paxSs5KNt~1FH*IAfk2seW65#Q+8%aEn3;o>`<9U}I@IEQ4a ziN-MY&}B$wwW0u5-LV`kkg`@xqXpc?ppgza-aL32B1i{w!(?>0V2&d&glmk^F%^Si zEiibTN0SeXjtufFSQ;H-+*qSys`tdu6Uyk|p#iC!fbJ3B==uc4%CF$2*lG$-V60|0 zOlR~N*yy(~CZ7QUgDyy9DKOS63%I@sTe_J9#xw&Ol)zXz5iXeH2@HW7Mwi4zHP9H) zF>{avT7`i{zZkL@7ZXF4ncyePCHUC~CCE%xObkgu1V8)uK?X`OaO)+~402Ef{N6WB zN6br145>?o^$A>(brAc6+8w-k${2xEJ9{;Ikbwqn_66&>zGWQ9vuB7@z-PX2bnfP6 z?QRE43}ysPfUX5#nxQeKLu(ACs^(!A$Wr$Bc7x#r)CNaf56sGt18YFYn5Yp`L23p% z)IIu{g3D(!NEI*zLOtO2QZl%#R19bloRR_MDS#RQngMtOdIY$_z%^41Zie181FYRV zUF|`v2mH?&URT>A0{u&cf`d_vGkj2%5RnX0p&Mgk;O14_iSlomuz+5GL5wWpIxJei zEE)p`>lhou9gqiT3=}6|Yz*pXah6`3jZ)r#&o2Vp|O8sDE(LkrgX@0QZX@b z90;7>gyjP41yoamxq?1|%X`56L}W1#Q)Zx)sn`Ysr6{(MLLH0>0mbVW8yhSPuxbok zm&Di@ZUFV)$ndwbfpfv&jwo0fkho~z_IWX+D2SLCIAa8klrs`HSXe>k+l*oZ6<`<} z!v(y5%_%tl(04nN7`SnYfe@zpRqTubxw;AF3ORuWZR%jalBuNNe`*XPBBsKC42?01 zh)xy$=#PwOAUFxmLn2s70+TnSq84x_5IKipzl>lzcA8_>#F}q zV!w5UL@z+_7p%X*Jf4vh{E_$H8V^{`f;Adr|F}zme- zpo#>LXnzCo;5uf3I|wb%o-@Xmat?;|1o*&h6xNRBiM)l6dC~-WXe>wn}Lgeu3?>~w;0qnvnenvaM zb@@Tq0bDc}a6HiDlp$xMKMn-Vnjv^bppnH)85|VGGgP$be@Fl5G=ZHo0u&}?;260r zcTmb?*&=34rQyaRnEjhh+d%Td#760%5D2?rk%09jG;2jJf`o&>VD5@x6xU_`p>COI zWe1>O8yJ~evqMr01Gvkie=b`v&twbZ1XWBu$O8UWmpBmj8e>$ei<-~mP)|1loi-_#$Xdtco^CQfl7he6BG z`~(?n`)L8QeKOnG!}se>)@Ur}IC{jNNP++EzdcEP}xhL+#(p zWPm_e2)94c0MipLa{qf|K&lHeF$|I+a4-~YIzYeu+|hGKp|eELLX?p;_(`@y@nArL z-u^%;XT3Nkz)Uf-iff4j-~p1eiQoa9IK(+Mu*urS|0;%+P$e&Xanto{yDpjd_W9WkupV#2>- z_4gP~V3}Nr-2cSZtI(c`qjeHUFZsd~^r1t*90 zjd66VV;#|gGxpeubO7^V(+w692FkqbPtGE+X%Id&!Dx~Ad82L?7 zYyisxOoOALV^8qIY)Kyqtbv1W zAUHtJUL_z`A%btf=@Zz{0O}Huo4ue9Fld4gu#llYA&Hh)cg4@s#BMwE%@P8fGRU^UKO-8o!H%IGcK{!ldN9eu zG>D59e?&bxQy0@2E|Ls_daUg6vl;)4dMx}6I?0j1+F-!EfKdu?BHzC?7$gsxu^X5g zw%z*5ZJ)eQ|LqAe2_p-z9i7|E|H7Wptq1bM=UbId4+7b zL7NVekBFT|0f=T44}KEo|IHkVo;-=6^lw%`=v>O*(<&t1jOk|3(88uoY}ASARuqO= z>HyBkrVlEV(2b!RclGwK>>M@Gr)Po^kkli{WLYkA@bfPjOcoxVnM{_Rw~pZ#7exPW z3>V35#riCMHtPTefMl0qWr9xOXA}#3GxneWf=$a<^970DI5Cz#qaHbNfZ7cKD1>1H zchWip)=babB_LOe18asE2S|(|>~ByAsmg*?CbAWS>1MdS8M)kwiCf%h5!!b_20V*t zC%_1R4geQ0+~H#E|B>&q1}|Kc`S*4RstGWP>Tl9R38?(g{zVGd2attw)`7tz$2u_m z;iiwEHO{7SIWgos1g2SNRl*jQ20#%uJ;_67Eim2SZmRuzbrI;ftpp_T3%~|)gCN$C z_b|}Xl})S~W{U|U@v|QXQZ{N5K&K9h;WBbg z!yO+O%#>w&OgXs({8Ym~A{xqU!;p>Z1_P6gwRij!#Xlk%Y^O2OjGvLq!l&q38_1@Z zf~tM{^vLCxz?EPnLu(;eH}D5IxB-3B7x`f%mG(D-Gd*_&stZSF0gk+4`oJBTnPNlq zmi|~6eTzp75x8(NfZl1Sp_)z}U=%JAgy{x%^h28~n>I0S3od{8i(!CGA7Bd|E;ZAi zW5~b*C}oG&<-k0EBnV7{xFh5r$(|`@3$x00xcpZ>_d94o>zn+5duX6(94H?^Pd7#6 z2Y&bdVf!UVF^o5I_gPAtc`~A<}$Zo%fh~#}T zN#!qqhzKX0GR**3XDA>K54olMUiK4ZSpgCezD1fX8Td{?gtH8p8OE$ciTH_{45r2s z;XuU%Mtj1~$^0{-F(W?08(YErD^Eu2r_YrUaY2qt&z!_Cj|*dHX~M-xnbm_+d4h;v z&-DMsQ{fCzCj5f4#n|d+02=_g`2CAsI<$bfDt6Rkx?P?j0>~h0)(v7d+K6Dl`{UIV zkqyxlF>Gcu@+4c?41w(;se6#g_QlQ-PMMX4yY6AiRH3WpKcjx%2|lb(tda{ALq!0siZ3*)Dd8f$>D7KoC?`iEWjOWj?a-R-$# zOr7n(C3!??=p%)v%kTaO!8Kw!HuK8b-Ll;RO+yV7~y3~+? zauM-!JO5f1RB3RJAsb=9K8u|J;Aa;bL-qi417kJgl!%>fkgC{-MfTYga1=z6Or7H{ z4~(sCT!E6&XI%RiJovq1GIxPx%4oXV^%v!pl?6A|Bep}6mZyQg_8Sicx~N=D%_G19 z#GSy70d&2i9EAcdpac_4X)0_;P#p-wTCx0qhjH)@4WQ`ESGr|^vv`=C2qOF#F0!#c z3;btt`aL=$$2f>EKLFLh-Xz9_a2H&C<0j%l|Jig92ct~z3xlF%Lf_a!uiyt`&@5zv9dY5NhfXFq@WQgnC=nJvdwLLf0ZS^T z=?T6(=iVD|$U zDg7uW5oVrgMgo`9V^0odN`T~p!p<`wU46G9z-9C3hT1_1UN_n6-VVWSFg%WMVRH`<}nAP4Mz03hj~MALH?8Am((>{teo{2OdVPonYDeg9e( z%<%DB7=7M=5eDvxq7Och3Z{(qKd4a-u_@qz^rTTt=ePk97L(`!EdveZs0yh64LIRXfVL)(8kH37jcM$5rZAMSG_B=dN1{o_CiK^j_G^$mzf z<|S71_?fwVng{5w>g@<$pAKjO1+C!-3?q49lL)(D!A~P*koP}Ba1h8N(hHOh{=gqb zMY!7*f2|@$q+`@kUuX+M5AH5iAJQRN>x?=X6w)EK4v-GcEN7a$;-?R@GmzhLrGEbb z-~(6F!07?&0l*NebtP}l}Q-5L3;+hu)tn?KWFC#*aGB4IG>ok1P}*6 z@~{6yBTOOVeg~ju3u7Dr7a9iGeL>QV8Oempq+|NP^?3%l{|8GP$RAiPhv7`=p_4wI zKlIrqMl|@NAQDCT<5366T?eBMJwcqYbNsYuv|dn|%KbylANm7!{sK2WkVcTSZbn06 z*FPgdvG)ghzA;87xbcz!`~gW=W+W5%19mqOKcSmJ?!R{<|5EP6zWXRK6J`8l^}g}p z&!vy!4=j_riEvl!t>G{I0Wo9X5A+msjP3`GQ>a|)ztjteJzjg=<9-KYm>xC&r2yU7!5q%n=7yU#} z%-Pumqej?|56I>vRw4M6g9jKNK*@fl7%K1wY<2_u7+aPZkss# za7MtyFRD9$KOkAX)$+IF18i%+4T$t}0F0gEC;Kz#<)7gc*pz-qIMB#S#Zq5Oe_l#d>qJ>ugOwwwUcG+X09G(tko77VU`MucMP4+!^I ze<0!4Z^rir5~`$gzxgs}_acw6Wtl6{41t=M!q~(a<%1n9|B#}yx1~>7+Nsw|h zC?gUcTA&?VdQ~DMU=0m{^pY4zhywbCeqftOKY0pL9FS=vk$yO?Pt_Q~knoE-qjrKx z$XO3YM+F{$IY&XlKm0LpgMeLzQI$04lOAj+#NISR!awOT2nw+*;~#JsM1z>^bP{|z zfvvR;+#uL59Kmx`68V7LV@FdO6#!CSk~SDyW$Bp&B;?Qn7CG>f?9nnoP6)urF(6U+ zQw-*-LV#w0LV+H!*$AWo4rctVyeQH%0)>sd`mBo4hG5mObs1#*Bf%xtq0oUNFvz|n z3H%WV$@W0316Vu4Hu%8UIez+MAEIE=K1fJrIwLV4?E|xifIP;QF$P2Uo%S(E?%3{S zB6*@rY~t@>Av#BDY%{XOPR4jXg5G29lNA#^f3XB3p^7l~OACk}6QxHU8=UxmZ)x^7 z$c}M*KO})$QkbJsqyZAq|5p5rF2^W-R!cAvtZBBQ`CFGmj+a0#$8rgVUptzO%h8k1 z(Ebl}j@hOl!H=yTa%0rzI=%*8ipShwe34U^L8u)jhN6&F*#CH6`=YOpMtkhn`$kJU!thz zkz5|aCGTU6;>=BR*vIMhBqs+b^&gk!7+7ZgI_DMaR(}>}Zh<@C2kl(iDhcR9)9&Id zzX?28+L#5+_@CFHiNKMA=?yRx-Lco>(H?VNKr!KI&tbC!x1yoEO4HpTPA zy0kmPTet|a%1{ce{v;q=-U5FrRL$O6uX0l^E^pyN$o=r+$}WeMW@xYIfSBPeTm*07 zO^YN&nM+)_;GW(jKt}=L1TF%?0P!yQ#qmnxB4~!VutWV>K#;DwB%L>X zcdihXxNvca3*5&D1HvH&51p~B+EaJw;1K?XYj)}>GQ#r{;CenrzMsEQ*x(HL!pA~u ztCCO$_@iFtQJMt!m&$;?@ZyetsV`F$V_LoE0QvX~a8?6!%Y`~xFU(f)5=^@Y(IUAo z)69koBk&Ii7@MhPI1LwOf6RSfuN?*f^H|!Eol9xB5Xd@oR=HCbsQzXyf(N7hgN~Gi zzFl%~xJ*yt+WR*0#`IJ%<9c{glM-k`LY!13GXM8A|zvvyiZSEFbIWeSUS>`Ux#r<@|xm}C=34lSwF`Yn* z3GEZqsKd8SGcPU-S=YmEj5`VoU`6`8zgb(5-=|GsaT$!amS1$l%ixoL7&zLF@w1kRFh$B&PGK=c^YxnSP2a>iV7!%AS&DkxGvNDi;H+!VL1E2 zYUD%T=>y@UOJ>?`gDfiC`Y)#pm440U`mb_>ieBx~JfO7qxaYTQqBdu9*WO{vvIyKW zo{9T}!Fb0CXeRmZx?`=B_zPEUYuFdC!NsK?UFflb(Copw%TFEXRDerRZLO8FT)u9n zi|#bf3r$HJmZ;!frjXl!^!@ecAAkDww_h+x?A%^4y;S1G`trRQdHq$WN|8zz@j5{p zZ`%^b)3ME0{y-=S2lFDh8AB3(nZ}R@7gR{mkQr!2S>tS&!j)f?v%|> z|58?DRxtgGx0_jV(#q~MqiY78XS^0WXSC2CxMo6gcz#!NLRpt-Hvlr(@Y&7;>!lAeI@xuf>FkO%r3ui3>881zTZnEW2p#CFePm zSl+JvB$i#W0~dzTGdtrR?WQaT zsw+buSAe+~^<@B(relePc@=tEk-N=?S;bH5YCMOtkHX9;G@$+U84FyXZ8lChT3y-T zb>7#aH=6w@*xWiHe>tKA+jbjd!RBtTbAHg?V5`ujg{IvFTMljy);WQ5Zzh7}Z9Pms z3)d8BbAg#|18%gr3sheQNNJ8cae?npirKiUAZi_VIi`IOcLL_##Dw(j-M1GRhN2S z7K#wJ6o18G{^COzy9{q&%gcg^G`YQM zXv7S-=7t>uV)@Yn=2&#)s;-3g$bZR64qxCJce>1qpRvGcKQPurE5Xu=@@nCG?;RAt z6wEz!p9(!0EgXOR`1>F2b>GXaJTqmN3W95dmX#HmxVV0qmEk{s{MVoR2WL8GNMTQ< z6cY&d;pcTBeEGy1lkMiG%P06hMHx#4t7zw(0%2273*>bZ3Pl2*Orkk+2x}rv1?}3+KOPS18%j0doWHP_&W9LVAkrV?W7InoVl?#m@8_% z=+%CF3cgA;g21dcyQ~yXT@haibQo$k3TDynt~STmcF&&UVP%mgZhddUZgry4q~V$M z*)Uy4%hJFX2)s4!R~O4^^VoRtm|v&r8{Gey$XYmX*BtjZ~WFFf@YQJ z67tW&*+@h4b>e!!t%fco3XV3lZgX6Ju{DFF=LE^uxfj{CsH=;)bln}q9D(V(bq+mJ zv>+|>@|q*>l^u@DeS9HzH|g~Qp-b#Gf(^np;6-9|_Pjx}bDkdtcmu5k4{ny1|d zT)Nz=fLoJumtXB+$CVJYp>}1!G35RjLsQ7~M;cf9Mkr0Owd5t#R1$ey9&lm@!AN3j z>m2ev2snn9YlR`;Rycx*Hu_~aZDoYxvR=JS+XVhh%G+)M8*Rb@tS)N-`c$Zzz0t-# z4M%hzHMr(DQ5kKFVef2h_i#CeUm98h_D4=W*f>-`i#Bu)^)I2uXmmZTZJ{RW7#Ht< z%^~iiQ1g?WtuCgK=~u?^&P{xz{*vsB`qQ)j-Rie-Ce=^6vT;V1{<1i$l!~HP`|%tO zFXK#N-)mSPr`p188vA~g*!MbM1WPJYvKvRG%nQ5mkUaV@kpVwe`yD`r}&fBJ7x7#F27!opBOEmRfCo1P9*9x$| zy1?e^098$h-yJ?>^M_cc*+Mpib3)PHb(*7eEf~F9zpgp_S>_Lha(3QpkXcvy*_`Q* zE66Ia7B|hBK$@w^_~Ljyvu*i9tQD*-YwH~EJP1ApuxmMyKWwE$Kbx`uL(uhr(ZmNC z+`X0lN*@SCDNFTs_1jRB>UX_8=bG#eHF9W#8tb*Q{qzP@qMmC~grRLsmZP3!mSyn$ zF@|<)vYfV149$@HIVtjWS{mA|$#Nv}AkG-*tmTCIpC7;z!W_6{3HOKUJdj%Yoj~ z<`U>#<9TDAtru$>Q6_HCyYj{O+N~ogro*qI#Em>@svCu$yCX#(9=I`pyUdr?ABWzh zj6i1qbJHIzb`@`IL`jSG5_sE+_bztq(^%}>+x-_hHVN(Gahcu;48>5H zyF%}D`uuhlx4xgQi@5du+l_3A%f=RlIIr-^veGdaf{sp0m;G9=Q@YkG;;-D-gF3kH zv;I-50j_V>vY>8I*a>WXFpb8)GR9rIG7^;C%I7aEtY~~}A^+{y-~a0m3?<1zn@zzG zOyIe%6?zW@D4?u5Q0SJz&^P3sqY5I?bl?8~B&-=QO@9*u+D%N(511IxrYV|8IAwDgUJAPXuI}Kkpt$2wp+$6?0~B;HE<_J!|A%%yFpWK?gngg97pn%@gE;6_u*Q> z*Elt)X!85VdXe6>V#@8Uch@P3!I~_tj7GO+vX_`$r+5X{PQlnVNVo6{BDrlSyC!0)2BMKw2Fg zZ$KPCDFJRdH=%5G|GtL8xt#@m-Uurnt`lFNClk6iq5=Fp1t8$1u~oVw`N!DG1pbX6 z=6_E;nIh|!-ybY`H#cGg@Col**`{YbTc9Ej9cqEQHaEdD7=B;3&eUUL@(yF3J(9C_ z9O^8D;)r$U#-Mh)kTp;uPJwVH!;mQ)6yTN;G=|Zkb&i{PsPLo85U=j_P!V#A+8g3l zdnkJT`8Cy*=JML(Kl98VLu}wLBn}*Lv1KQOv$0o}h$mE7YJfcFDZxyu8^Xg5L z*61ZGZubV z?s?pI)}gm-q8j<;i2L&0pZ@vRpZ@sU$Mf^?Jh%LD`oxPOuj>4Ed59D4Q+wB(lL%^z zo6BRl<(O;bZ?E0pTXXBfyt}09HbBJ|S9=wULjkft$G+xO9aqGxcC{Qe92B#RYqtSj z9<#y*7)LXxl28_)wN?oUzd5u7^|MUl#=4)f(*;_1)Yiz;Au2M5BEr4@U zXtnnt{!+W!CnXEVQO2vPcxq(m25MEUERLlo>l+I+V?i>yuW> zgb`)^K&)d<>qNMj+}~=O_cd%w>y^=A)9>?CL}L#LSbRQH96TTYcw zN?DhW$?zE{#qbSoIb~+IQW7`iHz_(#ndxF~cGJon;%WCAp)_UxL#ndp6Q|&R`OAO& z&kxu{oNg-yt0GbHAFry6iSx8&M=IG-09G$GLe|;nb!>Q9K-PY zeIy2OBlkImd(P$Xm+Cw5R7_9;)zV*;%fAel0u|_omBc)!&cpMUL~6J@MMzX9YR%t1 zpASF;9ROTWR~PPSAyZZ*V!atnNAjo&tv*!cRUtf|ewMk4DcJ4;+CP*+n|v+MbgPgCPCp^lixgP<#j8V=7DO%a2#X=@~-E(GxjB){5pVVj+<)*`T zr;%fWa(D}j`tb98cx#+W-E;Ho`|wVi{6SI`T!SVb&Jc9jaHn5|Q&sAItzd1utzqr; z>+dsk~AS;N!6z3awj}`XgnEI%#JxCsi7Lwdd{gwC7lV@(NG_wbkvIDb-i>=S-DH4YnF`V zp-sS@8Eab2c`OviuavH$SNriCysixGZnc?AV4QjVWjG-fQO8w=6_I5`N%KvywO~XI zJ#G|NZJAUa4x{Hf40OO>!a)X1B$mR8P&_QjlX>+H9O&M4ELTJ+{N*QQFq>$tBf0e5MNDuGv? zb!HxT<32@Gf}4HZ1vjb94lZnH4g9mvPG46hFe8fRgN+qe791=RR*w zw(H0y9;qX**LJ5*g*qyUNAQ;>)l=5^%2DEJcO8Lg?#f+oS0*Kh76fbuwAaqCR_=ne zGW>q-wXe(i?=EZQp0id4D>2h9baa6%?{=Bw^w5aqcsGocrc=g1s4K zoXf|#6P7eohD=uih`ZA+*zN1E7+`VPBH}@V;rFM*qC#nnJZYGY!ou0#;v|I#IVh#g zm(sdUss9Bd98P}}7PPCk`?eV>%ze8Nval$7D5IM6_JO_S6MHklqB1k|$8%`9vdbI& z?%Xc82}KK$lYz==A;>>NznBgS7g&DJSt^6D5NwquC&I#(Pt$CbyI`xd0XLRUE?b2^ z5T?f6>Q4f~WvkrD(y=_;Gu*sR@KYZMh{~JLzDVQ7AOHIwcU(S~Z_gJn@AB>Zx`Sr9 zUqmB=w|wggSm>I(;-XwG>1_SOEsyfoIlitXfxf2Jeh#CT=|})GcSZ#oT1JR;RV?wq z%p+YCMo~FRN391@p_N*mX`aiSc>;4nB{>>9@1KABX$^w!c*r;&xMbO`j6OLuWw`v7 zJAEpahm(V#tW7#cX*RG|t;xadbmSbje?XVhT$FgeTtDkk==uH-q;A$*w{y;EG+!6^ z=wKTkU-MFERvGxr4PhUzyrn4c5+BF;a3{k|+%pwq?(;hCFQ1q5tsz!HbZZx6-CZif zJ%8{*=|t3K`*gxrHsYFV;4}Db8yx4(->ZdQCEw(OjzHTWH<~_n1*rW{3L)%s|e zvcfmsW7);>#q1XJH|?e3_x!Dtl9RXF=7!6Bfzm5zVr3f_+)-bkOEpn_?6}vl)tz1q@Q%(oVog1HE3-P3E9( zmq^sADuC?q)Q`UR-KZdiMk!NJtMDCXh}zHhiWN1YtRIM7l2_{O*3coMt>GS=4Mu+tw5LB7iYXy1Lasp>D;TgUkntSI0c^2R-sYc@TwV zwcaluEue8f{Ta7k;>s9Y7W08CKkOUY{J#plQyAnS*e-4SZ3f|a+BCr*2u0a#jW|$> z>TE-bxFfMVdjqBH_=;Z5#~(z;2tTgua##YxCKS7H^j@$9SAp`#Ur=GwJiZD1=O|)l z1BLa_yGk0kqd;*28mL5i>>4|6Y$mXo!H&bIAgw}`K|}*~q=&o`eh@#YpMAQX?zTo2 zD5Sd1SMzheeSyNh2OT_^CuKjLlZm8VBy(t=UCcChn)|X zcJ#B8SzyJi~6~$lrb` zNouO$87`!8&guv0eD%w#V~nR+jK zB_ZPSOCH3n2(>c^5$5mcQ2zc0YzeWO$1WL_wVC|vcKsn~OCt@8vQ=>pBg({f{juE+ zNrEEZuoJTx+H*kpIvZ41G)q!Y&VTz(we{IE+*V)$J@Pz zR3x&Nhd3jlZUzCu3>YmZFv-}%pU?y_A4=c;kVcSXp?yYpFeAqL!whBUvE2qqaJZBq z{#0m#-N8|ojYs9P7`sd-E#?GiYe!z0C#@4U*(dDAPv{Svupu%M&Ezggo*FsHbeZL1 z^dC9VXA~mtHvNQqHBQ(TpHRr3Py-tUOjo`9?9UM<;aPU27JVRgP2P=cw?!5z?j}7a z!|e@~%6faSUTxmUDM&JfF^A>|yPVeJv6U)@E$Yb<~Qj%&eMF}VmH$0-cx=c5cHCsF3Ucfy|vwZAvY*qKr4 zxg?JVA>G4|%P3<$gtkj}Rn}vgH1c4uy&i9)on5#-e;}M%$)iZYz{pptOkmU!eOmfhMUBQK-0Ysi)(26> zyaKH)z;a9_F-^L72-3xH-h(J}pLgI7gpN?6Y`gZ8D03+oj{ys!+m}shr9>HXNwAi= z6bw9Nn;mk?C=)M1Gzq#(+i;_BHlB6l5z#`oPG}wf})5;x?;Dis>k; zH@WMm&)=08MVXu9EM$Rz?%|Y`_K(YPF5BWUK=gBt+phgA%KRLc?;mzXS$$Q9r`@e( zIkI0ynE;eGe_Rh3O*+lN;L^~{w>TusDn?wpbxx}(>6G{;+-z&p+8t)BD;xItRn>zq zWAwY$*4mM`>YvVX%&gD@MiU>bX3VJQrkjH}OPswrsc))3i8Gfe@E9Pmv<2Wn2>f(M~z+-v###vcIi(bvgpVKUd zCovEv6Mvrna1@M#QMLB)=&=$gCZ2-dFm#Q)#CtL3SOdM2r0F7Gd!F?2h*1?{0tdl3 z2R6)p{&6^Sk(i$1nUdl3a|=9OS_6L|6on&WhY@UKNF#Wa9)Bv9XJ2F#hyM5|dbN=; zO>1~E5d4VB0H?q>6V;JHqsK}pk+Hcfu&aeIic4#F?x8h21+AgEFb*r~rH*FiX+b0- zgAw>gSb(S3r33oF*BK#+4Az)+P9jmn^6ZQZc4$Nf>(zdo5dGoDm0b?on>yEhK4%^P zc?v9j^SG#-m|innPQ!B#r{T#r@oN`GWZXXN{gV94IrTfGaWwbAfM`PMojWMUs9N&RM1;L0@PCZjA}bZZ_NgbcN?MUEwL{3Onu6ZvHYZ zl6SIs=OJ+NLn-nQ5v?lsjgsdc&cc)U2VD~vKy6^h<%L#&7#b~1*SvzbFjPgp59F!# zfikl{-);~aDAFKq+50YWVNxq)6{!!RJYC|#JqO5mca&C8!ytK0y=iKh0Q0)&q+ zPHFvvC;0IlpY^W)Qg6^JZT_`8UMyKUY512aMyb-*;lD zAMjeP-o#?pFc+`^4Qb>`<3yI>OdO%A^2txkeD|k;d;HUX{`jvy_fLb^7DmpiDT|t* z)C@nbljr(I{Ob|v^XK(ko$*P;i!OJN+k}gnIDvmMokQyc$6X&bF%lSiG~)kr{`TkJ zfBEl!|M|B+Fe2`%pewuW;P!Yqr#Fm+mG`=8*O(?^1d^hcZb2u{9D`q^ol}IxIXrW! z!B`V3SJ%8P_{Ds){Y)swPBs8DWcJO!I>QkP|!t=ze%OPhL5b1I&KQ_& z(FWYAe(H90)6J2eyuaA4ejR68{iVP$YeQN#?2WU^swjHxl#za1CVKHAM6(MFZ6L=_ zf|Z9^Bgpdp*d2&rnzHd^l)GNr_Pp*3r}fO-B>INuV?f<4f57l|_;%P&bq!OCET#`CGl=pOw(5JEsv0f=@6Dnw%ATf|FA{^JxaeGZ>bhhcsiR zz2!~P^C>VI6F6JI>5}y7^ig+CA7R6#AnOi4S$uwuuRx7U|JOH_o+10$yO%RA!*)0*%#{=zO+g3mxo2f z?YimrmC=}eaD>rt7G<11=XO-f2wum5(e7gEp_$ZKMnOZQ43n4;?WRn{SLU;=plS?f zKMI<9f@#e#m2|h8Aqx=JSQjAlu`sLbZ0SM&Yq^6=fxk+qSe2x?Q|wGI0_7haAh5Y< zpW`;`bkpGXe8SPqZm|3=RG5xvN}XEZt!bF)600IzW&-3&SI-vW&XN|1l$E-t&(pc? z(5vn`-^vLBqYxLQffkQ2x2f=GQ>e;9IodiQ;^; znAO)hN~lpbuP;Xw7v_I!l_Gfu{~K*D*R)mf;P|Vo3C1iWa_G4G6PR8cbporb4kOZl zaJRR5tgJdH(N{^1mM@-aa1+q)nh?9%2q))ecJ#YY;c6vgywfo&nG1b~)Kj$)Q!M5L z$<{C3C#Z02tDtnNIEre4$8RBs*D<@DIQ?$Y3~#L7qo^jX0bqthBN-9Vjau2m=ZK^HmE=^vxgp5h4-$J z69Uw_DQE#r8}R+ESjmYFVS=MYMJEA_-c=MNMQFJgec41F7?6OD9{Lj3yML=Fc7Ut! zA0@v7tY(}$M+mDV%ugBUY0pU5Low}}ybv%gqHEX-7R69US@t~%XfVF*w@wJu99mDg z8>8X1X5bLwaan{8a6MM{Gkec&dG$XAWRe{{-H>U^9+=lF794slwU?D^D#LF#wa)Oy z)P9DikAbT&fLq}n3njePn>gp~6S zBT~*A80`{#xIb=?wqHq$V6_?sLw4 z0er(*DOV7n=GUNGeZjq;AH$|8t>zNqf^auwEJ>D3Bwhz21$s}Ho>5E+K>ABo0NXxr z1AvXo97?Y&91MH?=+^NZfw@t9ym9@pKF`|mitsXHTFcH%!t#kQX1c^|oP)zM!k5yV zuHu2;yr4DpW!cT~Q1T!oDAfl3Dw{C;MTu@L1sLnz)gn3J<$$=+2~)=n`(h_k&e(G5 zqggH1EktdrKr4Q)HU~p%V|p2LqcOWZM9ylc)}bdVj>u}ddSwuLXPq2{KMJYJO2&@s zWi{|Qy6lq^3X};1#Q!zsMiYFiFnLq?{w=zwUft^YFpep^4d zS?0%o`M*EIET|s46UCWise0ponmX^eeB$-w@9L^sub;s_g^Gw~jNgR0PoAQ=%MikNA&Nio@#K#M-1opNLi(b}hxRMiabZp~$J_Rz3!OV3=s{Tv? zN9b0LsoW>ttr#mN%YEn1^HQ&PfO>-5mu3N#lmshbELfhDfS_P{7hi7+cm%*M07OX6 zj#XZ|OxA^Chw^l~GRiCAWMx`oE6nIMU5R%?X7yIXy|QXxzIFOtx&lxllOQM_fKO}b z>m?B`ki352#23f!dYb(6aGKIbaFFVZye)oCJUM61X#=f!g10P3l4N1@?elf35oB_U zIHcpu4ucW`jVniMu*c)W@^>IW>T}u{w5>fPnc=%s9NKygXXDw#4={!fOJj^+ZA%NS z{b5@gQv4&8ZWmA{^wo^2wEfY>TM1QO3bmyr_>^bFNN_yLF zIpq++t=+n{XoL zMkr480fxg)=I%UWzHVBE1^g_&i=1#0Oaf&Id&tT^45zLvAu!voF*jSHgsb(twVn&b zIlvM&8l@$Cl=W%ATq%d0+qV_$ew46$ob@T^OhokykvD7)9j|nE^-Iq3&^|yJbutD< z$-CZ~X?-9q-m4Gr`v#Me%ts?Dt*fhZPR3{%CF+P{3ELVuwvYfQAfZaTTA5L7g7mSnI?D%uat=pJb&H|D4LiR8CmB0Y4lpTuk@*HriQj9O zI1DF2I8dR|l}Pl%Nh{DoaEKMqnf6aHr-1)^ERltZkNoGag(^BCR9b7=hyI@k!XHYY zLWKzxhZqTXc);|{s**EQYA$1f$D|bVs|r&Yx@A8{8jp%Hdaa`GQ~^_Lr@qGjq!o@V z$||PHinD$r6z2d-q@m)UEalIu91tp%FN6f*uC?E&Cmn00N6)>|jF?De4^jR*9BZ}Q1X=wiyH%Qx?L7){8g%MoK_W85*>t{lBsa?0r{{eG!`RG`)rzx&dw+oBD&^38-n$Q=*{6KX8XC2O0tOH^O ztWwksAUB}aM!`9CMWt?Ld2rG^Rd&gYQ})QeE^X(moSH0{aWW}EmbMjk?c|eIPB|?? ztA=W%Nx=qZFnAMc;sKo3k`TqMj+QKARz{4GDSt%gh>Y1zFW4G(0X^q$J_H%_ zEV%{EwBRAxvvmGp=;_*Byeu4Pe&8-Vt-fZX&15J}^#Oh!!(^dI672)Fgb@(4qO{%u z>rxiVGzi?Yc!%NTuk>D!b~XNC_!()5vv^tIHD)ybp(Wgvn!gc>bATmmG%8E@1#bQ- z%mLBZdKpEBT42%qtYp?iq?n;%|BYG zn1W%n24bCse4KMYev|{F;qAqmXM)@xJ_}nXpL6bw_NR9mXQNcxTH~xo3MQXraLy@5 z2l!Z;V%&3+bIv8&zXWq&u~ytB#yRI+s1zfK7A^dUyK5q6>KO=o3UH2ww}?{yVd&Dz z*Cdali5z8&k*|@eNSE~(#?CtjQ4%|j zYAYPK?>?O?kl7}PsqOa;OJt!^0W4OgwPX%ebVR7MRo5r%oO2q9G7?iWCiD4VRk?k1 z59ytAmP`#5$zW;j7X|EmsQ7G?Gm!>D0}VsP>$o`t`tv*|;>_UuL#%*MDeht1a*F?O z-eHL}RQzoueJ!-a0in{EM@7Z9Eag+~SON!rH}uaEB~w?`-9Mk_(+g{NIbS|;TGl(UEEtfaSvLS#nk?qsW--Z zL2t8w)N_vEk2YJ;@LkI&r&1*-yIGS)kM%%Tm>N_`E8fTYNaKko3tfW*(s5VM^N&@W zCE?>OL3@-P2mvN3(BQHAm!hA^F~-1K({sCDkCgxoS((=S70RSMPcj^uZs2*{HlKbN zj;U5e=EO`+HeV6p1b!~gX+?Ea+@bb97YnufxtP8ds;K1Rvm}8vjYqD)+9^oA;>c@- z@6(RXIfFzQUeZOH7!ITMJWm%nGne5Y!~1j*{zk0i8gG=&CncFLhkUxoIcG?y*pM`d zruN+W(eoP6wu$=z7Jt8P`k-jf53>@ST(nvuwikDZb_GRQGEQe7@Xlk$66`2Y`qgai zdR<(mf4;tI7963xYL@c@>;batiqAZA2BVPocda>5=nI!@!~#MTI*0c|2#__quY4x>eBc*-EmUeP2PFfl zMMj>QYB^Y0NYIoE^E&6ydF7hSgfM0$f`F+pa&|xzOwsV&b}HwZC{L6GlS$1*r4%l~ zDmKJl{y9@lQK{)&vQvgKW$Ar=L*ljJ@OdgI6*8|Mwc#WHxXJ2}0nRygSKCCi_S%@a zHzzjnE1sMQbATmutQ$-C+tC4Is8qQ-V!g70;Gh2HGl#3ISeJ11W+WKCJA2P54pFQ} z!*|=29H*}Qig_U>J#F=?3Q@;Jgnr)4$u-TxXyY*{Qy%N$viEZUU(cBoqgZDGfsqv$ z!LF#z$rYi=DQEV>pFTSJ#yQ6xZ=DFe?O>Lw90RXIBA9@!_AbN+2p9FO(s#>X&d|}J z;g!~#L!%@Yghokp_^Xv+4b{zR=*zI2taL!&*wm~7Cs?@CHv9o)SmmriQ*I%6$zM;9 zet3~22a%cqp*Y3%7x@avP?9WZss{;+-t7MO zlG6YN(Rz`O2Q*njVD%+`AVHhiE0YK!4Csc=Viy94hkD&O2VhhHoxu?nfEa*X<3 zY2wE%DmhwXnRb`8B{7kv-0XeHc@WB&k_XYkb(=A9qH9B9ronKLgo&XDS$0!T2(l7- zkIAoWABB|U5j0tW!srsvJi!0LPf?gUaGMDbC+=2@zY+T+w|e$iLdS`*gdZmZ63hNL zsYDfM#gCI5h^yn|oqbE*!2>+V#z|gnRqIemIq4_!al+W=t_sMB4x>1c=X{w1ERn?tE3Hq!>#F&c zaYF7#Wwy_qx#Tp6vZJv_XctL;5Iz8;T8GTz--dHkZXg*DZP+MD<&g+C@Y5xIBNV5) z0h2~1b3z`Fq7zf}*116r`qwJ*Hptsl1Ox>zdL~K;y!n;tu~DMb;}^C$lj(rfq|!p* zG;SGt&M#T~KMqo6)_o_TytlKsHuG|MeBK=-@3~&Zh_jHgD&j9nt+bJ>|5o+@>%VbX zu>MtfY|w!V#2N1fhGR!F-VfOKgWCxmg$%!j(4i8w5iIp5;KBEzU zgv^*Z6)8b2%{p8U&EivYz=mP^;w-?lo-UPxg3HN)sKb93{{grO|52O-7$-A>d2Yf* zaueQ?zsDZ~Xl3PC^ZziDF^|KzYLrj2M6Zw!7ynU)%RKV^BBAL`H_iqBmrcm`aQJSc zMX20RjmpU*k?$YKpT;^lBsdDq=nGx#nS;+~qy~lrcssu27APP9a)89KlUR>6M!M;W zr(hC*_n6`7hvD$5EMQ{Jq#KVar^+?J^F)>2aba-wx==LW<%Q`3^l$$8PR@p^wbqUh z)Ww7eK^G)(p|6EXEyct|a@Sh)MQMTcS-?jnsjXV^*;1GMu?OXwjPW;S#XZw~IHldC zD4e3<`*2zdvruWu6D4nAQgb2Dg;hn{7ymVYOHRJ19wvDgLz%K9uX~<81LKl|%}3j7 zM)!9n2h0vH^oG z{mozw>#rWhZzD{~lt;YWptAZp`{oGuVZ?jQFPQxL0Yn=hhQ{#30thV=8b0_)x02+{ zgspRVegF(&Cz75V>ZtIg2O`XlnO0;Qr3Wm|d?rUQ>*(+{t6Q^f12$in3$o^%%DTW` z1+<61{CaHA#wce{J7ULXXB9hS!!CB{YoR+IP>*qXqU`KfggHc2`Juc_{lXZ1X5e&= zm{Sv!hcJepb5$F3tB2Iak8#+Xi&3*C^9m*<$+D)#P_WiI%jY2cQLIYfeA5~*Nc5nE znf(+rAJaxO1;ef{WY|H|-u*`Ln(S3P zNcuvv?B;kVnGlvO=K=Vu)fnxXg9~5Q-}~s=<_Z;bK-}nzY2v2jbEC?fwdG~ZNiEhL zY$Fw2D!ATAFpp2aNPPOF7~!U)YadU+(J$NqKOn2=-G&cGzvM{YQAjcF-pC3DLYIAV z27-zwM)jL9x5iU|OoZ>;0)$O8Vkp9N*s~JgJ65JW73V^<1*hm?O9 zDqZH!g*gajIoX;&8SCEZJLirxAMjR(NRm7nMf0U7t(Pgy5L!i4WF4X+Kw5!iwa$>c zBlo5jeiUL-3auU+B1%1Ozl^^Yde8wO(zq{6#j$Ix@To*B!4BY2j{}*zqG!ED@bX>V zD)QOw+FYv_Z=rEt%5k(IOT!IY?y^>+%BtJ&vhYxTa98pr=U2$a+#>lEGAY1x_iQxd0xT7p!c_C7j_>PpI5-&i1l0w7p7uNN|J{< zHW_vOWnJ|CNcj9vra^~dkuZE|47!9HpfuxXQxpQs9Em#`nJ&4t!=~wK;~W5EW7xZQ zb?%bm@U_Ew8i;uQna_G7;CgZdxRU-?reyQtj zPF-?@eqGrZH$EvsmfckAn3d3Hw;aMb3MmQgY`za*wZ*3_^usWTY74o#qB^ZImB2SiE`2*Mgr2%S|?qts&ypcq4{2v zci)H;|A-}L1`OkbQj}2_;-u@f&NBoSMwa(ucD=it9 zNh#m`XJ-?0#DqX;;Cf&)XCRjzluVkgB8hGYMw?~+I zzQ8hNmtg#Ew~!-ZXGF$BT0nR=^tDyV8a82`ni(IU1v6$&5QtXhKqDQpdzmLSL|XP> z+zSDMN1B+mBMxmlU@J=#enCg3IOA#Vw=FVHOM7QwY40p7?fiOakm#k78BVNX6r%Xc z?@i>7M;s8{ddW4>?Vk$e?6F#sGS6r-<8ZN7PlXyCY4J%&OMXke3MPi^8!?byx;iu` zcqpgg_YBOKTb-sd32bbyjv4xJHN2#n4Ba$+7}c@9>lG>46Lg-nkyTJ!L>`O6F1 z64bRaV{W#DU)kg@G#p?F9fHOZe(!?p9(zZrdUu3id$MDx{1)R2Ie9>Zpd=48CxziV z;_Q%(v#Q`J2pG3*eP2? z?FGw#Q?@@lSKb&j<(6pIoIW7^@G9v8GK!ub0Lkcy@o&RrNBYQBQUcnrQMMZN>UcVx z?fDy_I3;gh<@*dnNwU1Dn3Y9u77l&QA1W9`>s68ynyh%c;Pk<`MQe|=Ak${{DkY3e zZxxtfbC~(oIn?(M82e{GnIg?*W&*T>9W!0;H%!YZDw9PZcC? zEwO$h^oSZKJ1n8|?Q6Z%>#)BZSN-=^&0#|c4^LkX0TBtp0?Ya8RFtGNf~FI)j$ z8Sig=FA`xsP8g5fRRKAedlV-OzBjTWPP(l@4rW)YW;CYV2V4oL9-4Kl=ytDvC3(Wn zXz!ucs8L5`x=|@vaptEhQwCNCuh@JO_5@B3MQmYDEAOOq+Oe{*XM1&ym9uyD_g=3Y zs-zXSBaPFaEE#IUQF$_{n%r z03$2#;9X&xlOmJ}Bq?HN<~{NB`l28wQygH4EKYnz#WiOU9T6w3^kx-zk(%FvuW~bE z-h0E_Bhc#GLtoEg{q%`D?<+s1 zsIRkcj%pvqy0>Z|*1KIm4&v8t6pY!G*EtM)tLV26K-f%@&hhV4(su>QF6jPM(hQoD zK=ECHk^@v{MDOST*Zf}ljfw|otOHbEb4>e`^j&($?-JiCedqBxAwXxL;|h$^webY5 z^_2qrTd9HYR|P;_E4KpwR+_K)t89_*S8Zosec16eCv(-+az`%HtEI_hlp?xZMqdjP z{{gFI<9G4&`wX9h^^3oDD;p!d3V|zo30#A>crq51cDQn$08sEZe)Ow~FvI5$*^1(_jAbAOG_Mut4X>`4crP z<5%zJZzL?CSKAH73j<-WZRNu^BpCF>Tk_`C-1=|z+6QdrsxFM9cu4$uTZ<6d+BR&=Hs^dRnk9(uE3eEU>mQM;&HnAbb@PsFT6Iq zSN_KPky5-d@{z(nTL{IjB{s zXalS+x)B6eY!jn{rFXw>`k;8}rmm+(rkvYR3X)wx(bi(tZI>zrET0_(NxzQGacppy zeZIbGl+)w-s!`6@+vkmRxK=48gRS*NrIZ-`<`j-ET&w|D2T~mnbzsv0O7~s|y5`sZ z>%b8C=*rZPqi8PIoS#wGXvW=h&KOFN#rSUyoqtp0?tFJ1>>p{ladJ8e%{2#izbSf0p}FQz_8E0! z>teONE~??``L4)#veTi^uqMNgsHkT)i*fC(6+@-LAYxj>tzprxm6w43Z)>c<3&V53s-Hc1cnc_CX1&~db=>)cER(* z&)BTXKMX~eS@$OC3C+5ZC*~k2#6;<8;jel&$p+(ysoQK;Ll-+NODOgMv17Bdh@GNb z8#~vWh;~4M)M!V=6jp*8q31)bC_j`B3p8WWCEp#H=0?8_hgCkpnEqo{W}Z%O`<(Pq zY=4uN=eG$)-kRl2MFwD9I5MqH9I9xPYa}|q$r_SrZu3{g-=dKnGR-Z=vTvP3My9#t zD1Gg)Ud;!nu|2D~<-pU8)9}bNtlZG(IxfU1MYC|^jicPj_y4bz1$hExOlB<5ztznp zYr^r>RhCff1L8;LP7^=wX$D7YEK@nn*k0M*8rkSpk6882>y~4vl`-Yj<~xnqk!fx@ zS)p1-@)U+5WZ6yaJ6H+5$K>e!QAjb6-K+@?ndX)Q`c*cF#WcOqjURSD${s#V;Wh>6M~+8P!XWuB-*?2ATMjTS`M%r#N5gl8V9smM zenopwBl2dyYMWR85r7-f9OA6UC+9atqE5<`#kvC47VC5#mZSGavCfEOBP+1#=MUia z0Hg$D6F4suXgRoFE%(v*&dU(D9Lc|RCeM!!B!A2C{WB8)#>OgT*G4kXQ-`|0<@o+7 z(d}{HEl2WiR6HPK9Z33?9~61bZbn3TVo1vq)^gt zOo3Zt3b2dt|F#@^q{wAP-X%J=u>IG^+4>1!p& zZWn*;7CMIHcS*HF=-zTlg7PWJL)cwF`IzimPGP7%AlVB;NwVclZ!~-!!!73wXcc*l zrfszVP=zMF=cIxIbfe?L=%#eqtxP%CV1MtW@;FX6CSTYH<-#?NPzN+O^q8{$nndvf z6!${NT~6Ns>vvH6*LGRzmCt=wq#SkqDfrvXHaFfxug$o2S9K}p)_<#{25W{j%Z5r& zi<-plH%$Oe+f@^ru=}@Y_-@7DjNNZS&%~9wSDe8j0AE6nWmy6!5&u!To*kt4mLoD( z%py|k$JU_tgtM@-TaMK(*$4R_|M%-}KmGjMA3y(em`s2Ar$2uD{g3}m!+w1Gyxa<1 zxFITy8_r+he`8#@S)gVt+9R-m-L!H{wX67Tgh>&yRpj*fyfw`I&A-C`mczkSh)PU& z^Y-t50J6!>b9LNN*mt!-G<=r<=0WFHKM>-cL0 zn$hnF(~ZCE1k#OYtyFfzpRQC){JEH{TTa6&mHbUIuv+NlX{kUEOGaB4X`cyn%Q5E4 z9~sWbX1!I6`S(9y<+7^I5#!2NBy7A5n$Im_bifDv-O2#yS=v+bEN&=Co_WO^mA484 zHQty5*hjl_2@r3x0_^Z{!Mbn1l&(9 z@4S7O|1Affk3ve~(VMyeumVF?{4+7cDv zY-2awd>;@ejq<_M@9V93`1_5q*^^E>Fj7}>PvPEgOkbE3VLnd$OTuqCQhFFC)F&{q zIt+wce*3;!`nl`Vk7;)r_Z%?4QFQc|jQ{4?Qyt>sW`_MCUfM1vR^GZi@{}D1U%wk> zp_+hqI93|=rB`ItmKJ*lgngw^KtJx5=1!3LP%F}KcZt8=X*j@Qv?h4)P>y1qJ5e>( zhA`eh8;UR;_N;`?WA3{E{CCEc8(C5G+e^%f3qw=w*~P-)t) z4y=6d4eH7hbUZ9=IW`aTmd&UXd?VJ~ro)|p>Y=g*>*Sp=y?v|`SqFGXlkD<~^OZA2 zBU>WPJqP7)oj_MlkUXKDlvD9r3PvfB0Y}M-i3DqaHuO19xp%Leg>ry;$8tA)^=)!s>vLtUd}!?%DL8-f{P3&4BMdeDAxWU{G8oi@UzQVf$8%pOawwWZo`3oG=hMU+^8^6pcRVhoSQ+yE3C;QmJ_YKiw-RAAha= z0!OJco&Wm|L*G6hxaZwBG^?*)?PfCiogWl=OHU;s-W#;(?$bx^^OS+R#PkbS>)tQp zA5OXe<%hSX7M>>XTpYvC9FpH9+_lYaYlOEu@QTtp!{-R&(MCkxI^SdkYp2`Y=7fPT z)@k==jN)(s?>RYO>nysZA}0&zbQp+!{{Si)aOjTTa?iogI$s^P4xR252aUg4G15@q z_j*6PO|Dam)ecwgbC_^tzbQap3!}a4+Vu6!N^+KNcKx9 z)<7HBPXLYH1UxEGsCN8x)46pf#764KL%54PJ7Src40bto0%<#X;{<>l1;o;RDRsmm zebqPn$W%ZKKb#-(fne;2F{B&<{`+Frr;NME&ioePp3V%_7v?f0W zeD{^a)RM3xGuS0TWd?sqps$7MJzz;_1gLu@I~h04!Q7FX%8R%~T?It389s-rEB|54 zyUBA~R|lF_U*YBqg4)bwDE_1*+3KLc8h2J&=a)GQRH;Z}L7UpQsi@;!-D&XH7a7QM z-=P~FA6hrHA7$O=tG9iXXmR4{_a2kq;^8&&-ZG5p4b3(3me1=MZIbp%$MyR8DDHH~;yfBhNZmENcSOLrK25`OyJV3jabSmYL>vs^bP6X7X!E1Fk97nc zQ*;^8&bLZUcM4zNkLYls&1$7c0d4;npG2z~J!lRPOS&dWVL={h`v%+_IiBlzl#fp$d_-Lcc)CVr*?^QOS zrzjfjm(qU2p{DC*ALSBcj?=xeHr(uylViTXL_Jy~=hFOR6o&+d>|rgOZYRD%SKWzr zPTcqh>B>v6R&{A3=nGWWpU378CQPZW5<>nFsg>&hqt=|C;HhGiv z3AI_KFzwY+iA$Bd4&Zed`&u-+(1EuaPV1U4&@c`rv`l#Zc?@DU^D#p>c{jPdllR^Ve3!2ZGbbz?6y=WJ!bKMW_ReL$Su zP0lg|gYdNa*%I#{6sP%sIP@7xl1JqA)=toi(rl~zF%K7iFmBnXePfvTZ*}n}zEVRr zN|c6N?A9ZHXQLb_?TV6ekaa2=?rpRwYZBj}P_ddHtu1W3mgd9+6(tPqKgI+#N-w%6gns$}d_WDebR+vH7}L z=KPSg>c)=Z-+ul5zy5&p$?M9tPqwV%wfSl)ECO4HhF5t*!pGkdr>}K8dgddCoY#cG zyoSlR%o4`w6IWJL)c=E&&@c4_0%r!&6d9mPdpX2YIU$4ZO^9ng0va2} zMd*JN&QQ4s!~D(o`84A_0t03H>85bNn#>r|KPgF;%oJqg!3ykUO6$+TdBH!5&EE{Z-nk~fIW0j8hiLbN!E{LDizm`pkzmMZ9=F>g3rAL6pB~dH?0_|rDK~34vWhzi z2#6XQDJrBG?r-c>QSd+kTh*tlfc6B+TV~|Vo6%q9{7Ct zOo;~pt3x}CB9j5yMr*JKJ&gj8sIfr@j4IMm2i>AM)tX)`*8k6c|F^_{_RR2{3Xl^ass~68#88rK1yIkFvM&1VLJqGU#j(6EzsdUj z4?uAP{@Xp?@W_GG+Sqiq(HWaD&&G>$wDZO}Fs?dyQ{XXwZ~i0DQTIN;gRVCZgK^_D zaEC46h*n`UC^zUH1sszyj2)tSWEdBffxwBji{OeDD{O#lU`FOq>byvK}@s#Y8+b51huD9Fg z?c;i0K5yiXbezD=LE5-iV)vsGm$7D)%{(5n@jW6Z8R&%Kl?QmfqN{9jYQWT(U74DL z{b$AmyvhdgI-$_+UwFI%K=A27nn&2SS2S>Q5dO6o5w}C*Kh~`;e$qW9@yOpCQ0sEV z>#p~6KI)qT+wxDRK^i;MV9h7;fr6m~*?N5We7yg&3YfIU0I^bq9$t*=>pX{QES>;9$kkDND9J0kLdh)IRlQv-J6HP88% z$^)2|MYGBf3OgUXMv`kqJlNu+!md0VhyqJAfqzA+3sez?HQF_I(;I^CbH zD0-`+v>`UCyyU&^O>1EP*13mE$#{QUpzu-}E)dwtqClqPC zBpXklZ;WG7R8Vb>`(x67zu=KW(nm2V9~5Y^;t;1hP0hjgsuYnIxwkQ+`Hy1Ke;449 z^Bd~o$d7MKW(S=evzV+`yBqJw-#a-VCfUW;m}C`qMN$4*f;JN6l}Xj)M&bisqm2An zf@&bX8h=s>Z6FI$mw->wLsv6Cb2!-`RG!34Sof*2!gek&;~$1uLR;aTRFPZZ#MA3$ z{m9?8IKUD~s1&93j+!s2r5muLj$|?cg$k+IO7J8e!<3PplFzX-pYh?i4iz3h6r~vG zDft{jDe_Pe4R6*%=QPj!*6d-ZP|47TO1&))rv^AKz&Sy882QX!evrZQl+Qd2H8Kny zx6a~ke5Gw{@F?53l^1(LEYt4bDGra|DJt%QCx4Pb8|kbEf3k`@3Fe6zPf5&Zeh{f9H&T26$X^M) zrkY984~DY55kG#K%c&W|`0-pyoVf7;S@&l6lUN?1CUpE9S3;kjG+QTr)^CL39AKC< ze%!-^=NvC&NBlHi$kS&MJ#+A~GLi&K4t+L{A6y@WNtrA^I8`bu55JYMDZwTC9M(hc zH2F&aqsU?6gOL@a-1`Tbv)yUzlYSn0sJ4YTtKMmg)3ZJTVC|6}+17yBVGkQHV&BQS zaO{zH^7j{P?Br{~R#Ep>D{13a8|k>L#%JniRd$Dm&zwfn%;fDOqQ!51!Ku^@qof>t zJqjDB@G_GhNiEDDmox|DzOM!agQ;d z^R$nre1Kx7k>ASr*Tna#Re-1oICS>e*T|70D}4#T;c+Ou*a1A znN12ho$D1P==Ge?eLJo9-eQ+*bv0G#?$A}{%1lA(9Vy+h9pTDlYgYAaC3sL*I$kD! z{Fndx^J*vJUrc&X@#6pQx1av^Z$AO(_VEq4THF!jA^uP2PrO+Eg8V<0*Y!d7^EWaf z;r$s*z2X-bS4e-=n(@J~nYCrXr~vw_xF?PhNc#2PxXnp_mEsBev{LcnulB#FoNTCV zZAeiw#D5v+IZsV`5@o8KFFu*-_1!u->!~>{NkV#~A-gTkJnbkN(q8BP%vnbP-Z2`z ztE|BUOL`e^j&5nW&nfzc#cD>f<4n_}nw%S#Ff5u^dlU8(rHxHamIC#iLH=kLU?g@I54*Gp)j`&x>DNp>GT!?oV2rE>5lmfM&ai5%C68V1cHKaT{{7h&S z<5HG1n#GM>^gM+bmiP(bO7gDMoh#)0mKnWEzKNB6T>}o2y*t&&sS8uG_fC?Q4r(Wd zb6Tun#)^4{2}AVR7UE~4raXz3MT=x5shpmbFoVC^0$VZkM*AdRsQm+?e67sxQJ9xP zz{-?*M5)Z4Oj~kUIj4oa1Js= z14nkvzQpsDH(q_m3L0G9~NK&OpG7{u04;6%x!0s?;X9I|zN|#xti&jDk?oD4M9mbhI;{UqzXCbCen`o<|^jYWwg0OPSPEVKHbpDppR7wz<-5UN^v~xH1f)AL+Q2Ak)`E)Q+ zo-$)bA&__T%C!0o#uPo}$&8*!NhVjssxz}9;-AwzoChQR*K4w~iC&iZVeP>g8*pVC-S|)jQia z?O`icw(Wal$%Rq973lmer%Q}NT{0-dx(l3#T2%0vbWu?zVK#~JqOB%z84wUBg&iJX zh%D4yPDC+#>>PVTy?7smI;*$~^_-QVLS3>lq~T3#6q;V$E}T`gmPf;zO%;Yux0Bkh zDC-~PSEY+kCfJAifFnR9HvY%nQW^RBbf~lTI`h(DNN9>-sC%UlU3P6lJx%V~B=4-H zqgB10^;mJz^{a;Z4nw4&PD<-6L0=0kvAe{EUzG|6f0Z>!X$3u~G#`6+fwd@)+*8^o zdsrm63vBtsvpKJW!R31U1iFRq0=qr$pBI|JdVKb*_eEhBU*?HIsaz;kN+zyoXdzu| z8#Zrehnz+eB_}2um_*X^;LqRahoPDG9}98wP3fAujX2K4*Gn4~Kssf7{C`%hg>tDp z&vc4@u3br&Q*LI)#L26bGu^CDjoB?2G@)?rUuhG(QpuW!ZvwN2|0wPW_zC`_EGPi^ z7%dPjAe30;Q^xNbYt6)K{}_!WrbEIsuh&G%)RIXN`W4N8b@BYod=ulB~h8Lq|3^q>lU|FK1015F3?n zQuW(eDd$zF>?lbTA~tZtf{Bf3?!@_<;=b!^#YG4JG%Md<~| zhfJlyvMNf`$7r9$kZvO@c>CtYdwhL<>njNf`z=u_6+7#+sH_pV!xA7Uo&-k!GG--f6*LUSR-V?{+LX$E9Slu3;Pv-!+VL?#LHz3IHaB7K)nr zWdi(nSx*4hGg{7x6Dpxgh<5|h;oL=)R zYWKBre5bIIC7udSE@;tqIe)8k10aI%A7u>zC%9@lxFrww@9HKW93|Zd(vuwoV_Qc3*Q%54bV22qbz&CwBbJ}ap6*PvRa8> zAfC+Z^LGf;Mxw&Q4}jI$TNcjWXz@&wNHWl*VKy^5HZHA|$ zVrCafl82luhbsfX$V7ef&>2S?6BqjmOLXf35?bW8oxOL8DifVqDzH2bwU|}@%v`T zt%j(;e>>@^0Ni1SEC4Gl!N^2k3k~4};Mh6z8dzx)=WmrFVI-j7jwSd9bOuhJ_ixu% z*;Y3CSb|IC3l)^V%v9(UHk?YyCEf0v$sxw|fEKewG-0-n8r`rOVVyOfg`>w?|5<+s z&uGzl`Bsmw*W=m>P4Bil)4TzU@hXRJX{Vp6*(R zPQg)nDLmIs-9J=r#`&AeUBN%6;_)%r zbVplVK(JB9l8gfH(N=)~p`T;cM>`3DWq-*EU{6<3=ICc_i6bk@3W_$4pL0|WutXn> z!V-Q(Np=tO)d9g+-+F-~mOX{hN=`6zpkyzb&qRvqqRt(0;62W>IO4cI@seaklHIPK zk1NdpP%^BTZJ*@wlFsW5djFXEz}_+dQPM;pqhY=Cu2L}G({^77*vkSSM_MwKFj%kN zn-?KVNRp*rl+g9r)~nhQGX*QcddXWaBhlTpkMj@1DATHcwXt`r9{1aHcLxCG3N5&y zxL#bBXyE|M=mcdf<8BVv4?@+I1hrn)*jpk`52actzK(7qPrvt#9PF=+N3DcoN;(wy zI4ZCwBI~7kGd6mslFyTfnbZ&LzWdor^6JdD5Xcd|P)pkTSD9u3Vry#;jDxZGe0wYkCJtP|dziQ=3SFD<=iIT%y%CYZg z>m=>T^-e-B59EOLQS2}Q!>9`l9owH=Z$eB$RL9PN33@v3a+H(uw21GPYR`J#T4sHvJ+hc&)pd2}6e(oSj;}sb0<8p9W?uRB zXKU}-XG2SB`8)#>)lj#W>-`gmKV~;x@83SpWY@JNRT?L>pDk4y*65P`kcG;O@t0r2 z5z8g?h?0+xc#zSUK+q?lZnHB_vWdn7RNi`Sx=;JUgEx>-F!iEPz?p&J#($S34>SWS zXS!e40qCsd7q2Y4ndDfB&Q1Mtr%I^9)>qiknj} z81agmQtuylB!zB9#rp=hP&MqfeIV$#mxCI>xJS;yaMV6Mh5!8VUw>AarZl|l0r4q+ zDIhe>bD!twNp`ly~_pDK3W$dtcaFxof>bmmKIu*B^H#ROJk zER9&_?I+X9R8?oe`0zqIX1WL1IE}tw%K>XTsfa>oGHfD;LXCPp)z=j8S83tlFF&q9 zE|Mh6HTvMgCxV=E%EKr~1qgWW@N$~xLY(;F zfJrTkniM`4g1-@Z-vRc}F=^}(L`2%hIUpw4(bsK!eVf5}X-+!za5HbEQnhuM!_A-* zei3i8Agn);S-44Y`F590ipp6<#%afpKH2A#(?MRj=dVBi_|vby{Q_5n&IB<;Is9YY zSsF#sZ$tO`!u=2Jcq_vLB|i#1RNg3pp%YTv2a2KYUYXN8PvXQV@)DiQZyD8}(L&ml zB2)Z`lZ54;MiTT{@^zogx>7c1DeIs=Sjm|Ssyvd+g(legxDIddjgRESi=Y3rDqyecl5* zR4fwT$#CK~65di%-FP+T#i>41VSOPzR?|)GPEDC9u-9Ht} z2^rPwl7P`BH(vm~LR&AmmB?VVo)+d>MbwNvL`7YPY5-m@2Ut5)qawm+#xF&33Q8?S zc0@#_nZRHClihh91s+#hF%-r&J&%Ha7lyeqx8zX_{cu!*`aBA}6F0LhWSWVY-2RC0 zc@%hSE|Y1rC16~(k+o+t;C=m4)?2JT^1CmM6#*8eCa`Fg?+hFkgnbncT=NWM_w4sd{OSf)H#&jFpW z&S!E7mGR{qvD+;#4;~t^tQ|lpy&*#1KJr@&tb=|z+w|51`m>IR0Ac~oLb!tXoSpxt zp$@byyiUSkz4)i#oE@ou71^#_=*8lnLvao;36^v9uBPbd6*cxUrG-`5+yda{*dW(V zY4;Q-M>zBJd#&W8hG@l;WQMlBJLl}k8ky3y4^VzNCLS?e-v`os-F$%3-lQZ+SgQU8 ztHLornz2T;H3P7Xs4xWKbIrm+zYPOG*@AJDX4u^0!ZzI5rwN?t8=*J{*h0snF^0R< zM1`2S>k;u-7g3(R;|{3=VLVdGGxmZT*aqJ@=VoXh@G}2qA1IZCb;-2WsXoBm0yDcv zk|Z8iMs9<>uqt{3%e z$bBfRwN5P4{$Q*;61b<^w&p;OCD_pvq9R{4Gz?fHd@t*~oL-C)#MLLDM-0|jmfPo@ z+-zl(lua0w$Yqg2(kb)AjQv7#%qIX)`QLv1{lDm}oEk5aaVeJyHE}NK8PNo61u!N| zBj|AzHiXim_Y1e@z;_r?2qYXjit6j|Rr*G0c#p1c9B5V) z$;HtCB*lMFM*LV`bFq@86_X>`ncoH(0b|&^dPS^hFjQ_yzbPqY^837fP!9j6fAV=M zD~QEtCCAXKRf5R}>{oBe*0+7S)?H5UsO7BWb_^xZCZX5 zHY${ut6;Q*^_i$aR{3dT<;^dk*THNf`;m3SHgZJBi{O!rkfDBMy6r8D!6?W-UUVq@ zlfMY&w2}jie*@kS0K~uk{Od1&`{TnH-`~%YtA$#t`&miznN(QqFI6f})p}Q)T&Gmga5t{z)H)mLA-H7JcP_W#m(Fa9S7fn($ zJ8Y1pl|psl9ZJ$Jtd&%03|6Le!8^i*OQjDNF3lMu0b!Lx*NO6*1kg8&;ZU$Um!cnr z!z(XfR)@*TTX#4Wf^BD;x1>v;Fw8C$mtXsJ{O#z;BtJgWMbX<{-9JfUk9PJo}F@*?3CgBhnDa& z(G)x7yu%VU8fCrbvs3tMUm1;!s#R3nt?PpeDV!6Ax9p?)jxQuCfT(!%#RZ~XKcCC( zb;5ALHC3QC-fp-|{dT*HhhlNH)tWiiR8wzSKv`QBYW!6aAQDk&HJyufJ0;|gjFglF zkH?po%F=s(2UN=$Kgr?1cKvei2uDc}vrJyz3BiZp-BnZP{H2%eoo}b}T#j10RFU&! zzCr}zuk&e-$=LI(q_botHM60a+qEKc`iXL|V9a(HWu;wxJIo-_Y8ri{H_TeKj)v#R zh{m+E)iXiFM`POSh$3Ah@~uYFla(o56MY&@D?4&pUcRYobmfq%uphS{NehNjzxIGr>}Xt_s>*TBHl{bpEa)+4JE`PDGgTs!M%YV^kpfXN&RCv?#${ zcD;epl;QZyUJ3d;0F6!vnH=gw?qV+dxB^GUTXOZMayZ?=A%+ztJ+d#Ip9_;1CZ*7w zAAJ+HV)I`Zp=}GcOU_0JGVw(c6Pl)Qa!?O#aD))_uK9mDX%9-3lbRUpWq6`p@A^Xg zhESXXY_g)I;S-vU&(dwgu#t%Hm&j7B3Y>Ze>a{u-1)e!0*+K1@m2UHb%{jJ9MKx*XYL2*Yvf}Zw~O> zhOOo4_nu2|<4}UAYLejhW+?P~?auW=KMaRg#%K1!q*sn=q>7?8zDVB)#c4k9PI{e> z4C-qpvlq&-UPzDwe?MzrSUltp5(k<3IoU(;t8Pcz!;f=Z1g0fXpQj{+Gw) z6N_$nGXf3x*P9V#(?=C~L6e}Cs4XE@uHy&kX8VNf|CXHnF2B3D^^!jfQBx&9 zx?s{zq^eP>YynzhJ|-qoBTnI>I3xh$E?0bQTW?Hpj`%d`?A^Dp_@KF zUb?AReCwU*+1L^B(YB)Xq}xc4ljXd-oZnhH)DmatfkE+L-atR&*uE7HSQppS=n@(b&Y^)T+jq?`|R z1vLJ(r1Xv{c*$u?UvSm|q6&^ugX7K%aehMOl?0O>ckXcMfCq;Yz3yD`LJ*Ts&@a#% zjuK5k%+p#f%(yTC5!GsWGVaeaY4A`Lb)h1H(lHB_cv$D+vso@V*+V4@N%m;Y^ZgIt zLI5&kRle4K;Z)W7Cz%3m*lj95^K|;pugf5CS%nI>@`zq#V&x7P73D;< zx(pIcY}OlenaIApE)*5qWhBTs3)=RwNMWMRcUXIoR1fZQ`f^lvk9uAvfEdHjn)b z^IE?IpoH=dC7BPvdgH1|d-?MZqh>O57EIbgN3Ao&nla~AKzd=M=J+`mi%-4%cN{MH zD<-w}*kOn?0BQB;og-~k9T0$xQ^C{k>w*CZ`zyQ1XTOUK7|-QiU&6a^MM6t>i7#Ap zR2s$BtJ8|BcX)1E~x<`2Oyc9YT8^> zD~tsF59gcjrr-vC63O`jHL>l`{OxnOgID2}_LVMRRWy~@nE7Oso_l2N09RTJwBoP3 z^T7EFLzImes3^PAA_w>@!%Iy2Zo^mDv{5pBBg{$F@ba~ip(J_8$&#|-x~)uUJ{px4 zj0Nv4(bX3DQzY6Fv|5-kb0>>yiwaSgzkr09wq|ZMLeO?>>!0k29r7xfImr7a)Qi?LOi(K%H5Omjrh2B00 zRWt~}rGoY89JJOhu!OCM_=n+Vq$L*l+{iEm4OT&(US|pZMkvk!mPkUdD6K2+2M@=r z8=kegqlUqxcj*0fWyhS0iaQBt>{u&ad0%ZF`KPpxs#nc9Pf*^UKXDbti==ryT|Uv~ zXOiX_$2?a`B6QBfB?~1OdRvD6a##B$eg5)IP60&+ za37FUjBhh5d`1?;3zKi0oRB7_yDMjq!b5wZ5@arX&?JBUW;WOZh~}P{~S)*z~QDmyt97N4RKMbd?stNi2$#DL1 zwdw(FxZ;(H#NOG`ZA=Zp9hT4`Xe{BcaRchs{t#pr$11z9iaV{$^JW$%iiU}e}?nbOwUC zl6iNVII+>3%(J8{(6uTU`8RRh%h1qZG&-{C&Da> z2vf(5Qqaf}J`sk$5jsXqhC3{gg`iJ_VY|mN9T0+L^~JR;W!GtvK>q%P3PFCH!8j>G z(DpNzoZh2CP;q&78-A^G`l64%5i7YAqD)LNvx{7CJ_K25UGb5F%7-BsBtzR^TykWr zw#1Riu!35xv#sCwN=u|67$n1dW!@ixm2-s9EG65*jHI$iLO0GKMZ|dTf*Dv zZ?=S=42gIXigSP^vJhmY_1=+Rt+GD^E4S*r_`IVf=XdB3^ma=yyah8XIafm)-pl7( z4e!%5mcb2&Wff%p%VaWmBLw~8IHw_uLQuZLA-4n;y}aZM3S|k!pr~QJ*%JP)Cnrj@ z5PWAn)|SXZu<|7A8kDch2ZUf*ea{Q4xJv>#c1wjIUvp`k6d~BPV{)>BHawj>n=v;V z-e2f*l1DW>zX&rai8j1GsnC5mR$8a_oJ=tcK|dK{P~%@YS3+Cj$Yki#B$feKza+yQ zmPkV|NQPS&rZo4CLQuXwB38lfM$KjZ-I0YK#W2?0nhco*(9b&U10P_C zECg9;y-ew)qX&dwEG&|^hP@*ODOgM0b}!`3K+j>Fra%myMswBw&X!~i;$*V{O6KCqM&?H zV#Avl1c1hoaEHNM=;xunm77RxTN`<+n^erVG5-MLRw`5Z49HNDJYNd%E$gDsm-)K@ zqkxsHfhH?qFmW(%XP=fr+8ACY+iVQKAkJS1sj;}j7&aD_G5k#=eJ%7?uSRJ~&$Uxy zk-dD4MW2YWnzm**i{;HnMs-@rgT#jqb zT2Q&1$Oy;ea-7~9b>>WmlCVL>a(Z2n9{XaTal}ubk5fxK|37nYvm;B6V-4=}DPlRZ zn8pG5pJSpVCn=JWyDYx^fUJT?Tn{sf z6Bp0e0CZ(yM0}bG=Mn;LAi4 z>Bh+qPw54^C^JY_ZsQu`M1zeJ4XPN}R%-%E`l353fkaL;sLzZ|?yiwHn`p2N5e>G~ za6TFn0zit5?X0s_>tV?4rMH}CP?cuvZ*Jo}gN^eH>N8(9M4FA;QSUL&^^1zY6!KMi z;7KA4ArYu7VW-zaJcAhoR-QptajTUu7ZEuF=NVKZ!SK4feH-T)Y>5Lf>i0ZHHaK2e z;!L#j&k)aGbm;oMups1jC>ATP0Q2HO@5H zIMbj$_GR0f`(*SEU(Zye+BnmoD~XK00SIlP!8S!S*!ZdTRaDqQZ`{xq-H}TW9#}1; z;kD5)b=te)6cA1rA_Som4&$kL9)y)>u-Qa|ZJcPZaiRe?ym2@%eb-gCus!D*)W^OG zLYr%_4RH-N&Nb*tG7LhSYOqaF4K_|SsGy z`uXAXAOOkv4)ac6FV$kIL~#PnlITh@4oG{7h+A-bfTa60RP2CJwm0l%D_g|Zh)7!; z863;5yAmh9g5&^OM1ZvKA=#4U{U9+9$ih!qHv86*E$$K*fFxo@Yx`zajJYSm@Fw@< z)ks{9^;d?Ec`L>6Pcc?P5Z-Ytcv;}sWR@)^j_7UtWi#0pBWe;m`QC0Ey3)}2r?nQQ z@CRs3!$qjgiCtksOhe8AH0B3)GRM!N|6kQY07WH z;R)@KTH?@c$0crdEn<$xn6%Bb9rIYmrA2E#dV@~r3Ob?H>V!@pt(I_OeMox1Q9m>c zTf@?$Dhjcm-q6hA)lg2R|1)StnVKeTri(6BDvwZ`Fh9T zMps8PAv$h?7a1DwR_+SM=dxBcr#B*^5=k_3IuRFuVq(MDcV<-(aI^)0W3#f1iJ=pjBTp@8Rg+eq*z+hn@1RM8ewmdZL~20Udh%qrFa`Vx}> zq><9x_!Ne{g&-zoXW!-0A#sqIE{O`X#}*K8x(@RMgz~+z0EbuJbuU<_d4^XT#%r& z60g&Rk%*#jh>>`0S_?E1<}P&2#GowW_(|GmLvPH&=;4PGGb+|m3O>J4H5fUjRam;j zoC|3hM!cx6cy<0sqcxNecT?rSHer|uKc(egJ4?8&cmRG1EoZq9x22hX5--C=ur$(f zL2h3twiTaC%u6XDjx$oa&JtsXZKYjohRP-8P4uwCsT5V#z1l@ZskVl6iHQ-?Ajf6X z3ysr2%}7RlnJ|e)4L3 zFS#kTq&K-xF6bdX&fzl;(O;VI5|a$N=t=3Il66PVqttlXYYI&f?~84$ zlGh9b6lKrRrU@&c2be~qh_6daUoNiEM#f(!5*9tN*b3j;w(+BF4 zUxkd#8n~o)xMM%S9vU*-9;J_6si;G&IRG-bG_la|+dz^U`(3R_BeBh}Nh9$s4yBSI zn!|<2DuYwrIBaa1!zDiKzY7}+;(!g#lE982=3C>SuuI%8EbYP)!AZNoP3&4$Ot~rG znXpO-9y@Q0`BzqsHmIF4-xSITv|^WD<3@8~B)Udo&|y18P__xEz!S4!s+ri*xT}(J z@YsFlmzd1a0}oLFl-|(BBR4;_8p3Lpv`gCiP|tsL<|(HiX>sDkL@uszdeW0tiNI61 z6iTL;+H?ST$l><|j?IX;#5Yt(BOMpvu$WMqh;Owhn4#1D2mK_*3}4&W43+d*JT zDsFrs=8p&naG5j;LKs+yU!`n=eOU$Y0 zL6FldxFvL}@+Ia<2nce*M1AI~5VUy`m-LR$v_u5KLIzNYLQG*h0)lz=U1<326cIP_ zN(kC3aIE&#sA5KnH2iTnDTZiI^of4aH9Qf127|#95VYrmm>bZ8AT>QJSrH6%|NSNA zH%LoZ>F%o#EWOy;F~YpKED|Ox5katYiffs|Er|mlSlB=CPdgPJzeeOjUF@ylHEoW* z==!`cJoU({XTEHBd!moYCB^X6w?8P!I0Q@UzcT#P1tJ%Ifk>UP0KsO?2seDw8$Etw$c64w79VNJ~TzEL01nqVzW3JLVxsF1{BsNyV*yhPi=+ptV}URl7|&ChonL zhPRU2mkn=E^f6PaK#-H9o;18!0IXlPl+%R0zHnC$SlW z{X3Zw(DQv|P>j5CGDBAinWku{uwA38%`&-8u}rR<9WQk!e$~4Cd1CM zM#~;DeSH+PJhQ@o0xfnpo8pyRIj@AF1-nLS=;L&fD{&nTLQ5K+ftGMOt!R1O5)oQ# zI>|MWSA6f7US4Gn-zOHEPI4WmlUzBSq#FLUkVfYoXnz?BXPiXRl_G-5>HYjs8T6!w z)@i0lBv+z8D7Q|x1L&deKz02qq0^ppYT7OyQHaC1#>`^=y~K;E+hn+0}_Ssl`LDcGvH?zQXydr{hlhH^K40nAh^5C^!B654`#8h)7hQv{a#9d>aO)(BXxW6lbG>$IZ zu$D8@K?|bPz3zs%9*Bq&L5@^q1@}jXkJP2_z9rA6I4$&_e*g7<{u5`R;)#ff&eNMc zj429+_m-w$o}_WdAe@M^VX9g@e8u@0w2)DSPic1EXA7H&aUJ5vTqhh%qn|q%S*t;R z%=EJAQG*)GG$uk792;86H+HKKJc#0~z!a75Nn{v3;D# zUrcH##^HRHt|G?KZ+H1$<0j`GauQ!8uNAATI2x?MXIOxj24J<{`pDM|0F*fX5SGXe zFhqnB(pl@HapBd-kO#F>P*P|}(28CDiU}XWNSyF7D29g;?2I)BOI%}~NHvfZw>?)G zM~Y1rxlR!?uAG=rHO3B>pyL8mG#@8vTscXjKHt+tFWt0B8uX6tBG}j@ zjca46f}Ip7s3fn7>Red zcbzm2X4r96I7tupHIcV8h$N$Y&x@xribXgn&F)&J5W78`B#ug(g<-o}zb4!{ktSgM zev>9xuH`NVEl#*8QE)P0i3lg8v?iLTxB1>NKcA3q?~4_i`f`oCy`_;33}#KeE~emA z>#%ObuAjs>;f+nyVBc6Lrh@cv!r38JRvffq98Po{Cx~1*L8Ly`3l(v+TCO%hgx=x1 zWeHlZ8$&~d8^b1uTx0Uo0aHq0NkJ=iRWGKf2&iyA%B+#n;Y2Ey#Wa?J7S3SlN)e&u z^nO|%eKR(frqwJAX*G8`} z13CaOg+Bzqm`nUyqJ+3w;efeurpK(2bPh%jcjc&P`IPlNDWSjy;m8pZ48)qd{ zb>ToForBRA-PcGHq+OrE9JGTI8lExguyTHYB_vJ+?I|m*#mR1WS4HzU$-a#o>_*q? zXbEmH|3MllA?*+&p%X*~)mu#Z$=11XzEAZ%F9C^W6=fYWirrExyK9{;PAp6VCwfEo z7T@_IO>kfuOv5v9vcJO}^#PU$abm9N$?m>P(`yxSKj;EDF=yFZ>i%~n5Jn=>$T2uc z_rNKr#N?b}AkND<5Gpnc;ubSv{`KoWzN6pa-~ayQ|AkthWKMzU3TMUp(~|EaR(IrS zRgJ8>ZxgDDd^zc)#aDWp>q!U1{6bnPYHniwIA!I=DJw4-YPpr<9nF2(I$AQ zCyx1Ssduw4LAgzlRc@TDQq_gyI8JZqoI?{ETC8wR5e~>fp9uP+(5clN6F|>!{X<3? zYl`uvLkmU^>nt>K!(HPPh1TOYel1EJxF?A;l71tswhkv3bVZQX(IsACom4q(!pjti1-F7enzE~`XtP<~8dmo$ zaWCA_^Tu`evAS{THpOFcU!%K(=!>rC0_B{$QZ?dXD|R5Dyn1#euF#NM({x%fiEYjHBf<+x2)LgPeO!j8eub2+NU zHNCZ>yUVMN@snN^3o&vMZnpK{p2Sx2Q2nXbY~e^JOh zxI>^k4=>s0tNnS@Yq)=^&5M`PmW5OI@{x2)KGkghUf%op0jCj__vS-C@UQtygY>EX zYn6)FI{SOC#6#IXQLppCElSZiX*`Pbim|L@da&-`rA(FZzr{`*&W+9Op4_a3nz&!hEXb(NUKw-?C~TSXm%wtI`|QBG&^jnkCs(D8WPT6~My zG|xU>x31h`?$DEuN3W7rcv!ajM0Wtd;0_sEJ0+Ox1ZblF5kF*-4$y}ma!_fw%(rpU z4l?jZhJ2$ybQEcap6yPpC9B|^imn7Qt61Jq5cu)WUw`}cZ{Odx_jRc;np6O_DZ`F4 zQEr@x(v>UDl+L^S93pwdLPn2dD+O6bMN`jbC)mGF$qh`x*g6%KgSewA&zCDRzc_w z(i*Aeq#o`?j2>i42}p}Glmt&$B!Y}h#=51qeI{fI4Q{#F)T>)ebCE_mF0P^6gW`cU z&`?a{^s56QV~4buNYH}}F^iN}W6M14m#e|YBr9o$RFF}ReHARFN~*+($yjB6nXp9! z7MqcEOK#HxvW1`xF;qI3FhmFwyL8Z@#?7Z;h5m+ZgQVgn(MUv( zL6{sD%bL5nZZQd~1TiA0DBttK=`c)evdb-PK)I^v!Gw54N>sq4Dfq=ZlmaH4Pt}bQ zaiCggcx`nh=3yORhzKS&8SBHnB5|cbX+V$(OLzun1EHR!?{;o zKN*LK9mCS@?CUVG7xmbO(j(u$=)Oi;;JD-$4q>MkL$}x5CJYh6#Lj>_l(>1A6xtiK zlG86~qD^h1Rxl@x! zGn+J>nDj;WHPQkHF63!=#;8ZK-x)&$Fv&{ODG$BPRndHjIJ-9X8t+6>Yb6+tW%opk z+L4RPce*~>fr+O4(8GOA&|c#`aiT^=7>9`&!|pLHp$8Lw>qpfVx^{Gl_#ShrButJ= zdlwp>0h9e5pJ|8)CZ+96$MSF=;|Q3PCH&pK6zQJmIj&ZOk%$j;3{3PA{vMOA%51{< zR@L`r*CwQc)?pGM##K!hCiW5@yUWT4;+s081&&L50TVmD=+L`zyqYjX2op2?y{EUi zmN)<=h3W>am`nJ3VyCzgNFyB=%bHg3d*Z0La#Wb4xIc*0;n~Dy=-gvoN)IN~PpU*U zI>GPhi|)vU1?ZX{^-M2H#Pj(=!)t>>OddPH5D`pD6RwUVVajs^O!7JnS}~XK_n1*5 zjl_KPo;X%We_k_^ZVVDibR`%^N!F3FP0S_fp@h>*s;KR8=N?mM1V}hf zW>5^b!${{HF}0?EgtKb85)6Z++@PXt6JMy%1qmxjRZ+1c-wb^Jp6dZp|162Ck9-*< z_G&C9HymJx5F~aI*x@-DRjxtp6p)Z(Cy>}FU(8StMoL_XymlTG!vjgPEQxs-)i^BW z>q;;Tl0qj?AW3(3xbE8nNg)yJwT>z(@+}RL^gZ`2QvZqRFgTC)(k;4~q^ah_jFJQF z5P_sHz?30k-p>Iu2|4x-l0qg?5+we>pplM?WSZ=7Pm}>yjA9&YUe_zQs^@vJbQ~mh z080b}SEMdTEIi{nwFV^n_uRKgkQ|rpE_a(=aZmgUSDXXv5Q4~|#)M&g`?u8YQz?Dm#uL<+F!1+FZ`Ld4%045%Se*bxr*!f?*!xzs6sxb#gLkmPY+BYlX`PbYl{ddSHUKIJ%v zPmY_1-K7V(mIlA@ExT$K^Z5>#AqlWM`0b0M?ukDzW;kNPC|CC8rx^SwA}ZgH*+bPj z*1Y415CWgEI7kO=1uMR|r3ZYXt0-*&_)T&U({==OI7eq1I^|{%ZHSmimP-;8$hGSa6US+OuG65Pv?7pUe? zg7X8i@f__Cb#fULpX~G}k#yYnBj!9#RH{B_TnurKNk;+wy*||oDLkqfry*d+2p*KD zBqBm9bjAJq670{!Ixg5h{q_6T-^D2n#Ku;l#5|}Hap}+w6~;XENTAE(vi7^UE5H3- zJX6Uu&d0fPKF(8iGt`4q3kppgj!m3j)Fx3NMcoz-j$%wbTTx3qLfg4dXi*+`qqLX` z3r)C;sb@J{-uW$Fg97^BP<&MH5YPk0|6hrjr!}Wx;!g19PIE9^ncjy>7vzRc3go-jop0Kov z;?^))ea}Fp@VKCrJTE8-V#bg#5~l}s{WP8t8Zy;A<`b3afM3qk6=57IHmT?y(`R~6 z;f$IpE1KV%HF9Dl^l}h=NAcZze6u!=DJZXwA|0KoeJKRIa|DAskfxd!cfh|okS1s+ z#k%H@h&ZmYjtcTZ4!5Vo_rg@cr&?ktoF4qnj&L0&O^I;NBz6)*QG_cj8|4!nV%~g& zE95(9#ZE_JGKa7yzf`9}*2^|K5l%Pn-(!|hiEwldotj`h5u|lw!Yk(=o6>QQ={ z{>!&7zx@6M(!eD-nP*Trl(3;1-u7t6jGqIv5&3F4R!{kAhkrHitA%7$l-VS%;}n}a z@el?bCH7UU;*}0vcC z0X)JkPz-TwW-Jln#GYq{I5sm`YvtJ79ga;}Ji2qVb)sq4Ke>HGZRN+I650BGzubTL z@lSvH`#*k08DagvQu%GWT;6fO{2k|^ZV6#O;wr1W8sBI6a>fBZW$R-WtDj0)Oki{U zNQ@nK?4MX(LJ41s60UCHiQ$!uu=%2`vp?LGqgv;g znI~{%o+M@+VELg$is?u~HJp1iiE?ZsXgLPX?8G2mSB-RBshgyPRyXmTT~zCYOcRcq zr(jGdR<|mCJsyKOL1@-GY6lf^#HFmX_{6skiowb_b_^b7?m({NVz55VIX}FM=?T?@ zj%!EU^fq6n`G{L6J4LrmSc$KN{3CL><$c@MM-F#7-`}zCLP^|my{2%43=VkCm4*^+ z-u8UYsy^V?VLZ2MV)!d7h~c1cRjkTibj|pQ$sjWDJU9eJY{+0o4O?Q!e38?l=#0$= z6r8bJpkjD@k%^c~3g-kXHoPfi#Rm~k){pdTS8ZjMW!DAuCFsnukA%^@%mgCsCU-@W znn{pO2}obAIANr-wY8DL-1PwMwDF3mC%JwTEn*g1nP?eXZB#QQLnL?GkxbVfH4?;R z8EK&Gtp~VnlLf^Tu4i|1MATjw|@Z4~+IRYfPI03EX&Key$c4N9mGA8w5`wUfKu-II2r$MR0 zTrTVTDNo?n<>N7dQ)EwI&-t|)N6Z0ZeL>JfNO zx}w;I6Hh-1p=)n@)4T=pfED!ja3S7gZf&Jd|W zlMa>N=DK}}N$jejzD$(Tl!=?1a>sJv<7lmf1uriJC!&*bG?Z_fg>TIF5_th5ca4tH zs~lmpb|EV!hLw4V2uo^oufRwy#7%Y>L=B05~`IMuNhCFiIp-q{y2rs z!)r5yPSb7JTGgb)}+0%s{I+c_x#W zu~JSq(mJItS<4jG@_INS0+CV|aMBbFh8b7u2Q_%BXL@m>hZBGrq(_;^eq`09KdUCx?mwj1cyS(Vm|?ffGB5#cZ7}PApH_ z%X)jqwHkfQPx5jHMo)QY18->d;u77%b^TWd(rhZl1QzK_X+3lh;0v#1{V4au;FDb^ zjfsLK98b7e7zC0>v=9sv$0LG3o+1$h>}6<7B@u=r+RZVBYl=?k;jW(+>oCsQ!|msT zK)FUmLo-Y&J!mr8!9zwsvq>g9Ehb<8@f|I>|BkCop>nJ}h;}of{?k$xquc(t>0uAW;|WtlKq2ilK#4ho2LPpT%8H&F2@)O0bx;8m;{Hrn>T&(7W_+w4F~O<=#hQWU zfl@9{Q67`Jj9lIDCQrz36=OZg#5%lXfaIsSZ$3a9k*}8KDy5AM?<{Y8PShg`x2!0$ z_0d9I3t(0^&t7E6RdjDM^HbNhE zM@!6-`ouj16!uVWL`erVHdOQU)dcHD%#{*wV$7xI%+>B9U=^)!6ti~N95VLx`RS0AG zJRpEQ!)E0kQ&J?XQV@S?lwPi|eiWvP8f{F$tLS=Wz6{VMYo?_!UWpV_n0n~~BQ*&_ zG%Mhk^WfRpdwjEr6ccV<9YwRKjagU1orp1Zlsnb->q2UPi#dQAIMD!TeBH%ln2|n4 z_G&Wr__DaNzMTm(Wx4-h67`_0@obeSGlv+i?cW=rBSG2}tp`OI4gf^q(#AQ(eWV@e zoY?(0A^_apkf_U0bBb^Fabke?2BdQO@nHw`ivxjE>eqyA^fCrKQ>Td2k(KPUr@o{| z`jq2>6!9ab`ju&B!pnjfknN@2jC{Jg7@CLyh3W?QnC0lxIHl-BB$q(|#)d;eC#^#d z_ccL@;ECPV^Z?ciDfY+v;~+b@j+qCZV)jlKI#%ZN)6dBLkkPBbCl-b~csB+(Z_s(b zDdyU|I*`j7W-#P#NTHC}sS$k)Pc0|Lt?An|b}N`Nd--}Q%ocnyy9(*OuF`WnnDICr z4}{Al+v#{fZ}VlEUV3B!0d|OBVzYlvo$HE>*E~vF8FNa64Ty>}IQn=-Ur&Wj(lq6tV$xH^3aiG{@r{{6V};wo=9rvfrkquc z>B>Y3s#wVx9~5JAuG1>d^@bUiOUvOH+m<@N-#$*If#jo{gjMQf``!|*mQy;J0|2I% zvIj-+2X2q>U?-dLZA5w919!v|=ty4Wnm8!8NuOfgSg@NlXiu7(#U>w_7|RTZnbkl% z3pFt&Qh{q>iNt4|KSm<#q2<^g?#fZ{$Ga7}x(P$!hC^%$3<|MAi4>Deq-s)5s64@o z3xR4N(bsQZb~XF}yM+_g0V>fUiR;9sn4CKM)HHpch=lc&I;t%)D)aD-2`b*5Gj__m zj=~KhKr|MTP`KHykDc=Eyu4#SGM$LD-M06S$4)t^uN_lNRuD0V$VW=Gh&j4_?iABw zMxKdOO(SBDBrZsYo~bwfPVv1gL(go2NlZu?dS;Wd#0$mS;zrg#j z=qq<9^%rkGzR;8R`}coIkHtai>!pU|d?2`zb(~Uj;*^@F0zlhfK&O&xQNqoI5}Xse zVze9;&R4hdW4^LUHK%d5%ZamHX0-$N#1R>AveXy}m}pJKtqXaPGodLxjR{qm`z|=} zBno6Xvpyv@->CJ-$;&iWSua`DO;t5!z2p$3&O%;KbPZ1v{Sh4y4Yb~O$h*N>q%WLp zFzXJdG_rP%OYd;SDS#p7lp3IyNQHpmCA)Hak3)h}&WG$o%Rj0k+H1e;SO*qm&F4ZZg>9Zkm@;c3ZUE!xAx z>4V3DVnKwH*)%Dud`mYHuWn&nU2JdQaKxg#|R$%U5AsygF<)yr;;AU7yd-9Vn}G1hv#?uho|B|qa_WK#&u{|V(N?ZW}a1zMJTVm8J$Y#`j>O)5;IAvN2m4s5pyW(6(lvZ9-X)ju6;Ul z=t6W9C2Hy_LBoL>eEP2YCSeESyQs&G#J4u8tP8O@8n;OAaK$;m77{FirIq#8$TW0W z&J#GfhZYm#6rx2?h zdm(rfUR?VH!3}8|1?hF4sV!U~dgj*_2yNy525rF@zd0*S9LdthHKwS%V9hSLW6Wj~ zEir{hOtgZ>GbVA+mijvhhTX;!d zbRAQgAk|{jGhab`^5Pb%pw&MV}^!6gP>_l2BfCy6JIP; zV8HJb>Q-0ekuj$_Nc$ zp-Yzrr3k!S79WTT)OdNKg1GkS&AP-YQKF)k(K=yK>mppvNwcIqd-axA=i$PA(c+Bo z4rWXvv7>BbufsZ&zIp5v+TOYp#*o&^K2f{gA&tbh?Z+VO<*R5zqjoDT&Hf5knlecc zpJ~ur#^IAw79~<*Znz?K;bSG3=-^r2YL}UMUra@j21x;VP!F9+X(~&(ssqDCgC_hb19j8_;f?73)ntE8RvQc`4>!QU*#1b3ymSNP`*9a|% zVdIL_Lyh1c6`cV!>9%D(_%MLwW(|xQ>4WoFbjj#vhED&Y^l1OmuL(O{1k?o&s%YfZ z39`_ippm*vvuPqrdarB816(VDKiR7V-%ycf0{#TqJ>e{E<;rmB*}hi(fW_+>=W(A{ zC*OK~N0g-1>x#=@+2WN}2PvJ-;T6e2p$jcGmvo7#agUa$KY#t@%WvO)&3LmXKlBqT zea0-ZPh2jet8t~Vp;}e1Zbye>a|Y@X6Q&#uaO%`TOf~8WqwB|UKzZ8&pmdX18?LWC z_ulTcs;cO#MG2d6_dFw+D5R(l3iYGMN8kDOknatF z0B}+(dZo3Pc_N}b@s?W2&+ji|K8ys!G~R7ibMyPqy3?+T6MjTcnwJ z6aT}NKpHE}C{KyuEf$f3YwLuGQ}JU?o9g;Y=*N8T+UCG4QLWh-wK z*@#v|n5asFq*U(^gTO!Rg;`A8I>0U(HNr4yHQf4aIB6Y$nwmS=clj*iCox|I5@tBBAgMKoMnsP9k7AZ82@L!@D4ayZ1JCJD9Bej;8o>d%n zLM4&I+PCH`F`MoHhb{D^m4@HW$uaxgA}T%Qt1ooHC`R!4w7w%z%x7xtw@)R%N?kBC zC-2Lf%3CCrw&RScLZWoQDKK@gD1&Pb;*!4TPLaVI%81}gW3smDj2vz zNtm%U+fyR7DJV(`4c&N&@AavOOR;_HF`grkM(Cd~{nemBg*?}nMwj-nf@z?fd- zTI>Kd$-t!aloIVmPaU#Y-mD-F+@R@X{#hRo;%KeKf4Rim1YGEUC{Q33P2#fpgf2Ud-AnkeM)w zgp9IEE~IJ|gasWw8nAOj+KULXke(yF=3PdGFgFb zjNcOwS4H!X$!?AWaV3hGmH>BqYK9n zIE-6FnrPi7rr30WV`Z4Qbm-#^3loKxm|Mg%be!{TXaJAZyKXq>J5uPY((ArElE?L9 z=|#_EyTQ_SU;p5GpG*k^ zeEp6quu~s94krnxU&;l*@&5>0=zmTG3;m_k#mL!7HY5dtR`)g016JKMxmn%>HRXbehJoT+emL(FGM zh(;71W&YQ|0-8Uu*|p1r*<`U1r$2SQC4>yGeUx@vEmDZ$E@Uip12XB-AN{5ZYu9iYT*suW3SE`l9Q{(u>kmc8C|jsdHkBPdVUWigp}5PU7-hIpJCw z{=&DCy_$7udYh}Z1Ln%Yx`Te~3?e?s!m6uPcN;) zsMty>SGcM1$2Us}I~2yt0i_(QOYJb5-r>f!1I!`8qU>RBtR*s?FH_F7XTVZZ5K?h7 z-N&pUX{6_iNnX!!!#G)E)>N?$r%83g#yD7Dk>mzd&5df4}Qn2>!4y`>O2-}GX2cZ z>0gu{*}Z;E=*QBF(o}Yc7oo0`i=+6I10Fs(jvj9GAK+RV{=&CXmAaX!I-JCL_!r_K zv|?uvo#PXXE!^tGBgmg$nBvgL_p&bCsSg|5!Y$`7AZ^a!6=5!G8Y8v~6p4A_qD;6d zr`@cDrZzI{c2;VDImEtMi8_?Q(vu)a4nP@s+?l-EXf?o>@C;5%>4HzF%VCgwx3wsJzZjNz>bWnQ{!|tVLj%S?y~(i);{c%W*jRS^2VfG6kA%v;2E`ym?5j720Al}U2iq*b)fm@W`XoofP$ zQ?)@Xf-UUKF<5C%nlQjdmWSP+yJIWjmFriK=0f5&H9B!r?99mCqj zJQT?d_XPfIvog+cpS6G@zdeR~fSwhxl#Vnl3{RI0sa(qgKyOy0e)QJw!zrOakyt&0 zk_ZE#He@vb-rSR{fXX|MH? zu+{AVD3*J**0W6yILGt=X(TH_eGN9)VNroHCd5?hJQK2q@j_Q4G{%W9v+3a_-9Mve zSrp1Ox9}WaR3j`<7!O11*xPJ6LP~2a(P1m?THdQ6&!^V8q;gK) zmu4(daU((8$}f#{Tr5KymV!yA?}}3UGhJtM^t4hMr?g06#?^(1+_&k%}7$VxEV9iC`DZ83{YD$|pNyf(#R) z?HmCUwl#JxJOeG#9u{1O>A{333rbYLVplIxYr5+T!bqYizZm{Sm^Afmqn;-CeN%Y^}x>>7I1;lx^IlLe#ynGj z6lu1QpW_yC7mQYx2Ons*AIE7gXQE>$fWPbm_A+#c@p2{#&!8mZV6^9mm{-sPBM~)} zs4zrR#2ja}oYO5S$E5J!=w%|Ca{TxvmJ$^w3=x6RX19>pKNF0Fa0D>g31rM-@W3eA z=u7Z~4>T3J^hI}oA|Mk9M)}oiV8o-~26ieC6N-u(2;xu~peM{Q9!vMC^33>9@-7~o zz=->I(iykPa#T$i$tg-?X{6&pTOL}bwJ`;7zH@6A*onP zKwE0Q1Qwg37~k{LjF^A^_T^u`efj$B``gF+$H(@=kN@`Dm+yajyT9M=%MU;P`Ri}L z{_Q)?`+8qb>*KJ@({_GGO_@mj*X1MGFp>JH#T#X#++mtN+77-<5T^MLO(||tN&_~^v>}uIytT(1;F%8j31Q_;1KF2#fWci ziVhK6+nU@_(>sYoR4zme3bJuvi12W^laaCbBN{CfUX2V zh{cggANA1Bv&~02zr;s5bMiFp`6W5sD@}LXWlb0sIk&BQE4bN z{I)1JPD43!8cG!dKHk(F$7v{MPD82BeAO9j8VbFmYj{pW=}Hm=8NqqTI@443Ileig zx3uCklqxEE-flAIksn2}b5Vp4uu}{tuhL8wUmax~|>20oxa{4xdz{);3 zJM5EjrpTG@VL2ppHcUc#xUY#k7jr^F*H6Y_Vpf5+MDlP&>cC{$=2EUT{T@l8HEYyz;Kvdcdl`|Fi9zt(iUK%S2SD9&5;&J6M%Z=%P`5V zAydw6=$e)pLxeENd8d{sE>KLvguD+PfP)atId)5HXV({mk%$d*h>_59s5B8%iYiP} z+#eKS7$&8Dpheolf~%SyOgP7-A{sE!4NBWM$7SOjm-@_CVPbP!=pF8;4=_Xo6Px6+ zCB~R9(>zQH(F;dHnwz~_OtXFfY(Ho0W$P#Wf~qp>5*W9P&jhSUMr$rB5( zV^6#>x4>n*~eaq`0A78tK4Trgv4hn3Gej zW32+bRAd|`b_|OdC_R{PQc6WMV4~}gTTCz!7T`RF`b=;5z1hSjmuwyOLY_@HFRCkv zG(-fG!fenm`Q&vyd0$$vlzlrTLENnF^b~CTYL}`dP;GPxl)gBILQdbQ&wSYj?B!WZ z@+n{>@`;)VJV~Sv_}7MHoi%YPW-xTYXoW5qLiayzr@iQ1F3=x9S zPOQ?~Twk59MQ`Ncd-Z@+-1tJwm+-*I+xDxy-~)QAa*I3eML;IH%^(DN0l75dtI!iO zmI@d-GpUYl8jPi5L5mcw2YX;7nt@Up4AB%Z(-&P&lZH4lfh77$>vls4`CP$4M0Ao`sjY%k;oxR65%jDDt<+{YMQ;Fd8E(PQ8$l?KspJd3(QJ?vK96<#xh3(neq8x%Nfo z-iN)nk+yf!Mo)DOI%sYGT{$NR#fQ=>xs}r%klfY3peb|z3!9g@#q5)eTKG7gK&M?R ze+Tun$@F6)RnYl3s(r8X5i<+K%=tdN67d^oQb9c?S_p)5w#8GjwVi4(yp|*G=FjsT zk%pAgJ?9MOyXAd(9L2qrMt?2R9&lW|NYxyd6(YDHs=;YsIq;D%V(1+bf|UZU444R7 z%AsU&LUA(IDb1N(L|b~BtGOKOHcn9+^rzg%x78Nr%_i%hB06rnXQqL8;==VF+B!q) zYT}p7yR~TXA|14oV@xaQr4F2FQjKP?SGJfgBGF^z=pL$L3*b66bMo6#tGn-#CBCH0 z;w3{!m`F<$&VqK1aCLhGOpRFE$lSM=Lp7|Fw8={|VL?SvGBt+S1H50Ddx*)Oq2uW9yZl+?=AWGPRVA25V zMtqfa)bTPPqh!JmAxiA@H|D_|kaZRI542)Vu$P$Z;uO#?oa9pN1s~8=Ir`#|4_r9O zr9Sh;NJ+zL7UlE~SDZ3qC2rInp>Xq6R}vX1{YHqaGwqg_m|@ZZ7Lgx^lx+y7U1n$iM6Z=92RgGkdZRT=<1MU2hx*W6mCA)^bUG`>uw1U?c{JiW@M5 z-VL~n(`7E4F4G-;oSLjOyf!Khv1Vor5rNUB&CuIiOB?~lJR?IZc9kYRvF%?c_VL=C zq#uX2_SW|kjzcM{MA~aeRmVKFW3sB7x3nbQ|Fs@gkdw^)cQnJLNMxr4m7U+wvNrN4_`qYsCWSmWceM?w5RV&`!z`+5-K;u5lh(%W@^uRq%aH`74dg zh=yUY_y8N|XvmFVQ-Ut>eMvbQUN~W^>lZXv7MK{~veD-yV0%Q*=q-FZ3d9^Drc4@WbB_J`(RoubqK--HCFEc_~BB zY^L#;bo7ZC1~7}E=g0{c0a#ZWA=F9~RHf3Gj3fdcXC76rF|HiF#LT08_`8j)kUSDT zg%9R=qAt4KN1Fh>AoK*R{a3W4sv!|FKnv2$*OVUA`WW@9r z;r(>+iBb*BzbCbCU{v7W@ig?@p|u5Hy+iugrbAN5Qxc=W(xb3-oa z9j-Vf^O`V(1dFmnUOsE*2>W-+!ejz0m9Ij>ZzE~U;E+Z-uBhqs`CdBh=Q*J9`*aQj ziyhD6F8v-@h&Q7g8~xW!J{R+bq#e>^pq}|ESnMwROUyH>V0mVxB%@XY7MpBziEl?b z0xY$rX>*G%G3iGbiEd0e#7OA1R$(LNI2G#<#bZzeX`Nsnu25aHNW1Rb=-P#eo0M3x>*IJc#W3Yh4wnM?YjJ91$G&bg@1e04UlIVzV9 z(_-Eb5ljjTLpw*flsjNHDV(O2hHt=R$M2w#j*De*Or?qV+MW``h{AI8Y+~P8a*25m zU6`ayPKgTkN3(S@&cC^UG*d5zorj6NoKNp?9pV5(gfOww-(+@Qruo^VT&)C{5VU4^8 zMdB$aQFo4se#>4(8ni>AldDI1qwhh{W^!C&%FzLKh@fb5J1#L-%}VRKzCqe4P%NZ0 zcxPtpDX2# z1BRa2EUIJL(kCbq4N2%+g7z{d(PD-lG6cGK`c2i%QAw0jO`0{$?1f8w|I@uEq&=_~jgPk{p%$}4ja!8T-xg^88 zwkX*Xzr|)yJ0*Lnozq#xdLeU-Sq<46fa`zy{n!8bPh4`Da)~QvIaG<#fu_{bA%{s{ zcZbQ>GiU;P)P3rcUqmAf&hUhA@sRWAE9WY8B?&;eZ%o$<6|?3VGsK*R&U)rbXI?e= z4jKZqA;KI=8iEz|y2I~5(&j8&V}{QGhLDg{mN1KgWOmnA=OLLr9CAy!cur92N)jOO^nO|%2qc}g2%Onay>gC2 z6%~d^O>OA;(Blu+SkfFv21H_mv|}F%h!f_JI8^p9w^gs{ZLY^2fJ5^09f#$hKxL#M zDhbVp#Di!9uwK*PrsvU$(v_GE>Y148B z#ng)4Oh}#YN>mg%KF~$}MqN-!1?ewb6$mJD0!0P+%N8*^)UF+Fz`R8y6qQNL3LDwq zRnZYp%v147!#A8Ftf`1K3%?qo8VMfINl`F9*lvllM_>@>RF*v=$S8_HyUF0$- z$v70voxp3{px=Wczb%9tVn5wg&xbL)LV_^)%Af{%d8vKj$2Go{rlcnmwunGzUjcGW zZ}a^l?|HqxRLoAk&xeH>q9SE#1a~D6Amro(ZhKufyvE(@!tlH+w?6Y#5SFHPJ;Xf# zxAS*Kc2THTWd^7 z6`;Hbz#6n1QJ+^^X&z2X{}hFC`55uY&K_g_fQ&~Bu~*{m`~b1^*2OhuNQ^wPNx5V4 z%f20eO7mZT`~AODnVD@LRXJnRQPmp<++Pc!09#x5sSq6614OUm6oV_L7z_&Jk=L3$ zXoz8Otx!6pM-V=_R7EnxQNufKfx?c1*q zVt*FPC#nGv6Pg?)ebF7Cm>PMXe|=_*Uk@%TjjF8+q<6UDl&O)(B!lV>U?V7oS!c~` zJ;gYhYb8Vu3oDC3NSD`yH(WWtp$^mY$`F+7 zK5$Y+8z4Hhw4WpJ5Mv}%&Juo5v(13g>Vmq}b>#zhH`6SmN}6voIo zyjsN#Q5?&tYQh!l1< z(Y3UeYLUX7$sSUOIH5!ZQkr~soMdw4B$G)Cpe@%<+lI&{*9k*tm zCxTo#5u`rzH4Cg)n+QVh=*O#+77Y4=G(-dwn+ZZ@_hmW&CN;||R0IW+@jFPaoOm*8 zB;CwT5BD{ZcMyq1@sf4?Qwpv&W91rOx$#JVWp^RJf}?5)g|nta6<;jUMWN-Vsk>Dh zc>kiSSdlJpl0_B$WfVR<&xu0eWL@%y35$n#w6p&XNogLBg@}YGWl!cYzd@Lb^BZQ( zrP*WGn7>f0LsX5U@mMGf%9Drq3Qe~!zi*^!2|#J;yD?9ohez)>xu&`9^sMH*VLn8ic{XHnOSWShW0eFP-V2!ji-^E2vI%l#qimz zwmQxBd4A*_6kQQy_7cpGr4L`K<`!RP(gDnkvnHyjz@=Vo(HDm%up7VDq(1XS3()YI z<6{o_)nw~vLq#**BWTmw*3Fr7DU{WUYtYR&}EpaMbtvI>Tjq@d{z2F0S5k+5g zAJ2p4jWZwWGd<0Bf+h{i@PU1I&lM;8z>VMS(v?K|fDgv3v(^A%oa%zns^-BEdQEqW zNe|Kx&p@DS2s4Mg#RQ-O3=x7cgF=VJcpjb3A+v{L&Z6Nr?n(2Z@16#!yz8FaJ}-0^ zT;Fke>Z7}0{n*~o5=bXxgEsxXCLN|e658@idxSYhN3Fhg8kDP$QTD0-TB!Ay!}IMT z_*DKiqp-nC+@YTX7E&*0nfN~{4NH76W%-yGGW1NHmD0;yTh4bK(5|I0DecyxiwQ%| zTSG*Q!_T>u!#VQ!7T=v9!UMlR;VJiPnX3&&<(3}qYOTZt;`b=SdQ(LZpil4j2T6`} z(17xmcnGdYQZ>g#PPCG6`asi2ejB<4Lu`zwKSvR*Sipz~tT&lLvPl}5Y zu5W0FXig`jzZk;}r?}LIdwAwES{qVU8fOdRA&Se5)1SJM$jBn`qD+zs0_|KD=6(m@ zg?xO+3#qt~V4RL}<8+j+YGNZbrxVh{U1uqH;oOI=2+}%1VouzhMl~@zq?eczn?XT4 zolc0p5p?SyGs1>3dMI?@2wg%3Fe~?e6*V^h;}%~Qa)4bz)Yxl>Ax_H7EJL22P-D+? zF*`vRi6|fB2R9=A{P5#zM$%g^x0o7JjKf(mT?vLkV>1+P@s%At&`_JPQr6xiyf@S~ z8Ft(aF0H^4v#t)}*tOCa+X*J7%p72i2r71i_AMqlSrg2lc1l847){WMosh*e0%0U# zgedF329+isiVA$Ju{M;lFu&jgv%Q&F%_=d4MOuOFU37KO zgNl7g$1SG19AJ$IDmLHc7W1GEfJz}etu*{LfW$lnX{6)g8Hyy@S@wr_b1{BBN7qlr zv0_iSQ`d9ch=|c(1vL^i@|b)gEx?v;-Qo9OVqedJV+%gn5D`oYdqt^e=z0#3d<|-+ z%qZz{n6$(%Y#Jp&Vj#Hg>Frn$-K6yaeURd9oSJjv)SSUldKHBAW)myu9qwpLsN{5$ zt|Y@?v?)5bnEueq@pw0W6%{$2Ze+NnFS;WaU`(W;y*`h2+4{`aG6xvK2|*)WFSiLp zgkZFjt0CUc3>fPrAU9HRvq(<0Jl9ATAF{h{=fP5xx5xO9>-&0N3#B*LKU2XlFD6k6 zDFjDKk15ua4Tu}fYx`f64Gk54hNw>P#r+A^BmrM<($kpW6l@ke7Y&6#u+d-&VcF4#Szd} zI8mZRSr0{CV&xDY1SJcUvFf>`C^X=>2s$oPyYI-UTek^4$~dMJX?8Ht+x#d(JFhp+ zC>zvaNh-)QcTg9WhSo}$YDq6=Ww${w+*!X|PpYNaU*YS6dWLR9gc^if;OnwfO|+vX z(8r{a-lWet3{_`VzexI`J91%%a`VR_lYU_(C_}^yt{QZnS&d0UNU%stlr3{5Q%vtG z3)LC00!Vn=?>q4{ zPNtbO5-My8Ch6h6Ci0xZuT$xY5W<8}GH*7C=9YTuU6Fb);RJ&!DqxcC_11$Alb)B} z^nd^I?brX~|NF;3{?Gjw6D%6^8^h(|OK-$$u5Tz3qAsFewmbi^iWI{`El9sH%urq& z9?D?)HDODhNC${KHkb|dB3xyA+zQ2Ly%pXEKIK@0Pj)1Xd3rSxTB}2EZj@Nlkx+om z?ll;?GG`{oE<818#XNTY7Bf?XF*!Y@V%t+qY)q`;l~Z-N2{{td%_f8XB&`!9428C! zMGE_pdvGEKgc23KIqMrLcx`V|OvicYWinbc!ElP9bMuDR5DwB8-7qK}EV-ys*=r8A z-UpK58z=IY#HmRN}l6TYV-1EYLrMK(*jn9q2?f}(c!((v0tN_=05G+c_fhZqip zUJb)=Rx8%wT%B%k8jlRQJ}E`IPjPkboU2nsMP#_Yq2`ArV(mrJZw*fY;0C#K>P~&d zi%6o;HvU2HaDjb*T>{i(9X6=xoCG+(vLIF(YF1grT7r8b;ka6nMq;PhO&SRWH0>!Z2zIJ4A~V0=b78-qv45{pn{^VDxK-7j<`{tsDds;zBfGG$#L4m>joQ zV=394l-9_U%TjCENkUg+s|dj}Kfa@qg? z`hg1}lMZ)YXx>Urd#|-_Mw!#PICMdQzUb=x6WcPdmjH<5LLRnTpe2;olwZa?2svUa|HrY%r#IB<^kc&;Actn0 z)`I_>YgemIpLagz+EpkcS5~yJVahFaV9g`Y!S;sAE=s!k$s-dWX1d)__LEVLLS+ag zA=HB4(pb;wSL}cHN4OWZPEg^7;2Dr8V*`CC)1W+w3Kjk#uP9KMQ8K4HDqNX`!uf?I z_rizz>1Q0Xo%kI3!Fxr{eej6e)pzvV&c50gZDVHT{2tS62Em^va-Rn8xIN`SKbSE zL18xLaY)s$f_qmko}3t}xxOKdf3^({D2Cg?#(E54+$pL5gj!|Bsr2eZoIMS6V`0uX z@0=|+sL~Py$Wixf$|fh><2HY3*)%iLnr~1Hw;%2Xi5dgN>38;f&WGws5%|4m!6G%Z z!7?Uh^wwsH=c6HtNuo*c#tA`pVoe-lgnJ=g0QDLyVY*eoGGPk|7G(^(%$eTysbI;e zC%|IQ5OEv601Ll^qwA(|7zA>gE}wf$lqv?|gs83*!(g%demd z#q1#omISv$f*R&CfL9x6D4KC{QP*3>!D0uq)VAx|rw0}yawvrXEX~R@=CDX3u&Q5o z_;JKq@6?st)NW$>OO0CAy2FF5G(?1y!hX@t5iZUT$iavRx94C)6Kb?F&b+yE=FOy$ zF!?v_e)N3TEsKFTX{0NKG?3pibuVYgDf`52@Li->zJxVwlktpmeD0j%(;a>nDLK`^ zq)L5UdiXiB#8MMBChf4btbKa3yF zDMzmb#$s9k8Jo9pkDK9pkRggpGr1s)sgcK2Az=j03z{^-O2b1Y*7SAVV^&cOUujBK z101tO1QwfGbno1ebO2cLUJ?{z-V=6@`5Mwr$F1?H_e{IYP&DIYqOP|L&o4G_;~w`* zcfn%$Qab)ik9_~4JF`k79G4EmC$e{7hR_lXWyKS=2*F~f#p!LXmJUe93Xuzn$rG>A zO5D;f>_lvr!8j7<0hsWR{%BFc=FAe!IGN};u-L8r_xLiJ9$1KNp%ezNG%MQ&3tF)k`0=r9Rg;ZAk_R2WZNd1-l8&W5;`Lol$AP1_m! zzfuObf1;4Me=1ZMZJRK%iaj=dqfb>2AkidS>5Fb;{KTC^EQ~PF94vWLEb^VU=P$mJ zX%`bv#SwoWZ==bu>l3uc0b?lb$Fv`*eXHh?x~GsVV1lR}832QNOH8K7A?knm)@ET~ zC}CqZickp=msC7O_e9g(8MC}T!P1zL)_b{m2vJdwaU68S*~w!ptRrez&<_oZLqk?B z&+pAiUb8}Jk`j`gKN91^jR$Os#8*>XQ^W0vv-cIWWpI^5=4ZQNlpKZbrx}zWNa*?D zB<#JYKCuCj~dF>%zkcZ)EPl%?$rHkZOyo%k~546H6p_}-GBJ;&tHH0 z^>5$blpgK*Jtk|FT%6yjbRSb81@7XP*irdNAZarkvudy3O>{54;@7E#m1(|Y8iYgaroB?^N`=TmzdYx3|6 zw+JIW#T@+c>xPhEQI@b4uK8_l&^ZDu$-xs?axSKf6w@0-*dtoVAx1*GCLKwor5RYe zo%bY)ACP@z|f?QI9KaisK4Dgz82`!9Ju4ma#l&t7`Emq~>wecan z!}rRZcTE@~LJBEObxpuI!mQ^Aq$Ka&7b&FTMuH*s396?5^!u;>^PgY8eTR$g4@+6c7MlVQME9+yD&zDbyx)@BuqIO?q~MwK6eA<#`TgU3tM0Xz z@(S54E1{jaW71FdgJzBrWS=4X|RX@57{O zxtp z*ZrUV^ryf7<7c$OEcfdpRl(&%$*TK_(G<;6GC~MygH4!+J%Y`jzy9*&w{O297GQ4Y z*DKYu46kca@%VbBPfS5}D%MB2WC^n#CLrbNskzFMGe1-Q z2%K4Njwdx3+8M6~2%bz2WWfvz%YFU!WsfaCKqX;-JwPSGWtB?o)qmoUxuNd}m(BH` zJo?rs!|a{&hhXna7zNWIpb9XlU^hbBffUcQu?@Sd$=boo#^W!YaW2KCn8t6Emin4{i1ns9Otx1%I@4>+nj*GIIIg=mmgEvosL^T=oi*bLoOUo zM}};ILrhH)f>Y%J3$E+b^HR*Ycr481Zk6+N)v%5ihf|EdFnfp`i4=lpU z-8N{T-+#)PL9>Gzb2r@KK8lnN$-tp#lo=KpTfV^KVev32IW|h*`|`8g+jD!nztL%R zDt1iMI3~pOPN+lwqLMP^S_t1J21p9H57FS;jr$FALe3xFGn@U_n0oUGHb#3N#7hXq zwYJ;-LndsYvkpyuT94t{H80a5(#}JrPdlY+?Q+elmbDumU}VOmnh=d51-8nx2qTrz z|HbM;GY&ekw4h;Zk@$Edh7XOQkGF|e@$Tr*Gn;}}d_U4B;Dg$;fr^|pViOcUrIIJ9-|T=ZO%k2LySC zbkMBskz0*+p`RF0qAyoaa@l#c>EKPY8(=3Ko6guKZ}vawb)Ts$Wb1o-K`kKOA6vep z7DHPxTOpCD&nL#C8&woVR{Uw6mGH?dVV)8f$JGiiAf6b-W>gHXfY|Iz(!*Uja^2xX zbfiHv1+FYgWu$}FIH{T66{)kdJu!;OLtAMlP|hLrMfWw*0%_4VIQ$qf=TqS-C~;zH z*8zr*Fp-uhoitjeeeQHslvlM=Fv-&tU}CPTPvhj86EQ!IF_K;?(ZhXBXLfybqI8yW7}U*5?YI zPcb_|;DxA3gL>%UrH~f1XT+IK30$vRB*IH+##A!JWV|Erk|z^bBbN%41mi556Jyyt zJiyE1V*F1A3fACBEmykY42Rci}Xcz2yAkidzZBV;S8{bBK}9WzlS0pJMt|HITIy@TRzNq}X9B zCL~E0J}yEoR#yb@P*nvX8NA1;b0fIJ~UQz-32}s(nFJV#)KCELB?`< zj4Bqut35UdjEONNI5AF4UxFYwt?w(V3)|$FQ))|hjUzNez1U zHV9bHNVM^*!bYd9EvIS3IvI48VWgC{G$m3@i0C1O_#aACAZ2~Sf~MVSPG5J!rZ5GO z7Sxkp#Y-Awn-HaUxZ+g2q||><5^0VIFQmFQu7~TbBk+=oMCTK!xRD^HU`Qh!7u1^L zFistkK?V!myYrHD1g}X3v!nyVVZvor7c7<|0W4QsCLZ-1!~_s&5LQl{3}En&>?t~8 z?CKtH~2sJig=ajs_HOv7S8hQG@_Al2T zDXqlZ3t=R>{p1+bGz}6XH_O1ZscJbW!Z2!xr_uzJbk&DzoGxlufxM<}ZS!A{~vyC&*kho~zG=IvcT=Tl7G5q4nLxjbeuZ}`2$ zjHuu3sE2xrh>E2~=Sdo$*xTmeKmuwU)YUsM1)@?%kcEMud8nnaH8`e-*D#-CK@kVCexzmTbo!I3j5A@(A z=Qpg#^czAHygHmF>czAq=~hRUGU|bgq)IH$&P&;0m6|vuvXPIyd0z|TfxQ+;p6$yt zU*1Rn+qFW&Z;J`iPW@Viw;K zs3}?JV$;T!aeB_e={eQ>uNeuaC}2gTl#!nA`b#kov6&7|IY|wM&x8=};>CjYPd_97 z*CXG*=#E?><-ixW4$gNcKW&hU zV{@qJ3N@5+V)|77f>wU`T~7Pbr}D3hO*vc=o5ockZ--piy-*K50z1t@EvBx`_d`;m zEUZJWibBGfMg%269m-+H87T|VB`BmVg)#P8U~Pix5EEr#*9&1xxq9qLAZslmbw9r^ zAN8oxC8?kR(h_&ri)csmnnN^$w#WT6qi611HCrXA9}i=&+KrZ&{ZvBdgt}$ul=?31 z$l4Rb)k_YY3+JhIU4T-s60!^32d#$_B>nGz#v4co_-AWHB zr5Ai}46mY=PB0ZF(s$j#3#1Sc=MW>Dmva4z7H4OfxZ;#Kg;T-m06lk*wuq2IdTW*l z9by=jM`z}gas?5zl1VmNE8zx(G}3Y5thupg>D)b%k;3jCdAQ%0bwq+C!&qVHXw`(v z$u3eXQ1AB=U?rd@)R>eZjgabwMY= z)p1hN!bwTpAQI0h%|TxDa9LnKpvW)>%3nk$GE0NGbdgjoz@>c0l%3cwJ{2ew&lV942X`1H)Ofa}bl9%8XCs z8|8j4%_)3;$0j;0F{P)A6bsZqir%eWVt$CI5V7ky9#blwnVOT8YsD;>SEbhnODr+j zNcvBj!3K@ji?oGkbSC}5vGFN0mz5?G)2SPTCtOPdU-*`t*>^~E^HU_T3wNy$(W{vv zW8#Z29A{ZfMrXt$%uC8Z9bQF_%tWsmbf4i_vQRFxNK^SFu{V^c=X}~X>!Qa_CSy`o3BnVG&`9CCAU5S?NpJJfJCBsQ5s+2fASEWd2&8bv%PdmXHzYF3 zEz|RTWfY`XQ;+8=!$>JK3oTMu)auPMoD@?P2Bb9kLCkECMqsVr^_kxAdq}ZKElW(q zsz`a=5D`*HX^o#ICY|pb^GK;1Wi1N(%KssLkwQzjT3)#6bNX*+1YoKx$jjC#6J)m$9Q`>@F z@=;Cy<-dOUkFUlqIhI7{g?KM-?M-e=%&T(RtQStPssg6#r}VJHBC%8@JFZ%@ zJBUaHO~SCo)CleuE2lTI1_Nq_^{h(KB+i!9_>owbKAkHJp{-VAJ)eFt}`wGaN|; zE%0L6f-_I9oWW2<#EMi+lk~l@D8p}}sgI0tUbXiSE{)n;9+L#&H+ z!VWDInISSHv?2C9z*W%!P%K0NK+&ud63l%Gj02^+-pLa(u{0h#Q2O+ay3Qj9*lxIA zFQ)*P4ObRFNEbP7?&(aqfk)|goDi{cLPQ-R64@qSf0+sLCrl*q9)HCU6_-$63p8da79K~8{!?l~G?7n^A81NiNxAR5BQTuMHHE!1z zs3B^MLNid)Bxf->=@VCINQKTcqCI#v$;vo;VV8tZFtLn@^rReHGZM>$(0vdf?d z!@-l-E_%OYO|9atNYV|gJPvN4Yuszh!|;Nq=)o@l#$D0P938jMi{MH0nL$NgO!FJ!ZTy5gn8k1ZEY6Qups1|PBWLD{muAV+Icp~Y93i7#Hm@QP$ zimzc2aO2moOlF>#leHW}nWx~26m@%^JRCf2uE-i+q9WbE4xUJ9^)SQbyB9o(p{@cQ zysVj`$4MtEF(TA6JyrICXT2a~EgSrL+#_u^>x6lu;7RIhf@gZ03(1ms67%dSJ(b{D z=_gs)tpxE##vibPSHW{d*gTcsiDMJ)Yz1GMI+UejN{v8`pwtvJ!>yalDt*zl)Mo}y zZ1=B)AcIAf9_#qBoyaCnxsg|{L>{i?7z&<(lT*~~AzlX0y>_$w@Z+Dq{`TwNzQ3*S z7q$^u8hNx#BkOj(r@j)!La7N;bvxue;<_#&<}GS*SI_ju9Y;DWtB~*@Z9raxlp8q_ z5;J6u$cf=6W%<2tWaHV&DKVREcP9F}#$T$lIjiaMNaE_)dsldwuK6#T263y~^I zFf42<=*i9`V}6UUD3Ll2u_)#S<+w4Or{wv9V$;(^1SUaSUF};?)|mOwtNM#Z{30tr zm2TSBqYqaU(h5gz^_T8?ElxZ`<~8+%F(g!!G0d(0HKsBh0F}yFT5YD%I!*_{1dcel zw{-91{$l&rf*)pKZJnX4j;J$(B0K>VJCMcHhb~l7+nACSd(}vSnk8M_M=zn0rUv!c z*Q{_}OMAUGMmXV?xHVyn5GqM;O@bSf$BuwXo=eVax|gV}+Qj!G2qW>{@|9RDhjof| zI>~b!BN7R@Nmqh#q?9gm?d@8eFcUn$6cJ8J|Gkze zKJSO9Yb#%+3_57R3HkQkmOw;`2B~p}zckWu!R+$ZEK}kh`)VGR{&iI`KBJUYdu5)u zfxL$lYSC9tjedHKlb5>nSLf+XO($;Fe{l?LD@shekbcB+(yk5T`o(!Ag2C?KrSBe} z>~&6}P`GjL01t}-K;dgB4~rQ_2TYX2#Yq9cP9MhaB3a9FYhQa!8m?^_N)PW^hbR;Z zQ{F@|j(__ak~OtED>PB$HEAZ%c&{?#KflqA^@Q_MMyJMKF3T2 zXAke3h&kGZ@b2xc>9nEmx_al6!T*s2Z?wop~#|%d-mcf}mHeAz`bxsd= zw**&V=D(Gh^j>GK)aklk`Hck}5DLGG!KQ;k8BKwAhZ8)PAoJ?3E#7Rrc z9*35pXz1Q5%zJ#3-^HE%ua2UrD%T5GCiUJJNqbbs%mV_#(ymIyxC>y1t~ ze~HDf`&bkK$X*TpVzS-=GbN!m=_jeZks$8fciqp@Qo4XfgJ}QapkJiGJ&5k!EI{0a zX0$P9sKgo0=jnRr6LBUft&u5i)$Spbw{TZkVF|qhi-~dA62z!D$`a-V3>JWqI0slF zLL(`yu|!PIIs%O~Z!M%12^ZSkb>Z?yFpjqOkLy})Y0Q1Qd;OT&68BH#^k{4Ym0-M< zb1<>hoP#lT;>y3~YU&=8Xd$0IL5E zx+Vzv@#|AZ<}!mvk7)~1?%4q5GXyH>8t0h)BFAdw(?+BDd7K4w=9HQ`QarOsI1)+5 z)!&euZR`q@_s_Hmdf!R&dyawLcT(B@1GT`P3n!@Qgg?i`j+_#KB!K^ZONW(y{gPaD zcYqgSukffJ|MLBp-@n&0P}fUAaO3W&pJdEj#1&DZp?_4j&Yt6bd*K4!Gn)i6ebJS3 zcY(u{m-}T>2RQ(l^CoJO#m_lBH3yTQ6AQo<@sBZhI;Lzt8X5!+glu$QqYEFM*DAQg ztPN?g6rma~K##kA<{qGe@lA&id z`b<6eQ#L|hq1I=(p#21-f&v0q<~#%02pwgMo@x9x=M88pN7FNY&IuTfip38(y+Hpc zx`;8u8Sn)&d8bBtqAKrN`Yz4H=sqx1Am24LT`9FXC<;!p1eobNO%xNoZIz{gp z-I0z~CiF0&c}c%)k3O!!K0&!?RzwdavN$vkK?^yDQ&NrJ4soUie+nPC#hNC&k&LN5 z6sDPXVtPbbQ_w-&gA&Nqow#WX=RZA86+GKFL!8I2Y&fUu6!OeJsTqFhu{Kq>3_L(b zlB}$hWUmEdvPoWdorwf7Xg8`4-M-8qB{wU-aVEu?Gbtv`)P(pUCdHYz?{_5# z99R&-&Nh+aJVm59b0S3*b#HsUzoAzGt3U0Xdwh1onPMKmR>=L2ICRdOb z_%hK}+sqVG$rUtwR0tGq$H^6E>eD{NNX-#f^l)Djc^ibvc7q~}qs9(tQ`CwxCqFRM zpfdpN3CalL%!D&tvM{KD-gZP`CMa=+SP16{TSQ2)SqStt-#-pON}g3vO0#F(xC>f@ zGh%lfVx+b;E@sP=I7U>HK@o&GpB-7 z0KPWKqnv8z97Bwd^Moxzq~zLyHb%UZnnz0EEgYUQkcJ3gl9kpZJ~0>QfCWprbOSpsCe>$4j0o3Aj|$yC{r>C!{3mXWgN<;w z5x;4mY6(7|SMBsg_wmvPa=~k7&DCdmn(z9687jA!!jr+camr3t66ph?3MSU9S!db> zwkayemUt?9j8P*j-NhnCmjElPXWBLTOZFPYwzo6A&HMRNv}mp4b7w#F@H zu!x{Yw2MO~M)dfp6e&I2)lUH-r?zwzBctK^zT7YMJi0v`)??vG2@M3cbQ6dxlk@{C zPQee#DSdjh``6tkORcBb;}EUG5A0}@F86T7IY9ZB_w9~5T=QXoO8H53?OZfcKi!Cz z^^|t4;={F%tPtx?vtg~k4%CS&7{Rx;=Z`zl*luo{3!B} z>YB)t-sa0h^@5Ev(yHF$Il4ri+Bvz|)E|1bFTq6a)jiODF!G#`pXF=}XU_85Kr&KV zt%g(HCq^E$11faI3HP&-H7`D9a_~tayEhaIQXif~ELB|OkT9!2I*7Ggq&GhPv>7JRk zq=DX2G?DV2P3@+;v0ayt2C-HS@jT)g1f8uW&2}52(`*xVk*HCIvDX~wZLW%Pg<~UX z!Jw~Dkg2H2y8%Fry~2nIJ;F#tM0rXK&q(NIRL+tf?rS1X1H?ob6k!-ONeAsTkXQt+ zNIldLIYNnwn^sFxGny*Vphz8{S{HlH>AqOnBt>KOmY0EJZXIng$)p6d3DZQtDI626 zqImCMe!3xN-=`ZQ<20#U%ta7J;=F;4?m#)}JmW2<8WiIsdPff7T?vM9Q`iQ|Ht8M; z*L{1qq3a3AqpPUsE5dQcNRV{c7@{%YVXz}{A=HCkg^GRG2ED@_`T^F6phB8!9W|z_ z8&IigkF`_g7;^6&Dur&M9U@%yNT{$v-=G-o9MkwpSWl?NVYy*ff^n$Wvu{k3=s|@z z0Lr8H%e=2|UeoFplRcy@ShC*LL}zWY6^ep(jW9<%z$Ou33Wq|=w0nRL;sXGaoIC-> zP77m}hAR`m$_>5H5g-PaR)5snzBik_vBfuTbP;2LJ2kB; zA4vWC?#R7ecz(x6H3lBvOj|LD;MGy26D+Ce7JD4jQ;0gA)iQ-^7$YZ_F2+irbyS{x zyK$iUez_+CY$yi>VH#Xw!-V(A*r41mc}g>x#vC+j!8WL|GMN&d_t;RV>dG?{Da7?m zVYqU2#@@g&D26wgHf>Z}Ort8+;S8#-2tjO+t7)4Uv&D=Q544=2Qbn}dr()hs547HE znyr}h#X$qJ>SGoCRb&!ZX!ksuU9-jXumdbE@u*C0mmNDSy7{@fQ01VNr0gal#GE6E zM^@#UG*{bC6kqOAt@BKNJ&s2^fXCb$509LNQ?&#EEEzwJH>0P1Mrv^QqYQB=1l|yS1Zz7$0asxPf^US zD%RmFs;=hO{j_)99_Uz(F=R*O&8NRd1&M0q3-Z|sin=(pm(?qaeyI0nAq$7 zm;`q~8d=zGfJsic6;M+rpsN)D6XF96uJ2+aVe3xI5f^gv5|h)Wx_&YYlhO^TMGDs) zJ(v&~MrjK$Y1V+_%ptUmVX?Ny&iO!7gC@+juD8!y#V^WuB%Hj;J3R)ncX@ZOCTR~S$edxec9Rpn34vJFop> zP$4#e^08U^gs&|oiSXF@B5Z}I>UgZ{QjW*MF)*@C1p6`VraV7LiS`_2xVyx>ENRh{ z=c_pM)6ZBV>1T#fedw7wUS1N-$%W$3Gxb{g5)-e6p4lu*Vt(1~%}^kAvR;em!ZpZ? z`6)sur2*DB9Vnw0m6HAVpi3beVUds3J$}wnU_U;Hw1E9@WYpS@;Z&IQqwkKtaJo)~ zC=SxlK)S?tjXe8!O|c<4T#xw#OJGriRU4LT2%}JIo@kMc@8=QGXm>T=-w<3e^&tPi zw1c@%X+35j@%VD#bfc>3k!aYq<{>Xoa>RG;Nn(&#O_5(|#_T_A@v%uau4=yjfpZ@gsfBcL@XE|Nou}_7q&3D{| zbG!ck%)L#oEID!|xX)j4f!TB~6nVqL-6LYz)lj1f)vK-nw8&MqUMedA=}D-`2K4ji zIE`e`KFM(RJ2ij+-TUgMyV1;!nbBy+d07(PHzrU{QYpTe2MKF@Q_dIeK~OG=uoTll z7#fHwef4~C@yBHIYv;4mJguw3UB zkKJ{0g5ZH(( z)O!^G-FRYPQ1*e>an8+3{15R^Z~bkG(R&_z+Scrb(mQ;AKW+()!NwF$irVb%%alui z0~oB$#H?c3BPM+N5*Xdkw~%tY1h`J0$o*6ST%Xs=qbUI>U~VM<@hQ~mVgeD5FjgAa zB8=Kx#%!Q3tSheQ!d%gUfQ#plenka$#ZK;ueZ4?=JmSydAdw{qeJhbl&KhQ4V=_v~ zd@T>9D?umzy%L{2=D&{LWU;olPE@(z6iV5f^SCJD%1+Vg6lLRc*mi1MLP@z)r_?m@z0TZ^_su5v}Rju92*6Lsu)= zK0{w#!iSb_&6k(f^=5y0X{|i5m5`#Uit*b@Rw5M%Z`=4B5++;7a&;5b6r(EAA4te? z^JA+g%Iy`zG7++k^GjB`(n2iPbLkjdI4uxzq=&mYDyX6>HU>rDsA8WL*k)BVww4>2usXX%6O&>X(pzRsT_Qrfng)m|4quq< zaXvk-h5vx#A>m6g+lJw*<-XLlj+j$->~K&iq1#GX{bK?}0wWXlcXU0qOI|qe=BHVk3c(T$pMT1Hn zPWCqMWQ_;v88nDZGbq<+vX8k)6$|gQb+}3-7MAsu?90%uFjLG-E6M%<777Cgo@}=f zVkQWSgDn%J`YoDhYGarF&_y`6^qE00twq-}G2N&bp6D2ZKF1BO_L+^{?HHJb+FcFx zx@hf$e`^S}^-O>0zJ?p(9lsACmqt0t%)apUnA~)NAtD6Y!}4o~19m(c6`~gqXcznm z-L8?2w9#~rXl!U_x=gsEy$kp6jww=KSa{qacpxMnc`52tnwZ zFSvrKQXdLwL#k%;6W86jYEIhoBPL;D1;a*|=E1=-&d@PQf%%U5)pMKx2hu#tGWz@lL4UjyB2GuKG*G(K6%)ow8Kl6G zBYDmm8ws~h4g1Kh@LvPJGgY?1o`JA^=S+%%jJuoBClc5)$hFMJ_{4?B9s1LMp{?ZoP!7+xWU z?~#AV1A=>czUwa~lC@V^bfp*uN*Ov8*o{a0d%1JEifYW!+{cL^_m&7!pSd@u*hCO|$5$F6f{M)qxhFoAFOyMx4Qi+4 zltPD^vx-#!iMc2YDlPG3Pz*1psL$fwW3Ea`C@oE;E5a~LY`)4pzTBb<6ARIRiEN49 zV~PZ~K(jOGF29#i?8__eF}dOdLxeC%O4Cjn_Zg1Egj{>eC~5h+3Xex;nUUzK6j4d+ zi)9#GQ8Oa<_~MyrpcMKCr5HvEO(!e+#2k~q{QmRzZ$JO-8-#&^Tc*iDA69cK^l(3( z=KWiH`A$~^uA9ht3;#hB0O@VMOvjzK+|5&TtIZScYPs{Kqo6^zi6acQ^+FRvte88I zh6Y98))Bkw$6U{K??fchstGH_UW{*v9MzE!agT{UEM2v1p9)$a zL7hg$B&MNfs)8b>#^h(pPwCw~k=5x@OayxK@#p}Es5r!38kHV)mX8l-xZjmt*)yg9 z1(Q70OGdH_;~r2`13f=DZgyt0Mdy3rhN0PkUor^LQo=9UztIxHFBuBh?BSPudW`*^ z)LR%Mw}qvR%yz&h62Q&{nUgG1sIX3Dd4%qO*&&)H1|U#KIflBKiJUd6olYjN^GSF6 zN}GGk4`Hfl`5|w)p`n_MwFy7k?i3a}ipeFV7CoRNon&81D%=twQ%sl3wdkE#UvIfi zX*AZGhwh|gnPlnNZzb8cEW`rE*wo}FK4sGIk#Fnc^2kGz^YwaJU*9>old;sAVrB%( zY3)dOUD%(0!p2ZoDxNO=!nlgv)_gDHq1a|gxwewRwI7Gsd2LD*C{m8FnNiS1B%(o2 z4kJ)Qkhzy{dJB*l(&RjaD#O3vv_RGHQ*XQ?-Xmk7Yc$DXDIX^P`nIlO!GP4 zOPRu4UBX&Rm8;l`lXVGeg^&bf*qi*AB*Sdql4PnFu)Qj+V&V%kJh6#{KgCP)&SYSC zd!jC8Ulqi)#H+eGPm&OE?Viyl!Q5kp2-i^B9v#;ZJfiZD?;R3C-bWu$iNpfaiM@}G z32i4}p)l9rD0#~%&=|8C81Z?%^+!{Q$Qf$np5EdLRHD)W9qFi4l|P3-BPxll|8>Pb+Hf2E$o?7XeNVskv=t8Fs69&|;FP`8KlFak9j zP||?{*CQsFw2A1^5=^RpW5W3J13f#b@vYQzJ%MZdyqG`Ja`Y-YSC!Cq=ZNBESYw(u z%U*h$tD+p%9xcVLD&2DiDs^&LBX5H)fQ(#r2_wb46=wUExAGuz&<`NF%I4oA{hhlg zX7`r-P#+nu%-#7v(o(eD(>q*o3gFsHJGzpDfNOTSqzWz4Av(;XrNdNFF|Z@?H-34_ zgSZ+LZt5fV+QFv7&^x};4ibON5H|jj)qR=h*q-5UP&);G^;|ovSe6x!H2m*M!0<a- zox5-r2V3G!clGUNRu}d&t<3&l2ET=2+J+c!6JdV$h0t@b!q97TgUI%!H9pSgK$UvjMo_ zo1NX@()_f*BfbuU z8KV6re|@I6{9cJ+vqT;-!Kg;T=3uES2{!~sAh$&M;J&a#%uG82fq7#+)ADuU6_52Z z1hzNDRMo)nD($5|bmIy)e2Pqie&02Gp2-zT9wNm&TINDso#%qw@PZW6ZtZMR%p+#2 zbP?!;LY4Rvy<^Z2XNE%iq7)E1qMlnp-~mH~2(&lZ>20oxPC#I#kkIlq0%HmZLtslG zsXht>%9FN7Ob1~IY&jwInV#SG5NK0D9vuQg-rW)LXfPHGBd~B2L{K=yPIu{n>`p*np`wY z7d{HZtNvz8#^4CF!uejqm*ITr?%O;1hJPms3?s0R2ZTuRtq#2$NK^xD2-R?mIUvjs z#AOiA+-(SZ#}hMNN)BuZG~HOh4G|%*@H2!ZVxq|z2+X+`_%Z2Fk2Krvx+lwl?NonN z4G^e0obf4}1>vV~BmS9k|*|pGO)H zb0uInY)8IZ^Q+xpJTb=%Puz$>@9)*3W!O#nVf>|&TmrRZlDaxCA`Ih@bf)T$H2Cgn zy^BMuV*wnh-Qf5|EDsvWKg$qyKpeV=<#E6eAp-6CIHm@jkO>R>XU-~CzL3!E`hqoI z+I7zsC_5&(mJ9A;dwu%;HqVzSPjaP;re5=A|b0zYMsKpe`Q zY3ReQ491y1kM`=8suX;QvwYiJ27<68{=!2e92m`yLIF~&;z!If_(HkRqZcZQX)-Jw zv^1Gk`Kk^Wr}I3#XR62+lSM}4L~~fKJshbVdZsE#-fjrQl&(^8&_y|KX{QWn z6e0_>k^v@R5hg+wL1++NhymNHNSFR_(C}9Mx({ktV(v7^C2sF<#i@pWrwtGyt~~-O z!~0yMXME>Jr|5sS*MPJjlqsD4&|P_pz-LR~sL$MK2wI*x?4WnJ;+$XziNMAZS!t@| z?%W8Jx7|k&m^T}9Rgs37hh?g6(d>M`2Rqrwl%< z)6DhH_<9%?Y3Mc+fkimD++|TXC)~B3 zF^Q!_zLvkzl^}?G?8Y-vfzHA)#e-`m6;!8aX1fgrmvg7fF76-sTmJ0?CuaGe2Ivcx z!%PFD#JMVlq;a-WPZhi?>!i}d*nWc2d6eT?w>c)yn0k~e{U?z!-coq3^vTm;ZGgfo zgP-H%i)Tx|==x*49mD2cSSLN)kN30ze$piQpe6!Hut_}9fdPrI-P0=uT8c!~7Fv(Z zC5lo7+YZ%L0fQh>R|fSEn__<2T!&|RhbvCO%K?jUya6=57Nb|{HC1wFj9bM94Clrq;349a4D z7l#G{FX_KTYe!G zD+v-+!_^8mQnTkgYNYa^NNJ;?EhiCF2BYgRYKXL=6|_YOS3zCWSnjlK02EllJs!71 z)AKrNho^nv$ulPV6tEmHLk3K=2u}VYnUA8MS!k-po=C#-$;) zceo+q1Vcn%v8gl9m{N5@W+~jM`Dqhno-s{@8|l1=hI0)}wCmn#OmHbSqRIb3Zy5)R z9nTIiN}hBNh!E9kW{xvTo{8=H#AgH@hw3);_@8l ztUSGE&L8Rnm-u!@RR8alUY$zEbQJDCsXcpAhALE)ne>OKjGvqc6t>7ua4iXZ=3Dlr zy+hwRp0x@^3tF+e;_-+&GaON0PB9$JCQufWhl+Jtl2ApHml;Cfw}U+PtoSo#uk@hP z5?ZRL0RH0x{)u(S^@rU>ZijRhe~KMuTBI&!;)_yFFoy&Svqx&?svH$lzD~%Jh1&(K zIO7g+2r)jORW0v1X)eM4@%y(wegF2$_uoH0FQ1>_NBq-o-+uq=$Mf=dPCxwk$6tQ? z^{>Bgzdf!m>clp-QD->r(`WLiZGwZ2hEpu0BepFn)*I%YLK3>~%OM!0T9nAGqFX>?DFaPoLKmTH|0RU?_Ztimd z+4v)` zFl>gAEg@7q2hQ?IJBs=wqQnnBj_6v?Ybr51gK&AbMKp_?(RvaDY5jVcKkMh!pJk3s zY9=%niz~!#5OU!IDS7fpJ~(W(GnKb|kb^3ppY|O7GrhybTS?&uRL@hm(0xXWP*HlD zFH^2}iGm~49y?^2St4KFa#rCf2nphk>vQY3o&VyMXwNCAg@KLRJ*C;VR_1;o?rDGM z{uy&`irrhWoFlo3&JW;6L7P2Oob$XJQMR-{z|5F#hm@oOkh4-qDFrJUp?#e6|z&HAQS+ z%9W1{hu65>Q&<}G`uKDT@>!pT#=^riQ1SOp1{lU4sZ5z2Ulb0uK1@U9WIG*mm!P*J zLZKikw;w`NVP$%PN+Kwv#T)S44Dh8;$SE){UrP`(DOf6Ebc$jXYy`x92d^5f1@Wh6 z#z116XvZ6hf$e=D73iMNjbkCD4JA@c7U|_8Vj_qD0z-%aG-x-B0vvMhgo0jBGRYbc zC-(N7j1CwgL|{gOjN4%;c0w{D4+ocumM;;Ao=RAkSTwn=d%n|8#l2x|KXtiz{Z!f{ zr(I>P`En}9lJHaVpR`Q7B8>SdEYL7YL=$L4(`osPxi>@46dPGQb4#dhNRB#mMN%(l zw5TyUbjVM5doEo7g1kIv7x7ehZ9JCFfZbkRGU_;~iKrV}#3GZTgu%X~RYvCNDXJ4) zOLGef%Kc0sQvAIq?iML3=uoQayZEjRuJJQNsmTh!%7;Sf0j=v$+Nj+QrRi-hawwES z@)RC~#$(7(Ukm?fPNsSJGL+_L`x013Q$1gFLHpy-X_dILabE8i&=`nuyj(voQ*vFL zS3!7tuM(4VzAzrOR8a}Au%cpMpppHz%7}DGL5RpL`4NdUt2WE)m zo5JlrGXimC#AEjv0SiwJTeahhnMtKgBC^+@B-|DpDZ&`JsuU_pZ}a^l9Yn#4(tkWC z6Q#?Flw2r4D|VYICVg-to!2RlbB7QwOlZYA#4Z}t1h%~u`QS#d z&h7T*6qD`&apFqnxGf^2klqwhV$R$N)mItK%gfi*S4^^CMrt?~cw8>W_Zdkw!PB!{ zz?LwU_%d7g_!WhBPl92fm<)^MGv+t+fI?H$LR6Xq5`av9=`NjH;8k4o*B{5KygtJr z!bzhQH(=# z`@;7;ppL`DPPOrfbr&YD9op&w@RmmjUeMvrMbwCwq?*Gm1{|uMqrpg`2$Dh29z9&sz%ki(SvgtPW~VL1*6t;APQFvB(1Mf6n@hwb`RN-51yc}6q@fvhnb1-+MYJ%SWz)0=Ce9)Rdt z1tBU5WmN*iS8Q-QyiU!I+5up3+#+srh&DlG1@8a?w_%+JvBMn!0zZjMhlk<{FVvF}x0pywnv_C_|yFAt3cd2x0R2Bm~}V zo(o=Mf=mY{*M>9m5;fhT%3*R%^Z?fa?VyWfPsx%8TuTC<`IdbWpWf!G?Rb{VU`_flds5e!oWTuuUP+13AnE>L zGRoGu((N9D)l>-l*XJ$ZI%Y9>jY%3k;1d}}hzj7#X--`H5pD381sVd-E0}oYL8q9c z<*Bmin%?2NB<05=hLA96EkR0?`H|k{%XBzDUhBj-EnjJc22Xqm{tnZeP~&|4p(Ob7 z2@#CQ)VjEQfEKQjKN5nUYL}{feyFsZw&I|CUm=(8TQvPFWOn>i{@ZMQWv0ZIaFxVT z7;%skIxs0H;zFV<9DAnhbzEakkaOH|^+c8@5!WFalRQmvAFm>1%v^a>dW^WLO;qBYhgeyJV4b#;+uer&-2$6fUkUWV&fdbN#61SCpi;{=#lCp zS1UZ!HABXIFKt5|W}4({N6wS3bl=OMTg0$v_nf6BlrkOa2oTjo$O>32ADEt@K}F+p zipw?qcdh|g9&Ttlqx$y(72az%1JXNOaZ0>8U>J!KW|&;mNW;V>0_}2+poAQIYX%hB zh?HQ1ud5Y-k_0s22wthuq6A|yn2*89vwZpXK=R@8GhiJ3;rvm`5xNFL50PEo zvWt%%^R|xV8k34nFcn7+HFyxZg3-=ci>dNIw)NxEbfbq zh#@rSEfJh}9Z)C)Ql=2%T{v0L$3Rp1+9Hl2<;%7|cd^UOk&?uq7J7A=d1(hQ6QUhSfEht*P#!O7%U`A?q9It5VMgG`GI7b(n=uic6*OOR6gMKoO zmO>+tGKC24p@l9;5wc=HPvza1xxr0v=FyE!+PKE#qKcEW(GaWioPk>+!U-u&?XqM3 z+X=a)HfU-222RFs;AnZjLiQ`M!l4kW9=TX%xgM}Yh!%UdpWfys-ZRjW_jFiTQnA8o zOypojYS|oD;wtU`*7EipVqu`pe0@$Vbkhliv#u25P)T~w!6c?i^q@jx{6bdTg{@W| z7JJ+XO=Z_zejKuvE74N4PlOS)&7aAFeS8&O*wdc1r)a9`aoi#Y($E%*|m& zB6^FkyO(lfBjJNm(pzE*QZWyaZ3bl+$BE6?xyH1JkX7`F>_CMRdQGJ&ZMwyAamdj^ED2f>j8IQS;%bezkx3&@87#y z#b^n}f-u&DPZ|1EXh!Fr74h?aTi~~dZa-X!;&;j=WYw>AOVXh2yAydqf zE3ve>XJ=HEWmi@1F@m1#q3ZZf8*b55w+=dT8&EZt5CerKhPWCDS; zmMJE9^vaNSI=}iqF!lJr0WMA@_55EvVb4wRPE{G3BCk_t*^xVF;8x9?R9)s+bmMpt zCb9c<>1}ROIs-3tZ)S6{rbLo)C1{N_H5bN=!q#i5sv%8!xGP7l45#LXlD-I|cp)7q zhnWuXcP6@XL_ixKfKfAy z$5E3#9i{4&7-)uTY;blZ;6^$xw4th&)`|Bn)LEHY>Q2{B#!+L2wCNCaXKJZCRn+aX z*r=iN*NBA*#yLDw%i*aH_hNewFKM0HGln=kM=TQI#pdwP+gxuw124(PH@uLF6=Gv5 z3BwD~5JcSEX(ZW+nPQquiDoU`r0XZccqx1e>AxX_J5||4R1Bdl;AQ)`eEwXwd|!AN z!%cBsfeg>cJ|e~0-r@UZj$j8Y65=KA%B4)_7P^JmbcP%)=p4+ ze&DQID5FEPC=6b#BChcJH3{5vBU)Oy-xnoaSQ4PHi~ zlNw<|czWVOh&9GD{oFOqEECJ9zZ54-ki`vJhE#>Jm$7(G@IQDql>;szQX)gR;OSws z<%(2MVT-~@kuM8#qGxdDB{&F`j}I%}oom72y?jl0z{H*Eor1C#ZwhZ^nF^~0Kba>O z)6iu((*Z||;tknL$H1`uJR#l`rXHfOond1J1v4D+PehRMFc=#S;{?)yVol`DNOM8U ziCtp*f|e;BQ13z~MS7tv5Nno?N-n~gn$$vAf^lo49jJgm$} z+kbXH6|NPuV&~qN;NcZHL_`qwfqn!y{w_MpBw8`I6aP z6`hd33Y805$&m(L}wV7o*&&!}y)hBVKSj&jSyVm^IK$5lBHp%I^J8}ui0K1VT}WU zt7KY=k_hMsV#y(t<>Cuzs=G$gRv zPsS{;VA!&%A@(5}2g9N8RLC_LmHXJZE%>y<2d=0koxx5t?T+8GeQ}b6(-)k@soT`2 zm`?nF%)tK27 zU+yvVOjRnx)SaPcY88jOwvlI|LX-Lo8!F`!a;dW_!c2@Wn%iPAIuA4B`+o$K4O`C- z99wwPtoXY;qKPJ^o@PZ?4S$j2FWL_HC3k@FQx3f7*3@|^{A&)l*l^(&iqbz|Pl{i1 zIfDvQC}^A$F}0kCis=zG>2Xl(d!!s&^6|52Zd?!xX3$c+#$f%+?W-;Iit@?kJxnnx zCHtc~!XS!=5b=Nh{QcMe@-Kh;hyU@v{_B>&P~KyYQD+dOD#o{BbT5l|*_l$0r^ z7?tYpfMs~~Crp!FUaBaBb*}yBPOgzZ#I!D5xkO=;uVqsid zvK>HsiOFGD(loqY?a)@NrJ8gl3DC(VgD2WhE0NN8yBjci37Oaw+7M~LgC-q>%Z+nT zrj~=!)kAMjW{>gHJKW`0R5p9Et|Z(d9F^Q8HWy`z8ANBGvhF+z%|RMzTd%7X50&jm zd)1QhIn61aMrWv`TP=ix?TyU##TZk}{V6`sa((LRJW03@u$(DeAXgyL0NXCYH9!}Y zc6h|VQT>Lad|y~Ia*HJAIm;q;r#~jwoM4dr3 zRNh)PMfF>tQszqfLwA9#58PUMM17`*g-&FqWvSLRy~7nJ`@pTesirH*C@QngWUjm& zB5K@PqDB=JhLDXC`a^f++z@-WT*h94mS-3utfNmbL;%LDG;N75E?l#R1B^GDnQ@Ef z?_Dsacqf8qngQd{<~Dw3%Z)C!5YOBTMtiQF-r>4r^?`R%!#Eht9C1sd+O9~Q6nP_7 zg-{v{AvgB)hwf__7+X$B1^8}5m`7K)n1ynJAtEr^RFqqy{kSSRo*A=;Z(wwYC!J4< z7-tA?*@yL~#?BCKxSZknIzzZU?iX~E=nUbpUaqe*gsFUz1bgJ%ctQnG&I@vaOMxg? z?IKddlou}$wd|Lw3;4errMksTmg60Ew*!c#9_*dVV>K9iOXn!A({Wr{L5@Zfm6eB!2$a_`4Rc9sUge!sTMhUe-BH>2Zi({K3kQ84c!9Yzs5Alw8 z)gDP4tIvT~iZfLCw)~r}cSgWwoq=1SuSl7WbOvrMW24H7(FwH~On>Mao?GIbs;`Sq zyV&bY!qc(F?SLgXI)x?7h5)_IRZ%W`j-azrnQmoNK=?w;M`1>486nm1`{07lZAdvr z4xMf-v!g4*I8F-3L&~(pAlEv*?E#TQM0CNqJK7EMJzWYA)d*trGQj)Z-|kz_4JqS+PN{TcFA7E6mo_%MVL@a;YZAcLG1G26(PnZ zV%*Z(Tos*xn7o$+V(f&Q2GLvzn30I!GN_t3|8F-uZ`WCGw~q=lrMI{O6*Cd-W>6Mx zrU)@*EKkZ5qPUl1i02_>-4LUSv2j++t!2ej816<)_7b`5FtwgwiV!h&sh$k)yT|dZ zQlW7zS;cCrl&0NX3B0WmO{kL-+~``FAAS^DC45I$q9LB^XYeF$gm}YVkFZDjZ!rg@ zctlG-scZEliNJZi;10mr0kMbtZ)xz@wMP$}?Kpgu6<2#>`ct4Gp7wX`-lX>Wd|`K$ zIb+_!&Lz<(t6bD3o~u0WU(IH}uEeuy9R3$F8oF8dDQ~*)lbxYra?c4q$D@$&IXen< zI8a6u8q`>6*c74@w35`WqEMRdb`9rwBM}xvj1b=V_$Z(B7QPabIhe1s%#Fd7`%N1f zWY-!P+Kje*kFF$9EaIiL&HT87SkGf8IP_;5L!#@37RMu3W#|b%pNTfp&H-QLX6VoR>s$*I59AC zkDz7WbS2^Th`>s!)4?^Y3#^T4S0>U<1#8}NKr7qw^}*H<3qwwm-)PWZc!B_H%P)|o z2zNZ(GtP_O(1BfM#3Y)6RN_2{HG4kFJwxt=o12ZZDwSs>YJ^*Oa21^w%*0pC(btrg z8wzR%R3$;gROJMKY#n}`>%i!HYP0_ zq6Rm7pIjAN@lZ-IIZ>bLfwfI2OxF}e(GFj3WvYZe>@OD{*2rHe+VTPGfs%zDG&+xH zf1)c3D@yr7ggAyF`jLotP~;?h9#Ct ziDz_*<`-I)uDD`~R;e})7(kC4zARu9RBl5QnHw?j-qKE~L(4rnfQQ|Fia9K<4_K&% z52%)Z%t>KB&=OOsU+qI5e8AqMr+2t6S$u#vR)hNPfbiAdExZU37h;YG*H9`12IXp# zs}Y~n@WaprEVl!y(eP+=lOnH*-7%@JY|{?F<29dZbE*h>zQY(3xo^2HPjdhG{aa0O zpP$PQKmOBi-+uq=$Mf=dPIYV&gxCrHQ~9dL?KyAX^GFl9^SWFfuZdiWE~)&qS%bIu z#+eL$_#5glx(-G7-uCGbk>}?4)*LsWSY0SQUY#Apx6*uJ$+umpKnmM5x()S$v`w@f z{GPra&yhVR#)a!UEwblCi8`n9zi5$t>pHRkRa}paU-1< z;n1v>a)kZo0+^Q5^QLvCOPh&=b#h>nGKC=S9L>zcJP`2(fWc?A(pSRKJd^JWy@A`} zynX~+BRh!{Cro}Pm?Q*D3XM9xhh41`08{8#(2BY7m}A-zGg8Yn>UwJ&)zIt2N0rc> z6E=z|M#V-gyQnJ&4{8x?Y@QMJAW=~3!KP)sR9W#EXE~KLr$2OKCpScznHEson_g_z z%ADHxt~e)HB0@{yQAqEI&sUGvV};eV(DHTC9Vgk$!~i+PNR_K6^)VFDh$=NG$Qx*} zBU+m5cm1Y^mX_wwqDAfW(jU67;b=+eU~v6$*jna7T#%LsQ)@-bJ6$bqi3ly_oK4EK zz0Fn83Av?koPZWP->SsYna`)ujJdtMp(_P9P?T8gYeMEU z$nP46`7jYc$cMXv#XhVOPfWEbzI#A5q3=4L6XsmYcL%jnnl<%AdbYVWb3C5Tj6*b) zQ;Z{Z#`75 zZ;*=~YAqfxM1&HX%`&IAxrR6aC53>sWEHFZOX_|368s$w4VDYI4X>VT88^v|kge55 zo9^wWdb8eK=;TQ7sVlJkuVhQLY&qKA3Eaf>;#75 zAdXY!a!JWHEiQaM9*$=*u)QVZEvfW@9ol_Tj=1j6$(!z=84SZR2tFS5*ol4UVJ>4z zQlxO>?wtM1EqADD3FEC`oq}}Qe19C9b@W^6SCr+;!&t=ak^nHMl(=wMW}8bi4{?d+ z1Loo9slq;KFRW1-7vy=XSm;~Zy%g0RF_uaJK@PvgiNf%ZMhd%Mu7@;@uqqK)Osb%b z$x_@0nr%mYX1q$5!!Z|@k6djS!b+V&6qrG83?RC-JL|;BTrR8-A|)1q8>o9_N=vDz zN`oO(`JDdHeGNB6Ds~5#-veVINr+X)jI$FA!NJHaQ6@R1OheWD5ilkXPhhn76EU5~ z10%6dguUPcs!C6PIOYT8GLJVxjt|DPEZwDwDP_e6S_)b9a4#%$z*y&7W}C4y$IO{7 z7@er7HVyF=D-4V+4WqmKE*R}*SbB%+7bh4(gR!xM-SCgee`V8f0E~s@x6EanTZDs{ zBIJRwr3O`d0gQ6|bRH)K%`GXYKGTCh&j)N$5WS=C1Mj4UVKCa9pgD1CT#e zkZdogslWll<@$kEI`|~=Wg(e_L6S%*gG%T{NMd8Cy;@A_DUdv14h_kdJxFy*rt~(~ z9%n!@XXj-uydP<#a7X1qvYilaJz@SpV@tjvEX%nYG*j^Pv3pTjKAJ1V30o?~4TuFZ z=#T*_ub1`Fc!8ZPVj_iW11BstG%B(5m_O1*zx_V+FU4!d2|{yA5ISz~9EO+KCWp}5 ze6;0&e8BWP0tnmNwR?J-FVpc3Qekl+0@#&$%qa1^uO*jM%Ym?>#@Azp2!nsig{aTm z3xAs(GItmd*$3zXp5yT6eTzbl5O|3RD?L~e&q9a_L#S3>Jm~M*+3M=y@haQL)Q7#R zkUfxP7aL;slq8eY5}!2fU4_~_#@rwdij*^ieRqSRmIFjT*?L*YaOmzVq+9yQ9(#?s zZ6$Xe01pqK0zJ9=CeagCnJ1J`h3W%Mndy5;bP6|wJHT4#`Ve7P081X*(I2{xZ+&HH zSuaNeC@o8M{pcO8IN1l5mi*F{B&eU53uK+iDtbxN=dMT{&@RNv(1wuIOZr3iHQW$Z z)Z7VLTApEuuyQ}a5CIso(zGQ)O8apzW)Dxn$_xifB3!se^1#?KDXO~yjPk5{iRlo` z2Z&E1{A#Zc5RXLZJ0|W_AK2e!QXKf&_$5_nw~VhH>4CAODl~>r7Y{G-^&{L6>Fo9_ zL)bYWW(l2O2o1)@5^3A4oFl#}>I5(r#?-Q;vtZP z=@XZ26teGhz2nNA%=TTU`(=Kyee)$LU8G5FwZPIAeF*#L; zj>#P(a>B^G$XsIj%Fr{_P9~;43_VlsE0&nuG4xDT_Qb@Lk!Rw3Na{1JU51`1H=wwL zJsLzGdZzaIP|hzLnZrC7;9hoX`8xA@D-Q_+;M^-~Wmnpyc2(^}kI$WBNJ&alsW_SaE0I#zGti2i)#3s6 zT%gJ?6r<2H;t2-QRQWSbqrj%rc;?<{X%D8;JA8fTXxWbIp9e;pO|ir`L-fE%v;+Yh z7(#7s(jU4jXJBlZ8r|i`5fQ7iOPTZzSDXsQrbpP7gh#~)j5e18{gEgv=GN4av{iW% zsPHylSK%NYV0Xco;+^&Z)i8)Jf8jo01^L}zOlH)pjmv}##`bQTH+>-J6WdgdB_0s& zfstql+7PNM5Ysc5AzCg*clkXqmPVn-j4_R)fboDKA~4!qjwQX#4-m&|$ike#o9r4g z9$@#tNVEj)1FF*&(=xaZoL8b^wRRm4(>RI`v@U6di4^FLs9X7Awn?PrE_}QSAwxHXW*Vm#VS{%*=<(>4~#@h&|p-X`k0o% ze4yoGbRp0S$o5G364N*e7+VHMSCU~c+Ek7u9t!S(vAxlxii$E-b!}sM1~n<3rVje!A&URu#2KNCg$nPyv?P2sK zCU8_AcqcWCgVC;>S}>{xPE5>ThG+Wd{B zL#H_bj5#AOi_O+p(g3xq6%UN<1qjt%V9lroPJDL(_kpz47uj~V57>1;;&`~LEj~cw z<%5z8gVE-0EQbgTOM53qYY5fAiTN1Z5NAe3o3^mTe2Nnc5rWYPi(5tDJI8S_62oXa zp|kH=S$5GDmX@~A`haTSjMEmDmbOrzxfhH!ZGqn5>pM4a+R5v#B*S2|X$#9C+QQOK z|iR&gs^<2V@W2z*wtlwgf% z6HaDa>9Pl5FZh6L;Lsns3uFPA*bL&CJHbfHGJGKHmXv_pUhq*p+>>M!j9F*e0AW1t zl&NdW8>phf5am)fzEz_?A2P?<@&M|SJtcG?xe{eUuRWYbIKdnNB(v(IOiN6*I^lq$ zvUakHr37nCif|!m?XBPma_6`v*2JI>oOt3)%TkbBTZ8b`=AB?Ubj8*4vR?1a)cfuEntH!apGjRmVvc;F;m9->mz^5Z47o*spYq~x z(wt8=@I(Dy>(@VMr@pCUspR8a5pDlgnTOd6lncdNiX6G%#ZWiVVuVM7w^AHHvYm;dQA|?EQ{3JW3^mne#xgVt5B_6pKA~bof5?8vPz!j$=rD++~#d(qhNMZBA zS!GJ3n3~ZmLR+>*6&3TRGDO783ucFweo-Ikt-gcD)n;9+=^bBb2M$GH2pb6Tl`gpk zWl$W{PK9DA&S?31ixgkL;6kz45LStBl&D-_`#V?X+y$EbV149n7s!V;GVW~eaK$N5 zB=W~#P#A||E;EEkF`J6L)vbygZ z)Gj>&#d_-pEnh=1=1h1{qycl`t=Q}+7Z|UOS}sKOs=ZLODG+OC@Sct?R=Na4EY6c; z7>Z>kUqW#U8mDAhvRmzLt=V|yXXSN19k|Z=)}ahb$c9&wRw?M ztO}%Eq>QyCWmJ3p`6rNLB^bw98EebRs1NlZ&?}8>RtCMJ?*#8;hG8(;tc>*#D`Rb0 z8C7Yx_)ytlJSeuLjQYskb}&g9>kuhpZP_thNw^&%FxsRHdYkVX$H7=h8MJ(z7voP% z9BGs4Ueeh2Lhud8=e^wXvlf~?w!1mTqW_rEd=5y^`4;BCL zV|B_AvtF1pv{aY+Ob^C!1_B2sckc1o2x0q}C{xmO%aQ2{GaOTFvdkKfVE2GY#19=) zRIMJ<0JwcEV8Xtt=!r=++`g%z6LRjhZw3)8ZEwqC8qNu3jv#Jxan=qg>Uh~um`~7( zy+4Zi5X=QyK14+T+G6UoC?+*97a)GhNiIDkijcw7hv)<|M-VR~I5N9zPUR}= zge+btUih4peIho_vRH`%AY5Rdx%3Rr5WQk;Z>s2e%y6D1N{VVq#+MZIK-dnSS5W~N zRRG5v2L?j#nW~P8XVF=ZCA!NgRt155>m;Y5+NLY4F?Xnd@PL^k5R&?oonu1T86d1} zDw|t`=2jF2xC_{&mil1*I?mWwTgFCp)qTK*3*=G@AEgq(Ziuw867^=#ABQ1sb2jer zJr-SvTO|P?F3aBg5HSNw#%8o-PE=c9C{)!^=?~r1!6BafZ&0P3trDqhgE*}UKS?u( zEsp~617;2(p7-)nrtsY11c(!awr)ZhtmP8ihpwkcE^u$}q-b40cHrm_hg{&^a!cwn zcS4-jvblgPDCsq>MA`QDmX6XDW)$LCZPFd@F$1Fm@q2qMMHLlrlx6Qd{>*zD!rf=e z5d!){SDP)w?}>S_?FjKb-74p9`<8q~t5OiBx4D9yVCDegS$*2hLxh{_%OaPvuv%kvH9PRSFzmopeBA!dePmEL)Q86QqG<)DEixfk zWwf5HxRO%Ev=D}{mILyZOqyG!l1I!NDadLmBV7r2Xk(K|JhIu`k$d`lziTfqveCr1 z02x3>Pko$&vXA+*qR_+J%uXj_g)KaG8MO^8Scj)Z#P7Exe z@E9p2vXMh?yQwm|CnOlBP~7Q$jJL$_#*1XrlAi5)Vg*OjVe7g{2ORa@kGsvBxX1T? zF!v=s$Z^fUYQKE{wyEIEzV3;{ zY!YsjrUlSdO&nBIt2uEky8hZSPP!6|`)dl8D!z>~Fzzh_qbpaS8SbxPxGxYsppwvE z?aDE|%{SBW_*ytOfU8|OrZI3=0`9L?l@*71FTSFKBjbEnnpNw3Nq{Y*@Is`RjlxvZ zQczBF*qE(yf=VKXwJ9t2_zIm90&JPqU+y+Tg^CT=VgDXmoe7)t>#M!G-Fi$Lopt`a zd^Q^$s@+ntQ(Nm4Z?=E4ukX1heu%qXwhd~zGSv&g5Lf3jG2!J4_d)2L!mugteSF}n zArf(6Cxas{1=Qmu4f@__7)m*|hr2p2$ZHP~s=B>+7-Ub-S{|=TybSRRkz!JYw-svn z8C68Ac=-s(74pqt2ap@(ybOU4ujhjYAx_NOIl(qODsto4yo!70$S0?9x)SIumo3{Q zBC2dr!shJUhnO9z2iTH48uuV}jnMlz&*R?mJnCaTUZJ*<98(q zAj)R(?>5Qf9$%=@J7R8`5>-^=s(|}AQ{qk|`)_%iSFynmIcG_~7%Hm|7=q(bSi&x= z)7$*`eH@RK6_Hh}Hajt2zzf$cf1ve&L~;{)kH^AUSSCKpNp4W4#icl5mMCGFI5vZl z4CApJQA?4+BcmQ3i3=h`1(`wZTw*eVYieurkH@PMr$aQ4ySEBe5~e?NA;aCNIUufH zx;MNR$^pJpm^Y~S$wqa|3Od2XqDVuER5*_rcV$0xAoCY48nlwst|W+Q3*2z$m1l~3 zc#m%{Db^w0jX;&>JHr8>jJJyb5Rdxz0st{Zgs4EM+AYNN7-oT%9&;Ev_9*{7X4afw zhzKTyUL!0KlgQ41NpkJ2K(?tg_n70tjdb2&4-PeiCSpoYu}(|f>H5hyOzak5%>C%X z#A?X_ChB}Krgm@(oVU4$LuA*Gh8Wu8A237+6T6m&oZ6a+N%01@C6fm~bn64zlU@RONX@GD$)dOQo;i#g* z5b8WLCSh=Er)Onr1h3N4Jbjd;j!P&C-Q`jqsei!O+5{aMkH~#N-_AswWn> z>PR(a+EgF4(xHbI9xQH`<@TtPXRPr^N-%U2PM$e?&qqtVXrct>3~;88u>zb);M@== z**G)AsjZ@L(F0t#wkCNb&eMh!FcRv!JbiS+_=D&xatlQ7aK$;nGVA5GOrB-zNra(0 zNAARudrLc|C0y?u$tu=b2@4$8H&TEW5*cK+M|{Z#_kr^YYTUtTHU}Otu`2t(qvcd} zy)i;3YeSMQG&*CRLMN9$+6zLetmuf#2GnDSVj*ib_wlj;^+?w`xaiuu3fX`ndED1< ze|jgz_F@XLPLQ2QkFX+sGBN##Ib_f;$INXEd9S41{WOI7GfFpiQ!W8mAIM`EJ5GIdeX6f9L%Y2LZly|&sE*aELK}lhHAa*1bE1agu zTvr0GW^HdMsS;`W$0KHxa2Gf;P}01yTI|pbB@g03ou08sXG*4+i_*hlOHXMGA)DZj zm}J#}| zF}^8LI|iP6w)SC+Me$7u^u7M+w{O4y_2YSYJYP*P3_atU615qXQHAzQY3Jq&f7?z| z6_b9de`nLZ9x)*z2OT&yp4wiHQN8icKgqr={hh13VA#1n^lcTATEKUu^&rYB!oy>n z4)bW~FmDN^S)b??i9nnl?y9FmKDtguL=#UhJo5RpPtt+jHpiS4FY~nbGgK`Z)9;V; zhwh45>Y-A)izD&>Q8 zCQ}aB`%xn9RUa)kqKcZR7T_Mp#fP;Qx5b$|^DP!LQ}b1;k;>+(ok5!a8rEap5OofCA=q^EA7(|yAiRyJs-LY2AEDK2PP zXyH1ij1x#6ErH}MH_~YrjV7VBA?C=VB`kDB7%q5h=Ex(aba<}PvOB7XV2r4}cKSot zPgsCWJfba}96zCfBiOCan5$F*>;VJl0Q)LMjFAVaSV<7` zIXoX|sU58ksA!b_aL@G z(29N1AG2+($-4@^5&ImkcIMA3sO4q!?mk^^$(uLEWKLaMwPREE9z%>J7#mL@dX6wW zPS;c$A*Ly@3d(z?+N7mFbYs>R);Dye99EyJadgP>OU`CEL{MY>kC=49P)%eNq0$kr z(Wq8K_K%niRNzcsavcJj%SP$R05X=mf>fILb6{$I%WYEAJXxO4eW$D@9G=T|p~$9E~|2-1Z4x!bq^a z>gUGyG;qTw{EBDpHoV=aiit(V@I<^Alq5u4^Fh#uP2zaOw1!?K)6yNPKj1W7O4576^s z&LZY|Fc7xaF|@W<={_cGFc1>=X3+K9o{-LW%fsesJYw2X0bxs1s;l$DK?j7dBGx8v zJmLZHE(q;th%+ydchWSD>+;+XXGTN&ev3zZ-OLGw&||L})@|IeSW2bvbpP4aHyhEMf2yNUYM|3@Vc;v5)pR)ybZ z%+%q5#!3cAGP1|{9KSQ?(cYO;;f-giept-p`NDk#PPZ4*5|oFgG=9l5v=+_g8FLz1 zY(HCKLtVQU+TtLq?k%E+yD=t%`q|zs(-ol;UF)WTbYQqz*!pf=?b+VlQspvu6!eGg zGP$}>45d$duKpPlY);U+4yBFS%}`19ggRRgK5an0FM}kI}oJl62r!Qz)sz(0X_#+9_t9 zbR&mQ8Vn(e8Tv!ly|^LLmRCH}TYenymM6}nt$dakDy}#u7(yekv4lA(@Qg2*Ist)| z5A`I1j@(WVx_zzu9f}!o5`3NKTFgvW+&l1MCFdlS;!DDhT3sS?xU1vw#YzHjYgNM} zcF?hB${OYwGg`i|hQYW90F%=Lh6V6TZj4}_g4eyznB>6lOxzH{^OHC9}xZSvdV*R*(JU*V=Tq3d`5)r_T zFs+7jo-u9a1l{o%D3oVcAsrGEwN#!hcV*oQLgg$G>*_ftPYRv<2w(zmwvq`6_8XG64JX>-~*IVK=2@wHO& zwMi+@m?_cQlC|WCsx6GGsJ(IeLwDs27sTi}C2B0RJRQVhPE?H?`wbDngp{V7G$zWO z0h1cqZPL^;=5TN$rSNx(kpO%FlMZbl2eFnmP<_wax)0YPg;5|y3X$7|iRDOui5yjV z#v~4IffQ^{u>fF_T|=&VdxwkK0+R!V2w{?xreM;UAU|FK)hX|1;#TS45wk3qky@%n z*GD_qMD+tY)BG7GL>C(LlVO;YL4V;e@u%i`Fdip>Uk#{9Pv5=vn~Kr80t&@-_R-1xzbbY3_^Q$)JuP#kN?MqNJ{ zM~WTB;?d9^QrZhys;Fs97<~e<_?SS#jgZoS0HBvr<8ZY+X|Pts@0cS~k@C*wh}$AU zihc3QbLirgBb}ha5d%`}OgqkMdA6*UYWsagLgPg`%LZf@vBf$qsiG?aw~iS6T-Mi7 zh0SVt#;leuQY=R@CXb)v?3QQCZs{(+3zHQ16ikNLEzbjn2w`GxqlefnM_^LfEm_4% zE4$b&&z9X%?FAoD8;@~z%d@?Lq&{;W3YVwNZlQPd!DwwRJV}PZXtP_MhuAI8mfcd7 z21BTQ&^Wv0*|J;eGri^az-Y5u=pA2ahzN|NGzEnrcFQ3!=1c|mbb8U-Kl@rSPH;!< z^N-)Z{ptI+U%vnTftwFM9pdhKxj%2O*|QtSx*$~KyWywLl-M>R%acyzw!>Qb2-fx! zbwJZkxjw@tI3JZGN#{>UhoOTk1;H32-1urq6LYjQdPg498Q1 zy?pA4uFB5ctedKk5fC#96rH=F8w{7tc0)rLzvL8)4jX>S)>DJ%&zO6`3gz|!mbc{9 z!^r5xi5e^>0hJo8<^Oa=2*N)5Ksf0@w>)C<4Oh*1yKOW|^*6-PsscQ*^+XF|8zO+U zH`FU74i&Nol!%ahecB)Me%j=oXNOglTURZss%uzEXyw4UTw1d{?Mr>0%`~~+5$|(RyqHn`CP3Y3lj^` zfQi^isAEetICq+F81HgkZ9XbwTG zp${;a5bbDi`5g;<61PXMI&Lu@H$(&zlem)2?poq-HYu+C@qwT0#%n6*dN8^{gmBm!C1b%!6&d4KGeHOEJe1^?S&bUIO-p}XeEx8Vz z@N>DqW+Q2c;1<(Z7{nVc%cwYB4@kw>4*McQyO|2^OTc67e3`G;+MwIo4uzKr^;o+Z z{Bu|&Wb=!!1>?VZ)eA2f)hF`v-?^~mihp%mUXP6Zz*Ri0N*7)+tE!YB2MmBqJ@2b$ z0J{K4Z*x^du@#DZ?TtpYLny(2T7ybjzSc@i`tV$U7%V9lz9jndPon9$ehw3UAnB{R zcs+8j3lKj>MbPvPU)=?RR`B#Bi7|-Jbm#AUWnCBs*r4gk)GKrvX2VNs+t>V2FN!!A z(-IgQ>1q>UUQYwPQm3#Lq&P#RPIHaVYki(1+!hj!%oZ6ILZ%V6(J?sYlm&0FN}c>{ z*Bgv4DE76|+Q%z(f@A0F<-VOrmrtIo*ITW0*5{-7UNnTyYr;?VE-=wm+||8;%^!dH z`P*;be}w}gwbPXs!WmJ^h@Ke!a4?$u1dKU>GNUj8LnGtk#Eg7as&m0i>9KRB~;@J?gtL2RG~DUoY3`RqL2!52$8* zr`NE2`f_ztbN?VJ9|}~YaUcX4St@HN5*|<%HhmZz`QtCY{rcD6KZLUEK$u?UI(w;0 z5`%}1Oh@%p>XO3Ff?e!DILyt#6V59hQ722|Y!CM}kxPb#64KX1;K}V_Ar@vAjl-lF zJR$8akAoa$D?H)+(63Yp^H-q~JF$$AWDqysDeMT>)nAj3ZJTqGB(r|To`5qF(rGhXhgl^_^mS*k21jLW(pVd5LCovcJasrIL(@5;pj1u9CoCO`-^#Xw$zB zq11Xl?Fs1sC;l^Q=0uLr-*irgxXq?`TthtHg zB`T;Ys`Wff7J>~=3gZ|L#l!L*M6Nolo=-4Dgc3Wd$DFqls*pl%D;ib}gAPYocb;D+ zA1S3-wJ$o)*N5GCzMv}X20n%m6+IY=0-iKRJ!DZThK7?AzD9U zP7}iaHQ}u&Lj-MeZw;D1f&$o7_?&7*fE=hBPDJ12jgzM zC&UI-I!l;>3Q`)TOW#k%k&*(pDy(8^K(EGbCOluF0x6FVls9tmp&M``yx$-2!qxgo zR13B6n2mITEkdN&rGHFaIs+-Wkp-mKS#9`M8t64)?G&V7lPsJirbHF%G;FAs`*{&{ z7%3)&1?vsL+e1pjCwYkql18Ev$;F2iB0~xheoiq$UZKt@F$e1eTSQ1HqdHPWTiNgD zx!kuoP<@pPfuI$8dzM%?t_0jj=Y=!0v7{!phr4oA>%3F8k0ZqnV=;B4hm?lNz>t#o zc4F}{4}lqhNJu9krO*?kI78!`L;@R>gxexQN*QGnGR4fh6LL!7F|D5_&kO%N9?ppD zF{qlj`jX5{$PsP|3Q~xBG$;bM4%>M1(%9tDK%@}7U8Go`Mo!VJN4zPlzFsH(|Lt$T z{-1vX0Uip#&Xa~Bg)%g?aJ|M?r|evaB&B6}++fG?eqJG;?S#yj^|<{+OV-(PgT&NUHyq_%LnfLUBjsK{Oo{kgmT7C_gA57bve9E$-K z$1O%e@)2_cV9BbJ8$)`VtD@sz$qt@Eh*?@*V=jaftFH|$0{YIW;m<$Gtrh*9yFTUu zLvV#r9nCEkX9U>@_&a@%odfM3K4SH$rv6J(qZ0_)Y(hkx!(%)kYbh$j|rOqy(I+3Btye;qR)1KXwx@+xx==-UC_XGCcB|J! z6>;@B@<=&oL$7up8Js^3`ieUbiWGY#eh)6g+kP$5>w635j)foR+Wa)p$0a+pO83c6}+XALPX`>ow*~RqHg{z^({^JG1Tl z)gG(AjHSKcU-O;q9mzg(z%F=}tAMBH$sK`%zKdKSt+RowZ=Ej*x->Ped^e`JaMhd_ zbQOE#+M#OX@{0IFgYe<7mX7=Sg!OtYG!AL5%@Dr1jt5?!yLDzwH#HA+CX(U8^=`>I<6gYWB%L;5x20N z03AExj+1+?ExG5Yb>x5-#LB}IqGzTq?sgDOvj`XhLDzYa!^Cq{s?%)q>>7cuL%UToonqmR>pu0%}{%G?(wvAvga3^f}PuWPj5!FSYb&&J#D=3N`> zHRihT_;_CFE7(!ku@DypwIGe!qZkE5kX}*vAhpuP1PCWmp_@p4{KyICfI@lE!8KY~ z8d9dAg1Ken4aT%`pi$UU@OZmm9=`Jj=5J<`2F38I0@NZx6GPmcD>0r15f`{SZ`#|` zoojrxOm7mRC9+g)rRhO6*>{bp4-8mzSJI#c;tn({no0-HKsmrBfPVd z?B$fgh!9>G6Ua)`I$(b4YTdxx9j3RDhQBZ7)UVTf66CRBPJLV=`gRLH%zwusFZni4I+)k@otn3@|`fV{jD zMd|&qe#l)R{jK|0Zu|VCG}~MryVLfx9HdUQ5sJA=)%tCP*^^}yi&=3pPELso;p(V^ z#i_a1q_u{e45Ght-_lxQYFRSXz`HGBmizP$SDX{<5TG#YOd8@EGv>}fVXp7jtYRs_ zG=8DWM68!nLM2AD!IS{U12hUAEa@#hGfgc~sOvD}a52k@DGijnB6ZIj7MWG)58ah> zJG5KU^31)-B`wczi|rk*I42k)0*lQzBD4E49R~|J_y!i6Z*)yS^dm3c?&<~9Dvd$pD&?;%^zaIPo-6o17BKvKF$-dDv^lX#Ycx@@AxP?<)qph z%pDx93Te`Lr7I1?3!_`SBa3G3;H{XtRnw(Y=jfOArxQ_-1`7@zw{91FOR;_e?|PX( z>zCZ8a&#*-6hhdU)|iM2BxL-bKY#!Azx>PJ{^5W8umAc3KA8#T4X8mlCkDv_Tnn0@ zQS%)1s6i98CpX7Tv`+RdKGP7v>WGcj%VT<7Y$I%xu_tU46Yg@oJQ0me*dz}4dDlvg ziSQ1sWXuZ*V!n{;C2gOt*h_ec^_kBprUh}>woSkCj(F8xxq;Q^I44&hHdacSOFX!8 zhTW4S!Y%8-k}h=Wjp+=%>YVsJ!dHy>iD{e-H8D0+Z~eXMJS|VJI=#aU7AIJOqmx_0 zCPhs#|M3iT=B6wB)7~G%bQ=a2;tmPJ?}G~lPy|k5a#pbp<7M^yWE>|p>1Img2G?V{ zII*G&a3W`drZoEGzJ{CN#NI0{Pva!MU*`l%ggD9mD!pS%Ev|}Az)9i7ty5aQ*2*}` zXd))eIXEdFN0$27!0hWISA#^D8Wdz4EjHI^ikUt=v@r5dK{mc5s><@1$ivXW*gWN# z`vTTV%QO69+sb^UB_gyG5{vYXxDQ3!*dsMx;d((U_D*j6(x9omG^pE`j8i{^fkxnm zLLV{Fszfx#vFdrt8)&g3TFhPPq9qlZ0@*-|>X44Jm?p+z>a9OE1pLCTHqtw~XgOes z5G{6vp5ErmbQ~?@;oALjmot<7=IM)$9k>K?el&?x1J~^%Y47A z$*EcXJ6QO7UTMACVYx{Eeu7B*K#aY zLmvXoF>j@mP(+*(fp08GPnY`xX3saUY|WEb`?=6oq)g)xhPW<+f^}*ewIhjnLMP}3 z2-t3#vUOvJk(kF>?MvRaVXE+mq;5KWGo6hmRvVhbHfv?^>q=8 zkgDok$f=+FM5^;&T1);|^3I>|+#F2@5< z#_O!K*Ths2F_0n`oc_-B2(J0m3msf_5YJM=wQqTulJmOaoS=U_P4^1<_PsAthwqeY ztM+27s_@t^OL(gvwWj6kiZi}>#&vXdBb(`B8~1_-FI*_?DO|uA{VQzO{GMyd|jEDj>DO88~F<;jSDx zr`}p_S675iK&>g2bf6L%n&G1<=hkwMx^e|oN4txnhkm6>bYyFEVn#MHfG-o38Ml@% zH>i&6zuC#q>dZRT0UWIg!||XHM?{E9JQmmYodWkp+!G;Uq6`rc z0^|PjJ;SB$jYv-7;oka#xU=ncZfSiw491kQ5}6NJA__w`bBFBinj$xIZi%rd3xZfX zB@B^oZ(+#hk=^1^f7cnBZd-*6HoS7jdrKSvcYz#+DN>*11Li2vEhY<9A9$zS@1U{P z*rW>WmNEaMv)#E7$wnv*hLHQtTO!c7hVbm{JzjR#Z}F`+dsm^Vt0a%R3g%8Ny{fus z3>q)0ueZWd!B3@;PfIrJ8P8(Sfz^%pjlyo8blk;sn0fga*-Pc+m@Ilmn8{TZ^kaV7 z=(dZ`b4#=w8TjDc^6-NGaL_N_0e3Ie*!-Pa=h~X=11*22>!I8S{O`hKohcVMMBll! z^qndy459X<a;8>v6q2pSRZm&b&NdeU@c;Tpq8j2Nh_kqREHH2s-9G%Ppn{y>i|kfBE^_Z{L4K zG)A4&vW2Q!!qw$53jLw$TVJ@@LC}GiIS;|fMA;n^kXVjv@0F=*-3LJcP|C5H$JI~4 z9x-QjB|;ViapL#d^p^`}q(tFy@?U=c`TMt@|Mo4rfmPwgs-c>j_@y9+VP()SM8Qm9 z@GV{`W4MJrL2Eo>akbe+bBEiNdnH7N8#E+^^Rh`HH{2jy#xI3ZVwTVg$gIw-<{Oe`-*@!^MKl1-ixD7ZyTV+Qpzg zyfPc~4{7(XgK&asI2aiFmXx6D`JS-o~juywDquEm^|yPutxBcz2omNSaRcyn0Ggew%P(26}95ns*2@!sCeGibStch#&PBGKGh5>5Ao+!(9@ zz>9aALvuUCp}Dmjnkp*ruG-Nt*C68^{s)tJ&s3e&m=nDa( z`auubb@5XnW8MmBLL~JeO3)F%ia<_^R3IOs1l^y!{DHpA*w@Qb&*;mrgd)>fSs?!55 z7Dkoi4jR*O|Lgcxp|D{Pb?pvId?gKsJ>A;zmgNZci1W~^#84c)TUIB9}qt!x!luLcQ z8Z5M~f3zDBHk3RzKe=3%YjcQ+n|d$67kz|czTFxY+O2#q4n5vHFM*$Oy8zqvS^?2c zC6)H|f29z#{ag8Rvd~P-@A|@nNn}doZ$uJ=dqiG@T*O)k30neDupruI#h&0U5i@M- zt?*Fvj8V)>VmqUjgj5xeHLBfKOt2VvrapI%c}-uTH_%OQ<%{i@lfXy9?9 z=fr(*19!XTxt+hSdK|H?W@=zwLdp^MVJWT7L^m09uwYNYzPn92P-lyCqF=bGarSU? zdr3>x5~zoA4@ZCKKAvlSFRl#D^HGSER%tALq~JSZ4_@X7gP8q9dYh}UT;|N}y;;=& zv76@oZuW2_^O;ylatnYD>PH=FZnLIyJ}8E_b5dVWorkWenG@A$G#m$LVa+_!fp(af z;?dcG&n@etY75gLa*IrV==uP&KuaE|&x{?xv%osr?2S3S!xbm*Q;!&e!-QL+&=s1O9ZYdN8jf9CZDaQy;Vs7uI>54Fn6q_b7A0n~L#0=3$k*7oShprDWm=Nzo zJacbCu`i&RV**l1Cgmc8Kv=i#CLQ`33`MS8+MOc|z!b){lL`!*^M}Iiz z180Uxo3Jtu5mxYhw>LK1<6yKSA+!539nV>nutLk%V2nSWVG&l|vJY#zjrDqOmnXh8TfaC4y8orLdY8}X z@_cdS^Nuxb@v^5QHy0LRPH@}*YLi&dIF2@eUQeK9u~c1PwpI3B=k$lJalf#wiHXgX z76*G{s8wWuaTd$mvRLX`_u&$qcPYmfeO>(&K(;KFt_VS7oG!~$X)Lo%W0~U#bk`%S z=mn3sePCiVeJ$?;LzFmhz&H`6@=RBm=k-`Vn|FH;Vj?NG>|f=c(O5@%+cI5Q^(fh&RMDnwLK$rB?e zVu3L)hq*yZ#u;3t-x{%r5p#NnyV~LdErX{kNq|+}8L`Pdb9{G6Z}Uw@g4z%u0IKG0 z%nET$Z55Zs9LgNe@^e$aQzmv1Uvp+!AaaEoVwq#Q(g}9w*cXPkx7i(5*KzC@>efoj z*F{rIj$sySnJ`^Q#1_N!o(K{#QKnkQN>5%AA4a83mYHK(NDq}QQKV`Mm{BK8F%5)U zARYM&q&5Zp+O zkIsd@vd>7W4j&>%%gF#GK~tUEB0f(y|w-sQ6x#3=c!x2)H8( zQRk5=;vC{bcyB?&TYW>6{?=6ybDowGQT4IUd7z29f2@><*``FyLzIXkj>SwFDFaV_husz@Pp^_h4nnYKM0QtOl zv1tq~laEUFtf2AEbP`Dv%TERR-D>wp_N-M}J zR`UmG+R)Vs0}9bKPB9Wrc}1E|4|nA#C?W31pa{b#DHE4cq!75h{Ma%t8kFFK9S8sN zO<@y?8=={J)yM8lGlh+TcBPakZf)eV)dS`T!D4T@(%XC&F>1*{1(mEq4!-qj%iO%s zO3Vr2b~>+?#F4L%Bc@waur%{RT}=#wrBE(}NNHBtHBJvKM&is$`zijKh(`I>MHhC9!BDPx_>l`!}z@6qs6-c$elVK%fuI09EqEj0)x#Eu*5sWN*nw z)CIlTp?Z&QbP=obBpHq^g_6PDa*2lmx&XDn1Vh|DFeatQDDyZ`VJ|(cW?>#4N?Xz!8WVsbu>0DWVbN$BK zTs~ZdvNkUY-MzfO!9JtL=EBcKZC3wR`vN7*pF|ar6PcEl=u)Ey03qwtWt_j`JyU(N zbV=j~H=MW8&V8obqAue%!nn_ra}V@~uAITNOx5Vyjz*hLj7Y6ySOfzM@nKF3zvq07 zSu%|A;P+g=0|)peH3B}2Z;;A3y20696W4um(NjVjjxTX!E=Ad%?~0Pc+R~Ctx_S+> z%oCT!=gs{>mn_Jrwj{=bD-z>;FD?C}D;PxV$x$5uGIRO3iX z%y~IU96b{x>@>tdIbtW7ep*8n4$4Z(P|1zXX@2$%M%qqmF&Xq8BvEv}s=td@k8 z8am2ba?qTXv`n0YsV@hbI&iinSN+WMc_L-CT@-hfuuSmQ%qVrAps^IOa%g|SV<7m zHaJ9D-o~JnXuG5)Etm8t7n#KvElZ>OLT(H;J;z2j7MrE9#4L?2HmvXgY|I}xdX~Tl z0|N)fJMP#i7-@xY223(YiH7#^2h0&-BdJb%X1FIi0UMQ!vDjn`%6z_)8ec5yI%#X7 zUF}JX{k&LgxEuNc3edV}hEeZy8GW$Y+uMa~C4T zGyl8_C-TRjU~QiWM-k{Fqa{XP%#}cj2Lnw!PXyP?7!-X69ar z(y}3{sOd6#`I_O$>4o?+Z|U1_8kD(uDNb0JonQ!w3T6p&Y|M5 zLUaY~1z`byf;l8M+GJ?cRF-6X-$jmNqfn}V4ZBYn-=JuH>#FOdt$p~`^B=!|tBr)u z&*g_7|LM1HzyI~)d3ijiAAbDfFTefz*WW+3&soi{E+IbidBJE>>Jp-$ZSH1YsGLvr zZy8*qXhR{=u0LYl%eI-s#`|A>``iEX`<95BX_@`&rj0F)&EY5oCp>6dqJN84K6&Zf*87Bn@Ye<&$h|nWYjIZFSOqym&UAG^4raCc?=_n)5#1NcZdzd(e zo~h0iW6IbU`XAbsh>f{-iGCe^$p}F|3%}$J9_9e?OR5m?p|te5E}@l}s=}%`B6}#0 zKto49GaMpjnkFiVc;?<*VRKNHm`GG|MN97KMh9L;snq?N3@rp1siAOd*-PCmX`_mY z&#@_J?&6s&-s4qkAZ8NmU4;q;F@M54p7B;8TgK}lE{LZr^l+rf!_dp?)|Pxx4IHC_ z($cyV!V7-NF#td10Dzxz-LTTCky@pc;c3hXSkZM=1C|`mq)c%;FxOS(DxYmG^~r-J>f> zCsfv+M%JA4$TdyuyCQXh%-T+BS5aXOSsAVI9TiTHaUU;J;u>GYv3C_R;IHv;{kyA> z0e_7N4SQE113qchH8~5FYkPrBHSkcVB+uG*7i4_;sRl?QWNiOyAq`0Ah5`KqtmshL zfW_Po5i(uPl~aoSp)v=dq%$*EukmFXEL74&v<{We541O}F}^kZ7w+=f!g6hC3WI*( zM@yTXK<{wHDIP#~v4~}Q9y%N?%Q(4k%0q;SwWTvur5Rr*T;p%-dvV5lyb8vL*b?q4 zR8UScNUr_fU4;t9@yvTKT6)UTK{-4PbCAn{3!%B8a zp|VWBh(E{B1yiE)Z#_i!~Sw_)?h7M5@aHXpuDh6a# z+KNAY?!_7J@hTX{qvX9f@N5Qg;+TBf*>dbnvxF@h3GuFNH3 zL#?oYNdw=)bbx0W371a}cN{EQwv+LPqh-0pL5ef9wV*+Nu|_X$4@XOTR304o2>m^H z(s$>DsEJM>Rvu5gdqKu|yrwFEk@E%1*rxjBIzq6#y$)3*$1~?U&9Si(c>OKyVs$@e*BH~x7VRMBk_d=yVpUi5NVU}^oXah!UcK%42ck*!g0APVkP*{zvaY* zf6HxC{FGY)_^F)K;jF$SJ$pJIpD&9LSyJ7BSQn)_3LGlcrMJ07!U=l=N+PKg$nAMy z<9J3r`zzupiV1U29n;@9pt+*ue^nd}`Y+t&v1~`&0TH;p9MkKhl+kCo)-bgc4|t22 zF`Vs4dCCFeFU|MhY|hTZBIwS^rH6Hg7na=TtMDBUx9(nt3ghwQ_}lAH;X9s*-@OhM z#^V9yx7VRM+wqL{?scg1#&~T0?RBULlXy~o?>f+Yzv2W#VkB%G@N`G?nzzDL9y384 zEs3hIRq#(WdDS8AwGb87wTyG)r@FJN!|E7E zuSZPV(4K1e!bHHo)c{uRKae^O!Enq7;YzntEsjERo}L=roVL; z%{B||6i$8YKIN|~%~7k+*vS<6AmM-w7O4I17@>rEVTANHS4DZdxYBI$>31DpRCkSXWIfQ+Ur*C4Krs_nF57xEmueiUE}516TU$0DjL!hPn@BjYr#G5tN3k z-*Qtof~qhckMEZVN)z9MA`C~+GBeJ>drw81YdXeHcYS}ems z2@LR##?QxsEL651_{I$O9EE21d|d9yuG_cB*20hAmcFO4eb@2YSa)yd@aw`roR#gW z`#5XBeWr}b_xNIhAvt9m=bmQdkF8y{RPOQA{)n84f=o(s<^00jfi4TuaLygrqT-jF zI51jU*B^7c|pJMZ0~5+hb}#o?JI*L>RN4KBOW&ZlLmV0!P&pdL3slw!(!s!&l( zR>>*mPB$66rJbB&vP+|-L(A7%iP<>+KS(jS#q)xjHi*c6MTU8m=ou|Cao^*w-m{QM zd;_7{KmUZWFkz`Q*zAVHp=YYl9a9KKo{4cD%<)K3!;{OQ$Es`?lTE%bBcMA8-_4<( z$p84gMEfoDmvMQFF=2iQzvrzex|u>Tspsk1kOd@~trva~S9RWQL-F|UFxqIc$y9PMm3(bEHQ|Gge}e_xLXeW&w6cis4|8*Kb2D@Er%)dMhZtvU&ZgDy2?MaT6s!lyx8c6aoL&mrIgmF|$4h~a zi$E3WR3lZkHI%0rYxdXc!`7O`ZsTx0c5laVfBEJ(TMF82iKUL7VLKe=$&}YmX z=`AkXQRBa$ENqufF@AFY5&Jd_;r8Ky#XM{G7rcp+?n$_~0PC`SR>Bjk#q2-Wgoh=Z zqy9(IY>J?J5^f&KI%kSdcI%6G8Er}Vy1#j_+h0<4TabM4{q`}BCk()rgtJF+9D!~5 zQkFWF?{=i)P1Lc3Yb;%q-SzVxt?avl^e3R~9@x~%n|r*@y}1{kZy9O&NT5z5?EI|8 zjFoZqj%9A@0>7YqBWW^0dqnFIrbm2UxjOB#es1HkeR^Rr-CD@|n;7bzpd(&jpw;$a zjXuw+O8Q_E9u{UL_w-|QG;vs;fE+^uP$G~O!~Yh_q}zns;d4c4#z>HD9WyuMd-Y_7H= zl^<$um?mlf@?8>W4ft|fM@L&5hA)-7mPW9T+4c-saco#S=&{WgA0;6c&Vh877LZ#c5Ib zLlZe>A8TxV@h;CSDPJ7~Lz(xwy-j%-K~|=505&%gPZ)r$6~{*z#x`w+nt8>kXQkf8 zm!3S0rR(&lB#sTo_BpIS0o9tTOfVQ|$=RK_lBLqHxGFC`NHS1$JLmdBu+GmTT-w=^ zwb;hXL+2-02Ie;`UG5Z4w(wCnY)A2i*uGP6Y;J3O?%f&q2q9vP-9E|iXiHgUg9MN7 zi0*Vh7TE363Xirm*4SG8U8<_swY9J_*tTTz-1S+KbK^ebH;_z#K$_1$GL`~K@&Rdq z9A?J$d4YvUZqevXBtO{jd)u$jCbo}7SQNL&_y=3?@LK5D`jIl5tHZsuum$B0O)O`n zM(*A|;IJh8%*^zagg;-_6X##H&vQIs0Jhe`^^**j?K3%E3){hof#Q$Nr+&VobwBl? z?Ndi&rgUa=xsetGImOT*Zo`{J|Ei%>4I-1ilnenv`MebkPA%}ry?g^`lp>+y36{QYh=tr0q!d zJ38pK9Re7d2uF;2y!PTr|`8}7R# zl6x!lJ+DM!&*bLJ7#pGKNZ+9-B8^zn6xP6Pa+lK{?WJ^?3&%`D1|A*M`T>I9%sv9Qpt~!aun!h=~7I z_}$qi%fWSi#^H-iczD@dKjUzH=n8n6=WjkurW?(cST-Lz;{abTmd*KThfZW}HXNgGuG^P4%#AqLdu5J}lxq!U zzB>vJl^K%x-KR2o@3&cO#Wp7i&FREXg>5C82`1qWN#_>Qd~Q8PpChJdn5T~)9Jscq z_NRyqLR7|fY@XJ&yl96j9&EzH0(!t>bQUo-hDg0=p3{M9cPhBJXqNM~OPcw`Y?DwhwABLssL;u3tDw^d}&& zKOwNa&)FZ(-|XtdDrTJP`cb6oc6|6k{LO>JJ83C8#Ps$-gvlnbSOo5sjsy#ty?x>$ zH0parhxWf6!2b|2Li^u7qwsKB&luoGF}Zym#6!&u(OVoY6rVbzR09qFY+mY_2LrsHhVcky=*Aaky-45fooNh7Z z4>lnd%DWB^hp7*XcMV{@%R$rZUmV}Q&f|e+!X+ZG+lzOLU#yfZdj7$Fe`%#$kITJo zA5M6}2$Yo)E7z*WqY*7u%Iz~FUMbrdgf~&gD`h{b(7K;)pzZT9PqqjyjYgk#yZMRk z>I6AFWLHEtg(30-6LylkU^_K*m!?H&<*goFz_4MD`8Z?^40MyaGyCzuR z`6xumNBAIhEbyFznnawdPUdSZ`+f^nwgwNj;L(_yk_We^!Xq*4f)ZxP1cQSKt5h&%gWehqf5C6#SAsuC(kD*H02$ zw?qBiFBaai(ybQ0P5IWNJVbP@vHo}G)w3$jgz4Aqz}jb3oM(ovHLUg*@A2g|c78RA z_ZP323wM%ni-GbNuX!zV_jNmr{#nC}UpNviU$>*}pH}rsH2!rv8vkikb2C?P*>=$W z)2hC`My&jGJFfp(RpUm)^D`(-+u{FDtNQR7fv?x?0O6-qeR{?BH}-l{1$bK3H9U2! zdehwWw5o5f84SC9VBuL+V~fsU(CrZUr&WD;&CtE=!vasM`t+Jnaofiro>ukc6+804 zX4}!)PpkU&irI3kdeijqtg11RX7J5+EdA4}Mk@;=#CFL2)2cqbV!;{-{vpx~LsN!x zY~M@pv|d-|@!0DR(akVqVqaZWecL`&@T^{AZo}p^R=pX<{YCMT z?DeLl>1kDyN^O*8x9zO?&#D^xT_h2^Z3nVHt?HHJPPgqG{7~iJJ5~SFswO%9 zxM<$Cqu8HT_3btMWw-4>{%2KJQP)Fd(pzAFJNX`SGl=W*5(HdaQc$7{Jr2KE1M~9jpEj z-HhX^oW~uj{*av}n@To!W7Qv`o3XQFhc#4P<6?htX~y=76RBg>AEKMF$6>EAR{bH; zjH8Gs{A1N0BF#9;xI^t8>;4dNw&Q&2j&rYL?nYK18hTP{?-S5V&o}c|U6<*%glf2mJ=JZwG`wuW}eJoC3G)5Yy*X4#P!~ zp4)bW?ei*!;leR+tI@Z=_=>XQsM0Q61GnwK_oww7-@q=M1GgHU`ktQO918x3bCrAJ zgVW!uKfI~pav$|D#kK>4H^}{_ipl956~9+^cvHpY66>+Y@6{aMRPpt7jJv&UhaW$S z)QE6|>DcG@sts@IGXfm<>R9!Am4>%feSGD#>{#`CRfe}!eSYO2=2-Q66^6G}eSJlU zj#a-`U3gQ~2<}MCb=wZaeinx#wmHf<^m;q^_i0rhUpdqh)#x27sX(xdONcEX}vzaa-wFe`a`4{K3&|aW7Qub&G6~6 zPK{N6h%{r7ij&m0?U40njWs@9+^b`+KSY|f!_H&XA0o~0>BiCOTMZfh#W#5ft~gtK ztMR?Rc-2UcI0<^&&OZGtHp8bIXGCw?skfh2HR0eP&9(!vpH}tCkj z;3!6Rm|WZW*PqpEh86OTRey+X25sr%SoMcUGkm&4!jDydh&02ei+gpf`a^b__;hiv zj#YoiP7|Ll?$xpC57Eu=>Ed1;s(yEz`kUr8K3yV}$ErU>H^Zlkdv&b(Lv%BIy0}-z zsy{?G!>5aTb*%bBq!~V4+^b{NA0o~0>Ed1;tNsvahEEsw>R9!MNHcu8xL3!jKSY`p zpKfulj+K9iJS#rk;$9spe|N0pYASw0`HDme;dQ$)7_<4;N5ZSZ>$`KOTR$PZT*XH0E5}YhahYf$fCw&)Sm|pDxKGmOa0GdUyQ#+s2$DU%SyiK;<7J;cyUeX-4$?ooWQl zF0zr_i|_Ji^Z7fKh__W79&J8JL=kJ_eZ>u;w+I;>_<>74=he!MJ>Aiu} zZ>xC4qfOQCoK$bDIz8G{{Z6Ifja8>do2uWbGQ6?s^k`G{I~9gER-GPgs{Rlew&KyI z>W`6P>CvX@50PUmS-E1>bkgt8QrJO1- zD;{mC{t#)l;?bt+50Pdo9&M`r5NWpJ(WdGTk!C9%ZL0ndX}03grs@xoW}H$UJ=#?L zA$r-0N1LiYM4GL5v@f6D8$10rX}03grs@xoW-A_Ts{RmZw&KyI>JO1-D;{mC{t#)l z;?bt+50Pdo9&M`r5NWpJ(WdGTk!D1Oj2>;O{t#(KinTGxF;#zvG+Xg#Up~DzO#N-r zY{jEZ)gL0wRy^8N{UOq9#iLEtA0o|GJla(KA<}HcqfONxBF$Dj+Eo1^(rm?}P1PSF z%~m|xRQ(~+Y{jEZ)gL0wRy^95Pwx#@f15O0@n}=^he)#(k2Y0*h%{UAXjAovNV7GM zc1yvzZA&S+I%#7Od*!a9k`uQE3i3xy1X_lyUiw#ae>SKHssnvOP=jhSO5T0n0uR*d@HNrkS{*Myy^iWguBG^r?(?;#1Z%inhwnD# zY41mM%eD7!?Q9hBK{h)kN8Frq!)q&vvW!wDz>uQLRn2ebx9=pH^E;gL*%zrJ~__ zKk7cd;d(!O&(OIY*{x6O9)@so}Kk;Cdatw%~lL60w%&MEc-5#q0EKPdmk1F7q71b^_)U`uh-!; z%ja7Ss;^I-JRDrF!}+Yi^*UOD<-%c3ISkY(-sWh1+DEm9y4E^s8VK5OomPD& z-)dUj_O$n-CZ27$-Veg>)XP4TZ#8deeXq~tTg~d(aJ?UWCg1u@zG)^yKh0!%*=O>t z&*WR5$+td}Z#CCmt)tK6Tc63dK9g_lCg(z-K9g^r$*g6?-byJz;!Ys(I8dX;3y>%d zBx(SO8W@awDW0p~G^#+NIFP6T)az(>QI-~Xt^)Nsswuj*Pji*`JXe8Q9kkE)3Ig>y zs*$gKt5M}??}z6qxZV%XRkm538r7OnOYwzBJneP(Y9w&I4qqY#uJwcV`NkDgo=z*z zWN@8UzHtRyufsDLT(8438C<88XEMuJufsE0D}U{WXYwL4ffve<++8GngUe!#ga>NN zA8=5U()#lczb(Z>jqL>WcdBY4=7FkMu!(+nEpF-JAui6^;x}zB%yvF@%rtQMw0+v= ztD1E8dO9Wn+&>1W(3Zk{Zykxp$aB9f)#=A18@Qqh=MQcR6P%enG^DC#cN_AzpMUdn zQQ_*OsruW*n^k}7GDvGyQTF=0_o^h)*ID9KY0->Q{M|PE`p4h=@xO8da8)IDZN}H8 zR9RB=NC`d7SkGL~ThCR`Pp_o)_#JI>i@YLwoofl^k)W0smaUc+P3FDSjOL|p>bKW0 zuS{N-tY3Of@`~j3$g7dpBCkYVU%a}MNXNRklyJuayp-@mVbrz~57;nXFG{GIF#VAT zR>G;;BQFLe#z`30W%<9eg0{1y-&ug~B(-057S$T9SMrj(KuhACRiK^6WoI>I=aJae zgFTRTCyfI@gRrw6u`}6sru)u>-ig95J1^inuiAT&j{My!aBqt3z1HqcvAtK&y%*5E zsj)XT_FjMXUUc^6!@ZY{gV+6o3p|+D4({N=qkVJ>kLK5-C)UwA-qEakH0h2e-O*az z(MsLXe0($wxy+-E^2^D!pUlfAt6C>>>&a|-GJl?|Ae~IrlN*0Bub!<4ovjC*&An%L z@NDiqYb;*QX4WuaJg09KDmTkLCZlIj*EFaFX6Y zrtZZO8JS{Xc`=iN>!o-XfXHz_uIBHZE^~&G{r2MBwF|O#<42)$--{uXM1y_T!GR>( zfh4c@&6vrz2rbnMq*eiGRQI3Ub0Ce(zQpENi4Nr2EU(FSVZPe$JPUwa^3M7(IJae| z5qv>7`n^f0PEvI*nTO`#-3*XhuSZ^ICw9%KX~^6Bre$>JVSxRoj|6+!LtXPX?3vppu6HLI@!K` z0rIz}39`J}cZt03a(932t-AJBT|2FAo^SiUpFi~dD3E9mG1R_;_j}wj;2juHqx}oS z(0Bm(+rH!a`ub${(o2Pd)mFgB2K9!)-oVHXV+_y}%mc z99YAY?CaX$Z0UVCdqp~&y-Nmi`!JiT1sEj_d38Bpc%3=rH{g;xBjZx_(J1d z$F)=P>c#bNv)vBJb=)lP58dJh)SsF=!MUZ~HiLRI;XH2L4s+=C6`=Os3ll}u1yW0y zG8#c3m2&)KZUX90Eg6oVEE#}$DW)1YjW3Ys2jr5w>^yeq2&DOO?A!_DQugMEBa^j_ z43Jx8*~Fa(R+x@mA_2JtOa^_+6fk64$`PhC&Jy!@urhNzXoYwI@_R?ib^zCL^h$C( znm2$nmO$>&q+uSdjqrg-=42i^o_d>4-a~-Xm;;%nC(-!@t+qey6z?kG@ocG#uIt*) zof>EJ+VR}EK;;>WKBY-rPr*3-# zQXLGSxD!Ytb3$2ZbO91IPJ7efv^OPz{6&|Yr-QAIr-PopoVs)ba=i!3;#1%6oQ{_K zr_P-~Zo#RSOeJbx-zJ>S-k1ToRb7u|ccXa(1U7i8#wpvSHPPg9kE{oZ4xy?6^0vjyR ziXE0H#1>2M0rjU|h0dR>t)I(juBshEQqkbN>-(y{A7={sJ#U3y*qMn!=Y21E?^$-< zTZ3nLbIE-rK6l;hj1r<2bnOeAOYX9c1yM9TAIzO+mL`8|*$%F~_X1=Z96UznqxFsR z(Q^UFC7;ZR=aXge`DFP5)Zeod1!wA>ENu}(np;5q3-b%OHp^blC(jC?{)N{vaHhf8 zTyo~JaP^l9Mh$fu^i3~9&vdz1qdj+SM-X~EF5Y2)b8Q#vf9Ff*oQrn^=dMSbFV+-* z`nT4CXtHFVO)gYn?BYH zQ&GO*yg&HiXWI4$ZUEhvI#qVP^xl7$UtQ_TYyA5icZTwr8rcijwMO^9XRXoa<%M5= z{)g}X`1_wJ2C{trk3W6?&;JDcmp}gSvzqjGpZ}#i{mtQ{?ph?3yyypJ){@6 zpO?j7B^^wyRX>gOq*`qJyl7qix}Wt@v{ilh=Vf@aQBKBz&7a2qO=)_O#v~O=Ki|C^ zAiTC{U-$DO<<{G2|8}(6-PDMVZEdvEOTW0hR`GbFAH_HEARij#E&iq)|E+mJ9;zZ&9JGwJ+sxf5SiJ_fMca(_{Jj-OH&^ zPdknL6;-53*eWjt6DO*8Kvtez`^5z$Doe|-mC6=;+p4Btjj{9)+iPX5r98FTzfw(h zvJmC3>eoI82U8o(w#RwdX|I#N8f`7(ySu+?k2HdvbOG;Br(exgt>ieXT03M7w&$(? zwly0UmFU`k)gLYGIJjCl{a5tU-gdYAr98Fc?yuUVr5qQO${&Agzh8E;CVu%>?bMDE z2UIIp`^sJWefjR?T)0rH+udKSDm$$p)2~X^j&rBwW2f~4$5yLVtHrMWwo$aL{|mn_ z)AJ_j*v`~)af7|*m-6m?*V=1O)%4p&V=qZ;|JA-pTpV9&&)r|ur+w^RyV&WsaX;>` z(jOdEdcXg+Sv28AKkawFd$}-*d-=odKeP92`W5vgJNDYc+mAN=HeTz0OHbcxF7Gvm zr@z&@{(SZCmp8u8f&HNz*_32kdfCfnWsBrqRtH<0_R^>Knp3vK?`1Qw6<(ggm%a2H zoMnxxq<7dJWxKqs^3!h{z3K1zJ?^qrj;&bJ-^Tm5jb?wYb$$A~amDm^D_mjEf&B(w`TO$a@6{{smgTpy|Fr)$E_`wH!a*yLy%qMT+IM=;=;H{hqjQi% z$KlofmIQYk&D($5oI7a8q~Dj}Z?5ESv*;i$vhDF8*_gj7x7oU0YnKs4+9$5-e@kER-#vyp*c1RfP+~hV^GU{a{7iKi@Bg;?ZDc<+ zc7M??>Es(5Ouwz4b~c`5(u-fYKjezUB&P4;w@pGkS*@L7cRQF)a-QI~YR{!^+ZAxq zQab%MdY>fJPEwaoQlISxa6kaQs#5NYez~N(37PzD^gYS(n!jy2o+Jz%*4KX(df{L% zNBf;*kDC4#o%BBJTufk&K*7b;$RM`vc$sLXztxTLz4kZuyvE+s`(hKxR(nBCV&my= zzb7(ldQ8tXjG8WvT(k$J|1G^1$6D)8|GTyehnw0b&0zXl2I||vfut~ z$3!|La{6uUaA4&0+xlx?___IT2#(bI&-vXbuh9WVV5O5it7q-;?T?2|ifzu}D|qeN z>+!4Z$gzYEuD#cP^}Gdnyx@=1^QZj2p}aPHXDw#^SBsd!3yR;awNHzc10(ZSWVFq> z|B9?_QF%FQalzl#%GKr&-&p%wY<-rr=G>t0`W4$Mn4z3~s?Ry~v-oAWibc;J!Hc-a ze!+`$gNt;7i!3(NZ)>)FhKl{f#I0&%+pyQL|H>cYaNFs(b$9x^lK-g zj^vPk663kr*MHk=zG!WaU#;zqMRml-MN-TWr;xK4#2iF=kudqnU+n8`)SvYG|H1Oa zRubY4&UyT|&8kb&X`tWYZ=4iuRzLpR*6?c!@x=47_?z7w)ArhaU#{8;OutQv zU8N(%uY|ZA>cj8LRa=7lzfBBWwPncPMz3qz5K^B0mIL+Le?Jl|BE9%eeSyd zYWN)gdH=T!uTu@{mn#8`SAR6Tju?$!4f9PFZvEo2YQvkq>Zerkn}0RTK9fECz6^hJ ze?Sw%KJM}8FYHoGtnG%kzu}7aw)jlHZP;(xkUseP;v)mougEB~h>vnR@VoztEpKAW zn-(z#kKLq_JCyJyRoub4{Z}fp19qq1+8-Qz>X)Rh4^!X&)$ob^eb8FLzig+aya2Q} z0oup{G}ZxVqJ?t}t#*J$763o8UrNU+iu=$7+9lPJ7Z%T)WEz0$Qt&32UZ4S~x%dm~ z7#ByPYZp_4@ypV6=a|MBbBsS%Oc|cU+EYp%Ff?@q!hw0or;ge^U7x+o=! z5#ifnRK_XU_XDH4T_jS6fN|OzsXy_vMlSS1Ron_o!Rg zc#@LEeErZBfJ?Ru;?p~TQnFd?0RSGU`Zf;R1VoK;RYckO^NvPqa>sVRt3){W8;HCm z-+y(ImMaegsDqB$q?k&m;@%Z8cV^J;ZCnHT%j&5QZc@1@FQ+0UOO^uoW%~(zrjwSs z?`FR4+nWFhMGnN(4pOpKQvkO|CXaq-4!{(f05`)-+x>e2aCot+sa|WBD_aJBY!~-6 z08QWc+`KFI8_sh@r_2Czvz%$=h658tvJs4j-)y}z$NN|-Y#`3CvkQ!)hOmmz{%`o%HpG3)KzIOp|Sh3tQ>)c_>xxK~P-coG_ zxP9gx$JA4f>E3`6(Xhmnx{R@ybci)avpq;MqJeQ;W*T|EDB1k9S7a_2lgR?l2UU2a zN;yf^NO|zmJOk?I7#h4-&j2;U{xRO^1wH`CM7%p#1kM0;*@vrlc7g#&hWS7j7_lQ~ z!PE;c-<<0*rURnJLB3W_zxA)&=8o|aKYORX3^mN?(<29N%rKJZ2lKaGV;r>8JedKN z*(h1Isdob=oi&6*^SDs5tW~Rkn`&uo?;FSe%^MTo#)W?StdPhephOQXF~k{7 z7?I^D^B(Ges9~od72wCyVVFpcO`kgD6?fK>)GD4Cp1hspiy^)ny+KB9zWA_h33R@$`yNY!rkKU8y?{Itw+Nuk1Ln135Xr^I!`~i1vzonmsP!g+Wi#jBL_v#WPQMwm z1gLxEk$Y~DY)>99_huN7#bIY}+)nN_3^CS;?MMTohRpzR4`V-FdI!I2*W^2yR z=^d*jK0?bjOiZ%|h-uEA1M<^RvUiGS%jUD^z}a))Y-v3MdNbTFD>L@A^^7C{?w2D< z?S-4a<8ox9B??rMWd>9Rpv_iYI8!HTcw1?I9Pji31F(9-u{wR;07~{;GQbonem5St zln!_oTZd<>e6wb{r@KdB8%r6oRA z6eQtZYX%v;1XwoPkw~HkwQvnk6_+*{DA|TN`x|>&8331K9mYP$`8y)!#qwJKB@Y-9 z$0#TbKx}leXup`_?S>?T*F7BoN}ez-$6L?K1c)JgQj#nw8<;xolJ zOdV@tcGvQ`rnnuq#*~uHSYHE_HhTajz_Y@7ppzBx^23ix$@#KbO^PK$QjZeY4^0fY ziTf0QMm(h)$UT(vXvrRaOTLT6+YIpVw|7d(9umtrpFrl3DQI=VK6@TX_}>Le$^D%s zau|RbMel>nJ!jOD0IIZ3^JL)tC41g@rEp3tnp){zO7;Le7kEO+U6fT1hckEe6ke-< zI_!h$ywfq_rmIa{07!F!=jICBa<<5x?vG5FV$yW5_o2O(imNUTZvBu|f@x1JE zz6g|(#ohMPgVD;m1z5XuN)s&HZchU!dB8x83F8)eJ-&9p>#_a%q>7SwlLw!Yt!B;u z_rg}g@K5-DF;j*w`dI+$l?{q{Q2-CJ; z22^oI$(C!60pfV4y5aN0RF*9zi+3LaeE!s_b8wl7bR$5?7J^%V&7Zf208M)*N5i`( z`8WeSD3*P<4Z!;q-CIDLCyW-7n|CKOz{K=6+*cRyd4FW8HorqDd5IxmWyg4%;%f|W zO-hE&0MpqrRGCsKdB9MzO&u!Lq;0+j4+4fyue890u39mK4|N4eDn(0B#FpGzO%WTwl}e4Aq$UxINJZ99*XM^h1?zvC)8 zMk)rWMg7heP#Z?c3bVp}+}aYDm@NQ7`99Kq>i4@07YW-Z90r zrKUHv@sup5jxpR7!fFfCI1@L3iP8bDe|znk$%5I8*Z#R}kolktw1O$0>?fH4C!ppEG6sShY zmI%%~J;%83(>om_zMOq-$~QSuN?u|Z!3kq(SbR7W|K3R?09-{6cZYO^=PVB#iw4Hr zqkS0y)eW_dJj3)yrhA+Hv{-J|8Q^Jm@Z53~8Dd!N^W^|w)Zq~**9P2q^t$~>qvf0u zMBl3M0w{UFP~cNAPbgVq><~ECD~Y$G_mZSkZ|vyAzI%t90r-*kfKFUNQXEOpqn9J) zq@c}SY8)yCrtt<6t9XD|5KUj0ee^FDH56 z9TUZx89BsNpj9+wYM`kNL&+CCAobLtGJ?@+Q?b`$V0SxhqZw%e+(Jum$E@+Pn>qlL zJYe7i&p5}dfiX260?c)qr^u0-$?N2a=litCtnPBZ2A~gL^En;9_xb-{ayf=&`uulJ z4d)|alckyC8`{7~j=TXNJi;r%CLn2eRzNUceF;O!wn=XS5*CihF_>)ASjMijwG4fTYyBeuR!6e4NRpejqt)U0p9XEpp)ZI zTJ0Ua3C4t50VK@M-Ue&|R{GCdfN9zmDBA3mY7LNlbYv%40nNK#1SolW$CcV<;DAny z2!7-hhU*BLdgbU!u9z~@SiN#ACD{qZ&Ay8Pi-U&gVn+9+B>Nd4f;$@vk3{DQW4?5p zBuCHGROe;^E+%5|~$PBI1Niz)iLN+c!t>xme%vmd2ElHE>&ijogm6 z0L7ocQIS>KSje-1#&o)uS52FF41NHlb?6BrG*vypIFmm6C$I}HJpY-xS98OM=QJn}%wv56g3bHKMOf$`7|03};x`g#4AqKf?-xAJ1NWF*;oMD?1CYxSI_BJu;HqEPNV|H<=R5$ zoG5!IKiNB*34ms;02(@B#N9U!z|8{BQ5qP9#f6TG03+f$^uicQ?vHF_yG?+5;nBK1 z4p8#rFTZZT?8Y}fC?%Vi<^yL&!PxE5@-EPK8}i7cGmAKH3Rdq*F-kVC%m9BSRiR(j z3DD{SXz$zyaO7{j)20Vkezt)FaLEel=(m9aw7wfd$+EJ1B>)o_2PBD=8@U0RKwbmn zD&rc`#;N^gQUHH0g~N|%sKhS^(cRCd0F?Z;a@<1c#CS5?9%&I^YTqeYQf&_SWqFc$ zNjo;R(2LMXe8~||1PXS8FPc0B;YXxqX zEJ02t0yCRJrofo;(w7dtJ=p;97LxG*`rM%@U_|E$kOqZ&Nuzuhpk%$n%qd=2{?>$< z07YOOl=p>qv}6E?l%#B^`xP(&v}J^v6wU(RK6rM`F#VC3kE7@+SzAW@U>#EdN}e$O ze8NZw&E65YXP9Mybg+eaEJ`+6Dt<@l2ip9wx1m{qE#dpi+#IWfFZa;Q8+)HC)bIdrHZ2VZ9H4 zcZ*y1KnSLY;$@By!-kmwVhG23;4{@%cLAbWzW7c_hu_N)aeb=;PS~b|%mLBVS#Wsd zuROa5uu$W@n)&iyQss+@?*b*Aet$=E2_Lcwlxl|y*lm_-2d2M#2cYCHz5_Mh@eU<> z-p&Azmz=@8^CpZq%t0l5uGxjFNYvN@JOrMujwK} zkM03vl)@BN?pg4JelkGQw@NmZbwY%f%`0C7O34quTsrvh%cW#f`MCfkTfLb99)9aN zSe3*yTY$UMyso8WkNP8kJV*U9c>ot^Ey6d89N508Jr4nv2-Y+lS3@~w*#^*NV^GOk z70$hMcnr`mIUnAh7Raj7>B&bsBBaUt1nqJMN5uju}V&)XDIe&|ZzZwvA z6d^-R-aI@8xHA^F4ve834eg=rD=67|&KjWcJIF@P%N^qudOzUgPK5AOdQq|!m4lqG zU_3~V0G446aiQ(CUz99s34psJYY7;!{sd@CO}LPFcLumS&3;13-cC4JhM_HvOp;l7 zXtfarV-i@Ia9|9N+=wp%RQbhsO6?C9*k4{P*}DM;>c9r3U;vKcIXH&L08wq;7R&$* ztuJ^XGnnSh8X#gS6Vxj&YqE|Q9(n#9t)DoR6OTj|2gYy*k4AI^%Jn>V9#O^0BMq*y zGx3hq0B2`nE>tN($=(4w7t`Trb=d(nU|hSE<6{D5(4*N=0JY1&HwQFuM{CJW)Wkde zQ3GJ&dV|u@Ozdk?2y}Bl&0$Wj#3K{;ivT3};yd)=P46^LrDXG^LwArbn#a!01V+7Z zI1Qpl9Oft*;`YRjzCtB1uMc7*T$OOCd?i9D;nEAK>ofo-TW=ieLTsNOSqt=?>PShq z+49mcDm>Ek@I5PFT&V(y!I%ggu$XiXCBg^Ag(E(Y}4?S}2(rVzj;(tQqOE5Ti z<=_Yv0MlX%a6heZD5QiITVwF;A_PUMdv>@V>`VvHu$|2rGfaO}`#=bsJT7y&PoWn>mzhRd^H7==h=)LX}0ji&tc4 zF(Q#>r5L+ZI1CoKnB(l=o?~3DW$f(b{zwCG@pf7OO&>7S^*)UG-5HBeE=|)7z!owm zCPEYSV0&G^cw2XAdpFwbS>c>6U?e>3IzKp%34nOcH-H>`sF9 z3{Zh~l=Hb*e*{X&@yKo!d)3L_>vudWoD1S$_d0;+EP#?HjN4Ms@`Q0y6|+YEbC)B95RBiE zMH5V82mrT7oA;xxrh#Ib4DSY9a#JH|vli7k;CJK&C1<<4EC9wT{vIBff>I*qnEuH9 zoL=q~xWig)`Mf@&Sp%lj<-yhrIDQhF1Gs(KW6d%BQ5^tEmLYQvxT%V7;E|a|>o^$q zqEDd-W5${=y{UHrheu%IR9$xg!rivs5e*$4!RbVG$8iy)+7}TV4gp5g7%{IrQcoQq z0Y<%e2v86Ow{p8*@~?t%Q#FNPqvHG^@xNOv4S0@m-Lejl%U`1F%K(IX{AHT)_{+~M z*?n(oVkueE!yy!0gW&O#Sq_ZE$qewYNpI#Ildb`jJYeW@$5;%Ib+@{+u%b&1MvJOg_{~b0Vb3ur$R4i^Mo;>6pXy4)XwB{-0(88gdf3=cLZW-Zem?qU;`}m`Cg3N(BPiV6g-x*+m;v68H%Qn+^k~`i4 z&y>e+0;E?s__y&5Jn-eY8P*7h@)mt2Rk--Ejt&t3 z&>J&A-SZtxT<#*ZlDM>19D!Q`JniJjCnnMJ&#rwiF3>ZOPz8N%jwi$*F#f77my~Sg z!+ENgt!U0L{>ofvuR0z|e`R*GpPfhk%Is)AJC8*7_W-ony6k&CXP7((a1W@<4WMKh zK#xvS(E$Wr@f-2CM@!o}Fs8ft-4`SsXMp(B-gy!vcx)QSQMVb*zHcxpne9nys4hQx zJ)*`N(*ae1I;lr(T_7sZGM^>ck2>I&WraB^&jVGgU|g5{Mn_*!1Rzo}II6j_m@xjzI^I-Hn**yQE0lItsTIQG z7T}(GE;+`4;aWa|EW&*ELc^F;M=vzK<_V0X?qh)Yz*A9)KIqK?z@CdnpUQLT%T?>(Z0TaQd|#ZK_@5>rZEVoHI}z`VKSPN**drDVP1JP#9Q z_DG6A$8^8}B-2+)3b80z%;pQWNTQS&vHIF0F^L`#F}m`37HEuxm!npQFcXuUEsqqn zLdjNH9g)IvqFH&i*5Du$9=Tz*jm|LrQR6x$dGU<0s<>HJ9-RGvFgHK)wAYnaeB~8+ zhB3X`%QW2!sA5F;%5(cHrvRy{G>|il%drK+aU%rT)t*zbqC1X`(*Q70tPsvHCT?e} zdjKWhdqm^E=FxwjB|9m^@nX$+6Od%_Od1 zTLkY^;X$CUH+IeqflegGy4dRJcoY((xuMp!FJ3MkkwVaBRXHfxTC#7<0;752+zVh# z3%U1a7|)979jlcyjJSD*k!m-?^h#ZqZ4plIKHcuEeOqm~ENjs;8tpRGr)k>q|Vfd;F0^(;a zfDOQo#VyAJi3(p}gWjuVISd7i$nES5bBrI^N2Rb6sLB*mbhnx`!5y_$O4dWSB!tXrTss1WKDdT2^@ zC#cl=(hSo}_E(44@4XfFEh^&sERHXbN$vpiN(Z)|ok`)jr4c&kZzVFagXm25$q6)p)0W{w1K4 z{H1qZ-}TOarzLB}BVDUUX95~u@~Cn143K?-vm+h?0q|V$w2|x8d?4c)7_*UPBsL{; zm=;GcW*SX9Fina8w7JeH6@Mi*2jiwsfSWEu0q?jE9ni*w3&(387(Xpob3nB_xdY}e z^}=xoU_=5ZcL1ZhcFilazRS(h z(gVgdwfROvJ@M86GqF6SyzBzy4h7Q-bPL-fN|tdNz~wvzC?%VM?<`PlcFY6E-m3H= zxZTU+3{c~pQ-XKY)5m~1I+QGf`Zgf)Iuivii;Y%*wkA`u7iVtkXyd*KxC$A<_}tx@ z0JmA1;fd=ARPq*JLdguzS8jXE+Hvy6mYvuGtJW!yQS;s}-S1}V$kF==z-V?t=zuxvv9UF-I zwy!p7H?K@>Ht$+yJyw?qH8sq0dn(yn*x%7M_e4HJvB*P!M@P`(;r9@FS}jBq`F%6GjC(b_0K!8a4uyeDBfg*FAcF$s!cVMAR?s zR8ORg5H+;<1f!u`0W45_Q3=jKanuZGyz$)&oWR`nWg>Gx+~c4Q9=S?c9>JL09gs(w zqs?`+pv8cpE|~=Z{P`n*S8}CgpyUZ-dU87QH*CV_bH}aBU+#~@ zA_SYak_B+AV8FaSh!Ot~Z0-V=03~~FJ0b>*h_D8@VRE|jvU)lKlU7cL-FYyc=(#(MxaUQTW>ZlU?xK^)uyq8aE+ z4`B4NVmCO#;I+cB8(=i1GmP5o3r~2bW7Ose(+onTD5^PB@KPG>Wgo$z2p!xFqIK=JOQ|rxBoJ@0Sd~G{ud2 za=HlsH{F`Y*?NXANjN#Z1DQbFtO;=U+W1kWh0v3M4=UN+vC;2r3F;&jC&wKCm~`gL zvuzIwK%mW5#C^vC7*}Bl>ZB9|BALA2^q(yzodn{fBoLenFcaIs4o2;{SWNmt2JRcu zM#X;hIi^3V03}Z^FCV>bKT_kJD1w*Wo(7bXmzYvuVnm;dMc_p;9^NfJS3 zFJ9^G!$(rM>DIL`W^`ZBaJtxpd};ED_Q_=fpeb%g{T!qD=Svb0MsCkNfRgV$qB|>( zOaiOa7jvO~=H%6><@mK2jAxks zxtL~pFj_IHK9SJjZ(ra{Ho+07&YBq z&^bo!o8D10Bbx&;gaBGIV5sYD81-`}8OF}0HkFdipfkWPORAAd&;8Q=4vgQCBEO3?7-?H*;0@^PSfv)bY0qWZGD%ZKOCmh^Swt9^J^YWB{Or1`+=OTZt-&g zYI<6Mo&(K@T1xh?Z2=wt>EnFWF&+sE0upzXu_-0@M`l@LoM-&(^VkmpRW=i;uedfW zn<6*~S{g%;n`8uJT_W$TdTNK#*~hQ}YB`jwuzh1*0n`hh$L6$@IQ0zxCBNgVQoBg! zlirlm+ao%25LaFHl4WNV2H)0Y7__(+&YV)yt5(Q-q?kvh;0oXhJfZD|I_m^7-LpFH z{N-TWP^&~7zfkQ~=bgbEgS2b+jMVX9N;c~g(XG$TM)oY7*8mATf_&Ro_CE1)eMB_B zRsQkOE04r0vqx&-4AZMv0;S|7rWB}woB(fE37u36zX(wB);sR0XNA0K)V_tO{VFB9 zgI471UgMD%z{jWY8)ztfY8s4&dji^`O37Bq)&PIy>EYAPd}WGxyKF}khY~eiaV?8< zTY%VR_Ob_8@fH-g#L#AMfi{8um6S%GwdV80Xu9H-b(>99PhecG?|)SEx8e%oIHvhv z1u!kF!a7|&U-d`sm4pY6%p%_E*}2CVP!GUEfJ>H%lw9JKjUcx-WOfOHaeF4fpUdpU zJ7!r0f}F84i-HZHhAAbRMW%Pm8573T zuvqj#@w5G+0RF0}*&M^La*h?L5o!%k?O0p_Nm znjb*KWLu*u8bHYt#?7#K!(LC07yENB;AoaA=N>@G7CLs3f^j($pb_*bY2I;n5@^~r zC41^2#?%bYCLfpP9rx5qH3RRL-Ba6CoMfLI`z~+-T#l!eeVn}Pay-9$+M09N?rED} zwAo9z0R&3%&+)*ecmcQblZrIoW`N7}B7a^1D)UjLS9_o0rok@I%l_H?ZEq^?xC*PR zGfbr*XFt3G`v5Z-5pIUj9GGD=IcFFVSC~?=7;AdRlflkUi0pD~A)I0QBbU=&rerU( zGoZiR0gc5}ptrRb&y$OnVgZ!wjqdcw6UJwv&lg*+-vCguXO|C9!#t*{*Fc}7=8?NR z0pfTcvgWD?EeZD2f@xpDj>VOD_8s;B9?=PK-Bw~Rwq(x$H^SPiU9WiiO=at@7cVT% z(N7?NMBv4&KLf;>?1p6%2H zP=QyQQf3%6!+u)kud2Apxj)DBN21TwZ0+OHysUw|TE<_^msj(p9KDiYq}xbdELJ|@niI%t&KXObtPFB9OA={x~$vrQsb?@DKY>1;jB zUUdo-!R?FZ+8VXlzIZTdvrnso5y5Y5E#tt47qy$``V3H;Z=U@=?9Nwe{LO3G3{acx z?&sy+R0GxmeO8`D+fAJSH`QCuTN5y}*^1<)<*aRNA&0d;)+?_|J zv(;ROB%lhp%@g1@dqLNo3bfg(@Xcb<0FtFTGH;faGoaUP!1B|<4_tcWMofSk;f=Wi zBzRd8^=64W157yYwH-~tS0sQ^m0<}(A_X4~U$ zUUO!E$?H|fVJ8$=8(&IxnH1A0>pcwxIufZOba%@<9a1yHirvl-wvdttkIJ(~fhvllkUx=^6$ z?Dgz60lldPyq-CM0*R>H<_R#J+X_dUy`Iegx7jP)&Fk3=aGSlb`GN`xbep}N-6o(n zwE={+w)Jcdv^Ilj0Ai?kyZO6~@f>K40Mmgs*kC%)7!*tgdfgS^%#>EDIp9WUrQ%T| z+#F~O3Z?^%a9}D>N_Lwkz)jVX#XD~51h}bMsd&e2ZU7}~rJ4h7vsNk|xy=*crfQ|) z9k;mwrDV5x0^DY;RJ`LhPk`I3m5O)V<_1u*R;oGRHfyEgk=r~0ZnIV@-f^27P)c^2 zC%|pihLv~R<_U0{wZid^+uQ(3*1|Ri+-5CoJaU^Sz-`vT#yf6v14_wm^8~ofTG)8U zZJq$PSt}gxxXlfqWUXg&z-`vT#v`|R0^H`lKS7|Bi6`sQvw_l3H~kSnE8fMSX>-8c zYil4SYcT|HKLvdEf-G#a`UM%NnTil>v+o6&*QVABLa)t|7i3)H{1;@KxEYz&IP?W2 zy>Z=32@`9q1i0glhGM4!+>;nr;DJPH75whc|NQ0i55N8KKmXq^G`B8W1^%-?`}Xht z+dqH)_OHMH$IpLw`Sx!<|KZ0!{`8yAe|Ww4;*W3tm*4&N4}bsKdMsO>c2*M8&f4Ml zuASB%A9g=Tk@f*Xx#x&UZyaeWD^s`{8g=2|t1hmCFbz+{TVra1)>?D8fut3&^wrfbI~tbIr1trKC;Adn)Y?M?kRBb>a&7@= zEhJ#4RYAl}J{;zinm#i`!bWgP>yaI@_;4G=$nDvB%Ba@b zOBy@9?3ip$-?a{M#IA`Pj-s`9$P3VDh!?Jv!*R*h3p_@`5Ux~5kaHZZQrsu{s&A#d z^x+I>?-0ILdrE+h4FSI491nCW5RX`+2G(R@oW~Hei7?Jq$JV7`Sle3*5h$%Y-@SbM zs~`XP!_Tj`-C~;6zRjoEU;OaHk3WC=w}1T4KbJ}OAAk45fBE)*{PCyXe*Wq2%Yc3Q zjtTgipa1@{`=*Qz$F#qpS)6tHhIrI+j+8&YqoyBz{`|wwfB3V%{11QmmuM+?Pv|tc zf%s*=USK>KpU%JHzmk;d@aNzD`tu)t{(JZF+yCSL{J&n|6pTmT6vBS@?ce_V(;t8H z^Iw1e6UDv~a=Pq88M^oPp*DKz!)dh-L;$q5(`L@gw06@6{;_OkTOVtmfC=fU z{e=eS|4{yRKQ*ZCCs!AI!!SiR_sedUv3-9xYrm#$p4Yosnp~RuYyDS-il*c7nVK)@ z>DKRer*B~BX#K!wg%97}`s?|d5?;&kyal6O)>~g%O6$v@l)