From d42b821c0067a0065e49a68cce406fa1f866281c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 3 Jan 2026 16:59:35 -0800 Subject: [PATCH] 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 | 6688 +++++++++-------- 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 | 448 +- 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 | 88 +- 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 | 468 +- 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 | 80 +- include/sta/NetworkCmp.hh | 19 +- include/sta/ObjectId.hh | 6 +- include/sta/ObjectTable.hh | 18 +- include/sta/Parasitics.hh | 210 +- 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 | 795 +- include/sta/SearchClass.hh | 67 +- include/sta/SearchPred.hh | 114 +- include/sta/Sequential.hh | 20 +- include/sta/Set.hh | 220 - include/sta/Sta.hh | 1704 +++-- 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 | 1471 ++-- 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 | 634 +- 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 | 215 +- sdc/DataCheck.cc | 40 +- sdc/DeratingFactors.cc | 74 +- sdc/DisabledPorts.cc | 28 +- sdc/ExceptionPath.cc | 803 +- sdc/InputDrive.cc | 70 +- sdc/PinPair.cc | 6 +- sdc/PortDelay.cc | 12 +- sdc/PortExtCap.cc | 48 +- sdc/Sdc.cc | 2761 +++---- sdc/Sdc.i | 1219 +-- 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 | 237 +- search/CheckMaxSkews.hh | 63 +- search/CheckMinPeriods.cc | 234 +- search/CheckMinPeriods.hh | 64 +- search/CheckMinPulseWidths.cc | 364 +- 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 | 640 +- search/ClkSkew.hh | 40 +- search/Corner.cc | 461 -- search/Crpr.cc | 133 +- search/Crpr.hh | 62 +- search/FindRegister.cc | 458 +- search/FindRegister.hh | 40 +- search/GatedClk.cc | 211 +- search/GatedClk.hh | 32 +- search/Genclks.cc | 661 +- 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 | 203 +- search/PathAnalysisPt.cc | 73 - search/PathEnd.cc | 586 +- search/PathEnum.cc | 294 +- search/PathEnum.hh | 42 +- search/PathExpanded.cc | 93 +- search/PathGroup.cc | 622 +- search/Property.cc | 505 +- search/Property.i | 24 +- search/ReportPath.cc | 1285 ++-- search/ReportPath.hh | 443 +- search/Scene.cc | 187 + search/Search.cc | 2825 ++++--- search/Search.i | 650 +- search/Search.tcl | 493 +- search/SearchPred.cc | 270 +- search/Sim.cc | 694 +- search/Sim.hh | 171 +- search/Sta.cc | 2974 ++++---- 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 | 240 +- 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, 33435 insertions(+), 32562 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 f41fcac0..dda0ca9d 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 4c7e56e9..baa1c0ea 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 97040fd7..5be334ea 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 4b8d340d..16595a28 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 2cbcf3b5..6babfce6 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 d20ca928..2f6fcbfd 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 05a11b71..77512413 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 62f973c9..ac6705d8 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 @@ public: 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 @@ 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; 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 @@ private: 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 43d57f46..f05f8abc 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 14e5d68a..96297bb7 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 f5462661..e068a73d 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 389a7776..27c5159a 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 1c5b89ed..e69de29b 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 77fc7ed1..bde9248c 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 e3494286..a0bbaea6 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 b920e4b0..e195fa23 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 8c15bfdb..ecaa16d4 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 82597f7e..6caa4f6c 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 e5b8f85e..d759fc40 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 @@ private: 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 @@ public: 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 @@ protected: 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 @@ protected: // 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 @@ protected: }; 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 @@ public: 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 e8515151..fc3fbb2b 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 9081a65d..5e8b37b0 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 @@ public: 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 @@ public: 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 @@ private: 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 ded4bec0..b74d7d16 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 e4d4d854..7224671d 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 6fcaa870..f75883c9 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 33436662..948e277f 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 5170d7a5..5cf829bd 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 5aa200ed..0500b57d 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 5679d7ae..aeb1d9ff 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 8bb5b18c..f18c785d 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 40c8bc14..45f86814 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 bce55d36..6379b85e 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 454fdc98..20e84c24 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 63f88a4b..c32197fb 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 b0491c0b..094626cd 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 d2ad43f8..553db6b3 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 932339f8..9c8c8220 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 f7a17c62..39de2a67 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 - - - [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] - - - - - -hierarchical - - - Searches hierarchy levels below the current instance for matches. - - - - -hsc separator + + get_cells - - Character to use to separate hierarchical instance names in patterns. + + [-hierarchical][-hsc separator][-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] - -filter expr + -hierarchical - 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. + Searches hierarchy levels below the current instance for matches. - -regexp + -hsc separator - Use regular expression matching instead of glob pattern matching. + Character to use to separate hierarchical instance names in patterns. - -nocase + -filter expr - Ignore case when matching. Only valid with –regexp. + 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 + -regexp - Do not warn if no matches are found. + Use regular expression matching instead of glob pattern matching. - -of_objects objects + -nocase - 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. + Ignore case when matching. Only valid with –regexp. - patterns + -quiet + + + Do not warn if no matches are found. + + + + + -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. + + + + + 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 + + + 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. - -filter expr + -quiet - 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. + Do not warn if no matches are found. - -quiet - - - 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 - - - [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns - - - - - -of_objects objects - - - A list of instance objects. - - - - - -hsc separator - - - Character that separates the library name and cell name in patterns. Defaults to ‘/’. - - - - - -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. - - - - - -regexp - - - Use regular expression matching instead of glob pattern matching. - - - - - -nocase - - - Ignore case when matching. Only valid with –regexp. - - - - -quiet + + get_lib_cells - - Do not warn if no matches are found. + + [-of_objects objects][-hsc separator][-filter expr][-regexp][-nocase][-quiet]patterns + + + + + -of_objects objects + + + A list of instance objects. - patterns + -hsc separator + + + Character that separates the library name and cell name in patterns. Defaults to ‘/’. + + + + + -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. + + + + + -regexp + + + Use regular expression matching instead of glob pattern matching. + + + + + -nocase + + + Ignore case when matching. Only valid with –regexp. + + + + + -quiet + + + Do not warn if no matches are found. + + + + + 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 + + + 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. - -filter expr + -regexp - 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. + Use regular expression matching instead of glob pattern matching. - -regexp + -nocase - Use regular expression matching instead of glob pattern matching. + Ignore case when matching. Only valid with –regexp. - -nocase + -quiet - Ignore case when matching. Only valid with –regexp. + Do not warn if no matches are found. - -quiet - - - 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 + + + [-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + + - - get_ports + + -filter expr - - [-filter expr][-regexp][-nocase][-quiet][-of_objects objects][patterns] + + 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. - -filter expr + -regexp - 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. + Use regular expression matching instead of glob pattern matching. - -regexp + -nocase - Use regular expression matching instead of glob pattern matching. + Ignore case when matching. Only valid with –regexp. - -nocase + -quiet - Ignore case when matching. Only valid with –regexp. + Do not warn if no matches are found. - -quiet + -of_objects objects - Do not warn if no matches are found. + The name of net or a list of nets returned by get_nets. - -of_objects objects - - - 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 - -echo + mode_name - Print each command before evaluating it. + Mode for the SDC commands in the file. - filename + -echo - SDC command file. + Print each command before evaluating it. + + + + + filename + + + 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 + [-name name][-keep_capacitive_coupling][-coupling_reduction_factor factor][-reduce][-path path]filename - + - -min + name - Annotate parasitics for min delays. + The name of the SPEF parasitics to use for defining scenes. The default is the base name of filename. - + - -max + path - Annotate parasitics for max delays. + Hierarchical block instance path to annotate with parasitics. - + - path + ‑keep_capacitive_coupling - Hierarchical block instance path to annotate with parasitics. + Keep coupling capacitors in parasitic networks rather than converting them to grounded capacitors. - + - -corner corner + ‑coupling_reduction_factorfactor - Annotate parasitics for one process corner. + Factor to multiply coupling capacitance by when reducing parasitic networks. The default value is 1.0. - - + - ‑keep_capacitive_coupling + filename - Keep coupling capacitors in parasitic networks rather than converting them to grounded capacitors. - - - - - ‑coupling_reduction_factorfactor - - - Factor to multiply coupling capacitance by when reducing parasitic networks. The default value is 1.0. - - - - - -reduce - - - Reduce detailed parasitics and do not save the detailed parastic network. - - - - - 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 + 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 ‘/’. - filename + mode_name - The name of the VCD file to read. + Mode to annotate activities. + + + + + filename + + + 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 + + + A list of instances to swap the cell. - instance_list + replacement_cell - A list of instances to swap the 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. - - - - - -max_line lines - - - Maximum number of lines listed by the report_annotated and ‑report_unannotated options. - - - - - -report_annotated - - - Report annotated timing arcs. - - - - - -report_unannotated - - - Report unannotated timing arcs. + Report annotated max skew checks. - -constant_arcs + -max_line lines - Report separate annotation counts for arcs disabled by logic constants (set_logic_one, set_logic_zero). + Maximum number of lines listed by the report_annotated and ‑report_unannotated options. + + + + + -report_annotated + + + Report annotated timing arcs. + + + + + -report_unannotated + + + Report unannotated timing arcs. + + + + + -constant_arcs + + + 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. - - - - - -slack_max max_slack - - - Only report paths with less slack than max_slack. + Report paths for one process corner. The default is to report paths for all process corners. - -slack_min min_slack + max_slack - Only report paths with more slack than min_slack. + Only report paths with less slack than max_slack. - -sort_by_slack + min_slack - Sort paths by slack rather than slack grouped by path group. + Only report paths with more slack than min_slack. - -path_group groups + -sort_by_slack - List of path groups to report. The default is to report all path groups. + Sort paths by slack rather than slack grouped by path group. - -format end + groups - Report path ends in one line with delay, required time and slack. + List of path groups to report. The default is to report all path groups. - -format full + -format end - Report path start and end points and the path. This is the default path type. + Report path ends in one line with delay, required time and slack. - -format full_clock + -format full - Report path start and end points, the path, and the source and and target clock paths. + Report path start and end points and the path. This is the default path type. - -format full_clock_expanded + -format full_clock - 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. - -format short + -format full_clock_expanded - Report only path start and end points. + 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 summary + -format short - Report only path ends with delay. + Report only path start and end points. - -format json + -format summary - Report in json format. -fields is ignored. + Report only path ends with delay. - -fields fields + -format json - List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr + Report in json format. -fields is ignored. - -digits digits + fields - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + List of capacitance|slew|input_pins|hierarchical_pins|nets|fanout|src_attr - -no_line_splits + digits - Do not split long lines into multiple lines. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + + + + + -no_line_splits + + + 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. - - - - - -min_pulse_width - - - Report min pulse width design rule checks. + Report max transition design rule checks. - -min_period + -max_skew - Report min period design rule checks. + Report max skew design rule checks. - -min_slew + -min_pulse_width - Report min slew design rule checks. + Report min pulse width design rule checks. - -digits digits + -min_period - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + Report min period design rule checks. - -no_split_lines + -min_slew - Do not split long lines into multiple lines. + Report min slew design rule checks. + + + + + -digits digits + + + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + + + + + -no_split_lines + + + Do not split long lines into multiple lines. @@ -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] - -clock clocks + clocks - The clocks to report. + The clocks to report. The default value is all c - -include_internal_latency + scenes - Include internal clock latency from liberty min/max_clock_tree_path timing groups. + Report clocks for scenes. The default value is all clocks in scenes modes. - -digits digits + -include_internal_latency - The number of digits to report for delays. + Include internal clock latency from liberty min/max_clock_tree_path timing groups. + + + + + digits + + + 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 + -setup - Report skew for setup checks. + Report skew for setup checks. - -hold + -hold - Report skew for hold checks. + Report skew for hold checks. - -clock clocks + clocks - The clocks to report. + The clocks to report. The default value is all clocks in scenes modes. - -include_internal_latency + scenes - Include internal clock latency from liberty min/max_clock_tree_path timing groups. + Report clocks for scenes. The default value is all scenes. - -digits digits + -include_internal_latency - The number of digits to report for delays. + Include internal clock latency from liberty min/max_clock_tree_path timing groups. + + + + + -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][-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 + + + Report delay calculation for min delays. - -min + -max - Report delay calculation for min delays. + Report delay calculation for max delays. - -max + -digits digits - Report delay calculation for max delays. - - - - - -digits digits - - - The number of digits after the decimal point to report. The default is sta_report_default_digits. + 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 - - - The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + [-digits digits]net_path[> filename][>> filename] - net_path + -digits digits - Hierarchical path to a net. + The number of digits after the decimal point to report. The default value is the variable sta_report_default_digits. + + + + + net_path + + + 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. - 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 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 + + + Annotate the rising transition. - -rise + -fall - Annotate the rising transition. + Annotate the falling transition. - -fall + scene - Annotate the falling transition. + Annotate delays for scene. - -corner corner + -min - Annotate delays for process corner. + Annotate the minimum transition time. - -min + -max - Annotate the minimum transition time. + Annotate the maximum transition time. - -max + slew - Annotate the maximum transition time. + The pin transition time. - slew + pin_list - The pin transition time. - - - - - 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 + + + The gating clock is active high (pin and instance objects only). - -high + -low - The gating clock is active high (pin and instance objects only). + The gating clock is active low (pin and instance objects only). - -low + objects - The gating clock is active low (pin and instance objects only). - - - - - 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 + + + Set cycle count for falling path edges. - -fall - - - 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 + + + The operating condition to use for max paths and setup checks. - -max max_condition + -min_library min_lib - The operating condition to use for max paths and setup checks. + The name of the library that contains min_condition. - -min_library min_lib + -max_library max_lib - The name of the library that contains min_condition. - - - - - -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 + + + 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. - -activity activity + -density density - 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. + Transitions per library time unit. - -density density + -duty duty - Transitions per library time unit. + The duty, or probability the signal is high (0 <= duty <= 1.0). Defaults to 0.5. - -duty duty + -clock clock - The duty, or probability the signal is high (0 <= duty <= 1.0). Defaults to 0.5. - - - - - -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 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ⅆ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

V=zF6OD_1` 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ÿUm zZUV?0TW!m!ozhm7GVD{7=hNd6ucZf@&*b@-4nNwnZfWwhubpQgFEM9V`sBpz&`Y86 zYEPtZ_?2D8`T8CKccmp-%q5{r~h0FF-00yxgZ9Y( zg0bE~#9=FLXi_s=0NbnZp4T^AT75%pc5jUIU!LG^_)}n>mRY7U8Ld&$6z>mS`uc`T z{6@tHt?jcF)oRFU<=so|k8pEZy17>-LG^d}fN^!RVf^kK1K2zm9n6DsFR;+{C&T$~ zY9wZ|kA)sPhk-`HoY%b%>qC({3#lnxe^5QV8y7WW*2Bb0W%yr;`~vp~^(T1h%Xi{O z=1zs5eo+}~ked+5NF0oYVgusem-`5pg;%AFa zm>yZ_m3GC8#!2SpYb?Kzjw5ie<1Q>|IqMzCODr}i&S~Ws!yZhdo^4=Q;8)`NyKo#p z=i2i0YhOX=Rg)wC&izX3fNT2}Ms0FuvB{(B40>vp`hX(>lw_OP^cyZYI4 zdF0BX3hf>~necn?E3jqO&+GHGDbD^d(Y+&jvzsp|L zgTKaalC*`}mBk|pzSy|YQ5MTQa+4AK@=5=k_iXY1;ve$j%1GR<^J0W+D@J&$_m+4J z0fx3?IF*&$&cY29rh9U`AUd!SY|$}KdB3)l_i7uIRR%!2DDQ6bdWZ8$bX+$aMTmP{ z?x=OHF&Eeart<}(h`ACI_)xEzagT1IDbUD!9Q$j`v43|ECVA0l-9d;-fR#9tVH{Ti zxC*~a4cDSDz`v{DuZ&AJGJ+icYj*?c8q1$ffS{}(Bm_BD>=YJOpt*v$vYO{Dd*&KD zC$(gizC7w0GkztadMnY6`rsO~zTHT4nm22=_`L@aiFEmYmTlL(cr~=sLgFz z7vJeWLOj#uRbS!qzGmP`L~++Mp5vX57}3BrX2kCt3%(8;DNj!E z>_d~C z>1(bjPpHR807O+PyTbG!W~7Sj%$#}yRTv(Dr^7oHSMI=Mj8M{3&F+nnvhAb0lfL10 zr}&DytYs|VIp|xeoS7bquQ$!-l08?~)PB%`=uq?>My4i3GdFL3= z0al-Y!VFHi{$w}>ih%V-Vs;S@5#+DlXC5D*{OR7O8p#brfs0nRP(8dG7d2xR-2&d` z$|ME+MMX@i;rd+qEA_?g_6hirxl`hY?Q1d@q1Erx@yfQUiFo*SMwut3Uzz+lnOfq7 zp&?^)-hw&6;H@WWU^U$v`#dnk$m~gMmMMQChr&xL*@Wga})#hG2^(nm9 zSY1(Ya=;T3C(0B3^%s53kB;Lwsl;X;eg`LE%9c(#@7xoip@Suyt%`SA4aAu^$vIZ{ zDJ+zRIO)|+z=>HJ#0*U_YchLZ;RJ1(bwGSm{obydwLbO7jb9l(v_e%~-wkovA54>C zJvY1-9;$ZY)M?9szzSiXHw|>3(jJv-wiA{XRReM@$`93pn5irr$6A3?90x&)?nkP9 zd`sl%v6WPu2>U)?`5NCDKf(^_74$YUAZiNRSlA$4a9-vGV)|P}q?GthPeeFjqnR=? zmdBiclcIxxRdT_t;xlFidwW_e4^WUn445;_SWzKkpm!v)H-8q6g&J-A)<&6xSrDN{ z=k+8mR$F3CUsG|NR}WlU^}sMq3<~+qlak#27$UuSf6hD_BEmd*m8e}7@17j5Agkyl zo7aGJaJ^-Rcl4HT1RrGXmL5FayWL(-_u_J`&n>^)yDr=3jZB!{?!A`VF>z3wvmy@k zawfo5X8m`(DoFJP7YbB)6c)<0D1?-2;wL&pUDt{(s&znm1)* z%!Yoqc{AI`%7S;BHyb#ytmGRBN+>;-Yk7qPa|-?;uP89B;2&}vu}}~HoU1})Jmub4 z<{;*SR)0`K#!EACvcqhk`!T~ozP37oVHcu_VEJ4h^`;DP22m5*mFoOk7gyFFn0Uwh zfY}650$0I7aEl)HE_8WcGqZiH1d-mQY_CSJ&%4XDK7MiSKYnqIWiMskKj3aTu_<@= z59|kGkvy^S3o>unsnB7HAO!a5POZ0CebDCbTProVmggnXV`dsz{7binXk*~kiVbR` zJ7a(cV?f2d+ z(!l5$0C&H^tTDO>%KQjnJAHETb`D_;?dakZG*j?_I zBp#Y7rn+D^9`Xdea1a0)_$$xL_^a$Mu_ZdViFa^#-{J^vbjB38>8@4zYpyBFYPrpr zlUB?bGbbdnR5xeeVp6+sCUdwsaZh%`z4=RTOWvq)-xAq7{Bpo#7ow~tw))u!HezV0 z;r3~i7Fyb8H5XPeCi}#zG3kOc@l;k)DmleEgfhPz@Px#P@U{adVvsNSLF%VZbcz)8-r$~LiTV1yH9h^xK=C%P8D&8r7) zUYmOTd5fh3JG&0HxVBKZI1|y+$`WeY*gIub&OIBCrp&^**Ik_9bKP2%&9HYeup-zO zm4NCKeeGLeU(1I+!~YiZeWl~5JW+AH&v7t^vP7&7DX?!JUwG3y!Gknx1Uo&(+Jhl> zn$7XrR}}sFnE5uZ7r3>0f!gLi7vPDN?u9}84L>Gxp>n_zAx@kGoMuhGPo0JLq;*Oe zSu{CIHnCX%#ySOOoAB1^6KanT1Nyw3f9MWSz_3*&)HZj=K)-07QYPG3W-^%&WUh?R z*jFap;=AdkiFym;eI~+WqB7NhgPZYjz%~&+3O`r(DV%;!C?7fK(lVaoEjKRl)4$_cKmEmdjRu;9eR80;d3nmM)sIx90{v!z z6zfNVLxS4sOmK@+D^7eEEj*fQ1e5}pUoab~5yyfJ0lZdv@K)cfMj5k{?nfNt8F;OP zVAzEyDP2DAmwEtzS2cPE0JnKr#*L*1DqBLivU~tsdZ>Fg{oOB^*(RpKitRbC*XFSf zBaXSvzhdC^9O0x56RuLdY1kQrQQ6$))eUZ!JI_VjVmkD@z3%8IxHoS4l^AV~afSdH ztll~PY1LKVSGd(9YK{Q@Uvpizu!-s4F=5?QTCrrStOO3gT+Xiw=KXqzzUG=Y&#yNY z;VFd2nYq-TMel=$1^Wj-M80=>ARx<(ebo(;dMGf(A`cNX{EmTYGaj0|HF_&T0^OZ7 za(9Tu@vVAd7`{<{m-})>=AiqO=0>+qqa`4#Bb3$P3A6JP3reIX&Rc~8jCtzUqYkH^ z6Feays63IG3A#^d0(L!d0tD+*9k(KHQ9X#Y0|J7rcAy3+9B*o?SoYw}3{LZyy>yqu zl1aoB>oQcV?nOB3F;W%X7e3r#DNK#9-WH12rx@#vxbnv4GZRnzM!^W{Om9~9!?P#L z2Qn67OM50oxf{-|<9iERe8enLV-oz{v2^rxxA^4(i3+Fpn)7mt)dzcfZ?bOu_Ju^p zndegV4TM`^!UHFDqqe+(u&^2Iwj|O0+|W4zJ32cH?2r!)c1Y9X_lY;?juc2mHU>-e zOSD)$AmXr94>Z{sW58DwPU*Mqz(kC+ih$azm#s#HbzcW?8?evw8kl7#Di?V@iSnip zR^AFTU7ILOWh1n<+JtH~@Y3mx7b8r%5wF$thjeqVQX*jzfUn08t}FMBVg2A2Huz}= z^~soLKQj`zS+^O^_dADyn1kolm*$lsHM&l3{xjIC4tQoV?!Ec4Hzufw20`7|& zJ|%Y^z>mzG3O^K(CU?@;{5~Deot&6bHP%;@++vclNOJsUz$wl&RPS4?t0>-SB^5Pe z?8$C&-NAf!&iJce z#?yCwh#e2C2=;xA#Vr=-oB{i~H(luW3T|}MvuhOr`@*wNFFzf0oKfQ3sByO+B`LF> zLP6;S*mt)I?<5Ft`!vALy-VW9BPhDCa zH*JrXksRA;dMqy^NSxeB=HNQ1&CU1rm4J#9I9{KG-eciKZd2cQ?l!NNG5kZf%WYiU zNqUp4cpTb@tDJxFSGU;Vuelw|Sig5QKKro#e7y^u&8H*m9;-TrSf>P{><5?Yy~}$n z^pIiXw5iMMd(5xi+x3oK?;i8~r+TjolzXgU*xP$upxk3({7lzT8>*ZTbN_dC9rHAb z1^uNk|4sGYq=%TFySMiy;$wN1%tEyPp-|)@eoz@Qpwn4AV~+pIv?u(#A|~hfc-w$ac?tIC=LLxk& zSL^)4IZv>hLAANt6WxQ7dn{%tXguHvjmG8{_-r&&Sqwx!hig|3KgwW5F(To@M^mno zdi>S5kRrI3b2o-OKDXQRbz{f|%5=Uk1ljuYa;E{L8$)i)g-V5ax%v`+)kQ6DW~cv^ zH%{rVoD@;Y*W}H$WJ*}TC#hIr;pV`m>T6mzvp4m^^=Uw> zt&PUeWAYaC&mfB8G#}x0!QVwzVP^SQLLf47s~ec?j7PUJ69cvFTBYE>RT&I>Fv`T0 zMtdh4z)%u>_m}`JLW>#joFZ$-aH?mT49h`j?Pf#0HW|C#vo_q{uYsmi1NQ?~w^_Hb zdIq_+`b-UUZp*O3QjPMg+ybT zF3t8Ht2^>$^C3u-dHLQ~6mZ*qz;uK(Z-wrTLIj7{m;0*_S+BURELS4i*W+`2RU$cm zr5d7$N_6O@n$#WL@hO?}vKS;MqjFIR7PWlm2iC8;ZwZ8M-y|%WRrp)xw>lSdUBW$9 zXB1p8-+mIAQOXbm_KJOj?E`hCZZ@2Oy!y}kg4xg_O0Vlt zF}*l#V&!PG%J-Nz|7Nd^9{4^__4jOzr7707sJkIGgQ`m#J*RCh)tuXe;k8;NMXRVJd5SOvD}3 z3CToGvND-4sBE9Y04$R%KhUG{NL+f9y0a>f?)|q^?(61xMl9E0j+$?C-oyFlppy77-kgY&7Hem~cJXPH*o+uU3O6%tAXRNJ~#N zVz}DoZcp?VIkCp0K=6PkBm|Ww`kR3CH8)sKfMDfo-}?%X`@C)d`-hlO@~&`o5*%+T zxL7yfO%q=8W=kO!RmiwHu|Q-~(G*Z#7SLD^r`!@@EIT*_>y08Hd&gisCeDwr-g4<% zm-nCfyHAn-%~^``5v-b(eTg>fqGjh;uwh^X$oH65og)}7M4j#R-b_q~-VSg-W3n#w z$2F2bN6W9S-rbvZiz)#YsfUw==Zt?>OQrrB1NQ;@nkjGh1K82oxy26q&|oK4qMebQ zDLtiidO00X5l{4a*O?*?nRBnw3bc+Lnfg7}JjfV1v&8PJ0q#?83*6hc1%}ZY)ks1O zz_zek;V~C~lxtdge(fu1zp=14^3!`v^OkPzRZ5T^cAojW$7J@MV_+F2*4|hLnP>j) zF-?DJBxa!;Gf(#pqw~z)Jr+I)T(s(k>fyb)vuGX`js&VPShA3EL(vA{um0k4z{tH`Q9Kqma=<+eOM8`=V!I z&rkv=+5r5!Ts&#(hP$>W1eS!o3amct2a*_OklYXKDHYiZtde)>v{k~BKIypLf_9(d zu-Bpcks5d7w0Nf#Dx3-Xe7q(5v=X<(?b84|Yy0Rx?JHof*Z=%O_cN^v)>e5?+uY}Z zg@DYD_Vn|latw0DJSy)I}sKhr{)M-Tjjzq*5fI@Vb*5z=?Gh6`u-5> zy=5O<{`g=KJ=UvOG^_Lyv-{iPf8|#clz%`YxqcWq@)}e5ca8*G6GqUv#svSV0hpkT z+5CHZuaEoInBYIvb$#5o9(tF+b20WFjIIn%uSJvPOz*J>(9sx9z&j0(6{7k~tj3W^ z>AZSlVSH0ks`sZdQuyEH@(jN#yKx*M{J)mnI5s^0yF6j&;e}Q_A+_nrQ=~Shj7fuY z=YivE%avtul<@mjiOJhJUd=?uoP3k?jPz=aH3=f7S|P%4Qi+*i%i1Spud#-q#6T-w z81^Kd-WsYp+X4});xZNuND~njl-c*ynAoV({fJ|c_g>Zmw$b3p^j8zXE&i@wq&f7klpVb@^QD!=HF{u3^vBD^&BbeAP6z81dr~ZZ_&y^%#{3C+dgm%x@$M zVha>GDbH2d`@}!w0}~@~T*o2H1)|PXeLLu2mhg`kFN+!+QXAS~rF6JbQn2n#5(< zAL+S5lUZVJ_?sO!YnGS*KVxa46RXgsTgFHJGqssx8PwKTeyT6fZ{075+M|7Y;2?Mw zj!c?%i=tpzi{1f$71rWf?ykna>lX;I&gTT!$)ZKUu3u8evaDRxudO0$(w>=(nDbbg zn)}+Ib^fCKm6?qe_?>4g`L}b?(vrz1N9Pg}c{sLkrf<02DZX!a=!ZRtQ*QG-Wwx6s zieg&(XsOL20Mk43F&g4x__==PtQ^5LuMz!wPQuN)VW$+^n!xp7&aL-YeOZa%|ywD+5h2 zxz_{u(Ydq554+LehrZ^T@`T)3mqcry$-NTo-R*&JW_ufdh3h_NqUcxNiEr>P*^P<& zlkpT~H*qG)Mz9$-W~7fG)sob!x$t_CNkB2vT)Lpy-LGx#bHTDQGvCDIfZCilMhOfZJksGVbbK+MfY)=Mhyt9W@o3g|d8GdC=!()va~4L3Rs0U)l6v5ld{*Alzq@~ zMv}vg`w1{7WtKN6D0#6n5BomLdOgJIUzu#K`q~kt@DJUg3+ynJUA5_rKhC@h56|Kx z)@2l&9PmVhlfuwfIEi&TCrpopAh0Y*Zw8!Yb;GUA-tjl z#{RM)|K0U{5ih|$c^5&)$4mk<$%c9T%7>7Pm;x}CLddu{Guf1*PCeSG6vIjeHM-*| zrZ0wA=V||IOZ%_+#Oqi*&ts!q*=CCNzaFqngpWS$pTFj&l@liTDpkq0H3%Ktde$KZ z%sS@3|NLV;{`~s7{^p1O^_L%i`SZu~^YOg==7)d$=`Vl!^DiIk=VjSmEP&hB^Ap>| zINb*sV7q*g3)5YGD<3(gM94dN>92a-LtYoBe^->i_$#kHkQ(}{kXTV5rX!}dE5sll znA#@Rad@?j*Sy(HqaV8Q5}f?cI_>xW@yCDpsdKmvqc1Bk(n!pJ@_dEI|f`8}+pP1|TBK%3C#)*7ko57U$tE(_sr(PXc4>)@L zd|V!VM`!byyc(;bN@izC(OY&Zr>8H9Q~%Y0ZC+2XwR(a!)O32|A5Mk9*1lv=+w^cX z41sP2=Wn>($tCaB()@=#86mvhl$VXB9lyo& z6VqYARQ;2}g2rMUAInin4kICq9-Ws>2RZCnxQGkeqSL2v(L6$bdqsN9wyr+xWrbGycCR8$xjj;8u2M}U#?*Op4GBZj{;P++} zZy;tyiAl?Q2V!TG`J~UUa-4Pf7lQLa=K37*2c6w4wRI_{2R=fx>8pVwocl%NBalkebAJR5_`@6AC ztR^`DA4PDn@bC>jRw~DQs~p(SvwK5x4|d!>uV2lB9oU)sL??NV@A|lY-YDj2m#2Uye#NW6nhC>ep*LXKIbtO&zjp*eqP;~a?jQn+rY1?_#g98uz zpCfwN-zQJ>g0m|eTK(PM{qWEK_dkC6;eY(Y|D?wPxAOP_{>?xAw~sIEYz%&~jDK>$ zPqb6Ql-_Yl?%15DrsEW|5{(rsGd|hhCC?{CVDk9rXg0Q3AoC42!-j)$Tdbn_b&S^D zlFwXr)W3#$zQrn?AS3N}Gj9u4db+k{xQh67Y^g3|N3nq#2y;5$L2mAm-eMWiH-scI zskusbi<#ZOjwL~{fr17iC}v#%I0!(TRl{QFt`3DXiH5VKnxc-zBo4`NV5E9Mym_}5 zQ)VoZpHVs4*I|BrEO?dbrL)UvkmY44Ij|0?dKJca#TEirk0$NzxufZY=4|Jk<2bAx zbr(W}kCxXgyx;}IR{91`dq*a+#d4ifU1vt`O=eg@FI&-!a3<0ANk!M+(nW^iy~XcW z{5po~J@~NEx%%JKRu)m=y7yPI70m}K+3GNRyVfjd8>KOW1)lK@BW{az9>0$1+WCm* z1HN__(*V?8$SA;Wy}=fTT~IHXr9hF81gi!4lkRFk{v@py1|PbPe{viDcMBQ85`yOM0^t8)^6Y$85?@iEP3(>9)H|-jso}xyi{MlP%^Ko(sEkOZ8(WxxI?VX&-na zu2O{7M}2*gjbJwLwpfTDj>K5Np*lvCd%GS`Nr4mx?i}xJqdOloyx1+)kjxB%7cX_P z(A^JLG4FQ&`C-^&8x7SpkOTiKk58Te45XXtz zdhz18%gWjaHGZF%J+Q5G@UKERu7xnerOo1;8PdjZk+G;CnG%}d)2RhIzEj{H&iSOYQIC1TNu z>MMvnM82~`EBd(`YtkQQrbkwhn4{TPs&j%rLO5o6hz{9sS1KPDADJVIRt!-}CN@^t zC0N*431A}ucb2Umn9|iSW2J?NgjNbsF~2u47mb4MPB>4LNMOm18l%^f7@0T_DsPWx z(^)@+8(t%1w%qn=wdQtkZGdo>N>t}g%c!_?n};}q-e>d)`-4;wZc>@*y54Mj#G;Iy zwnz1D*nE#Eg$656D&R1^;j zheP^z>_q-6uSf9j`i0qmBEy$1YzOeKX99u${t7NuNuDqhaAHk`8M{YkpvTl(5fqyb zl9psPa&<5s3wV5B8GzM^;cD@J;Rb0t13X$WN^N{+^zfh%6*5`zH{9;z8Q{@MScW}` zW&rU(SGJz%Q>^G1;P}zL-%$ICyNNF!Sc0L*R0fFk9c>zQTfTpw$kfe-Gwa?lY#%6I zP)(w+15ETM!-LP6kpLOG&9E%nISdp*s7KV7VzrRO#fdfYqbgG#6bY0!NsPmP<-Ca- zx{0f1VwGe{--#cYK^1Wve5Dx{J3;~~D_UVyL_ z3_OZx$6nF=m{*28oF+|nip3x^z0|4({gV%QLe8qq?Ls&1W!SY{U}lcef0K-WfvmBupr^B?zwxEnpgjz#$7{|8JE=&lU;~r z-0SD_CB|kWz)~vt#@F;q+fdau*|9-MOE*{#DMt3xvE{}wH%)O7TH==;YFNFWVE&Y_ zZ}oscnZLV1_8@2bA;Agz!P1$xET0buIOb9+d$0(=1OSov-5!X90OMC-ZpChS%^4gH zWpDbq+p-L;=IEie9A^a=b`Kg+C(^^kd3AqNVGMf`p-*f@KiG8E_=k$D_TKT@SNOwZ z=$Mi(JV8Q&ysW8h?)F4~ri{f3CwM{vQF)@f9>(1U5jQTcJr4mfS8U6JO>AQ!>_G-1 zX41d^>!1Gf-|$ce(g9G-*T$>VfEb|TT=0rCCc`Z@83`w*`v<>dV8=X_lH12VhB5gD zCi^Kl=G>1KKKuxR@dxN#$`i4=KzicL>`242*)f)96igoQM1)B;nvIT7rG5e?Q9EEl zOOqoF-&!T++Y2Z0`xeUa`w}M;0d>5DYy9FMQeDiZ?|G<@9XjR-v&j%Dy##}!P0cZQ zfB+NlmZcY(L(O5T7}Tf_rlp#&jO7m}cp`*K4p|)?;il6GP~q@SP@&=5SeQ~X@SMfh z^`8S3qCse5OjUaHvdTG7Ss(Qtkq6nKW1bMcBdE06gc{a>%HyLF0h|-)$COlo{lbq> zoZ^?|QIrVEH}nm6QBc9NzY5pj5x&W%BC1dE8#`x!r`CRKVv|y0wSmZR`~t`+P6B6_ zkE%gaQznUZ8pTcgF3DtH;@L*Hsh{IwQvVP_y^zB3@bd#iTpZRu*z|y|?sv0_zz@F+a*C61whb;S z=wvwbQ~05;xE(5PDpc-tHAGG8Ud=dv2x;*1TQ(V3mV$P)sc!A&0NyRTiTsbNNVD`C?ud@@RD-!~8JIn}OzRIeo$*Jk^f?1Cd20xO$)%fiQaI%a!0@XQ%Ke^cVkhz8KhElL zj;=ctt}n`j(`r|S0VCZcRXksxkK1d_Q&bwdPhqVxgiSA`e)k9P2SDBdGYq6Cu%YxP z(|_bftMtp6TLr8rFo7CNfKKp=29NMczl@GWJtu&N13t|*MYE%v6l+qXlg``v1YvFg zb8-RGaRCQW^(oe|R8RF55gwN3@zWQxJPrv*9<6YsVjUy{5CRvlx)yNEz=gp7XAbV@ zznlY6t0Ac$@eC3tw}r=R>sI=P+np0!6M=}WXn`FTOlN?oZW;77Bo9(IxK8{*$oIvn(1^468H<+ z24Pr8r<_+u13CRFCDs|8;1Z1-;S$P~200V}ewd`19y7-Q#sf!tntuACBs{$_T_d#o z=V7!C(p$PsCtatL?#wa|KtBfD=e+D;S&xcHFWt_gr|_axG{+qA5qem7qWYT9gA4*9 z*8+0e0?O6`SL^T27uT%_=ct1o4k~Xyq$g<${x97S5pxl1NfcB)gACFfkJnal^bNN=C%7g+QMRIu;c&HhLKRnqJC7pQ z(M}2jwsq386`VZz6_~l(Y3vOPx_0BXN6vY+s+3_5re~igKRltG^NeKy|NPT`{Q@%7 z|IXLRF3j(EAJ5hW@JmE1znn zf5+!D^jF?X!JbAwL&jhAzo;q!o|oxstX;%^n?pid?l~_%da_c5^K*3|&MtMzpMOfm zYf^8hJ?Z9Y*S{@2*lW_P6rUuh8MUdIj&Ae1e0;`_n>a*Dw|OmBdQQE^GTnRY!|*UD zP*c>vcpFy?b`(?`lL%iF7tbPvLV>|ikir`EI=e6Z14=PAcKQeU&kIWQmeMm;vWR(( zMII{HXU1gbIV?3)5|2eXC5g9UnxR?kL{}vM%V%@f725!&QeBEA00Lp{s<`$G#o+U! zy_gl{vHnE3pz;vmd<~ViZa(B`&WxaU$?B5?3vZ_huh%9NV&%(JubaD?wMoKa;Z2L1 zd!V7~iMB9i`fl!{dy?T?a|?*F(&`+DK3VUSCQ8yrrCjnP#TNfnQp&Tw`tYn1IoqKF zshL}co(E8#N;N#$+Pl(@?P&W%Uvtfy*Ba0EJ)Vm2IQf-Qjq@g1FF&-VVlq~Nh@fdz zAhjE~RPX))%Cv5FdOPjO%AAwphSe`7oyK~mlHpq^QyrjZ=ODuiZOOB*?06E*+;#n6 zAMM$4?rT@!3p2;k58clQ00aG1$yM9*hTmH(U0#e=9-a+&`i9$`6C5Eys63G?d>yM{ zadiR+i}q}JvWZQ2#&4fv_yDb*zSa zUT1)?EX;ZMHZ_s3!*!BZoskSBbI#frFgvgFdX*=ODb9)ka-&T-VR>F+fF(YYJ(&h0 z+st$+7K;qQ*iVcIHx+3lG;#eb!FXm?>}x=t1Dye%@jVYXA_Sw87Q?0C31BR8p@oNU z!ARR}u9L)!$eI!rqsYSg9R3;07d(J_&71W!{m|{Qh^tn)QsIy>0!4gn68+WmeMY`n7buMcTOf3@& zTuyLA1oghkXD}tQa&HU%%EdM67z!#%b9ti;39nyW#rOZa$*H#-gWA_DHe;>K zH=Y%tuts9aQy=CPl?Y-9i_G)A1ckAhk}lTZOtoo>hL{RJ)ux>=(tzB+|3;r+_FbMu zFoQ}F|B%BIvoZc5pIf6I)80z;klSOCPnpoKa&JP4#j_Py z)He4zp2lL!@+pDElSL{jzP+Brz+0JO^R|P5x?C1-qq%p$)3d6Xe1O*7ND3FM=W3%t+QH9L&XX_Vdmplk{~?MlHY5adp*%t zBRmI`_kzH8Hg}{aGy+>s6gs?`!DA8D84#?i4mPog1)BO?xcu{XuCpBe<>m95p8G57 ztjl%z#C9yNv#xi{`eZ{|XBFKr&_`E`rZrVx$@L^1ggefhY7FbHm50>0#HAHx#S%+h z-fiAY__2iK8y5vw4=ijuWN$115H9%VJfEQUQSnTC^^uj4=29a)pux0eK@t!X2p6wcyI{VlzQoR=-0-0?{NkdO0`_463>un!0wh;B5DU}M>5Tm;G3vQzrcCwH$bvJT4eH!R25B8t_-%($ZE*ZM!`X zDWRcTzPbU5mGOJZsBV+e@2`3B)nq&}?<#YQFeh(4!f?l~_`N(Vf!oE#J&E!5DuvOf z-ag6YD}GoaUXC=9DqeJfwY;Ss6t`z zG?m>H7>8$0kpKPXAOGp+AMyIY#}_v9ogDxOFxwq~_44(Jr6%tHY?u1t5p)0khjY+L*ncM`OJbZ?2-}@2|7UN}@R-OP3J(115SZSC z&}aZ&A^E#Mpm^7&4_6Mt^bLpq)#gsqr(*(y<_9)8P<>-{ffXhezInKIS{x9`k9$U@ zC*_&`rr$tYlhYHw=rb8DoV-i1z>n4>2(#%;dWtFk(s_CNeQIZJFo$d(ki%o0qeCg) z1{vhhHq$!yT-yz%k=wh(>IpF56c zgYqIt9=^?yu_{0~js+enu=hC*3s?<1tQtiLl@(OY8%)W)UypQ)FT@YsOLm>7?_jO?`O= zx);t(6(_yv!Gr2NP6}&ZVK?S>jc~$*e~S~d`5CJqgbP?vpxX4tKPX(jn6+&qB4l5j z;E4z)MJ1s;5$kx)zzM~6sxFIYL-img=X>Q0s~VKQkSk5ONI!HZOc?`b=E^SV9!8kT zm5mf(yasv$jr~KFm~K5pV=sB(fcxtMJ74SUzg!M*)t^Uf&*`NoYk zY+|CDd9wohyx8HxKj)oIEo#R95@tLB_(tr~+EdFz(Pp@*IMt?E_r^LH%V>S|aHdQU zd$7Fof_;~49Rwz}Ua*;#N0$Ei_JNg{N(`}NBM%!##rVI!&{_6RKc+yXI9F5U+kO7w z_rLt{ufNnI^6Q%KA;yjf*2!3{Vh^NM1Y(7R!uaq0?uUQ=zyI;m5C7vI{wHs56yXQ{ z%|HFOk1tqa@RMcylgs!g*YQtoU{2GI|oM0`1vJ?(7nxg|f_ONi2ZfN%}M+O+x zu|?>hyA$p_o!|;x#|l^UtH@Z{RMwV_l4%?sY#pQssH`quMec*Y$izAr(u;6GCG7-5 zdPA?=A^PeOzNgDN{^1l9)7yNuO%DWv7~o-7~H=oE<{A}e3Pk;RJFF*f@CqH`brO*z_uo3X~7H-%#oW{R# z{e$x?d_7lpbx#>vP}pSSy(jEdnoXzl{9<4bfld}1Dcr|%f`?A*=qOlH<3m^qL09pF z*9Ds<`!^3=*a$Xt$BLtNgL4szqtY>ZUmHG^vXVPauh90IH{}=o&~0A2vsb)(wrs&{ z*Sr{3g+A6QQ(p#mr?ugA)Dj^0; z%DR0;OM1C6Fp3PYUN;b6n)H-jgbdo05kT4E6hLE${e&Aou~YmmbYcZQo{?i*H}Xif zg~fHuA|91LETB{}6-=R-InR|YBv#G)539kWPCxeFm+_C4r_?f?ZCg`{c(fnf;bHL4zM%F=Fk z3K)gNXVeoJ0_l>5c9In_(67g1QNa+5PFmC_LQDJ{iS)#oX|YfCUt;ao37&|+NH#NI zoYGnynGUPgibroV)Zl%ro@R9vo-I(kMacgP9V;&X`{956<;P$C{PFz6>+rRZxO`r= zW)*@5<(r$VzVN>i!qO@vJ!nqrO-zM#hlrtMOdYGfysX3O7@KwU!N)>BbYcFDYY-f9 zUX~)`!bzt}KP2jL`z6X9)~A`V;@niX8K$(UHcbf?E6%3cG&4c0Bm7RE;P8AYaw_n* z@ejFH2Qd@>kh3z5ptWUxt(7c@s;Voo=0+s%R%o*n*6O}W%ETJA?Z$6O+N~^S*aJI# z*3^@BbE`mh={~J-Sz^VBmr0nq&giOLr^=ABKorQHBn2f zaPcy8t88gPz}zRY%qv?KmR-CxN^jp5qs=R7g+=;?8~7y#SPV4TlK_iJ4*EJ!?+aU& zSk93uE)D{I!(y^i?RvuQgQmC9@dN`dSjF25W)>YN2=cUbz(x`$%1Hg~Ir^IKx8peB z;7ijM+u0yBR=9XLX_YN4PE6@IuWVUbWlL>yZ?5bsTlgEky>kq(7-%vi1DwcI*%!7f zvF>9uXE8%wbrq$gS(L_l4l!x9B7)G5H@?#m5{$|d{TebB!kth<7Gcq4=~Mc7 zLiDZD*@2n$kGwU^d7tTuBi?4W<$l3M;^oY@y)|s%+MbHxy2HzfGyS{1mTrmlQ^68^ zc8uN?T-evb=1@zK-;{$(RIyb06JicpNg-rm2Ku zjZ~TA_by4rhG8lEiaaWj>X!06hUM19;+SvfnWf^zTAG}3LlMOO!1qKDIGZvTDJ)ZJ z4MbQnIAwDcZTseEY)9(|4R^ds5lb?iR2bnBilKqz^R_$3q7M=(~1pM|prrmkS0SXm$z5WM*{W}T=nP|w^XC(3}bw?Ad*xG=vgDVP z0?7IYL@`TUxUQwDO?Jaua&}ou1G-u+qxlsXg#s^GL`ap@8_P6I(8LdCjOys-jfz)R zfkM(udGo3kJW2UF%RlG^K&8L%r*90Qa=;T3D#{c6+CJ7God6XM?^J3QVTH_cP&`Hl z?Gh>k_sJlQvmCawP#DXu;K7fmDb#B|BS>cl|$hTEMS1Gv(#GdeFC-#}wGU4kne^=XL4Uj8C36iy9s zJSo!=lqJ!}Bqpg0IVCe*c#s``#PQFC#1Vglm?YC_O!C+K7&(kdTv(RE3)2@wd;FJj zQY_i7NFp7k)voPEg& z2tmHu{WyBwF zMuhmH&(J1{D}&>R=Xj?O?au+R)Im6@)gqt(hBeW@eNMuAbb5Y>1q=l%%%|u08}=cd zARo7Sl6Wa92IZcZ!#=`F%c!n>1!3?*U#xP8>Cn;%O|4QJ-RFeMg~n&+##pLyf-5vq zge#mljZuW)Y9D!qNrRSG*_+E|T5|xx^|C;fPkUanKP6kS+@bZ`{tzE2PdzQHssuVNYb@ ziqO)Rd|YBJP;Vn-*e9`PcqyVESf#(Zi+S!|B}u9bZ6zEk>;39WEZ>kZaAwA&VcCos z%RLGpTcyX)-ZBQ{0fN{JV_Voh#bodyAbTkco+v~YogCB8=aM78LNkekQ3=k}c(am< zMIR^lLu0bhyIlIpj|+LDheA5pQ4`KC@rxqT6K7^errq1P zOW*J#Kd-zFcp^e08_o79p0$tHlN{n{bsyWqjf3Jo&?{-M83Ii==FNdYbN zYjkth%Ow_SNC)=YM4r+X4)gXm1UQNdY?Ng+KXNSoA0uga0+BCHz(EJF=PGR3IX`J67SIR?E-;3!qh*Bxb*VLnc8I z!Pa+99`K&-Pk5Xt$4GOmsYqda-DYyYq}`l04Z34tg%{SXfTnee0eUPCnKFYxcg)0p zx620TF_C_%%Ld&s(|o2)#J7ruDVDt{ejLvskahFn+IFhT1{5)^{Y}q7Bc-}LrPlhJ z*K0+~h4hJpQFCa09OS}p?4)&WdCUfX)f~zSiS)l#?ZBl72k`H5eXcAKZwml zxs=Wnf8!`bfNnD^s-;nB(zb1y8grMB;No@J#MYE@0NqTsbh>P&o|wm*&0zAQ*Swj9 zViNh3l?@zXj{H=cX6lJ4)^C~tVW5I7CX~-~8NSzTCKr!DG3z;mfo}dV=HPqX<_zYh zyR6}NjTzxnR@Rui5~KDsD;xL`klf~ftp!aHr(o7YDxqzPG_@9e5B{nf(RIW6QVIK) zBHqC7a(>5OIl1Gnx&&fEJhfcD!9GkUYixP5EV{RS) zVFL^>?+=2$oi0^Qm%68i3(|uM=~nx6|9-j%U-xyoz__*)=Vts17^%9^aLx1$21xh% z!vZ-x(oNEfVIEwGfd031OS;QlTPDo1zYajtHTdb0as61;wNQ=y_pH=Udhac%pY)zv zQa?$rEh)3nN!2^9%T3O)96QHBW-^Eg!JUvk+Ji(MV-gi2*#=U4X?YGLD&DS4aprpS ztzLC*OQEY$v~$7~l8@`JOu3%yg*>iLSCr!5GfOYUMbTHj=v^NYU*n6SodWDi4C{PI zQ7uJ;3D^){GFN2qf8hqEbVE}^*T!d#D6AVm-O4kZc14(8PH;uKBI6~8R<6j)XWgf8 zGRW+iYpWWVv`#)f%kiD&_&!(r8WWVgU}y=;wMPgBGuzP*=VE}a?$-gfHwFstRoOd! z9-zcP`!YZso!65Hj^YKIYatToK81&R1NO|7X}v0SfR2sL;B@?{=h&G0ElN7CWxdq6 z#A#!zkMU=+EVuGu)zuY`T)L^A!F8lTK-_4?O*@$ z*Z=bi!n|d@)rQ};XD;v3+*G*x$VG5;ON#C3dpJnzY#j8Is$sd)d6NrNgg#LQu7qIc@2*hB~i)sL9JeS&Q) zK3=vVBiQYD@`M{cXW*k899#?8TUliwKkvDH;OjhD{OYx7X3sGR`vzf}mwtoWQ@U_bodUw;1ar=NfM=||+gbE-Cv zLJj`6xmDyEKZWrfb~EK-OxhO7xFu~@K+Is?)+}gB`z&R8Kk?;qk(uYGOjOMq_t(PB zR(+c1VqaU9aJ5bUb`k!qJ5*tE=7~>^H=h3>w(XZ>F~z=2g9j|1ra|xcg6(F?mRMs^ zra=}Zyk(tICYHxAV3mCKu6-Y#B#4QE>By=Z@VzNGVp6+wJneF*Huqr-L+saKF@?W4 zo(b5KJ(;f!3x`{oJ!Vvowy>D`t@;X2nCDoCydr-BmdWjyDGk^uxK_+me=RO5g4H{t`VV4;RNX z;a`o;>&ZL>3rAR+J%!$*_m3~${;_=Ba=}h~-adwY8GFez0$L0fnIa~@ zj6RIBKif}9C9qr&`^QX|S7C`z*R0)ZEbjQm&0|cm7;@e+N0vpZR}PN3@B)SHCGuC6 z1A*8KW~f2+$dCeZJcmTA|{~r;`iFI-i>M3_@Z`c zajM&LfE}~=PVFEh{qyRV*6i)ml@3k6|YVvqiDAj+_Nx&-o z(s-U{e`Ut5auT+QG}2;afbcuhfmM_7$h=F~aHUUw)fbB&ir<-{JlT^NZ*9q0SkB7t zF|B`8c`!Lz^%Zhx}I^ocInnLIY4ZqJNl9U-PH^d~kgL zfR*rmE#!7-mD{}b;L6u^D<@$ZHhvGYx%7Lx<~x{pc_?yknF8JbV6F+Y z)l;r;`!oRHtc?#~3xhQHuC>i56=@#i1^_}3pXE8#2jfIylw6M3NYCeC8T6f|aQiug%_6Wk+@ zuBCg}xH@BSFt`@UIqSw6rCqk5n1n5gK}`=x=;8K221u{61NTcWkg;Z*o~#6gf6mQr z*?yz3SjP_teLPv+`0n&cBS*LtG)J~Yr%$O`a?L)f8rpmLD>yprsvG^#{ftPBEQ*;N zf1Da|`Cpd=exq-=-6;?};0Xyq<%#aHmHqDbiR<yru8A%*K*wG8@`bX1gXNF48eH_78brLZDQTT2!c%LSAB&i^a18A z7CuN%(6)nWbGIkD%hX$}YpM|3?}-S4eHFwl7T%pO3v!4j2=?_)x0rP4`IwVIjBb zU`yX{Gr|d;2qEYs!*C<<1PHQNEzN>O8>6g3q7Bz70)i|QP(g~^Yc?+Fhi;p~@hm`5 zZF+$}f*=b}3`@%g z?*&ZuWFCTSG}EV8R565LKN%v(REWz4(S_i92N))Ez77U^APN- zT5j=W|04(rNk{1`JYlwD<|Q3B=DREJ?oEb$Ne6%9J3SFWurKMj4fI9FXThQ&!sB86 zePSa0x>gYoWPyfq5*%-~V`8C-JpUE4loCy`=gawTpRat2^(z%|?b_YTAQA>85-bC6Kd6yL`2@1M%m%j|DOvj7WaY9;>4MI!#{ zPsdY=n;R>VR2uMlERmx#q(Dx8?J|&mosc1OaRjS$FU7b`y^+At$j<;M(~#<+Cos6t z&t15RFe#Do22I8fll~h8H~NO#osu|NGNHye=YGj+lBf3N1qD?;#nOinV7t_Our9wo zFn8!Lh1IZhMlW}ICKzpC7QVgiPMAVYaE8WWLw&v#;-KnNtOPn?!N5_TcCMK}Z5EFt z)Nn&oOmL3s1R%$pfkcT6iPWe@I4?lCweJ$tJ%Qap&|jg&SGJcRV1>^4i0n@iu~uV* z$gX??h}=FfCn;ydG7N#pw+_T3OKv#dhdaI<9?-$p*zWGQXL5f za<#_J7-GjH9xj`X|Dsbi9asEaw~HmYABZcRPnx*O-qL-##VWI2KAE&wS+f`U0qoPK zhY$zaw@UAdtx_A$_-w}&ThGt?ZF%wU>vD?etEpHi7p{YoRsYv==_pPBi}vDH?tH_3 zXbuT%%jCle{FUchEJW&(q=;`3n7L)02-3fH&6NuxCC z>HPuKbc~}w&{IA4Hk=sa5RKef`$Ac;)DP@+&W9zu*|PNS`dW^AtXdFhynRFBtua^$ zzIn>w9xEMkuD-V-hhZ0N4N!&P%jaX+O2%Ouz(~r$M7^Jj%2q~D`vn311(F-2ud&ca ze>yqU{j}KMy|;3M`t3NhIF2yBmn7-l7aDNuZck3|OPWJB&oMc+wRPf^JMUQCTk*qN zcEKC;W%9_zHK8#tG`P1ygW3&fee}whf9P6TAh8u1)HY+6yL(mlzR=)4B{aD6#5`Hr z1%MP=RoQ0N>S_9OEi|ghTb)5Q8XRF}&v~7}ok{SM!yl}Q?XOK;7V<9X>M9IMKXecMa@p?PtKCd&l^D7;y&D{vh)3r)-Q#ylt%Wkh> z?e%0Hf!(dC`yrXZy_Ff%u6AUP{6iP$A{DkWgW9Gy{1F2CGK2e+%;1P4A_Vqj2K+VG zM0p)~2!XY(?8^-9^D={bD>JB00>Sd@!(3s`zjX&DeNV~hBoOwM2KOnY!M#-))B$=? zI1R$S(BOVZXmD?Dx367=FU-!=ywKpzEcmk=(a(nT4L2@IA$q_OAqbsx_}ztQ6&hp{ zn`v-fXmD?Z2Cd_-A4ccJzjci*K-g*xYO`MGj}mZSYk=LMsPHSkZ?y))o=k(VFEzNw zPaBMC(N=0uy9y9$qR)L^YH;UwI8Fj#UuwYLaO2_xM?@g(OAYQ*QiCHiVU-$W6B`iD zOAYS)?16F;9AECaH<|N6%f1Q{{cVx*-o%9?f%Z@<-I~qH|E|ABHKhnS0vw$wTjcZ=A^bHr zq)%8gR7FU3g~4vj#ud@XG=G&-aD+m1Mz9Z6n9$GNv5AU>Z{Jrf?+uf3x}>@jizCWJ z$)x?so`iX`;TGN5a@O`~wd7i8gjXiItFFQw9S-Q7e(Qco_@WgE9ENOPAaI`&2;2|& zLPJqFBh6NJQ;)vpn&<>5a*(G3qC!wt5gOAzB@`*eS>LZ85CSmo5`;h9e)^Bx5y~K- zlULRLZb0^zYOz$J0J2p{48w6Ako_ql76yy}$vk*vH1NEZ4-oK_FX*?EzM$2&YId(L z`uc(Ulz!lTz!wo9*={yFhGz>W0J7*)0LcCZ$Go)Q&Mb80B>3LUim@s}fUxEH4?};n zjxXeR9fx7*UO>n!aWy(G1Va!$arbvw;1uS*?oybh^eO)3&Sd||&b4NeycoKy)1Hen zYgTtLWT2d2H4PVGHYaz5Rrv{U;c!gzSCLmJtI+YTo6~w%#J&~P(p~b_SX>R;dt7+? z#8bJiTVpTl^%FTPKQ4U5xf+ciJr0q13~6+g*_6)@(!Z~$drT+KbG+s*v2;a6I1z}I2VymY;nsNVK0eS*-E_F&CyEoNS#(af8HVg^ccG%t z?S@Hhs;_3kCV7Ax+|zx%exM=9A3$;g(OvgDobjgmZB9^Qw)AwrZ9IfodZw=+SA*dV z;yGy1xb!ZFtedo6zOoR$C7-YixU%^~8}{wSHuhMtD~7$P`f@5ZC#xJ5yB55+ zqdhrQ{T%S*`uVuLo`_MN>{mbZH9xvC0gY8IZ^d^hR?E8-mwTU;yv8q7$XIGtbO&RJ zMZelXseRuVtlqe{(v)E@BB*@|A$uXmi5Z0Y1I3~nTx5DBuaHID)emEM_qH`^89w@8C zOK;&MRvnz;BowQtQh7n6J2aE#*7jMB6Y;}5bCYCZVZ{hPt=6LU^>=^Zg&az6l*_!L z(Uu4gd8BKo+P3D|BAq?67| za=iA{U71R7t;D28qgSfLGf#6iw)Q0_Yy9HE2sy1bqW1N7e?UduTb|=-r+Ht{9B~q0 z`cEmW17bZF)9V3$Xu!1oD5QHGBk>CC@M>pe^;DX0%5JFY!Mq}4Z50{S{a&cX1sm4u zs8yj^U$ENJJqkDdN@2j#n#qWXansN{R#vi@87I~Y3~|%ja=@ciSf(sk5G`0zEvP^j z)T-JRVSI>m#uArFgT&sstgOMXW(fB+PjHQfkZ?`En2z;gC)8{l_6b6L712s(3UhM~ zt034(YZ1@vgHU>EIc-0wh>2w+#YL@LWY~{+^pw*T-KQ{$N9bu44z;h~b=GY+{k1Si z1!kB7eu_JAB-M}4DV4)|z#$Q4`Z5Rpnj5h_%v8h2!J0SO)}r=-Rq~#cwF{V$8IaLI3fGmALV)i$<7fl>@b}m~qN0mC69I2tatF zRRYwONGF|_;joxc zmWTy6#XGDdnT(SdGwpFG8Nt>nu{dIgnO-D8wtxFT<*ttZwK3)^=$3y!%p#*{r!Rq6 z2dw*CvmNk_Mvm|enbGF6a3y#Ka`KW7$jQ~4auUfV+$`UctJYQpQk{flt|rCt58XBe z8e0iSZF6tR>`Oq_DGA8RTAout*;avUG~@U~Z$zw2Cs%!iC(Ps(3m>E>s9aTT?)F5# z$|6?38yF=$9`J;QpzuV7f(Af*e0aR_D-xW`%Ay7n3$YYLK(N()RJg$LW+IvweXQ*h z5w*>|j_->;_#0!#zq5}L&x@_&`=XCEmPd@{#a1y|H z7-3ypAb7wN5d;epT=(g_3(+FvL45X0;rJ;E0YPS|TgPja!%9D#b3Ak4RhzpV-{1P9 zZ@Aqlj%Qx}WKZTHSg8HV@$r)kLkK#_(CoypXg&HRDmE% zUnY}*bbNFu$4V4!_U+JvA6qNts6h%(=;`?)uOU^bdHdzkqKv*1S35#3jKLT|_L4<|iwVlv$N8j~%41LOox zgb?iR6tLf26CIxgH`X#?7UbdE_?*|6Y^}zmLU3Q6#LQ;C?`(IzftG$cJESPt zT182XY`Te14Silqva!%%a$+4#@qH}`f8#qn5uv58CD};1;`ixrZO7q<%z+%zU3+Zv zT9U2Rl2pg~4Od2hdlrwBo3g~NLwpe~5eD{{*AA}mS)ePPWucu?nf6~M4fDWI?R z+G6#F^c|(vQ%W_Q%k#w-I+DVftNMtt{bWa?EFkXxU{lyafFuzvJV#LCS2I*!aeu+` z(O%;)y3SyW1xCV6T{Y2Oh_N2TY*57#q!aukv84Rey-sI~pXoVa{aLhMm#wcl+DOgd zMv8ROc?`qebp~53KPnD-OL-InsUexM9Xdb?!94_05A<}MfyyDVRwGZRxa;%p|N5u@ z{I{Qe{sq`XdSrc$!6^>xbn_uca_=Zi4IfLRWGtPxmjOHE`uVa|p~2P{ploy#s~heo z#F$P3BF1vXue>u*xgDQS30SnOE*H_N9?Z)xHd09BVrXME$NqaC^-(9cK6FpZ@gMeK zJ_Ypk7uzBI#n!%8QTqzbl7=XerrnWy8Ng}XY*xe+`cVRKzG4b^`kgxlf&W%8Qq8h= z!Y_i6%f{66R5}*cwvO}!LAQ$q{|5pr!av*56ejdFzfT?fPg<<3sTs4$Dt!^j7W4Xr z<5;7?bIxQpJmIgIreZBiaZW2^8TMcv{e5!(7QeJHLJrA9bhgBnnn8b>4&4xt4rn&t zhr^FIHGq=-j1O3a9;h}`D z{Z(l!mWNaaHH+V2FXoZb*NAL~^cY*K$EbZxHyC~32+g@wg8w5oCZtFHf8yS#SDG8g z5`6csxWK;5K5Y7CL}X?pFfU!Z15~4SU$zG52S0h{`^IHp+uf*j12y~W_k<`ZNs6SD z5piLlF>~!oD=3nOq9`7TaeM3ddlwx0If};+H{-$iI$cSmRU+8f+zk4ftD+NNQ`k8Z zO~2hjjq@lT)MS3j;({}FjhV3MUogYl_TRKl)HIWn(CQ{;HuP2(>cm&-0%n@!CH>HK z0*Mp0W$n(tx4hV7g-6WRIl&SkPV#0^dq;fOay$vE1PYqI;S1q`qA(Jt7#uVbPW+W7 z(%`PM6zdQ-M){r>Ph}K~a8i2QwM-#)dpM!D5R|OIiDo%GVh)Qm0c-O+XoBfMl!+$g ziRmvVSR%qnc|by2A}l4Hu%3`_Z<`gH@$!h9yrq%O3ub6Y(3%+9O46Gf%J=r+gxyC# zN@!z9%mC@(gp)$5Poq}x`GF=CHT!X1$b<7j>ad;)dP|DU3!!iHgBGhgb|sNPOGAYl z!{&uNV$ReVP|3TuDXX{%ATcvVK!wv#x~l0IDLp@;{UtnaEwPLfSGrP21L;*|eLXbs zuqiB$m{`$8i{(r}OSzFrD`0$!&$$y_Ep%)_lQbelxT2n54-FV$kEFWRUBe9M1i%#9 z5CCIt@z=xzal;4)%*qKKhmEA7D-G_-QLXbO`+HJC<1uUM`gUdNpk?J`gsQG|cbCKo z{m}i4G{K2`dYYaw>zLJ>u3RE~9=R~O3Zbcm_4a;8f4Zd}Q5{XiofdfLz?f<_MYCZp2IIEiKDB$oO)FJ-a7G`%)h zt4(5|Z}?72$W1P}V~{7CZ=QB;$!oj7|$Z`!H?ceeCYA`m|*|L}hV5 zqwDnaL)Vc-Zox?`UF*eeG?8&q(o-2MG-V?o#tGPQ-wN?>Z}Hz}uncM}_Yq{wZuqwu zEbBOfW#tT(K{32O0<``p-yfo{ten2m6(L4G-#pp$mGuyPW#v?pDl3rR>>JY$$FM^j zgi~^viKf@$WQgW*#1a}O!V>8ut4a6Xr+HM3$5}+dNu_zLHqGM}A7=d5pa0wc^L^VF zu!FeKH^ctRZ{Pm;w{Pg9{`kCne%^lg@n3%Z_WR#Ho|pAG)n~!4)8%?+?}8^55MZ5O zL&jhEQP=G+qOGUDa@fIjN4e)8Pt)kem9Y0<*P zd#V3^`(NuB86GZABmaO4iOP5Ly&w2pzSbc9RsYv26%lzhsfdP<{-W;s?cY^d9_0#T zG)CopHhMjZERS+YBF^h_Jp|R&GPeg6#y)<)4!QgndEpX?oN~I1*ThmWx)_@kC8pTCIXoJRltyAg(YwRz7HpcP ztMIRM2hf4n!qh?g{Z0H@j4-1v z8*?He5JtNa$gE&v=&y6)=Id$@WKJT%c3Mo$F_R-E#MD&O8EeFvp&HP z5{$|cIcd<&5uZ{X2V;3T2wJhX)iHlb#8{&1D8uhWOOxTmY^`dYl*kT>5G+5^I3;OU z#))YyJ(zI1OLb|$q^SqR1PN(@ltc_pzXy}To6xh4T{72xIJKW2BK1&`vVa_ClIc#t_M8C0f*=6w*K}g(z%w9IDidp|bSSMP zVvD=p3dqpwAg36Kj)%14t^2|f8R-IjJ> zwURol7uCC9u@jD%ZdMlh1Gb33Qp#UiMI+7eN7k3}{LQr56ts1m>9f*1GlOm#8wmv> zt%;#fMy#ko5yp{XN3xjL(nAX8yHuA3QkrEo{ctQ?5j|>f`mqI2TTD4!6Tf4IS%pbj zj|U}@hKOKNYQx$&wy(J=I$=#IPbfhv=1bS>IMHV%7RsP%Vk4odr!+Cd@LBoWHr4mM za4M}69$2*WV3$vtcQ8cPo*oaa(S1x%V9 zO8TK2Dg{i48FfmanrM3ML3QUyHgD<=7$Sm6IaO575%U30SWW7qFE|}WY&5ho9?Lkj zryAZ1<#8;-QLWNM8r*f3vepwBYS2%{VPeLxXJW~?B6VS6@93Mi8rO8p?0!bVBrO1g z(~m7M(ew<52-naP3=zV_PJd%A;2AKf8+rSYo@b%}4H@Z4w3Abe)I29igS&EM>pc0R zKwYGc!i03tILtHVv2q$|5NdS0@MTM0J&x)Mkuofpe++A3F*XQH&Ya#ZW2xIgG8<1jH}*fZv^ z^kBjX8yqHj3;&F{7QzDjxsp2OzV&>f>9xTjCX}6EhzKU6wAM+(l;`+rLf!{mKr7}J z{uy(Jq>;`mN1_!$!6cEAx-j7n>~#HP7$!DX=ovG0x-hX2jV4-s1;jIcEJj)&EdYa2 zqL*~p?3-ujbrRZ}JP#Nmgo&O0#{9n%U_z9*1QUCWjX6oeNSuJw_0iZ!&C`j`n3+_9 z7$+rlMHq&O%}#p8Je(d(I5nq=3Yh3E{4@Q~4G_`-X@7YVOl)q=Gj-a!;+$ZJ2qvVo zW;ID>_hmYsbQLbxMAL8A*qCu7jdWftYo0fJChn52i5$c@pQrksmvxN8#ExMx5ughb zdkfz%v7Rw&N?PE&w0C+Ezol`>#BKUYLxeD~)8AxvU#1galGhNxB&oR3O58Coj6@He zP%L{A0phG4<7dngDlv?+fw~e5qlDN6t!BbqTMs3?!@i1&8|rkj8NU}H_0Q6{I;0mP z;?*1=u^lo|7<`A!(dvL5B9M^IT0?|wyH?6IsGZ!GOwN~fCWXPFb_6j^K^Tcz`%eLh zJ~Vm8WQ+n5Vj2ueFbtByQqYG&8ggXDl6B9Ih5mzF6e6c{VXpmqvK3TI_{7M`1yAS{tfBb^t?h>f8|*~WMMr2>Ve zeO+aQKw*~@JPXM|i4-$ddO+bMmMSVDwoQWR-cqQpjWHF1JpY2b6mvN$(f z41jOv3^9E|7><}QD&X%k9Lkv5fx@+@SckKPy1o;lp7Cj(HizOFx2JcZ!*)egROFCN zVi41Iqy^3ky?}}15JTbjfFU$YgeB~KPWqY;-Q(+HA$dV7NyR#NP?M-HfkA%n76NjT zKT3XI=DEB#Ok}?;=Ur>mTZ=iT%n{iB*YY$3{>sxrZ&8kp_$xaA+DkIW4ToTb8vjgp z3R1Iy9K8~vLL^FcAh()S`TimrTAWrn1bAs&S1GRZAO1vM-=`UwFb=4!|th`?cuNY62 z5d0*rMC%P<$dT%KC+&;3(Td}X`YfCp1wYgtQEt&{+rI1L|G3)Zl^{xOW=wj<46~d( zJc;@AmP3_YL!pwvyECAbR$_vXG~Ah4-v_FMN|baLM&CfzqU^yw@@v7LdfEh{XH1pp zB@moFQ!$ABb(1>9@7xG0aQ;Od)0=+`pNXc2kf$t>zVVfokWf*UFk1qjiOb~6Ln!J}40#>3#-jXmR+&i_;F~fI3we} z{_x|U{`7DE_z9W8@`;zvKW_K?_W$1S|K5@gf5epqlt88}Ug7#8B&6o7Z97MxYJ)?# zfPCRLt6?=hW2%8f$UBK&jF5(7_>4JP1seS6p{^51XeinkE>aDgZcXz-d#%oT0DyK*<}Z13J=WC9Wi!H1r&L5$MVJZiD(M8>qr=o3fG{FvYYb z;lZ4vG$@9*(&7}V;cPr(YEyAy&T8sP5jrt1`;~`aRCtdGG*Ua~<;*QZp~Kb4zI#A5 z+INL+?3}qn{mNP50pBh3F8FTJZR@++Qtu*KTIamz+p;L)XQQg)rW(p%iZt%HdD|x( zcm5oHw+KH??bL&Eb)v7TTIIoA6P=(M-*K}NjFlJ;*Y|{mo zzP@rc+*NzQi3BBW^W%P*UO8L6)%97}j{vAuAH?*-P6zu!KKxg#>9t09G}5YyTmGTz zTwj<7;v_Rq4&H?WV4Q*A$QLIKNoOB8!NHLljzcskL&szB3WKMZdkQFVDxu{FD?tQG zi15yW)`?doTg0f2r;LVjjBs8k#~~11wyPDieZqCD6BN`&Ev6wSqKCU4+G#>LjM-AL z6YukKaVopq$L85NekrZQWGmsmoLMy}hL&vAQA*yKmBgUQ+O} z&^U2H2LH8XG~Cv#F22k^bp29VARU>W58)))EGz?5+pRe`Eqz%oVF)>A|=9NkG1>?T{Pygfh?|&B; z<+pceYm0@E_ykqmFAwy=lZ58 zVe)Z?uO|Q6!n#TLfjvS53tuI2!CU6=!aBXCqzshCcU_|xh$vozQpo%?a#xcj2K9sn z>VX(;TTngqaP^Q@c-1FgW9|*>Ce;bE4p*5IED=H_W!cS6Atqg(0F}a>$!mRbM5^X>wtG}HDE?r7jZHrFRbw|nuT)@bhfO4J1fy;v zEPQC)K#qWANyWm%Q0XrN@8kw@8IT7VN~w0~Bw5gxDQ~~%NwPsZ^L!;cQo^G7G!cFf z=5k)qkcc`;qI7x>$ZNfaOPzF_6r$Czd)J^eGsBO=C9$kQj33u`Vd(|}TCyBs@|)(* znBUY(2IzTSr8L;0Nd{uZjB9EO5bcd;m_=BX+&hcrU^r%ANq4deMSI<;kgqV&ysG6X z8!i^solbBu8EKS{C3`h#PIBlkjWjvELP)VoO^FZUN+1l!m@s|x#U%!6)=?p;!+pui zE4{s>faST*a11E47%fuhGd=V$y3$Kw0BN&zkEsk^4Dg0;Iss!I&E8owOL>xK(3MgJ zdeDfmD*^A>v+-9(+5B0!8`_@Xs#jvb0T+v60NJaF0WnduY;_J~8->*Y{Uo(F5)6}# zpx5(~NMzI01g311KH<7iu@TX;20bVY1CQ%16Dlx0J0XmlMtcCF$9VNMCR>l z{$g=_t6m-)f>zSf*CHj{s!JoCm-QfNz_P9lG?d*Cqem@Ao~w)_B_BOVk*-JH9D>R% zUjkB^b#3^~A&i-pfx+o_kV4aIx0)lYBuMg3T}2uqL`qUx>!)#rJR*qdovY@cx@$}7teTWGupW0*M@ea)BYFkZ5cBS*awb426iGrk+} z$O%+?5;Jzaz;=Vc^g5KfE+}%9cJ}459k?dLrgN!sB^@eHIi0}%GcV#u*J`z{Ttrvm zVN!^hc3tCFkn-jTk$qEXT8>nd|I1Ivd$sF@%l8+adO-saGw(^T@5DYti54?k1Wp-^ z1%u{Q(_7QyMXdp68ruf~mF2F)$RLGxB?trM?Oq$;uSo@Mpp2;yQZp>)>dF;p21k|O zfBUwn;fMJ;^1KaxmArkIN_4yuIia{KB0vE5rfU&|{t zkL~ZgT!ET=E$tZK2LNjTCH_@j^_te!>k!B3f0;hE~j_ArTexCgYN0nqJ(R9Nf#4K zYE(_(iuAfo2F5h2h}W3qBelaewFk8W$AiDYziR)C*3|Yb7cwVkN-tDgRW?QF8nf4O zaT7$zx1GE!RJ9KZx6f{aG?6|v^SfHf!3miAr@#O5AO8x}ZJ3*{#IsSbL(p$;jLpAw zzaZR!^NS9;1HPM6ZRG|rHL17(XC-wd2?4_jcUf(%4MGfeN~l+Q_@}kSb~yx0rDIo2 zIdKhcrH-KLHp~1W;*2*JxSO_OSl*LP^*(yM=3O{dPB`&b4u1H{LDu$N*Y8hoG#zh* zr{w~aR$G`coe*yb!P~yI&}x(f<7ApEC)0E>7;IOzE%@>Qd9*08GV*4QQ#iU(3`YW+ zOLINMrMYr0O%)ZB2frMtS(C-YE%;s}@IKO%*3yd`uK)LPM1sl{x{}%(2~t+$ zOK@O|ytkyMZl7Avl9BHVeJt$0P$a(9;)PlZ+vo0b?Vf+sMh*H$kb&-%Krz=u;Dr-T z4&nt~sO1=nzQi6<$&CkD2?}xMz~2#=uZ8fSMH&w!M4B0tD_)xr^LbA=X54frUt_LP z$t4abOGXf-Ejy7;U-R*si)@_9Ht0iT?^3uKu$Ot@eT`Wf(v=fpPqA0iwtS7LQq?-| zDHfQoL~Uu7l+Gzp&geKdtx4ivI zF>On;$ZXB*;pjF6I6>w%swhXfIULf7a<4&GD4SAMJ>|i8Lr`_ zYTgM>9o1}>d|f_Ct*u(Jp}p||Wl5kaZDqh{nc^dxyq+cg-DoA16jjzUa=*=$&F7!y zYs|9oHe{S#Q^OfrYMau`I4|W&1c{S^b>XFGeTSKG1x(sf4N4LKM#gH=Srbd*_VeDV zndn8@HBxW0nt9Aa61XHL&*1c9T*ANXG%99YonQzFMrjFBT4RZr^jEg0N5ELicZDpa zjFiT9gQwMTQo%Q}q{#E9p+jA@YEIYrf*XF*gBIA5UY@UplWU<;BGlL=iI`vXh3AJ@ zs+tQ$uZgwd>RJNgop=xf0$fLB^O&_$kU-3lK}-fANnF}TI%pI5n6D&NlM>KDxjL#T z90uw_2S};O7Tug>?2tkq;pI|U?#Ta~BZrU40ne(3l^ zYB=}jqmV^uy-lZK6uvgR7rKzfdsvl-QqM7+8vR|QW}Uc9F(F5KKF_V*k)vIrA0N$0 zY)sjyKF=$tJsIQ@J>v!?ep~eGE$W%H(0JUGXeX}6apg>$7}5=japL*>K>uXz7qsrc zq_4SxouDk#9~XWLxloo#e7kl?PB}o(^E4_V$IDj@5IM<&xpIT0MAeiSFRoUkxl)jO zOAIfHH`jDdoo5Fq$C(oSsgz2NxUwiFkP-}64l7Nab#&tNiR#_Bp;X^oK|dTcK`KF> zVgh)Cnb}P-gDT_Xh$SRWlqJlv1AWa^QOP#<6mKh=26unO&z=F4OZ9KsnunO_cR?) zqD;!K7oo*;Hl<6%^flkGwB}CqfXtv_3CVfQCC^D{D|8>Nm2h2=E`DNpp?w~Fo?;eQ z_40R?oxsaQWnm%)P-CB%ozq)&iBqENh6CZ$$fzz>RAz~pM$#Py?GRE6~S0^|xO#-t5H>&8o_xi8|+w{)O9RbZWxLdF88@KU%jSPBEd*(|{Fo(Ta5Y zK+{9LpwK+r$HBJJQcM+jPXZj4k2)HD2>fBru{J~7A6PpHH7J2xNahRPWMB_*K3`Y1O!iAaQ_P$Y>o2EI z98}GAR|D?Jx0Wf~r<7Hg zUhNt*s4V*w5+9ImZ(Uqaf)km5?9xQBG;A$ew&gr#Z;JH z{>_;gRaP9eGz)i3s*omND?uG|XK)&@a?nV%%&p?a5I1N_l(Kv<*%c&43+scM$^~#rrqrhE+Ng&X-u+x< zMIT(c0!4o*JdMdY9O4wL=?$wJho+c$vse0NQye#93pXNK*eT%_ddNrp_$wc_;V-j4 zXiDF8?N}o*I|Hcp@*sv=|e5w4& z&Ca&d*Ic!o5fXA`3?U&eZAvTS)TfC*R8-x2Ur0b@PRkKiXNq-*B{mow!(hNSV?+~c zq9u-!g0x5YG=ID0h-$Z&)haBTGxA(0YocQ25r?2^Xzuop-YPz==hn7Z3Vu0M+X#F8PWe-pNJr z4v>NRbhi`r3ZXXA++srik?RlA8YwQFVvU)m+G-$vsptfwNXRIwm`C_?%$6*v=Ml){ z3<8j`L;pBE3U~V$P7ov4Ya+mDgo4ZvJqk~LydlCcWRebAhe_>$uK)HRL(e2Bmj-0k z51hHwjyJ7^EhM;~6p-PJtid?ySp$$U6HQzu9EnpPbHFGOWbAjQ$OT*#od6kf^KF%} z=~lQ0{^O!KjI|KfCorG5Gc7o0&8GpbL*L+z%S@Sy{S;M`jY43|B8h&AX z-Z!NTy40ldnwsLBojS&RgJ8>wjx54loFq`?Tob{d{|JWyAeU7<&9;##6`W1T%eBsUz;x$`)m*~zZtj;SjWvG2R6 z8_RAVNbHp;F^{K$mnE)Ul?Cxk7LIJn$~^uS(@d|q493|Yp26+uMMWg@)Tyh~6w_!< zP?L;tN>64zdX8B~CB_k*>n#=47?-OQ2(mfaX|042LHIGf_w$w*o*yG+)pCS6da(|F zXX%I&%Tf-tsyB&g%rok38#wi-iduT8kw)qHhgq<+L)zM(Vh6ZIPL8!WVOT!F91<+b z9(LB+dF+a+(RlG;cS1l8z9j^uZ%k<=WpP zh-=b-B_^u$z(SlKr7eIZz50nXOwT_o5=c9+5@%Nl@$xdyHXUUiqD##O%n^abrc2S+ z{M_|8SPF>_TCvx*m?j{NbYetHFOy=Ysa<9$pmCa5*IPo!@WKaCe;UZd+>S0}tgJHi zfnqmVVec(5W{HpKGTM}ZJMqhOD}pS!|g%6Fi+;Wuwd|)V(Y1yR@eWjXY1W|gjBghbEY9`v^Ti#Q) zrj;|5_1xfToHI3Z&eTCmVH2RhG{l*jIWMRyLg2(Q+qNlFbIk4Nr4Gb3Qbt2Vb$KpS z&p+InizS%zv${&?AciK{R%M51T1U(w!J_Q3>B5jH-jL;N`v6!fO>4GkT65gLDUJ00 z4%w^Ypn9O4C9GqVWQf>7$^&;qd}SQFmJxG|dSKzSqw3NC%ku*}pr+w^j#)&~4k_>s zDxn7!n?W>p$T4{VK46XrEQL6uT_oO$odA|xKSC|YUfW`RfG|>`U~MA(vYo0Aoql>? z!9E$Ab<_})K4O+vfea^jbp;967ruQc9n)H*5XD`{SlfBDPH)+EGx>1yB(|nRE>fnz zuub-Rh;B4{pJ_r}`k|Y(NiWKdbAlIPZ&KZx2t`}L(ta0e9^!({2V6_TU-(vXgC?y@ zUvt$~wx$QxN`h?CN>Y0xK^oha;E&&$;yuj^8cFjDjLUqvCVZxo-AY={Db6;}O190W zY-T*pxtTfVW+wL6-Ye6Z29P1@%}l(ZjPsrZL8P1U+RCf*BB`Lw+~VG!9Di{BEJ)Zh zu`ZN|IDk~qZHeCj64A{WQWrAO7Zg>S-W0eT%9|(N=q#YS+i*cxb5Rzlzy*?TBqUlq};7YLpm{R8?ItyVLjk(GWschvy;)77+9ho=L8OFtZb_bzYChm>x5j} zm$*Zn`{a^%G9~#bNW`&>1~&b0$Qc&?K+oXRyo3sdHv>FO&{lH1ObuAbD7p@98JeTyG&!`?c`w*IE14a6VE6xcPkx&%- z!mPd4BwIP(mub@o*4F({%&wjSl$i=GF-1Z^k+eS}wKkhcRCoKCRVi zOb-BE!<$u)W#}cJg@{9gl8i&q%!Zajypn|zKo}I!*Sp<1G{e~1BI&2&KWgETC8la;dIT;-%58 zu%sWlnwEx7CH_GG^q`pL(xyW(BdHjk9&H(vWEhI1vsUX{&~P=>TNUY%1tlsBasR;K zG`d{TqpW>@JUG%rK5FpYP_%<%`i49G6D$%!(TR>>L%|8FVtF76P_*}oMX>0Q7(;BrKW3oeP~FlZ2df_cm!K#iSkbgmjcZV`r%L2S-{UEdXlF-7v9t^fCXaz~q1- zLYUauaQd2Si4$N_s0o0Hy~d^&XIu$#aZUF-EW~_K_9`7+O#>GF-2I5~1Y*~yk@tE+ zc}_<`GNwNjPaww6pd`bPv^hgdOpNG3QV=FeG6r+#7PTdA;+KXvv+P-V`?Vp$PVy5B z5kayvPHCBjTDK2W4=R6bp`NxT#Z1fr7cDL%ty~X2@ce)yi{YVjPn8dJ*#qdXOZdhKe5PK22qaaaPMBSS+xF6*myXp)x_wA&2o;x>prYVkSiwk9K&(B7n82xvX@Fc@okhXNE_cBC*75o)gRw zqSA?v@#1~FU5Z?Ns~@K6w_CiJw&9^tFgS|SrfXZZ!-dCf!Usghd>PXNLT{UIb1;@p zpM0){5${K>JDw!NkSs0qT0>!2>_JixD~i%!2>0^bWt?HLB&NgYp+?stc90xmQ!EE8 z5}}f`*Q``Ax$T6tl3aa5C8=0TKn;@aVrIP@F}jZZm*2kq^Kak2|MvUG=jHSB_QQ|= z^6R(X|Mu~`tk3E7wDED1I?u|Jbl9zCma68X((Mu zg76}G-?Ps2VtqSAL%DGpN);7`&~-=pp*wSq${S~&)G>D(!t{3fhAYkqhL8wUmT>%k zgne=Vf!V`TTr!V^ZsSam8)u3fG*ZJPq`~{vNdcEOI$aTlVPaE6ZZTz}v(dQ`#XyN_ z(B|le?#u;Dh+d<{+zS(%g>j4NNEIgg4H3b_=4IT5m?1}CQkfw%{Wglnv>nz1O^v24d2;(rZV_3{z=)%NuB*3IeF=K9yv;f;5b)y8|$rw4_&E_ZE zu-pa3;S&rI!X)RN+BxE`11ioPf=Sr`)AU=I#1FR!BM}>B&_`n<;j2m$F{P+jhf{~T zeliS`QaR8fZMt)j+k*+`xKvRA6TRiXjdNUX)cAjj1#tbVon8!aTy6&p5y8YJxzN{K zOXO0R=>r~>P{cQjX&tZeV|aB z)il%3#{wq3HZrLHt|!<5Gp70!ND>8QP!j10ICfa;p{%Tc+H{{=%wy<5k|-fcR6tS_ z&2QuUnHztiqndaxB#8>rtXxAhn%eZCUatU2yM7VBSKvXCH}6+_ zf$>&uT33*ayYEGCChCpyuHBxH&%(4gVZTvA~hGKqCS ze`Y+Hq<1FHGK3u)(>Hwg&#UACLxhmDlgXHKb^;^|xop0br#zIA;s;(lNOC?$byq;L z$q{2xh5#dHX4Ek~2=vxRn~-r!-{||mm!zzPA)pNq)^vMd?!|My4Z*epC-dX57pRqkVa~92kcKm9{-dQxcN_;c11V!pnJ1H<0Zp=*Hfa*T} z%7F=g<*RI3irfEME)2t8)itQpl`41HHIU+!)}I?K zncM$L?|n7NaQw7Njy>>kjKJSlsd@t6sVV5jbgH29v5wl&{$Kz7=l}lQ=zK(S4KedG ztC%`gSfp7hsK@l5uaqv`z7MV@Sb{_n(~^bjfQUVul=7C=+EdnssdgKpqTDzYr7J>^ z7EhPybxHWmrlQfO{gLxLV;0<(+d&4jK;q>C%B{a956OojWI$ zbVV2q0HlM)VeW|*;)>LP&YcrLs;EdOb&fzkbU!04kdhr0RCYp#rf1B0BoZGnL;#bl zwAM+ttix5&@uf1mb_yP5K6y{=^sWTbNay8~AP;DmY=gUURG3(ckLM@DFe#J*B~oHI zxFYpn!U-T8Cc46Ok69eT0>pkf2_`my;~uk&PB26S6H;0Wlg;d|CC-3JU2ScG(LE-W zIEOBG{t^WD0ez-JKXfN3ec;ScS>ATgo<9^SiIp_yjpJZ6lh*qo>dHMaQnuYWUWMg` z(CIq;aL^F%#G=kACrz(ivxcZE_XCCq!I(jzont#mbX9Z$80!hfJx#yWO3YO7z({0| z)(7;_Q~-+)b#5fwS^jtVa+KQ3pf_r$qJ)R}W&-To?PsoEvN ztd*aK-ua6=)k6)EG5ydrX>j6LM;!@3La$UB++FQ$VUWg zPY4i%v>fraDThSPE$Zqf42kRO$njoI(6vb6X{@&<5l2L6DRBjXHvNS#DU-@)%|6|k zbF{U}pYJwz;XXvIxgW5Bj)uYr_GUKbhUI8@=d`dv?c~byy^z456??IcnG~)YB+#{P z(4?pFJ0;Qw&P-2Bi@tWrcwbg<$d1^%`VMi(NIiG^&60b}pb#1&=84k!&JfikkukNW zBn$j?p*K~d;lXud^C<4=YwpT&20W6_ZR@4Y>LKX)5*RI3U9Ir&B^I@@WZ`;wY&X^Y z=}-UmkDt)%gGb6Q=mfgoFZT~zpm$kr$>o2NZA#y{s+j)fILRf`&S%1Ah;!hwSGX18X{ zaH-y=k3l~6E}_B)%ZcSS&Ah3A@2EM(yc#h_PNJ!9voBZ&uIIoO5ovA_UP~ zG$q|_^36SFfC%sB43H{U;ulqV2|x$ugTJBc8Rh`343mLMo1Vmb8q7tVG>O$Xu+tQ)jy0th% zN$CMYNU$hN4Aj^8A@mAbq_uB&=aH`_NE}#F5Z6O=^8?f%A zGM&{uVn$0ZtD`4KRNO+1ps~aF?vs;cy0h;jbu>G}>4r9;2ka0+(cXm)(Mv?}c~CnA zij`h+ClzZ~pt1d6@CTe^F8sNod%3p@1;KK@b}#qVpze_&hnzK6_vf5(Ym#NS$*Cq) zHu!FcF*tCJI%tQ+)WQ3aI834bAQ(T?JgPU4CeSMn-R0w4&&O8M2~O>4y?^{%@<&eP}z@7jOMgWAe5S|^kbr@>u0a{l}v3Xt** z&xwQN>3YEde(AU+9W;5wBjz#)pCwv~k_(#_ILr;J65Qqk?F;Att4I5K`eCRkz6|fP z;*6(&UjAgW73dpZX$qM?DO==_qV+RU6nk(A+n|T${3$y(Qi!Z#oj*NNU%9K5>S(G>Z9wcqf!Xsw*)aYofoSr1o5E7Eg5;jzF@!jx^Ks+W?(f|6K`0faYX|>Z34xlh&snx?@TNrckAv$y0aN@aDep5fhh+ z;WW%txl?rNxqK;BVb|942rpQDKO3t0amr+V{hq ztF*|Oki0CL^)`nI7C!=5CKTd&I<~|P_%g}kNEA29u$dBphN`}(@m-hgfY89fRJblYFJML zz4b87u#G`6fuy7-2W%06u<$UnisChzPQ8xgrG+XqKMFHMNst=B2SI41?67@PH;lX2 zrQxm6zSr=jsa;Jmb^f|jtcHJQ9SA|lcZTJpK#3G{4upn?ym1^FC?0(O?c1h?ALd8Q zia9|g8Vg(}Hsj!tI6S_YPQXH52=nyY2obXk#Dc^--0R|jCNkY+`ba-?XC*CtW^QP| zWATVNKgIBz?o(&yp|XR8wU#c+S((Pd7FJvh^%f-R$XD<%nqD92hwf*jA>IiEz17IR z?EMk*Yfdmki$G=xJ0*;XStsny%0oLqpq&!N*L7LQJ1jox&PA|5Q@u=>-o!gb?EvAp{K(tUC+M8kV*h3Ul z1ll>tU{D~hm*iV{Dv8DW4yr13V7sOh!hjG@A}UU-Rvla}%NgDF}|ZHe9*pCZzOcCmGWL zGHh|A^XKnBfBW^fUmyek!XG)P21HEI>}2|(J3j#z-e6wGj3?>A1p?-l2JHfIZ6eo8 zh)y!7?f^D|WSDi<>VGqktD#<&LXR>iQLUDXan8Yma}JIh!p;cNH{4m5_;tV#5{t?b zc8(B}q)L`@0E@M2E33Hih4`TY2a7Aau)dlrT~S51LA8A->A!H5FF?6&b%ZSn;R?n& z_+C(!tGpB_(>GjksxPo2L06Ixl)R_!>_9q0TUvSD8uhtSJ14W1(c&o&b}=je=IpO~@Ma zQ6x;>NqRc5tf?T!FUpj>i<4EleliLZ(m_)wU*mrGR|5P`fB)q_{`LEBzaxO4r<6a# zQAI@KsV-5iF)g5jLiaP8jz~XrU5p!e&9N(%s){dZMLgot4NMgDdAa9?dQBI>cDe{l07B zB>c1RvqvC_b)TrsHKPQWGYB|O^iQ>=+C;tH1Wc*No% z9?c|qJXcqE!h6RHx$ z!WF3p7|xrhq5?2_x3#7px}T92NIA|~fN`QwSdA$#IbetgCZx3167gl`$6-RQy)EYD zWepWPW157QD{;mI_W`|$TGI~)eZUIDJ6FY($Re%2w{N)OR3CUJd=G=M^x$g)gxlR7 z7!#YpSR{4R(;Cwr1Q?0IauOJcCej!prU0E_hzN|Nv___wq;@=qT#4PYfzcre#rF_e z{RI_#BS6{FU+}o#I`3D1!TfoG?#g+* z>%b+oqkjWa&+0~(EqZ_P&v{L(7a*%Bln4>7v?4rka>!fm*B-m0^-;?acbw*Ii5@B$ zj8H*Z*z5TLHa#@E+F6#k{NW3VM*n89t6^hdTLwKp{s!eiP z#|bGbC!`F**6XZk`jBaG*I5c)IQyY1LJ;?*acm~a8uLSXi8-f?R8dpoFYYi!M98R0*N-z!;yB?XE;9bphp<&wUEqrJ&&O)L-hThaoc{hF)6^3XU8RABI0Y;+5DC6!0W9hHg zZWlX#iHiFT5rMH()Rjz$W#n4o3^3-p1>Do_dWr9N`Xf#(=F9w?3#peXlBrFg-uFTK z$Mo+C{TVql{xxkv36=zSfOnoKZHY z!;(~#H*4l>h$htebjf8U>rHLUH z&B~wh=!y`+gi$g{2W|coQ%8CmNlq!Lq5>xAm2WxuXH1sa^`-yw=ih$$PygRP{_($V z*O=g7M}INgF7EmwK2shp*B=X)gk2vK(?gYCe=*EZc6|}e1WiFM+1Q1zjBxMlyyFZT zSk9G?nLKCPEIteG1ApbC4*X^J!Nfeh5(y7rO-Dijw!CMT79(W4sw}mF18GCysX=>Y zGWCp^DcqP(PEV=W-nU%gTuN$!2KO~Vm7FJ$p9X76VI*X%Thc)j37_%%8yz@3>A4Uk zD&RDK;Ha|cO?t+3oLwK2(V_{4hZs8U^fcjse&`xjI@pQPf~=QfL{csNW!B4{F-t5* zh9kbGBZF{0GcwTET(upK3>nQShT4%~oU8NXT%DtlLGM0ka9ZU`#;hzpD(!VWM?&w=z#N=s)T>}31^%* zbjJCwQbWvq85tSJWbkg3MR5d#k|=7~`V-+9!56w0PIz(552fZpHcrWKj)lWaoMGW0 z4gW)~YoiXXW!{`}3})%Yj4f5ct*_6*hx*G;csn}rIrP9=9^wu>;&$~Ly*0^x+!r!t zz$wXMnvL`XhO;UT&TSekhFj*q@^#SO9RL02|NdRX_1p^cEQ}33$?ynCOp>TSja|2E z?umSWDkv(xh~g;Lp{#}Sh5lIR_@~;s(S!H4^Zc|KEYFxaA~Gb-A9>4CZA%KOPFiV0 z8%820DdTv~5^NZ!%e2(@D@g}!U2go*k|({WF(_A@#iJyOGQzuUa7gax^=PfuFz%Gp z|A1O$#;Nq`M4XZB-;=ZD2EDBYf!x>wr?m?%{Y#0gPd zZwVF&Wgk0~#C)ADSS(xvEKN!^&f9r%-cEP=u@UBH;q8DxuiYX{l~1rm2$rO`X1^3O z+|B??VL4_kHxk6`ApsUn6*_1qG-x#sls{vJQ85rF7j>l=28+!ydZv>PH^BD5LPQP~ zT>zG5=NWTYq!ChCMvb{IVgW36)i0*MoM4CuDTV!_(3{NeTH*wx6iyekk}F?Yf^d^4 zjg<1pQ;Y;oRNL-1M80`)(#VNOu_Iah%1sX`L_|>vGdd8T;~bwS=lFD|-&vAb zQXiLcjv*S)5sO5Sv1vT?wJ(KC-Y(5q*UjP*^ECuyi07g_U|(FrwjV_TZJ=MyN@!bbpYa_tpDG=& zMF}; zJk!CHYo8uih$5pDX1x3RjHyD>2x<4J#`LD&TVKk#8@9CC7GWj$1Y1O4v8hGR4#(>_ zSPGGAe%eH$XWY^+?Q~vHLj?%M7;T`TXvWDz=dCPeYd>BZ4zF$Pg2lo$z|!ofW5SR$ z!igC%O|LC~$Ly4Xlmmtckz%LC>1%G-I$_(A_-v`god{S>4wlN&z63_ILf1WcFcXJb zW6cWp%k4U|ORT5srQTt^JTBMQBiYY#w;KxMS-+_I)na+M1Or>Pyr^Sw%~eVKRsRbz zxzY?!5=K^b#)h9J`hbe{fP_l6@(*2Yf8j|YDvfzYa0cEKi+!!``NI!4ErnuYuDIf_ z-Z8u1C+Lhrx12$Q@cc|_-x4HsPoaPT)7Op+Y=lH`##h3$KZmHWdt(30y`#-McOAg+ zuQxAtts+%oEo$hVP~DvvGhCfEhdkNR*eU0kV1h_`wLvyi4$je9GfIbu9(Mb!JM153 zXKPVdK8NWX_-P7uDe&MQ^1HqJl1!E5=doR#(Rztry};L(AjtChjpZ09`EL`F<9>9O zQDB7E{7ZLVUQ7r&L5WB@uFoHS{PXu;fBD<*A4-dPi>+k} zgB~Rv&{bc$6_5umg$Qy3=@+z13wB={T@shMdc+bV4|w`_)NHQlNNXYYv1I7~=st&@g{&k3nlq3l2_7E1~LdOVgD#sugSFT;BgB!&rs5df@^lE!!a zr5K2qAcL+V4HO{7d~F8W3BlV%isef{N>idr#1wbt5-I29zj=BFQo?2Q1Vcm%3Xyag zq{Qr}6OcmQ{Q=jeM{ybn5*x!cQm##^N5hv-#!Y#F20qY~1=A1R;e`(nV`dNnF%Ti3 zy(Nn|M%4$borM`C!_tmCskVx*PNd`xbJTng>*za7?@=^TrNm9s(Wo|d?CBx z<7w;G_(5XDxH5SQ5+nP()YOuF@YN7vVnJeLpVycp4$-O}-?jI1=^O5B%Yt;k0=LUL zzm6G{1?(u4zUIr6x`5!_@0ZF-94t&9XvJQp6LZ9s!1Vz;G1c@cd^HR;(g)5=Oou0y;*!dTHDfK}JAPBSBI-z0#1x4GG+L$1el_GEx9%7zb2~fnpddr1M#!v`t zMcIu6F-=LVB8-#NccXX}!Tv(?GM3Mn%2b@0v6%W&1kNl==zkS|H*=M+#dCpR)7X;YV*ULRu)^a#at~SURx)X@kTodNIg5$86(1 zk{5d|Qn(gWx7{z&h>vWe`=KrGJPyQ>!q|d^(`KQGBQZlp#*y=~QO(qWctdF@QLzTy zbl5=(NkchEOrhy*35f5ZL`_T`>;bVU*B=Xy;e{QDSfobw(tzH8oh1(BQydY-YanYsTd9rXN>@&4$Lw|PFkivMQWo+*C z@Zs{-_RAmeRZ3d1m&bHdMELk1vIZ6|c%2mN8nab$q>Gpg_)1=hdO^>=kWY6wTw`tp zck(Me%#+n$#6WXVGs$7xcth%wMf#naD_90C^7Yi@)by_(3#kU~J8yqy*WS9zwM|1v zpK8-aSdX1TcLr)f-$unzCAQuc;{K1(6SbQQnnBVoZ)!=f=fzTJR7ez9k0OnvBe zjUPGs3Sgw`Czoq^l?Rjx{6jw7(Ju_+e;G;b&Ix$K;HV6Prq%vBPU^V|Qcp4aNVI7W zjL^lc-7gHGIR(86R!>g^nS!bs+4FT&M>=Q{!0R}x=PFn|J-K)mXsE_|iCHTvjB6(- z3;FtXh+d0iLR)#!*D}TFGHq_JqP<{DQMpK#-4EGo*0$cMT&Xg@>lVm1{X_+f9)UT30^A+qnwfPSF+`5}G_H{m@NXqy^H^=-~8Y z#9-EWW1|fbhT;knYc(4)goKH*gjuB{v%4zF``RNgsl1)5&D*)gFO^6mrCQo4Mr!&e zuS1NTt6=OD-y5@zi1^AlXftS*ts5J>|BX_qFfpV zkq9rOy#_DoYrdBp&%VgVx9rQN?p(*IJ6A#7={akR7k%9|#0xjv6)$N481$2Iyx76) z5OwD&s5?blTdwy(4M$ra#O*`B9lp?i=emSIjbQSWPwYianr)kc4l#MIf>PC!MA{`n zjm_ktuemBZAsHi2-%w*Sd9LG3o-1)323@v;nx?9f2KP0QW167xyl0&t8MA3TINXS~ zbDaaZD|(4D!2$$9B89KudJ5|70dr-ZKqh6rI|r^Q1wo+HT^xpsny zz2uJbY_5W5)AP{|OxjC~7aG5BAZst+MN}Eb0;Jd+n`_KA5iU%`p;Lkggi(o6Otz}N zyI(bt@7kP`>k#YZi0@X`%he`j(b&GjSm(&^xUF}NyzyYrL24Z%-!At%Yc9P$7S>#P zd$42VTyRZAx18Hx!<0{kkZa{CIR46!197c%WRmLJL?AtH07W^w+UN5KO3l?cG`_EloYiu+LsyEx z?`4TGsiBR2F(0It)e?U}K>-a3O+G*Tjt$J15>YkgJ|iqwo8m#=_)0@aq$o?+yYggq zU#46#If9g&%N7+kNQsFVA_j6gM%PFAn$@ljsCPCOo?K%VM+s#_DjIZ^VWboehl;K- zYodn~P7tUHLzVIK14&DJauv7uOH-u4JE(-%6u^tU-$^~dzHjEhb-)}EUP|krwnuop zdcw*=KECCe)B@e&WdpLS6=|gNf?9iWHS&%PCc#RK)dRh_It~^)n8gH<9#}YMq>7qO zu260c3iB}+L>Pp#FX|^eZS+uM^FXd04n{%Ee!E1dDf|qLn(b?@&z^yrTt`}H`fbKB z{F)6$I5D&}n~*fPuZe6OBDV}i*J0EYVuUhI++^NGjpaxKh56x^ZD5d`6K}US9Y>8_ zag2FGW$8a)mk>2chfSpKFuIQCX0;VQtGJOMrbLL)Mud_bWgs%_E(Ly}%7$WB z0%@f4A{vT?#PO30&Gfl_!j-1l$l584r2+mzGbJWWtDu;G(gO<-TUxNJ8>bP~T{ZvI z&+pDZALi-%H7DmIsAr)Dq`wfNVCQsDa@3y*t6RdY%6m2^#NcI0)`Ai7S2;MHh~?5? zFJ@?+;A>GlD1;}aw)pL*{?iJ~<U4?`mZ@+blp$iaSVhYI++QPwTcWhyDVbt&-x)~Bs|vJY z=402?FyV)DqusIEytA~tF8L068?U%1lV9?Uy~eA#kzewCrFOUNxVhY$i8I^U&{*zG z#2HM<%c;J)t*yC2m3FD+znD@gOe=3`ketdhdWb9LLC2KHG_JOIGvq{MEalRF`3W^y zelIH8m7Doi$TTekxJ&fVyRr zRQk@?Rh}4g)pC@aI8AL()n%P5Y@dau-|ov|nuoMFtA-DX;oeoyls#hpQLzrE9(6?s zz##haCi}^jDel}+ zcwtoUJ%p6HR@!d}O);rN7>VdH%J%zK4KzI|XNf6D#X6jQ)b*2Lq}a@oDP~gikiy9o zRa77)k+AjT!%d+y0_)bV!|pQzE;`TNp~cz0;bsyi*djs->8;rVCCmFV9Y;zbe?cqu zsutg3FO76wIKx`1t+Ng8Ya$nPO1uA7Meq8_I8y8|78784Na4hoDk@r5a0OzMk_89H76>}6jz zF=y(~L2}eOV2cnbb}cr2%~jC}NGTMqg{I%SLrhnaMmjN^HAV86&s5?Vr!sZ@Bt*(% znd+w9<}^()d#8sKPUxwk0x3=28B;+-g~%K8@zGp+FvWzIz0xA)WAH;+fX@nD_?__gXrufmIf|LV> z&`9A29;CF!66tHca~$7x)q?<LiaA#WDF+M@BE?Q7)7QQdDfOU?rr#nZ z=CVj5ofpomSFENERiYpzRTIueN?}nb7mf)oJ*4mlXliJ|n$jGirysg*AdEo#pTX(( zwp})7Wr}%L6)F1-5h2B9u}m>7@PzD(XhGZYu}!g}g1T!Y>p{wD^UiWIbcnuQo)dd0 z8(WJ?s7y%1@l65sZ}(zZGMl?!x8Jad>r5Y0(2{<6z=F-1e{ zf%wBQRVQfnZ;pXu7F%#iUPtX{+B+pjO$v6;dl8iFbQlP$r(Q=jaZD6cG41BSFJ|CK zRoaU$JVA@R1#8z^hsJEfRJfoU&paG83tSXtNn07b--A0fr^43BkNZr-!7?8AnG+eN zrvTjrKPN_5ZqcgJ|K%ra$fSPf_zfMdUw2IH2Xo9h8X8lVD(K^`8ot10sLU*+d?Xtv zHQ*m|D53sr{Bu4kvHF$gL=1A}%{pk!mKt0ds9}Gi%@(?_RIxLr-Vx4mzx~jdx)6(p z!y@>8h1n1RN%=4v_L}&IycQsZ#6M&ZHm$OAB1X6dDD<8^RAD?OTjf-7=6t6??c`3P?8nHQW+E!+0tk)mYsKid9F$<}q4Lkm zrllYM^rwIO$4`jB_s8RsNXhpLNFvi`QrGq}Nrpo!{+98%;sm(;DR3%|Ys+Y<1 zJ9l)cch*}{hxBIMi!wIXV2=4QC+J^C8DW5w2Q(mwSzI;Bu$sxBc1o0~eabZbHuk2* zf7eK^3$Sf_l?c0%#s$J4AYFhRHq;?^yFk)dtL|_MR$U;i2fs2G_)PY2^KRaSkxi++QBAR&^KJCJHZYu6kmq02^Mqb)gn4?nEB&ggW9Q3%-Qdf zRor-I%(Apeoo$F|ccj0j|bYGIAvDiLvyKaBB~SZ&IIoK?#) z6!7RdoO1%+YeQ3>$WS81WD&8U;bf7j8u&qz;l)%0ZxQo`Z!)*^LpNMWYrel!dp-Tj zTu3GO%j_nYJ9Gy+?3fRjw~eDO`;z*aD3-qFDyyV4h5EBKiB{9bU@UR8-bhc=HzG~E zojkB{YPTF?aTI&08!NltK5^rBj-$8bF5)N|tI=xQt}jtz<Bexekz2TcBI{nb~G7&uadj?fxPnEskSx6_#Tn_lPdAI1{3RZ&W z0rN(|lhoJ3JUo^w!IPM0Z+U$Qo|S$w+w_w;jqPjYj}M+R!sfLE&rfuNupNA9>QI)B zDK!!`PVm${0CP-_`O4slj`_I|WSYk^=D0gsz>UZzZ@G~dJW+AeIv)z2{E?k5;$`sM zYB$plKmPgqufP25_YX7yv4cqBS~Td2yXn11M-&UCro^yQQy%A|;0-Fop4?v1op&7R zPzywf5@jTma@30m%Ul}hu?@%mo2X7mkC0?2*@rkRfZiKoOLN^Xj~R71(_A)*g}&y8 zqFm18{HsA#mS9-eR=|^;NyhvZ0VX1KC@8^f=RIN=R#6Z z9uqSkIxV3yZ^W;%0#xe5p`w|t>aL!c_4F4eds}jcj7aspg7Qwa^|>? zUOlEvBlIJ)^B$DhVJqe_7qD7^2nJeSO1b`k=I458(vZ7wtc8|rK{lWyq zh!|8r4<~k0{~VJ~3Qi7~BEpH?=0C^eC=(||zl|PoXT3BM>rt(i zkHv7eGI~XcoAHZrh(a>xCgV6Mz4#g@VG!%$Bpu@`S&@9<=~?}qa2F>{!1o4Yp5U1P z9+Bc+ljbBQfuH8&kafTmAx@IkTA8+rfiIJ_OBvHn!AW`K2U;+Y3j#n^WE%kD9mnASkU^WYV)jrh z>9LJL!=0BT zlS}r_LZ5h%JY$Z;9~wEhk}PDT6anf(BQ)hUo)0}Va>U1?2tfAI5rDqts_l5HOu!=j zm={~)BIqayaVp!mQks=>nRnklnob+~tvg4tI0zC?8FugDkY;AM!L~lQ;^d?pkAE2x z^duP%HKe!34Ptgg52S)MQM86o(_~pO7sRu*H++-I#2kaYvuJXfB#&!A=}+$@jGY}g zo-*28Vp~iLp#*=WL%+tJon~fAHjemM6l%y`txR$8`FOHH;NA2?ed3J-ag(_)9I;pi zSNhm+P>0-_qN{r(0tli{LDlgHV82fST6QE;7duvNhK>!?ubO-#Q8C8g!(ZDtyi(hb zY~aF#XyPRtA23D;6|-1?Yac?Hjzgu8v=Bhd(g1h_kpu!Nf(6s_(+*Uc>@lX!6zd4q zOizUI#nR@_p#Lku?^N_8$`#Cry(V!jm_LJ1mDUN45aBYW$HbI}E>0{@!ovfa9=F8r zb9lQj(Te|qA=XG0f#S~oy|ZXCgSc5=I?_9P>E5bXd-zF@X+LIn^Dmul{^LFtMgY4E z9h3D=SSbm$+b)cV5=~r;d-px}qqjN~64L`T%Kc(OjqnR1si-l%9ahgT?AIffm@`ze zCBf$@;PNCHk29pS)&gM#s)ta~!d+yACG-jG68B?EOPqHI4!5&CKjxvFV2KEgq_ox& zU!AvB9@bJwD-teq>K5|X!9SpB{&x9%%;lE&+_#HxGE-Y(`YWfI$ev5OF_(KVk$2?8 z17~vOf6bMyEhy1Oe*23yjp?uKFbG-slnPE{SF7X4U~=$6M8Wui;KOjz?C;VKT@yt8 z_+?ZtQt+5%@aU0yLd>j8Y+$5oEHV8>&eh7NjYjijoCUN9N==z5#%vOfAr?@!i6dtl zyS5+xAvVF)2Bi5dvq0}Vscid)^x6mhx%jB=gkNG}M-I5Scn|;g>u=w_|MvU$Z%Hk0 z>_A+P`th&7|NQsg|2Yl&@p+l=1;LHG#}P-k6>&w>0*$+lnu==t=*iHSO@bLyZ#Gz- zro7xQlRC&@;FPX@Y*+SU0mnb*@Pr7i5gKmx{}_W~rNaV@n1;Xt2qTC9^YB?MdI&IS zu@s>iFMxg2m@$iGXw0UlFhsmbjN6~!j*N-sXf^kkIQ9idMW6*N(^`UiYi}PwC(A^2mhRlLtr7` zA9Cmh5dka`{By1)qNgAKkc$H7#NRIOTnbwOE%<7_F@GiN5JCRW-+%u0>u7#fx5AwD=;8(jN;q3tymI$c8IQ>5X=1A?I*Psx$!?LSk=0_vcgiSPFY$ zBx7o?_-WRim>yB~6r2Xpl|b(9#7kqi|LJwB;6gTRwiHlYkhw_Bu)0WBF23@&n&FQg zYg2`YKS4*5tgMuj*G?@{c)XRXAPeW24C*JZJl~M6Nhu zmk>2}hLXI%RnZBkspN{qCRg0X$rU$Ft~h9<=87vC+}A`t2BET@az!t?j-rNi(1h9B zA!-FKkb6~3sTX*stDK~m#VNe4dq|gkFiN}f0aa#mP$$D#}dsqrM zj+E@(NXD{?wFI~L`3GsFlu({xq^33Q7PDn?s(d4=$)E`1NHN3MEpA%xtS`4j@5o2^ z94Ye$>hKyVp&Ljeu!>L}){7NAq!1mT^@;5pE^tqcwpN?4v44$ zq?jx1EhdWyBPIHZw*5XMp)*ctBId>v>u{1v*H4C#QivW}q;TWiLkjQrucBg2(MA4S z`l0KW(gN&MwT`*Z0yxJucha|*-gAN>BAAfU8t5joyDB_u*G@llCn$a3%us1}_TOR(PxXPcz^K`I-Z&0MJ5nB^ zg4~E-p+tot^bsljaLf?I-cVzD)97E+21i<{YfnU1eng=V$T z^cyhJdgx1Fv^b0g%b`~vZ$ zgGB^IdJRO)bsrX+{>NL)Zz&)o-o>B@G8)d8>3OgB(QPHXdM!LDp@G0HrgI2CpiXxs z82q5z(x>2^e(rjM&^l*nyyXk+al=A0GA0@~RMGzSc0l>}%k4Qk$|uz|=}7vTtD>Ba z+=!Q@^d9?UK5QV5gD#*IvulaQ_9ZwF0b8u9JNcojfBE&>?|=JvUe@Qt6esTF$L(LQ z_szhMyDZ7gwri!cu^O$$G?Xt4L+F~)7zmd*iGlOV2cVH?Il>)Ci9G!Ahb|1qBTso4 zLdg`9GGyej(_b{EflGjK)W|WvLTdfYB-o}_++qUI3Ca&6kDUd_OtlhuI3um=5R{pg z$V2Rv6nX4=LCjsVUR4^DfG%JXO^y2^&lUMu&c^23%3lMy5mK7S6VsW#F!JET3Ij32 zKwyA4CvUAU?j={c1LqdAdW!RNLQmJsGV+K%2>iq6E9J3i2Dg|@BUN)=P|i)7>7HUp zK$pUzUlF~0+H^y~`hd#hUQOvNZ!ok>VG~)27n~H;HKU9dh4_SS^rZ6!TdD2Z%~Yh- ztmPt3m^_C@o2AyoHn=NCb?A52mB69JG5pPLJ-@}gnO=R7s2~ddXr)E$!{vFbXHzL6 z{dcY+gh7aFpgytBAmEPVjjtAG+wr>MoM0D;8fBQ=zNci0`E+NXCa><$ib*xSCB}=Z z6=|fje?G-X=ba76k!}SPCyBP+(nHAaJ;lynwrrFiZ*2G0M)wK{?kF5 zq-Z>-kX~ufejMW#lT1!9O$Z!2IZTe=s^|pZ6xJ1hV=s6y7eN4ra}jRz2Fh9Y8Q)^6 zK`~CEcjN=Lt^~ulDQpAfzHv`=4>$aMjw&i$XN*2*9X8UhT|bakV5wRic<&lR4f{jR&VLQala^Sk#ZI_{9eEmih@?aNK+B2 z)KJsD@D%~3a43{aomXM#*y+Z%GF?BBTbV-i0MFA-3uBgsG}3udt!Y}lCE|zcECn&d zHW~Dj05Le+y50)+?`E?%Zt;s7UBp<9l$utR55#Xcc&G6EjgRUR7`|Dy(s`Y$*uAr0 zO}FR=pq@e;t>VPDZJA19^tW2NHiMaCW_;D zEJQPC#V+%vW~Z_6NtB>d%+>Z3#q6qzM^?Y9&hCYTaXgv<{GM1Mu4+1fxhGE0Wg~UQNjRw)VHI;s&Ux%HvD$mF;hM zH`r=_TQ1XF?>M{BA^%o;O-G5TlaCI`Qav^=k=Xs^6#?7I+5%iJ8n{u8u>BXr>P-qU zm)Cnt>f%jVch2Cdpal|i6CC}}RdUc3Q%4=af@t`mIEgMD+>w<7B)ZUQzu0zr7JE5b z#jKbl!ei$fWlIiQA*#9_>oJuQ8LokmZ6Y{uZx7}9MM`w$sAm(Vk;0;!msNF-Yl?J- z$~~s`42{{u%b2}1G^XBb-xD**^}L}m^|s_5^UF4GMh=COtnrGr>l)<6{1zeL9Mxqw zbw)1=%&2nXpVRj~K7ju1`k;HEKW*0skrog-P@B`f5iW&UKe#SPkdf}hC}=W8RBiO| zTU6?)e|UIJu^~C!;lIEVC~2aWiMWcY0o0lo+I%0;y*w>~@(KbjHZpW+5_VEbEinU$ z$Co>&8&yP(M8mc*9{Wu#$H?K#owJj=B6M=I+M0K_X-fCh>h79U0G8+;jTd4wt*5!I zi0+tD-Fso%svDOvn%nw&V$-0T19d16eVJJ;`k|z$Ih7dE7^~? zyo1O@IuW}kdV;GJY50`uC^7!>6H*&Jt1$dX!zV1OG2^Ludm!AkX>ZAf*gtnpKsgGx zwWbn@0o87WVx~lIE0m~1dfNg+G=Vzi7zsmgj!}2|eBrbZn5JjUI`(!dtQ|2#i@=v9 zNNJ7#4>5rbAuz92a8I*Skj8cqX!Ya2WgnUI4$&j87w~<4`0-DF`nP{Lu7ea&};oB(is*dX{z0#G<$2tP^1O#qDfD$?>+mbO{GDOJbh6JdEyrx=`@p8?p! zlY9DxujLH5w>pnHfP1Ywg!sqSA6(F|FH+eTNN{Lmg)W4Z31UH^_^(U{D@0QHY3n6(SgLazzyb z4J+F2>38m$B0xqDb)04QT>I5C9IEc6S6A{LzD~~bzz*16D;%k;iNGoT z=S__r!nIx6fl=PNv+q?KY<|W)CK46=9k4@-KV}HK1{uG#llv3y2k`fLiN+=o(bz6n ztZKukeFjSl*O}eCbiZ$JG8#88-78P*P8Mo?I*j%*kgy_Soz2GQ(8vvQ@*t`RGd2ejVCGNyL>>>_>5k0d#l z;KZ}4@z@zlE>TE4(2Bj%#+(&tc($>qV!-fCGBM8IxR-}Zw#L0WW;`{&a#8pj8g5}o zs)lFf%C00GxV_deV%;=>H|E#~XzZdH6vW=xg)?!1(C1}cUQ15l%qWp!E{1Re-k#6hz<4_2o__0MM{3+!NA|}2(sQq7 z8S|k^%s-%b9pJd;OUt5`DcswY07v|>L1!R;EQ@`;U-fRYr0zqc7knDn;uI8a`=&fM zPC>cz7i8*~yA5w=RGs%p@-YRGf(Df=S2l&X+x@k$*}Ri`+)yu|L2Q~qxq@IM7WdoF+#f=1MY+oyXK)O9$CcM#Jn|7A#9gRA< zvW82DdAeN~w}>zKo(%kzHyCo=f5gw<$Vg+=bP;KEo%j(`q`olmMpG}`Jcrcg>V`*5 z!Qc__!6_IAf!EvsK!f|fMQOl;UV|B2ZiCRbT=42+=~pBjv_T;5y%(zC54;>z4bGY$ zA86l17bSW-(LaYKKJ*r%rwk|NICl=HC1_?CWN|2uk4Y#w-W}1tjCV@?8IMY)WNY6e zDBe91@on@-No3#IpRoadkok1Hd&DFW=>V)t=%54OWL0S*CcqTy5KH4YHpEp4qyqyR zVTrDn7I02S)fUbs(gW=lujrBf3)dN>DN?K()I%>nD6|W0k$7jAqtyY6NW3VMm>K#b zea%%-&d`tGrO!Xm7_8AGUPk-nWphx^@O<7_RsU$D9T`7hkV>4JDhv*^? zP8X@7w*4FQJJ$<@4LDn*4!O?;3(c-wSJO9KaZa#91QeSs^5{^B44@2ZCznCV`JfAE z#bk>-;)Zc)B(@7>jp9H$I8H}!ei|epb#Y@0maS%6K>P4KY zZAD4bZxhn^Z3JN?dMZU3ekV*oH)*!@X?)i$i-Gt9Gv^_tT!W06<}zfw1$^V)f7{AAAwF}}RbQ*_(J_(Y;{wIZE2g~D^3x5=G{ zSTPSG4GoGA#`wol?{#|E@31^l&%CReUX14ig6hkN@%l1`M@&-@M`_-e>GE-fyO>b3 zQ+kx{^k>4ZOW{A|C6=u(wrXTw@LR|@X+vDNE%vyge5NqWO;Il>^(m}hRr+!@5I9=#i ziI{cXop>GYXQTzr3%$#O+Kro)Abv)uz~q1-G)#mg>=H)$nyaD{U{YSt0Zi;fKfX2e z2rsJp9<$RweY-rb^+L*g;Jjt0bd;9<%F0BPtxlbD9aY!Zr?8k5J=cilZak(<+_m$Ww8l-#Z<+k)kLj?pzaC;`B2y;lbag%PHgY@pRnrX~65P^-@M1#|f zHNMbv+8t9&z^Y;Gon4GnybfzZ^LBG&%$F-+jXx7rl^$mwHLTS%+a_#1Vv>xseZrP9 z5<)2{u%NSX`k^}}xAe*% z)2U;|F!#xAtQ}d^M`}$YR?*10lzTu#<3VCnkV1T z9@cUG&C2;VZyB~tym6+X5I01QS?T4MLF{)zU)|M`4jOk~V@8Nr95@N#sA{GUB4?m_ zs&zE7^*^GcHYzbCnOlGQnj5%tUa)dj+n|1Oj-0)2i%O+Dt+bNH_9gh^xBge$ZCbYe z)91>!{@GYftRH{nW=;I5c8SH|oYGTFwEDsbgcA}NFE6w>BvEq3L=p+Jl$#F1pD!&& z1ZpC5hpIqX_exx)K@q|T#5SO7VKHdcgn$P9(LRAx`+`P=Z}^Q z0@u%aZH~wqGnz`WbwKettO?DVTYoaMtD+LtIK^&IJ2kA8tv^k_U43HSinKk0#Y*I$ zA3(g<#lJP?pGdp2eQh0bZ<1|uPu33QAp?%Tp3;>h1RUGtk}3?Phk)ztb~qiTii(CE zjlc9e*SUlpI31=Ax!Vrg?2Jhd?K%(GLF11b!luKlLyxDd9Djq_Dfp}B+N({6Srfm) zHIl#|-BV7nw~B#N>c;Nd8t4uw?anH1b;#Xz&!>@U85m;OteoA^m1G!yq_QUOi96Q2 z__O1nKD%CHE|0XsnQ@T1|C>q2q@S|>9k4@)zq}4<-`KK!*Hx|cuj?C>j}`n?md$Fv z8$;OkCHMo<`s-!8VYp17kk%K9RS2s~#!6%0m@t$>30g7!{Qc)|zy9_M0tv?A)S;?d zR2Fnli5WmaT3-hp2`i#DlU1+@nX{++N3-2XtwFB+1jvX|qvGd?RBc+{>@H$9QUMod z9d-2*1i9>h54kknFoArPVWPnpf$fYlBgrNY(nPXkQkO8jn#Gn;>`Hh(t!VNgxqKbjUvIzz; z8$f`rJOr}!Qr9u}8orPOl#*kPQvo06GIb>hz$fmASW82q7AefZdKm_%msC+v}Lu4MQhyFFA->`me_#k zS#3WYH2j&lrQM^y4v}KesI+Yi2N!{H1lp@X%vR|kkiWB1(PEHd)|h)ELLn!`)G_xW zu#icVU!-rieo+y)-w+W3%iA4Fret8z|DX2;A(+95x?3e6M@AW*M^he!@E z42nc*gl9&A;l=)@PsV5vSidM!P`%sm`VpniH_3*tc^h3xhI3#h4{!vAIB)u5F9#CU zKpH}9IQkrr)(~fg!c2eQvkqgJvp&HP9s)BzgUjS^ejm?)nM(rv=ykCF>PV9VYvx1o zQ6Nz4aQY4Tt>LxGb}s^RpFUTzTND?=zf%tnBT#RyfBL+R9s+BsM{S7t0%{sqLu~&3 zgCLDLh=#(jz1tjmK=kQ8Cz!*-p-$j<#wU;L*l+?4QwE-&I@9OrcYC)u)XH?=P^Cv?{_Bul#Ha$2N8nFobxekq*l@cMnut_0 ze~MCY6KB5LoFycm{PT&2Mrc6+C{iDSU==@omcb{=h0|58B6p*>RlZg`L;0c(7^m|* zi4Gwm8xBjkoWYAs(2U556GZ+^w_Y3iCND|1*lT@EPJZ06jlWi7=@Vx&eD3ZdCAc7r za*1$e*0EA#ogOIXqEjlZyIZT{03btSSz^26wEa6LJB94iQ@^l{S-QVFuvPui4lX6e^0L8@Lq(t$|E z+jzVGRzHMLQHAy}1XT|M}wfM@9Cna}je zcsXE^7B9>sdVLm0Grjc;yreY>w4$%gebPc>q?)R5)JVLGX!H3E**Zk6VNPgOC&PHj z^akd?KBu6Em%7irXbYvxbir^q0>S^!$8!sp))dt`wtU>uLk};xg`JBtw2nPs5f3k< zJyAu+(LNI|mVk|RW z02V#v`h0}ONJK3;$w(Jy1cqYz@R>N-Ml}iNO&i%)&}}+3Ve?zFyI|4W3CmY;M&UoL z(%Rv?qIjn1$wJg;&75Ee7cBa&K{UG=Pfn=BGIt7E(f5sgno4V=^CB9=f^W*bRsXq{ zVxyP?22tAvGzTm?$z?mlDA{U8Nx`+YnK{lV*=j~f`KA@mdznRNl+YNqW2O`z2|)Yy zRy?rijFPQGH%gnC2Qo{3XlG6;7G>Bt`(vZt_(31_jf8N`a>@|xW2@N)T|XIzi5|&( zK1MI6)D(>(se`xw;6RwCdbBYDv5PGTMQ;c%FI$#JFCVD9xM>AD)LQW~Pk4? z8Cq%|(2eK3`K=2s(+F>OI}qbbhh);Ni1CqLVtP#Lyjmr^&v}vZxF*8KO*wLCA=<|o zo?E>Nw&@g`%_n~JVtoBVL-FMwenbJnWbVJ72X)q_9fCg4pcBWxWyL=KAYtjXG-$bsMZmYzxd2a8U~TA89X)AZ%~(0{$6 zFG4bfV*1LojhsT-?Ebs|Ukl&=%OLJl5-iC*r<3~WX@t*xV2-g44} zlYjX3^Pj%`{L8oBFCW*B595)!?TYuYUet-(eY)Zld%<0}xGd`S5j`q)aM0<0Jh=_9 zit&*S58`fnnjCgV(p^*dQ*r~;{N-O{(l=s9IoZ;wW}D9iYWhHGE>O`9ZdDbffKOu@ z`ldMc@ktu#oBRd0%@SE;!J&6$hCeU%;e9H{o6@7KVIe5;BTVmp_w&M@K0(Xr>nUE+ z!H|5rkvbVr{LbW|00Pz)_)Dr4;Me#|0s+cD{3Z2Tpq{~B5)@Ec;4cXSKmga9TeB+k zhNBZgoI=V%h(@49{DxwIzecF#!++YN$|v|~-IGH^iSNE2(X}KeNHN^tZm=g$q(r=Mh(V__c|jR^`#N1}E&7QfWfU zJi{2KI47vSP2o)U2`yYj!O*r$aWfMWif>4~r5!>#{%Ha;{*=JWB=B4JTkF?5|1aya zB^E{szcVM*+Pz-;#DQGhdXrQkUD0pT_bX}4Q;Sci zq8eyh<5vgpm+Htg zeZ>{rbxwo7x_iGE36-L-yBpA3yK4b`H~ymOgpPeCPR3u&xG9sfBpJq^+NQmH_)Yp< z{ONI!m8Y=ycpR)>9_Y@#hd-h@NIL{qw0%9{?|>a#{6(Efnf#o5JpV=K1L|OnghZAd zVt>1-l?H!x$9^$VxG9%^(%n&S@K-;nQ9fyTeb?@KhWB|m8Gkk3rYp%X{zzrQ?6|(h zam7|cCq*a}MEUhY;NPuGPf&>mg*bVc2z(yX38^R(meTa41U{3Zg@PUeQGszRjK{#{ zIOk{$l6&>JI72b8euv^jEa)N7CyVrQ5it?i(f}(mcFebY_>7GPa`l5C-Rbv&QkstVE1VP=@wX@7MJ?z4lX=`|{$%Rr{%s zGwx4>tV-p2%&#f?K#%4BCHsl+@AFfdK+{kn@?7EHY|{z;?WUBWZ;FMi&yetT=(40E zsk|XC(z+e170f7Y8K5Fd(*ul9(6W%`x23nh)}r=^h;n0#-%1S$-AJEvYQyB|X~`)w zmg~p-+S$r``CK*bW?Q40hF{W?%a3_G*mW>9{I_tKFrAvJ0ggntd?j4lf_+ywL+CH& zjke22oPr%ZQhk5G0BuNR7D)R{%mQ&VTc%Xs6FrC}G2buq{#NE&rRmE=;}dCGABYj` zEitT{GSJz|G!aQ9wkDEw>Z|Cxsn>x=yZND#Y@ji>80u9X4Z|cO2F-f1p%){$nYA@S zh~xQCJ%85s1=9#LL&YcDT#-Z70b8_4VaCvRN#ba>e;h|jW;MYYQN$c(pGLlqsiIt_AIK@>-Jzz<{QxYM`^=fvNGFCfvC-sHYqE8S^1>dmSj<5 z8!h^kSh(_eBCQclEP-i;icf?w?D(DX`vF^cNXe`Pt|EUs>Nrx!y8}{kKN*)`Je)N& znQVJ2p!?yB_eVp#p*YsCYI-7!BSjBmK9Qt{l!jZ9MFmns-RVCml6H_-v)b}ueO{c( z`mdnuorNeMqdX?M8n8Eu+qSAlRC0nOqob-HWH~^@OljY+G;{^JS zQH521XqymcRYQyu832sWixU|CNsMj)Fot1mrD?jRC$|CM%(^%~f&z1u{eP8t0uhID z%mEj31Avia{3GGx0U$F^()49rZB0DRDGYMQ1=IWs)VSm`gDE zB$(Dn=fyMhd9*N@{5Mn*Os4Ajl)j&|)?u{Y7bhF4QRCR;(>b~@(GaaP@J!=GlqoVg zLZR_1meaZX#W&{*+I^Wqeb&KV>G|dniDY_k>pwAtG3j@Myf}7dzbv@QJRG4*7k3MT z6I_b}zwxc;22v(}FZggVNqB-T;N5EFFvWiNF~bqBiA?^Fl9XZ8$_2!xZNPLvWZhme50RG`lU+ahPPv z7PO+4k{|In-IhRkH}S2F$)*Q#>;Z@8^)WBs3Xm(dVG5=@|^^#Af)69zPuRQI4x72&5w2dF#jn?6-(=$m|( zWAdp6#z}`MvPii;;pE-V^MjPhe-Go`&-49nnx<()yXp63#*tj!I6OHq=wk4!e}F6)$|!=(42PyUM$fB(zB{7b&902|>Z~0XU>r>`RW&Mi+a-7yO zRkW7(uqwOX`TfodeY|TOVkHe~!gKGYrF7EyER#>O=+$o(^Wvp0bS;V{tIv{XA`Fp% z2Iucb7^qyWR3@KKl>^NIOSCv)me4EdIGXJp$8nPRM9@mg1PrNuBYqp|s@BPu6WSmC9xhJl%zEbM@elMovuS}@Tn+CD?Xq^&u2bKqeV$Y z4|oqH)Xge-6sHaYAYp~xi`iw8+qJEb=8qRvGCP2Kty?{Iv7)(8Zc;?+8G5j=3M)4N zqBRFyL}(vE-w>C>tsWcn5ZPr=L0$CddjvkID5K|qsao`etAr|3MXd@%$I(6$J!N4% z>3pKeCvLPxIxobbO$4nK=p5oMGZf(>2kM;EQ}5$s3IUVJXLt1AL~I1+**KehUeMyi zudiDspS#hRgJ>`8%X>49PTQD#dQrj5JIgFT<23frH*9`BDB_NlTTYUXBq*Q$whD%Et)6&@FQB%_%QCmx-~&e z2ZQtP<(B-;1Q*9mxfv%1EYao`W{IdYkz0KJ+X*-+8?;m8R!N-r1dhf?6?0&;FtZq> zI9sHpB~uBwOfG_ZXdzYtlND$Y2c7YU=G0q5q{hUdWnB*~ zI&ow22}T($2Q1;CMW=F1aWqp!C!nPqV$k#zw2X(es?Gi|TJr6y+%tzFTI55WUx^V} zMnp89CecGn#hiF44L5;_-FKhr(4ytMl6aYPmc!&f&r{H%E!(VWu#HT$oEKFLe@k1IKnsLa;pyG3ovVn+eGblxCAg>J5={i#+Wtict z3l+_o02Og4>5~GiW4($B(;!joAove*?45-;`}El}tuLKd&jawq$)g_OUH2%?qF6cR~;-q_l}>B|z2g(NibO?RLFIv*NK6oRr7B5)uF%a>hTUu)9pODINYBZGDD6<;!1C zA;p`n!+)c4Jp8FJd#6nCV=m+q9THj*qVO$xXoAAkL`<*mAhZCe&-QYfaLPx0Gf;m) zxJWHGdZ9~$N1<_!%R~eWHZNZUYtKPcE<@awiP%7c67WRue3rfs+yVKzSZ|6dkQK8} z=CI;(%_%5$if!B6p~jc5nR*=d7|J*IDIVuh7p!0}+Xb!E=%GfFAKD_}A*qZ4-q5Ow zuF-PA9~XOQoqNDgZRaSnl)iK4bLDaCz*|j! z85F}p5nAQA93#A!TXofc-=7>9HQTDB0|BF(Pxk1PA-9?YQndBMkLU|THy{!i@&H$X z+il;PqAEwq$9Lk2=BZ47r1Usqkrpq^Bx-LBch8@xAtnP?K zT)e2;T{N04Q|brZUzH^v4_Ss(%on&fpH|Wu>AawZj!f<>;p4U@vUOrU81$22yku?# z6Upb7^r|wVWiU~J7rdXCZ}sNmh29iX1&t|)0Kz`L7cZGJ!o?Y?lWYD`2@94atwlV% zWKs*0DUN2U=!E=|X*bwPNgNv{K_nuW60}CD_Vv4}@vAbl+@Ud(i!!o*f2&CsT`5`v zF^YKAM|I153@(z3;x1$~bpP-px(v&;_iQ}BpWfJixYZP%q7sY~uhi!wxG2~;BecK? z`UFWe=vlD}It%_WtcMg)wh8foGD49ot~w8+q#%93uHK=zXU z=9W+=)CZYW1+Aph6{Z!RykWUt%@C+O9sON`9&+>h`GHVZ7(f6r+HbrAN;-Z{l zI5bl->-e0XVx5>u21OVS1H@Vp$-r+`??H#yD@;@%bi1H`u`txQ7TlAJ)*6wC!oF`s z#9rFY2lreYKOLreowzB3lC(zgAfwZNZgI5FgiNMfEjQvxNxyI-c59>+Fh~-?83>jD~LT(lRlR2x{C%!#^_pp|ys#Kd=`vxxR;yQQK!nPvUl6r^oBPV4O5w zaT(RRUcu`}gW|eG9mb}_d|5Tx9&(FeI-`zrrte<*q?Z)L5LvJUA+ivQG60M?=Jk0!W?prG9ur=`$LBVf zAH5N`Me7*8`TUKMPf97>Ro%!ZMF*bGbIG^O?cHfnz=!5lpeZ)<;qh=ZScWUO!`0y7 zHp_53Zn*V1+>IMi{+Mk z^E*Gj(T^!LeAoH$Og~O)==^xaj~NJjzDQFa5#fSTNb%Qw^^w@Ei{CZ_Nv?~C{4pqj zQ=o=hXUY@nH=RCm^B+t}7~yXw-RNNtCgOkp>D#aW>7W1lcmM4l|8qci(}>OOF}`@r zB!kc(e$XBZ%~sZ{ag5KjOIxop!|01gpQ@F%C~q}oYEU~sN2us-g;{hX6^s2AzxCX5 zje0p-BvS5+zr`P#N^dYp91rGh`!ER~&=-$B2_|E+=D(ELS&}%|Y};i?XVL&}>h3Kb ziTc5mmM_^SA_PO_IA7*g^JThvs8=bx=*bC0U&jaX(=I2{f#=ylK%)&y)2WVO{GW$fOg34-LR%T~O zGK$KiGmgr8q-~fQ>Y(yYqyeTh7-G9%iGUJJ6!7#f%<*cA-0LT8x_anZM2(X)hAGYo z7I9FSw8u?y8+v}`5GqrSUXqHr1o!x&iK!J6l_9{VD^rTSz-ZtrnR_IPn4fQbpgQ^} z-?XsM^?|gT(?-F2hb}FGUg$L>huK-0nUxriqF*3#L2$t|9cO7{~Zb zL%3ix!lK_vK&KuDGGp@a07g$EVz}dJiS2djG2^5Ii|u^u z@wnb&wxcz2Ih&wmMmWtV@5!XN8t0x)I9Q(aNjhdA(h>)fk*L)p>A_U3b0V-`!?byT zhU(>>**9Qb@@a?4(UhyfC2?37*7pTSqJMHx+%4c#t=?aJ*mfBvHA10rz;k zE?>Ck6EH55yR5gof|wgjQ*6r(O}l7xt*Lz=Y}L#q;-l2W42?W-A1BM)smq>;wan={T=mMPZv)a2)`Dsn?PMqj1 z4|0ozB;F76v)w+SuTXs&RYqq`mnyThB=OKWUGcl=2W!3T z9lUo6rh9(vX1Qku4*sMzY~LeE$rSMf&ugO*^Sl%lxZ3=Uz~j*GJ?iu!jfMOb1{pisUELWmDtDA+V+;*K#Kni%=Tr z9i{|UiX)~0^8}XSYR(axw*S0M>lx?8Z|FcTH2eo-vS$$Mg3Wm!QmcoV@0qm0R21+1 zH|^4@=)7P?+%O!R6DdqE+V1|7J}0P(gQ!-O{uaVL8pc%B2_PyA8=Ah{`HE+Twgi8J zhKa{@yWx!c4}bXAfB!Mmn&?Bx{Sx!zex0JXg?@>2kRCcB=&ru-r+ncOHnq}n0e?#6 z28!i$fP(@c^-$nX30c6He)Q>cBVKXlkH7r%^KajN1&y5jCLg^mkhl0D}Oe@21Fl3*NDJ(ljPm1t2Dx}M_;4SWE% z)Oii?U;p~+|M}m)eEU6FR~vyP5p=8ZvBJMHCzAprs!ROuw{JgVuwQ--!^N6_f_IsX zZ*)c9-~ajdpZ@y$pWv)v{k9N38nDa?mB1Y?sHfy@2Tl5MMU_gSfu%!lqQoB&uE92NX;)aEtxBPidN2` z2Mi!v4l4`j!8wj*Vk&3Q$U6+XE}-=ljr$nNoiT+XbJwX{b0oW%5|}e0q z_x`-T?jgBTEDLp|&KjJ3dR78|Na6rgtUXx_LZdCet6}!v`7r{D`FY;ZML#s%^b?D{ zZ~*`*EL5S9e1^ZLu#WCk7=a_(EzauLH%Cg;dlBA>edtH!a*PDMxqe@#>%%}PCG=g z?1ZHr8h+6FCf~$E~uev)4RF+LSW=k%OTlCTXF=OcLux0hrD+Nyh<`nPq^i zdN4Hm+hmQAh^fNz{5~Uz*F0vQHGJ_W)CKa*9uT&w0%8 zUzp>ywurlYr`Tedb%Mrsq8Aa{${35i7acPEv8c|;cz2yzq=t0e=xO8Eo?;nTIpj>7k|OITWSg2uZB;;t$QKH)x^9YzF7wMT=f`_#Cf{mIIdX(2|eR zxUY^}N^~T*WRBA^lZvG;xY?~W(upArG-TXYE7mYWOq-dW%o+5SH_)O-G@m=rXo&bj zgM#^Pj8;wND?azC_-{;*gCexL@%%R*05h36!>K;RL2E$GvsPOyZF6Fv8I*lEWX-I;*~t*B>vltav9{I@tdSg-T*`8qn7w&jYZ z5jr}+UFFy5b#{<;7jbrwHcR54Qn*GTn5K**m-|#wjtIZY)GQ8IH}N&64fH$`^I9N* zHzmwIt?Cnx1JI0%i6(`hgBvZmY`Q+9rU7Y9qnR@YY9B~N5{uuq-7uBIHOZzc0S}YG zss8xOZ@>QK_e*mh#GK#U;F=k61m34S)3Y7q6GcRvImecuUMmj$Yf}xaHL8SO2HIzh zWzJL(u7WUzDNYWV2dvVbwlcHm%eXk2EmI1aH3@1^JE<9A&O|3@MNcF?<-s~TtSJyh zqS!FSlpcR*PO$L-V(Tzx+v@|lai5FhFGUhEi9s|diG$2gzm$HIIX4h;k=&i8Uj0Hu z7A7hTAr1|EdQWSJ>Hw!0cef$*TEQp&WH25uL<>e{3B7uVqnW;%>-+;?%zU(E*110O zu4g}Hdiz59fXLkbOC_xjRLkJQKA;mc<_;ksgYliz&;jGC`z5~@!oV1*3#JS7z*sXo ziqZf^(Wc;f5J^FBo-Z#_W@r3K;hrLW1#S37xAk`?R++z?d@fLerP$nm%#E z%8b-M&+G*s5VfjL#Atn>T2L0h+U)~+9nc{bBrwvu9c*@%B*S3LoC5|%pBB;uV_YaO zQDF!?E`qm3#HO52_GryGb=7TQ|^R{$5n(R$eyY$a%3AF5&UO;LSsPt(TrVWg+xL!&VC!4y@F6p747%?ItZ9qo0#6GY3YX_ zvA4~=A^gx(dg~4EtWdh%pcg^0t=|+U`#}A;QP(e9AFw%PxCuNIGTa3gpU%>OHh?y}e#wR$J+sfqJfVcj zL?GxaJx$z3xW(rln84C%9pIMp0T=%fmeyCapuE>tbSA;#KSh*$V5zAwU9TMtpri`v zmVUQ*5482uB}G&ig5SfojFVm#VuY|-X}2NN%7Mn{f$@kTJTU4+7aGl$>2Nhe-WS7G zCf#r_x+`1@j6?!p_JR-aZKfsu(41fcMtV<&eRH=DsJniPPwUBGB(lSxBpoo8jbBoQ z1fx$_=z>v?is-B2b`C!rG{l)vG55i9aoj5R1VczLR+dmp-6fjcRMGLwNZvGBiK-eUq1IzfQ1UT$v=K5%+t-A34mMb)<{D?ZBlo#r#OfF(R;`2sQ zXaY*{1tQ)s#DXIuih7?KGxSYS1N-!zp>K*J#_zZv`lg7*{xdQo-xTBtJOS;6m7#Bn zB}=3Lm?#+frdUAuB#)tQiu%Uqr;L14be!VZuA}-yCkVq)Y!^SIPc&1oZ~AmUrzvhU zVEy^ue)?a(GQ&+;zQFa{*vMk`u6abyhOtVPw`)-edWOH zAm@#-DEx&&@^|PP!e3H`Kx-93+=8bKVHW%nJuUc4>PJLV1OAdkL35tC^%$2Tc-CyH zAL@yw2!>0#!f@Yx-wEnbN0LP>a{o{RK5qA-LBqjHR|2Q%x#RAnvV}`3faaM0jQ8Jz zto?>u{(|0W!JF!`(mOpN6EY zx8|cE6+bW~Xk41wmrrqNR0yK01dYL-DA_=Dcm5H#ZkqE+4T@X4>9AMK*UkAwRHc^! zb(c5u)(F)}Vfp61xB{O@MIHxp7{m056AaNJg;_#9DMbIcE7NhLl;(WY!x&2XLTs`& zwbB|Xrkit+68k;4f!sV)3>2|HD20;%TKiD1xkv#1Qm2O$dUu0~3Z#gdH~w(U2=s0S z`{rJxP}iiKwOihuUq%V}-Xl3BQy3Pk^mL@SQ8N|Nyjg#t&VObvrOkzY zIOqdsMoYZ|9a?ZB6CJ1{)&^Cv*;(E=4n{p#`klaCFlx~&wz;q{6p0FhhIl6y`9&Y> zub)HsPj_T69x#Lp#;7y_#vy{l5ik~l#6n~jA%WkiZh?{b2h3gJ17ZWl=U+5gu_j#{ zE?x8v^u_0EWFM%h8eK_-!KiaKaNC|2A9`S=tS2BXf_ zSo}uZE*OodC^m2+Ibp0!t?3rs>30pGM@65bae^USFdAX;v$r}kIR;BcegD936t}=g zgaZ+fMT7j}b1oV%) z#|@#cj{J95PB4TA#>}4JdCAY#$1`K*#4L;c!EyeZ&;4g zQfBM#Vbf{jO5Byf;4lnHo%FE!oPZuAYnnh&nnFET{a*hjHr5UQ<(s<=p{|kEPIvin zLwH~$rHSC^GqH{z{U;9(U^IHX{adTG8!SidGt}|EUDxUHx~}qe-*7{v((;~~BV%7& zYWTzneTr~6(NEjxILApH4){~~f6Fx=OSxTODJ)fM^}u?K50aSiJVh>m zIH+4oFKCG8OS(0_gmLVfu?d%DLZ8(#Br4w^U;Uorp>K+4J9LC z^r&l_z0qh@CMQ82Km#29k~UA#qK2dH^EJ zqtQlC8djgqae|uK#K-iM0Kg#X(`!=VTZu{VmWomz^-7KxqI%*LaOd$8pH+p{&orz= zN(hf;ix4#aO2b;e(ojCRxAvjVb5Umuz0$B|)l;sHCfP;453*Gw&7P>iG5GSPytf#RTcG8FS0 z8Sp9f{ABfc1Qrx)UO|xvM~U18{6_S~1&H)P@uqxouM6bB0kVLd49;I`eicBp!9yR+}4#+67Na?(RvWe4owK~ZNctZ}r@gkokj zEvx>x#p-h=Oen@^CtN`6mc~YObAGK0RMpX87tkpXYp3x(?FbO(hE2{23gb}J6T~4_ z##*y7iqb$B=TICcWvn$RqkPhueGiH{DTBr^-J*oVYDL|Zq_u+wMV*wfMyuO09nXqo z^CGEO1gOYfFeR{{Sd%h}mhh^^g?Cx4<8N22)U!SafvywiMZgd%W39;mWqOu7woV}K zIV<;3kSgS2?hq?urB@-C(th|+?0}4uGS>QejPC4vcF;)~G=}MR8H@+);DXVJihiy> z4#q;tNGcX!^sksWvP~-YlD;3>ImKm-+ao{0z80E&TEvBgZzd4qPYH79a!(tjtSI$4 zCrx6mi6#eqkoTIcJ~t)%UQJ8sO5ntR=E0Oz=claWERD5hX>{fC(TV+MoJ|oco~YM2 zvne-;V;>Q_huAa3&LA8Uw#RNqxkFX7Z;@bKNFPvZ3u&fT{WPY-`$>?lc|U_H&8yB# z?!omrYV*?Sb1zbUMfe7i>IW~17+B(n?FOGf(cT@l6%gvg8QQJbSH+|+vV_)l=Vgci~UGEiL)aO3^qj979xsIX^t03}&4v25% z0Ib|0wdTV!mN9%{R0-9eQ?u**kku#dAyH=ooGlXN}PNz?5nrcRbw{ih3hz$D@a3xBYMsMUQMKx1F) zo1*CPNi~gqYX(hMr9JzWv+y;ymoqc_G@K0a17>c^hSttH7iW#5*{*lIY=|T{~2_Vee8QeLb zF3%r6iKBLbN6px%-HI2k^uwV%`=}Wk<(oU>FwG^jgzIvT?e@_bWQzRUFKo@?E&J8`piu+qz+M z=nm&EI8wveOS`Cf^qccT(%;(v);I*oi4#07LWqi{Q?_Q%mg8urcwBI>g7Xa=ykILB zeuS-aK1%qf<|x51r%m+xKmY#IUw<#}OFVA*T=*{{^UF(WT%f~ut=YRb2@2dH77dx9; zY&L0kwJ4{E*_A=d9^ zPFCkv**@qI0VbpDC8pnvDV`oFV4v)`fuZ>2K^@_8vLoEm#{mPZ*Ll5L21t32`DTZ0 zLN&)DQixJi8Fb5(bsptuh(~@0f=l4;QE9z8gA&jcZcU0At2sWd&!u8Hq9fsoWJ&M( zd_1q~5@!&qj$|xp=}-=sj@JaUnom+Dh7~xEk3J{FDICg2N4&Yw7MT#NvPUL`h%DmM zLYmN4b3opbNxs-2l1KcusqY-xYRX7gf;O~?Zp13G>5Dd>$6!Uan$l22MEd65;L{Qs zz1KbQ<&z`6AoY&xJ@qKk>4c}P@5cfC%hlA&0J%$^#g#ak?Gq`o5d-V3xSu0iCg?yb z>e=HX(gsWkS|e2z^g$zuO-ug?k_-;wzYHp(1CG+FODd47Z@*o;)l6Kn49ex~{K0C! zeET_A15XzD6q*xMBK@_}No@NNIm4$_r47Ev0e{ULk~Dp36`xJgTBX|HJLs=stJ$ZW z6ysD&y{-hq{;E??9^(v*N6o5V1}kI%g!NY zhjb6E0>vI+WY=&v2){Kk(YnDqWu;TT#oeLI^WfsRM-9adVip@zxdYLrQTL&fJRXNg z9uI2pXQILuV%sAAaLf=jQ=-6sUyxjAdWz=_ZOGPao313SAzD1PmLR2xqIQTC@i>CV zlC^bK#KV7B!`iv38_SE9-~-PKc7PcG{I>80kHlwTuC?0@^ujQXVR~i8BXMj7C28G2 z>>j>01M(3y(~#fH-)s4xHz1g(AV`Rni%(`SO|6Gs!LNH9qIo>5S%{L*r}4DzbY^(Z zlmk2ola4jR za5~2{(dl7N+tds zJs0vgUK=&II1 z5r$!+Gh!Y-_oD}snhsKeibCpq#2=a!LTiDT00yV;TL5o4srNrTI_>^BUTJCC@`ctA z9!xS7fH{XxnmQqy6m}F%UxA4mGOc2U_#Vt&@By)Q@wqgu51bh)bsEaU=f`9)5?h1K zZp;UqqBV15S{HamVuy*64j7*`g`)-|KhLBen!~h)cn1O_sPnTLcBr5Bg6Y_zrVH`wZ)ug~W0@_D(UBQbXM%+K|Dt2Vo7cQh2PwDylB zYzoCF@$CNHeC_ggmJ$v z_h;!mYc?K92?96I$mcL+GQ#z$xwr|BwMaQDVj^f+Rf^?F}!mSxoa5E{)?QA*{{$oLD91eeehOZX0(wuFD5ht6gjl60ZSWiKKbv2I8#`D&&&s7dC`BXp)p+M8H8~6j4z8I_U$vA zKtcSr=|9;x#GN45=}Isj0HO|L0PtUH=s~BZXcSrHh3`+FqR|>4I?>?t{Q?QZv6$L< z-Tds_8=2~)1BUQWLOPQ&`G-_g%QxA$DBO3?oQ=t|L+qdj9Ha#93a2$vwZA?nh85mK zl>R?SMx3T7`!|9HmV4Z6UYfs*=- zr3(x#D2WHfCTFR89E+Ka!jieqMd@Nui(DvK1s2_1#ukh3AfSsyBLIalOuxujJYWbH ziwP6l5F;lhM>1AkFTI6DBDXMi4VSU=ue&XFjSJL#p22`)<-_HfJ1@C7 zeu0qkVod|;N-~Z`oe=c&2^Kvp(t8`i5PVDiIZm*768V9B(h5aAEb4ER&=_4T9x#N5 zMbeoF)NwRhrsLIPW?#WQja8nDmQUq(-oE?(pMLxK_rF}W>vO|j{`kvpzy9U-3!0vB zr$jD)Y1cDuMx_voLOAzxP+buh)|u3ciK=w3r%#ATK?fd)5`u1cXWsb3k7D1}=Sexu z&RvApF5dKwlUl%crTJj$g7EMhr^7sJI?P)_N%kRj?O5a+qQyLk)5DZ)LBEZB;tZ_x zI+G6Mvf1aPSed7$rxYzI-S1DIYSN^hnp{!7X-&I!2dVkxnO|Js2Jka|N<9Y*AkV%k z3+VACj@FHML@pY13ervv7@3U(t)!D6t`&E^Z2Ev+fC{F;(`P`mK5%9Qnz;{rwnG&t zF=7Vw-2q|go+MRB5XP2s(*=6fN6n2WzyTQN3%Cbt@_zSxV~e_xu6%Yc9&=MY7stKS zC9eh!*h1p*WsH1k%4F(L!BPcs1dq|lLjori%NoeVqgH{uWuGQ}j?u30=<2Py)oS_J z%2_nd#OfO+QuYe7(@AAdpH$M69mLOJAcSX&!a8zqOaNIC%=kP#d&0cJr%QceWrGtZ zI7&(#*vH3}OXYzU{2wYw<~jaQ$dleNXFA>ob8HB6t+|^I8Q(Qwq3g+=So=!1I&65Hk!%`Y@9nOS_U(g_BT0rq79ov-m6VvIbAlJ=H% za=}9`9YT;!XnOj54$}uB)|f;JYv}3oGg=>rz-Hgvhd#_!KP2k(&8MQ59%mVpqyxfN zj?FFgEH3yAh+e@%-T6$^NZsMXxZ$TY#F>>RF+oIL_lZgwj0X%M!B|;B-~055bom_O z02qme6v3G49%iKYZ8Kv22dH-Dj|tTB+;;O$B9rQykg(1g=l4Z@{+00TpJSwylym;9S0QRU81M|jz_EUGvjFP zSLKQiW7rWRCA0&I=W(>rydFn8l&rLBteHSvXJ~hEGVcoD(Br7j`DkokKlV_>z&XCy z&-IBLjp2##athRzr_R}U`oyA)xSCT`X15D*)rFwgkB8IA0^6rG^eUN}?odRH-NDS| zI}P%<-xW=V|1(|s3;i7}E2nbD?=!c~bBV$bT@W6y1qWec41L+#A$%PNVP5b}mD`k^^YZHTRYGp2lVw;|NhlEyH_Il&MTfyEL@Y25#xKItqk zCk~{+%r0A!iiHI6+qPB|BF|g)*|B+w2a6xLX1Y8Huz+roRugwz)myD53bXcE$b+O& zfx^va>ePW|tC>1wt_U=oT)mCInPYyF7s>QP(;T0;u0Zn%d?e)wG#lVAX?sMY*=#h3*n+m`r}ceYvb~@JS90p6Q7TiV+)eXwIUBZSmWtt}>46H$%D-H~|%< z^=ngf(}@n7&v|JyQxjaea``D|y4)_{c3+mudU;%)mo4-qqRv3&gCaz0&e?q0%n7<{ zV<1zWUWIf>OeqG|@2w2#EmtArj3G+sOqp$*NwQI&{V9%(*6TPU5qnSF%mGc3gEqo4| zzO;(p#ok(_T1_6YO1SAgb_emi5Wa6KBP}TF$8Ne(4EwC!c)mprhAC2`otlr*m21>z z<44W$i_cb7Z+GZcC7#df1eC4wBGBCjpJ5=rZYdhQcgDzX$-rrS?ZzL>cxpjtu7}13W{iL;ylTD%yWL)*H zn(M+uLo{Hbyd$vr@0qknd8gX(jj(JwuVM3vKp80qY~dm$>P?38u*v+XNJ(h~NYVHB zd}cvor1~M2qeu}ONInTETc;))bwwCP3NbN+as1ZM9#ZNDS&FDYigLsM<`YO-BgDDD(Ab`@Dpi66IP zMk>8(R!dieair*B%xAUqkW#Z-im2!Z6f2Z*cFR_?Tgo^0Szy`pw?}A<&oqPw6P?|% z4Y6Cc1Dj8U-I7!+qUJ7k%SKEM7S`YcV&O5)ZrSQ5NRIn}9x8{}En7{h=t?pSMxEWV z9b&g^HM^yV3PXr>kpD(Uld~f8Bvk#l46uB2HyDi&oOQquE*SONYKYx(1dN5< zvXP1f7(4fTDJQt2_W6fzKmY05&%b>8{eqVdKfun_t`1qRcnjdwdv*s|7w0MIZTQ6-Hy4C&Iss$z$u~_rrsrhNQH^$oI8cfm z8f?>HcP2Cuz}f-z!k7WzpQ%KI>}f0a7%?sCX{5udN?TVoxu4*)ObbmcJj8a{4j`qlT{fMMwZ&$9Gk&y2isw0}7zw+J zEGczZ2Wdf0M5aLz#$lobk@e%q+cNmq@y0eLDsFaU3-a{CK?}rq%f2~MKFIsE()1LV zgfUEU3QXckZcvie5H?J*(nJOBGxYMl)e)Gaqe=u1Eu}%Q(O8C`#=>h*HU7R8w$mDz zq>p#3gI~RKX4YEAJPS!W;0}ZT>@FJpY=VESITB#Pmdfde=FF==g`ZYk=Osh!hDmY_ zZV3O-A;v36vfmIcOtgKJG@7ZR!!W@BX-$z;%z(On3{!CMg%e{LJ2Ffkw>6Qi6XX6M zQiov@bs%BlQ*e4PsaZM2r2!MRl7VXTT)okTiY87Xp4w_iT-HVk~2eM~F8R#A@nH*H6Y_qQ@|w)6#{B=171E+Z@8o z!w&~7aAw&{+#cbQ{-Z%ubV2C2h}adcT$n_qNtxV|=LDE!>I^2P8id2V*eF@qaJ74E%DjRfR+adE(@g;pV zqo_`dCB<0k0Q4E9GFJ&ME7C->idDI_>VX%nQVPlRgSF(41h;HAFE*i zl@J5%xoakht@zbzZS+#OuN^`Oew|`Etft)@kN@nJAf^kvs*#sOKm5oxJ@Iw7uxMT2 zmDFCJ^e5@KfL>HYvWJ=NvkO!Vm%b!E262&ny6`0FLUGFgO;e^`p;M7PUb2SU{IU9A zanPqFw6=JaID4yIx@YfFasGf3NlG#~l_A)IFb92^@ikmYk zhE+Lm(n0#Tt%;PTDoRLS1jm!>!$ORmK+nMCKY>%g71Qn@zFUzRe<>otbeSs*tGQK6 zE?f^dNM@x#3n|)hEw~}333bHw8B~RjXMQg}#Pc^Nl7m!?s52cTmf>Qfp!4j-aghcb z#AkM$;UICn5VlLy$0_iiT^DT~Bu2q=97N>85vmNNjkVLD2rXQjTv*_WR=}hK8C-pS zNN>$i@kU<03^In;0PtxBjRlBiG&p@9qU%RyAFznwleKc(JYWbN7SRy7uab%6Q|rz^ zNy_g~ioX8uuyEzx>dWLK#bQ&>vc1*msqd}cQO;o_E3Y`_+XwbjQ={whz(a&liBYYY z;+ql@JRLq?4kuO89yH-b?6gjRE8<&3W@2s$S7$v=_@SGPM!v}z8fkE|^!UUT2R4n8 zmY`7V8>Q)mzu+PqvEw_;<-vE(3$$2DGcb*Cc_6|`fwsPK2Vd1UIfh9#>qkYhc@JW; zrw6-k*w&-#-*Hz2R<2k+V$le!GnI|JDRW3=2#MhcrM!Omy5k8m*2~)w)(vFZzX_9B zH(BSRe)4%JP2Q?F5J#61)6^l_;Y#y#0G8=-6@zM!$igP zwaW#Ic0SVhRz}5lI%)*?M2rv8C)@ykf-PF4v}K(78W2QvdpvUWZ~!Tp`GKMIm4;7x zXp!>HdepD*uwlZT#jW*=b>7K7!$={9h;W!Aw3GFNC>#vn5ho@b+>_#lLxp3K~Kr%<##F0AME67AfzPiCv^5@8IIN$uuM7fGu34 zq?I9;$z2?sfRx-013%X@?J#F&{irxQFWc`6XIM2dO^gtBAi}G?zc0ctQdGhY26Ky$ z9#Sg8&P&vI@h%u;eDbavG)Sp9JFm~~GeVl7+^`zq>>RR%hZNGA2wNkZokQ!C%-O-j z^h`U<*})gi3ul~rm_BZX)f~qv&Q9M?#*w0ivFQ+JXR0_mFHwOM$?IFu`RC=7*QxV= z{p+v)=YNCZjl6!rztX~nB+uc8?i#QId21L>yGrtyCT#H@li4Zd3C2y;68@BqdD4`X z7~qx(CwQ3?1Ck)ZP?b-2}zUx&`WK&siMQYyn1J#=AESUanpg4b*Ae5 zn!X65F@SU+9A@(0U+F?eTP)&C1>L6T!Qgu}lTRjS?QmW!23XY6a`MSoCz!(pixHFE zn&LQEl7j;*G=1q2{);76tfu~S3V0K-dcMQy17BKAK8vDpfvSUlEuY*AMfLF!eAZVt zt@I}1>#)gLk_;mnrv#P-{P9;ZG^9Vd)yM z6{eTgfALRgcLUKU|G&wqLjC?LB=u?Gl*t>o?x}WqzFM7zA7ELnPx3V&{myg}=lpTd zSImw_q*$B)lZNN;=Uf`HRq7NwaZL#uq!U48FwXh=Iehndir+TH$jO%IDuWU@k+wN_ zBo)X^=d(>(&72nl^`}upd+5}mJ*3ohKw)i2WE#_}lQ^1fL~41QYGT!(o9B>79vDJm z?vrNOahxwR)qI&jF|3wH#Ahx?Sex3K$kwTrfn5iU`HlIFI}`h75N1Bm zkvzF-+{J!D`hL~4j>~kT?gF9p*)`X3$IZ+kwEm|+%(ddKf?MlUXB%Cw=~#y!giSu% zC|id(7=sStI&Q@%nIex$#^G_&%|wilLAiX@plIMvQowj2&UaybKs(%b8};ZxH;!h@ zbb{}ubL%vHX&|4H()eyoL^BPkR{lrI+`{ach(;wAzvIlF zsb=P+)s(PIZebivI^$^aX*I2N&MR}Vl4qKTuc~w%w7kuaE$Ezw$!GjnCm%J_r}&V3Ok$nJnLbm^^eNx84jlZL zj&bz`=@8RriUhHs`+95DlBA7a4nlOM&*YO%8idrOlY<(#+68{w|82eE{_l0g6Yc9-KhBQr#B$t_uld1h zxBR#KSSS_}<-bwpj^U#GH$sD`*8Fpt=BR=m8)A4uv=B=MpW!j|O;OGH1guXytiaYJ z+$t?Zumgp^#0&?rl&Gu5=|EFW2YSob6grToAckl^6LFfDJ$J;%cKcmt{Y?Jz9!-3# z2`@)g)3T$05aOa>>4N(r02362)Jcr06j_|E-bapQQMJRwNNb4$3T>k*)2JTPd|F;! z>D7$9L1pFx4RX;CMe-3T*UI=7MNQ!u6vH~u;8*WMJhyE&Zy*ul>A1VMoAx?&XFBq( z5GEAMOeSjV9+aJZKJ}pm)_J8lWQ-+*$S09r)vZ4c*rG)WGlpKC`+UDMkWzM`b#~b_ zPT`qq3eQ2Lpj2gu9pdgx)tfl4o`fBVp*4<9-I;vCO0SErCQlS?%_Wn&`*Y!nCfFU>*NzwdPu2>EHzS4(TZZ*r#`escxNTqLrP{uaIf^& zkE!~q-mrQ%YI#T@y@@>M^Zic9DVaS5r0AK}e@Ue=Qcca6ho2!RzA$4*7bvwyXHi2ND(=R>{lo28Q*7He$7-!mS9STr`>e3i z9OZGhPZ`Q#>wq;}s2Gmwm*-ki8PiT)sFd5br#O9~m7D*nOT8g+t6p_M#7)QED7`Q1 zg|7iGs~ zC!sL8IJOItiiHH@kH*}pM`K=g-)AI@iW^pOn=F_R+-pYaid*$)Nne&>xDd}o>d0Lb z^x#4dp)h14b@8lqi$64-p}|Y_dQ5-%J-q0z$J~Zqk2zup4=f>cx+7u&8}Nw?lNJTSGU>q5>=;tHvJ= zf~8)P=5Ow^1KdI_r*1=hquT*PxL{Ft*l09UMJIqI6V9NObo$c9@~LwTZySWi1{=r9g|Q0ww?Sfl88+8dxvB1 z_-La8%KC6ojLoGP-ey*_WRr(2kf^yCsf!j_3vtv8~v zy``O8FO%;Fe3Obr1>*CCEH7!OLm5)cOSEe~Z}A_R6K;JXW~R4hZJp+=H=Po6>(E}( z8Y=SO_U$XRxNXiVNEfmq^XUw|>YN_UU|Li9CvM|xs9VE^%GMr9XflAp5_)!tW0;On z(D}~pRvSTEbTUh*hu=4!|CkDnL+C8kIjLBp(~aQ`T!=fw48PAvXh31P#DCx^TPISd zJ^?3cF?RFGE?t~x(FHi+#}qf8q|ut-9q;UAvCQ4zmIz~XadN;CE>85tLLAMO>39~) zyf{RURHZVljI)ex#DqBqCyxu{frD^q!j7)3kCau2Ff}Mh8_T?`qH~RI@j)9?rXE@v z@=wkHxN}#O&!Ts zEz|L6M39crNMH5Dy&zixZ3}uZGW^50pa1mjXIw6NaTq0dvK%{)+^3y=;>I)NsoV8MPx#H}wKTE1 z;k67}Bt9%J_fUW#hGw6)lA%JxsX-vOLHc%mJYn|q0!!FDd2K&u+6tE`7~OQ#RyWZD z1_kRx7_lPpc|s@X1_%f*P3gMP!-&u0%Nt7#g>TGd^0qH|TfkoC5i#qGlZb8&iD*m= zb03!XiqpqUFDcg1HfzSp3+cJ9@o8ppq}v$Wo72w0m+f${bgSOd zMw8|%1>YX{f1{tp6r8ED2m|^$Hxi3X_@(+5ff<3MAy;L~!>`4n)+b7RV!DQ{AZ9+~ z7JJz7Al#g5i??^;O(!9V4P~FTl|x>`&dTcE8}eSsI(GxGSTs%@x;50Ho?wn{kgCc{ z!1PhmHYex=?Is&uid2)d&KbJ-oVOf&h>P`B?UIvpCbz9gr()-!Lv#DA;Nz74AJ^xC zm-zClnz(848riIYQ9eE9I`)tQi#bJ3kZI{A;xzoI>}iNNqIW9((ER3SMw|yCPG&hF z#4=R*T$(20)Wn$^ak=)BCq$LyQ*AQ5hyp^c+?Aot?cyqnSH#ijHn;d5ifJ=zXQh57 zrih4!6ruU?+A2F%xWJ=)(h9UrJc|ief92&C$1o>*g8pSUU1b2hwu;uaWlCEa^}|?$ z+R5Q9bDCC~zN|QX8j0xw)nc(ogo9h*0^xV&{95qEnqkoKyDp$F`Gy!wx0*9kCTBTg z2ZFEaT4ylb4?S;luOFW&qGB8M!V53@<)9_1!mE7Lnt#U826elRc7U$>9ubtdcUU?&Kw1pU^uzf-{!u}47C ztTt1a#zQp3M!Q^^i`Vg=)x+bvPg$trDmI4^fIBr1 zQb`9QvfRhHNB5e0)RoH#boDBVK5BZ=2`Z71?WGf)YeWXHWuh|UUi0M!)sg%+IT^M( zlTLYTrEl93D66PJ322wI0adkyGro#ip7+|=cp;u)xJS~2 zsoo|Y)O3-zbS%$61a;^hKW@sAoxgsms4K#7Jcv3_;(>bvVidS{;-0Xm87V_VgbW(#(2~j5_HI zeyhLb40Y>&kq8T3zT&<6=faH-)UN@RZ;sRt5{BzNpYpQo?30Cx54=VSzJl|cv`>)>YorS2b;&e?6x9$}87nta~-6=N+ zkRAaexR?&COq%#p_4$*h+HGQXu3PXrB={SAoUekBV zH?8S+4WT>!5Pj!}Av`dW(u5_3=sQPpez_?(saQzh-+*vrQ^h?!c9tH~b=&T*1Dtu; zUi&P|^1MD@OOJew0xFt_MCw}h+nV-)(K z8FM~yu>%K!m}%z>aA!4+Pe5v7A>E&RN{$pf+q=(D%88JeGlL>HK@cZ?uSLI3Qo8$( z?fm)opT7P4(_eokH>hba#SQ%EL^U@;4uFqv3rB*Ha8WXmaUfHn8ldH#nyk@e$gSto zFlZb2en4w&#A?;1-Z)Q#nU0>Thx&=Hw-g3nEaPB?}q_^s0THR!WPOkS+Ehn_5#lus>cq#$N^_Qw^{HVeSjU43 zX;RdgKB4afsE~Ju3Q(tx-6KiFG!lc#`+Ja-@kQhN-REXyW7J%%;(v!jy8cMc-KPw7 zp`tCCu>h7q!d>~csEG0PBvfSD4v$%GB0IqlE-F%4!(|G4Nv2Ppfr_*=04nqp=64Ub zsGwJf-m+5Kd*nF3&-LhOS3WDl!;wLZ^# zithgXUwdaE_IQ0dPlN56(^E8Kg|=SC%S@j2o>?RKli}H+I=8WvR!;|9%)@(Tw=lf> z1j-ZeP7n=QFgMmS34B&fi}#vVGibSlcd=O?Kh-yemRc-V8OFQLp}8O8(A;YdP0mf}7=gvF5e zE4vg!XclsH(;+>ixL%VU-ZZ)B6n~wmWj+M+LchDeheslJ2))C~d>A*QS!T69FKV(w zFVNhrVMGBGe>fIZY9de9h`p$iA6i-Og${AopQO1DJr8t0;9_1>Aw`O)(qY=9F0}(Y zubB-4{e*pI2|U05wuL>t+QEF<&j$p1#8iZT?)XD{fAzyBU2kclJcIo0_Q)63-l?Nm zn8W9*FlD?`){q+~Wr#YeLy1Vi?|>$_XV$97bO~-9cSrt&d{>@;x#!kuCPDOiGo<`2 z4l$SOGF69|c&_-0T81v6nD6(Bg_h25LXDH}MZ=%cb^(^{rQ0OmL4wl0{J#`}!oSTo zCo|3X{H{+tm_&&R;Sf`hGEt5oq#&*#1S9q$=t5c;70`k~;Z=DzXH|o0vZaW(U89E$oprLi z%zp`}S@6&^HIyZaP&i)~VbAAb1%=}yc$BF0@|Umqo?uQ9&1LI7^H4;O(d_e*>Xp%4 zlaPv3m?K|p&5`P12CpSXZ`$;!K{);YOUa9XS)-WKaaBz-aE>CLM@s3%93x+gC=hKi7}6RNf{o0Hq%e zYCax44o=&z!dWW>TqC72ZjX_I?}$CxGKU#N-A|;^OpT>7XRhDPDhBY~G_7}MVwfPA z&%{dNHRtU2YBgx7U#l4ugJPjXi?~5`_Ng5iEyT7NbQ=dP%}G4zK%&K`cyw0abItlF z+Cp~-Un0{FO&@42Q1u*_Z~Bg4Szw)Y_Qo9h-%N2%FhmOzW{H??rIU_4oq7Z&}A3{n@+z}~ynjNQ5%=AjfDMmtz9n-`R zPhut>&!7my358fQB8(j(vCNTSlW4^?Quyf*{m_iy4NT~Dl)>qDVUk>f8)B#qK41tJ zCh8FnncY;;33V5_c7Tcg0L{FMurk+#mD&gRX&(L1oM7t%XNJl+Em=wpJvcMhgq1Qo ztGpQoqfS_v4-r`@4M0uP2+yzwkEc_Veg~|^xU$nG|pm~YZgnH zs}+`fT%wJX$uY!YnQJ~qR|F?A-mc57&{$@j#xnaI=+-V#{lH8S5gs8flSVr$x+!t; zfW9KM%`tuW9GWI^)fAdC;y$~qH0g*PA<+n9=7@#bkjYG>Qf6p06ID56)(5wP3d;LN zg)%eil$kkl2TTb}Cy97u^(KOtsNVf|ds;U*Zxa`8px3ZIgC}o_5VL~K&T>cxrR|=G zPV1R{xQdyS%)G@J5aLL4=HptZo6;ZY}l%(0=s6z2p(crYQQ ziJHDcSfbvh`9O-yq$EU=sA3_3PlITTL@m|?lbKi*`|rVJz7oIPHBQ~L-j%>XN%Nk} ztkWXqL$rvw<}DOaaZ?lr$q*|7I!B;0J}Ij8Lre&36l`^i+e7KMrqx>Ksd^NOKK5qC zLWYA8@P|^~|#j%=yK-g8D&?<-)j-A!zz?hs7sDG=`&|>r+sV z&L~`D$&eqv-#1WIsCAX$AiphMbb`cuh#)c7EQ2EI4?pr|a{8S)@zw@$fpv}zXm*NA zgfUEU3Q+djp#=&vgnD7p9Q*xjnT`X6oI6yJI-y|pn~z%~Rm;X=_k9r#9RwUDZY`Cq zL#&ZOKN&}fzTEV=7QHI6=3EqQAwNzRG<)#L2d6XLpfy6Z_$r^>XM}am4|3qRol6vk zF(o>|94=V&C6~{)(w1C<3d&ifJl`_sj(MgPpAFL3sU}SfieWX0pofLaF%-&bjzw35 zVX$Pf1sBQ1Zx1Z_(TQ+ba}gC_5j#?`Vchh~v=&kdd zSxe^ralk#rIs8dKh49};$*4czS03ugUSjExS0_rm>2U^klp8QVx#&E==7|c)p zMvG_&pM7FVvCjSihH+(FgmPy1)6J<9)^nUIJ~>bi7^Yy#6cjL5Xq6qgzhyiQ%lnLE}u#cicaq&=7- zxA4+xUp%qJm#EA5TQH`ae0f1XH05;qWQs;d0zuFhQFi|Ufw?1Rv^nbw`8fgC2!40;g;Mk=wRWZ`1``ttG(3plCBhPmf7%J zd|KR-8f2vPU+!qw-r)VBmU}Pq)95%X#&IrM2+-Jo(_Eajqi=w2AmkbfE?_#~N#d|> zATEMiv8LQ}#7I2%G*-%ibJ2-0G_I+jl!cc1g`=(uQ4T16k^^xvH_t+gPeNfPiimxy zRv-5J>miR?;^qc{RUNh*Ht<~+tAkADlVx$k4B6U%z?TO*7#gB_<>Op6&=Uxpk zYo^4Y=B+*cb;bDVRCyK*3ni;lFNKJ*3k+du%@Naj~SZr^ZsM$Ljd+`-_lGQ9WJi>i_ zCr*T7;+tNO47TexdWrcomrl%+8lRm|NdXuQ&L{S?zu_w5%|{O}`PEyi8W zt^HpEG&DJL1hMw>oBBU-`wz`R>ykVE>+96;U*oa?4`hoaq)&_J#vW~J0$qH!3y$P) zdWd5~u6%dbLAi3OSaT%yG>%ORlyZw4PM@KOR0`r0lL_XNaGIK-X519<&}f1~oe4)q z?88H(7`(lX|NZvu=U=}4{>#rOaq8R3${25};CrOezyAKG_}R*jr}W_4x{lj7uhw z_{t>bp-TYykZ$})p5hvGPzK7UjZa$!rN(pV)bFBgzhtB^*}9B3`MkC8ehVquAsK>K zb2sss6AilSF88<8A$Q88yT*^(r9~=t=!p_q=yXMJtIv8{JomdZclLY#+d7;`AcKO{ zTjDrB68)h|q-vPTIBR5tlYCF7Xop&SVozS09k2&4KO2LTa|$A@h*BtyYbq!gqS)gv zCrVv!sg1HO#Rld9Rju*vH1#bgpH)hL^SKmV*4-h3QnKEc9Gp<dxJ(32o zE6Jhd=A3K{V#5p$=%T$YNpkll6Upzf@1dR87fe+2MCM1x`C#tGe6wP%2`h*3Vsz3E z@m7`t=HS?+2`<8v$t*3wZ_H)@u z{PK;vIDxc-_$LVAtvC+*12ua7(R}1KvsYIy2HZlKZFwL=Viv4n*MKUC(Xb2c5$}Gfm zl5-b(t3IFX6a5bd3J3_Pguos>{*n-ao?QGT1r6+s<1YzCxa|k~u&wph*v)TRuP*4; z`rU#OLU1J@-_fBTn%}Igx7O^M@=ZTkn`NoTyG zT74QrCyTGt&M({(v6LIvtzV3tJM;6rJX-w|>bJj%)mY>C@rnzS-=4PlnV#4Q~YnlhzS2LF*KxOy8BHhU`3W#6qnHj_-%H;QxJ+eOUpBcqevkzC1`?V{Z2>`BXmSS=NEAq`KxJm1TQ_R3W3c&ywiDvwpp!h#GsPxrsxP zG-qxF8SC>RCHh_ZJ7Sz)Jn`l%L`(DPGbZ-VLI!-dqHIHDO&KT#_EC{TW%Mix zi+P=~W~#ut7IFj4T5(LbmOcS1GE`PzQP)GOPtiLeR8kO%2}BRpKH;DVl{Md>m<~;g z@p)IA0$_igySJO2nrK;*3d+ZQzp$faoRsu{PWw7-2CwvX#M#j@ceZm4xnZn_(3*!( zlm?6NjO?;@ZS$aH~SB%pi@PIH%pV4xzbJVfY4ZLDq5y!AqokAs%SO(p5xUkgO4|r+A ztN42Gr1otlDw>UXvnw9&@P@I1jP-eu68&!ey|WMj-=`cOzt7`3869* zgpgFyz6F!OZ&GhUCB6C}u!-{%9O6)4I{&!4;%Ev>YNKZ#_x-{yEORS57iVZ`L2dqn zk_<;n{mBgQIJ_{{Lnt+uGf}}25W8glHSboOu|6+?vH!%t-dU^{Y`!xS#s>w=g!k@5 zM8Nc0-uF&K1k9*da~5sDtQiq+2F&POF-(?QjzAI$RY_QB=C>K}Cw+U)XIJEac>pe6 zz$D)j1&_~QIw4?YIuFuF+P&Z&=q9@+VCE+Znhm``W&!s>CA8o`)(Zzsz@$ee820xD zOzJ8V+KywGewsa?evhN;p~C@_lqW>;*#&A<-76>OZ<@Auw2 z4;*Vv*aT&vFiK_GzD;9q@jTisl1#2yQN-rt72y-pSmt-&|4mz`_(R&p#h=P)UAfec zRD^%aXX7qhPY8Bix+4P%nPO-)HGD$4%TEH)ca-JpnyjzsSC ze#^HNR*kQVBp2IG&z^%waen*pn{yBe&cAbgryWFk8)^VY(_KTHUky8m}ct<>iq7HXx< zeJyS7Yi@JL|66jM{%F7&B{a=)LTvN$8hNM)ZXF_2sB|##y7#06F+cKW-Db8EN&$V} zD}LM7%HO2DcikTOI@HuR7f*zXOVBY+_;2}IDy&k|+8wL-QvPJ34@>2KpLZ{kQR6&w zbd;bXrNoYIQ?phWus$zx%ArpE)fy(#n7PVA!P%P&Iji+$5sdwA<-KOuF6glkj6<5Q zIRU4Wb`Wp02ir8WX9(uL`gP4c{`gbz<@~My{+qsC@Am+UrtYKiiWdM@zp*)At^oOC zfMLM!qm;FC4S;h8bT@=@@TMl^K@r-*biUqig<%lhM0@oO<_70hq(|&6HeJfV_oMCu zF17+8PPy!#ert|UFAg3%QiP>^tP53^6pdkubAk;PsQsZEA?Zyd&}ea6rj$UblYP)H z+C_J6b%$2;g<<@*ErF6ii`hrOQ9{ZD_Z*_fd|VOjzWe?UfB4sb4_8Jmh%iKsL6^XF zevxC4WoZ|I`?Y%(m;5@$`bXWeUuFeo=4Jh3=qUjAn}TgggPV%~#E_15EqFtU*695R zci__uv7i<#zlYz(-lF)t5Q>f&Zr70z`k=e}2ca?=LP-Z=yW!#Y6IvmZddr!J%F7Fn z_(Rh}8qHJZI{W4bKIM%(0D>Mr;~1tmDSjR?fEPbWX*_H!|2oE=k56%4xrBkX{ZFy>3&80N(94Yxo#0yfnS)6CFbO}-8Uh$Zo4n4 zg){TroyRyy;9-4Jgyq=XY+8RrPV}O%FcSwizxjz_8EZi(Fok6_W#KPrO^B8c^qt`E zX=R9>DJHbvKVNFxM}5V8v6c@6tIR^j4ijSN68i_Wl@CU$;Q*Z*)0s$drEMD13ea?2 zKNInXAJLh_KOX|6`S~V5Q@=d>=044@kGz4v#c`WZa)3Ty053r2Hx8IgK0hU$d^~ER z$e>^50G;0sfL7w+nha)c^OAN^?4Sh7nt8OsZ36Us$K|l)qFBV|SB;y{BIe}39MA;5 zy3@Z*49+Y9^f)!)VSQ5sXuqv_NKU>z{qP&QN4|-E8s??`V+2Frl)=GWhkRlpLEj|6 zka7q5VDOimIG*7zY1xF%9Q-9#?3W7~i}07!DT9t8rXRmozX^2I>V8U~Lq8Lj!wtAO z!cpJ-pa{b$M(^Bz_?_g*b@++Tqi!m%P>H=?KI34=u@qCcyq0h7O)+{`_#=*CigSVi zycCn0V7ZF?w3pTZM9Ua-ie?YxQP>+aeYpbKlme!MFm*92Krp zcd^|BM4~h>#p2!rzG41|z0;=ejC>Q_6--W_r7-kOTnHiJ-4H7QQ%2ApXGG*U zL|lSF5rz{%KC0ucI06}w27n=FKVpBaDb5K7 z@DhREg#YkaIC-r>RFk)~lh+!Vkpje|ii-PyU!VN#H-m2LbVc_8Z3f*x=IbNQAf|%M zykT?lo7|faTlcuv&MazUza{+>V_29qdN!D%(`W(0% z?vZr{SiY%0w5Au0TS0a>!evSU7s*{oCr=O&u5K$YBF4ve_~!$r%#hM*KCW&~(L8)X zKS%O8Bqtcai*UIKgsaGpFR9cezQ|kJ$q}y5N*+3y9vB(N_)WgoSq_CbJKAWS}d^{0g-?XOP zE9P=*J)fI)s=kx2+#0-s3mw=d=%^A*aYNH93FwLC^TGh(3M~>xitPcJpZnqmqifz&In*dQMI$#`5DvE&_=2f#v@r+7bI*_*16r zJaShm{#)8(=Eir^X%pP)uIoV|t+XP}9)|j*A4DW!K5eOKxPaE%r5`?z!pbVtRnA02 zR^j;YIUtSRsb!sg(wcQIspMXF<_13TB`1{w`uCDb?!;#@`7EfsLr@c-2EDRCCvCH# zU0rRrd-#MYt$rSq;H=X~T)TlTyOdSHO1R#m4rBeSv#uUKwWf)n#D-vwhno(BFT2Mc zFQn8jTt1ACqj3k_L(CI=Dvb!y)~7`i#1MG~^G8Uvz9|x)&zKpOHNNu2)!@eJ5!0b5 zg?-cKCk%@#0(#WfC#F9IK^fMCy7MU1okyYWJPLK^k*PZ{Zd!7Iu%rh9pFYq;YwEFQ zpxGC#(T>5bn;0~w)FjN zmFs*hY4QVUvRZv*dJKn}8*D{jtO+S)a_COyM?+7a1J?v`>zh13J|oYg7i?dPE$h2H zKR$<8CzKzpZ}R;3jBWhKWmPCF&++^JAC|a0KR$gj!LS?f{P>Jr&ZZiM+<<3?r+*Xw zu&go$@7*3PKp2U_4a##a&vdAja1&OyF+FwS#M9^8_(m=rYS7%k&*0%RiT(qe z#oSTCq*l$x6Ok{`}vn|zyIL!?UJ16a$U;k6aW8<`;e3R8)@HHN~MT!f?{mDGqoG-Rsb`l4ebDC?dj*#Rld$ z#oIt6agod})Em9l`AZJi!27($pyq&moG#v!*x{jr)F?H*N~WA*oo@&+*%~ zTYlD}Agl?D_q5rX;PwFubxNDJ`B}fGP3n_dbVC#eq>d(S)?|mGWb6)!+~l`RH`^`N zH^nhZ>|{2rF!W6knRk;i%Itj?Gtbx2RE-X0+=`h_%F(=tza%z48g$X0iNEKKc~NV} zUU^d&%_@hQ+Rq6aB76-I2A;&N7!+YRVC#H=XKeg7RbvJ0y2-uRR(Tcl8GmSgzSVqO zqz+EIF12K;gfFD#3Kp;+r> zyPt8YfmaT2H*`*cO%DcsvwN5jLNtb^c5e#TPkMae zyYKygWYyQA9*W;Km72pewVn@3Ao8Sn+L62088-dK;1~Ftma}KwC|tCJmNULP;Wzy@ zd6IhS2dCZ3lX}1I)2An#V1SS(QLL+{9i%gnCw<09-VMyJQG_;yqHy0WbDk^&2AaNH z1o+$-Z8vaG0=7T&F-`SFe(|*9Zk=|7dwk1Hez<`7btL_`eZ10S=4o)Rb{o!`P+K!G z%DiwOEnL~%0&pT?`KWI$bCD2saWbt!q$|HQxB(ck6;1(|SV8&xjts1t&(YP2h-}t5 z2X&JRT={+OR$!wpXQm{q0Jt~A-`pjMGT!OcGjDr#=Slx92-g>*e765nvp}n3lk}-)?ASMbdE}GmNLg6uCw|G+`A`4$`)~jH4W`2ks82kZF%KFxC3F(O)9>=n^Fk^%Kkf%7n$dI- zTXQ~p;jDr0IJ;ub78{UFWu7%Ku80x?yJf~%13xZ&dA9kD?`I9X;gwG|<;`!8KWk*5 z744I)nK!?K{EX-!JHl;zNH)G(*!2I#>A6UlPsG{fur4KD5iMK271CYM(=hHZnW^U#a3QijuFCvL(=0A0C+R)*gPR#mCq8W}iamLCO{w2{YbSH==qR+`w&#db9x z`b(l2&PT96&WHYzXoiyk99Zz7za*Mr^Bdd1Y-qn-{H&0IEoW@g@}b?+kh6xy#U!|b zgAe^B(F{A<*pTK!e@QgM4mmcx`Osey&9LW<-Df`ZmqasM=P+R(n2-G>;cUV-@Pz$X zKKPdev0_a5$Vi6o|d6pT2L2CQ%G79L{gAJ#Xf4IC(JOH+-KrI2;b!Dcsw? zCc0kynJ}`yMxafc)swSqRsCbK5O9h!h32r zzpePJfu9$CE63*d9-lQZLbzy#*!pS-WwX(Qo7mRCD40A&leo8 z@>%a)0KGSKyj+S8{Uy;1XS(7ltIfY{{j89Jog;CT(-vDaKRtcmOc%$iOyPdR^=Y%7 zFXB#{&2KqBZRqs^CODt4Fi=hyIdihLt?l(|qVJiDpl-n zR+N0!UlPr5ri+CcANot88P0TZyvl}-{k@+aQm~4}dXf*lcZd3|^nv9h7Iu8-FR9G} zn_jq?--vydhhuq#We%V9mqatHalonMLw`v$!$JcEKOg!_q8ZL~alFch{*u}ZXSz6E zI`b(l2&UA6S%7^}vXol2`<5fQNmqat1=^{_@p}!=W;Y=6Dt94xJ~KKPeJv~Z>yj#v5M zUlP;8nQk~<<%54oR10Ui;dqq~{v~lOoas*dMBwqUyJ!79Z^xPL#LomU_?N`CaHboM zSGn51B)Ww&UA$c?2LF=y7S41helqa**xkGSp3O1L#LosW_}4@@oas*dbO3{YO@zbo zz{Jl7F!h}f~C)yZzC$!!hSe$5M;GM8~Z(!v_8$<5|)tf`h6KxE=vuJp8XnCTIp?8)H zZw@U_v@!I~rO=y0%M)!3y|Y|+Z)oL28$*9h6q6^~82U@%m~x{1`1t?Xd%IrCawARe ze*cQRo0*HL%Kg2!AqZ$o-LT&dGB! zkFv)fg-Yx=vAD=sWH1d~g@FCxtlTnLb|3FvvY|LSVdhDV#P{vy(B!=p{pUqqU1c(iHyi%2t= zxq7s(?>{wu`gPI_j;bDQn*JivY{R2X(_ciIZFsb4`in@j4UaZWe-UZ6;nAk)FCxu0 zJlZtcR$2_X5T9AvL-dds8zsln>6Y^{qYP+%NB1xc-24t-2z$AY>pmNO-5D^-xv%csT;6frem$W19ll}k za`$bAK(mc*jvQ~yzUs!hftzh~Q&z`y%hG^m8{O{G`>&fp#?#q`uk5@=3OKAu1WVdx}G(lK`H&$wUB|EquST#ft#Gu zSFnMbquS;Bz|B$Z(y!yXFdERLbY^zHj|tIe9%=MDG<#gx@S$%cJMT|t8zTdA!#U+J z&}_rmwZYBaaKdMBvyG8#xYsg!+_`rz_a4^TsV9U+a|RJ$X!I5IIi)So?5jDG?>)in zsAaZ6m`1Nf!Dc)KQX4>O14!cmr0)Py8$kLFAdLr*+5j4D?C?wLq9&l(hG#Ok*@kB_ zxY>qhGPv1>XEM0?9iGVu)Y(RNPxf!~Oy=pVWn2~d4*EP-F%C~kyYr{N>$%F)NvY>5 zxJjumoCG)9=yvW=%cRtE730mMRCAS4@LUBNJq~@It3b2IJy*fa9`{^jBQ?Lna~0ei zRnJv$b5uQ7!Ob?t;oHU=eNWiG!*?m6PmVUAuWw-bQ_o~@v#&gp!Ogz%Oa?dm$}<_< z>?_Y?7O&Y?eU3@1H-}^I0TqByd++>z;x)!!n*f>ho8krMtQi2Zbx1 zKl}67p#@P?55tS?p5&Q-`0$6Hn!45}X4lmg-t6hi=N$5yB`)kU!mAQ&Fz1U`rHB4# z)jvtYuYdT%&;OZAZ@b#K_Yr(9QR#K=xM@p7Nb*=dSvpxFSqfRoSjJd}SXx*@Ec0Hg zlfS`|Ysi$H*X`K@mROdAk{|8^_kc6pdxiD->NT~WYM>u`X7u)}dRlN{UoKmOVFPw8 z;!haKxfX^ej5?>q#R=njysWhdrt!!NSc~i#7%x>V;zbx2_6oE20<+gDu-jYo?=8K# z7)`Icm-p`Gy`}Ho!gp`!y0=K(`|gyzJ8^GaW$*6WTPxXH?Kr5Tb_e$cw`&eE4qo~X zUhfYh66PIKMB{hxQhV_Fdhie(t(_drOh)w78|TV*_3V?0}7JX>Ep z`vcGB@(Y<=%M)jexx7qiyI`pd=Ldy)3CySNiB{)&q!MDZUZh(;%1>Srx?_DJUrBW+^JOTDrnFu}fbURq#Bi+cv%?WatGvsLHbAOz( z@8~YxAKiac>{)j5wrYQtiui7SwmP^!TP*`JeU~{^FY}FymCF6>_B}gA&q;SG6VAA+ zhXM?2W7We1Ek92_zv2G;5iZUOiCztGOM>qj0r~2yVV>MhB;93go`J zPAPNsJlJ2Y`T@;a=2%`Oe0Dr_EjKIl`u&MAzk4zu zkMwOol81+>?jJ^=6@ngdWf3&|$cXsETtb2L9YCV_@ZQ1?NFxX&PDI$bz7{ju#rR=z zF_7Qb&vy47kHg;AumR0?EshU#QLk?YG+UeWae=W~JIwkH9^b>kBM2mJLiU=LgO%;W z!4m9nm~9;9mmj_M9gb7VA1!RajUL(^j-K5>ZsRzG|Iu^xa5Rq}j+4?U<-vIYIZo=2 z=0B#nzxOyPJx!`kbMZMZkZ9Q*ER0D#F-Pz=snX5&@W#B^`f#`L0?1! zpn=3WtY6{?x=%cL99};74@lpExKjVoeQE<}KDF$mOVuyOy~i9#qk7zXOLjyI42}Sr zZ+LzjnLc_7^BVHZJ09E{K&IdznX@|{ELDz&S@Xf;aXc(FTa7v%EggZ}+R+>6(Pj22HU{+z`}zsF z+;#Ta)!cr(&T*TP@Mx(=f_;tK4#<6an`5LaBH@Vp?1Q@{|M6~l59A)Y&oRGy_8jkC zd{0yU0f~Ab^(2tK14y)-Y>wG6dFE4d=jpxs6lm6B^V8|Q$NV()V<5NN&WdzN2u{=k zx!rbj*r*_0aIIl;iy(HJ}gy7KA$(^Ueq=hM}r%b{LndT{*PDjrzAX5MX`VDjE z>1Zwi(mXmHXFpFVbu!29fL!imd4(G4Hcsa3)5)9-_w}Anf zs&QiyQu#DhRv`1~T^i%=G_{QLII+tc=Mls_AW?9h()+ykxSi+L2}nJ}P^yjdTz^qO z)%Pf%>W%Z!>&JQOu|VPjpwaH`eDvPweDWLwayi&(i~?$`DLBuX&zAh>i+l2Xv7QZN zsxDq<&R6$6kiY!uxrHd!90VHuyaSr$%wY&>(>FCsHcx){X3c{=lHa|V6WJyCJI0;S zZ4_=chyeNNbV{A?<{=aSNqHdm-^h{EYFUi<W`MNBe% z2assF>@6dJG#=b;=yC^Bb(vZk(0F%unX4|4dV}T7H6Oe-ve0RE1Bu5kQ~m+Xr~hu$u>*1~7q1YAFuyUky%+pb`tqp(AmAp>Yt&`({pITU zak*N{05W}ZuXef4Z<~9ti}`+cnS6h_TAl*UcIQZ6=NMh*DBQfRvW@pw-Yh{bx5>Lx z2SHHJa+A`j17B`lKY&IL?GWhZwVP)sSMF*a0huG_9OPO_^?_~g<@#A06> z;lpo#_@Nmjzx@%zWc!;W8DpTo{_PKc{_ywT{>v|Z@%8kxfBll=-?T^B^3YS}6R)M7|U+z60eE$=){e}7ttxeV4 zuY333?N`^_QjGuq#D$r>CL(7A`$6>ldqs_2Zv}q+;h(CYbkh5=vm?!iQ?{?%yw&p-d?zx;mp z{@w3!L^PdjjHFZu|BPy6%h58R9|^Vb!{)G8;-A6P;!14|^ds|#e@5ejS5cvRE&9Gu*Y<1ZHSUd!kGlv=d;E1G#(V8OlYcv-}L{{eWcYe<(S6 zLNZeV(|?Wrl_;`pZ%w$ons^M0PFWy}M~_-B9m^xL_}m6mr&(5z8*3&}0`6I=5 z>t|Px<6{eQe5E!C(i|Rsc2XjwLHK73pVS5EMp}}$es)_(JDwUQQ-rjlZch@o-^m66*o86JIY^VRn(>M90wKu89bUjnw7#&+? zm)|jYyi1$ETB%w#^Zvc_SM*B9+iTUzU)8If<9@7XyS+5R`K$c~?upUP{8hW!*YD@A z+SeMmm)^Gg>N~Y6?qxl)_4(=F-aej0wfp4n5RrSWta~l3wq!5AV->Mod->IO%GZFC zrGNJ~yHn#Xtwi}dCV;I!@v9}sc2E88%HQvv{##$AmC5_E@=JZ{UG3JqYkT@vbZhCt z1vEx^tYJ7oG}p8!?VtSBsB77=Ef*(>>g&JMC%sJC_g;&Ey#~v#zE@Ivzx}&Qykv;% z4SNaY7yeS8X2ny#lEnM?6<10O?}q^1?ImnK_WRB540j!5%SA!&y@ms*{ElA6%`@=Q zYwf!}Xs@ilalwq<#}zZmYrk%fq&?ir@94ed@4*f7d!rSLF3t7j?=h+e&EJdii^lTK?8(&hq-Y`S*}$OL_IGp4-m6JRkMjOJ*Ng z`Q4SjaRv=KKJj;FkGFkoIDR^J9<>Eqeh0VXz8TN8@*HK2lV5Ik)Q;+?(LZYYbkwNZ z3T~g>M}Be4sNMJ-v*;+B;?uul79EES-W??~kCKh~t9qr<9^-e+I(ZPAW2X1VH@hp` zV+$BAm(fe&B3r?11G5Ee`5oM0dzgN4s|>Ed(bDTXNfy~Mi(_TrrG1^GAK~~AetgYo z=p)SyGVszvPSP(e#*lVsUR z+^%1&8|vNpt5MW0=PYi{-$9?Pz2~vAV&@p|&tI*qI7|AtoWDpnw{w=3Y~L7elF^Q|T*vb0cUS)A{$zcJxb!ToHGc%^$CmB0Y}m`+`u?HkQlI|zuVm)Li{IzpF6UqAxq80)W_M?noW;dwaq;rETNjls zC$3(~>G|N=-NZ{m+YfKgyaVLUlHAMRW7aut?mV!g^km#{B*$5L!}7PdNfO_| zc8awl3U6@r@~hF1zTv<-$K5&X&haAlAm|rcm)?Jy-&K9%yX~jANN>gU)VcVgoxOd# z_Ux8lMwvwgYnl`SyLj!+!?!f5i{CN)7pbQXXw&bm{7t6v!R#;p)n^RFgZljU80L$X zq~&*Xvx77)nvD2+2K{1kIVX1)TNbr>vwIq!%qTy92m4;cM)*3$a|ut5m9@V)a3lQu zRlC}Z*mXpAH{+$fM)nz9wX!e2gN&{>X-ixa_w)mPfGEIJI5U^ zzk>}NaTvdnnODg~#}q1}5Yy)P9zMSr|Ln%PN+f;eFE-Dyl)usM{|D<6TS*1FO4z^n zS1O4@Hg*o(SboKBHk*I+cg(u0guH%9$u7KR@yg@;iq4K86#&!jQ_2F8TQHU~k8u zl;54>pFjRPzRyRVpZKM|@x6CN0mQE~>-)=ocS||7uYIUvrk?)QcS}RRkKf&0)`z>4 zjF3cUP^3@O&* z9?swH#@iVbE;UQhorh)s{K;~Nz}C(3&4V$Hgx>ng-voe& z=js_uPniTfw36d^BTf2HyQx`Wp8)QQ1@P-KbWp}0sC656ru)>qE6-^2aH_&BK;6B# zLCuaW>G^PdN|uuU-G$AdKF3G!zCL*ci00|~TLI#py`&S7&!%v2Ve@Nh;8uWoY_AQ{ zy3AO;)|mOaYaS7xt!8!I8w2Z_&AWE@(UhB#O#9xN1B zfIe8BC3a9|wS!sB;h{LD^*Ig`U1K!-t1`22|72>GzD6*r8@1%~xep$xe_jDlv)R#p zA}|{4gQeOE(5&#$B?^ljE6{3w;t?s@EhZO0`%q5GxaaL%K3Gi3Geyl>hXMS_3t$Sh z7E{J#*qwXOa!jUpcjj1_saY%Z8gPY{eLglr8Q1(A&^eQu&Ce^qt(%|iG9&!JEMXU_ z&q^>onxg|@Bo;Y(`Sw{Z9*N?kVs*i2df3}U{DL@sg^@n6!ng^q^{Yo3q!mVe;ehx3 zQMxHW|4w!q(mDbBy5h^hi057jXnGv2AslBwhBX8`$Oy@BqchmTI>XV5K?R`M8iE~a zU|f&q$Rz{G|T=5#%(PaGvtDC9}YzTn&%_4zGK*liE$Gz z0!Hi1ljcfd`PC#_0iw@NJjzUr2(@32M`G&=umRb4q-n-UsnMq^z+`xdIC+VXZxB*F zPfng5Cx;u^PsqV%bsPtl)Pw@Sd1O)-j6Y{!pxKP>Blcj_xtwQGh4S_B$gh7CXf@Bm zCc`q`NA(Fla<|Cs2S&{2!~0MbniaGu?7@eBU@tj`L zw{HNZ$1+}CJH|`9xByzsZq!oW=fTk0{K@8cyZJZ}>i)6RwxHvl|bLc;jrQJ8`kF90W&(YdyRQT^em;TLJz+bD`X7 z)NF~q0{mST?e?`%I3H;)+5bkF{>jvAo!rNIz?f!-hMldPb7pJINe*QhP|X*!wSX2l zDAw8nG~D*DQP?#vfcp93IdB=Q-)eU23oyyhP;v-N6k2dQ$Hd-VIskns=f7HEYF;q2 z9!(jiqoB;d5SiV4fo!o%ja3&8ZmDhS@pini`|_7>0H%0MGpO-=yLg(d0MloM{^BXL z1-NFf`SNpx#xEo$C)WSYGW*uP0((`;@3x&Z189V=}$`*WjJn3Z3%HIAKua$^d!(~o@G24Y=L(AAIf+#yc}pXzy7T@?oYi1 zXY|7=PnUS+ zpwrW8HeufPI1qsBNS^i^fTxES;_CuPTe>Q&hkDG`-Ys1>0Dpyoen^lvG4plTtf)xJ zc%E1Xz1hfSw?9T{O>$>903+h<`Uj($E9M>P`P2zpfNS;^?dAo&0+=>79+?cQur~qB zu6|g@dAoR76LE#nmv3Oa_AigT5e)?{zCSG*0-YSw7IG4S6`&DMKv8-Q3=2m7Jf z7M=yL5_0p(z5?8JUKJh0v3C@Q0c!RF^)f({{HC~AUQbNtu~xI0?InQs#JA^wE*7cT z`{tJc=5QN`9HK&|Oiw%o!h&(nKL@ybZ8CCf3$MqcA@ZQvvTqCU1h(vR5DahSBX{`N zf47<^#vQKM7#axK}MYZvi;ehC^cNX9wW~h9tm9 z5>!No*8qt0=K#x8-#jq05d!G*6qkdI%B~DY>VVN;uQ2L@6-J%AU@R6DIYZ6f<-81V zqZXa_1#nw76gbHZO^C5pfCj)f7?7Yyys`pZp*J&%rh#Vf8JwYv+^|}p97+Smz2W`0 z)9K+*bz4?L9QeW`6XvbClR%Qh&P{lae)kMqmGQtWW%^e@v$yZh0iH0<_qX2y%v#=> zJ0&IlVULbC^s7hijpdQM*LsAbbEv0x0yNvYb+=|=3@&K*#!_ZfIMZPQjNVvdde37O z(xd_yG*1k3sh_8F+E#qrF0lY(kT_vErY6AnJb?LVbQ7m92cbOb7~0m7Weg`a<&y^R z>)P+)Pj}7Q4w7rw6>3uirhg|j7YsGO2s7A`!tTKWw3_`n3CL@9a99q2drVABFo0>6 z3Js>uNotm8-vr#f3T2?o#7w}%FOPcbt!8(fY_(uan56z1Gan6M!J{XDR`U~&=VHYgz=X*jN7`?R9EUc6(agc|sqd2VIdkPY zRW3DaVKL?%fcVlW3&6Nh%Lr#9fWw;a_JGGoEqNY0VLFf8UkP{~?>B&6A@q4d`-ndn zf8H~IC%q5y&+NYjs9DP2YXS3$rHE6yb6UMih*q;Hv=nhp;h9_*z|5yTF@Z=Pt)~EL zo{yv{QqOEF0GYjcOQK81`x!5-n*icnR--Y3D?nrFz$)UQTxJ6h(=gtuM}8UJQjZyc zX_^D_upHMncatGmFHgX083~Z}XaP*06e-dJh-sXroEH4n24ITi52vsRl|Ouq>5s6B zE@funrva~8y{Y2 zWz1M(*{9~Eut(iO$1!u1$w%%4?OZA2?;5g(nzeIX1MXhS2*<-A_U9vipl7=?+B>y9 zhj!T%HRkiIT><6;udPmvPU08Q=X4V+Fq%?Mj}JyOpkm(n(W?P{42iqjuwf$0R5AMAMYJaNq2!Iy>*^6Y;0u2eB<&}`+?`M?Q+mT+|ra4_Nw zXMi|3qzph@SOIGGGO`8C3KQ^nc|AE5fSSEiTmkL`a~-*})h!9g8c0y~n67YhbJ_B@ z-Z@*auK@L+lSXirg}&VJXkhd)P9OoMe+=e6Z;~9dhQ^^@-U1{c9K}Xqx8-eDHqvd!w)Gl|pS@vv&@JRrg z&CSkr0Y-vz1(+}kPRF`Y*z^>j)$GBw+C~;~(Yyd|%W9jGR*-zkoml{Co*3B&xdg;D zdv-a*jYlR!ahhPoh>82748D^4@PBNM*?xW|ULZ#A2Y7Xkiu9fxAR zHAgs_unC-1c3^f-_E2BxZ0Jd)XZZ*yk zNsc&~3K>0o1%;#QcifqzT&*0$>6+Ups04-QCXt?n(=LM|bjiKI%OPSzZtpz~-~d z2H^g(t?jY_^!}n|tMAVN*2o?B$w@4Cx;`dK`?2?Lchph)c{0E@Qs*tr|R1KQ9<_ zp>H7ICJ~Y9>@Q%%30FI095Trxw>|*O@>X(l$3FW-Vpk(10HECd}kdNkS<54 z*@iAh8G;dqe-_Yc&U#Ft6&&BZz#STrYrcVj`PNf~I@XO6{swc#)k=s{&|t-Jr&z&o zHWX4?ifIHupSkB{fLpi9;Xoz~MgFRxc2l#p?G<3c%zO?>qOcfr0**xDIH8!vaYtC5 z)J@JN0!F>;3nsv*t!Du>>PR2nGRC}ndlLYI|5A@X$CKf<0P0>x1M$|RN0Z^^IiM>t zsM#EUGk>oz8k7~r6{qUUZk>2A&EZuQ3TCI^L(;9T&8xqA-y8X`=rVp+#!U~3(`!ti%r>D@f&>)|-g zD^9t4MZSCe-2gnsPIQ9lw!aQp-q<eGqSNg!`0T`=g9RU~vw*^SA!1z9fY2Hu?FotQ3>7ChZkY4Sswu!06>S0w7&% z4&dGqql59sh|#g!4~_@$H;Cgg^AC13K+R(7HDKbk#Pi5s{t{r!0cw`%_pO1yZq5Eu zO+|bi?lEaBVBEd3jev1KYf^#npbY&4nq~Lc1l-|LT_|HRBwWFaK>r=0gc! z9;rfd4|Uy|bOK_eEnu)UW%OOXnG%f1SYbxZ%z@?=Cf$MGk@N*i zBsIkt%>A{jiFsMhpL#Bw<7p-R3<7ws;G$adme?14dLY*1st==ir zEKO()%(mPuo~{nq;KEw>;RfKbk=uLTbGrq|8N?OertqTxG;fqK_ej_0B3)M~XD}Es z@sj{GXBp2fPJ)RqwGEgz^a8lYr0a7xsRzZgt771dQsy}fmuxggChzzg7Qllf8$P~8 z&3@uFMX{VpB#;+bD?p1Hk$a=?D*-h@oR?o0JwypL3~mF@w>Jh}U9&0l#B=%`vXf|u z7eLJg^KKI(4kKnm-@X81M}h(TNb_yMc)H@0R+$A5tKo+ABR3(p?!4j|z%_flbgC{Trls}7A_kMQ^vfSSF**Z{11IO!5!BUa=7dJUVW(9tvJsE9NQ zdu*NoJk91^DO-TQYs@8R_M}ezW z%FNdv0c!Td^L@-sQ5AB^cS?>Y?= z$mWB8p2iEus56JJRJ zW)|+>MQ@nZ922s}cqo@LZo;C}mzwa_}dfHy97UlV1i9(0zXqkYr>Zhi0*G%uL>NZhmaXx3~BGm>lERn%w#S@kX|0=|4Y=b5 zzwIvQGaRC`P{{1po6Tw~^=0>HA(+eA8fQS9&tFluRq z(b%jo`UWSd;4P6Q0B4rxjbkamXwg$3?wRC32Ojz5PXPGNPk5`*^RY%>U9(3^%jL<}0s(l~20+cS zByR#{P|s{K2&(Ho3Q)7Qi~we=R{>f{@d>zpJn1WjJ}hM#MxEd|jnl^gymelY$+R{~+~ zcd!1O`BWb+K%X$sES-1_m{&Zv9Y4VBCT0;&as?958``V`^tPy3R?ih6efQ*i8f(CO z>2=(hG4Qy#pFKI92ZKi@Rr_WzvmSSgS8XTFpv-*ajv5#orkvAcrF@DSIrZY(34fR`IH&+HncVaE}G zh>#B*2dWtj#Z`s9QlK{(Q5V;B2%)#`xsM(^PIi-3gU{P;xIGG#t6~o5Sq;1~JRHEi;y#;9QNQS>a2G)|mblHOo55BiCc*TQJKbjm7fFeCeDLRM@+& z)$9*6%dX03uCFjA)l$~EDX8c1QLFh8^KOlCx5)p*qltMM7!#(|9A!KQmPg*$EtvV1 z*Ma4cBFi>wp8exStr^7#?$Knvxujn z5^d0N1w&ijRQkpzF#gUL0E$Dv)9el63ea~t!v?o-N%HA3sCQu0agH%KImQ4$4C+J{ z#_*P>s{;uLED^`A0Lcj7`E)wVo@xM^7mS3(*|vzYf>$WxuXs7oYL2&L_v8jL6ZSfw zcN8^yl7A8ahc&kxqDM^ahzBql^$oyt%a=hRu*9GhfWD1K?hCIb&WnM(^RRsspyrR3 z;k%YH{y=Z7&R(g`o&!pMLCsdJd@B^vR)k%w09|aRaFT(OBOEBK`Bs75(~sUg`$(QY znh;-BfSR9rM9rV`=+!m5qr5mf(*k5XK58{jjOM@!GareywjOCrSC2F+Rv0mxqfKZu zF=F+tM;^-YR--vze;VT*r|;t;h)p8TsT#mYu3h8%4vv0DZj620E@(ME_b%0Ls0($X!=S!|wQ9ZO?#8w#BV+)4w zw&L={KC}c4STl442pEmJBR5tUuP4rjKz=N-Q3a^^sYmZV@6rELYj&?%b9UGXy`f=R z0PE6D9C3OQXw27qfv2k(e+v+cI1GitQUg|i7~dCIk@2h#E!}Mssjr6uV~WQ%j@G?t zTLEsu3#bz{puBZ@sM%VwgH^z2u3x=ZbF>LMn_11vqwl-nktVc~IZ*S0albDZ6SiPx zqq81=psf>E1*=f=6OZVG`N*r{)pPP$z*EsW=hgN~-+;rlqaMKp5Y3KkA;?zW<;WH= zomApB>;&-rGF(1fPdMEbFVB8QpW`?UFzP5_kXY=bVUfE+p8&+&Zvzk<@wOEb7y?hv77OZ^p~3FDL$MC6O-R)B|F zVP2Fm@k7Q_vxWWL+nc+m;@!I%2S~7gmauRT1sH#zcO#0FpymbRMr})0x(SBc`qBzx zw)U>Z(G*lD!Q?;-V_H3vjMf z6`N$AV{8D1j(m3J2LKnA=@-oS7=SUM0o?k;NVH><82x2{(2RoNE8Yb2|H7Kx&zg92 z)R=)}U>%GO;2~Imv51kJ)nmE<9!kwO%6Q;3@yO)r@zPEMjGGt}kABv~127rVbKuM7 zjpKm{O+ZtMe6X%}0~o>svz5L<08tVPMjzu)2Of>N#eo{XwE!Y?FLj49Zel3i)U1^Y zK*TSAr^gC2D|BJ4cdOTB_FiM$sO)Ix{pSGC{5h|`d;0Zt%`)&|ITSnYwSDHik~`t$ z05!{0zX`a1WW}S*i!rU{7Z>K+wVmGt%vwWfq-JT40Gcq|Y~DQ>*ikiMwgC5-w)hk_ zw+w)qwf6wfbXWoY9Bn5k<3=^#NKow73qZ|U`T$I`Vgs3_%}sdr1s?#e#glXO$S=z# zO<{k-Qvhv0^{6#EU-!o>nEB`lU{$j-T429cFFy;YLhjfZ!&U(zah`hgjz=0BheLqT z*lYk67K(zPX6=nO0d=pVASk15c@7wRDr(l|3c%mB0H#^nEy|eY=YZZ(t!9%UCoWzA z%?h%js}DB-H{q3vWZEh`7AR`=O7$$@dc1a#VpjD$1yHk$Yyk6hkGt15-x)%g`RHRn ztJ!6=GhAVoNB+`@c?OVvdnPHTzRL?Qc|YF(1I!-N%@R`1*R7Yf zkUq_Uy((cXVxic=Y=wz#k}$(1WzHA?^c4(kEg1KO0zY_UUJ1ZK9cRT?5Nae}+a@3u zAwAR>KQbB_n2$86$l2pZ9Yf999IpWpMv4s;{sf@a{28ymd;0Zt%@#VOq7bRx2>4o( zGbi%($a2KtAw1IHa`e2Fp=OzOwg3ewoqg%Y8gTb|adyrI((QM`s9DbYHQ-^AYmP^W zaUXiQcIXFAPS?EgNX+J&MkqWnW(lwDzG;Lqel*lF!PaS zt?w}5t&d?EHCbj-JOPh{>|I=Qptp$9AACSOyMaFZ#4dSu`F0dA;tVHvSYzfRwMDqF zDl7ms7tFioF(ypbEea>5vq-C1pZpvkEB={KJ5l%?&{>U|J#QDlt36>@s%HamKYIc9 zT`1fuAz5>>y6&hMj{J+i9W?{Sz3P?RkKnN8$;M&tmOR@W^ui;*{0RVGo^Q!NaK>Av zFZV)>3&R+HgQttrO_0B!^BgsMRyfN9nE93g>%iDDRkJs!n83C%)GP}RfWF-~sBmeA zC}jUWUiS?uJksYlK;!HH4S-G$(OzCFP&)Oxx0)-=eB`#4!ftfIh#JL(|{%V)$`;k%r`qm z2j2Cq5NG@F0W?Uy=mCts%DT35N)QPqiE2mvSr;onpCbAJJOEy87mQcis~P>{05!k7 zFdt|xTmk+-Zy~R?H($M=U%gXwq6oyLJHdwLt5y5qC4|^rvRy;{LY&I5c-MrXhiRpG|q=`7C_B%AOmQU`33`&6Ze-_ zQagT$`gAv$pY9eO0;t*CV-GNQlzB+ZJ@x>f?f3<7&0eQgnBMQyyp)kVw|AFE;+{_d zTFsyF`a85ZkNW~hP~9cNNB}lgNr2J40OKo22{yhRU~m|~=rMpn7_8R(bEt_fp^fMo zGe&)lnT1C!+#@y!1#kn}TY_;J*?UM(W{MY#Utch;M{LAfgP#l#O;j*^MPg)aCV1Fi zA%TZN>_)|NV0sTy^MdhP3&!6rb|lT3Kj)(WHGi}WUm>jvzymn}aVjCuCSw59EM*J8 z9X0=Tj<%}+8WcNL!Kf_*@Kf_75%=t(Vl={n#f*FDIBSeX zVpYbkE9V@AJE2fhFtqh5j0}?G!c!0W2sjw`#RSZ$do8k;y%t#j7dD64CCCMJCVpsC z&@9!8eU-mdODY%*iF4AgF&h4*jJZc=q^Vh|`db5US+jd%2#b&S4|nj19?Mj_s>pL`QsulNtEGFx z!qvx&c{F+h=4-}uObo7f0o=J>nC&d$+<^)^l{gr2&kEB&k7yYg!8ZXlVW$&i^qqDw zUF>24n2$O@%?0!B;uF6Bnt#5H9*p?O4jc|mxOFLiVAS2t2F`g3^`KAug3+*j3INTY z^Ew~6ysp7k5^!p^*me?dF#f<#1bUBAvvgSikFs=GFz!_|sJ%uPJAwc_7G_X6d8pZZ zY0nK97uIeUjK^Iz7mQtIzo!6de(Di@_&JZ3nwdGxNkDa`_-%pK(p%MRUKuM)tJ%Z$ z95DM`0?$XR$%Ly};j`Ap5OuRW(mYvVM27Q~Q&>$r3-s2hSsp3?b&LH`B;*xCtN^jT zJxiqDbs0~49lEAwtN1>Kec^!kkg^=n{&awvU-Y^=N;VDdw{`0aVB*IDMa`blKHv>I zHb(+>UsLQk>Z8&;a(5@-WrY(u`rrlVT}hMH+3j@VB$)}d0>sG&y>8{*-X{vXb3#5zY2K#iMs%GzV61iNY1;vBLJ&tT`T;K?yn> zoj|zWs{ukso_kBIPn4c(eg^R8jCHcr{D|RmCdP`KJye$!;Lq`vn!pydJ^*T#6KoCm z+dbcWSegvh9$Zfk9Ji{_>zh)vQ?tx3Yrr+jS;eEoNY<<#ndULgTFs9bdc#|wXMy>a z#3KGs6E9JXu`4ISJ3vf5dXo?@ougeE>v2Sn}^1)EgL&+UrEiSkn2N zIT*d=bLJPH>jv=a8-QA8tE!qUHGCWWg?-zgnJvo;0FzGdOx zgpX=p>|X`YNUSg-V})_eHi|5dJO{2`9({nBYY$wHZ6Wr0^2m*P30+~-&nt|HpKsaL z=R?hSxWzPA+xmo|HQv0Tbs0BdRo8x5CPVW(G6l5At^l)<*Bl?Hrm)HKDs;6rYacAPRrE4^u&2ig zkes_JNPsf_m<2G!o-p>@5`EBzjhfBz&f`zk1AWEKbHL}Hc_fZ^ihnR-H6M)zqne%T zpXskJwdWR$h+kpEMk|bKwyvm0L@rs_JLx|dS7;S+!FU69vqGrr?cWh+;kSk*hMH}X z@kweh>Q(!R5m{0reUciCNi|>k+%%8+5Ky!Ea(QH7zhKP7H;ez}kyR(VnF$H$WKgrM zfA%+n5!cz548}uXsc&C0kK73o|8R42nj(Mwj%EDKYLcDIlyUD^qTj3n-OQK1P98aK z&agklWoWWH&wS7P#h&FgUvaWkb@xPE0qSRa-YIOF7r->zCnfJZHG5UE3m%MV_N=wzo=2v60Zgh_ocmbI zTFshdchB_|V46Mq@AhP^0Mo2c5bpOeZ&(ZTX?Y%*W~+nt^Yh3wFMw&bfRR@pnk`Oz zTLKvKhL<_tjld)K^8%P=yM7%VfC^*Y@G9w>5O`#MUI5ck2SGTbSMLuUG-@ zXK!I0n?R!`-ZJbyfg$D2I5mZ&ShL|C-?1_KJa2W<+wFMtPoDBaX-rF#XK zW@{P_;h?Zb-HuG(jld&!+yGk5o=Xd0nr$q1G5`vjW()iK0+{BZx=^!~{uN-Fy$x^* z0ScRDuO9aWFwIthoNs`)OuUzx`vRC$FFsC1z*}ZeFFpz>fo5w*E5J0{X6OJC3Y%tc zyPc(gN2YlIt!9(D0PbgRyL~|fg-yH{wi%jow~{7+SVkEt*TA3|%mfChU?wp58O#I* z&0spvY92JNfk88v2@F!fOki{`mU!coSgE`UkZ!bTa>EK38szaeMWfN9pk#v{|b0H#?B8)B&W+9_%x7QjSk$)b#j zn1B{2Hk_tL3lx}64PBs>Y7LkOtyDa6$1Q+6PAe5<+;IbFHJj!IFwI)2C}Wx@paqKE zvT4>z1;#XMfdb7 zficZm*ua=(t!H3NvvvqzOtTg?Fs51S85q;7^$d(@)_MlUG|Pwt#x!eT<4Qx*to00x zY1VoM#x!eT12br5QneTY{+{)|DF7S2s%9;HT+8UMT7dplkUG{tYSv;1;Lj7lS6$x? zakyj6l89r`8e(h5Vl`yajOdfN<(VwSc8U0*s=C#f6JOPX6%l&MzfH` zMkdN{e)-GYhws1p;UE9c9R&6jtHHnc#n-?4umAGl>)(9)#}9wmef`@HfBE6(AOG;- zFYhp5b=r4d|4)DV?k~T8H@;CkqD6Wy!S0wY2OAxJKHef3`6!Hsdz2xq_-KOE_=6sj zqU)`bls`sHulv?mGAQaTUExS}CS$DJkiVvXa!aByYR0K2z_+dAdK)!!I&Jh12aSaB zC80QK^pW>Npz^(^|E@4x@yr?3Cv=YRaE&8h$Jr|M(b6-<4M6#(ts(nRUm73EdHEkxmo*J<+9?CW65Xrt8sz+*&PG7;L zm73!tHBDT7GBu<5**8QhFVqY?G~xQ`)b!>TYEDm5(@*&oQf~K0 zQrc4TdQ!~7z3oZLkcE=OUOGHbc!M|D=JrU>X9;71#~!9sm(5!%0|B&F112H4z z*1B_k_3cmJ{`rSL4y6yhb?8*P{`cSi@ZE>6|Kndi{4YQM^yfc)-*m$N^-mxEuMa=| z=Eo2J^nrB;-4LDe*MIuUzx?^z|N846{_wj${q*M#?^u^U?C627{aVu>hc2K+>vunX z`0(2we)x$8W1Zc7{U5&l{*V9m!}tIGKmNnN+kO4_-~RZY{>ShC=?5xr;D_&eovq5h z|M1hd-$8MEv#-bNp5Avo#UIyb`NiM;?ce zl#uKC`rj?k@WQ(&B=0%-`Tp{2{}@YJa0NK^mUp0x(Sx345=4yh-B`3e-+@!P(*{jD!2sJr(<>j@GkutDb^ zIHGj1D^S+N{^^JBfBo%GAO7vH|2qs{eEAj7SXh@ce?6AIqr z#C6@Zlo_7cA|fA-`{m{X;o6j!khcxb8K5#F4rgt*_wvU2_GuZ<~;U>ro;F z*Mk5Hu7|l6TsI0AT=$R`Tt6oLh`S$}fwJKG$((}gCX#|<^^JEo#sjYi6S=&*(WjWA zQC>>99&qx2gALkdxZvKm{lNo{olvtSKj7&3CR7TJn;ZkjR0&-7&X#w3&_lryVqf3o z=vTDCY^r>~PO%x?9_wR=iXu>OO!2@S+HSCv>nH6Cu4kVvxSmt|5y#=3ppWc?oXrQAR~H$O(HuLlPfT=%IJT#s0(xGE#) z`+YNMugkT5u6)nJ+;!Sz^xM;x(5rJpf|9hw)Yl|;4%Jc8HAlu-B-KIoTluHS8A-h6)#ICPL^V_M_*jvl!2h$FNh+NE3KdvS8d zx81ogq_%-GHOpNChc4HQ_l1`3-+sVxId04?G`!#!#C`26^vjf;SW!d99#C6LKl5(S zQc`H?87m5IgJ$f*_1jPin+xdi-F?rdT5$cg&Vu6%Q+=1?m$jeCT^nonfo#F?ZQNxX z^wEJ)4z|y7{Y2>_eW*EEt{+A!BBzX z&5moD!I5=1%JuVJrCiD;II_8SN;w2z;Cf8bV>y46q)Q|jxn^##`hl^=08HV5@x^u*E!%r|nsK9(b`NzZV# zu8)|CYpgS9D$NnME{6u(Z=5bTDvWYmX&Ja4-T(M5v1HL##LboOpxqu~_V{j7&T1P( zAV!}OZ5d-kpi{ov4+}iBaU@MfeV3In<`EJiq*!PuIFwkxE@6Fh~tug_%<@XMn5xE>l|cRZ9y=P^^wW7_C1*a633ac zXahfQ^dCvFqhH9?7BrLPFzL(vG~4cKJ8Zl2(4~+rAv;+utBsL&``P2iJ|I;<(99iu z!7oI1MgJlGg8wiNMZa)>C-|B5C~(*lqYc#O=s%p?F>bijgJw*YNna24ee44w)1wWR zw-|G#R*V}rt_97vo~ZA!joUTlBte|f2DZcKKcWO;jEI5_nlS|heMBi&99F5&cNw+X z&#bjEf7`m+Vky?&M$|{@%G4L@AZl#LQxbT$(2jL|*yH;-$busRE6Sz)l<6CC?Zh^( zjuFu!p$nWa8fCeD``;ridjfJREp0&$T+j6J`0n8l`Z%qIykgfA_Jh;LUJWx>ejBOc zE5Bg*%KBK8<9plUUB81ACeinV?bP{3`tj@ohP?7KOUg!lJ;>&z{BCJb&u}7p0g1Blq{H(!4h@7mi z$6J*)aDzpCsmF3va;!bva-BG~Juyc3g=5S)y&L65Fy|wEYzLFRekJH*IWn+DA6%ij zejB1Q%iUs6OsbnH)#eRX*^Wmps_9m~9TeJ2(*Rh2^fOH;FUo(5>ySWd*x)FR&zLC(c zP}gD|B=vpFBg_oJYb4DNU4T_E<~cb-VxE&{Kjalwkl-N_ebjY^^|GfFUh{Zc8>>vj zxN*#OInr3y53bZm91)py4sr!k%mprL%6G9r)p^-&QmH(IStV?sXT)jcdvLJ^w0m>v zxg{Z;LOzpPrGX0i*Mn1_E$ZtDyB^zRa~kz+>{Hkpgl`ef2>U6y=|Yxp zHX!*5lYW(Jq?n4eo1+lPFDP~~Zsee=bDyZUu!pf4g+06FI*a}zmsR32RtWb;&U0=@ z2>J*ciSKR9TgQk=7~{s(7%@idXA&275KL|yC9G}WvJU!)amaH0IN2k=U`C35VQU|K zkGnS8NLx6OWhrB^kww3dXeDqAdGx`?UWim7+4n2~@xAxdeb{}3UV@1+))bcWlsSZh zRGQmi)aWbJgWwI;t>{1YTlHO5ywE`iIZ1xzD0UsUEAlz%OS?)t*jZ&AxafN{`LLA| z>Jn=O-odbqu-%V7Aoe)x;}BZy7cw}7?VpId7$aCbd@-zo*$0PyA=U%mlfo%&dTYp>AEhm z3M|(1w(SWX+VJgMuW3_YYn8N+;V1fqq)#z!gfK@r4#$SvU~^D-gZaB*yFu+N{6|`Z z)O`r03q68mHSA{Bjd*mdNTD-aNxqWb0rM6f#}yOnFSl8R{=%{!e9BRPumPZrSGk6< zJ$*K4wjqlN=?MM)#$lYwS8ZEc`jpeOY4@R5B0dcBTC5918HL_S0<>78xK}IsnP{KN z2MG2<=-y13uxZiD!B-@C%kel~Ll;HW%lc3qE1zEx%DN_bD9IjyZ_>7coj$?_*scJSX;fw`QcRJl+iC6^F4tc!;}l>zrw(n=;RF zRHm&3_isCN`yjJ#?3toZ*^CEnTvGM;F3b`AjBP3CW12@fHa=DEqS54hW1Y!wyH$C` z-2mx-W5vt*SQ?`~6po}NZ4~6z%Wq>*so#s*p8dj5M!RGKjD8`(V9?SIht+YzksbVk zyQhvXuZCX|%VE|>JXh4mex}w(L~8O2e*4-75;4|z5&*_t2-POwhW^KWd$BgM zbk{z>S=$q1KFrIeMpPam??dRygkVHmBwrT$GKM2!tck!4y8sr2kf&HDV_iT!2>led zU+7Zz4Szh5wXx45PhaT3gq-B~@@k!Ln1MsalCLlH155>B446mr~};kL*oGWu=c zGL{ap5k5msL56>Z6<~vwjEyJ1UhLzEWC|IEg}cgoWKPJYCSNK~VwLaN<<|3!DD#c^ zK{nVb6Uo4r{fEoC%Gl;!s5%nrbB-IVQP%~c4{|K|ju;~*Yw#(8GH7N2DetyW&ZK!m zmuI_B$BlP;LaBBC9m%1WbA}Ht>rqROQ^$k);>;Piep}PJ4R9eNz=eze7ydlj4H*G0 z&WM2v838V21h|k9;7(O9Mk4mR$sfOi>>y>_csFE3lcsea1+HIbRq8ub*+iII#EkK7 ztZ(4ryaw+cs{aUF=+xk1zse>aht#7ip&VI%0vCHV-i`PY-tFmLAHNqnQjw>4A+~5a zRXaLuaNxAkf*Y~SU&_Uv9bCV^;E@(Cu1H!!hCv^vC~Lcj&gd8X6)|U;JNv;!?Jg<2q@HJs53-{h2%zv3@{bzr5seEOEsIEhub>ix?K_+n6&b4YgnR zifYS+zJ3GN11-25>e@)T*e`(VncvI15qm_rID5!pgP!*N@m(%ytz$&wYTf(sE^n7{ z!=hi~>nIm?U~nk6_1#0ywD(90v0%|gJNQ}Og}f}s)!?;UJ7gEQW8y$X8;5(yHI~}C zZ^WA%@s8la<^e9whNH=cu1vYG4}pu=Bi@a(+~9gj)yF<%?WuB?v-Q=6$GiRZqQ`d; z+0oCq(5gIOctgL!o0t5Ik2%JJOStN~MHE@cA!6fWt`b3z{6|byj3vI~XqSMHx&~lo z4S7Y@ifotdTed-vL*+H%prZd+F7sV1p7Cz%FBx%+rBNonpSNn?TAG1Gw0)D&JZsZe8BnmppS^zx~HQJ9Bbt}T3A*6wsst% z$^*T%5fI7ydHp3%d%qo-+IK zJBWsj{=?s0Wnw$`9{s}d7IT3$CF)CG0<@%j#?%-4C{E9Wd`9yL`P@Pm>fC2*8nWb& zxn7z#zS_uI7Ml%P@LW~?<2Y0F0r_(hMm_!_>et}iYwU40&YDv$azAkia-8i0m-~3! zwZVU!#;JBp-i{CntmG9Xagq{el`c1L$ zr2U}f_l_|q3?=x5fvdcRC9=i~v?GPt2NVpN@po5##)21i8g`N!<%q7R{G8`2_}+f` z@+1F|4yDRdREDr~wQ#eb4_|qVFQ%6GHqIH=`O9`N<{Mp*bBkp^#)EYszqeod`}l2{ zmjvygCvabvLvQN$x~*|dN2+rl?`52IJF)zRKFJV;96}YSzCdV6o#9A8W|gOyMXT?w zofr*$ja^>Y%<(LSUEs*JF?g3io*2vYoty~Nsxf0Nk}mm_{YQ*B%YM~&p)TJ=O$=Ia z*alyrHYF}{n$TBqW(8coJ#Rhc;MnApdDM*8F+Xt4#wH73#onbU)zMdo_QY+qi?g@% z6>4_y1{z8U3>?Vx{gshzL2sqq3>!w^K#Zk(;BiHM^Bhh`G*rY-kGBIhg@K3qNxV_L0mV@;t8;ycK&nzXPt zh;QRCK#T`Rw4?9YL&R9JhX_8v%oTi3AZ_s9b>rK(wKwP^)++jd0MDSAWjbgkI3Q@j zWgUIb^{hc3rvG?1{q|@ykv9Og>No8^%1Pp5gJu@~q&d!B(?-OUHx3V(%}L}Mv%|X^ z^OE3&`fc<;?5*0-rAqS|zf0i*^q0(giLO%hH8z|$xC%d`bq1eeS&DU)aFvkvM3%+= z9J6=Wb2!f){D-EJIQ-|8H*kk;lw)ev?;tHnd|NZR)p8^;E&bOra)fP@B7UrUVI0e$;!KkzmUZ(RH&}CXv(n;qM~PC z!9?+GZTDB{Ym0K=;yf4vJ7NqG<0+dE?Xhm*a>%*By%mSyY$cg=^bpoO4C{}F5&xI8048@YF8 z*ByIUJli=33CIjyLzKjL5N{iOPoQA*J;8o4&q=FW`Lu=M)pxPTgltEniS-=!Ti6f4 z#o9=SNQ@EZz{Adf=_};lCF4)qDXP%~Z4TP(f-xw={CmFYF%B zqbmP#qBU}NfZLcK*stpN644R$aS9`3NxQb7ejCwnp&xL*JN&ww@7|~{@^|vRaejtN zRw8#axU_+`BdtkaoFU@fj2FP#9h|%LP^4g*Ho^$E)d}rvJNX(=m(f> zLvJSpGWHei{CTv2t*Y`fJs&cWXu|M+u$zhfIj1zkMu8?D>jyjC^-^O+|#+-awF^@1B<+~{OmG3!-7WL&>LA0=qIkVwEr%QvcP!B?0 zwQ#sf3z=hLzM&z-oVgL8UB(Ui;*26pA9>oqr7pmk@8BUqwqso&b#JT-js2=@B8^S# zX9x+5eIw_QGMAiYHS@#JUd~dz(N*t5ZB!XE#WsMN;U1u;KJU{ zxx0|N%?up$;i#*8$|=Z@r(`V)8vrK@!q16WIpis(mXJ+wY09Pz+ZX4+L-z)keTAi= z&R=>j_HSsrF;_YBUF8dxu%#c7QwbYwqz#1?IrdSc_f9_G)u5S;cg#zS09A&Owj%5n z994*Ql~p?D5uw;wADgBkn>ZDnZ8U{E%5BVF8m=<2xrq~(K4wIA?5(hvXCJ_*RgU9z zj(tx{yIR+MPOzm+%pCD>ee6w}S1oL`&HWMch$$1_%N9Fv8M{hIR@jRP-OBk(2vzV9 zHmxe3G1tZXWvxwq!8TXNx1D>hGM4>$$QRC82k&w=Jl5K_&8u>V2sWFe zHr8a$ns3mO@lBXYL++wlgbv3xK6EoIv>W}*F~u_XIYS;gKbFnp1I$v7P1&)j{hV055IGK8zHk*ZlDRoeh_O($Tds=A$JiQp%a1& zodHup$fi3-TY_eeCB(OJo+M}{hN{XYLPNt(a%bOD$~6~c&KZ=cY=hZVc?088$P%Vy z%vFk&cH0S^(7jRbW4}d6e#$yF%7w3}k4ThC-$pza9_E}gq&|-N5Mn_yibU`bYgAoV z?i+gFog>DNj;-A40 znfo|K;KCouK*rfuaFG{+cjNw5%0<3IaB;pH9Gj9#3;Ey+pHeRJl(U0~eN@|3RG%U5 zMhpw($k1QgMNF1?M7hwpaN0*a2-ggR&zg5b4+9teWN=|CrHzOYX<;bUPDs1qXKz;? z)L15PtWx!R*{77bK)J|Y3@-AmgNuCs;0OY$ZGeqF#k^bQ8{dX8Jl>5sWXgr!#JYfS zxW0>dDBF!YEqFI#48g_Sc7()49s+PEQ;+SUGekUQ>%Pk4NXf{b(@y- z_91Yw|KQ!wU%+k11CBCA|6u?O-avE)4-vu_W0~h0X?H_zaO^nhBeh}TB1ZuAMJz1W ze}tV7T*PgHi`YwWkxv?2#3qA7=sb=&I25~r6fEDFJ2ccY!Iopq_m$Xo; zy02iNh_maIi+k6>MVv41#$EB?l80Dks%@BZ1Yp%~!);pos$KtE`Gr-h?vtr6@&Ul~ zDHGY5R(}uWLdRr766fP7N9LYNbGu|Sa4B<;_SLpT8*ye79MQzJKGvw{17f$LU3@Hc zoyH4WZJ@{vI3e0dS&RuZPAlik?i>#yF&=U#aLrbc(WDF!mi`d7v@W132 zRL7`~wIXOCtSa~~J>J<~D@BTtm4(KGt6>b9E1fV!;fqZLqxz`Uo)$KDfj= z8)(5`7xhKl7ZVF-M=i%5BKi*pSN1{JWT-FK1*T=qhr;)U9}!&mFTri_WS(uI-3>X3 zxT-oKM*-@&F?O{PhYXIB`L$m%W{h?t{;%zllm8gXnA7YRYCpHD{3^|an%1=fogvOP zqaVb(sDI&S!VwcbaBz{|0$kkl2rldg?W(9sAGp{ff{VBoa72TZe&z^ymG^va?6KRo zRXa%gvgrHfCaLqNx!x<^bDllqRlCkG#)GrwbuO@?#605FEXM)SXqVW4pbt4-cn+KdQ)&iz|k z_Ujx(n&uc`(T?$8Hy+=H1{nQ9a9N#~obZgf&yZJ}Ob^+PcCmd0%>*)*v1}_;wo4Py z_t>DajfnZAzBmWoqSEqR{PFRQ`GD2LL%# zSjrKFmpo4RU$zl;VCsvrOAS}|_GAW+y(w~o!(WfIuyqc*ZM)&7?oGiF{rmWC!_{`d zg-wv4=7?8mC$*|v1D9)j%hML^QmpnVVXv|8X-B!MZ0D?bj4$Fj`W|aU_5X5|f?%QA z2ZSk9nS+KNoi(o_H&+3f#%3n z0xoiBfQvW^n3Two$}cTAK5$Idz=aR89Y3mi6S#;!z)BdonwsUX+Ov5#^bN{!QE;VY zW8ETRr`qq?xWt)E>Wlmb;KKIByWyt;m-ZLVz1KK^c62Fp0d}y-2YFtccOy;#T;yJa zmRv76%bnvvOje8~!7ef8uybAC(CT76!dzVaWduh=3?I12u>dY|8-mOIAZN&9J;(B2 z{i|Fk73V6!VVE!c(h?|E`Ha}8elXs}%pLE>p0nL@QRh)xw<^sfj)}NyW;ueRwt)&9 zI8^j1J2`0-HV;NJ<|=2tW6t0K4;_T_-7#kn#r4}z@AuX3+@e53x8($7*y@Q)3HuP? zNFm!X@r1pJgcLE4@UAA!oLY%-gZ+XJ2)~a$g%P6fu`LC^ke8&cwJZyv+qOgBQ6Kt! z(83BAv@|O~U5lJDf?$`Rnf0yq3$~z;Iha{P z9yBLfgTXy)WLOV^uiCV%d_^=v z$n0ihtaAZlY53zY`i6Z7<5%d3h={P!vQY{7hp`~`)7Wmpmb-DUImeI# z$~u&|kab-#F7vV}vX!s;f*$1}Zveq&1YFc|P5DV&?0XP^v44Z>>)PF{@}buuP^%7y zAYpg#*ltT*USm^m|3+SM5~ioGs5M;WGZQoPwI*Pq58CejkruMDM;vz?Cb&9iu|n?ncTNPp$Ew!&9l1DAFNBy93H-JG2EvXu^&9*p-z2BU5|mQiL7Gn%7dDR=#J;UgMfuR_w4XA#-AFMAeHmncI3} zzH$08`wt^e(h~8?IOx+>KsZf~5oZc3Z(x~=eIBc2J+s$5q_OX5S3wk6+{9YUExeZ- z?-B%F$0*MVw#c3ww{}9lzRQuvDkCtLguRYa1T|(62^)8lgQK$^+hFa9yu@vtsXR{L zWt=a=h)!VNV}0Nvwh=Lzdp{y7lkai61dns5Kii1A<7hYTIs(Tb@=D)^K91Wt`FUgg z;0Ss06_<(zKNCL`G_$XWu_WB5u03q6LXU0Rl&FuxG9hyaTZ;a}{1SXc+-trY`2?Fe zBHP6+8Es$-s`VlLLXW_WQ_mo@XzJL*wp90ZPPZ$x3h%Nc$9W;@OaCBSzwpIy20C;Q zyrE(H!ZQ)F_=l{KJaLOy}VMHQ^e~=LR&~rE`7&;9m z?${6F(u_43wKLWavJ!`FgfRWsOA?M5@}OOTS7j4tzGF>ps%@3UnDj$0VK<%g17An< zGmB4^Lx|Iy(|Na3OX-x~3zJ71d|b@SmN?*%C#f&u8Nj8!OG@(4rBLNVFX7mB&VAIx zpk*WelyIc**V1gAN1Up}(^?N0y!F`*sa)m5J?WeU-&*w?ZDE)kWWP_B!89KQ`O9xT_4o z6gg4ZG%|3tuLztA`iNVK@8Is%cy}YN6mM;;Gn~h+>j(0gL!*zhG+A6_JBwhP-)GCo z#^Ui^zBg@Jgu{idOb>*vjO9P#4()`$U zEWw2z3>$aS%t4@_dE;zGJF-&ws-5MIzS@X`Vs<1gT#*rDi9M|HDQCH>9fUP6a(A%4 z#hF5mOhpc7Oq-FLnQR#u%Y^Aa`8j;00R%4dgWxL4vj9z8Rr%b^Dlr};;)^kF$%cZKjec$$ z+b9<~&f91tE!T{@X;+p+yLsmyg>nt$%R)cEnjN|iTZpRLUO3yF{M;(6-$uxBj2nU| z<{QU^gAYh15c7@wQ7K1sM8sBri+o?;ILY@&U&GaPA6&#%fQ#4)a1mQUzl7}zT*QU* zF1;GGL~I4`5{y{Ou`U!DM!C>IP^d%RgNuCtL343eW0S>|uUMo*wBXl#$5e-|*5rk3X6|S~8!-4BA_6E(J7w`7NH_=A;qj(qV zf0Vv(WRyRPRgTYc3nn(MRL(poz1>LUe5;A^Fs4Rz4RIl1^Y62HZ_5u=pl!Y%-P zab^HqtTR2Wc+kh0>KKpaY%AZ-V;- z7Zpc7k1E61&4kP$ETh_jpgGQZfD4}-!CY`mZ5K^8+Rbx(OlWF+e7ET@HP(RLe#D5d zHb%Y-XbJlUxVT@p;gUC+GWAFcZG_K;cf-C0F3uZ(i* z3~kU0^~>hC7PAkshhqJE1S`+5(rl0B6IiJ&|(Z(*zejO>mL>6Kiqg!fc^O zp=06}k2M(yQSBSN8@YDDv5_x*-=<~dNwxtoMz~mFjM`c5=uo3eOBIOm992W!ozT$)V?A#G~10c{k%)SPrMtkbSy?2 zeZ}${e!<`Jquwt>AVzJu7>ps!_KsNai!Ci=NW<3zcL zew3(zJoxN#6_G(TWMpTjOtVE#gxl^GAGwV zFDBw?0~fv%>Px+Z$o;yvXZ?+Q3T>^e=XKep5O7h(2*W`2*|7Mc@Ro9@f7PCd;f4!x zq8y1j0(Ysj&_?)$z{R}^;KJVsjt;DCWE?W(A}0bn-Ryh3@zwr9IdLv zV~sw*TN`tlV`#BIhx@AEnc$|l&lOzcbOIOWQ;0l@d{vaoeIw~|YaBp}S_*xGs67*qnoyL4prjuvPsI)QBiYSV8ax z$3&`(#X}nQ63%VcxY9O4IX_7MQojv#E$16DJ86!5pe;*&=qjwzIR`WTig)AeH9JWh zAf>O6oHgEza=GuuR1ovBg`?MgZfCyhw;^(3O=g;hUd(kn)sMq&njqXtGe=S4JJ=1K-`@lmEk(Z?U5iz_*ZX0kpSKAS`%9BLw#9ZJU zc+3S<>d;SHIBSdt0y@5pg{#hAPAO&o!Ib%KoQ*_bWd*L|#zFu1HqLU#_~H`|zG4l? z`f$6}Hpqe)^>N%Zc!T9K-ffyw)ED9k2NvY`7|Y1e!-VE@I2?h%+2Gog)?&T*@JKB(eSyB3ENvi6TpVlH)kB##rG;aL^?NtYZSyXj74LF#EN~lqKGDcRqqwhJl z9d-*&_r&a=1QZaXUVp^AH3U~i*?OwPV3sw;G$n}6o-wcIao{o zu{OpX6m1=>zGyNBMqUUm`b|BS*%oU`bB@)%LR|~p7RN@|@7XnGUtznce2+0a_A_kR zV-JCGJ@&-Sh*$chW#b8+#8?{gs-=#OG1}1Yo1rUcW_T;_Hlsq7VHh}K&)JRwL^(up z;)rl6vI*rj*0Q!?sB;GMZph9Sb`vzC)a1L7!;cLy3r(e^CH@Uu#-+70HSule?a?l5 z74%`}k2YE)RJ6On2YL5Tv)sh~0~bN|J%?_i4O|$(YlIjE&BOo&zue-|Z z(@4IXb^$zX;qzkqk-Wx{hM*4{PqcxJC&mqvTgU@Kp6VLPneULhxX)uBfCNkWxT+%C z$o0HkQy4tSdF=Q$w)>SnFhL(fp8bN4xz0gu?2I+ET|E`$NS9mbD0%L=I*f1WQ&P;+%EZP}t4Hnu3Wj+i1Z;rB6A^6Z>Sgp)ux2gWz#YhhYn9 zVE|RGaRxf+Lzj;}AfIZL8)V^2`cQ%M-L&U$6f|TkX#>Jd-A5}Yv7yCSpuq7X03ab3V?1*urRLp(c$*~{g%yR6@*x3h9vgC!%z6UzF05Oi$nh5nF#sZu4%2ZX~SkjgmC2c zYEi>6FNw$sJ41`7sp|*YQ0$r7IpL%wb*^^ZY@O2_TMQXX2H4OUaO1>287qItdm_+c z?cw}y@Hoe!!>7&)5WX>bIc!VqjB%AYs4qFbxO<{sNPb-BWt*zeFUah8mt`T^#efyK zmf@$Y`alGdtg;<#En|)e-$=Q^h9}q5x*m~XCg&yF_-wG z?)qq^uex6a7jgBp%hasjLA*~{w|JLhFtr@!lIRz7OO88^ z=kqEyQM{|YF9?CO>jg&!IW!K0kg)JYAjx3->iwyIc7ERjo@l0~&(q!2)pf6GC>TBQ z+<&XrfyEEU@t@eNad{6{6=`CwzD-R(Sl(0t<8a!2@U4fgv|0;oaI*$iLT&k04qsr> z59Yqv2am+A1#iz;UbSk*rH%j*#CLPNczF|pj*#EqyAxU1Wnlys`NpM6^USyxT=gwZ zUYhg?1dB`tOC1$h`YnMG-n$lTDC@(svo}8J;D9f@-==3A%S9&l&a2^4)*{CybinVO zc^z-p=LPwlgR)W>B`M>&K4H#$tNwC*zFgrvCi8n&mbG-M{x^HO8s1&smF6y9t~_e@ ztA=-J*IE=8>{|F=3X2UP9H2sKzpcN?xY#{li5*L@Nv_qkXIP0ZU&xVGYcEamuy6{K_*I>-V{k$kGrVmYn?K zSF7(TQ?IPOHnU6jS2`CMwifzpuWV6CFI(>3`c5X!qzW+OQakMTV#oXT1^0CH(5WPm z;8eL<*4OuJ%<*y;E||4zW=-ZE2?}R#qBi}OcXNNQbx(>@Vq=5F9t6|4C%+dP;xay0 zZwE_nC9w1ixlr5MR$%FwB%Bvt23UIF+_<$Rh1yb=aN)typRjmn602J|jl-_5%I^t` z53aN!SU-d0tN0GV^1cRG{A*wg!psq0nsLbi2m2;E7dF}7qqpywD^tqdVqUHd3(vI| zq_;=k^#s}axV=`M-m|=zh-3HL8Z6^dV+@w}KfvPebbI520(;r-70T^eI(nddkmCnH=4&Br5Lu+y>vk&pw<-7OZaq!0s$nVj!0~2U3Of~4@UQ$BVEWnz1 z1#ENX^5FA(E<<7I#l>KviS1ehMKXs#PH?4`cGpb3if()9W*x_aI`dww>dt|c1TVTW z#SZ0J3jM)y=#-`s8Fi&H%R6e!3caZG?cOx*Sz1%)nls0WBac)i zgs!Cjg*Ifz7WXG?7x&bNJ@c#0&3Uw^@jcrT{zn~;opIdd#Acu|m3Sbd-(37uCf zuB?yBP*{4hk&USZ1xp`ru=KJ8tACePTzWpO|A{~HJb6$jyO-{-y<_-`=zer5VUqAa zReZhEFLuB^kDW6YUk(1+!`U-LjXChBChl@+ub~5pJfV9wNoa^l9o{KPq%a+fpLo_) zNS4UJuDmC7FDHM;QB`7N3sH5=u4TXFZppl&K!JS~A3Vvu@!IQnoApUwi@hadBJnLX zsEJp&g0Z)hK`#BFmh_wgD_(>G!%Mhr6L-N5a({VH!gqO=qFbm{&6!_H$I{igsRL6Y zv9y7U7xxU6r7N|nIdk=}+uK1(d1FP~C7})VwZ%Q9A?uSewsfT;X?e*-+T!!LcZc`> zYEc&5Q5t-FMnb94v+wQT{$Ac%wU)ef4wl~jV7fK!T|gYcy&U!6MW3DEh0dSfV@c)r zq#UoD@ybACvd2;5+tiILve8oFr@6;CJFg$y{Sq^wQ?e}kkRH4Gq#D%n2x>&WRf@9n zeiZfZS|n*^E!+|Nt?)WGZaMvfknzgi74h1fK{3UTG z-wdoWPdRq@%Qa08UM|rn>&x3e4x{vpO3O>gl-;j1-`IXaRM9(Gy5S`#EpqEx{bi09 znex%_diHkBs8?>?mD%%<)e`x^gB+XbYG1RKm%JOPbkRX%fQQzuUDfg?TsJn8I@hj` zYSwR43#I&5_UhuL`IE+_eh@5mmb1cI!Zv}X~EP3ISzvNzCcgNUo8Y(O=F|P7ghC_5Nh5n@jCeHH7 zt7#1mgj|EuE3{tRs}9dyP&ScoS&o@klpx>olJ(olTGJ-Z`BG1(X`TiWyJjJ!l_^TU z;(wOn-}ATvn*BX)n!qlJAmdae1*TehVfXM-V0nwq-W0^nI7xhoUurwu`zx_|*GHTO zr#!UfFYV7yF-tEHu-b?c07GlIP}V0hku#F47J3n+k1i^~By@iXMA@%WvON#($;5;u zoh*+y$-R91WcTKHUH75*UoP0Y=gVS>ogxt+Ih6vFx#wn6DOF#Rt>W*uEr|QPoOtOok zg9u}HUe2%8X;_!h`PJ7Z2Bh^)>8iQ=JXJ4r@O|}ISwZCyiW*kNUyF*+kX)hNhj)kL ze^DzP9f@X}IMda%E=^vI?4IvS%`G%Hzm5wp_NDNCXzfBuOQ$NeBG0u)3SANVu~T?s zbGPpKh3rEj-q0zTLg|0rnUfzY7bG?|7Oli2{xzyfaVmLc&r&EgHqM2pW-TZDhrjL54QeZ!OB*EJ{T%{_eF}r@2}=I z>$`?QJ1-+Fdu#7cFJ7gW0QTp>r~F=H;9NJ+6L}7Me`QZ+&6KXtIHZOTSz*QzNZIcZ z)~>ldb+CI?Fc~Lndtq#d@C{4JxGQANx2@69{V(>L(t;O$P&<$KD82AYWxwo-UGt@? z1y}NdLK{TI{@#hQ-LK5}=oYwrzwKMtkk@=O9ae)ER&x0GrCKc=yuANQv&dUQ=5212 zH;{9GiSwML$Vup#_LB3EJsH~N>WkdE8s0sR3sD94yq0@sq@b;@p4>{mo0C!Nhr7~; z;uNa(E|@hu(N!p3==YIj)T_MCxBL{IHS}BfY03 zavond-}Y*PvzGQ}psqFFmdB%a1|QE|eaRse9*hphYa4ys%VN>TrIM8X*a3U@SszOW z6e-He@HJS8oIaH`Ji^rq9dH%o+>lFXh=9xbtft?(!Mi?6LvVTxOfpVV;KC4F_RECr z@3Fk|ZF9g?6!v_tDZ=wxQIf`#xaA}Buna7*G^3Ct%twu1) z^7Hq=qK`8_G-1lP*tNcm&%?LzdxLTM?(b1;=iJWhS-uOF_#3Go|1Q|ed2s(_AN)Q; zYrc*D87%yY(jI?=ajAI(<6+zV>Yo#O5xxu#_L^U(nLf)q*aJrqXXY9QB?A^$ zg3%i%;X+AV7pyXXs=Tt+xEJ0|2JBicb${svXydCcdGhPPVkd*~HV>|FtCjm7uA9Xb zuCaQAad{U7EV&C{F8a=^xO8ck8)<$2TuaHf@O$YcYMkKW&LP~re8Zhye-4LH-fX&# z->VmcCGPCo)V6>nhYC!>?XItHq8pb!^yhM08w~7aZ^U5E*HbgWeS7ctJqdQZKINA~ z2l%86j^{W$G5Rh=AU+Q_y8I7mguh4)*jy&rC5ZevFIf4m^yly#*;kR5GCEdA_j~bu zG3Zi54wf2nFw$q&cfq2CDXR?K3s8sW2-M}=w0ZBhsUbf%>-zP0;44>E)U2&$T>5!{ zG#|8mE@*Q90DwP;b7^fdM@s@iNTU%2qwW~ z)@K)+LuF^Qiy5b$c=M3`UUEZyoBlpv<-5Y6k^A1^4e#W^+q+6J<1Lu=UAy|_o$7US zXRzY%SYFngulnEO$^{<2VJ-2QgQ?Wq_3`m6ZCFXyLd17m>Kn~b-Av{}^f0x){XIv% zw5v8Z`n~YN#&8{Y^3A|%Ybddc4kwiDmkDnr zSB;|}v1sG+o)Q=W-1X7CM8)pP0IF=}mQ3RCqh-5q+17B02~!+|s=;XZ21japSty z7uy*u{ZGMC_X$>=@Z}BKYgN24Yq09_N~_jCX`IWtXDO7h^d~DVwBfczpQQ0e595OG zy!V4UFE?gk28(8gHw#&dxI1ajJOxE%(=s zBSQP_%iWjQxBU6uxefkk+S!L(h~LW9dK@Uvw25G4#R`&z-??LVvOkqGwYbH?M$lTECfE+MLm~m{~ku{crYx zT?NmS`8_x8@y?;GTE9)*la!P6oKkg?SQ}V%d0y|%0STEGGg*4py`+2N;|UIw1ovBt z-O`^z(!$nl>+z1?GF3sXq7 zd`ReceNfVZcrx>CYGte?`Pl;7>F4J6k{XnR+w{SZ;w6xsag}A>xs7d)yF@p; zl(DQ?*;w`~`J>~yKJ~DXQEGUjD@$RH9mNWWKBP1)y42MZ@A~d(=*5fc5gqBCpvXA7 z{Nn!JzTN$*GK`Ld!n==ig3cM;d$XOlek5vLXo%Z3I+ApY+!rwX~6WRL9tPRua7(EO$%VdUy_x%+@*Fjfu`cJKgvZGc<7x{8@LU=Xn@ZU6}=& zSe826=rn40L*u+s;l(_lYgaHn{CTbgEd2y8RWf=V%OLks7u@(oMFeawfZ~wh(SsXAcmUe~xqN8&mM;>Y28{3o@Ztp67pPGT=?dw}-XnYZ!;c9M| zc3GzJ0Z>_c9@j)@?k~+E_w8zebG`&&?gE`Z<0#Ns-!<>pd0+fKvR|Sz$b#zmwvPiL zNK4;26rg8)E=_R2-Lx`57&d%YJ#p^QwRp?@B^L^#-R}CXt88Eb;|pU~geHZBf>SZ! z>{mH^#$Bb{&Y`v-b6gjf9miY^{w`X%7JigA}wMx5WPw3QT2)($z*{|meGw(Gb*mL9J3I4op92h$w-y)*r zM zw@ZymUW*jh&@=8FAE?a1@&jhsu8&obTnG{R$WUT6GT@R7cW;*pwlwsSC_U#P^CLdS zOWa&KIMLnTYi#k7#+D8w$>n}XUWmTS{Egm3-H2UrNm+a55@5n(QEtuyEV6=hiw=ye zV!No`ivG+?7~j$bZZjgOoLxEkuXU^@yV8OFIo*gIHkvO72NzW3?$(dh+ z(U})D$L6FxM`yiS->iiLD|FA+SblXiyuly!FL-H9uxMj#M8;pwh;h1nO?vzKHpv-Y z#wQL=rmS8fQzJbouS%&jB=;ydh5GDGNF?~9^Jg5zIQNoT657yCb@pL>k)dGC6VyR1 z{FKlOO>OtJmc|3<$$H`mTfDkY)6mmWfp(i#(Ot-2QKbM6A(o;k>hteFW>SaoXd-SF{q z17{Awlf7?zWbpy8lA}MnG2z)VFd{4P?9wDhVQ}CY*6+jhzw=s4Wi1a+@vNvnIK9V-vX8v8a?dfxP8N1v z9%d~fqR-zuv9&Vv2Vv*Et5@Z)^2oQ5pa!iLJdKV0jZ0 zEboqhd4OSYAnrUgY2Nez14}HB$V)CFQJcPRzI~g6mu%P4o)Ug9ec$|E{DjwSWaY1K zQ@03~*zCd9_Vatm-!LvcZ{Z-lyiYw?{tOnMFPMgSiv!}Lu=qxNn;cwgNk0F-zpT|d7Ja;+b>uD15136LZeY{evfq@m;~N|T|M#AGtbxR0_IJOPZFps z@cdqKg{&|AP<)%1$f@$HcY;X>-Z>Qa4ZZliH`sy)!Z) z)@SM4^tS+u|L@G1wco(fx9nhR<9wJdIk$W0Y}YJyoPFHmQyG_>V{@cm5Hm^?a=(?V zn>p@*oQz9P*=yAl8ka4w`nYk?ufWnXPUTJdB7&s{F&HCp*C%l!a&~W=poJ;O$++r-%=p#YudDOg>^$q$S+CYNIORYnEV{hk zi_AOkedHzf5?rb4T^|4|C4I}dE%HVNSaNp2QojHO`GY?-#?cvgnpbxuyVH;CNLlI)nCWv6I0P7XnNDDp=n8gwxtWGHZ*MLu&RBU)r^dTfZ5Y zVCsJB8NmD=R=juGlk(A}q@Qj)SzsbLcVLM(f~Bt-SbE-qrRObJ{Ked1(gSvHwwrw@ z&RChOc6ejzelIm$U=+p7QF{yOH|BpgwY1h+WaAR&1S{`k{YIaZUL5(Ru6O+#_tHmX z50zo_s?3{w*6Suy9?_gn5;~y$4c_ZeHKT)E1@-mEb zHTQhggRKm`^z79MS%{HuYKMD9LR9%ZPSEUw?|=0J6^cD0zW@Fn#9*^xT?6>@xtGT>H`O*53j7yCZSbU5Ji@k8HY44?uU7I69KqUqQmfAjZK=j~` zb|3p!wO{OW5sZyDGO*L9&AheCU?4giH9I=+)&FL{>KvAaP-OAUu3b9t{%>p?S!o-y zv5(|c36_<{vG(W;IC13f)zR;LQ#!ozR}-(qlTV)S`FcdQelDTj+y$gj{3(Ucncw56 zO9w*MS@S*Gx#NUG1Cu{gn2fl5n>t)x*1TtZEm5NrYLy-Ph&~kiLz-6U0G_j7DS<0j ziQmop5*Ffl+axflB|o79cdPe< z_{hB81=#iRGE`P*^|$BCw2zFsGN7d^wZoey!=qK3*jvA`w_Nk^99By7rb{_lI^||~ znK$pvUbnvR18QgVcG^pPGTh^x<2td-`Chu?(i%+VzG;aN`jfdI`<#RdU2%KIzPx(k zIS-dDInj7=c%tGzT75LgP{rmT#yyi;n4*_uO7=2sOst8yg~PZk!k=sqG@msOND72fJT3MD|7% z&ED7$q1`Lu$nVuJATk!a`<_O~y!Y@|elPhsE>Y!z7Dv%p)k=rAEB`3JlHM5_(#e1C zr5dr~g^3fqAl=-B>x{U2BYSoZszq!U*7@eQv)EH}p@=j+AC0SDR7zX;z4Yhk2UqTr zyHz_AOj%18(CVeBiEfS9|ptLl)fM~$xsd{9fdMx#-=k^684DRo`Ev@Om*?nB& z#{JedvR_WYJxgifk^5Z8p-HacjrmI@DNnp7K{BtWOqbSV!baxF;>q2jIYvK_Rv-O9 z&28@fl~yf1Uw!iGLzmtjyiodc9$Jg+9n|F{{FxVS=?^nVKW;KgNc4Gb7b1v#$?Ltj zro61R z8|P|*bLK*+`Bt%I=B1AZ2Yf*p=V`#KPwY5z5Xt?P8oW4dEd?iQdco0llmB8|@)N;Q zW8&Kvx+hY)mh{=VO0AxUw1?p8GJeqQs)n*`TOpd0+YR+5s`6%<$)y^R)8&Kn#3`CDpn#<{?ODc1<@xsmox%YY2uHLP@{D80XzT94~mw(=J&wPZg? zA&qWfYvFY)`}pdWAQXRWgU|u_F?gmkBwz0%g<$TltUigaOD{r@!C&jaG`mjj(WTZ# z50gb3UkgjO{7QOB=vjQBYe{{Nd0+UNCx&y6q?!({e5(cO;y`L>cn;|}_!IC?&v`J8 z^!YtmmeIqm)zz-$Ou9V}FWf}u$03qqqb7KL9@dgxL!?&TPX_B*UfuEXV%4y*XH?3> zZX_9Vr$s8muVko4r@8lpg9GI$!M!%ZOB?q-ad7$)OOO#0-;zwf$Zppy{6H3Hc!^4* z$OW~--J6z_*#~_*^g_YU-n<#M^sI6{J`}L<=L>1}+xod!y5aYpDU3`ZX``#i@QThQ zGbMLR3xn9d%AO)GRl6j2Q1_DX2(G#K<+WFiPl44KzXgv>^jP(^@l~B#oV&oiu{=Vg zDEg@|Y;1cj{n#4U@+Y)GR)jVnHMG$>Vf;CDR1%7+(+C}x)~?WNd6)vd?3XJtYe7~k zqqwCyhfK!Mq>P{7S;V{Js-H^BU3wu0X}r&Uka@{@rvW#vrh8)f^BLpM-?OvoYdXEe zCT49#mZ?P!UsGe8JID(cpR^GD%ecI=qS;dV&VV(~^6H8A+=Pm9Khz25EOiMCPecWg z0bGZBMwdkvUjdUdJ|ov7x{A)Tx#zNTa?ekF*?V+tQ?g%fsXbpBNOT4oL+(BkJNm}G z^_$;tMFL$#L-K>$M1s!uEW(!E_F3)#@v@* zVUxWJdPOXcm8Rc2{o)@|v)r?Ea9Q&`fWBvmm1KQKygSY%3~gWt`ButAaKOjY-)mf4 zEpp&n8xrOY)IaIe3Bk@GuG5-G@BTJxizY^6d)_$e!hEm$($X zDCN#Ltu{KY@{&}2SzqKOT_85e$^E_ivaiwy9IunOzWC!(U0p?ls^@kA{(M{An|C)l zq1zijh=@aY1agi);>_Q@zk-X!DgA!=nxJdxfccwoTt?ZOAW+VdOxb(sT1NI$LK@2x@kTb!(TIA4%xNh#uF}B^WxNgpfE}t`U z&4a(hb)ESYLhc-p(BE5raNn+OcCfWq54O33V9@v6+l|||=MT2^wFlcC&Ij8%SFn5h z?6J3lZJxrxwvPT_8;?5J)?XfM^C-ZQ8+tHK?#EhG($d3*Hjba}nVVs496~F3s^*aN z`Q))T@5WsYCQ9{K%YD1KA2)7uKMuC}S77ww;8}U&b8k1t))sugMlRg%ZU5{Ww>cOG z+t@LfV1DM66!WC096C`WzUpo4AS zt%I%Yd@xb3-3MoL@FI8;I?$xHU;K?U% zoX1G=d%OyPZ9nt-RU58nD-eowQKoDn5y*XQf8SMT>|>A~s7fR2Yr(3x>uD5Jv}mweHK zZ5`0TglzMB4k<8%wXoDt-TE+-9j7RB&+?v{dCJTCcJtQ`#?z4B+n9rKiuN)G8zOTk z3CVt?5M++(=p0(9J3cwHexECSoxBlAxrzp|7AciW&&uv5E`EF4{9-Wf=e>hWzvAB8 zHJKxMLHB$6hTFlmkNUy37sJ8!?Ex@0MCPULhBhSTcATz?d**D2eoHXyxJxn&OoCWo zo4;$#mn4^Q)vuVa6JNb=_w81&Z#S3tVA~VnV4I70zqhv9jay&y!B&qw*!D>P`)=#s z4z~4g_wCld9c+8@-nU!-cCdZ(;9y(-26oN)7S9~jxr2F^?0#?S-|qJ|&+=d!V>#H? z#em8B%)Fd8D?cs-8NR`-l6|NI%00g-uRbHx39$(Onqmk$L+>pSq_k}^r-d|4uL>^s@aq!0y*n568#w!gJx)9LKkAAIK%YN9W)3y(f!H>@+yIhZGm5`c4PW z)Y!}`S14;156FJmE}4THDRX!NuduB*wGXugnOEXSXvotcPdu}>s3lL@#X70-&ozU| zAAgQBFJN$hgbGtizc`iBmc3EKavt1V!4;(;zsJW@*uJ4)&Gr^taUEvO{7spczbSW+ zTQt9yw}Z`li5`ms<-ftHw3h7S8iwz;Qfz`hZm#^EU`)ng#eu1V3=V|tvu1vz(3SeK zoSWd{{+=|vu8&JP--<7EAB4){;GDUEk<lExprfpkp6h zn&8eWO)zWbL5_ZUX()S-gfyd6mBur9|He^ta+VnM z@|RQFg98W*t`N-9Gq3W?KK3oxTZ?Riq30_H3!cB;S^(c_%h~l+#;d3fA5ujSxo?Vz zi#SgYU%D^PEXAUwTcqTuBXGK5Do34y6H0cSq{@%vQd79}> z`8IC_So4c7=*qzR?e=WFZ!61af6*te^e}Yb(T&2kXM;H|xpC?09xe!Md*9u+^_8l= zoH+K4+w(p7i%UG_I9V&pe_)QX`@o-{J>L9e(=%;F>d80vKIBxJS_WJ;avv zd3-N0KAGUa+XJ1W{_#su+WSjGiY|aV$0k-;+HqWcIZOFX*;{HA;N{yF+7&kF?+%#U@c>{PKGgtjD*fM^F@>uzP`LWbK6l6{0=H>F$lLa&9j%f-7+f zuGkt&yIPse`s8rG*aqCsrJ)!7UW!fpSJ+x)fU2>cB}F28lOmEeYi9Ap=@mD=oCkF% z=ZiVz_Xy?CDFr5L!DOH3W?o0xZxvt9xnaf8pD!ewahx|%q>c3w`ctc6o%VeE>%Gr}6Bt1Fh- zZ}Ilbfvkcns(k0&eqZ(`6dihzYn8px`LkxB{M=tC=ZdmfqO4>`)n zWc8uBgWP}F8}1jIj>;9>^BzP$rDexQ%bU}+NI~1XkE)`_suK=QSy9os)F;Q^bTzzt zMk0KXt4!kfsV}WB>r>aZGFeto_#gEucZ&xvaR}9>v8|ZwT_0c2(vT=qaYesh8fWUp zKcv(sw$;@(XO6s4KyK0HgHuM%-Xk14{vqn=)RXu<{eAW>Tv|)#P#pQV=O=&DGxKEa z*rw#K-^<>(cRPm!#m5}CzS@^m;K)HsDUB1}nS;*1JVIJfX`Da`{poM9yiQ$j)RvB5nO53a5_eDNaPENf9j8T_dw%kQZ1V`l7>m5hS`YvhP0d`|MYH*5E~J zmf#P+&wlay` z2bq`Ap7qiBmpADmoERDvGdz|nBX)(_;o_N!6#OX%d)~{FgL$jsI)#l4AP21_XXIiA zr-WHy&GWsdm!E6F547XXnz5naIkj&0EonA#Mn%``-Zz!#Zzy4=1i-chdSZz z%{!EPmLh?jLtfC{nR`Sl`<08cbajsoW!xn;FP^2JW-XF+_YSHuObq-U56L(tN!G%g z$y%_#tOd_rxkVa9US7$3$5oftJU@4kY7t*4$_P(Hxsj_Pq@DLA--pXG{6$0B@HH8R zktxP4kCnu{{r}LP_NO_wyhm{3>SwrCTq`To6UX5fjb6^}< zv$q1ew--9)YM%93i^SLGTEG;m2DW{`IaPR}W-VZ;4+YzIP<@-ep$Cg^&bO#{*Ps$6*C*G0=)h%*e*=u8 zE8pUFfyMvg+vwR~n}6UuJX4tSm28#s!0)r>OO)BQ@bicM(*N1K0=7A$Yr>x2i%j@wUmOOn<)$L7i<2DE5V9A5>EpprS3CiV+l!Rn&9Abgd$FpB%Xz)jE%6V`A zh7R!T+!@V8v*y-xowIp;DDDhz#_m@$kif)4v*v3mw%>AGj0~VuZ|^L)XB*@mQEKvg z^!ALCcaybv#j>#Ex0^TmE@m7*7g*|Dj$qcN1MA&CMwv5bChu9^<=-<$ec{Eo=>0vg zk>|(kSHJp@m*Z!^Tet}q&wXF_n0RV}54gEKK+@WV4%7*+&R`s+Fym6Qtc^oz3&6O8 z@@?|0kmLG`LMxI|-sPiT_)K%DCT=v^O4q%C)fyI6UOO6Ow;;Ue(>%G^b*8c~V{9Rec4GF>un-t)2#zMItru7AYdqkG;UYbGiROP%$d#`?YS z1B>1@U-3g=TN8J>#rDAY&?DwDi%Y`IuQVF&Mn0-@pBGJ9ggW5*xR)d*7yR)|ppd z>pS1Rm3g$u=;K@u zp$*=@$O?76(Zh7oi{0qvZ=6mhr%ct|uaf@7pA_}T8P8=!Ug8kzUo(ftb#@NG<&!EY zq90(p;WKLRc{jnc*lfQ|PMce~Z=PD;Jyer9lD}qL`iOwNtWSzV=pJwH zK9YCHluXSESY@r6+weMN;H5P=q`?*6P4-KV2>!%%Gfq3f>`f|X?hG3u{Dla}@9EuE zSmiz|bYmFI_}ahcDqVW^LT=Wq_BH;9tHTYh^2RVF`x;9Qy<8pc^7ebPcz;jbZ0_6D z*Jd2Y=;B3v?dETIFR;3gkYVf(HNl}5Zx%;}@&rY0L0|NPdpme#1z2=+)^B9lxj~lJ zNY%|TWX#ac7SE?R1y(+L8I?8^+GM|uBRC}ka&CITWqtJVd@DwfajsGD zB1RDY(zx@v^)|l*j1{)$t3Il0Nv@=C1s(Ej>NGD)aOl8!a8+eq@rvx%)e3)>yp?$! z@18Gmjvo8vUA8zjGidVExZ$V z36GGfQM$kA?3|mZQ0~mt*DkGHz4M-%TJzu!iwI4g%eV6;Us-p!@?v$k@$=L8!|PQ0 z<;<0lMkkc|79CC$wz$&dW9drGZS;F;Z0s_bWr=y=y0OpI>qhQ#p@a`fgAc#Ir?zq) zSWf7Vvb_8XBsA%S;$Kts6dU5+N)BGAW|`MfE)A(VNo+#IfAf4%%gP1vn3)n9Vv{UC zxaVR5Yi;he2#t)t^r7Y1*8*bskP6S<1#S29t(F&A%S)`|Qc{^f-%CJ{cc-a$3Xu{lo->$iw9zn31N z)>3`<(ng**wPqx~bcMUTz#2nyeWU+bad@`Y^wGIU{m3^b9^KnbE&jet9hH5gK3hddHCL*I`kB6U%{zf+58_4;+=D1$S zI<}SifaDK>Z9XuE%)V=6Z|cy3KXqtX%N3F?uGE}`PPyfSD?Y892bDVGFv_e2i|Du2 zxh`arvvfTQ^9;)FgRICt_`^bL-c<-*6a#d9>AS_#QW?rh34akOkA8YJ!MkRy{$ejs zHzI45qQw6726A*;eGPJMJlF9bAcMpvq`yRuP}dv0sF>>So$Gea!xK=^m1XWn2FRcq z-^j_|&GYz>wWGI#$=6%{!t9Pt?J5-a7eL=NvxSxpyvH29$sENCJ0Lg^um}vBT;9pc zANzx<7yhCeqU*ar*zN;=-n$@UC%o=zrSmPv?cNXX9rt|Ix&?ol0_OKL;tNh0Ynek0 zLSW}a$=;gJbs^!dMS^0!^{V5-Uh+z>RzJVTHInslN96Zj;_Oa0KAB^?*&A6FdZy@P zA5zAGXNMHJLRf*ZQ}QjV)I1*d8sf&z@rVCidfnbPB{h-z+Fyk? zag~Obh^E9x!HbkwjF4Px5_Qb64S3BWr=^YLF6c^Km(d&?N z?yt~$_#{~5k?>&ni#qG%BS}Rp?!{;J{_>WLj&w49^99IrO3jnM94vhdUGmh!{@hhW8QhacSqtSh=f?DV;vW8}li3@2 z6gs_P-`$6LlI(*#%03i+W*_fU)_Mwn(_rctXOqTT;UFx2Z*nQv{k%KyM<-S2d ze0?fRw%+;ZG&~U`GWp(t&OXSfJrC+Za?5m4kL)>D(H)a3xC$4F+LQX3F2Gw zbZhwY58Pqd2YV*_@afW(ZjsRsyhIl{tqwf?9HbULsct)RRolkmpl^U)G%7lXwgIv8 zRKUFa9z|^K02QT*vMDj(2V!n-4xYvKx#YwfF7X5_gW&%=U%M7UIS&SU@GS2u_lSWRzSh1UGI6RGUwez?&)9DG^O+0T zhqCSLgQA#yC=;s`@9}YeHpekn{7Bl{i?w7z6;nltAl_sEEX?fJ2?+q%RZ!l56@A; zM&;Pw6XXkx%R|n2U`;s>_Eye=N}cogmgcncUN9lB^k?8Wq=;o)YK)CbYywPzcD_x{ zyK#9Vi2a6A_gjvd?4xg#DyK_73b1`M_HO0o4S(GIk^5k5u*CuOe*6q{KP3$v*L!(Q zGbDavv5){IJ?OfBufINfGmsmXH} zjFU^e^Ilo>lds)4M83Z#N#x1Hj61_@$1%e4duVs*K;Gu?(<|!55$5;gY78$v3BPqp z2U{6+jXJk}`d|#RnS(l>b2}A%_a=n#0^2uUDUMA0{Z^I3#6z#3Ve`ii#;G;y!(Ar! zZXA{7$zu<;H4Vo*w_o_dw$A@xn+GarKxv(QfNh`38<(|!EuXw``wk$O{HECl*!Bpx zabER#;^j(J_s)QA@2C4VaScKyaW$~*b8I2X_n&v>V2@nWzyJ03fBug@{`J$Ze)ai3 z|M`Fa_?d9{FF*g?U;grUf4f-kXTSUO`9J>GpZ;?HL7aU2^QX`L<+ES?{KJnw{QM*3 z<=fw~*1r4cw_knr8|ZZwKYsJ&&)remxkk9`0)8{|^ra3C`|tny_y76pAOG~} 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 00000000..d84bb579 --- /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 00000000..1eee9b1f --- /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 00000000..a846c2e2 --- /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 589ca9b7..f75c1528 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 00000000..14bc4d6b --- /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 00000000..5eb10b46 --- /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 00000000..fe49a5e5 --- /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 cfe32cff..2f538a30 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 a9a90dcf..73b877df 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 d50db11f..2e855586 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 9d445559..b6d74df6 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 713fa2e5..2fe4d934 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 @@ protected: }; 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 @@ public: 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,6 +1102,15 @@ Vertex::setTagGroupIndex(TagGroupIndex tag_index) tag_group_index_ = tag_index; } +Path * +Vertex::makePaths(uint32_t count) +{ + delete [] paths_; + Path *paths = new Path[count]; + paths_ = paths; + return paths; +} + void Vertex::setPaths(Path *paths) { @@ -1137,30 +1118,12 @@ Vertex::setPaths(Path *paths) paths_ = paths; } -LogicValue -Vertex::simValue() const -{ - return static_cast(sim_value_); -} - void -Vertex::setSimValue(LogicValue value) +Vertex::deletePaths() { - sim_value_ = unsigned(value); -} - -bool -Vertex::isConstant() const -{ - LogicValue value = static_cast(sim_value_); - return value == LogicValue::zero - || value == LogicValue::one; -} - -void -Vertex::setIsDisabledConstraint(bool disabled) -{ - 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,55 +1327,13 @@ 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) -{ - is_disabled_constraint_ = disabled; -} - -void -Edge::setIsDisabledCond(bool disabled) -{ - is_disabled_cond_ = disabled; -} - void Edge::setIsDisabledLoop(bool disabled) { @@ -1444,6 +1352,18 @@ Edge::setIsBidirectNetPath(bool is_bidir) is_bidirect_net_path_ = is_bidir; } +void +Edge::setHasSimSense(bool has_sense) +{ + has_sim_sense_ = has_sense; +} + +void +Edge::setHasDisabledCond(bool has_disabled) +{ + has_disabled_cond_ = has_disabled; +} + //////////////////////////////////////////////////////////////// VertexIterator::VertexIterator(Graph *graph) : @@ -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 @@ protected: }; 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 032c056a..da7be0ee 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) + const MinMax *min_max) { - return Sta::sta()->vertexPathIterator(self, rf, min_max); + return new VertexPathIterator(self, rf, min_max, Sta::sta()); } -bool -has_downstream_clk_pin() -{ - return self->hasDownstreamClkPin(); -} - -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 331fcc73..72c72e42 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 4001fb82..92aa24d0 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 aaf0bf05..59e431fc 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 b0497d25..ee224975 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 c95fc5cb..9b639624 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 00000000..45bfdeed --- /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 62d89a78..8b4692fd 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 d372aab2..25dd9244 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 50944034..38e905c1 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 1496deda..43fc4899 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 9e05680b..9972bdc4 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 d4eb6ff8..48c4db06 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 16259873..fe2b76c3 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 00000000..49268ce8 --- /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 bee765d0..00000000 --- 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 3cc5a045..867d620c 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 3e55f4a8..23ed758b 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 76af5fe8..00000000 --- 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 0f3923ae..4c958065 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 236158c9..1711f78e 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 ff1c9629..dbadf0b6 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 88aeca96..a834bb57 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 e722bc4e..87beef23 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 10e7a965..f19a6411 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 aa4808a0..dcc6b344 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 6168247c..d41661d4 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 d7c60c38..50ef13e6 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 4a337b0c..3ceadeb7 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 e5dc3b8f..2230f45f 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 c1c09978..f5b89f14 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 ae7a0bc1..3f537f52 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 ac4280f0..0ae490aa 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 6bf86cfe..28625854 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 381a9b8b..ebe184c8 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 1cafb081..7f51b87f 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 6a746fc4..aea61fcb 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -24,15 +24,15 @@ #pragma once -#include -#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 4389d7ce..be6d5375 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 dba934cc..995e6e14 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 1da22817..9a3e3119 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 0c30a38d..408e946b 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 6c1ea621..eade8d31 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 a0dbd86d..0b542636 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 09b4579a..c89a5a66 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); - static void - makeCornerMap(LibertyCell *cell1, - LibertyCell *cell2, - bool link, - int ap_index, - Report *report); - static void - checkCorners(LibertyCell *cell, - Corners *corners, + makeSceneMap(LibertyLibrary *lib, + int ap_index, + Network *network, Report *report); + static void + makeSceneMap(LibertyCell *link_cell, + LibertyCell *map_cell, + int ap_index, + Report *report); + static void + 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 06f2729f..1d200a76 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 f70cd737..a173ad6b 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 c66b3ab9..68e95577 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 bfcb6455..00000000 --- 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 15de0e72..4642c790 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 aa302071..6a96ee0b 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 00000000..0c0827e1 --- /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 9a96a6ad..943596ed 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 00566c10..5e88b04c 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 8241481e..9e72fef6 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; +using LibraryIterator = Iterator; +using LibertyLibraryIterator = Iterator; +using CellSeq = std::vector; +using PortSeq = std::vector; +using CellPortIterator = Iterator; +using CellPortBitIterator = Iterator; +using PortMemberIterator = Iterator; -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 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 c343ccab..dd7b871f 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 6f3b7703..b4c04ef7 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 3c752178..515d476a 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 948843f2..bd2a0940 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; - -protected: - void makeWireloadNetworkWorst(Parasitic *parasitic, - const Pin *drvr_pin, - const Net *net, - float wireload_cap, - float wireload_res, - float fanout); - void makeWireloadNetworkBest(Parasitic *parasitic, - 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 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_; +protected: + void makeWireloadNetworkWorst(Parasitic *parasitic, + const Pin *drvr_pin, + const Net *net, + float wireload_cap, + float wireload_res, + float fanout); + void makeWireloadNetworkBest(Parasitic *parasitic, + 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 Net *findParasiticNet(const Pin *pin) const; + 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 2d35781d..02a7e2e4 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 a7058efd..a33daf01 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 09763f73..bf938249 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 6d6a7e94..00000000 --- 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 c61eb16a..4f974f3f 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 784409bf..0269f685 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 cc84f9ea..a3bd6ee3 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 3531b8a4..03c308aa 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 7d3f2711..bdc51135 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 4220bf7c..9f36e396 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 186914a9..b61f48f6 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 2bad21c9..716dcbb9 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 ed4a4860..5b1d36be 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 9ddafeac..4bfa86ca 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 MinMax *min_max); - PropertyValue pinSlack(const Pin *pin, - const RiseFall *rf, + const RiseFallBoth *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 7d2aebe9..9e8aed56 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 37ba416d..fe3391fc 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 1aacbdd4..81511ec5 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 9bba835f..5d102fdb 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 e3ba0ca7..a02c979e 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 00000000..9b2e61a0 --- /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 b14e10cc..441db5d6 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 cb5e6712..adf15947 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 a3473759..c20627f3 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 b23ef5c4..9889be75 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; +class Scene; -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; +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); + 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(const RiseFall *rf, - const PathAnalysisPt *path_ap, + 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 d9268b94..65683966 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 618dd2ea..c1e4c21a 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(const StaState *sta); + bool searchThru(Edge *edge, + const Mode *mode) const override; -// SearchPred1 unless -// timing check edge -class SearchPred2 : public SearchPred1 -{ -public: - explicit SearchPred2(const StaState *sta); - virtual bool searchThru(Edge *edge); -}; - -// 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 cba09523..1923c270 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 7e08c3b1..00000000 --- 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 cdf67203..e0f30787 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); + 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); - void checkCapacitanceLimitPreamble(); - // Return pins with the min/max slew limit slack. + //////////////////////////////////////////////////////////////// // 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); + //////////////////////////////////////////////////////////////// + void reportMinPulseWidthChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes); - // 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 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); + 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); - 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); + 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 92a9bbe4..0d34ff07 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 cce271be..7d6ce5a7 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 7f10effe..4d40594b 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 453af7ef..ca7c304f 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 ae643e35..36fa7e67 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 c858df5f..9523670b 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 40dd60c6..a392e7d6 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 a70f8400..e521c4a9 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 3884cbd3..080cf84d 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 3ee8cab8..c9f5ee8a 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 b9456cc1..3621f63c 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 f5f1bb20..a339a6a8 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 c7a490a8..39190c6c 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 8b54be5f..79bf472a 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 316a254f..00000000 --- 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 2d859815..00000000 --- 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 1d60408e..2bd99990 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 001b3713..00000000 --- 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 00000000..33cc5080 --- /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 dc76ccc6..2b244d0c 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 b4a08e8c..a0ba6346 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 b56b8b7c..c2dbcfbc 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 f6114b3f..e07fb2d0 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 d0ea001c..42621171 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 3aa51768..02abd693 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 7a9399de..c1e14344 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 f8910791..c39553ec 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 67de253c..41faa2f6 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 a68dd196..407b152e 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 73e4a618..4219cd96 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 f4b873c2..c9bc84bc 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 3e15ac35..79d6d35f 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 78cf21b6..4532b2e8 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 3cf867f2..cb986a59 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 + // Return value. + float &voltage, + bool &exists) const { - supply_voltage_map_.findKey(supply_name, voltage, exists); + 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) +LibertyCell * +LibertyCell::sceneCell(const Scene *scene, + const MinMax *min_max) { - is_disabled_constraint_ = is_disabled; + return sceneCell(scene->libertyIndex(min_max)); } LibertyCell * -LibertyCell::cornerCell(const Corner *corner, - const MinMax *min_max) +LibertyCell::sceneCell(int ap_index) { - return cornerCell(corner->libertyIndex(min_max)); -} - -LibertyCell * -LibertyCell::cornerCell(const DcalcAnalysisPt *dcalc_ap) -{ - 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 @@ private: }; 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) +LibertyPort::scenePort(const Scene *scene, + const MinMax *min_max) { - return cornerPort(corner->libertyIndex(min_max)); + return scenePort(scene->libertyIndex(min_max)); } const LibertyPort * -LibertyPort::cornerPort(const Corner *corner, - const MinMax *min_max) const +LibertyPort::scenePort(const Scene *scene, + const MinMax *min_max) const { - return cornerPort(corner->libertyIndex(min_max)); + return scenePort(scene->libertyIndex(min_max)); } LibertyPort * -LibertyPort::cornerPort(const DcalcAnalysisPt *dcalc_ap) +LibertyPort::scenePort(int ap_index) { - return cornerPort(dcalc_ap->libertyIndex()); -} - -const LibertyPort * -LibertyPort::cornerPort(const DcalcAnalysisPt *dcalc_ap) const -{ - return cornerPort(dcalc_ap->libertyIndex()); -} - -LibertyPort * -LibertyPort::cornerPort(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 e56891e2..10bb9963 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 d4ac84a1..672e876f 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 46bde91a..fa29f5ec 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 9ce87931..27b60b69 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 aa2850e8..10a214b1 100644 --- a/liberty/LibertyExt.cc +++ b/liberty/LibertyExt.cc @@ -67,7 +67,7 @@ protected: }; 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 @@ public: 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 f1eddb1b..4dff3a40 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 c1a9ec46..9b1fce2c 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 bb24b5fe..5479093e 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; - } - } + 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) { - 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."); + 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; - } - } + } + 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."); + 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 bc1a47a7..648e8f1f 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 c31888f7..a87a2039 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 2c4dd1a7..4afa66f0 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 fbb47d9e..1001e74b 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 559c6158..a89ae99b 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 b4a0b325..28741630 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 f203a4af..0a7acff5 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 819ba929..1839e52f 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 8dcbb4f0..97e33309 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 9c8ea3e5..e85ee603 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 @@ private: }; 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 2dedc9f4..0ecba436 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 d165e9f3..6f4b2632 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 @@ private: 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 @@ public: 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 @@ private: }; 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 @@ private: 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 94477739..a6f1bb1f 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 578c0664..8e03a704 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 @@ private: }; 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 @@ protected: }; 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 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(); + 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)) + std::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) -{ - 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)) + std::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) -{ - return Set::intersects(set1, set2, NetIdLess(network)); -} - } // namespace diff --git a/network/Network.i b/network/Network.i index 52bf1ca0..349e9405 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 b9d958a6..a8cd501c 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 e00df131..dd82c344 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 c0fa6527..8584ff0a 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 0f70b461..a13285fd 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 18ca03f3..17108412 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 693f6908..4c6946af 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 3967a82b..29a0037e 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 94cc2f3f..5a1dc8e0 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 9d7f5844..dd866664 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; + 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); } - 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; + parasitic_network_map_.emplace(net, ConcreteParasiticNetwork(net, includes_pin_caps, + network_)); + return ¶sitic_network_map_.find(net)->second; } void -ConcreteParasitics::deleteParasiticNetwork(const Net *net, - const ParasiticAnalysisPt *ap) +ConcreteParasitics::deleteParasiticNetwork(const Net *net) { - 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); - } - } - } -} - -void -ConcreteParasitics::deleteParasiticNetworks(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 9a06058d..bf5ca547 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 72c44a9c..597512dc 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 5f656cfb..415a7f1c 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 689183ea..a6adf42e 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 400e9239..cd7da4e3 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 2854d38e..548b5014 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 15e8e81d..2b4ce36e 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 7f2f4ef1..1650f8e3 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 @@ public: 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 @@ public: 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 @@ public: ~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 84a51e64..283822f7 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 d27ac0f4..dc327468 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 @@ private: 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 eb446c05..a3c7e6a8 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 b9178a1f..e9c1ab7d 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 15f47ded..17b15046 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 b8fbb0c2..39ad246f 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 e81d691f..b27efe6c 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 3e391348..a3cae771 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 033a3431..f96480c4 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 3265fe96..6cc8795f 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::searchThru(Edge *edge) +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, + 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 abed2f40..83930253 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 cdc59840..9ff3438a 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() +clock_min_period(const char *mode_name) { - Power *power = Sta::sta()->power(); - return power->clockMinPeriod(); -} - -InstanceSeq -highest_power_instances(size_t count, - const Corner *corner) -{ - 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 97769501..f56681d6 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 00000000..3cd77090 --- /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 00000000..c069a8b2 --- /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 ecd14277..3a758ba2 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 b5a57bdb..cfbb7dd2 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 267aff3c..0fdb2301 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 2adebfda..420e1449 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 @@ private: 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 @@ private: 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 a5a67ef3..ca1d6d2e 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 8e7aadd1..3960f670 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 850611e5..7a67fca7 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 4c245518..ee317e4b 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 22fe82ba..d9267663 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 6ac5f875..9df530e5 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(); + ; + 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", + // 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); - } - } + // 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 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 15a839ba..23d3abd2 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 34609412..3da41c41 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 20f2cc36..99c99350 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 163dcf65..70d131c9 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) +int +exceptionStateCmp(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()); + 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 @@ protected: }; 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 845bb5cf..2c647562 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 6178a9dd..e88801da 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 e2381216..073c8e26 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 5dfd2180..48287fec 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 daca9186..0c11320a 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::searchPreamble() +{ + ensureClkHpinDisables(); + ensureClkGroupExclusions(); } void Sdc::removeNetLoadCaps() { - 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(); - } - } -} - -void -Sdc::removeLibertyAnnotations() -{ - 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) + 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); + 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 @@ private: }; 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 @@ protected: }; 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); + 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); - } - } - } + // 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 + 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 { - PortExtCap *port_cap = port_ext_cap_maps_[corner->index()].findKey(port); + 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 @@ protected: }; 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 @@ protected: }; 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 2c66e43d..e6b95667 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); -} - -void -set_net_resistance(Net *net, - const MinMaxAll *min_max, - float res) -{ - Sta::sta()->setResistance(net, min_max, res); + sta->setWireloadMode(mode, sdc); } void set_wire_load_cmd(Wireload *wireload, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Sta::sta()->setWireload(wireload, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setWireload(wireload, min_max, sdc); } void set_wire_load_selection_group_cmd(WireloadSelection *selection, - const MinMaxAll *min_max) + const MinMaxAll *min_max) { - Sta::sta()->setWireloadSelection(selection, min_max); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->cmdSdc(); + sta->setWireloadSelection(selection, min_max, sdc); +} + +void +set_net_resistance(Net *net, + const MinMaxAll *min_max, + float res) +{ + 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) + 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); + 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); } @@ -1130,97 +1352,128 @@ update_generated_clks() bool is_clock(Pin *pin) -{ - return Sta::sta()->isClock(pin); -} - -bool -is_ideal_clock(Pin *pin) -{ - return Sta::sta()->isIdealClock(pin); -} - -bool -is_clock_search(const Pin *pin) { 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); -} - -bool -is_genclk_src(const Pin *pin) -{ - 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); -} - -bool -pin_is_constrained(Pin *pin) -{ - return Sta::sta()->sdc()->isConstrained(pin); -} - -bool -instance_is_constrained(Instance *inst) -{ - return Sta::sta()->sdc()->isConstrained(inst); -} - -bool -net_is_constrained(Net *net) -{ - return Sta::sta()->sdc()->isConstrained(net); + Mode *mode = sta->cmdMode(); + return sta->isClock(pin, mode); } +// variable sta_clock_through_tristate_enabled bool clk_thru_tristate_enabled() { return Sta::sta()->clkThruTristateEnabled(); } +// variable sta_clock_through_tristate_enabled void set_clk_thru_tristate_enabled(bool enabled) { Sta::sta()->setClkThruTristateEnabled(enabled); } -void -remove_constraints() -{ - Sta::sta()->removeConstraints(); -} - PortSeq all_inputs_cmd(bool no_clocks) { Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); sta->ensureLinked(); - return sta->sdc()->allInputs(no_clocks); + return sdc->allInputs(no_clocks); } PortSeq all_outputs_cmd() { Sta *sta = Sta::sta(); + const Sdc *sdc = sta->cmdSdc(); sta->ensureLinked(); - return sta->sdc()->allOutputs(); + return sdc->allOutputs(); } -template Vector -filter_objects(const char *property, - const char *op, - const char *pattern, - Vector *objects) +//////////////////////////////////////////////////////////////// + +// all_register variants + +InstanceSet +find_register_instances(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) { - Vector filtered_objects; + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + InstanceSet insts = sta->findRegisterInstances(clks, clk_tr, + edge_triggered, + latches, mode); + delete clks; + return insts; +} + +PinSet +find_register_data_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterDataPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; +} + +PinSet +find_register_clk_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterClkPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; +} + +PinSet +find_register_async_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterAsyncPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; +} + +PinSet +find_register_output_pins(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + Sta *sta = Sta::sta(); + Mode *mode = sta->cmdMode(); + PinSet pins = sta->findRegisterOutputPins(clks, clk_tr, edge_triggered, + latches, mode); + delete clks; + return pins; +} + +//////////////////////////////////////////////////////////////// + +template std::vector +filter_objects(const char *property, + const char *op, + const char *pattern, + std::vector *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 112ea504..1e59be57 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 afd14c70..00000000 --- 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 1cb10bd0..c97bbba1 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 32c2450c..e317a8a4 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 @@ private: }; 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 @@ private: }; 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 @@ private: }; 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 @@ private: }; 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 @@ private: }; 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 @@ private: }; 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 90392d9c..7fd42340 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 649853e9..63ecbcc6 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 @@ protected: 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 @@ protected: 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 eb1b5ff5..1973fe74 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 8e35fd0e..395e0f7e 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 32ce32d3..ab6572c2 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 00279e49..8f695296 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 @@ private: 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 a2b4e064..fd17bb84 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 06749391..52bc1cf3 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 2c2bfa82..23b055cb 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 @@ public: 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 @@ protected: 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 @@ private: 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 382faa71..3c992366 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 0845c24c..89d77655 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 ebea9fdb..5ea3d470 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 391ecda1..00000000 --- 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 cae73774..00000000 --- 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 00000000..c084ca8c --- /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 00000000..4d395cf5 --- /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 726fbc31..00000000 --- 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 0af63f3e..00000000 --- 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 00000000..7b5a03d0 --- /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 00000000..0cd3d1c3 --- /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 3496fb0c..8e71b8a5 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() +CheckMaxSkews::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes) { clear(); - MaxSkewViolatorsVisititor visitor(checks_); - visitMaxSkewChecks(&visitor); + scenes_ = Scene::sceneSet(scenes); + + Graph *graph = sta_->graph(); + 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_; } -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() -{ - clear(); - MaxSkewSlackVisitor visitor; - visitMaxSkewChecks(&visitor); - MaxSkewCheck *check = visitor.minSlackCheck(); - // Save check for cleanup. - checks_.push_back(check); - return check; -} - -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); - } -} - -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 4f40ffbb..0c7a5882 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 06358382..7008d413 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(); -} - -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) -{ -} - -void -MinPeriodViolatorsVisitor::visit(MinPeriodCheck &check, - StaState *sta) -{ - if (delayLess(check.slack(sta), 0.0, sta)) - checks_.push_back(check.copy()); + checks_.clear(); + heap_.clear(); } MinPeriodCheckSeq & -CheckMinPeriods::violations(const Corner *corner) +CheckMinPeriods::check(const Net *net, + size_t max_count, + bool violators, + const SceneSeq &scenes) { clear(); - MinPeriodViolatorsVisitor visitor(corner, checks_); - visitMinPeriodChecks(&visitor); - sort(checks_, MinPeriodSlackLess(sta_)); + 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 -CheckMinPeriods::visitMinPeriodChecks(MinPeriodCheckVisitor *visitor) +CheckMinPeriods::checkNet(const Net *net, + bool violators, + const SceneSeq &scenes) +{ + 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::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) +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); + } +} + +MinPeriodCheck +CheckMinPeriods::check(Vertex *vertex, + const SceneSeq &scenes) { Search *search = sta_->search(); GraphDelayCalc *graph_dcalc = sta_->graphDelayCalc(); - const Corner *corner = visitor->corner(); + MinPeriodCheck min_slack_check; 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_); + 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; + } + } } } -} - -//////////////////////////////////////////////////////////////// - -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) -{ - 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(); - } -} - -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 bcba3218..a8e0b918 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); + MinPeriodSlackLess(const StaState *sta); + bool operator()(const MinPeriodCheck &check1, + const MinPeriodCheck &check2) const; -protected: - void visitMinPeriodChecks(MinPeriodCheckVisitor *visitor); - void visitMinPeriodChecks(Vertex *vertex, - MinPeriodCheckVisitor *visitor); - - 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 395327ea..b13de4e3 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_)); + if (!violators) + heap_.setMaxSize(max_count); + + if (net) + checkNet(net, violators, scenes); + else + checkAll(violators, scenes); + + if (violators) + sort(checks_, MinPulseWidthSlackLess(sta_)); + else + checks_ = heap_.extract(); return checks_; } -MinPulseWidthCheckSeq & -CheckMinPulseWidths::check(PinSeq *pins, - const Corner *corner) +void +CheckMinPulseWidths::checkNet(const Net *net, + bool violators, + const SceneSeq &scenes) { - clear(); Graph *graph = sta_->graph(); - MinPulseWidthChecksVisitor visitor(corner, checks_); - PinSeq::Iterator pin_iter(pins); - while (pin_iter.hasNext()) { - const Pin *pin = pin_iter.next(); + NetPinIterator *pin_iter = sta_->network()->pinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); Vertex *vertex = graph->pinLoadVertex(pin); - visitMinPulseWidthChecks(vertex, &visitor); + checkVertex(vertex, violators, scenes); } - 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) -{ + delete pin_iter; } 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); - } -} - -MinPulseWidthCheckSeq & -CheckMinPulseWidths::violations(const Corner *corner) -{ - clear(); - MinPulseWidthViolatorsVisitor visitor(corner, checks_); - visitMinPulseWidthChecks(&visitor); - sort(checks_, MinPulseWidthSlackLess(sta_)); - 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) -{ - 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(); - } - } -} - -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; -} - -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 d250cc20..0acda273 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); + MinPulseWidthSlackLess(const StaState *sta); + bool operator()(const MinPulseWidthCheck &check1, + const MinPulseWidthCheck &check2) const; -protected: - void visitMinPulseWidthChecks(MinPulseWidthCheckVisitor *visitor); - void visitMinPulseWidthChecks(Vertex *vertex, - MinPulseWidthCheckVisitor *visitor); - - 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 2cc392ca..00000000 --- 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 ba907103..00000000 --- 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 00000000..faa3a77c --- /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 00000000..60a167db --- /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 76e11698..9c692fba 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 1bbc6dd1..26edc150 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 a8679101..ac8e1bf9 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 09d2c0e0..8b0432bf 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 d9b4a68f..5fc244e8 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 3a7d0385..c8fbd49b 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 663c350c..a27ad610 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::idealClocks(const Pin *pin) +ClkNetwork::clocks(const Vertex *vertex) const { - if (pin_ideal_clks_map_.hasKey(pin)) - return &pin_ideal_clks_map_[pin]; + return clocks(vertex->pin()); +} + +const ClockSet * +ClkNetwork::idealClocks(const Pin *pin) const +{ + 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 33df2af6..9a34fe5e 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,6 +51,321 @@ namespace sta { using std::abs; +ClkSkews::ClkSkews(StaState *sta) : + StaState(sta), + include_internal_latency_(true), + fanout_pred_(this) +{ +} + +void +ClkSkews::clear() +{ + skews_.clear(); +} + +void +ClkSkews::reportClkSkew(ConstClockSeq &clks, + const SceneSeq &scenes, + const SetupHold *setup_hold, + bool include_internal_latency, + int digits) +{ + 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); + sort(sorted_clks, ClkNameLess()); + + for (const Clock *clk : sorted_clks) { + report_->reportLine("Clock %s", 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_->reportBlankLine(); + } +} + +void +ClkSkews::reportClkSkew(ClkSkew &clk_skew, + int digits) +{ + Unit *time_unit = units_->timeUnit(); + Path *src_path = clk_skew.srcPath(); + Path *tgt_path = clk_skew.tgtPath(); + float 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; + 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()); + if (src_internal_clk_latency != 0.0) + report_->reportLine("%7s 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)->to_string().c_str()); + if (tgt_internal_clk_latency != 0.0) + report_->reportLine("%7s 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", + time_unit->asString(delayAsFloat(-clk_skew.crpr(this)), + digits)); + report_->reportLine("--------------"); + report_->reportLine("%7s %s skew", + time_unit->asString(clk_skew.skew(), digits), + src_path->minMax(this) == MinMax::max() ? "setup" : "hold"); +} + +float +ClkSkews::findWorstClkSkew(const SceneSeq &scenes, + const SetupHold *setup_hold, + bool include_internal_latency) +{ + ConstClockSeq clks; + 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(); + if (abs(skew) > abs(worst_skew)) + worst_skew = skew; + } + return worst_skew; +} + +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()) + return; + + skews_.clear(); + clks_ = clks; + 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()) { + if (hasClkPaths(src_vertex)) { + dispatch_queue_->dispatch([this, src_vertex, &partial_skews](int i) { + findClkSkewFrom(src_vertex, partial_skews[i]); + }); + } + } + 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]) { + 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()); + itr = result.first; + // Copy array elements + for (int setup_hold_idx : SetupHold::rangeIndex()) + itr->second[setup_hold_idx] = partial_skew[setup_hold_idx]; + } + else { + // Update existing entry + 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 (abs(partial_skew1) > abs(final_skew1) + || (fuzzyEqual(abs(partial_skew1), abs(final_skew1)) + // Break ties based on source/target path names. + && ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, this))) + final_skew = partial_skew_val; + } + } + } + } + } + else { + for (Vertex *src_vertex : graph_->regClkVertices()) { + if (hasClkPaths(src_vertex)) + findClkSkewFrom(src_vertex, skews_); + } + } +} + +bool +ClkSkews::hasClkPaths(Vertex *vertex) +{ + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + const Clock *path_clk = path->clock(this); + if (clk_set_.contains(path_clk)) + return true; + } + return false; +} + +void +ClkSkews::findClkSkewFrom(Vertex *src_vertex, + ClkSkewMap &skews) +{ + VertexOutEdgeIterator edge_iter(src_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + 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(); + findClkSkewFrom(src_vertex, q_vertex, src_rf, skews); + } + } +} + +void +ClkSkews::findClkSkewFrom(Vertex *src_vertex, + Vertex *q_vertex, + const RiseFallBoth *src_rf, + ClkSkewMap &skews) +{ + VertexSet endpoints = findFanout(q_vertex); + for (Vertex *end : endpoints) { + VertexInEdgeIterator edge_iter(end, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + 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); + } + } + } +} + +void +ClkSkews::findClkSkew(Vertex *src_vertex, + 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_.contains(src_clk) + && scenes_set_.contains(src_scene)) { + const MinMax *tgt_min_max = src_path->minMax(this)->opposite(); + 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; + } + } + } + } +} + +VertexSet +ClkSkews::findFanout(Vertex *from) +{ + VertexSet endpoints = makeVertexSet(this); + std::unordered_set visited; + findFanout1(from, visited, endpoints); + return endpoints; +} + +void +ClkSkews::findFanout1(Vertex *from, + std::unordered_set &visited, + VertexSet &endpoints) +{ + visited.insert(from); + if (from->hasChecks()) + endpoints.insert(from); + if (fanout_pred_.searchFrom(from)) { + VertexOutEdgeIterator edge_iter(from, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to = edge->to(graph_); + if (fanout_pred_.searchThru(edge) + && fanout_pred_.searchTo(to) + // Do not revisit downstream fanout cones. + && visited.insert(to).second) + findFanout1(to, visited, endpoints); + } + } +} + +//////////////////////////////////////////////////////////////// + ClkSkew::ClkSkew() : src_path_(nullptr), tgt_path_(nullptr), @@ -59,9 +375,9 @@ ClkSkew::ClkSkew() : } ClkSkew::ClkSkew(Path *src_path, - Path *tgt_path, + Path *tgt_path, bool include_internal_latency, - StaState *sta) : + StaState *sta) : src_path_(src_path), tgt_path_(tgt_path), include_internal_latency_(include_internal_latency) @@ -167,319 +483,6 @@ ClkSkew::srcTgtPathNameLess(ClkSkew &clk_skew1, && stringEqual(tgt_path1, tgt_path2)); } - -//////////////////////////////////////////////////////////////// - -ClkSkews::ClkSkews(StaState *sta) : - StaState(sta), - corner_(nullptr), - include_internal_latency_(true), - fanout_pred_(sta) -{ -} - -void -ClkSkews::clear() -{ - skews_.clear(); -} - -void -ClkSkews::reportClkSkew(ConstClockSeq &clks, - const Corner *corner, - const SetupHold *setup_hold, - bool include_internal_latency, - int digits) -{ - findClkSkew(clks, corner, 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()); - - for (const Clock *clk : sorted_clks) { - report_->reportLine("Clock %s", 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_->reportBlankLine(); - } -} - -void -ClkSkews::reportClkSkew(ClkSkew &clk_skew, - int digits) -{ - Unit *time_unit = units_->timeUnit(); - Path *src_path = clk_skew.srcPath(); - Path *tgt_path = clk_skew.tgtPath(); - float 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; - 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()); - if (src_internal_clk_latency != 0.0) - report_->reportLine("%7s 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)->to_string().c_str()); - if (tgt_internal_clk_latency != 0.0) - report_->reportLine("%7s 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", - time_unit->asString(delayAsFloat(-clk_skew.crpr(this)), - digits)); - report_->reportLine("--------------"); - report_->reportLine("%7s %s skew", - time_unit->asString(clk_skew.skew(), digits), - src_path->minMax(this) == MinMax::max() ? "setup" : "hold"); -} - -float -ClkSkews::findWorstClkSkew(const Corner *corner, - 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); - 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)) - worst_skew = skew; - } - return worst_skew; -} - -void -ClkSkews::findClkSkew(ConstClockSeq &clks, - const Corner *corner, - bool include_internal_latency) -{ - if (corner == corner_ - && include_internal_latency == include_internal_latency_ - && clks == clks_ - && !skews_.empty()) - return; - - skews_.clear(); - clks_ = clks; - corner_ = corner; - include_internal_latency_ = include_internal_latency; - - clk_set_.clear(); - for (const Clock *clk : clks) - clk_set_.insert(clk); - - if (thread_count_ > 1) { - 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) { - findClkSkewFrom(src_vertex, partial_skews[i]); - }); - } - } - 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]) { - 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()); - itr = result.first; - // Copy array elements - for (int setup_hold_idx : SetupHold::rangeIndex()) - itr->second[setup_hold_idx] = partial_skew[setup_hold_idx]; - } else { - // Update existing entry - 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 (abs(partial_skew1) > abs(final_skew1) - || (fuzzyEqual(abs(partial_skew1), abs(final_skew1)) - // Break ties based on source/target path names. - && ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, this))) - final_skew = partial_skew_val; - } - } - } - } - } - else { - for (Vertex *src_vertex : *graph_->regClkVertices()) { - if (hasClkPaths(src_vertex)) - findClkSkewFrom(src_vertex, skews_); - } - } -} - -bool -ClkSkews::hasClkPaths(Vertex *vertex) -{ - VertexPathIterator path_iter(vertex, this); - 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()) - return true; - } - return false; -} - -void -ClkSkews::findClkSkewFrom(Vertex *src_vertex, - ClkSkewMap &skews) -{ - VertexOutEdgeIterator edge_iter(src_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - 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(); - findClkSkewFrom(src_vertex, q_vertex, src_rf, skews); - } - } -} - -void -ClkSkews::findClkSkewFrom(Vertex *src_vertex, - Vertex *q_vertex, - const RiseFallBoth *src_rf, - ClkSkewMap &skews) -{ - VertexSet endpoints = findFanout(q_vertex); - for (Vertex *end : endpoints) { - VertexInEdgeIterator edge_iter(end, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - 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); - } - } - } -} - -void -ClkSkews::findClkSkew(Vertex *src_vertex, - 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(); - 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(); - 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; - } - } - } - } - } -} - -VertexSet -ClkSkews::findFanout(Vertex *from) -{ - VertexSet endpoints(graph_); - UnorderedSet visited; - findFanout1(from, visited, endpoints); - return endpoints; -} - -void -ClkSkews::findFanout1(Vertex *from, - UnorderedSet &visited, - VertexSet &endpoints) -{ - visited.insert(from); - if (from->hasChecks()) - endpoints.insert(from); - if (fanout_pred_.searchFrom(from)) { - VertexOutEdgeIterator edge_iter(from, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *to = edge->to(graph_); - if (fanout_pred_.searchThru(edge) - && fanout_pred_.searchTo(to) - // Do not revisit downstream fanout cones. - && visited.insert(to).second) - findFanout1(to, visited, endpoints); - } - } -} - //////////////////////////////////////////////////////////////// FanOutSrchPred::FanOutSrchPred(const StaState *sta) : @@ -488,10 +491,11 @@ FanOutSrchPred::FanOutSrchPred(const StaState *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 f59d03dc..cedefa8a 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 19a7e5df..e69de29b 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 5420f720..c2226541 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 e26353aa..379608c1 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 66f9b553..f45f2391 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) + TimingSense from_sense, + const RiseFallBoth *clk_rf, + bool edge_triggered, + bool latches, + SearchPred &clk_pred, + VertexSet &visited_vertices, + const Mode *mode) { - if (!visited_vertices.hasKey(from_vertex) - && clk_pred.searchFrom(from_vertex)) { + 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 @@ protected: 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 @@ private: 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 @@ private: 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 @@ private: 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 f9820f9c..96cd5062 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 804cee9d..e7fa73fc 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 8f63307d..6cec6bc4 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 e002c4c7..66b8cd14 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 0f69f5f0..b3da5bbd 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 73ffa95c..3c113c51 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 689e1685..864904e5 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 69273479..02ef86e5 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 fb9c1af9..aab99a00 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 e20cf407..40e27ae2 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 0f9c8ea9..bf929526 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 babd247a..8184c46e 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 00000000..80e5b5fd --- /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 bf8fc2de..40d96baf 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(); +} + +DcalcAPIndex +Path::dcalcAnalysisPtIndex(const StaState *sta) const +{ + return scene(sta)->dcalcAnalysisPtIndex(minMax(sta)); } PathAPIndex Path::pathAnalysisPtIndex(const StaState *sta) const { - return pathAnalysisPt(sta)->index(); -} - -DcalcAnalysisPt * -Path::dcalcAnalysisPt(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,35 +711,12 @@ Path::lessAll(const Path *path1, //////////////////////////////////////////////////////////////// VertexPathIterator::VertexPathIterator(Vertex *vertex, - const StaState *sta) : + const StaState *sta) : search_(sta->search()), - filtered_(false), + scene_(nullptr), + min_max_(nullptr), rf_(nullptr), - path_ap_(nullptr), - 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(); - } -} - -// 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) : - search_(sta->search()), - filtered_(true), - rf_(rf), - path_ap_(path_ap), - min_max_(nullptr), + filtered_(false), paths_(vertex->paths()), path_count_(0), path_index_(0), @@ -733,14 +730,15 @@ VertexPathIterator::VertexPathIterator(Vertex *vertex, } VertexPathIterator::VertexPathIterator(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max, - const StaState *sta) : + const Scene *scene, + const MinMax *min_max, + const RiseFall *rf, + const StaState *sta) : search_(sta->search()), - filtered_(true), - rf_(rf), - path_ap_(nullptr), + scene_(scene), min_max_(min_max), + rf_(rf), + filtered_(true), 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 476a8649..e69de29b 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 12b539d4..2a7af024 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 c3a1189a..7411b7d2 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 @@ private: }; 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 @@ private: 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 @@ private: 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 @@ private: }; 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 1f13f1e9..f2f68cd8 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 170c280a..6f77a595 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; + 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); + // 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 3ce7443a..6ac127f0 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; } -void -PathGroups::pushGroupPathEnds(PathEndSeq &path_ends) +GroupPath * +PathGroups::groupPathTo(const PathEnd *path_end, + const StaState *sta) { - for (auto min_max : MinMax::range()) { + 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::pushEnds(PathEndSeq &path_ends) +{ + 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 @@ public: 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 @@ public: private: void visitPathEnd(PathEnd *path_end, - PathGroup *group); + PathGroup *group); int endpoint_path_count_; PathGroups *path_groups_; @@ -714,7 +744,7 @@ private: }; 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 @@ public: 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 ad63155c..07eddc0a 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 @@ private: }; 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); + 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, 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); - 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 bb3853b2..45f02d08 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 6b991557..9122a805 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; + // 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; - } - } - } + 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 + 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 { - 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 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 b9f4364b..39c8b1a5 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 00000000..154eab34 --- /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 8a5f69a3..be52ad10 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; +}; + +SearchThru::SearchThru(const StaState *sta) : + EvalPred(sta) { } bool -DynLoopSrchPred::loopEnabled(Edge *edge, - bool dynamic_loop_breaking_enabled, - const Graph *graph, - Search *search) +SearchThru::searchThru(Edge *edge, + const Mode *mode) const +{ + return EvalPred::searchThru(edge, mode) + && !edge->role()->isLatchDtoQ(); +} + +//////////////////////////////////////////////////////////////// + +// SearchAdj is mode independent. Search unless +// disabled to break combinational loop +// latch D->Q edge +// timing check edge +// dynamic loop breaking pending tags +class SearchAdj : public SearchPred +{ +public: + 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_; + +}; + +SearchAdj::SearchAdj(TagGroupBldr *tag_bldr, + const StaState *sta) : + SearchPred(sta), + tag_bldr_(tag_bldr), + sta_(sta) +{ +} + +bool +SearchAdj::searchFrom(const Vertex * /* from_vertex */, + const Mode *) const +{ + return true; +} + +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 +SearchAdj::loopEnabled(Edge *edge) const { return !edge->isDisabledLoop() - || (dynamic_loop_breaking_enabled - && hasPendingLoopPaths(edge, graph, search)); + || (sta_->variables()->dynamicLoopBreaking() + && hasPendingLoopPaths(edge)); } bool -DynLoopSrchPred::hasPendingLoopPaths(Edge *edge, - const Graph *graph, - Search *search) +SearchAdj::hasPendingLoopPaths(Edge *edge) const { if (tag_bldr_ && tag_bldr_->hasLoopTag()) { - Corners *corners = search->corners(); + 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. - 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; + // 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; } -// EvalPred unless -// latch D->Q edge -class SearchThru : public EvalPred, public DynLoopSrchPred -{ -public: - SearchThru(TagGroupBldr *tag_bldr, - const StaState *sta); - virtual bool searchThru(Edge *edge); -}; - -SearchThru::SearchThru(TagGroupBldr *tag_bldr, - const StaState *sta) : - EvalPred(sta), - DynLoopSrchPred(tag_bldr) -{ -} - bool -SearchThru::searchThru(Edge *edge) +SearchAdj::searchTo(const Vertex * /* to_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); -} - -ClkArrivalSearchPred::ClkArrivalSearchPred(const StaState *sta) : - EvalPred(sta) -{ -} - -bool -ClkArrivalSearchPred::searchThru(Edge *edge) -{ - const TimingRole *role = edge->role(); - return (role->isWire() - || role == TimingRole::combinational()) - && EvalPred::searchThru(edge); + return true; } //////////////////////////////////////////////////////////////// Search::Search(StaState *sta) : - StaState(sta) -{ - init(sta); -} + StaState(sta), + unconstrained_paths_(false), + crpr_path_pruning_enabled_(true), + crpr_approx_missing_requireds_(true), -void -Search::init(StaState *sta) + 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) + const Mode *mode, + 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 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,41 +1677,63 @@ 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 Search::findInputDrvrVertices(VertexSet &vertices) { @@ -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) + Vertex *vertex, + const Mode *mode, + TagGroupBldr *tag_bldr) { - 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) -{ - 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) + Path *from_path) { - 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); + 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) + 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) { - 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); + 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 TimingArc *arc, + const Edge *edge, + bool is_clk, + const MinMax *min_max, + DcalcAPIndex dcalc_ap, + const Sdc *sdc) { - 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); + 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,80 +3228,65 @@ 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 +Search::clocks(const Pin *pin, + const Mode *mode) const { ClockSet clks; - clocks(vertex, clks); + Vertex *vertex; + Vertex *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + clocks(vertex, mode, clks); + if (bidirect_drvr_vertex) + clocks(bidirect_drvr_vertex, mode, clks); + return clks; +} + +ClockSet +Search::clocks(const Vertex *vertex, + const Mode *mode) const +{ + ClockSet clks; + clocks(vertex, mode, clks); return clks; } void Search::clocks(const Vertex *vertex, - // Return value. - ClockSet &clks) const + const Mode *mode, + // 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)) + if (path->isClock(this) + && path->mode(this) == mode) clks.insert(const_cast(path->clock(this))); } } -ClockSet -Search::clocks(const Pin *pin) const -{ - ClockSet clks; - Vertex *vertex; - Vertex *bidirect_drvr_vertex; - graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - if (vertex) - clocks(vertex, clks); - if (bidirect_drvr_vertex) - clocks(bidirect_drvr_vertex, clks); - return clks; -} - -bool -Search::isClock(const Vertex *vertex) const -{ - TagGroup *tag_group = tagGroup(vertex); - if (tag_group) - return tag_group->hasClkTag(); - else - return false; -} - -bool -Search::isGenClkSrc(const Vertex *vertex) const -{ - TagGroup *tag_group = tagGroup(vertex); - if (tag_group) - return tag_group->hasGenClkSrcTag(); - else - return false; -} - //////////////////////////////////////////////////////////////// void @@ -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_) - && ((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())); + for (const Mode *mode : modes) { + if (isEndpoint(vertex, search_thru_, mode)) + return true; + } + return false; } bool -Search::hasEnabledChecks(Vertex *vertex) const +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, 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 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 @@ protected: }; 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 @@ protected: }; 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 3e2e761a..8978d530 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,23 +491,40 @@ 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); +} + +void +report_slack_wrt_clks(const Pin *pin, + const Scene *scene, + int digits) +{ + Sta::sta()->reportSlackWrtClks(pin, scene, digits); +} + +//////////////////////////////////////////////////////////////// + +void +report_clk_skew(ConstClockSeq clks, + const SceneSeq scenes, + const SetupHold *setup_hold, + bool include_internal_latency, + int digits) +{ + Sta::sta()->reportClkSkew(clks, scenes, setup_hold, + include_internal_latency, digits); } float @@ -546,102 +536,50 @@ worst_clk_skew_cmd(const SetupHold *setup_hold, //////////////////////////////////////////////////////////////// -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_latency(ConstClockSeq clks, + const SceneSeq scenes, + bool include_internal_latency, + int digits) { - Sta::sta()->reportMpwChecks(checks, verbose); -} - -void -report_mpw_check(MinPulseWidthCheck *check, - bool verbose) -{ - Sta::sta()->reportMpwCheck(check, verbose); + Sta::sta()->reportClkLatency(clks, scenes, include_internal_latency, digits); } //////////////////////////////////////////////////////////////// -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) +report_min_pulse_width_checks(const Net *net, + size_t max_count, + bool violations, + bool verbose, + const SceneSeq scenes) { - Sta::sta()->reportChecks(checks, verbose); -} - -void -report_min_period_check(MinPeriodCheck *check, - bool verbose) -{ - Sta::sta()->reportCheck(check, verbose); + return Sta::sta()->reportMinPulseWidthChecks(net, max_count, violations, + verbose, scenes); } //////////////////////////////////////////////////////////////// -MaxSkewCheckSeq & -max_skew_violations() +void +report_min_period_checks(const Net *net, + size_t max_count, + bool violations, + bool verbose, + const SceneSeq scenes) { - return Sta::sta()->maxSkewViolations(); + Sta::sta()->reportMinPeriodChecks(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_max_skew_checks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq scenes) { - Sta::sta()->reportChecks(checks, verbose); -} - -void -report_max_skew_check(MaxSkewCheck *check, - bool verbose) -{ - 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() +max_fanout_check_slack() +{ + return max_fanout_min_slack(); +} + +float +max_fanout_min_slack_limit() { 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 limit;; } -void -report_fanout_limit_short_header() +// Deprecated 11/16/2025 +float +max_fanout_check_limit() { - Sta::sta()->reportFanoutLimitShortHeader(); -} - -void -report_fanout_limit_short(Pin *pin, - const MinMax *min_max) -{ - Sta::sta()->reportFanoutLimitShort(pin, min_max); -} - -void -report_fanout_limit_verbose(Pin *pin, - const MinMax *min_max) -{ - Sta::sta()->reportFanoutLimitVerbose(pin, min_max); + 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() -{ - Sta::sta()->reportCapacitanceLimitShortHeader(); -} - -void -report_capacitance_limit_short(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - Sta::sta()->reportCapacitanceLimitShort(pin, corner, min_max); -} - -void -report_capacitance_limit_verbose(Pin *pin, - const Corner *corner, - const MinMax *min_max) -{ - Sta::sta()->reportCapacitanceLimitVerbose(pin, corner, min_max); -} - //////////////////////////////////////////////////////////////// void write_timing_model_cmd(const char *lib_name, const char *cell_name, const char *filename, - const Corner *corner) + const Scene *scene) { - Sta::sta()->writeTimingModel(lib_name, cell_name, filename, corner); + Sta::sta()->writeTimingModel(lib_name, cell_name, filename, scene); } //////////////////////////////////////////////////////////////// void -define_corners_cmd(StringSet *corner_names) +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 = Sta::sta(); - sta->makeCorners(corner_names); - delete corner_names; -} - -Corner * -cmd_corner() -{ - return Sta::sta()->cmdCorner(); + sta->makeScene(name, mode_name, + liberty_min_files, liberty_max_files, + spef_min_file, spef_max_file); } void -set_cmd_corner(Corner *corner) +define_scenes_cmd(StringSeq *scene_names) { - Sta::sta()->setCmdCorner(corner); + Sta *sta = Sta::sta(); + sta->makeScenes(scene_names); + delete scene_names; } -Corner * -find_corner(const char *corner_name) +Scene * +cmd_scene() { - return Sta::sta()->findCorner(corner_name); + return Sta::sta()->cmdScene(); } -Corners * -corners() +void +set_cmd_scene(Scene *scene) { - return Sta::sta()->corners(); + Sta::sta()->setCmdScene(scene); +} + +const SceneSeq +scenes() +{ + Sta *sta = Sta::sta(); + return sta->scenes(); +} + +Scene * +find_scene(const char *scene_name) +{ + return Sta::sta()->findScene(scene_name); +} + +SceneSeq +find_scenes_matching(std::string scene_name) +{ + return Sta::sta()->findScenes(scene_name); +} + +SceneSeq +find_mode_scenes_matching(std::string scene_name, + ModeSeq modes) +{ + return Sta::sta()->findScenes(scene_name, modes); } bool -multi_corner() +multi_scene() { - return Sta::sta()->multiCorner(); + return Sta::sta()->multiScene(); +} + +ClockSeq +get_scene_clocks(SceneSeq scenes) +{ + 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 ba5acba4..a9c1312d 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 9cb0401e..da7d5236 100644 --- a/search/SearchPred.cc +++ b/search/SearchPred.cc @@ -30,59 +30,113 @@ #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) { } -bool -SearchPred0::searchFrom(const Vertex *from_vertex) +void +SearchPred::copyState(const StaState *sta) { - return !(from_vertex->isDisabledConstraint() - || from_vertex->isConstant()); + sta_ = sta; } bool -SearchPred0::searchThru(Edge *edge) +SearchPred::searchFrom(const Vertex *from_vertex) const +{ + for (const Mode *mode : sta_->modes()) { + if (searchFrom(from_vertex, mode)) + return true; + } + return false; +} + +bool +SearchPred::searchThru(Edge *edge) const +{ + for (const Mode *mode : sta_->modes()) { + if (searchThru(edge, mode)) + return true; + } + return false; +} + +bool +SearchPred::searchTo(const Vertex *to_vertex) const +{ + for (const Mode *mode : sta_->modes()) { + if (searchTo(to_vertex, mode)) + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////// + +SearchPred0::SearchPred0(const StaState *sta) : + SearchPred(sta) +{ +} + +bool +SearchPred0::searchFrom(const Vertex *from_vertex, + const Mode *mode) const +{ + 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)); +} + +bool +SearchPred0::searchThru(Edge *edge, + const Mode *mode) 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)); + 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 -SearchPred0::searchTo(const Vertex *to_vertex) +SearchPred0::searchTo(const Vertex *to_vertex, + const Mode *mode) const { - return !to_vertex->isConstant(); + return !mode->sim()->isConstant(to_vertex); } //////////////////////////////////////////////////////////////// @@ -93,123 +147,109 @@ SearchPred1::SearchPred1(const StaState *sta) : } bool -SearchPred1::searchThru(Edge *edge) +SearchPred1::searchThru(Edge *edge, + const Mode *mode) const { - return SearchPred0::searchThru(edge) + return SearchPred0::searchThru(edge, mode) && !edge->isDisabledLoop(); } //////////////////////////////////////////////////////////////// -SearchPred2::SearchPred2(const StaState *sta) : - SearchPred1(sta) -{ -} - -bool -SearchPred2::searchThru(Edge *edge) -{ - return SearchPred1::searchThru(edge) - && !edge->role()->isTimingCheck(); -} - -//////////////////////////////////////////////////////////////// - -SearchPredNonLatch2::SearchPredNonLatch2(const StaState *sta) : - SearchPred2(sta) -{ -} - -bool -SearchPredNonLatch2::searchThru(Edge *edge) -{ - return SearchPred2::searchThru(edge) - && !sta_->latches()->isLatchDtoQ(edge); -} - -//////////////////////////////////////////////////////////////// - -SearchPredNonReg2::SearchPredNonReg2(const StaState *sta) : - SearchPred2(sta) -{ -} - -bool -SearchPredNonReg2::searchThru(Edge *edge) -{ - 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(); -} - -//////////////////////////////////////////////////////////////// - ClkTreeSearchPred::ClkTreeSearchPred(const StaState *sta) : - SearchPred1(sta) + SearchPred(sta) { } bool -ClkTreeSearchPred::searchThru(Edge *edge) +ClkTreeSearchPred::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 +ClkTreeSearchPred::searchThru(Edge *edge, + const Mode *mode) 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); + 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::searchThruAllow(const TimingRole *role) const +{ + 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 959b088b..70b6384e 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,144 +759,204 @@ 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); + 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::setIsDisabledCond(Edge *edge, + bool disabled) +{ + if (!disabled) + edge_disabled_cond_set_.erase(edge); + else { + edge_disabled_cond_set_.insert(edge); + edge->setHasDisabledCond(true); } - instances_with_const_pins_.clear(); +} + +bool +Sim::isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin) +{ + bool is_disabled; + FuncExpr *disable_cond; + isDisabledCond(edge, inst, from_pin, to_pin, is_disabled, disable_cond); + return is_disabled; } void -Sim::clearInstSimValues(const Instance *inst) +Sim::isDisabledCond(Edge *edge, + const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond) { - 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); + TimingArcSet *arc_set = edge->timingArcSet(); + FuncExpr *cond = arc_set->cond(); + if (cond) { + LogicValue cond_value = evalExpr(cond, inst); + disable_cond = cond; + is_disabled = (cond_value == LogicValue::zero); + } + else { + // 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); + is_disabled = false; + for (TimingArcSet *cond_set : cell->timingArcSets(from_port, to_port)) { + FuncExpr *cond = cond_set->cond(); + if (cond && evalExpr(cond, inst) == LogicValue::one) { + disable_cond = cond; + is_disabled = true; + break; + } + } } - delete pin_iter; } -// Annotate graph edges disabled by constant values. -void -Sim::annotateGraphEdges() +bool +Sim::isDisabledMode(Edge *edge, + const Instance *inst) { - for (const Instance *inst : instances_to_annotate_) - annotateVertexEdges(inst, true); + bool is_disabled; + FuncExpr *disable_cond; + isDisabledMode(edge, inst, is_disabled, disable_cond); + return is_disabled; } void -Sim::annotateVertexEdges(const Instance *inst, - bool annotate) +Sim::isDisabledMode(Edge *edge, + const Instance *inst, + // Return values. + bool &is_disabled, + FuncExpr *&disable_cond) { - debugPrint(debug_, "sim", 4, "annotate %s %s", - network_->pathName(inst), - annotate ? "true" : "false"); + // Default values. + 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) { + 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 = 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) - annotateVertexEdges(inst, pin, vertex, annotate); + findDisabledEdges(inst, pin, vertex); } delete pin_iter; } void -Sim::annotateVertexEdges(const Instance *inst, - const Pin *pin, - Vertex *vertex, - bool annotate) +Sim::findDisabledEdges(const Instance *inst, + const Pin *pin, + Vertex *vertex) { bool fanin_disables_changed = false; VertexInEdgeIterator edge_iter(vertex, graph_); @@ -819,148 +967,36 @@ Sim::annotateVertexEdges(const Instance *inst, 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); + // 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); - 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 (sense != simTimingSense(edge)) { + setSimTimingSense(edge, 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 (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); + observer_->fanoutEdgesChangeAfter(from_vertex->pin()); } } 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) -{ - bool is_disabled; - FuncExpr *disable_cond; - isCondDisabled(edge, inst, from_pin, to_pin, network, sim, - 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) -{ - TimingArcSet *arc_set = edge->timingArcSet(); - FuncExpr *cond = arc_set->cond(); - if (cond) { - LogicValue cond_value = sim->evalExpr(cond, inst); - disable_cond = cond; - is_disabled = (cond_value == LogicValue::zero); - } - else { - // 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); - 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; - } - } - } -} - -bool -isModeDisabled(Edge *edge, - const Instance *inst, - const Network *network, - Sim *sim) -{ - bool is_disabled; - FuncExpr *disable_cond; - isModeDisabled(edge, inst, network, sim, - 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) -{ - // Default values. - 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) { - 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; - } - } - } - } - } - } - } - } - } + observer_->faninEdgesChangeAfter(vertex->pin()); } } // namespace diff --git a/search/Sim.hh b/search/Sim.hh index 958e9d82..84117094 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 eb389646..b79a74c9 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) -{ - return corners_->findCorner(corner_name); -} - -bool -Sta::multiCorner() -{ - return corners_->multiCorner(); -} - -// Init one corner named "default". +// Init one scene named "default". void -Sta::makeCorners() +Sta::makeDefaultScene() { - corners_ = new Corners(this); - StringSet corner_names; - corner_names.insert("default"); - corners_->makeCorners(&corner_names); - cmd_corner_ = corners_->findCorner(0); - sdc_->makeCornersAfter(corners_); + 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]; } +// define_corners (before read_liberty). void -Sta::makeCorners(StringSet *corner_names) +Sta::makeScenes(StringSeq *scene_names) { - 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); + 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(); - sdc_->makeCornersAfter(corners_); -} - -Corner * -Sta::cmdCorner() const -{ - return cmd_corner_; + if (graph_) + graph_->makeSceneAfter(); } void -Sta::setCmdCorner(Corner *corner) +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) { - cmd_corner_ = corner; + 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 +{ + 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; +} + +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; + } + } + delete lib_iter; + return nullptr; +} + +void +Sta::updateLibertyScenes() +{ + 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_); + } + } + } +} + +Scene * +Sta::cmdScene() const +{ + return cmd_scene_; +} + +void +Sta::setCmdScene(Scene *scene) +{ + cmd_scene_ = scene; +} + +SceneSeq +Sta::makeSceneSeq(Scene *scene) const +{ + 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::pinSlack(const Pin *pin, +Sta::slack(Vertex *vertex, + const MinMax *min_max) +{ + return slack(vertex, RiseFallBoth::riseFall(), scenes_, min_max); +} + +Slack +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; -} - -Slack -Sta::vertexSlack(Vertex *vertex, - const RiseFall *rf, - const MinMax *min_max) -{ - 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; -} - -Slack -Sta::vertexSlack(Vertex *vertex, - const RiseFall *rf, - const PathAnalysisPt *path_ap) -{ - findRequired(vertex); - return vertexSlack1(vertex, rf, clk_edge_wildcard, path_ap); -} - -Slack -Sta::vertexSlack(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap) -{ - findRequired(vertex); - return vertexSlack1(vertex, rf, clk_edge, path_ap); -} - -Slack -Sta::vertexSlack1(Vertex *vertex, - const RiseFall *rf, - const ClockEdge *clk_edge, - const PathAnalysisPt *path_ap) -{ - 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; - } - return slack; + reportDelaysWrtClks(pin, scene, digits, + [] (const Path *path) { + return path->arrival(); + }); } void -Sta::vertexSlacks(Vertex *vertex, - Slack (&slacks)[RiseFall::index_count][MinMax::index_count]) +Sta::reportRequiredWrtClks(const Pin *pin, + const Scene *scene, + int digits) +{ + reportDelaysWrtClks(pin, scene, digits, + [] (const Path *path) { + return path->required(); + }); +} + +void +Sta::reportSlackWrtClks(const Pin *pin, + const Scene *scene, + int digits) +{ + reportDelaysWrtClks(pin, scene, digits, + [this] (const Path *path) { + return path->slack(this); + }); +} + +void +Sta::reportDelaysWrtClks(const Pin *pin, + const Scene *scene, + int digits, + PathDelayFunc get_path_delay) +{ + 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); +} + +void +Sta::reportDelaysWrtClks(Vertex *vertex, + 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(); + 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); } } - VertexPathIterator path_iter(vertex, this); +} + +void +Sta::reportDelaysWrtClks(Vertex *vertex, + const ClockEdge *clk_edge, + const Scene *scene, + int digits, + PathDelayFunc 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, 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()); + } +} + +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 @@ public: 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 @@ protected: 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 @@ public: 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) { - if (sdc_->haveClkSlewLimits()) + 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() +{ + 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_slew_limits_->checkSlew(pin, corner, min_max, check_clks, - corner1, rf, slew, limit, slack); + check_slews_->check(pin, scenes, min_max, check_clks, + slew, limit, slack, rf, scene); +} + +size_t +Sta::maxSlewViolationCount() +{ + 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()); -} -void -Sta::reportFanoutLimitShort(Pin *pin, - const MinMax *min_max) -{ - float fanout, limit, slack; - check_fanout_limits_->checkFanout(pin, min_max, - fanout, limit, slack); - report_path_->reportLimitShort(report_path_->fieldFanout(), - pin, fanout, limit, slack); -} - -void -Sta::reportFanoutLimitVerbose(Pin *pin, - const MinMax *min_max) -{ - float fanout, limit, slack; - check_fanout_limits_->checkFanout(pin, min_max, - fanout, limit, slack); + for (const FanoutCheck &check : checks) { + if (verbose) report_path_->reportLimitVerbose(report_path_->fieldFanout(), - pin, nullptr, fanout, - limit, slack, nullptr, min_max); + 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::checkFanoutPreamble() +{ + if (check_fanouts_ == nullptr) + makeCheckFanouts(); + ensureLevelized(); + for (Mode *mode : modes_) { + mode->sim()->ensureConstantsPropagated(); + mode->clkNetwork()->ensureClkNetwork(); + } +} + +size_t +Sta::fanoutViolationCount(const MinMax *min_max, + const ModeSeq &modes) +{ + 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) + const Mode *&mode) { - checkFanoutLimitPreamble(); - PinSeq pins = check_fanout_limits_->checkFanoutLimits(nullptr, false, MinMax::max()); + 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()); -} - -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) -{ - const Corner *corner1; - const RiseFall *rf1; - float capacitance1, limit1, slack1; - check_capacitance_limits_->checkCapacitance(pin, corner, min_max, - corner1, rf1, capacitance1, - limit1, slack1); + for (CapacitanceCheck &check : checks) { + if (verbose) report_path_->reportLimitVerbose(report_path_->fieldCapacitance(), - pin, rf1, capacitance1, - limit1, slack1, corner1, min_max); + 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::checkCapacitancesPreamble(const SceneSeq &scenes) +{ + 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) { - check_capacitance_limits_->checkCapacitance(pin, corner, min_max, - corner1, rf, capacitance, - limit, slack); + 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() +{ + 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() +void +Sta::reportMaxSkewChecks(const Net *net, + size_t max_count, + bool violators, + bool verbose, + const SceneSeq &scenes) { maxSkewPreamble(); - return check_max_skews_->violations(); -} - -MaxSkewCheck * -Sta::maxSkewSlack() -{ - 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 c9eb7438..fca51792 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 27badadc..5e368d80 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 +Tag::minMax() const { - return pathAnalysisPt(sta)->pathMinMax(); -} - -PathAnalysisPt * -Tag::pathAnalysisPt(const StaState *sta) 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 e084bd29..1b24c190 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 2496ef11..93e0251e 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 88722d5f..2a02f2b9 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 b5a762d5..52859010 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 e61fccca..cad69282 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 c304fd6b..b23b8a9e 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 ae8bab8f..694ba7b3 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 @@ private: 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 @@ private: 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 @@ private: 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 bf4ab506..a6d014d1 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 f336bc8f..9ef7b05d 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 98995a44..3e55e294 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 727bf82b..e7adadbe 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 af15905b..7bf9674e 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 848c9987..c13a918b 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 c1a59889..094fc633 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 a2380ace..3eab6018 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 60c9cc2b..65d9d950 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 f4c0e384..e5b68051 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* { - $1 = tclListSetConstChar($input, interp); -} - %typemap(in) StringSet* { $1 = tclListSetConstChar($input, interp); } -%typemap(out) Corner* { - Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); - Tcl_SetObjResult(interp, obj); +%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) Corners* { +%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); - Corners *corners = $1; - for (Corner *corner : *corners) { - Tcl_Obj *obj = SWIG_NewInstanceObj(corner, SWIGTYPE_p_Corner, false); + 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) SceneSeq { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + 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 f5d73442..c5cc219d 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 ad47cdce..49028698 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 a019c295..336e7286 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 a0645c2b..c05e6a3b 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 64c906ff..dc99d32b 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 879b8a74..323145d3 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 00000000..e220a881 --- /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 19f4adfd..a79a9ab6 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 7edf1d3c..108b81b2 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 47a16002..5733375e 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 98c9c3cd..a4ac1d9c 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 a6a86f97..447a85ad 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 acf6906b..c1ed29ea 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 51e8ae79..7f70fbf7 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 1e892fe8..0fd4d548 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 16836d7a..ba46f808 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 b198c4a4..4f0062fe 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 b6c69e6e..f7158366 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 231634d6..261d143f 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 074030bd..864b09de 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 2dd32df9..8b1e49e8 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 413d03c4..f2d0955d 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 abc96eb3..4b21a426 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 00000000..c0746997 --- /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 6dd4fda4..fb52568f 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 67d51a4b..f5615d6c 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 22d0d303..79863547 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 e9b0d74a..16237331 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 dba068df..e048cecf 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 5188f9f2..a77a898b 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 6c7ec10f..5bc47a53 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 5e1744ef..c1485fea 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 f4a459f5..73e753de 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 530e9cf2..8572f71d 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 26ed54d8..e7ac21d3 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 @@ private: 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 @@ protected: }; 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 @@ private: 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 @@ public: 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 aca56682..00000000 --- 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 aa1dc207..60498c7d 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 dd856d7b..3ba02ba9 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 @@ protected: 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 @@ protected: 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();

bn;6VKLY#jMZB2pw< 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