diff --git a/CMakeLists.txt b/CMakeLists.txt index de2a90c7..a361a3be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,13 +24,12 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14) cmake_policy(SET CMP0086 NEW) endif() -project(STA VERSION 2.5.0 +project(STA VERSION 2.6.0 LANGUAGES CXX ) -option(USE_CUDD "Use CUDD BDD package") option(CUDD_DIR "CUDD BDD package directory") -option(USE_TCL_READLINE "Use TCL readliine package") +option(USE_TCL_READLINE "Use TCL readliine package" ON) option(USE_SANITIZE "Compile with santize address enabled") # Turn on to debug compiler args. @@ -69,7 +68,6 @@ set(STA_SOURCE dcalc/ArnoldiDelayCalc.cc dcalc/ArnoldiReduce.cc dcalc/CcsCeffDelayCalc.cc - dcalc/CcsSimDelayCalc.cc dcalc/DcalcAnalysisPt.cc dcalc/DelayCalc.cc dcalc/DelayCalcBase.cc @@ -195,8 +193,10 @@ set(STA_SOURCE search/VisitPathEnds.cc search/VisitPathGroupVertices.cc search/WorstSlack.cc - search/WritePathSpice.cc - search/WriteSpice.cc + + spice/WritePathSpice.cc + spice/WriteSpice.cc + spice/Xyce.cc power/Power.cc power/ReadVcdActivities.cc @@ -233,22 +233,22 @@ set(STA_TCL_FILES tcl/Util.tcl tcl/CmdArgs.tcl tcl/CmdUtil.tcl - tcl/Graph.tcl - tcl/Liberty.tcl - tcl/Link.tcl - tcl/Network.tcl - tcl/NetworkEdit.tcl tcl/Property.tcl - tcl/Sdc.tcl - tcl/Search.tcl tcl/Sta.tcl tcl/Splash.tcl tcl/Variables.tcl - tcl/WritePathSpice.tcl dcalc/DelayCalc.tcl + graph/Graph.tcl + liberty/Liberty.tcl + network/Link.tcl + network/Network.tcl + network/NetworkEdit.tcl parasitics/Parasitics.tcl power/Power.tcl + sdc/Sdc.tcl sdf/Sdf.tcl + search/Search.tcl + spice/WriteSpice.tcl verilog/Verilog.tcl ) @@ -258,8 +258,7 @@ set(STA_TCL_FILES # ################################################################ -# Earlier versions of flex use 'register' declarations that are deprecated -# in c++11 and illegal in c++17. +# Earlier versions of flex use 'register' declarations that are illegal in c++17. #find_package(FLEX 2.6.4) find_package(FLEX) find_package(BISON) @@ -371,30 +370,25 @@ find_package(Eigen3 REQUIRED) # # Locate CUDD bdd package. # -set(CUDD 0) -if (USE_CUDD) - find_library(CUDD_LIB - NAME cudd - PATHS ${CUDD_DIR} - PATH_SUFFIXES lib lib/cudd cudd/.libs - ) - if (CUDD_LIB) - message(STATUS "CUDD library: ${CUDD_LIB}") - get_filename_component(CUDD_LIB_DIR "${CUDD_LIB}" PATH) - get_filename_component(CUDD_LIB_PARENT1 "${CUDD_LIB_DIR}" PATH) - find_file(CUDD_HEADER cudd.h - PATHS ${CUDD_LIB_PARENT1} ${CUDD_LIB_PARENT1}/include ${CUDD_LIB_PARENT1}/include/cudd) - if (CUDD_HEADER) - get_filename_component(CUDD_INCLUDE "${CUDD_HEADER}" PATH) - message(STATUS "CUDD header: ${CUDD_HEADER}") - # Referenced by StaConfig.hh.cmake - set(CUDD 1) - else() - message(STATUS "CUDD header: not found") - endif() +find_library(CUDD_LIB + NAME cudd + PATHS ${CUDD_DIR} + PATH_SUFFIXES lib lib/cudd cudd/.libs + ) +if (CUDD_LIB) + message(STATUS "CUDD library: ${CUDD_LIB}") + get_filename_component(CUDD_LIB_DIR "${CUDD_LIB}" PATH) + get_filename_component(CUDD_LIB_PARENT1 "${CUDD_LIB_DIR}" PATH) + find_file(CUDD_HEADER cudd.h + PATHS ${CUDD_LIB_PARENT1} ${CUDD_LIB_PARENT1}/include ${CUDD_LIB_PARENT1}/include/cudd) + if (CUDD_HEADER) + get_filename_component(CUDD_INCLUDE "${CUDD_HEADER}" PATH) + message(STATUS "CUDD header: ${CUDD_HEADER}") else() - message(STATUS "CUDD library: not found") + message(STATUS "CUDD header: not found") endif() +else() + message(STATUS "CUDD library: not found") endif() if("${SSTA}" STREQUAL "") @@ -426,23 +420,24 @@ set_property(SOURCE ${STA_SWIG_FILE} PROPERTY SWIG_FLAGS -module sta -namespace -prefix sta - -I${STA_HOME}/tcl - -I${STA_HOME}/sdf - -I${STA_HOME}/dcalc - -I${STA_HOME}/parasitics - -I${STA_HOME}/power - -I${STA_HOME}/verilog + -I${STA_HOME} ) set(SWIG_FILES ${STA_HOME}/dcalc/DelayCalc.i + ${STA_HOME}/graph/Graph.i + ${STA_HOME}/liberty/Liberty.i + ${STA_HOME}/network/Network.i + ${STA_HOME}/network/NetworkEdit.i ${STA_HOME}/parasitics/Parasitics.i ${STA_HOME}/power/Power.i + ${STA_HOME}/sdc/Sdc.i ${STA_HOME}/sdf/Sdf.i + ${STA_HOME}/search/Search.i + ${STA_HOME}/spice/WriteSpice.i ${STA_HOME}/tcl/Exception.i - ${STA_HOME}/tcl/StaTcl.i ${STA_HOME}/tcl/StaTclTypes.i - ${STA_HOME}/tcl/NetworkEdit.i + ${STA_HOME}/util/Util.i ${STA_HOME}/verilog/Verilog.i ) @@ -463,7 +458,6 @@ set_source_files_properties(${STA_SWIG_CXX_FILE} # No simple way to modify the swig template that emits code full of warnings # so suppress them. COMPILE_OPTIONS "-Wno-cast-qual;-Wno-missing-braces;-Wno-deprecated-declarations" - INCLUDE_DIRECTORIES "${CUDD_INCLUDE}" ) target_link_libraries(sta_swig @@ -515,6 +509,7 @@ target_link_libraries(OpenSTA Eigen3::Eigen ${TCL_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} + ${CUDD_LIB} ) if (TCL_READLINE_LIBRARY) @@ -525,9 +520,7 @@ if (ZLIB_LIBRARIES) target_link_libraries(OpenSTA ${ZLIB_LIBRARIES}) endif() -if (CUDD_LIB) - target_link_libraries(OpenSTA ${CUDD_LIB}) -endif() +target_link_libraries(OpenSTA ) target_include_directories(OpenSTA PUBLIC @@ -559,7 +552,7 @@ target_compile_options(OpenSTA # Disable compiler specific extensions like gnu++11. set_target_properties(OpenSTA PROPERTIES CXX_EXTENSIONS OFF) -target_compile_features(OpenSTA PUBLIC cxx_std_11) +target_compile_features(OpenSTA PUBLIC cxx_std_17) ########################################################### # Executable diff --git a/README.md b/README.md index db16c0f0..e48cd86c 100644 --- a/README.md +++ b/README.md @@ -85,15 +85,15 @@ The build dependency versions are show below. Other versions may work, but these are the versions used for development. ``` - from Ubuntu Macos - 22.04.2 14.4.1 -cmake 3.10.2 3.24.2 3.29.2 -clang 9.1.0 15.0.0 -gcc 3.3.2 11.4.0 -tcl 8.4 8.6 8.6.6 -swig 1.3.28 4.1.0 4.2.1 -bison 1.35 3.8.2 3.8.2 -flex 2.5.4 2.6.4 2.6.4 + Ubuntu Macos + 22.04.2 14.5 +cmake 3.24.2 3.29.2 +clang 15.0.0 +gcc 11.4.0 +tcl 8.6 8.6.6 +swig 4.1.0 4.1.1 +bison 3.8.2 3.8.2 +flex 2.6.4 2.6.4 ``` Note that flex versions before 2.6.4 contain 'register' declarations that @@ -101,38 +101,47 @@ are illegal in c++17. External library dependencies: ``` - from Ubuntu Macos -eigen 3.4 .0 3.4.0 required -tclreadline 2.3.8 optional -libz 1.1.4 1.2.5 1.2.8 optional -cudd 2.4.1 3.0.0 optional + Ubuntu Macos license +eigen 3.4.0 3.4.0 MPL2 required +cudd 3.0.0 3.0.0 BSD required +tclreadline 2.3.8 2.3.8 BSD optional +zLib 1.2.5 1.2.8 zlib optional ``` The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html) links the GNU readline library to the TCL interpreter for command line editing On OSX, Homebrew does not support tclreadline, but the macports system does (see https://www.macports.org). To enable TCL readline support use the following -Cmake option: +Cmake option: See (https://tclreadline.sourceforge.net/) for TCL readline +documentation. To change the overly verbose default prompt, add something this +to your ~/.sta init file: ``` -cmake .. -DUSE_TCL_READLINE=ON +if { ![catch {package require tclreadline}] } { + proc tclreadline::prompt1 {} { + return "> " + } +} ``` The Zlib library is an optional. If CMake finds libz, OpenSTA can -read Verilog, SDF, SPF, and SPEF files compressed with gzip. +read Liberty, Verilog, SDF, SPF, and SPEF files compressed with gzip. CUDD is a binary decision diageram (BDD) package that is used to improve conditional timing arc handling. OpenSTA does not require it -to be installed. It is available +to be installed, but it improves constant propagation, power activity propagation +and spice netlist generation if it is installed. + +CUDD is available [here](https://www.davidkebo.com/source/cudd_versions/cudd-3.0.0.tar.gz) or [here](https://sourceforge.net/projects/cudd-mirror/). -Note that the file hierarchy of the CUDD installation changed with version 3.0. -Some changes to CMakeLists.txt are required to support older versions. +Use the CUDD_DIR option to set the install directory of the CUDD +library if it is not in one of the normal system install directories. -Use the USE_CUDD option to look for the cudd library. -Use the CUDD_DIR option to set the install directory if it is not in -one of the normal install directories. +``` +cmake -DCUDD_DIR=$HOME/stax/cudd-3.0.0 .." +``` When building CUDD you may use the `--prefix ` option to `configure` to install in a location other than the default (`/usr/local/lib`). diff --git a/app/Main.cc b/app/Main.cc index 9b45ae21..1a15e6c9 100644 --- a/app/Main.cc +++ b/app/Main.cc @@ -19,6 +19,7 @@ #include #include // exit +#include #include #if TCL_READLINE #include @@ -39,7 +40,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" { @@ -133,15 +133,15 @@ 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); } } bool exit_after_cmd_file = findCmdLineFlag(argc, argv, "-exit"); - if (argc > 2 || - (argc > 1 && argv[1][0] == '-')) { + if (argc > 2 + || (argc > 1 && argv[1][0] == '-')) { showUsage(argv[0], init_filename); exit(1); } diff --git a/app/StaApp.i b/app/StaApp.i index fde2e64d..dfb0f3b7 100644 --- a/app/StaApp.i +++ b/app/StaApp.i @@ -16,12 +16,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -%include "Exception.i" -%include "StaTclTypes.i" -%include "StaTcl.i" -%include "Verilog.i" -%include "NetworkEdit.i" -%include "Sdf.i" -%include "DelayCalc.i" -%include "Parasitics.i" -%include "Power.i" +%include "tcl/Exception.i" +%include "tcl/StaTclTypes.i" +%include "dcalc/DelayCalc.i" +%include "graph/Graph.i" +%include "liberty/Liberty.i" +%include "network/Network.i" +%include "network/NetworkEdit.i" +%include "parasitics/Parasitics.i" +%include "power/Power.i" +%include "sdc/Sdc.i" +%include "sdf/Sdf.i" +%include "search/Search.i" +%include "util/Util.i" +%include "spice/WriteSpice.i" +%include "verilog/Verilog.i" diff --git a/app/StaMain.cc b/app/StaMain.cc index f78dfa3e..5c1be7e9 100644 --- a/app/StaMain.cc +++ b/app/StaMain.cc @@ -51,10 +51,11 @@ findCmdLineFlag(int &argc, for (int i = 1; i < argc; i++) { char *arg = argv[i]; if (stringEq(arg, flag)) { - // remove flag from argv. + // Remove flag from argv. for (int j = i + 1; j < argc; j++, i++) argv[i] = argv[j]; argc--; + argv[argc] = nullptr; return true; } } @@ -70,10 +71,11 @@ findCmdLineKey(int &argc, char *arg = argv[i]; if (stringEq(arg, key) && i + 1 < argc) { char *value = argv[i + 1]; - // remove key and value from argv. + // Remove key and value from argv. for (int j = i + 2; j < argc; j++, i++) argv[i] = argv[j]; argc -= 2; + argv[argc] = nullptr; return value; } } diff --git a/dcalc/ArcDcalcWaveforms.cc b/dcalc/ArcDcalcWaveforms.cc index 90f83b9c..02f14479 100644 --- a/dcalc/ArcDcalcWaveforms.cc +++ b/dcalc/ArcDcalcWaveforms.cc @@ -14,30 +14,64 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include "ArcDcalcWaveforms.hh" +#include "Report.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "ArcDelayCalc.hh" +#include "DcalcAnalysisPt.hh" +#include "GraphDelayCalc.hh" namespace sta { -Table1 -ArcDcalcWaveforms::inputWaveform(const Pin *, - const RiseFall *, - const Corner *, - const MinMax *) -{ - return Table1(); -} +using std::make_shared; -Table1 -ArcDcalcWaveforms::drvrRampWaveform(const Pin *, - const RiseFall *, - const Pin *, - const RiseFall *, - const Pin *, - const Corner *, - const MinMax *) +Waveform +ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg, + const DcalcAnalysisPt *dcalc_ap, + const StaState *sta) { - return Table1(); + const Network *network = sta->network(); + Graph *graph = sta->graph(); + Report *report = sta->report(); + const Pin *in_pin = dcalc_arg.inPin(); + LibertyPort *port = network->libertyPort(in_pin); + if (port) { + const RiseFall *in_rf = dcalc_arg.inEdge(); + DriverWaveform *driver_waveform = port->driverWaveform(in_rf); + if (driver_waveform) { + 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); + LibertyLibrary *library = port->libertyLibrary(); + float vdd; + bool vdd_exists; + library->supplyVoltage("VDD", vdd, vdd_exists); + if (!vdd_exists) + report->error(1751, "VDD not defined in library %s", library->name()); + Waveform in_waveform = driver_waveform->waveform(in_slew); + // Delay time axis. + FloatSeq *time_values = new FloatSeq; + for (float time : *in_waveform.axis1()->values()) + time_values->push_back(time + dcalc_arg.inputDelay()); + 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()) { + float scaled_value = (in_rf == RiseFall::rise()) + ? value * vdd + : (1.0 - value) * vdd; + scaled_values->push_back(scaled_value); + } + return Waveform(scaled_values, time_axis); + } + } + return Waveform(); } } // namespace diff --git a/dcalc/ArcDcalcWaveforms.hh b/dcalc/ArcDcalcWaveforms.hh index 7015a27a..62343183 100644 --- a/dcalc/ArcDcalcWaveforms.hh +++ b/dcalc/ArcDcalcWaveforms.hh @@ -22,37 +22,25 @@ namespace sta { +class StaState; class Corner; class DcalcAnalysisPt; +class ArcDcalcArg; -// Abstract class for the graph delay calculator traversal to interface +// Abstract class for delay calculation waveforms for ploting. class ArcDcalcWaveforms { public: - virtual Table1 inputWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Corner *corner, - const MinMax *min_max); - virtual Table1 drvrWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Corner *corner, - const MinMax *min_max) = 0; - virtual Table1 loadWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) = 0; - virtual Table1 drvrRampWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max); + // Record waveform for drvr/load pin. + virtual void watchPin(const Pin *pin) = 0; + virtual void clearWatchPins() = 0; + virtual PinSeq watchPins() const = 0; + virtual Waveform watchWaveform(const Pin *pin) = 0; + +protected: + Waveform inputWaveform(ArcDcalcArg &dcalc_arg, + const DcalcAnalysisPt *dcalc_ap, + const StaState *sta); }; } // namespace diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc index ee94eee0..e318fa56 100644 --- a/dcalc/ArcDelayCalc.cc +++ b/dcalc/ArcDelayCalc.cc @@ -16,9 +16,11 @@ #include "ArcDelayCalc.hh" +#include "Units.hh" #include "Liberty.hh" #include "TimingArc.hh" #include "Network.hh" +#include "Graph.hh" namespace sta { @@ -48,12 +50,67 @@ ArcDelayCalc::gateDelay(const TimingArc *arc, //////////////////////////////////////////////////////////////// +ArcDcalcArg +makeArcDcalcArg(const char *inst_name, + const char *in_port_name, + const char *in_rf_name, + const char *drvr_port_name, + const char *drvr_rf_name, + const char *input_delay_str, + const StaState *sta) +{ + Report *report = sta->report(); + const Network *network = sta->sdcNetwork(); + const Instance *inst = network->findInstance(inst_name); + if (inst) { + const Pin *in_pin = network->findPin(inst, in_port_name); + if (in_pin) { + const RiseFall *in_rf = RiseFall::find(in_rf_name); + if (in_rf) { + const Pin *drvr_pin = network->findPin(inst, drvr_port_name); + if (drvr_pin) { + const RiseFall *drvr_rf = RiseFall::find(drvr_rf_name); + if (drvr_rf) { + float input_delay = strtof(input_delay_str, nullptr); + input_delay = sta->units()->timeUnit()->userToSta(input_delay); + + const Graph *graph = sta->graph(); + Edge *edge; + const TimingArc *arc; + graph->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc); + if (edge) + return ArcDcalcArg(in_pin, drvr_pin, edge, arc, input_delay); + else { + const Network *network = sta->network(); + const Instance *inst = network->instance(in_pin); + report->warn(2100, "no timing arc for %s input/driver pins.", + network->pathName(inst)); + } + } + else + report->warn(2101, "%s not a valid rise/fall.", drvr_rf_name); + } + else + report->warn(2102, "Pin %s/%s not found.", inst_name, drvr_port_name); + } + else + report->warn(2103, "%s not a valid rise/fall.", in_rf_name); + } + else + report->warn(2104, "Pin %s/%s not found.", inst_name, in_port_name); + } + else + report->warn(2105, "Instance %s not found.", inst_name); + return ArcDcalcArg(); +} + ArcDcalcArg::ArcDcalcArg() : in_pin_(nullptr), drvr_pin_(nullptr), edge_(nullptr), arc_(nullptr), in_slew_(0.0), + load_cap_(0.0), parasitic_(nullptr), input_delay_(0.0) { @@ -64,12 +121,14 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin, Edge *edge, const TimingArc *arc, const Slew in_slew, + float load_cap, const Parasitic *parasitic) : in_pin_(in_pin), drvr_pin_(drvr_pin), edge_(edge), arc_(arc), in_slew_(in_slew), + load_cap_(load_cap), parasitic_(parasitic), input_delay_(0.0) { @@ -85,6 +144,7 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin, edge_(edge), arc_(arc), in_slew_(0.0), + load_cap_(0.0), parasitic_(nullptr), input_delay_(input_delay) { @@ -96,6 +156,7 @@ ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) : edge_(arg.edge_), arc_(arg.arc_), in_slew_(arg.in_slew_), + load_cap_(arg.load_cap_), parasitic_(arg.parasitic_), input_delay_(arg.input_delay_) { @@ -107,6 +168,12 @@ ArcDcalcArg::inEdge() const return arc_->fromEdge()->asRiseFall(); } +Vertex * +ArcDcalcArg::drvrVertex(const Graph *graph) const +{ + return edge_->to(graph); +} + LibertyCell * ArcDcalcArg::drvrCell() const { @@ -150,6 +217,18 @@ ArcDcalcArg::setParasitic(const Parasitic *parasitic) parasitic_ = parasitic; } +void +ArcDcalcArg::setLoadCap(float load_cap) +{ + load_cap_ = load_cap; +} + +void +ArcDcalcArg::setInputDelay(float input_delay) +{ + input_delay_ = input_delay; +} + //////////////////////////////////////////////////////////////// ArcDcalcResult::ArcDcalcResult() : diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 20f8d9e5..38e976e2 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -115,6 +115,7 @@ public: ArnoldiDelayCalc(StaState *sta); virtual ~ArnoldiDelayCalc(); ArcDelayCalc *copy() override; + const char *name() const override { return "arnoldi"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; @@ -382,7 +383,7 @@ ArnoldiDelayCalc::gateDelay(const Pin *drvr_pin, ConcreteParasitic *cparasitic = reinterpret_cast(const_cast(parasitic)); rcmodel_ = dynamic_cast(cparasitic); - GateTableModel *table_model = gateTableModel(arc, dcalc_ap); + GateTableModel *table_model = arc->gateTableModel(dcalc_ap); if (table_model && rcmodel_) { const Pvt *pvt = pinPvt(drvr_pin, dcalc_ap); return gateDelaySlew(drvr_cell, arc, table_model, in_slew, load_pin_index_map, pvt); diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index a9680b49..45fe8fff 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -52,6 +52,7 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) : // Includes the Vh:Vdd region. region_count_(0), vl_fail_(false), + watch_pin_values_(network_), capacitance_unit_(units_->capacitanceUnit()), table_dcalc_(makeDmpCeffElmoreDelayCalc(sta)) { @@ -82,7 +83,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, parasitic_ = parasitic; output_waveforms_ = nullptr; - GateTableModel *table_model = gateTableModel(arc, dcalc_ap); + GateTableModel *table_model = arc->gateTableModel(dcalc_ap); if (table_model && parasitic) { OutputWaveforms *output_waveforms = table_model->outputWaveforms(); parasitics_->piModel(parasitic, c2_, rpi_, c1_); @@ -95,25 +96,25 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, bool vdd_exists; LibertyCell *drvr_cell = arc->to()->libertyCell(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); - const RiseFall *rf = arc->toEdge()->asRiseFall(); + drvr_rf_ = arc->toEdge()->asRiseFall(); drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); if (!vdd_exists) report_->error(1700, "VDD not defined in library %s", drvr_library->name()); - vth_ = drvr_library->outputThreshold(rf) * vdd_; - vl_ = drvr_library->slewLowerThreshold(rf) * vdd_; - vh_ = drvr_library->slewUpperThreshold(rf) * vdd_; + vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; + vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; + vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_; - drvr_cell->ensureVoltageWaveforms(); + drvr_cell->ensureVoltageWaveforms(dcalc_ap); in_slew_ = delayAsFloat(in_slew); output_waveforms_ = output_waveforms; ref_time_ = output_waveforms_->referenceTime(in_slew_); debugPrint(debug_, "ccs_dcalc", 1, "%s %s", drvr_cell->name(), - rf->asString()); + drvr_rf_->asString()); ArcDelay gate_delay; Slew drvr_slew; - gateDelaySlew(drvr_library, rf, gate_delay, drvr_slew); - return makeResult(drvr_library, rf, gate_delay, drvr_slew, load_pin_index_map); + gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew); + return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map); } } return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, @@ -312,9 +313,7 @@ CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library, dcalc_result.setGateDelay(gate_delay); dcalc_result.setDrvrSlew(drvr_slew); - for (auto load_pin_index : load_pin_index_map) { - const Pin *load_pin = load_pin_index.first; - size_t load_idx = load_pin_index.second; + for (const auto [load_pin, load_idx] : load_pin_index_map) { ArcDelay wire_delay; Slew load_slew; loadDelaySlew(load_pin, drvr_library, rf, drvr_slew, wire_delay, load_slew); @@ -466,69 +465,75 @@ CcsCeffDelayCalc::findVlTime(double v, // Waveform accessors for swig/tcl. -Table1 -CcsCeffDelayCalc::drvrWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Corner *corner, - const MinMax *min_max) +void +CcsCeffDelayCalc::watchPin(const Pin *pin) { - bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin, - drvr_rf, corner, min_max); - if (dcalc_success) - return drvrWaveform(in_slew_, drvr_rf); + watch_pin_values_[pin] = FloatSeq(); +} + +void +CcsCeffDelayCalc::clearWatchPins() +{ + watch_pin_values_.clear(); +} + +PinSeq +CcsCeffDelayCalc::watchPins() const +{ + PinSeq pins; + for (const auto& [pin, values] : watch_pin_values_) + pins.push_back(pin); + return pins; +} + +Waveform +CcsCeffDelayCalc::watchWaveform(const Pin *pin) +{ + if (pin == drvr_pin_) + return drvrWaveform(); + else + return loadWaveform(pin); +} + +Waveform +CcsCeffDelayCalc::drvrWaveform() +{ + if (output_waveforms_) { + // Stitch together the ccs waveforms for each region. + FloatSeq *drvr_times = new FloatSeq; + FloatSeq *drvr_volts = new FloatSeq; + for (size_t i = 0; i < region_count_; i++) { + double t1 = region_begin_times_[i]; + double t2 = region_end_times_[i]; + size_t time_steps = 10; + double time_step = (t2 - t1) / time_steps; + double time_offset = region_time_offsets_[i]; + for (size_t s = 0; s <= time_steps; s++) { + double t = t1 + s * time_step; + drvr_times->push_back(t - time_offset); + double v = output_waveforms_->timeVoltage(in_slew_, region_ceff_[i], t); + if (drvr_rf_ == RiseFall::fall()) + v = vdd_ - v; + drvr_volts->push_back(v); + } + } + TableAxisPtr drvr_time_axis = make_shared(TableAxisVariable::time, + drvr_times); + Table1 drvr_table(drvr_volts, drvr_time_axis); + return drvr_table; + } else return Table1(); } -Table1 -CcsCeffDelayCalc::drvrWaveform(const Slew &in_slew, - const RiseFall *drvr_rf) +Waveform +CcsCeffDelayCalc::loadWaveform(const Pin *load_pin) { - // Stitch together the ccs waveforms for each region. - FloatSeq *drvr_times = new FloatSeq; - FloatSeq *drvr_volts = new FloatSeq; - for (size_t i = 0; i < region_count_; i++) { - double t1 = region_begin_times_[i]; - double t2 = region_end_times_[i]; - size_t time_steps = 10; - double time_step = (t2 - t1) / time_steps; - double time_offset = region_time_offsets_[i]; - for (size_t s = 0; s <= time_steps; s++) { - double t = t1 + s * time_step; - drvr_times->push_back(t - time_offset); - double v = output_waveforms_->timeVoltage(delayAsFloat(in_slew), - region_ceff_[i], t); - if (drvr_rf == RiseFall::fall()) - v = vdd_ - v; - drvr_volts->push_back(v); - } - } - TableAxisPtr drvr_time_axis = make_shared(TableAxisVariable::time, - drvr_times); - Table1 drvr_table(drvr_volts, drvr_time_axis); - return drvr_table; -} - -// For debugging -Table1 -CcsCeffDelayCalc::loadWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) -{ - bool elmore_exists = false; - float elmore = 0.0; - if (parasitic_) { + if (output_waveforms_) { + bool elmore_exists = false; + float elmore = 0.0; parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists); - bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin, - drvr_rf, corner, min_max); - if (dcalc_success - && elmore_exists) { + if (elmore_exists) { FloatSeq *load_times = new FloatSeq; FloatSeq *load_volts = new FloatSeq; double t_vh = findVlTime(vh_, elmore); @@ -540,7 +545,7 @@ CcsCeffDelayCalc::loadWaveform(const Pin *in_pin, double ignore; vl(t, elmore, v, ignore); - double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v; + double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v; load_volts->push_back(v1); } TableAxisPtr load_time_axis = make_shared(TableAxisVariable::time, @@ -552,7 +557,7 @@ CcsCeffDelayCalc::loadWaveform(const Pin *in_pin, return Table1(); } -Table1 +Waveform CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin, const RiseFall *in_rf, const Pin *drvr_pin, diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 35094007..1be5eab2 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -23,16 +23,20 @@ namespace sta { using std::vector; +typedef map WatchPinValuesMap; + ArcDelayCalc * makeCcsCeffDelayCalc(StaState *sta); -class CcsCeffDelayCalc : public LumpedCapDelayCalc, public ArcDcalcWaveforms +class CcsCeffDelayCalc : public LumpedCapDelayCalc, + public ArcDcalcWaveforms { public: CcsCeffDelayCalc(StaState *sta); virtual ~CcsCeffDelayCalc(); ArcDelayCalc *copy() override; - + const char *name() const override { return "ccs_ceff"; } + bool reduceSupported() const override { return true; } ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, @@ -49,26 +53,11 @@ public: const DcalcAnalysisPt *dcalc_ap, int digits) override; - Table1 drvrWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Corner *corner, - const MinMax *min_max) override; - Table1 loadWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) override; - Table1 drvrRampWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) override; + // Record waveform for drvr/load pin. + void watchPin(const Pin *pin) override; + void clearWatchPins() override; + PinSeq watchPins() const override; + Waveform watchWaveform(const Pin *pin) override; protected: typedef vector Region; @@ -99,14 +88,23 @@ protected: // Return values. ArcDelay &delay, Slew &slew); + double findVlTime(double v, + double elmore); bool makeWaveformPreamble(const Pin *in_pin, const RiseFall *in_rf, const Pin *drvr_pin, const RiseFall *drvr_rf, const Corner *corner, const MinMax *min_max); - double findVlTime(double v, - double elmore); + Waveform drvrWaveform(); + Waveform loadWaveform(const Pin *load_pin); + Waveform drvrRampWaveform(const Pin *in_pin, + const RiseFall *in_rf, + const Pin *drvr_pin, + const RiseFall *drvr_rf, + const Pin *load_pin, + const Corner *corner, + const MinMax *min_max); void vl(double t, double elmore, // Return values. @@ -114,11 +112,10 @@ protected: double &dvl_dt); double vl(double t, double elmore); - Table1 drvrWaveform(const Slew &in_slew, - const RiseFall *drvr_rf); void fail(const char *reason); const Pin *drvr_pin_; + const RiseFall *drvr_rf_; double in_slew_; double load_cap_; const Parasitic *parasitic_; @@ -148,6 +145,8 @@ protected: Region region_ramp_times_; Region region_ramp_slopes_; bool vl_fail_; + // Waveform recording. + WatchPinValuesMap watch_pin_values_; const Unit *capacitance_unit_; // Delay calculator to use when ccs waveforms are missing from liberty. diff --git a/dcalc/CcsSimDelayCalc.cc b/dcalc/CcsSimDelayCalc.cc deleted file mode 100644 index 6b1de326..00000000 --- a/dcalc/CcsSimDelayCalc.cc +++ /dev/null @@ -1,902 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "CcsSimDelayCalc.hh" - -#include // abs - -#include "Debug.hh" -#include "Units.hh" -#include "TimingArc.hh" -#include "Liberty.hh" -#include "Sdc.hh" -#include "Parasitics.hh" -#include "DcalcAnalysisPt.hh" -#include "Network.hh" -#include "Corner.hh" -#include "Graph.hh" -#include "GraphDelayCalc.hh" -#include "DmpDelayCalc.hh" - -namespace sta { - -using std::abs; -using std::make_shared; - -// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998 -// McGraw-Hill, Inc. New York, NY. - -ArcDelayCalc * -makeCcsSimDelayCalc(StaState *sta) -{ - return new CcsSimDelayCalc(sta); -} - -CcsSimDelayCalc::CcsSimDelayCalc(StaState *sta) : - DelayCalcBase(sta), - dcalc_args_(nullptr), - load_pin_index_map_(network_), - dcalc_failed_(false), - pin_node_map_(network_), - make_waveforms_(false), - waveform_drvr_pin_(nullptr), - waveform_load_pin_(nullptr), - table_dcalc_(makeDmpCeffElmoreDelayCalc(sta)) -{ -} - -CcsSimDelayCalc::~CcsSimDelayCalc() -{ - delete table_dcalc_; -} - -ArcDelayCalc * -CcsSimDelayCalc::copy() -{ - return new CcsSimDelayCalc(this); -} - -Parasitic * -CcsSimDelayCalc::findParasitic(const Pin *drvr_pin, - const RiseFall *, - const DcalcAnalysisPt *dcalc_ap) -{ - const Corner *corner = dcalc_ap->corner(); - Parasitic *parasitic = nullptr; - // set_load net has precidence over parasitics. - if (!sdc_->drvrPinHasWireCap(drvr_pin, corner)) { - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); - if (parasitics_->haveParasitics()) - parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); - } - return parasitic; -} - -Parasitic * -CcsSimDelayCalc::reduceParasitic(const Parasitic *parasitic_network, - const Pin *, - const RiseFall *, - const DcalcAnalysisPt *) -{ - return const_cast(parasitic_network); -} - -ArcDcalcResult -CcsSimDelayCalc::inputPortDelay(const Pin *drvr_pin, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) -{ - 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(); - parasitics_->reduceToPiElmore(parasitic, drvr_pin, rf, - dcalc_ap->corner(), - dcalc_ap->constraintMinMax(), ap); - pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); - } - - for (auto load_pin_index : load_pin_index_map) { - const Pin *load_pin = load_pin_index.first; - size_t load_idx = load_pin_index.second; - ArcDelay wire_delay = 0.0; - Slew load_slew = in_slew; - bool elmore_exists = false; - float elmore = 0.0; - if (pi_elmore) - 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); - thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew); - dcalc_result.setWireDelay(load_idx, wire_delay); - dcalc_result.setLoadSlew(load_idx, load_slew); - } - return dcalc_result; -} - -ArcDcalcResult -CcsSimDelayCalc::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) -{ - ArcDcalcArgSeq dcalc_args; - dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, parasitic); - ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_cap, - load_pin_index_map, dcalc_ap); - return dcalc_results[0]; -} - -ArcDcalcResultSeq -CcsSimDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, - float load_cap, - const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) -{ - dcalc_args_ = &dcalc_args; - load_pin_index_map_ = load_pin_index_map; - drvr_count_ = dcalc_args.size(); - load_cap_ = load_cap; - dcalc_ap_ = dcalc_ap; - drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall(); - dcalc_failed_ = false; - parasitic_network_ = dcalc_args[0].parasitic(); - ArcDcalcResultSeq dcalc_results(drvr_count_); - - size_t drvr_count = dcalc_args.size(); - output_waveforms_.resize(drvr_count); - ref_time_.resize(drvr_count); - - for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) { - ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; - GateTableModel *table_model = gateTableModel(dcalc_arg.arc(), dcalc_ap); - if (table_model && dcalc_arg.parasitic()) { - OutputWaveforms *output_waveforms = table_model->outputWaveforms(); - float in_slew = delayAsFloat(dcalc_arg.inSlew()); - if (output_waveforms - // Bounds check because extrapolating waveforms does not work for shit. - && output_waveforms->slewAxis()->inBounds(in_slew) - && output_waveforms->capAxis()->inBounds(load_cap)) { - output_waveforms_[drvr_idx] = output_waveforms; - ref_time_[drvr_idx] = output_waveforms->referenceTime(in_slew); - debugPrint(debug_, "ccs_dcalc", 1, "%s %s", - network_->libertyPort(dcalc_arg.drvrPin())->libertyCell()->name(), - drvr_rf_->asString()); - - LibertyCell *drvr_cell = dcalc_arg.arc()->to()->libertyCell(); - const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); - bool vdd_exists; - drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); - if (!vdd_exists) - report_->error(1720, "VDD not defined in library %s", drvr_library->name()); - drvr_cell->ensureVoltageWaveforms(); - if (drvr_idx == 0) { - vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; - vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; - vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_; - } - } - else - dcalc_failed_ = true; - } - else - dcalc_failed_ = true; - } - - if (dcalc_failed_) { - const Parasitic *parasitic_network = dcalc_args[0].parasitic(); - for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) { - ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; - Parasitic *pi_elmore = nullptr; - const Pin *drvr_pin = dcalc_arg.drvrPin(); - if (parasitic_network) { - const ParasiticAnalysisPt *ap = dcalc_ap_->parasiticAnalysisPt(); - parasitics_->reduceToPiElmore(parasitic_network, drvr_pin, drvr_rf_, - dcalc_ap_->corner(), - dcalc_ap_->constraintMinMax(), ap); - pi_elmore = parasitics_->findPiElmore(drvr_pin, drvr_rf_, ap); - dcalc_arg.setParasitic(pi_elmore); - } - } - dcalc_results = table_dcalc_->gateDelays(dcalc_args, load_cap, - load_pin_index_map, dcalc_ap); - } - else { - simulate(dcalc_args); - - ArcDcalcArg &drvr_arg = dcalc_args[0]; - const LibertyLibrary *drvr_library = drvr_arg.drvrLibrary(); - 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]; - const Pin *drvr_pin = dcalc_arg.drvrPin(); - size_t drvr_node = pin_node_map_[drvr_pin]; - ThresholdTimes &drvr_times = threshold_times_[drvr_node]; - ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time_[drvr_idx]; - Slew drvr_slew = abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]); - dcalc_result.setGateDelay(gate_delay); - dcalc_result.setDrvrSlew(drvr_slew); - debugPrint(debug_, "ccs_dcalc", 2, - "%s gate delay %s slew %s", - network_->pathName(drvr_pin), - delayAsString(gate_delay, this), - delayAsString(drvr_slew, this)); - - dcalc_result.setLoadCount(load_pin_index_map.size()); - for (auto load_pin_index : load_pin_index_map) { - const Pin *load_pin = load_pin_index.first; - size_t load_idx = load_pin_index.second; - size_t load_node = pin_node_map_[load_pin]; - ThresholdTimes &wire_times = threshold_times_[load_node]; - ThresholdTimes &drvr_times = threshold_times_[drvr_node]; - ArcDelay wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth]; - Slew load_slew = abs(wire_times[threshold_vh] - wire_times[threshold_vl]); - debugPrint(debug_, "ccs_dcalc", 2, - "load %s %s delay %s slew %s", - network_->pathName(load_pin), - drvr_rf_->asString(), - delayAsString(wire_delay, this), - delayAsString(load_slew, this)); - - thresholdAdjust(load_pin, drvr_library, drvr_rf_, wire_delay, load_slew); - dcalc_result.setWireDelay(load_idx, wire_delay); - dcalc_result.setLoadSlew(load_idx, load_slew); - } - } - } - return dcalc_results; -} - -void -CcsSimDelayCalc::simulate(ArcDcalcArgSeq &dcalc_args) -{ - const Pin *drvr_pin = dcalc_args[0].drvrPin(); - LibertyPort *drvr_port = network_->libertyPort(drvr_pin); - const MinMax *min_max = dcalc_ap_->delayMinMax(); - drive_resistance_ = drvr_port->driveResistance(drvr_rf_, min_max); - - initSim(); - stampConductances(); - - // The conductance matrix does not change as long as the time step is constant. - // Factor stamping and LU decomposition of the conductance matrix - // outside of the simulation loop. - // Prevent copying of matrix. - conductances_.makeCompressed(); - // LU factor conductances. - solver_.compute(conductances_); - - for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) { - ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; - // Find initial ceff. - ceff_[drvr_idx] = load_cap_; - // voltageTime is always for a rising waveform so 0.0v is initial voltage. - drvr_current_[drvr_idx] = - output_waveforms_[drvr_idx]->voltageCurrent(delayAsFloat(dcalc_arg.inSlew()), - ceff_[drvr_idx], 0.0); - } - // Initial time depends on ceff which impact delay, so use a sim step - // to find an initial ceff. - setCurrents(); - voltages_ = solver_.solve(currents_); - updateCeffIdrvr(); - initNodeVoltages(); - - // voltageTime is always for a rising waveform so 0.0v is initial voltage. - double time_begin = output_waveforms_[0]->voltageTime(dcalc_args[0].inSlewFlt(), - ceff_[0], 0.0); - // Limit in case load voltage waveforms don't get to final value. - double time_end = time_begin + maxTime(); - - if (make_waveforms_) - recordWaveformStep(time_begin); - - for (double time = time_begin; time <= time_end; time += time_step_) { - stampConductances(); - conductances_.makeCompressed(); - solver_.compute(conductances_); - setCurrents(); - voltages_ = solver_.solve(currents_); - - debugPrint(debug_, "ccs_dcalc", 3, "%s ceff %s VDrvr %.4f Idrvr %s", - delayAsString(time, this), - units_->capacitanceUnit()->asString(ceff_[0]), - voltages_[pin_node_map_[dcalc_args[0].drvrPin()]], - units_->currentUnit()->asString(drvr_current_[0], 4)); - - updateCeffIdrvr(); - measureThresholds(time); - if (make_waveforms_) - recordWaveformStep(time); - - bool loads_finished = true; - for (auto load_node1 : pin_node_map_) { - size_t load_node = load_node1.second; - if ((drvr_rf_ == RiseFall::rise() - && voltages_[load_node] < vh_ + (vdd_ - vh_) * .5) - || (drvr_rf_ == RiseFall::fall() - && (voltages_[load_node] > vl_ * .5))) { - loads_finished = false; - break; - } - } - if (loads_finished) - break; - - time_step_prev_ = time_step_; - // swap faster than copying with '='. - voltages_prev2_.swap(voltages_prev1_); - voltages_prev1_.swap(voltages_); - } -} - -double -CcsSimDelayCalc::timeStep() -{ - // Needs to use LTE for time step dynamic control. - return drive_resistance_ * load_cap_ * .02; -} - -double -CcsSimDelayCalc::maxTime() -{ - return (*dcalc_args_)[0].inSlewFlt() - + (drive_resistance_ + resistance_sum_) * load_cap_ * 2; -} - -void -CcsSimDelayCalc::initSim() -{ - ceff_.resize(drvr_count_); - drvr_current_.resize(drvr_count_); - - findNodeCount(); - setOrder(); - - initNodeVoltages(); - - // time step required by initCapacitanceCurrents - time_step_ = time_step_prev_ = timeStep(); - debugPrint(debug_, "ccs_dcalc", 1, "time step %s", delayAsString(time_step_, this)); - - // Reset waveform recording. - times_.clear(); - drvr_voltages_.clear(); - load_voltages_.clear(); - - measure_thresholds_ = {vl_, vth_, vh_}; -} - -void -CcsSimDelayCalc::findNodeCount() -{ - includes_pin_caps_ = parasitics_->includesPinCaps(parasitic_network_); - coupling_cap_multiplier_ = 1.0; - - node_capacitances_.clear(); - pin_node_map_.clear(); - node_index_map_.clear(); - - for (ParasiticNode *node : parasitics_->nodes(parasitic_network_)) { - if (!parasitics_->isExternal(node)) { - size_t node_idx = node_index_map_.size(); - node_index_map_[node] = node_idx; - const Pin *pin = parasitics_->pin(node); - if (pin) { - pin_node_map_[pin] = node_idx; - debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu", - network_->pathName(pin), - node_idx); - } - double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node); - node_capacitances_.push_back(cap); - } - } - - for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) { - float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_; - ParasiticNode *node1 = parasitics_->node1(capacitor); - if (!parasitics_->isExternal(node1)) { - size_t node_idx = node_index_map_[node1]; - node_capacitances_[node_idx] += cap; - } - ParasiticNode *node2 = parasitics_->node2(capacitor); - if (!parasitics_->isExternal(node2)) { - size_t node_idx = node_index_map_[node2]; - node_capacitances_[node_idx] += cap; - } - } - node_count_ = node_index_map_.size(); -} - -float -CcsSimDelayCalc::pinCapacitance(ParasiticNode *node) -{ - const Pin *pin = parasitics_->pin(node); - float pin_cap = 0.0; - 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); - } - else if (network_->isTopLevelPort(pin)) - pin_cap = sdc_->portExtCap(port, drvr_rf_, corner, cnst_min_max); - } - return pin_cap; -} - -void -CcsSimDelayCalc::setOrder() -{ - currents_.resize(node_count_); - voltages_.resize(node_count_); - voltages_prev1_.resize(node_count_); - voltages_prev2_.resize(node_count_); - // Matrix resize also zeros. - conductances_.resize(node_count_, node_count_); - threshold_times_.resize(node_count_); -} - -void -CcsSimDelayCalc::initNodeVoltages() -{ - double drvr_init_volt = (drvr_rf_ == RiseFall::rise()) ? 0.0 : vdd_; - for (size_t i = 0; i < node_count_; i++) { - voltages_[i] = drvr_init_volt; - voltages_prev1_[i] = drvr_init_volt; - voltages_prev2_[i] = drvr_init_volt; - } -} - -void -CcsSimDelayCalc::simulateStep() -{ - setCurrents(); - voltages_ = solver_.solve(currents_); -} - -void -CcsSimDelayCalc::stampConductances() -{ - conductances_.setZero(); - for (size_t node_idx = 0; node_idx < node_count_; node_idx++) - stampCapacitance(node_idx, node_capacitances_[node_idx]); - - resistance_sum_ = 0.0; - for (ParasiticResistor *resistor : parasitics_->resistors(parasitic_network_)) { - ParasiticNode *node1 = parasitics_->node1(resistor); - ParasiticNode *node2 = parasitics_->node2(resistor); - // One commercial extractor creates resistors with identical from/to nodes. - if (node1 != node2) { - size_t node_idx1 = node_index_map_[node1]; - size_t node_idx2 = node_index_map_[node2]; - float resistance = parasitics_->value(resistor); - stampConductance(node_idx1, node_idx2, 1.0 / resistance); - resistance_sum_ += resistance; - } - } -} - -// Grounded resistor. -void -CcsSimDelayCalc::stampConductance(size_t n1, - double g) -{ - conductances_.coeffRef(n1, n1) += g; -} - -// Floating resistor. -void -CcsSimDelayCalc::stampConductance(size_t n1, - size_t n2, - double g) -{ - conductances_.coeffRef(n1, n1) += g; - conductances_.coeffRef(n2, n2) += g; - conductances_.coeffRef(n1, n2) -= g; - conductances_.coeffRef(n2, n1) -= g; -} - -// Grounded capacitance. -void -CcsSimDelayCalc::stampCapacitance(size_t n1, - double cap) -{ - double g = cap * 2.0 / time_step_; - stampConductance(n1, g); -} - -// Floating capacitance. -void -CcsSimDelayCalc::stampCapacitance(size_t n1, - size_t n2, - double cap) -{ - double g = cap * 2.0 / time_step_; - stampConductance(n1, n2, g); -} - -//////////////////////////////////////////////////////////////// - -void -CcsSimDelayCalc::setCurrents() -{ - currents_.setZero(node_count_); - for (size_t i = 0; i < drvr_count_; i++) { - size_t drvr_node = pin_node_map_[(*dcalc_args_)[i].drvrPin()]; - insertCurrentSrc(drvr_node, drvr_current_[i]); - } - - for (size_t node_idx = 0; node_idx < node_count_; node_idx++) - insertCapCurrentSrc(node_idx, node_capacitances_[node_idx]); -} - -void -CcsSimDelayCalc::insertCapCurrentSrc(size_t n1, - double cap) -{ - // Direct implementation of figure 4.11 in - // “Electronic Circuit & System Simulation Methods” allowing for time - // step changes. - // double g0 = 2.0 * cap / time_step_; - // double g1 = 2.0 * cap / time_step_prev_; - // double dv = voltages_prev2_[n1] - voltages_prev1_[n1]; - // double ieq_prev = cap * dv / time_step_ + g0 * voltages_prev1_[n1]; - // double i_cap = (g0 + g1) * voltages_prev1_[n1] - ieq_prev; - - // Above simplified. - // double i_cap - // = cap / time_step_ * voltages_prev1_[n1] - // + 2.0 * cap / time_step_prev_ * voltages_prev1_[n1] - // - cap / time_step_ * voltages_prev2_[n1]; - - // Simplified for constant time step. - double i_cap - = 3.0 * cap / time_step_ * voltages_prev1_[n1] - - cap / time_step_ * voltages_prev2_[n1]; - insertCurrentSrc(n1, i_cap); -} - -void -CcsSimDelayCalc::insertCapaCurrentSrc(size_t n1, - size_t n2, - double cap) -{ - double g0 = 2.0 * cap / time_step_; - double g1 = 2.0 * cap / time_step_prev_; - double dv = (voltages_prev2_[n1] - voltages_prev2_[n2]) - - (voltages_prev1_[n1] - voltages_prev1_[n2]); - double ieq_prev = cap * dv / time_step_ + g0*(voltages_prev1_[n1]-voltages_prev1_[n2]); - double i_cap = (g0 + g1) * (voltages_prev1_[n1] - voltages_prev1_[n2]) - ieq_prev; - insertCurrentSrc(n1, n2, i_cap); -} - -void -CcsSimDelayCalc::insertCurrentSrc(size_t n1, - double current) -{ - currents_.coeffRef(n1) += current; -} - -void -CcsSimDelayCalc::insertCurrentSrc(size_t n1, - size_t n2, - double current) -{ - currents_.coeffRef(n1) += current; - currents_.coeffRef(n2) -= current; -} - -void -CcsSimDelayCalc::updateCeffIdrvr() -{ - for (size_t i = 0; i < drvr_count_; i++) { - size_t drvr_node = pin_node_map_[(*dcalc_args_)[i].drvrPin()]; - double dv = voltages_[drvr_node] - voltages_prev1_[drvr_node]; - if (drvr_rf_ == RiseFall::rise()) { - if (drvr_current_[i] != 0.0 - && dv > 0.0) { - double ceff = drvr_current_[i] * time_step_ / dv; - if (output_waveforms_[i]->capAxis()->inBounds(ceff)) - ceff_[i] = ceff; - } - - double v = voltages_[drvr_node]; - if (voltages_[drvr_node] > (vdd_ - .01)) - // Whoa partner. Head'n for the weeds. - drvr_current_[i] = 0.0; - else - drvr_current_[i] = - output_waveforms_[i]->voltageCurrent((*dcalc_args_)[i].inSlewFlt(), - ceff_[i], v); - } - else { - if (drvr_current_[i] != 0.0 - && dv < 0.0) { - double ceff = drvr_current_[i] * time_step_ / dv; - if (output_waveforms_[i]->capAxis()->inBounds(ceff)) - ceff_[i] = ceff; - } - double v = vdd_ - voltages_[drvr_node]; - if (voltages_[drvr_node] < 0.01) - // Whoa partner. Head'n for the weeds. - drvr_current_[i] = 0.0; - else - drvr_current_[i] = - output_waveforms_[i]->voltageCurrent((*dcalc_args_)[i].inSlewFlt(), - ceff_[i], v); - } - } -} - -//////////////////////////////////////////////////////////////// - -void -CcsSimDelayCalc::measureThresholds(double time) -{ - for (auto pin_node1 : pin_node_map_) { - size_t pin_node = pin_node1.second; - measureThresholds(time, pin_node); - } -} - -void -CcsSimDelayCalc::measureThresholds(double time, - size_t n) -{ - double v = voltages_[n]; - double v_prev = voltages_prev1_[n]; - for (size_t m = 0; m < measure_threshold_count_; m++) { - double th = measure_thresholds_[m]; - if ((v_prev < th && th <= v) - || (v_prev > th && th >= v)) { - double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev); - debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s", - n, - th, - delayAsString(t_cross, this)); - threshold_times_[n][m] = t_cross; - } - } -} - -void -CcsSimDelayCalc::recordWaveformStep(double time) -{ - times_.push_back(time); - size_t drvr_node = pin_node_map_[waveform_drvr_pin_]; - drvr_voltages_.push_back(voltages_[drvr_node]); - if (waveform_load_pin_) { - size_t load_node = pin_node_map_[waveform_load_pin_]; - load_voltages_.push_back(voltages_[load_node]); - } -} - -//////////////////////////////////////////////////////////////// - -string -CcsSimDelayCalc::reportGateDelay(const Pin *drvr_pin, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *, - const LoadPinIndexMap &, - const DcalcAnalysisPt *dcalc_ap, - int digits) -{ - GateTimingModel *model = gateModel(arc, dcalc_ap); - if (model) { - float in_slew1 = delayAsFloat(in_slew); - return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap, - false, digits); - } - return ""; -} - -//////////////////////////////////////////////////////////////// - -// Waveform accessors for swig/tcl. -Table1 -CcsSimDelayCalc::drvrWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Corner *corner, - const MinMax *min_max) -{ - makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, nullptr, corner, min_max); - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - new FloatSeq(times_)); - Table1 waveform(new FloatSeq(drvr_voltages_), time_axis); - return waveform; -} - -Table1 -CcsSimDelayCalc::loadWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) -{ - makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, load_pin, corner, min_max); - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - new FloatSeq(times_)); - Table1 waveform(new FloatSeq(load_voltages_), time_axis); - return waveform; -} - -Table1 -CcsSimDelayCalc::inputWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Corner *corner, - const MinMax *min_max) -{ - LibertyPort *port = network_->libertyPort(in_pin); - if (port) { - DriverWaveform *driver_waveform = port->driverWaveform(in_rf); - const Vertex *in_vertex = graph_->pinLoadVertex(in_pin); - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - float in_slew = delayAsFloat(graph_->slew(in_vertex, in_rf, dcalc_ap->index())); - LibertyLibrary *library = port->libertyLibrary(); - float vdd; - bool vdd_exists; - library->supplyVoltage("VDD", vdd, vdd_exists); - if (!vdd_exists) - report_->error(1721, "VDD not defined in library %s", library->name()); - Table1 in_waveform = driver_waveform->waveform(in_slew); - // Scale the waveform from 0:vdd. - FloatSeq *scaled_values = new FloatSeq; - for (float value : *in_waveform.values()) - scaled_values->push_back(value * vdd); - return Table1(scaled_values, in_waveform.axis1ptr()); - } - return Table1(); -} - -void -CcsSimDelayCalc::makeWaveforms(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) -{ - Edge *edge; - const TimingArc *arc; - graph_->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc); - if (arc) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - const Parasitic *parasitic = findParasitic(drvr_pin, drvr_rf, dcalc_ap); - if (parasitic) { - make_waveforms_ = true; - waveform_drvr_pin_ = drvr_pin; - waveform_load_pin_ = load_pin; - Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); - graph_delay_calc_->findDriverArcDelays(drvr_vertex, edge, arc, dcalc_ap, this); - make_waveforms_ = false; - waveform_drvr_pin_ = nullptr; - waveform_load_pin_ = nullptr; - } - } -} - -//////////////////////////////////////////////////////////////// - -void -CcsSimDelayCalc::reportMatrix(const char *name, - MatrixSd &matrix) -{ - report_->reportLine("%s", name); - reportMatrix(matrix); -} - -void -CcsSimDelayCalc::reportMatrix(const char *name, - MatrixXd &matrix) -{ - report_->reportLine("%s", name); - reportMatrix(matrix); -} - -void -CcsSimDelayCalc::reportMatrix(const char *name, - VectorXd &matrix) -{ - report_->reportLine("%s", name); - reportMatrix(matrix); -} - -void -CcsSimDelayCalc::reportVector(const char *name, - vector &matrix) -{ - report_->reportLine("%s", name); - reportVector(matrix); -} - -void -CcsSimDelayCalc::reportMatrix(MatrixSd &matrix) -{ - for (Index i = 0; i < matrix.rows(); i++) { - string line = "| "; - for (Index j = 0; j < matrix.cols(); j++) { - string entry = stdstrPrint("%10.3e", matrix.coeff(i, j)); - line += entry; - line += " "; - } - line += "|"; - report_->reportLineString(line); - } -} - -void -CcsSimDelayCalc::reportMatrix(MatrixXd &matrix) -{ - for (Index i = 0; i < matrix.rows(); i++) { - string line = "| "; - for (Index j = 0; j < matrix.cols(); j++) { - string entry = stdstrPrint("%10.3e", matrix.coeff(i, j)); - line += entry; - line += " "; - } - line += "|"; - report_->reportLineString(line); - } -} - -void -CcsSimDelayCalc::reportMatrix(VectorXd &matrix) -{ - string line = "| "; - for (Index i = 0; i < matrix.rows(); i++) { - string entry = stdstrPrint("%10.3e", matrix.coeff(i)); - line += entry; - line += " "; - } - line += "|"; - report_->reportLineString(line); -} - -void -CcsSimDelayCalc::reportVector(vector &matrix) -{ - string line = "| "; - for (size_t i = 0; i < matrix.size(); i++) { - string entry = stdstrPrint("%10.3e", matrix[i]); - line += entry; - line += " "; - } - line += "|"; - report_->reportLineString(line); -} - -} // namespace diff --git a/dcalc/CcsSimDelayCalc.hh b/dcalc/CcsSimDelayCalc.hh deleted file mode 100644 index 5f000472..00000000 --- a/dcalc/CcsSimDelayCalc.hh +++ /dev/null @@ -1,238 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#pragma once - -#include -#include -#include - -#include "LumpedCapDelayCalc.hh" -#include "ArcDcalcWaveforms.hh" - -namespace sta { - -class ArcDelayCalc; -class StaState; -class Corner; - -using std::vector; -using std::array; -using Eigen::MatrixXd; -using Eigen::MatrixXcd; -using Eigen::VectorXd; -using Eigen::SparseMatrix; -using Eigen::Index; -using Eigen::SparseLU; - -typedef Map PinNodeMap; -typedef Map NodeIndexMap; -typedef Map PortIndexMap; -typedef SparseMatrix MatrixSd; - -ArcDelayCalc * -makeCcsSimDelayCalc(StaState *sta); - -class CcsSimDelayCalc : public DelayCalcBase, public ArcDcalcWaveforms -{ -public: - CcsSimDelayCalc(StaState *sta); - ~CcsSimDelayCalc(); - ArcDelayCalc *copy() override; - Parasitic *findParasitic(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) override; - Parasitic *reduceParasitic(const Parasitic *parasitic_network, - const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap) 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; - 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; - ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args, - float load_cap, - const LoadPinIndexMap &load_pin_index_map, - const DcalcAnalysisPt *dcalc_ap) 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, - int digits) override; - - Table1 inputWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Corner *corner, - const MinMax *min_max) override; - Table1 drvrWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Corner *corner, - const MinMax *min_max) override; - Table1 loadWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) override; - -protected: - void simulate(ArcDcalcArgSeq &dcalc_args); - virtual double maxTime(); - virtual double timeStep(); - void updateCeffIdrvr(); - void initSim(); - void findLoads(); - virtual void findNodeCount(); - void setOrder(); - void initNodeVoltages(); - void simulateStep(); - virtual void stampConductances(); - void stampConductance(size_t n1, - double g); - void stampConductance(size_t n1, - size_t n2, - double g); - void stampCapacitance(size_t n1, - double cap); - void stampCapacitance(size_t n1, - size_t n2, - double cap); - float pinCapacitance(ParasiticNode *node); - virtual void setCurrents(); - void insertCapCurrentSrc(size_t n1, - double cap); - void insertCapaCurrentSrc(size_t n1, - size_t n2, - double cap); - void insertCurrentSrc(size_t n1, - double current); - void insertCurrentSrc(size_t n1, - size_t n2, - double current); - void measureThresholds(double time); - void measureThresholds(double time, - size_t i); - void loadDelaySlew(const Pin *load_pin, - // Return values. - ArcDelay &delay, - Slew &slew); - void recordWaveformStep(double time); - void makeWaveforms(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max); - void reportMatrix(const char *name, - MatrixSd &matrix); - void reportMatrix(const char *name, - MatrixXd &matrix); - void reportMatrix(const char *name, - VectorXd &matrix); - void reportVector(const char *name, - vector &matrix); - void reportMatrix(MatrixSd &matrix); - void reportMatrix(MatrixXd &matrix); - void reportMatrix(VectorXd &matrix); - void reportVector(vector &matrix); - - ArcDcalcArgSeq *dcalc_args_; - size_t drvr_count_; - float load_cap_; - const DcalcAnalysisPt *dcalc_ap_; - const Parasitic *parasitic_network_; - const RiseFall *drvr_rf_; - - // Tmp for gateDelay/loadDelay api. - ArcDcalcResult dcalc_result_; - LoadPinIndexMap load_pin_index_map_; - - bool dcalc_failed_; - size_t node_count_; // Parasitic network node count - PinNodeMap pin_node_map_; // Parasitic pin -> array index - NodeIndexMap node_index_map_; // Parasitic node -> array index - vector output_waveforms_; - vector ref_time_; - double drive_resistance_; - double resistance_sum_; - - vector node_capacitances_; - bool includes_pin_caps_; - float coupling_cap_multiplier_; - - // Indexed by driver index. - vector ceff_; - vector drvr_current_; - - double time_step_; - double time_step_prev_; - // I = GV - // currents_ = conductances_ * voltages_ - VectorXd currents_; - MatrixSd conductances_; - VectorXd voltages_; - VectorXd voltages_prev1_; - VectorXd voltages_prev2_; - SparseLU solver_; - - // Waveform recording. - bool make_waveforms_; - const Pin *waveform_drvr_pin_; - const Pin *waveform_load_pin_; - FloatSeq drvr_voltages_; - FloatSeq load_voltages_; - FloatSeq times_; - - size_t drvr_idx_; - - float vdd_; - float vth_; - float vl_; - float vh_; - - static constexpr size_t threshold_vl = 0; - static constexpr size_t threshold_vth = 1; - static constexpr size_t threshold_vh = 2; - static constexpr size_t measure_threshold_count_ = 3; - typedef array ThresholdTimes; - // Vl Vth Vh - ThresholdTimes measure_thresholds_; - // Indexed by node number. - vector threshold_times_; - - // Delay calculator to use when ccs waveforms are missing from liberty. - ArcDelayCalc *table_dcalc_; - - using ArcDelayCalc::reduceParasitic; -}; - -} // namespacet diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index b6ce0b50..b08bef9a 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -23,7 +23,6 @@ #include "DmpDelayCalc.hh" #include "ArnoldiDelayCalc.hh" #include "CcsCeffDelayCalc.hh" -#include "CcsSimDelayCalc.hh" #include "PrimaDelayCalc.hh" namespace sta { @@ -41,7 +40,6 @@ registerDelayCalcs() registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc); registerDelayCalc("arnoldi", makeArnoldiDelayCalc); registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc); - registerDelayCalc("ccs_sim", makeCcsSimDelayCalc); registerDelayCalc("prima", makePrimaDelayCalc); } @@ -82,10 +80,8 @@ StringSeq delayCalcNames() { StringSeq names; - for (auto name_dcalc : *delay_calcs) { - const char *name = name_dcalc.first; + for (const auto [name, make_dcalc] : *delay_calcs) names.push_back(name); - } return names; } diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index efd55ae0..04344296 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -1,7 +1,3 @@ -%module dcalc - -%{ - // OpenSTA, Static Timing Analyzer // Copyright (c) 2024, Parallax Software, Inc. // @@ -18,10 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "Sta.hh" +%module dcalc + +%{ + +#include "DelayCalc.hh" #include "ArcDelayCalc.hh" #include "dcalc/ArcDcalcWaveforms.hh" #include "dcalc/PrimaDelayCalc.hh" +#include "Sta.hh" %} @@ -62,82 +63,9 @@ report_delay_calc_cmd(Edge *edge, return Sta::sta()->reportDelayCalc(edge, arc, corner, min_max, digits); } -//////////////////////////////////////////////////////////////// - -Table1 -ccs_input_waveform(const Pin *in_pin, - const RiseFall *in_rf, - const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - ArcDcalcWaveforms *arc_dcalc = dynamic_cast(sta->arcDelayCalc()); - if (arc_dcalc) - return arc_dcalc->inputWaveform(in_pin, in_rf, corner, min_max); - else - return Table1(); -} - -Table1 -ccs_driver_waveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - ArcDcalcWaveforms *arc_dcalc = dynamic_cast(sta->arcDelayCalc()); - if (arc_dcalc) - return arc_dcalc->drvrWaveform(in_pin, in_rf, drvr_pin, drvr_rf, corner, min_max); - else - return Table1(); -} - -Table1 -ccs_driver_ramp_waveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - ArcDcalcWaveforms *arc_dcalc = dynamic_cast(sta->arcDelayCalc()); - if (arc_dcalc) - return arc_dcalc->drvrRampWaveform(in_pin, in_rf, drvr_pin, drvr_rf, - load_pin, corner, min_max); - else - return Table1(); -} - -Table1 -ccs_load_waveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - ArcDcalcWaveforms *arc_dcalc = dynamic_cast(sta->arcDelayCalc()); - if (arc_dcalc) - return arc_dcalc->loadWaveform(in_pin, in_rf, drvr_pin, drvr_rf, - load_pin, corner, min_max); - else - return Table1(); -} - void set_prima_reduce_order(size_t order) { - cmdLinkedNetwork(); Sta *sta = Sta::sta(); PrimaDelayCalc *dcalc = dynamic_cast(sta->arcDelayCalc()); if (dcalc) { @@ -146,4 +74,18 @@ set_prima_reduce_order(size_t order) } } +void +find_delays() +{ + cmdLinkedNetwork(); + Sta::sta()->findDelays(); +} + +void +delays_invalid() +{ + Sta *sta = Sta::sta(); + sta->delaysInvalid(); +} + %} // inline diff --git a/dcalc/DelayCalc.tcl b/dcalc/DelayCalc.tcl index 5ab60ff0..9709a85e 100644 --- a/dcalc/DelayCalc.tcl +++ b/dcalc/DelayCalc.tcl @@ -148,17 +148,17 @@ proc set_assigned_delay { args } { if [info exists keys(-from)] { set from_pins [get_port_pins_error "from_pins" $keys(-from)] } else { - sta_error 181 ""set_assigned_delay" missing -from argument." + sta_error 181 "set_assigned_delay missing -from argument." } if [info exists keys(-to)] { set to_pins [get_port_pins_error "to_pins" $keys(-to)] } else { - sta_error 182 ""set_assigned_delay" missing -to argument." + sta_error 182 "set_assigned_delay missing -to argument." } set delay [lindex $args 0] if {![string is double $delay]} { - sta_error 183 ""set_assigned_delay" delay is not a float." + sta_error 183 "set_assigned_delay delay is not a float." } set delay [time_ui_sta $delay] @@ -283,7 +283,7 @@ proc set_assigned_check { args } { } set check_value [lindex $args 0] if { ![string is double $check_value] } { - sta_error 192 ""set_assigned_check" check_value is not a float." + sta_error 192 "set_assigned_check check_value is not a float." } set check_value [time_ui_sta $check_value] diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index 7db5663f..074a506a 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -22,9 +22,11 @@ #include "TableModel.hh" #include "Network.hh" #include "Parasitics.hh" +#include "Graph.hh" #include "Sdc.hh" #include "Corner.hh" #include "DcalcAnalysisPt.hh" +#include "GraphDelayCalc.hh" namespace sta { @@ -64,36 +66,6 @@ DelayCalcBase::reduceParasitic(const Parasitic *parasitic_network, delete pin_iter; } -TimingModel * -DelayCalcBase::model(const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) const -{ - const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); - const TimingArc *corner_arc = arc->cornerArc(dcalc_ap->libertyIndex()); - return corner_arc->model(op_cond); -} - -GateTimingModel * -DelayCalcBase::gateModel(const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) const -{ - return dynamic_cast(model(arc, dcalc_ap)); -} - -GateTableModel * -DelayCalcBase::gateTableModel(const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) const -{ - return dynamic_cast(model(arc, dcalc_ap)); -} - -CheckTimingModel * -DelayCalcBase::checkModel(const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) const -{ - return dynamic_cast(model(arc, dcalc_ap)); -} - void DelayCalcBase::finishDrvrPin() { @@ -114,10 +86,16 @@ DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin, { LibertyLibrary *load_library = thresholdLibrary(load_pin); - float vth = load_library->inputThreshold(rf); - float vl = load_library->slewLowerThreshold(rf); - float vh = load_library->slewUpperThreshold(rf); - float slew_derate = load_library->slewDerateFromLibrary(); + float vth = 0.5; + float vl = 0.2; + float vh = 0.8; + float slew_derate = 1.0; + if (load_library) { + vth = load_library->inputThreshold(rf); + vl = load_library->slewLowerThreshold(rf); + vh = load_library->slewUpperThreshold(rf); + slew_derate = load_library->slewDerateFromLibrary(); + } wire_delay = -elmore * log(1.0 - vth); load_slew = drvr_slew + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate; load_slew = drvr_slew + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate; @@ -176,7 +154,7 @@ DelayCalcBase::checkDelay(const Pin *check_pin, float related_out_cap, const DcalcAnalysisPt *dcalc_ap) { - CheckTimingModel *model = checkModel(arc, dcalc_ap); + CheckTimingModel *model = arc->checkModel(dcalc_ap); if (model) { float from_slew1 = delayAsFloat(from_slew); float to_slew1 = delayAsFloat(to_slew); @@ -197,7 +175,7 @@ DelayCalcBase::reportCheckDelay(const Pin *check_pin, const DcalcAnalysisPt *dcalc_ap, int digits) { - CheckTimingModel *model = checkModel(arc, dcalc_ap); + CheckTimingModel *model = arc->checkModel(dcalc_ap); if (model) { float from_slew1 = delayAsFloat(from_slew); float to_slew1 = delayAsFloat(to_slew); @@ -219,4 +197,33 @@ DelayCalcBase::pinPvt(const Pin *pin, return pvt; } +void +DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArg &gate, + const DcalcAnalysisPt *dcalc_ap) +{ + 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, + nullptr, this, load_cap, + parasitic); + gate.setLoadCap(load_cap); + gate.setParasitic(parasitic); + 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.setInSlew(in_slew); + } +} + +void +DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, + const DcalcAnalysisPt *dcalc_ap) +{ + for (ArcDcalcArg &gate : gates) + setDcalcArgParasiticSlew(gate, dcalc_ap); +} + } // namespace diff --git a/dcalc/DelayCalcBase.hh b/dcalc/DelayCalcBase.hh index 76137d0b..1726d8b0 100644 --- a/dcalc/DelayCalcBase.hh +++ b/dcalc/DelayCalcBase.hh @@ -22,6 +22,7 @@ namespace sta { class GateTableModel; +// ArcDelayCalc helper functions. class DelayCalcBase : public ArcDelayCalc { public: @@ -32,7 +33,10 @@ public: const Net *net, const Corner *corner, const MinMaxAll *min_max) override; - + void setDcalcArgParasiticSlew(ArcDcalcArg &gate, + const DcalcAnalysisPt *dcalc_ap) override; + void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, + const DcalcAnalysisPt *dcalc_ap) override; ArcDelay checkDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, @@ -50,14 +54,6 @@ public: int digits) override; protected: - GateTimingModel *gateModel(const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) const; - GateTableModel *gateTableModel(const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) const; - CheckTimingModel *checkModel(const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) const; - TimingModel *model(const TimingArc *arc, - const DcalcAnalysisPt *dcalc_ap) const; // Find the liberty library to use for logic/slew thresholds. LibertyLibrary *thresholdLibrary(const Pin *load_pin); // Adjust load_delay and load_slew from driver thresholds to load thresholds. diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 70cf302f..c75b4990 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -1501,7 +1501,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, const LibertyCell *drvr_cell = arc->from()->libertyCell(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); - GateTableModel *table_model = gateTableModel(arc, dcalc_ap); + GateTableModel *table_model = arc->gateTableModel(dcalc_ap); if (table_model && parasitic) { float in_slew1 = delayAsFloat(in_slew); float c2, rpi, c1; @@ -1516,9 +1516,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin, dcalc_result.setGateDelay(gate_delay); dcalc_result.setDrvrSlew(drvr_slew); - for (auto load_pin_index : load_pin_index_map) { - const Pin *load_pin = load_pin_index.first; - size_t load_idx = load_pin_index.second; + for (const auto [load_pin, load_idx] : load_pin_index_map) { ArcDelay wire_delay; Slew load_slew; loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic, @@ -1595,7 +1593,7 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, int digits) { gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, dcalc_ap); - GateTimingModel *model = gateModel(arc, dcalc_ap); + GateTableModel *model = arc->gateTableModel(dcalc_ap); float c_eff = 0.0; string result; if (parasitic && dmp_alg_) { diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh index fadc0140..8ee9eb3d 100644 --- a/dcalc/DmpCeff.hh +++ b/dcalc/DmpCeff.hh @@ -34,6 +34,7 @@ class DmpCeffDelayCalc : public LumpedCapDelayCalc public: DmpCeffDelayCalc(StaState *sta); virtual ~DmpCeffDelayCalc(); + bool reduceSupported() const override { return true; } ArcDcalcResult gateDelay(const Pin *drvr_pin, const TimingArc *arc, const Slew &in_slew, diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 8006a0f8..9745a0cc 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -36,6 +36,7 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc public: DmpCeffElmoreDelayCalc(StaState *sta); ArcDelayCalc *copy() override; + const char *name() const override { return "dmp_ceff_elmore"; } ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, @@ -81,9 +82,7 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *, { ArcDcalcResult dcalc_result(load_pin_index_map.size()); LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); - for (auto load_pin_index : load_pin_index_map) { - const Pin *load_pin = load_pin_index.first; - size_t load_idx = load_pin_index.second; + for (auto [load_pin, load_idx] : load_pin_index_map) { ArcDelay wire_delay = 0.0; Slew load_slew = in_slew; bool elmore_exists = false; @@ -130,6 +129,7 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc public: DmpCeffTwoPoleDelayCalc(StaState *sta); ArcDelayCalc *copy() override; + const char *name() const override { return "dmp_ceff_two_pole"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; @@ -257,9 +257,7 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *, ArcDelay wire_delay = 0.0; Slew load_slew = in_slew; LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); - for (auto load_pin_index : load_pin_index_map) { - const Pin *load_pin = load_pin_index.first; - size_t load_idx = load_pin_index.second; + 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 (pole_residue) { diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 9722c76d..25405f34 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -366,12 +366,12 @@ GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex, float drive_res; drive->driveResistance(rf, cnst_min_max, drive_res, exists); const Parasitic *parasitic; - float cap; + float load_cap; parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc, - cap, parasitic); + load_cap, parasitic); if (exists) { - drive_delay = cap * drive_res; - slew = cap * drive_res; + 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)) @@ -796,7 +796,7 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, else initWireDelays(drvr_vertex); bool delay_changed = false; - bool has_delays = false; + array delay_exists = {false, false}; VertexInEdgeIterator edge_iter(drvr_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); @@ -804,14 +804,14 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, // Don't let disabled edges set slews that influence downstream delays. if (search_pred_->searchFrom(from_vertex) && search_pred_->searchThru(edge) - && !edge->role()->isLatchDtoQ()) { + && !edge->role()->isLatchDtoQ()) delay_changed |= findDriverEdgeDelays(drvr_vertex, multi_drvr, edge, - arc_delay_calc); - has_delays = true; - } + arc_delay_calc, delay_exists); + } + for (auto rf : RiseFall::range()) { + if (!delay_exists[rf->index()]) + zeroSlewAndWireDelays(drvr_vertex, rf); } - if (!has_delays) - zeroSlewAndWireDelays(drvr_vertex); if (delay_changed && observer_) observer_->delayChangedTo(drvr_vertex); return delay_changed; @@ -840,8 +840,9 @@ GraphDelayCalc::findLatchEdgeDelays(Edge *edge) Instance *drvr_inst = network_->instance(drvr_pin); debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s", sdc_network_->pathName(drvr_inst)); + array delay_exists = {false, false}; bool delay_changed = findDriverEdgeDelays(drvr_vertex, nullptr, edge, - arc_delay_calc_); + arc_delay_calc_, delay_exists); if (delay_changed && observer_) observer_->delayChangedTo(drvr_vertex); } @@ -850,17 +851,20 @@ bool GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, - ArcDelayCalc *arc_delay_calc) + ArcDelayCalc *arc_delay_calc, + array &delay_exists) { Vertex *from_vertex = edge->from(graph_); const TimingArcSet *arc_set = edge->timingArcSet(); bool delay_changed = false; LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { - for (const TimingArc *arc : arc_set->arcs()) + for (const TimingArc *arc : arc_set->arcs()) { delay_changed |= findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc, load_pin_index_map, dcalc_ap, arc_delay_calc); + delay_exists[arc->toEdge()->asRiseFall()->index()] = true; + } } if (delay_changed && observer_) { observer_->delayChangedFrom(from_vertex); @@ -908,8 +912,7 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, edge, arc, dcalc_ap, arc_delay_calc); ArcDcalcResultSeq dcalc_results = - arc_delay_calc->gateDelays(dcalc_args, load_cap, load_pin_index_map, - dcalc_ap); + arc_delay_calc->gateDelays(dcalc_args, load_pin_index_map, dcalc_ap); 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]; @@ -959,9 +962,12 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex, const RiseFall *drvr_rf = arc1->toEdge()->asRiseFall(); const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, dcalc_ap); const Pin *drvr_pin1 = drvr_vertex1->pin(); - Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin1, drvr_rf, - dcalc_ap); - dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew, parasitic); + float load_cap; + const Parasitic *parasitic; + parasiticLoad(drvr_pin1, drvr_rf, dcalc_ap, multi_drvr, arc_delay_calc, + load_cap, parasitic); + dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew, + load_cap, parasitic); } } return dcalc_args; @@ -1057,7 +1063,7 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge, // Merge slews. const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - if (delayGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax(), this) + if (delayGreater(gate_slew, drvr_slew, slew_min_max, this) && !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max) && !edge->role()->isLatchDtoQ()) graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); @@ -1328,30 +1334,29 @@ GraphDelayCalc::initSlew(Vertex *vertex) } void -GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex) +GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex, + const RiseFall *rf) { for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { DcalcAPIndex ap_index = dcalc_ap->index(); const MinMax *slew_min_max = dcalc_ap->slewMinMax(); - for (auto rf : RiseFall::range()) { - // 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()); - } + // 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()); + } - // 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, slew_min_max)) + graph_->setSlew(load_vertex, rf, ap_index, 0.0); } } } @@ -1377,14 +1382,23 @@ GraphDelayCalc::initWireDelays(Vertex *drvr_vertex) } } -// Use clock slew for register/latch clk->q edges. Slew GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex, const RiseFall *from_rf, const Edge *edge, const DcalcAnalysisPt *dcalc_ap) { - const TimingRole *role = edge->role(); + return edgeFromSlew(from_vertex, from_rf, edge->role(), dcalc_ap); +} + +// Use clock slew for register/latch clk->q edges. +Slew +GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex, + const RiseFall *from_rf, + const TimingRole *role, + const DcalcAnalysisPt *dcalc_ap) +{ + if (role->genericRole() == TimingRole::regClkToQ() && clk_network_->isIdealClock(from_vertex->pin())) return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 67cecec0..a06853bd 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -122,7 +122,7 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) { - GateTimingModel *model = gateModel(arc, dcalc_ap); + GateTimingModel *model = arc->gateModel(dcalc_ap); debugPrint(debug_, "delay_calc", 3, " in_slew = %s load_cap = %s lumped", delayAsString(in_slew, this), @@ -155,9 +155,7 @@ LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library, dcalc_result.setGateDelay(gate_delay); dcalc_result.setDrvrSlew(drvr_slew); - for (auto load_pin_index : load_pin_index_map) { - const Pin *load_pin = load_pin_index.first; - size_t load_idx = load_pin_index.second; + for (const auto [load_pin, load_idx] : load_pin_index_map) { ArcDelay wire_delay = 0.0; thresholdAdjust(load_pin, drvr_library, rf, wire_delay, drvr_slew); dcalc_result.setWireDelay(load_idx, wire_delay); @@ -176,7 +174,7 @@ LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin, const DcalcAnalysisPt *dcalc_ap, int digits) { - GateTimingModel *model = gateModel(arc, dcalc_ap); + GateTimingModel *model = arc->gateModel(dcalc_ap); if (model) { float in_slew1 = delayAsFloat(in_slew); return model->reportGateDelay(pinPvt(check_pin, dcalc_ap), in_slew1, load_cap, diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index b62a2faf..06239341 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -27,9 +27,11 @@ class LumpedCapDelayCalc : public ParallelDelayCalc public: LumpedCapDelayCalc(StaState *sta); ArcDelayCalc *copy() override; + const char *name() const override { return "lumped_cap"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; + bool reduceSupported() const override { return true; } Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, diff --git a/dcalc/ParallelDelayCalc.cc b/dcalc/ParallelDelayCalc.cc index 732a239f..2648797c 100644 --- a/dcalc/ParallelDelayCalc.cc +++ b/dcalc/ParallelDelayCalc.cc @@ -33,26 +33,24 @@ ParallelDelayCalc::ParallelDelayCalc(StaState *sta): ArcDcalcResultSeq ParallelDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, - float load_cap, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) { if (dcalc_args.size() == 1) { ArcDcalcArg &dcalc_arg = dcalc_args[0]; ArcDcalcResult dcalc_result = gateDelay(dcalc_arg.drvrPin(), dcalc_arg.arc(), - dcalc_arg.inSlew(), - load_cap, dcalc_arg.parasitic(), + dcalc_arg.inSlew(), dcalc_arg.loadCap(), + dcalc_arg.parasitic(), load_pin_index_map, dcalc_ap); ArcDcalcResultSeq dcalc_results; dcalc_results.push_back(dcalc_result); return dcalc_results; } - return gateDelaysParallel(dcalc_args, load_cap, load_pin_index_map, dcalc_ap); + return gateDelaysParallel(dcalc_args, load_pin_index_map, dcalc_ap); } ArcDcalcResultSeq ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, - float load_cap, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) { @@ -74,7 +72,7 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, ArcDelay intrinsic_delay = intrinsic_result.gateDelay(); intrinsic_delays[drvr_idx] = intrinsic_result.gateDelay(); - ArcDcalcResult gate_result = gateDelay(drvr_pin, arc, in_slew, load_cap, + ArcDcalcResult gate_result = gateDelay(drvr_pin, arc, in_slew, dcalc_arg.loadCap(), dcalc_arg.parasitic(), load_pin_index_map, dcalc_ap); ArcDelay gate_delay = gate_result.gateDelay(); @@ -88,8 +86,7 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, slew_sum += 1.0 / drvr_slew; dcalc_result.setLoadCount(load_pin_index_map.size()); - for (auto load_pin_index : load_pin_index_map) { - size_t load_idx = load_pin_index.second; + for (const auto [load_pin, load_idx] : load_pin_index_map) { dcalc_result.setWireDelay(load_idx, gate_result.wireDelay(load_idx)); dcalc_result.setLoadSlew(load_idx, gate_result.loadSlew(load_idx)); } diff --git a/dcalc/ParallelDelayCalc.hh b/dcalc/ParallelDelayCalc.hh index 7f361218..06ddbda7 100644 --- a/dcalc/ParallelDelayCalc.hh +++ b/dcalc/ParallelDelayCalc.hh @@ -29,12 +29,10 @@ class ParallelDelayCalc : public DelayCalcBase public: ParallelDelayCalc(StaState *sta); ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args, - float load_cap, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) override; protected: ArcDcalcResultSeq gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, - float load_cap, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap); }; diff --git a/dcalc/PrimaDelayCalc.cc b/dcalc/PrimaDelayCalc.cc index b1d580a8..32f6cf9a 100644 --- a/dcalc/PrimaDelayCalc.cc +++ b/dcalc/PrimaDelayCalc.cc @@ -57,6 +57,7 @@ PrimaDelayCalc::PrimaDelayCalc(StaState *sta) : dcalc_args_(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), @@ -71,6 +72,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) : dcalc_args_(nullptr), load_pin_index_map_(nullptr), pin_node_map_(network_), + node_index_map_(ParasiticNodeLess(parasitics_, network_)), prima_order_(dcalc.prima_order_), make_waveforms_(false), waveform_drvr_pin_(nullptr), @@ -184,22 +186,19 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin, const DcalcAnalysisPt *dcalc_ap) { ArcDcalcArgSeq dcalc_args; - dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, parasitic); - ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_cap, - load_pin_index_map, dcalc_ap); + 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); return dcalc_results[0]; } ArcDcalcResultSeq PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, - float load_cap, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) { dcalc_args_ = &dcalc_args; load_pin_index_map_ = &load_pin_index_map; drvr_count_ = dcalc_args.size(); - load_cap_ = load_cap; dcalc_ap_ = dcalc_ap; drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall(); parasitic_network_ = dcalc_args[0].parasitic(); @@ -208,14 +207,14 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, output_waveforms_.resize(drvr_count_); for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; - GateTableModel *table_model = gateTableModel(dcalc_arg.arc(), dcalc_ap); + GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(dcalc_ap); if (table_model && dcalc_arg.parasitic()) { OutputWaveforms *output_waveforms = table_model->outputWaveforms(); Slew in_slew = dcalc_arg.inSlew(); if (output_waveforms // Bounds check because extrapolating waveforms does not work for shit. && output_waveforms->slewAxis()->inBounds(in_slew) - && output_waveforms->capAxis()->inBounds(load_cap)) { + && output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) { output_waveforms_[drvr_idx] = output_waveforms; debugPrint(debug_, "ccs_dcalc", 1, "%s %s", dcalc_arg.drvrCell()->name(), @@ -226,7 +225,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(); + drvr_cell->ensureVoltageWaveforms(dcalc_ap); if (drvr_idx == 0) { vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; @@ -241,7 +240,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, } if (failed) - return tableDcalcResults(load_cap); + return tableDcalcResults(); else { simulate(); return dcalcResults(); @@ -249,17 +248,18 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, } ArcDcalcResultSeq -PrimaDelayCalc::tableDcalcResults(float load_cap) +PrimaDelayCalc::tableDcalcResults() { for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; const Pin *drvr_pin = dcalc_arg.drvrPin(); - const RiseFall *rf = dcalc_arg.drvrEdge(); - const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf, dcalc_ap_); - dcalc_arg.setParasitic(parasitic); + if (drvr_pin) { + const RiseFall *rf = dcalc_arg.drvrEdge(); + const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf, dcalc_ap_); + dcalc_arg.setParasitic(parasitic); + } } - return table_dcalc_->gateDelays(*dcalc_args_, load_cap, *load_pin_index_map_, - dcalc_ap_); + return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, dcalc_ap_); } void @@ -902,7 +902,7 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin, const DcalcAnalysisPt *dcalc_ap, int digits) { - GateTimingModel *model = gateModel(arc, dcalc_ap); + GateTimingModel *model = arc->gateModel(dcalc_ap); if (model) { float in_slew1 = delayAsFloat(in_slew); return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap, @@ -950,96 +950,6 @@ PrimaDelayCalc::watchWaveform(const Pin *pin) //////////////////////////////////////////////////////////////// -// Waveform accessors for swig/tcl. -Table1 -PrimaDelayCalc::drvrWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Corner *corner, - const MinMax *min_max) -{ - makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, nullptr, corner, min_max); - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - new FloatSeq(times_)); - Table1 waveform(new FloatSeq(drvr_voltages_), time_axis); - return waveform; -} - -Table1 -PrimaDelayCalc::loadWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) -{ - makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, load_pin, corner, min_max); - TableAxisPtr time_axis = make_shared(TableAxisVariable::time, - new FloatSeq(times_)); - Table1 waveform(new FloatSeq(load_voltages_), time_axis); - return waveform; -} - -Table1 -PrimaDelayCalc::inputWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Corner *corner, - const MinMax *min_max) -{ - LibertyPort *port = network_->libertyPort(in_pin); - if (port) { - DriverWaveform *driver_waveform = port->driverWaveform(in_rf); - const Vertex *in_vertex = graph_->pinLoadVertex(in_pin); - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - Slew in_slew = graph_->slew(in_vertex, in_rf, dcalc_ap->index()); - LibertyLibrary *library = port->libertyLibrary(); - float vdd; - bool vdd_exists; - library->supplyVoltage("VDD", vdd, vdd_exists); - if (!vdd_exists) - report_->error(1751, "VDD not defined in library %s", library->name()); - Table1 in_waveform = driver_waveform->waveform(in_slew); - // Scale the waveform from 0:vdd. - FloatSeq *scaled_values = new FloatSeq; - for (float value : *in_waveform.values()) - scaled_values->push_back(value * vdd); - return Table1(scaled_values, in_waveform.axis1ptr()); - } - return Table1(); -} - -void -PrimaDelayCalc::makeWaveforms(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) -{ - Edge *edge; - const TimingArc *arc; - graph_->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc); - if (arc) { - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - const Parasitic *parasitic = findParasitic(drvr_pin, drvr_rf, dcalc_ap); - if (parasitic) { - make_waveforms_ = true; - waveform_drvr_pin_ = drvr_pin; - waveform_load_pin_ = load_pin; - Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); - graph_delay_calc_->findDriverArcDelays(drvr_vertex, edge, arc, dcalc_ap, this); - make_waveforms_ = false; - waveform_drvr_pin_ = nullptr; - waveform_load_pin_ = nullptr; - } - } -} - -//////////////////////////////////////////////////////////////// - void PrimaDelayCalc::reportMatrix(const char *name, MatrixSd &matrix) diff --git a/dcalc/PrimaDelayCalc.hh b/dcalc/PrimaDelayCalc.hh index 3cf2a779..d89d2e6a 100644 --- a/dcalc/PrimaDelayCalc.hh +++ b/dcalc/PrimaDelayCalc.hh @@ -24,6 +24,7 @@ #include "Map.hh" #include "LumpedCapDelayCalc.hh" #include "ArcDcalcWaveforms.hh" +#include "Parasitics.hh" namespace sta { @@ -41,7 +42,7 @@ using Eigen::Index; using std::map; typedef Map PinNodeMap; -typedef Map NodeIndexMap; +typedef map NodeIndexMap; typedef Map PortIndexMap; typedef SparseMatrix MatrixSd; typedef Map PinLMap; @@ -61,10 +62,12 @@ public: ~PrimaDelayCalc(); ArcDelayCalc *copy() override; void copyState(const StaState *sta) override; + const char *name() const override { return "prima"; } void setPrimaReduceOrder(size_t order); Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; + bool reduceSupported() const override { return false; } Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, @@ -83,7 +86,6 @@ public: const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) override; ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args, - float load_cap, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) override; string reportGateDelay(const Pin *drvr_pin, @@ -96,31 +98,13 @@ public: int digits) override; // Record waveform for drvr/load pin. - void watchPin(const Pin *pin); - void clearWatchPins(); - PinSeq watchPins() const; - Waveform watchWaveform(const Pin *pin); + void watchPin(const Pin *pin) override; + void clearWatchPins() override; + PinSeq watchPins() const override; + Waveform watchWaveform(const Pin *pin) override; - Waveform inputWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Corner *corner, - const MinMax *min_max) override; - Waveform drvrWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Corner *corner, - const MinMax *min_max) override; - Waveform loadWaveform(const Pin *in_pin, - const RiseFall *in_rf, - const Pin *drvr_pin, - const RiseFall *drvr_rf, - const Pin *load_pin, - const Corner *corner, - const MinMax *min_max) override; - protected: - ArcDcalcResultSeq tableDcalcResults(float load_cap); + ArcDcalcResultSeq tableDcalcResults(); void simulate(); void simulate1(const MatrixSd &G, const MatrixSd &C, diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc index 39c02fb5..76fc4a9c 100644 --- a/dcalc/UnitDelayCalc.cc +++ b/dcalc/UnitDelayCalc.cc @@ -62,6 +62,18 @@ UnitDelayCalc::reduceParasitic(const Parasitic *, { } +void +UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArg &, + const DcalcAnalysisPt *) +{ +} + +void +UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArgSeq &, + const DcalcAnalysisPt *) +{ +} + ArcDcalcResult UnitDelayCalc::inputPortDelay(const Pin *, float, @@ -87,7 +99,6 @@ UnitDelayCalc::gateDelay(const Pin *, ArcDcalcResultSeq UnitDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, - float, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *) { diff --git a/dcalc/UnitDelayCalc.hh b/dcalc/UnitDelayCalc.hh index 8d247f5d..d9ed73f8 100644 --- a/dcalc/UnitDelayCalc.hh +++ b/dcalc/UnitDelayCalc.hh @@ -26,9 +26,11 @@ class UnitDelayCalc : public ArcDelayCalc public: UnitDelayCalc(StaState *sta); ArcDelayCalc *copy() override; + const char *name() const override { return "unit"; } Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; + bool reduceSupported() const override { return false; } Parasitic *reduceParasitic(const Parasitic *parasitic_network, const Pin *drvr_pin, const RiseFall *rf, @@ -37,6 +39,10 @@ public: const Net *net, const Corner *corner, const MinMaxAll *min_max) override; + void setDcalcArgParasiticSlew(ArcDcalcArg &gate, + const DcalcAnalysisPt *dcalc_ap) override; + void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, + const DcalcAnalysisPt *dcalc_ap) override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, @@ -52,7 +58,6 @@ public: const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) override; ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args, - float load_cap, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) override; ArcDelay checkDelay(const Pin *check_pin, diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index 22db3f50..1e36b469 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -1,8 +1,26 @@ OpenSTA Timing Analyzer Release Notes ---------------------------------------------- +------------------------------------- This file summarizes user visible changes for each release. +Release 2.6.0 2024/07/?? +------------------------- + +The version of c++ used by OpenSTA is now 17. +The Cudd BBD packager is now required. + +The USE_TCL_READLINE option defaults to ON. + +The variable link_make_black_boxes has been removed. +Use the -no_black_boxes option to the link_design command. + + link_design [-no_black_blocks] [top_cell_name] + +The all_inputs command now supports -no_clocks to remove clocks +inputs from the returned list. + + all_inputs [-no_clocks] + Release 2.5.0 2024/01/17 ------------------------- @@ -24,6 +42,11 @@ timing groups. report_clock_skew -include_internal_latency report_clock_latency -include_internal_latency +The all_inputs command now supports the -no_clocks argument to exclude +clocks from the list. + + all_inputs [-no_clocks] + Release 2.4.0 2023/01/19 ------------------------- diff --git a/doc/CodingGuidelines.txt b/doc/CodingGuidelines.txt index 1c6f8dce..001e5900 100644 --- a/doc/CodingGuidelines.txt +++ b/doc/CodingGuidelines.txt @@ -8,6 +8,7 @@ member variable - snake case with trailing underscore (member_variable_) Trailing underscore prevents conflict with accessor member function name. function - lower camel case (functionName) +variable - snake case comments - use capitalized sentences that end with periods C++ code files should use a .cc file extension @@ -55,7 +56,7 @@ this: instead, write this: foo = (char *) malloc (sizeof *foo); - if (foo == 0) + if (foo == nullptr) fatal ("virtual memory exhausted"); diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 977ee6df..6fd8fedb 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf index f69f758b..c39a5717 100644 Binary files a/doc/OpenSTA.pdf and b/doc/OpenSTA.pdf differ diff --git a/graph/Graph.cc b/graph/Graph.cc index 2e21e0a4..d30c464c 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -757,10 +757,8 @@ Graph::makeArcDelayTables(DcalcAPIndex ap_count) { if (have_arc_delays_) { arc_delays_.resize(ap_count); - for (DcalcAPIndex i = 0; i < ap_count; i++) { - DelayTable *table = new DelayTable(); - arc_delays_[i] = table; - } + for (DcalcAPIndex i = 0; i < ap_count; i++) + arc_delays_[i] = new DelayTable(); } } @@ -1041,7 +1039,7 @@ Graph::setPeriodCheckAnnotation(const Pin *pin, float period) { if (period_check_annotations_ == nullptr) - period_check_annotations_ = new PeriodCheckAnnotations; + period_check_annotations_ = new PeriodCheckAnnotations(network_); float *periods = period_check_annotations_->findKey(pin); if (periods == nullptr) { periods = new float[ap_count_]; @@ -1057,10 +1055,8 @@ void Graph::removePeriodCheckAnnotations() { if (period_check_annotations_) { - for (auto pin_floats : *period_check_annotations_) { - float *periods = pin_floats.second; + for (const auto [pin, periods] : *period_check_annotations_) delete [] periods; - } delete period_check_annotations_; period_check_annotations_ = nullptr; } diff --git a/graph/Graph.i b/graph/Graph.i new file mode 100644 index 00000000..9ee71255 --- /dev/null +++ b/graph/Graph.i @@ -0,0 +1,459 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + +%module graph + +%{ +#include "Graph.hh" +#include "FuncExpr.hh" +#include "TimingRole.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Clock.hh" +#include "Corner.hh" +#include "Search.hh" +#include "Sta.hh" + +namespace sta { + +Graph * +cmdGraph(); + +} // namespace + +using namespace sta; + +%} + +//////////////////////////////////////////////////////////////// +// +// Empty class definitions to make swig happy. +// Private constructor/destructor so swig doesn't emit them. +// +//////////////////////////////////////////////////////////////// + +class Vertex +{ +private: + Vertex(); + ~Vertex(); +}; + +class Edge +{ +private: + Edge(); + ~Edge(); +}; + +class VertexIterator +{ +private: + VertexIterator(); + ~VertexIterator(); +}; + +class VertexInEdgeIterator +{ +private: + VertexInEdgeIterator(); + ~VertexInEdgeIterator(); +}; + +class VertexOutEdgeIterator +{ +private: + VertexOutEdgeIterator(); + ~VertexOutEdgeIterator(); +}; + +%inline %{ + +int +graph_vertex_count() +{ + return cmdGraph()->vertexCount(); +} + +int +graph_edge_count() +{ + return cmdGraph()->edgeCount(); +} + +int +graph_arc_count() +{ + return cmdGraph()->arcCount(); +} + +VertexIterator * +vertex_iterator() +{ + return new VertexIterator(cmdGraph()); +} + +void +set_arc_delay(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMaxAll *min_max, + float delay) +{ + cmdGraph(); + Sta::sta()->setArcDelay(edge, arc, corner, min_max, delay); +} + +void +set_annotated_slew(Vertex *vertex, + const Corner *corner, + const MinMaxAll *min_max, + const RiseFallBoth *rf, + float slew) +{ + cmdGraph(); + Sta::sta()->setAnnotatedSlew(vertex, corner, min_max, rf, slew); +} + +// Remove all delay and slew annotations. +void +remove_delay_slew_annotations() +{ + cmdGraph(); + Sta::sta()->removeDelaySlewAnnotations(); +} + +%} // inline + +//////////////////////////////////////////////////////////////// +// +// Object Methods +// +//////////////////////////////////////////////////////////////// + +%extend Vertex { +Pin *pin() { return self->pin(); } +bool is_bidirect_driver() { return self->isBidirectDriver(); } +int level() { return Sta::sta()->vertexLevel(self); } +int tag_group_index() { return self->tagGroupIndex(); } + +Slew +slew(const RiseFall *rf, + const MinMax *min_max) +{ + Sta *sta = Sta::sta(); + return sta->vertexSlew(self, rf, min_max); +} + +Slew +slew_corner(const RiseFall *rf, + const Corner *corner, + const MinMax *min_max) +{ + Sta *sta = Sta::sta(); + return sta->vertexSlew(self, rf, corner, min_max); +} + +VertexOutEdgeIterator * +out_edge_iterator() +{ + return new VertexOutEdgeIterator(self, Sta::sta()->graph()); +} + +VertexInEdgeIterator * +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; +} + +StringSeq +arrivals_clk_delays(const RiseFall *rf, + Clock *clk, + const RiseFall *clk_rf, + int digits) +{ + Sta *sta = Sta::sta(); + StringSeq arrivals; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_rf); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + arrivals.push_back(delayAsString(sta->vertexArrival(self, rf, clk_edge, + path_ap, nullptr), + sta, digits)); + } + return arrivals; +} + +FloatSeq +requireds_clk(const RiseFall *rf, + Clock *clk, + const RiseFall *clk_rf) +{ + Sta *sta = Sta::sta(); + FloatSeq reqs; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_rf); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + reqs.push_back(delayAsFloat(sta->vertexRequired(self, rf, clk_edge, + path_ap))); + } + return reqs; +} + +StringSeq +requireds_clk_delays(const RiseFall *rf, + Clock *clk, + const RiseFall *clk_rf, + int digits) +{ + Sta *sta = Sta::sta(); + StringSeq reqs; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_rf); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + reqs.push_back(delayAsString(sta->vertexRequired(self, rf, clk_edge, path_ap), + sta, digits)); + } + return reqs; +} + +Slack +slack(MinMax *min_max) +{ + Sta *sta = Sta::sta(); + return sta->vertexSlack(self, min_max); +} + +FloatSeq +slacks(RiseFall *rf) +{ + Sta *sta = Sta::sta(); + FloatSeq slacks; + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, path_ap))); + } + return slacks; +} + +// Slack with respect to a clock rise/fall edge. +FloatSeq +slacks_clk(const RiseFall *rf, + Clock *clk, + const RiseFall *clk_rf) +{ + Sta *sta = Sta::sta(); + FloatSeq slacks; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_rf); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, clk_edge, + path_ap))); + } + return slacks; +} + +StringSeq +slacks_clk_delays(const RiseFall *rf, + Clock *clk, + const RiseFall *clk_rf, + int digits) +{ + Sta *sta = Sta::sta(); + StringSeq slacks; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_rf); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + slacks.push_back(delayAsString(sta->vertexSlack(self, rf, clk_edge, + path_ap), + sta, digits)); + } + return slacks; +} + +VertexPathIterator * +path_iterator(const RiseFall *rf, + const MinMax *min_max) +{ + return Sta::sta()->vertexPathIterator(self, rf, min_max); +} + +bool +has_downstream_clk_pin() +{ + 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 { +Vertex *from() { return self->from(Sta::sta()->graph()); } +Vertex *to() { return self->to(Sta::sta()->graph()); } +Pin *from_pin() { return self->from(Sta::sta()->graph())->pin(); } +Pin *to_pin() { return self->to(Sta::sta()->graph())->pin(); } +TimingRole *role() { return self->role(); } +const char *sense() { return timingSenseString(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_cond_default() +{ return Sta::sta()->isDisabledCondDefault(self); } +PinSet +disabled_constant_pins() { return Sta::sta()->disabledConstantPins(self); } +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 timingSenseString(Sta::sta()->simTimingSense(self));} + +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))); + return delays; +} + +StringSeq +arc_delay_strings(TimingArc *arc, + 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), + sta, digits)); + return delays; +} + +bool +delay_annotated(TimingArc *arc, + const Corner *corner, + const MinMax *min_max) +{ + DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + return Sta::sta()->arcDelayAnnotated(self, arc, dcalc_ap); +} + +float +arc_delay(TimingArc *arc, + const Corner *corner, + const MinMax *min_max) +{ + DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + return delayAsFloat(Sta::sta()->arcDelay(self, arc, dcalc_ap)); +} + +const char * +cond() +{ + FuncExpr *cond = self->timingArcSet()->cond(); + if (cond) + return cond->asString(); + else + return nullptr; +} + +const char * +mode_name() +{ + return self->timingArcSet()->modeName(); +} + +const char * +mode_value() +{ + return self->timingArcSet()->modeValue(); +} + +const char * +latch_d_to_q_en() +{ + if (self->role() == TimingRole::latchDtoQ()) { + Sta *sta = Sta::sta(); + const Network *network = sta->cmdNetwork(); + const Graph *graph = sta->graph(); + Pin *from_pin = self->from(graph)->pin(); + Instance *inst = network->instance(from_pin); + LibertyCell *lib_cell = network->libertyCell(inst); + TimingArcSet *d_q_set = self->timingArcSet(); + const LibertyPort *enable_port; + const FuncExpr *enable_func; + const RiseFall *enable_rf; + lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf); + if (enable_port) + return stringPrintTmp("%s %s", enable_port->name(), enable_rf->asString()); + } + return ""; +} + +} // Edge methods + +%extend VertexIterator { +bool has_next() { return self->hasNext(); } +Vertex *next() { return self->next(); } +void finish() { delete self; } +} + +%extend VertexInEdgeIterator { +bool has_next() { return self->hasNext(); } +Edge *next() { return self->next(); } +void finish() { delete self; } +} + +%extend VertexOutEdgeIterator { +bool has_next() { return self->hasNext(); } +Edge *next() { return self->next(); } +void finish() { delete self; } +} diff --git a/tcl/Graph.tcl b/graph/Graph.tcl similarity index 100% rename from tcl/Graph.tcl rename to graph/Graph.tcl diff --git a/graph/GraphCmp.cc b/graph/GraphCmp.cc index 6241c16d..a4ca3dd9 100644 --- a/graph/GraphCmp.cc +++ b/graph/GraphCmp.cc @@ -37,7 +37,7 @@ VertexNameLess::operator()(const Vertex *vertex1, //////////////////////////////////////////////////////////////// EdgeLess::EdgeLess(const Network *network, - Graph *graph) : + Graph *&graph) : pin_less_(network), graph_(graph) { diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index b826def6..159495ea 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -40,6 +40,10 @@ class Corner; class Parasitic; class DcalcAnalysisPt; class MultiDrvrNet; +class ArcDcalcArg; + +typedef std::vector ArcDcalcArgPtrSeq; +typedef std::vector ArcDcalcArgSeq; // Driver load pin -> index in driver loads. typedef map LoadPinIndexMap; @@ -56,6 +60,7 @@ public: Edge *edge, const TimingArc *arc, const Slew in_slew, + float load_cap, const Parasitic *parasitic); ArcDcalcArg(const Pin *in_pin, const Pin *drvr_pin, @@ -65,6 +70,7 @@ public: const Pin *inPin() const { return in_pin_; } const RiseFall *inEdge() const; const Pin *drvrPin() const { return drvr_pin_; } + Vertex *drvrVertex(const Graph *graph) const; LibertyCell *drvrCell() const; const LibertyLibrary *drvrLibrary() const; const RiseFall *drvrEdge() const; @@ -74,9 +80,12 @@ public: Slew inSlew() const { return in_slew_; } float inSlewFlt() const; void setInSlew(Slew in_slew); - const Parasitic *parasitic() { return parasitic_; } + const Parasitic *parasitic() const { return parasitic_; } void setParasitic(const Parasitic *parasitic); + float loadCap() const { return load_cap_; } + void setLoadCap(float load_cap); float inputDelay() const { return input_delay_; } + void setInputDelay(float input_delay); protected: const Pin *in_pin_; @@ -84,10 +93,21 @@ protected: Edge *edge_; const TimingArc *arc_; Slew in_slew_; + float load_cap_; const Parasitic *parasitic_; float input_delay_; }; + +ArcDcalcArg +makeArcDcalcArg(const char *inst_name, + const char *in_port_name, + const char *in_rf_name, + const char *drvr_port_name, + const char *drvr_rf_name, + const char *input_delay_str, + const StaState *sta); + // Arc delay calc result. class ArcDcalcResult { @@ -127,6 +147,9 @@ typedef vector ArcDcalcResultSeq; // DmpCeffElmoreDelayCalc // DmpCeffTwoPoleDelayCalc // ArnoldiDelayCalc +// CcsCeffDelayCalc +// CcsSimfDelayCalc +// PrimafDelayCalc // Abstract class for the graph delay calculator traversal to interface // to a delay calculator primitive. @@ -136,12 +159,14 @@ public: explicit ArcDelayCalc(StaState *sta); virtual ~ArcDelayCalc() {} virtual ArcDelayCalc *copy() = 0; + virtual const char *name() const = 0; // 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; + 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, @@ -154,6 +179,11 @@ public: const Net *net, const Corner *corner, const MinMaxAll *min_max) = 0; + // Set the in_slew, load_cap, parasitic for gates. + virtual void setDcalcArgParasiticSlew(ArcDcalcArg &gate, + const DcalcAnalysisPt *dcalc_ap) = 0; + virtual void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates, + const DcalcAnalysisPt *dcalc_ap) = 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, @@ -172,6 +202,7 @@ public: const Parasitic *parasitic, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) = 0; + // deprecated 2024-02-27 virtual void gateDelay(const TimingArc *arc, const Slew &in_slew, float load_cap, @@ -185,7 +216,6 @@ public: // Find gate delays and slews for parallel gates. virtual ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args, - float load_cap, const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap) = 0; diff --git a/include/sta/Bdd.hh b/include/sta/Bdd.hh index bc95283d..0087fde6 100644 --- a/include/sta/Bdd.hh +++ b/include/sta/Bdd.hh @@ -21,12 +21,8 @@ #include "StaState.hh" #include "LibertyClass.hh" -#if CUDD -#include "cudd.h" -#else struct DdNode; struct DdManager; -#endif namespace sta { diff --git a/include/sta/EnumNameMap.hh b/include/sta/EnumNameMap.hh index e3f3172e..230cf8d0 100644 --- a/include/sta/EnumNameMap.hh +++ b/include/sta/EnumNameMap.hh @@ -49,8 +49,8 @@ template EnumNameMap::EnumNameMap(initializer_list> enum_names) : enum_map_(enum_names) { - for (auto iter = enum_map_.begin(); iter != enum_map_.end(); iter++) - name_map_[iter->second] = iter->first; + for (const auto& [key, name] : enum_map_) + name_map_[name] = key; } template diff --git a/include/sta/ExceptionPath.hh b/include/sta/ExceptionPath.hh index 92e8c47a..c50b5c82 100644 --- a/include/sta/ExceptionPath.hh +++ b/include/sta/ExceptionPath.hh @@ -49,6 +49,8 @@ public: int priority, const char *comment); virtual ~ExceptionPath(); + size_t id() const { return id_; } + void setId(size_t id); virtual bool isFalse() const { return false; } virtual bool isLoop() const { return false; } virtual bool isMultiCycle() const { return false; } @@ -128,6 +130,7 @@ protected: const MinMaxAll *min_max_; bool own_pts_; int priority_; + size_t id_; // Unique ID assigned by Sdc. ExceptionState *states_; }; diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 8bf525bc..efa0637d 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -46,7 +46,7 @@ typedef ArrayTable RequiredsTable; typedef ArrayTable PrevPathsTable; typedef Map PinVertexMap; typedef Iterator VertexEdgeIterator; -typedef Map PeriodCheckAnnotations; +typedef Map PeriodCheckAnnotations; typedef Vector DelayTableSeq; typedef ObjectId EdgeId; typedef ObjectId ArrivalId; @@ -352,13 +352,13 @@ protected: EdgeId in_edges_; // Edges to this vertex. EdgeId out_edges_; // Edges from this vertex. - // 4 bytes + // 32 bits unsigned int tag_group_index_:tag_group_index_bits; // 24 // Each bit corresponds to a different BFS queue. unsigned int bfs_in_queue_:int(BfsIndex::bits); // 4 unsigned int slew_annotated_:slew_annotated_bits; - // 4 bytes (32 bits) + // 32 bits unsigned int level_:Graph::vertex_level_bits; // Levelization search state. // LevelColor gcc barfs if this is dcl'd. @@ -369,6 +369,8 @@ protected: // This flag distinguishes the driver and load vertices. bool is_bidirect_drvr_:1; bool is_reg_clk_:1; + + // 15 bits bool is_disabled_constraint_:1; bool is_gated_clk_enable_:1; // Constrained by timing check edge. @@ -379,7 +381,6 @@ protected: bool has_downstream_clk_pin_:1; bool crpr_path_pruning_disabled_:1; bool requireds_pruned_:1; - unsigned object_idx_:VertexTable::idx_bits; private: @@ -441,6 +442,7 @@ protected: EdgeId vertex_out_next_; // Vertex out edges doubly linked list. EdgeId vertex_out_prev_; ArcId arc_delays_; + // 16 bits bool delay_annotation_is_incremental_:1; bool is_bidirect_inst_path_:1; bool is_bidirect_net_path_:1; diff --git a/include/sta/GraphCmp.hh b/include/sta/GraphCmp.hh index 8753d69e..3ee38807 100644 --- a/include/sta/GraphCmp.hh +++ b/include/sta/GraphCmp.hh @@ -37,13 +37,13 @@ class EdgeLess { public: EdgeLess(const Network *network, - Graph *graph); + Graph *&graph); bool operator()(const Edge *edge1, const Edge *edge2) const; private: const PinPathNameLess pin_less_; - Graph *graph_; + Graph *&graph_; }; void diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index c25ac2cf..1ed9bea3 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -18,6 +18,7 @@ #include #include +#include #include "Map.hh" #include "NetworkClass.hh" @@ -32,6 +33,7 @@ namespace sta { using std::vector; using std::map; +using std::array; class DelayCalcObserver; class MultiDrvrNet; @@ -118,6 +120,10 @@ public: const RiseFall *from_rf, const Edge *edge, const DcalcAnalysisPt *dcalc_ap); + Slew edgeFromSlew(const Vertex *from_vertex, + const RiseFall *from_rf, + const TimingRole *role, + const DcalcAnalysisPt *dcalc_ap); protected: void seedInvalidDelays(); @@ -171,7 +177,8 @@ protected: bool findDriverEdgeDelays(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, - ArcDelayCalc *arc_delay_calc); + ArcDelayCalc *arc_delay_calc, + array &delay_exists); bool findDriverArcDelays(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, @@ -193,7 +200,8 @@ protected: const TimingArc *&arc); void initWireDelays(Vertex *drvr_vertex); void initRootSlews(Vertex *vertex); - void zeroSlewAndWireDelays(Vertex *drvr_vertex); + void zeroSlewAndWireDelays(Vertex *drvr_vertex, + const RiseFall *rf); void findVertexDelay(Vertex *vertex, ArcDelayCalc *arc_delay_calc, bool propagate); diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index fd8868ec..f507c49e 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -73,7 +73,8 @@ typedef Map ScaledCellMap; typedef Map ScaledPortMap; typedef Map ModeDefMap; typedef Map ModeValueMap; -typedef Map LatchEnableMap; +typedef Map LatchEnableMap; +typedef Vector LatchEnableSeq; typedef Map OcvDerateMap; typedef Vector InternalPowerAttrsSeq; typedef Map SupplyVoltageMap; @@ -472,12 +473,12 @@ public: bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; } TestCell *testCell() const { return test_cell_; } bool isLatchData(LibertyPort *port); - void latchEnable(TimingArcSet *arc_set, + void latchEnable(const TimingArcSet *arc_set, // Return values. - LibertyPort *&enable_port, - FuncExpr *&enable_func, - RiseFall *&enable_rf) const; - RiseFall *latchCheckEnableEdge(TimingArcSet *check_set); + 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); @@ -531,7 +532,7 @@ public: // Check all liberty cells to make sure they exist // for all the defined corners. static void checkLibertyCorners(); - void ensureVoltageWaveforms(); + void ensureVoltageWaveforms(const DcalcAnalysisPt *dcalc_ap); protected: void addPort(ConcretePort *port); @@ -539,18 +540,26 @@ protected: void setLibertyLibrary(LibertyLibrary *library); void makeLatchEnables(Report *report, Debug *debug); - FuncExpr *findLatchEnableFunc(LibertyPort *data, - LibertyPort *enable) const; + FuncExpr *findLatchEnableFunc(const LibertyPort *d, + const LibertyPort *en, + const RiseFall *en_rf) const; LatchEnable *makeLatchEnable(LibertyPort *d, LibertyPort *en, + const RiseFall *en_rf, 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, + const LibertyPort *q, + Report *report); void findDefaultCondArcs(); void translatePresetClrCheckRoles(); - void inferLatchRoles(Debug *debug); + void inferLatchRoles(Report *report, + Debug *debug); void deleteInternalPowerAttrs(); void makeTimingArcMap(Report *report); void makeTimingArcPortMaps(); @@ -596,6 +605,7 @@ protected: LatchEnableMap latch_d_to_q_map_; // Latch EN->D setup to LatchEnable. LatchEnableMap latch_check_map_; + LatchEnableSeq latch_enables_; // Ports that have latch D->Q timing arc sets from them. LibertyPortSet latch_data_ports_; float ocv_arc_depth_; @@ -792,20 +802,20 @@ public: DriverWaveform *driverWaveform(const RiseFall *rf) const; void setDriverWaveform(DriverWaveform *driver_waveform, const RiseFall *rf); - void setClkTreeDelay(const TableModel *model, - const RiseFall *from_rf, - const RiseFall *to_rf, - const MinMax *min_max); - // Should be deprecated. - float clkTreeDelay(float in_slew, - const RiseFall *from_rf, - const MinMax *min_max) const; float clkTreeDelay(float in_slew, const RiseFall *from_rf, const RiseFall *to_rf, const MinMax *min_max) const; - // Assumes input slew of 0.0. - RiseFallMinMax clkTreeDelays() const; + float clkTreeDelay(float in_slew, + const RiseFall *from_rf, + const MinMax *min_max) const; + void setClkTreeDelay(const TableModel *model, + const RiseFall *from_rf, + const RiseFall *to_rf, + const MinMax *min_max); + // deprecated 2024-06-22 + RiseFallMinMax clkTreeDelays() const __attribute__ ((deprecated)); + // deprecated 2024-02-27 RiseFallMinMax clockTreePathDelays() const __attribute__ ((deprecated)); static bool equiv(const LibertyPort *port1, diff --git a/include/sta/Map.hh b/include/sta/Map.hh index 450a812e..5622579c 100644 --- a/include/sta/Map.hh +++ b/include/sta/Map.hh @@ -101,9 +101,7 @@ public: void deleteKeysContents() { - for (auto key_value : this) { - KEY key = key_value.first; - VALUE value = key_value.second; + for (const auto [key, value] : this) { delete key; delete value; } diff --git a/include/sta/MinMax.hh b/include/sta/MinMax.hh index f3d8eab9..e2c67c44 100644 --- a/include/sta/MinMax.hh +++ b/include/sta/MinMax.hh @@ -108,9 +108,9 @@ public: bool matches(const MinMax *min_max) const; bool matches(const MinMaxAll *min_max) const; static MinMaxAll *find(const char *min_max); - // for (auto min_max : min_max->range()) {} + // for (const auto min_max : min_max->range()) {} const std::vector &range() const { return range_; } - // for (auto mm_index : min_max->rangeIndex()) {} + // for (const auto mm_index : min_max->rangeIndex()) {} const std::vector &rangeIndex() const { return range_index_; } private: diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 5f0f1e21..891919ec 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -513,6 +513,7 @@ public: LibertyPort *port, Net *net) = 0; // makePin/connectPin replaced by connect. + // deprecated 2018-09-28 virtual void connectPin(Pin *pin, Net *net) __attribute__ ((deprecated)); // Disconnect pin from net. diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index 52c6caa3..7cf45d33 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -175,6 +175,7 @@ public: // 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)); // Make a subnode of the parasitic network net connected to pin. @@ -188,6 +189,7 @@ public: virtual const Pin *pin(const ParasiticNode *node) const = 0; virtual const Net *net(const ParasiticNode *node, const Network *network) const = 0; + virtual unsigned netId(const ParasiticNode *node) const = 0; virtual bool isExternal(const ParasiticNode *node) const = 0; // Node capacitance to ground. virtual float nodeGndCap(const ParasiticNode *node) const = 0; @@ -308,4 +310,17 @@ private: float coupling_cap_factor_; }; +class ParasiticNodeLess +{ +public: + ParasiticNodeLess(const Parasitics *parasitics, + const Network *network); + ParasiticNodeLess(const ParasiticNodeLess &less); + bool operator()(const ParasiticNode *node1, + const ParasiticNode *node2) const; +private: + const Parasitics *parasitics_; + const Network *network_; +}; + } // namespace diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh index 46a526a2..8c27be9a 100644 --- a/include/sta/PathEnd.hh +++ b/include/sta/PathEnd.hh @@ -136,7 +136,8 @@ public: virtual PathDelay *pathDelay() const; // This returns the crpr signed with respect to the check type. // Positive for setup, negative for hold. - virtual Crpr commonClkPessimism(const StaState *sta) const; + virtual Crpr checkCrpr(const StaState *sta) const; + virtual Crpr crpr(const StaState *sta) const; virtual MultiCyclePath *multiCyclePath() const; virtual TimingArc *checkArc() const { return nullptr; } // PathEndDataCheck data clock path. @@ -244,7 +245,7 @@ public: virtual float targetNonInterClkUncertainty(const StaState *sta) const; virtual float interClkUncertainty(const StaState *sta) const; virtual float targetClkUncertainty(const StaState *sta) const; - virtual Crpr commonClkPessimism(const StaState *sta) const; + virtual Crpr crpr(const StaState *sta) const; virtual Required requiredTime(const StaState *sta) const; virtual Slack slack(const StaState *sta) const; virtual Slack slackNoCrpr(const StaState *sta) const; @@ -426,7 +427,7 @@ public: virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const; virtual Delay targetClkDelay(const StaState *sta) const; virtual Delay targetClkInsertionDelay(const StaState *sta) const; - virtual Crpr commonClkPessimism(const StaState *sta) const; + virtual Crpr crpr(const StaState *sta) const; virtual int exceptPathCmp(const PathEnd *path_end, const StaState *sta) const; diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index ba5fc14e..7f9198ad 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -200,6 +200,8 @@ public: void deleteNetBefore(const Net *net); // SWIG sdc interface. + PortSeq allInputs(bool no_clks); + PortSeq allOutputs(); AnalysisType analysisType() { return analysis_type_; } void setAnalysisType(AnalysisType analysis_type); void setOperatingConditions(OperatingConditions *op_cond, @@ -953,6 +955,13 @@ public: 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; void portExtFanout(const Port *port, const Corner *corner, const MinMax *min_max, @@ -1046,6 +1055,8 @@ public: bool bidirectDrvrSlewFromLoad(const Pin *pin) const; protected: + void portMembers(const Port *port, + PortSeq &ports); void initVariables(); void clearCycleAcctings(); void removeLibertyAnnotations(); @@ -1231,13 +1242,6 @@ protected: void annotateHierClkLatency(); void annotateHierClkLatency(const Pin *hpin, ClockLatency *latency); - void pinCaps(const Pin *pin, - const RiseFall *rf, - const Corner *corner, - const MinMax *min_max, - float &pin_cap, - float &wire_cap, - float &fanout) const; void netCaps(const Pin *drvr_pin, const RiseFall *rf, const Corner *corner, @@ -1252,7 +1256,8 @@ protected: const RiseFall *rf, const Corner *corner, const MinMax *min_max); - float portCapacitance(Instance *inst, LibertyPort *port, + float portCapacitance(Instance *inst, + LibertyPort *port, const RiseFall *rf, const Corner *corner, const MinMax *min_max) const; @@ -1364,6 +1369,7 @@ protected: InstanceSet disabled_clk_gating_checks_inst_; PinSet disabled_clk_gating_checks_pin_; ExceptionPathSet exceptions_; + size_t exception_id_; // Unique ID for exceptions. bool have_thru_hpin_exceptions_; // First pin/clock/instance/net/edge exception point to exception set map. diff --git a/include/sta/SdcClass.hh b/include/sta/SdcClass.hh index 7ef169a7..68f1ef14 100644 --- a/include/sta/SdcClass.hh +++ b/include/sta/SdcClass.hh @@ -85,7 +85,7 @@ typedef MinMaxAll SetupHoldAll; typedef Vector ExceptionThruSeq; typedef Set LibertyPortPairSet; typedef Map DisabledInstancePortsMap; -typedef Map DisabledCellPortsMap; +typedef Map DisabledCellPortsMap; typedef MinMaxValues ClockUncertainties; typedef Set ExceptionPathSet; typedef PinPair EdgePins; @@ -107,12 +107,8 @@ class ExceptionState; class ExceptionStateLess { public: - ExceptionStateLess(const Network *network); bool operator()(const ExceptionState *state1, const ExceptionState *state2) const; - -private: - const Network *network_; }; class ExceptionPath; diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index b5aadae3..52c2ef2d 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -28,6 +28,8 @@ #include "VertexVisitor.hh" #include "SearchClass.hh" #include "PowerClass.hh" +#include "ArcDelayCalc.hh" +#include "CircuitSim.hh" struct Tcl_Interp; @@ -114,9 +116,8 @@ public: // linked network. void readNetlistBefore(); // Return true if successful. - bool linkDesign(const char *top_cell_name); - bool linkMakeBlackBoxes() const; - void setLinkMakeBlackBoxes(bool make); + bool linkDesign(const char *top_cell_name, + bool make_black_boxes); // SDC Swig API. Instance *currentInstance() const; @@ -773,7 +774,7 @@ public: const RiseFallBoth *rf, float slew); void writeSdf(const char *filename, - Corner *corner, + const Corner *corner, char divider, bool include_typ, int digits, @@ -938,7 +939,7 @@ public: // bug that should be reported. void updateTiming(bool full); // Invalidate all delay calculations. Arrivals also invalidated. - void delaysInvalid(); + void delaysInvalid() const; // Invalidate all arrival and required times. void arrivalsInvalid(); PinSet startpointPins(); @@ -1236,7 +1237,7 @@ public: void setCmdCorner(Corner *corner); Corner *findCorner(const char *corner_name); bool multiCorner(); - void makeCorners(StringSet *corner_names); + 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. @@ -1285,6 +1286,24 @@ public: const Corner *corner); PwrActivity findClkedActivity(const Pin *pin); + void writeGateSpice(ArcDcalcArgSeq gates, + 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, + const Corner *corner, + const MinMax *min_max); + void writeGateGnuplot(ArcDcalcArgSeq gates, + PinSet plot_pins, + const char *spice_waveform_filename, + const char *csv_filename, + const char *gnuplot_filename, + const Corner *corner, + const MinMax *min_max); + void writeTimingModel(const char *lib_name, const char *cell_name, const char *filename, @@ -1432,7 +1451,6 @@ protected: ReportPath *report_path_; Power *power_; Tcl_Interp *tcl_interp_; - bool link_make_black_boxes_; bool update_genclks_; EquivCells *equiv_cells_; bool graph_sdc_annotated_; diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 7c29d7b8..31d4d165 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -39,6 +39,7 @@ class Table1; typedef Vector FloatSeq; typedef Vector FloatTable; typedef Vector Table1Seq; +typedef Table1 Waveform; TableAxisVariable stringTableAxisVariable(const char *variable); @@ -66,6 +67,7 @@ public: // Return values. ArcDelay &gate_delay, Slew &drvr_slew) const override; + // deprecated 2024-01-07 // related_out_cap arg removed. void gateDelay(const Pvt *pvt, float in_slew, @@ -455,9 +457,10 @@ public: // Return values. size_t &index, bool &exists) const; + size_t findAxisClosestIndex(float value) const; FloatSeq *values() const { return values_; } - float min() const { return (*values_)[0]; } - float max() const { return (*values_)[values_->size() - 1]; } + float min() const; + float max() const; private: TableAxisVariable variable_; @@ -492,26 +495,43 @@ public: const RiseFall *rf() const { return rf_; } const TableAxis *slewAxis() const { return slew_axis_.get(); } const TableAxis *capAxis() const { return cap_axis_.get(); } - Table1 voltageWaveform(float in_slew, - float load_cap); - float voltageTime(float in_slew, - float load_cap, - float voltage); - const Table1 *currentWaveform(float slew, - float cap); + // Make voltage wavefroms from liberty time/current values. + // Required before voltageTime, timeVoltage, voltageCurrent. + void makeVoltageWaveforms(float vdd); float timeCurrent(float slew, float cap, float time); float timeVoltage(float slew, float cap, float time); + float voltageTime(float in_slew, + float load_cap, + float voltage); float voltageCurrent(float slew, float cap, float volt); float referenceTime(float slew); - void makeVoltageWaveforms(float vdd); + float beginTime(float slew, + float cap); + float endTime(float slew, + float cap); static bool checkAxes(const TableTemplate *tbl_template); + Table1 currentWaveform(float slew, + float cap); + // Waveform closest to slew/cap; no interpolation. + const Table1 *currentWaveformRaw(float slew, + float cap); + Table1 voltageWaveform(float in_slew, + float load_cap); + // Waveform closest to slew/cap; no interpolation. + const Table1 *voltageWaveformRaw(float slew, + float cap); + Table1 voltageCurrentWaveform(float slew, + float cap); + // V/I for last segment of min slew/max cap. + float finalResistance(); + private: void findVoltages(size_t wave_index, float cap); @@ -519,27 +539,30 @@ private: float cap, float axis_value, Table1Seq &waveforms); - float voltageTime1(float voltage, + float beginEndTime(float slew, + float cap, + bool begin); + double voltageTime1(double volt, + double dx1, + double dx2, + size_t wave_index00, + size_t wave_index01, + size_t wave_index10, + size_t wave_index11); + float voltageTime2(float volt, size_t wave_index); - void waveformMinMaxTime(float slew, - float cap, - Table1Seq &waveforms, - // Return values. - float &min_time, - float &max_time); // Row. TableAxisPtr slew_axis_; // Column. TableAxisPtr cap_axis_; const RiseFall *rf_; - Table1Seq current_waveforms_; + Table1Seq current_waveforms_; // from liberty Table1Seq voltage_waveforms_; Table1Seq voltage_currents_; - FloatTable voltage_times_; Table1 *ref_times_; float vdd_; - static constexpr size_t voltage_waveform_step_count_ = 20; + static constexpr size_t voltage_waveform_step_count_ = 100; }; class DriverWaveform diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 87142e7a..bc593fa0 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -27,6 +27,8 @@ namespace sta { class TimingArcAttrs; class WireTimingArc; +class GateTableModel; +class DcalcAnalysisPt; typedef int TimingArcIndex; typedef Vector TimingArcSeq; @@ -147,7 +149,7 @@ public: TimingRole *role() const { return role_; }; TimingSense sense() const; // Rise/fall if the arc set is rising_edge or falling_edge. - RiseFall *isRisingFallingEdge() const; + const RiseFall *isRisingFallingEdge() const; size_t arcCount() const { return arcs_.size(); } TimingArcSeq &arcs() { return arcs_; } // Return 1 or 2 arcs matching from transition. @@ -235,8 +237,11 @@ public: TimingSense sense() const; // Index in TimingArcSet. unsigned index() const { return index_; } - TimingModel *model(const OperatingConditions *op_cond) const; TimingModel *model() const { return model_; } + GateTimingModel *gateModel(const DcalcAnalysisPt *dcalc_ap) const; + CheckTimingModel *checkModel(const DcalcAnalysisPt *dcalc_ap) 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); @@ -247,6 +252,7 @@ public: const TimingArc *arc2); protected: + TimingModel *model(const DcalcAnalysisPt *dcalc_ap) const; void setIndex(unsigned index); void addScaledModel(const OperatingConditions *op_cond, TimingModel *scaled_model); diff --git a/include/sta/Transition.hh b/include/sta/Transition.hh index e4f3d44a..42baede9 100644 --- a/include/sta/Transition.hh +++ b/include/sta/Transition.hh @@ -97,9 +97,9 @@ public: RiseFall *asRiseFall() const { return as_rise_fall_; } // Find transition corresponding to string. static RiseFallBoth *find(const char *tr_str); - // for (auto tr : min_max->range()) {} + // for (const auto rf : rf->range()) {} const std::vector &range() const { return range_; } - // for (auto tr_index : min_max->rangeIndex()) {} + // for (const auto rf_index : rf->rangeIndex()) {} const std::vector &rangeIndex() const { return range_index_; } static const int index_count = 3; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 9a6d6264..5acdc63b 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -121,10 +121,8 @@ LibertyLibrary::~LibertyLibrary() delete units_; ocv_derate_map_.deleteContents(); - for (auto name_volt : supply_voltage_map_) { - const char *supply_name = name_volt.first; + for (auto [supply_name, volt] : supply_voltage_map_) stringDelete(supply_name); - } delete buffers_; delete inverters_; driver_waveform_map_.deleteContents(); @@ -204,8 +202,8 @@ BusDclSeq LibertyLibrary::busDcls() const { BusDclSeq dcls; - for (auto name_dcl : bus_dcls_) - dcls.push_back(name_dcl.second); + for (auto [name, dcl] : bus_dcls_) + dcls.push_back(dcl); return dcls; } @@ -228,10 +226,8 @@ LibertyLibrary::tableTemplates() const { TableTemplateSeq tbl_templates; for (int type = 0; type < table_template_type_count; type++) { - for (auto name_template : template_maps_[type]) { - TableTemplate *tbl_template = name_template.second; + for (auto [name, tbl_template] : template_maps_[type]) tbl_templates.push_back(tbl_template); - } } return tbl_templates; } @@ -950,7 +946,7 @@ LibertyCell::LibertyCell(LibertyLibrary *library, LibertyCell::~LibertyCell() { mode_defs_.deleteContents(); - latch_d_to_q_map_.deleteContents(); + latch_enables_.deleteContents(); timing_arc_sets_.deleteContents(); port_timing_arc_set_map_.deleteContents(); @@ -1306,14 +1302,13 @@ LibertyCell::finish(bool infer_latches, findDefaultCondArcs(); makeLatchEnables(report, debug); if (infer_latches) - inferLatchRoles(debug); + inferLatchRoles(report, debug); } void LibertyCell::findDefaultCondArcs() { - for (auto port_pair_set : port_timing_arc_set_map_) { - TimingArcSetSeq *sets = port_pair_set.second; + for (auto [port_pair, sets] : port_timing_arc_set_map_) { bool has_cond_arcs = false; for (auto set : *sets) { if (set->cond()) { @@ -1675,7 +1670,7 @@ class LatchEnable public: LatchEnable(LibertyPort *data, LibertyPort *enable, - RiseFall *enable_edge, + const RiseFall *enable_edge, FuncExpr *enable_func, LibertyPort *output, TimingArcSet *d_to_q, @@ -1685,7 +1680,7 @@ public: LibertyPort *output() const { return output_; } LibertyPort *enable() const { return enable_; } FuncExpr *enableFunc() const { return enable_func_; } - RiseFall *enableEdge() const { return enable_edge_; } + const RiseFall *enableEdge() const { return enable_edge_; } TimingArcSet *dToQ() const { return d_to_q_; } TimingArcSet *enToQ() const { return en_to_q_; } TimingArcSet *setupCheck() const { return setup_check_; } @@ -1693,7 +1688,7 @@ public: private: LibertyPort *data_; LibertyPort *enable_; - RiseFall *enable_edge_; + const RiseFall *enable_edge_; FuncExpr *enable_func_; LibertyPort *output_; TimingArcSet *d_to_q_; @@ -1703,7 +1698,7 @@ private: LatchEnable::LatchEnable(LibertyPort *data, LibertyPort *enable, - RiseFall *enable_edge, + const RiseFall *enable_edge, FuncExpr *enable_func, LibertyPort *output, TimingArcSet *d_to_q, @@ -1736,66 +1731,101 @@ LibertyCell::makeLatchEnables(Report *report, for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { if (d_to_q->role() == TimingRole::latchDtoQ()) { LibertyPort *d = d_to_q->from(); - for (TimingArcSet *setup_check : timingArcSets(en, d)) { - if (setup_check->role() == TimingRole::setup()) { - LatchEnable *latch_enable = makeLatchEnable(d, en, q, d_to_q, - en_to_q, - setup_check, - debug); - for (TimingArc *check_arc : setup_check->arcs()) { - RiseFall *en_rf = latch_enable->enableEdge(); - RiseFall *check_rf = check_arc->fromEdge()->asRiseFall(); - if (check_rf == en_rf) - report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.", - library_->name(), - name_, - en->name(), - q->name(), - en_rf == RiseFall::rise()?"rising":"falling", - en->name(), - d->name(), - check_rf==RiseFall::rise()?"rising":"falling"); - FuncExpr *en_func = latch_enable->enableFunc(); - if (en_func) { - TimingSense en_sense = en_func->portTimingSense(en); - if (en_sense == TimingSense::positive_unate - && en_rf != RiseFall::rise()) - report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", - library_->name(), - name_, - en->name(), - q->name(), - en_rf == RiseFall::rise()?"rising":"falling"); - else if (en_sense == TimingSense::negative_unate - && en_rf != RiseFall::fall()) - report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.", - library_->name(), - name_, - en->name(), - q->name(), - en_rf == RiseFall::rise()?"rising":"falling"); - } - } - } - } - } + const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); + if (en_rf) { + TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report); + LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, + en_to_q, + setup_check, + debug); + FuncExpr *en_func = latch_enable->enableFunc(); + if (en_func) { + TimingSense en_sense = en_func->portTimingSense(en); + if (en_sense == TimingSense::positive_unate + && en_rf != RiseFall::rise()) + report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", + library_->name(), + name_, + en->name(), + q->name(), + en_rf == RiseFall::rise()?"rising":"falling"); + else if (en_sense == TimingSense::negative_unate + && en_rf != RiseFall::fall()) + report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.", + library_->name(), + name_, + en->name(), + q->name(), + en_rf == RiseFall::rise()?"rising":"falling"); + } + } + } } } } } } +TimingArcSet * +LibertyCell::findLatchSetup(const LibertyPort *d, + const LibertyPort *en, + const RiseFall *en_rf, + const LibertyPort *q, + Report *report) +{ + TimingArcSetSeq en_d_arcs = timingArcSets(en, d); + // First search for setup checks with the correct clock edge. + for (TimingArcSet *arc_set : en_d_arcs) { + if (arc_set->role() == TimingRole::setup()) { + for (TimingArc *arc : arc_set->arcs()) { + const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); + if (from_rf == en_rf->opposite()) + return arc_set; + } + } + } + // Then search for setup checks with the opposite clock edge. + for (TimingArcSet *arc_set : en_d_arcs) { + if (arc_set->role() == TimingRole::setup()) { + for (TimingArc *arc : arc_set->arcs()) { + const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); + if (from_rf == en_rf) { + report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.", + library_->name(), + name_, + en->name(), + q->name(), + en_rf == RiseFall::rise() ? "rising" : "falling", + en->name(), + d->name(), + from_rf == RiseFall::rise() ? "rising" : "falling"); + return arc_set; + } + } + } + } + return nullptr; +} + FuncExpr * -LibertyCell::findLatchEnableFunc(LibertyPort *data, - LibertyPort *enable) const +LibertyCell::findLatchEnableFunc(const LibertyPort *d, + const LibertyPort *en, + const RiseFall *en_rf) const { for (auto seq : sequentials_) { if (seq->isLatch() && seq->data() - && seq->data()->hasPort(data) + && seq->data()->hasPort(d) && seq->clock() - && seq->clock()->hasPort(enable)) - return seq->clock(); + && seq->clock()->hasPort(en)) { + FuncExpr *en_func = seq->clock(); + TimingSense en_sense = en_func->portTimingSense(en); + if ((en_sense == TimingSense::positive_unate + && en_rf == RiseFall::rise()) + || (en_sense == TimingSense::negative_unate + && en_rf == RiseFall::fall())) + return seq->clock(); + } } return nullptr; } @@ -1803,29 +1833,36 @@ LibertyCell::findLatchEnableFunc(LibertyPort *data, LatchEnable * LibertyCell::makeLatchEnable(LibertyPort *d, LibertyPort *en, + const RiseFall *en_rf, LibertyPort *q, TimingArcSet *d_to_q, TimingArcSet *en_to_q, TimingArcSet *setup_check, Debug *debug) { - RiseFall *en_rf = en_to_q->isRisingFallingEdge(); - FuncExpr *en_func = findLatchEnableFunc(d, en); + 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); - // Multiple enables for D->Q pairs are not supported. - if (latch_d_to_q_map_[d_to_q]) - delete latch_d_to_q_map_[d_to_q]; + latch_enables_.push_back(latch_enable); latch_d_to_q_map_[d_to_q] = latch_enable; latch_check_map_[setup_check] = latch_enable; latch_data_ports_.insert(d); - debugPrint(debug, "liberty", 2, "latch d=%s en=%s q=%s", - d->name(), en->name(), q->name()); + debugPrint(debug, "liberty_latch", 1, + "latch %s -> %s | %s %s -> %s | %s %s -> %s setup", + d->name(), + q->name(), + en->name(), + en_rf->shortName(), + q->name(), + en->name(), + setup_check->arcs()[0]->fromEdge()->asRiseFall()->shortName(), + q->name()); return latch_enable; } void -LibertyCell::inferLatchRoles(Debug *debug) +LibertyCell::inferLatchRoles(Report *report, + Debug *debug) { if (hasInferedRegTimingArcs()) { // Hunt down potential latch D/EN/Q triples. @@ -1845,15 +1882,14 @@ LibertyCell::inferLatchRoles(Debug *debug) // Previously identified as D->Q arc. || d_to_q_role == TimingRole::latchDtoQ()) { LibertyPort *d = d_to_q->from(); - // Check for setup check from en -> d. - for (TimingArcSet *setup_check : timingArcSets(en, d)) { - if (setup_check->role() == TimingRole::setup()) { - makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug); - d_to_q->setRole(TimingRole::latchDtoQ()); - en_to_q->setRole(TimingRole::latchEnToQ()); - } - } - } + const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); + if (en_rf) { + TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report); + makeLatchEnable(d, en, en_rf, q, d_to_q, en_to_q, setup_check, debug); + d_to_q->setRole(TimingRole::latchDtoQ()); + en_to_q->setRole(TimingRole::latchEnToQ()); + } + } } } } @@ -1867,22 +1903,26 @@ LibertyCell::isLatchData(LibertyPort *port) } void -LibertyCell::latchEnable(TimingArcSet *d_to_q_set, +LibertyCell::latchEnable(const TimingArcSet *d_to_q_set, // Return values. - LibertyPort *&enable_port, - FuncExpr *&enable_func, - RiseFall *&enable_edge) const + const LibertyPort *&enable_port, + const FuncExpr *&enable_func, + const RiseFall *&enable_edge) const { - enable_port = nullptr; LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set); if (latch_enable) { enable_port = latch_enable->enable(); enable_func = latch_enable->enableFunc(); enable_edge = latch_enable->enableEdge(); } + else { + enable_port = nullptr; + enable_func = nullptr; + enable_edge = nullptr; + } } -RiseFall * +const RiseFall * LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set) { LatchEnable *latch_enable = latch_check_map_.findKey(check_set); @@ -1893,7 +1933,7 @@ LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set) } void -LibertyCell::ensureVoltageWaveforms() +LibertyCell::ensureVoltageWaveforms(const DcalcAnalysisPt *dcalc_ap) { if (!have_voltage_waveforms_) { float vdd = 0.0; // shutup gcc @@ -1903,7 +1943,7 @@ LibertyCell::ensureVoltageWaveforms() criticalError(1120, "library missing vdd"); for (TimingArcSet *arc_set : timingArcSets()) { for (TimingArc *arc : arc_set->arcs()) { - GateTableModel*model = dynamic_cast(arc->model()); + GateTableModel *model = arc->gateTableModel(dcalc_ap); if (model) { OutputWaveforms *output_waveforms = model->outputWaveforms(); if (output_waveforms) diff --git a/liberty/Liberty.i b/liberty/Liberty.i new file mode 100644 index 00000000..8aa0d751 --- /dev/null +++ b/liberty/Liberty.i @@ -0,0 +1,542 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + +%module liberty + +%{ +#include "PortDirection.hh" +#include "Liberty.hh" +#include "EquivCells.hh" +#include "LibertyWriter.hh" +#include "Sta.hh" + +using namespace sta; + +%} + +//////////////////////////////////////////////////////////////// +// +// Empty class definitions to make swig happy. +// Private constructor/destructor so swig doesn't emit them. +// +//////////////////////////////////////////////////////////////// + +class LibertyLibrary +{ +private: + LibertyLibrary(); + ~LibertyLibrary(); +}; + +class LibertyLibraryIterator +{ +private: + LibertyLibraryIterator(); + ~LibertyLibraryIterator(); +}; + +class LibertyCell +{ +private: + LibertyCell(); + ~LibertyCell(); +}; + +class LibertyPort +{ +private: + LibertyPort(); + ~LibertyPort(); +}; + +class LibertyCellPortIterator +{ +private: + LibertyCellPortIterator(); + ~LibertyCellPortIterator(); +}; + +class LibertyPortMemberIterator +{ +private: + LibertyPortMemberIterator(); + ~LibertyPortMemberIterator(); +}; + +class TimingArcSet +{ +private: + TimingArcSet(); + ~TimingArcSet(); +}; + +class TimingArc +{ +private: + TimingArc(); + ~TimingArc(); +}; + +class Wireload +{ +private: + Wireload(); + ~Wireload(); +}; + +class WireloadSelection +{ +private: + WireloadSelection(); + ~WireloadSelection(); +}; + +%inline %{ + +bool +read_liberty_cmd(char *filename, + Corner *corner, + const MinMaxAll *min_max, + bool infer_latches) +{ + Sta *sta = Sta::sta(); + LibertyLibrary *lib = sta->readLiberty(filename, corner, min_max, infer_latches); + return (lib != nullptr); +} + +void +write_liberty_cmd(LibertyLibrary *library, + char *filename) +{ + writeLiberty(library, filename, Sta::sta()); +} + +void +make_equiv_cells(LibertyLibrary *lib) +{ + LibertyLibrarySeq libs; + libs.push_back(lib); + Sta::sta()->makeEquivCells(&libs, nullptr); +} + +LibertyCellSeq * +find_equiv_cells(LibertyCell *cell) +{ + return Sta::sta()->equivCells(cell); +} + +bool +equiv_cells(LibertyCell *cell1, + LibertyCell *cell2) +{ + return sta::equivCells(cell1, cell2); +} + +bool +equiv_cell_ports(LibertyCell *cell1, + LibertyCell *cell2) +{ + return equivCellPorts(cell1, cell2); +} + +bool +equiv_cell_timing_arcs(LibertyCell *cell1, + LibertyCell *cell2) +{ + return equivCellTimingArcSets(cell1, cell2); +} + +LibertyCellSeq * +find_library_buffers(LibertyLibrary *library) +{ + return library->buffers(); +} + +const char * +liberty_port_direction(const LibertyPort *port) +{ + return port->direction()->name(); +} + +bool +liberty_supply_exists(const char *supply_name) +{ + auto network = Sta::sta()->network(); + auto lib = network->defaultLibertyLibrary(); + return lib && lib->supplyExists(supply_name); +} + +LibertyLibraryIterator * +liberty_library_iterator() +{ + return cmdNetwork()->libertyLibraryIterator(); +} + +LibertyLibrary * +find_liberty(const char *name) +{ + return cmdNetwork()->findLiberty(name); +} + +LibertyCell * +find_liberty_cell(const char *name) +{ + return cmdNetwork()->findLibertyCell(name); +} + +bool +timing_role_is_check(TimingRole *role) +{ + return role->isTimingCheck(); +} + +%} // inline + +//////////////////////////////////////////////////////////////// +// +// Object Methods +// +//////////////////////////////////////////////////////////////// + +%extend LibertyLibrary { +const char *name() { return self->name(); } + +LibertyCell * +find_liberty_cell(const char *name) +{ + return self->findLibertyCell(name); +} + +LibertyCellSeq +find_liberty_cells_matching(const char *pattern, + bool regexp, + bool nocase) +{ + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + return self->findLibertyCellsMatching(&matcher); +} + +Wireload * +find_wireload(const char *model_name) +{ + return self->findWireload(model_name); +} + +WireloadSelection * +find_wireload_selection(const char *selection_name) +{ + return self->findWireloadSelection(selection_name); +} + +OperatingConditions * +find_operating_conditions(const char *op_cond_name) +{ + return self->findOperatingConditions(op_cond_name); +} + +OperatingConditions * +default_operating_conditions() +{ + return self->defaultOperatingConditions(); +} + +} // LibertyLibrary methods + +%extend LibertyCell { +const char *name() { return self->name(); } +bool is_leaf() { return self->isLeaf(); } +bool is_buffer() { return self->isBuffer(); } +bool is_inverter() { return self->isInverter(); } +LibertyLibrary *liberty_library() { return self->libertyLibrary(); } +Cell *cell() { return reinterpret_cast(self); } +LibertyPort * +find_liberty_port(const char *name) +{ + return self->findLibertyPort(name); +} + +LibertyPortSeq +find_liberty_ports_matching(const char *pattern, + bool regexp, + bool nocase) +{ + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + return self->findLibertyPortsMatching(&matcher); +} + +LibertyCellPortIterator * +liberty_port_iterator() { return new LibertyCellPortIterator(self); } + +const TimingArcSetSeq & +timing_arc_sets() +{ + return self->timingArcSets(); +} + +void +ensure_voltage_waveforms() +{ + Corner *corner = Sta::sta()->cmdCorner(); + DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max()); + self->ensureVoltageWaveforms(dcalc_ap); +} + +} // LibertyCell methods + +%extend LibertyPort { +const char *bus_name() { return self->busName(); } +Cell *cell() { return self->cell(); } +bool is_bus() { return self->isBus(); } +LibertyPortMemberIterator * +member_iterator() { return new LibertyPortMemberIterator(self); } + +const char * +function() +{ + FuncExpr *func = self->function(); + if (func) + return func->asString(); + else + return nullptr; +} + +const char * +tristate_enable() +{ + FuncExpr *enable = self->tristateEnable(); + if (enable) + return enable->asString(); + else + return nullptr; +} + +float +capacitance(Corner *corner, + const MinMax *min_max) +{ + Sta *sta = Sta::sta(); + return sta->capacitance(self, corner, min_max); +} + +void +set_direction(const char *dir) +{ + self->setDirection(PortDirection::find(dir)); +} + +} // LibertyPort methods + +%extend TimingArcSet { +LibertyPort *from() { return self->from(); } +LibertyPort *to() { return self->to(); } +TimingRole *role() { return self->role(); } +const char *sdf_cond() { return self->sdfCond(); } + +const char * +full_name() +{ + const char *from = self->from()->name(); + const char *to = self->to()->name(); + const char *cell_name = self->libertyCell()->name(); + return stringPrintTmp("%s %s -> %s", + cell_name, + from, + to); +} + +TimingArcSeq & +timing_arcs() { return self->arcs(); } + +} // TimingArcSet methods + +%extend TimingArc { +LibertyPort *from() { return self->from(); } +LibertyPort *to() { return self->to(); } +Transition *from_edge() { return self->fromEdge(); } +const char *from_edge_name() { return self->fromEdge()->asRiseFall()->name(); } +Transition *to_edge() { return self->toEdge(); } +const char *to_edge_name() { return self->toEdge()->asRiseFall()->name(); } +TimingRole *role() { return self->role(); } + +float +time_voltage(float in_slew, + float load_cap, + float time) +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) + return waveforms->timeVoltage(in_slew, load_cap, time); + } + return 0.0; +} + +float +time_current(float in_slew, + float load_cap, + float time) +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) + return waveforms->timeCurrent(in_slew, load_cap, time); + } + return 0.0; +} + +float +voltage_current(float in_slew, + float load_cap, + float voltage) +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) + return waveforms->voltageCurrent(in_slew, load_cap, voltage); + } + return 0.0; +} + +float +voltage_time(float in_slew, + float load_cap, + float voltage) +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) + return waveforms->voltageTime(in_slew, load_cap, voltage); + } + return 0.0; +} + +Table1 +voltage_waveform(float in_slew, + float load_cap) +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) { + Table1 waveform = waveforms->voltageWaveform(in_slew, load_cap); + return waveform; + } + } + return Table1(); +} + +const Table1 * +voltage_waveform_raw(float in_slew, + float load_cap) +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) { + const Table1 *waveform = waveforms->voltageWaveformRaw(in_slew, load_cap); + return waveform; + } + } + return nullptr; +} + +Table1 +current_waveform(float in_slew, + float load_cap) +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) { + Table1 waveform = waveforms->currentWaveform(in_slew, load_cap); + return waveform; + } + } + return Table1(); +} + +const Table1 * +current_waveform_raw(float in_slew, + float load_cap) +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) { + const Table1 *waveform = waveforms->currentWaveformRaw(in_slew, load_cap); + return waveform; + } + } + return nullptr; +} + +Table1 +voltage_current_waveform(float in_slew, + float load_cap) +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) { + Table1 waveform = waveforms->voltageCurrentWaveform(in_slew, load_cap); + return waveform; + } + } + return Table1(); +} + +float +final_resistance() +{ + GateTableModel *gate_model = self->gateTableModel(); + if (gate_model) { + OutputWaveforms *waveforms = gate_model->outputWaveforms(); + if (waveforms) { + return waveforms->finalResistance(); + } + } + return 0.0; +} + +} // TimingArc methods + +%extend OperatingConditions { +float process() { return self->process(); } +float voltage() { return self->voltage(); } +float temperature() { return self->temperature(); } +} + +%extend LibertyLibraryIterator { +bool has_next() { return self->hasNext(); } +LibertyLibrary *next() { return self->next(); } +void finish() { delete self; } +} // LibertyLibraryIterator methods + +%extend LibertyCellPortIterator { +bool has_next() { return self->hasNext(); } +LibertyPort *next() { return self->next(); } +void finish() { delete self; } +} // LibertyCellPortIterator methods + +%extend LibertyPortMemberIterator { +bool has_next() { return self->hasNext(); } +LibertyPort *next() { return self->next(); } +void finish() { delete self; } +} // LibertyPortMemberIterator methods diff --git a/tcl/Liberty.tcl b/liberty/Liberty.tcl similarity index 100% rename from tcl/Liberty.tcl rename to liberty/Liberty.tcl diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index fb23fe3e..a2123a68 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -695,22 +695,22 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group) } bool missing_threshold = false; - for (auto tr : RiseFall::range()) { - int tr_index = tr->index(); - if (!have_input_threshold_[tr_index]) { - libWarn(1145, group, "input_threshold_pct_%s not found.", tr->name()); + for (auto rf : RiseFall::range()) { + int rf_index = rf->index(); + if (!have_input_threshold_[rf_index]) { + libWarn(1145, group, "input_threshold_pct_%s not found.", rf->name()); missing_threshold = true; } - if (!have_output_threshold_[tr_index]) { - libWarn(1146, group, "output_threshold_pct_%s not found.", tr->name()); + if (!have_output_threshold_[rf_index]) { + libWarn(1146, group, "output_threshold_pct_%s not found.", rf->name()); missing_threshold = true; } - if (!have_slew_lower_threshold_[tr_index]) { - libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", tr->name()); + if (!have_slew_lower_threshold_[rf_index]) { + libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", rf->name()); missing_threshold = true; } - if (!have_slew_upper_threshold_[tr_index]) { - libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", tr->name()); + if (!have_slew_upper_threshold_[rf_index]) { + libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", rf->name()); missing_threshold = true; } } @@ -3927,8 +3927,8 @@ LibertyReader::endTiming(LibertyGroup *group) { if (timing_) { // Set scale factor type in constraint tables. - for (auto tr : RiseFall::range()) { - TableModel *model = timing_->constraint(tr); + for (auto rf : RiseFall::range()) { + TableModel *model = timing_->constraint(rf); if (model) { ScaleFactorType type=timingTypeScaleFactorType(timing_->attrs()->timingType()); model->setScaleFactorType(type); @@ -5110,13 +5110,13 @@ LibertyReader::endOcvDerateFactors(LibertyGroup *) { if (ocv_derate_) { for (auto early_late : derate_type_->range()) { - for (auto tr : rf_type_->range()) { + for (auto rf : rf_type_->range()) { if (path_type_ == PathType::clk_and_data) { - ocv_derate_->setDerateTable(tr, early_late, PathType::clk, table_); - ocv_derate_->setDerateTable(tr, early_late, PathType::data, table_); + ocv_derate_->setDerateTable(rf, early_late, PathType::clk, table_); + ocv_derate_->setDerateTable(rf, early_late, PathType::data, table_); } else - ocv_derate_->setDerateTable(tr, early_late, path_type_, table_); + ocv_derate_->setDerateTable(rf, early_late, path_type_, table_); } } } diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 7d01985f..8fa4bf34 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -217,11 +217,11 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template) if (axis3) fprintf(stream_, " variable_3 : %s;\n", tableVariableString(axis3->variable())); - if (axis1) + if (axis1 && axis1->values()) writeTableAxis4(axis1, 1); - if (axis2) + if (axis2 && axis2->values()) writeTableAxis4(axis2, 2); - if (axis3) + if (axis3 && axis3->values()) writeTableAxis4(axis3, 3); fprintf(stream_, " }\n"); } diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index ab68dd72..21786e63 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -31,6 +31,9 @@ using std::max; using std::abs; using std::make_shared; +size_t +findValueIndex(float value, + const FloatSeq *values); static void deleteSigmaModels(TableModel *models[EarlyLate::index_count]); static string @@ -1425,6 +1428,25 @@ TableAxis::~TableAxis() delete values_; } +float +TableAxis::min() const +{ + if (!values_->empty()) + return (*values_)[0]; + else + return 0.0; +} + +float +TableAxis::max() const +{ + size_t size = values_->size(); + if (size > 0) + return (*values_)[values_->size() - 1]; + else + return 0.0; +} + bool TableAxis::inBounds(float value) const { @@ -1434,14 +1456,22 @@ TableAxis::inBounds(float value) const && value <= (*values_)[size - 1]; } -// Bisection search. size_t TableAxis::findAxisIndex(float value) const { - size_t size = values_->size(); - if (size <= 1 || value <= (*values_)[0]) + return findValueIndex(value, values_); +} + +// Bisection search. +// Assumes values are monotonically increasing. +size_t +findValueIndex(float value, + const FloatSeq *values) +{ + size_t size = values->size(); + if (size <= 1 || value <= (*values)[0]) return 0; - else if (value >= (*values_)[size - 1]) + else if (value >= (*values)[size - 1]) // Return max_index-1 for value too large so interpolation pts are index,index+1. return size - 2; else { @@ -1449,7 +1479,7 @@ TableAxis::findAxisIndex(float value) const int upper = size; while (upper - lower > 1) { int mid = (upper + lower) >> 1; - if (value >= (*values_)[mid]) + if (value >= (*values)[mid]) lower = mid; else upper = mid; @@ -1486,6 +1516,31 @@ TableAxis::findAxisIndex(float value, exists = false; } +size_t +TableAxis::findAxisClosestIndex(float value) const +{ + size_t size = values_->size(); + if (size <= 1 || value <= (*values_)[0]) + return 0; + else if (value >= (*values_)[size - 1]) + return size - 1; + else { + int lower = -1; + int upper = size; + while (upper - lower > 1) { + int mid = (upper + lower) >> 1; + if (value >= (*values_)[mid]) + lower = mid; + else + upper = mid; + } + if ((value - (*values_)[lower]) < ((*values_)[upper] - value)) + return lower; + else + return upper; + } +} + const char * TableAxis::variableString() const { @@ -1588,7 +1643,6 @@ OutputWaveforms::~OutputWaveforms() current_waveforms_.deleteContents(); voltage_waveforms_.deleteContents(); voltage_currents_.deleteContents(); - voltage_times_.deleteContents(); delete ref_times_; } @@ -1616,10 +1670,9 @@ OutputWaveforms::makeVoltageWaveforms(float vdd) size_t size = current_waveforms_.size(); voltage_waveforms_.resize(size); voltage_currents_.resize(size); - voltage_times_.resize(size); size_t cap_count = cap_axis_->size(); for (size_t slew_index = 0; slew_index < slew_axis_->size(); slew_index++) { - for (size_t cap_index = 0; cap_index < cap_axis_->size(); cap_index++) { + for (size_t cap_index = 0; cap_index < cap_count; cap_index++) { size_t wave_index = slew_index * cap_count + cap_index; findVoltages(wave_index, cap_axis_->axisValue(cap_index)); } @@ -1650,6 +1703,7 @@ OutputWaveforms::findVoltages(size_t wave_index, prev_time = time; prev_current = current; } + (*volts)[volts->size() - 1] = vdd_; Table1 *volt_table = new Table1(volts, currents->axis1ptr()); voltage_waveforms_[wave_index] = volt_table; @@ -1660,39 +1714,33 @@ OutputWaveforms::findVoltages(size_t wave_index, FloatSeq *currents1 = new FloatSeq(*currents->values()); Table1 *volt_currents = new Table1(currents1, volt_axis); voltage_currents_[wave_index] = volt_currents; - - // Sample the voltage waveform at uniform intervals to speed up - // voltage time lookup. - FloatSeq *voltage_times = new FloatSeq; - float volt_step = vdd_ / voltage_waveform_step_count_; - size_t i = 0; - float time0 = time_axis->axisValue(i); - float volt0 = (*volts)[i]; - i = 1; - float time1 = time_axis->axisValue(i); - float volt1 = (*volts)[i]; - for (size_t v = 0; v <= voltage_waveform_step_count_; v++) { - float volt3 = v * volt_step; - while (volt3 > volt1 && i < volts->size() - 1) { - time0 = time1; - volt0 = volt1; - i++; - time1 = time_axis->axisValue(i); - volt1 = (*volts)[i]; - } - float time3 = time0 + (time1 - time0) * (volt3 - volt0) / (volt1 - volt0); - voltage_times->push_back(time3); - } - voltage_times_[wave_index] = voltage_times; } -const Table1 * +Table1 OutputWaveforms::currentWaveform(float slew, float cap) { - size_t slew_index = slew_axis_->findAxisIndex(slew); - size_t cap_index = cap_axis_->findAxisIndex(cap); - size_t wave_index = slew_index * cap_axis_->size() + cap_index; + FloatSeq *times = new FloatSeq; + FloatSeq *currents = new FloatSeq; + for (size_t i = 0; i <= voltage_waveform_step_count_; i++) { + float volt = i * vdd_ / voltage_waveform_step_count_; + float time = voltageTime(slew, cap, volt); + float current = voltageCurrent(slew, cap, volt); + times->push_back(time); + currents->push_back(current); + } + TableAxisPtr time_axis = make_shared(TableAxisVariable::time, times); + return Table1(currents, time_axis); +} + +const Table1 * +OutputWaveforms::currentWaveformRaw(float slew, + float cap) +{ + size_t slew_index = slew_axis_->findAxisClosestIndex(slew); + size_t cap_index = cap_axis_->findAxisClosestIndex(cap); + size_t cap_count = cap_axis_->size(); + size_t wave_index = slew_index * cap_count + cap_index; return current_waveforms_[wave_index]; } @@ -1701,7 +1749,9 @@ OutputWaveforms::timeCurrent(float slew, float cap, float time) { - return waveformValue(slew, cap, time, current_waveforms_); + // Current waveform is not monotonic, so use volt/current correspondence. + float volt = timeVoltage(slew, cap, time); + return voltageCurrent(slew, cap, volt); } float @@ -1709,7 +1759,81 @@ OutputWaveforms::timeVoltage(float slew, float cap, float time) { - return waveformValue(slew, cap, time, voltage_waveforms_); + size_t slew_index = slew_axis_->findAxisIndex(slew); + size_t cap_index = cap_axis_->findAxisIndex(cap); + size_t cap_count = cap_axis_->size(); + size_t wave_index00 = slew_index * cap_count + cap_index; + size_t wave_index01 = slew_index * cap_count + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; + size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); + + size_t index1 = slew_index; + size_t index2 = cap_index; + double x1 = slew; + double x2 = cap; + double x1l = slew_axis_->axisValue(index1); + double x1u = slew_axis_->axisValue(index1 + 1); + double dx1 = (x1 - x1l) / (x1u - x1l); + double x2l = cap_axis_->axisValue(index2); + double x2u = cap_axis_->axisValue(index2 + 1); + double dx2 = (x2 - x2l) / (x2u - x2l); + + double v_lo = 0.0; + double v_hi = vdd_; + double v_mid = (v_hi + v_lo) * 0.5; + double time_mid; + while (v_hi - v_lo > .001) { + time_mid = voltageTime1(v_mid, dx1, dx2, wave_index00, wave_index01, + wave_index10, wave_index11); + if (time > time_mid) { + v_lo = v_mid; + v_mid = (v_hi + v_lo) * 0.5; + } + else { + v_hi = v_mid; + v_mid = (v_hi + v_lo) * 0.5; + } + } + return v_mid; +} + +double +OutputWaveforms::voltageTime1(double volt, + double dx1, + double dx2, + size_t wave_index00, + size_t wave_index01, + size_t wave_index10, + size_t wave_index11) +{ + double y00 = voltageTime2(volt, wave_index00); + double y01 = voltageTime2(volt, wave_index01); + double y10 = voltageTime2(volt, wave_index10); + double y11 = voltageTime2(volt, wave_index11); + double time + = (1 - dx1) * (1 - dx2) * y00 + + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; + return time; +} + +float +OutputWaveforms::voltageTime2(float volt, + size_t wave_index) +{ + const Table1 *voltage_waveform = voltage_waveforms_[wave_index]; + const FloatSeq *voltages = voltage_waveform->values(); + size_t index1 = findValueIndex(volt, voltages); + float volt_lo = (*voltages)[index1]; + float volt_hi = (*voltages)[index1 + 1]; + float dv = volt_hi - volt_lo; + + const TableAxis *time_axis = voltage_waveform->axis1(); + float time_lo = time_axis->axisValue(index1); + float time_hi = time_axis->axisValue(index1 + 1); + float dt = time_hi - time_lo; + return time_lo + dt * (volt - volt_lo) / dv; } float @@ -1773,14 +1897,11 @@ Table1 OutputWaveforms::voltageWaveform(float slew, float cap) { - float min_time, max_time; - waveformMinMaxTime(slew, cap, voltage_waveforms_, min_time, max_time); - float time_step = (max_time - min_time) / voltage_waveform_step_count_; FloatSeq *times = new FloatSeq; FloatSeq *volts = new FloatSeq; - for (size_t i = 0; i < voltage_waveform_step_count_; i++) { - float time = min_time + i * time_step; - float volt = timeVoltage(slew, cap, time); + for (size_t i = 0; i <= voltage_waveform_step_count_; i++) { + float volt = i * vdd_ / voltage_waveform_step_count_; + float time = voltageTime(slew, cap, volt); times->push_back(time); volts->push_back(volt); } @@ -1788,42 +1909,21 @@ OutputWaveforms::voltageWaveform(float slew, return Table1(volts, time_axis); } -void -OutputWaveforms::waveformMinMaxTime(float slew, - float cap, - Table1Seq &waveforms, - // Return values. - float &min_time, - float &max_time) +const Table1 * +OutputWaveforms::voltageWaveformRaw(float slew, + float cap) { - size_t slew_index = slew_axis_->findAxisIndex(slew); - size_t cap_index = cap_axis_->findAxisIndex(cap); + size_t slew_index = slew_axis_->findAxisClosestIndex(slew); + size_t cap_index = cap_axis_->findAxisClosestIndex(cap); size_t cap_count = cap_axis_->size(); - size_t wave_index00 = slew_index * cap_count + cap_index; - size_t wave_index01 = slew_index * cap_count + (cap_index + 1); - size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; - size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); - - const Table1 *waveform00 = waveforms[wave_index00]; - const Table1 *waveform01 = waveforms[wave_index01]; - const Table1 *waveform10 = waveforms[wave_index10]; - const Table1 *waveform11 = waveforms[wave_index11]; - - min_time = waveform00->axis1()->min(); - min_time = min(min_time, waveform01->axis1()->min()); - min_time = min(min_time, waveform10->axis1()->min()); - min_time = min(min_time, waveform11->axis1()->min()); - - max_time = waveform00->axis1()->max(); - max_time = max(max_time, waveform01->axis1()->max()); - max_time = max(max_time, waveform10->axis1()->max()); - max_time = max(max_time, waveform11->axis1()->max()); + size_t wave_index = slew_index * cap_count + cap_index; + return voltage_waveforms_[wave_index]; } float OutputWaveforms::voltageTime(float slew, - float cap, - float volt) + float cap, + float volt) { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); @@ -1845,33 +1945,109 @@ OutputWaveforms::voltageTime(float slew, double x2u = cap_axis_->axisValue(index2 + 1); double dx2 = (x2 - x2l) / (x2u - x2l); - double y00 = voltageTime1(volt, wave_index00); - double y01 = voltageTime1(volt, wave_index01); - double y10 = voltageTime1(volt, wave_index10); - double y11 = voltageTime1(volt, wave_index11); - double time - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; + double time = voltageTime1(volt, dx1, dx2, wave_index00, wave_index01, + wave_index10, wave_index11); return time; } float -OutputWaveforms::voltageTime1(float voltage, - size_t wave_index) +OutputWaveforms::beginTime(float slew, + float cap) { - FloatSeq *voltage_times = voltage_times_[wave_index]; - float volt_step = vdd_ / voltage_waveform_step_count_; - size_t volt_idx = voltage / volt_step; - if (volt_idx >= voltage_times->size() - 1) - return (*voltage_times)[voltage_times->size() - 1]; - else { - double time0 = (*voltage_times)[volt_idx]; - double time1 = (*voltage_times)[volt_idx + 1]; - double time = time0 + (time1 - time0) * (voltage - volt_step * volt_idx); - return time; + return beginEndTime(slew, cap, true); +} + +float +OutputWaveforms::endTime(float slew, + float cap) +{ + return beginEndTime(slew, cap, false); +} + +float +OutputWaveforms::beginEndTime(float slew, + float cap, + bool begin) +{ + size_t slew_index = slew_axis_->findAxisIndex(slew); + size_t cap_index = cap_axis_->findAxisIndex(cap); + size_t cap_count = cap_axis_->size(); + size_t wave_index00 = slew_index * cap_count + cap_index; + size_t wave_index01 = slew_index * cap_count + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * cap_count + cap_index; + size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1); + + const Table1 *waveform00 = current_waveforms_[wave_index00]; + const Table1 *waveform01 = current_waveforms_[wave_index01]; + const Table1 *waveform10 = current_waveforms_[wave_index10]; + const Table1 *waveform11 = current_waveforms_[wave_index11]; + + // Interpolate waveform samples at voltage steps. + size_t index1 = slew_index; + size_t index2 = cap_index; + float x1 = slew; + float x2 = cap; + float x1l = slew_axis_->axisValue(index1); + float x1u = slew_axis_->axisValue(index1 + 1); + float dx1 = (x1 - x1l) / (x1u - x1l); + float x2l = cap_axis_->axisValue(index2); + float x2u = cap_axis_->axisValue(index2 + 1); + float dx2 = (x2 - x2l) / (x2u - x2l); + + float y00, y01, y10, y11; + if (begin) { + y00 = waveform00->axis1()->min(); + y01 = waveform01->axis1()->min(); + y10 = waveform10->axis1()->min(); + y11 = waveform11->axis1()->min(); } + else { + y00 = waveform00->axis1()->max(); + y01 = waveform01->axis1()->max(); + y10 = waveform10->axis1()->max(); + y11 = waveform11->axis1()->max(); + } + + float wave_value + = (1 - dx1) * (1 - dx2) * y00 + + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; + return wave_value; +} + +Table1 +OutputWaveforms::voltageCurrentWaveform(float slew, + float cap) +{ + FloatSeq *volts = new FloatSeq; + FloatSeq *currents = new FloatSeq; + for (size_t i = 0; i < voltage_waveform_step_count_; i++) { + float volt = i * vdd_ / voltage_waveform_step_count_; + float current = voltageCurrent(slew, cap, volt); + volts->push_back(volt); + currents->push_back(current); + } + TableAxisPtr volt_axis = + make_shared(TableAxisVariable::input_voltage, volts); + return Table1(currents, volt_axis); +} + +// Incremental resistance at final value of waveform. +// This corresponds to the pulldown/pullup that holds the output to the rail +// after the waveform has transitioned to the final value. +float +OutputWaveforms::finalResistance() +{ + size_t slew_index = 0; + size_t cap_count = cap_axis_->size(); + size_t cap_index = cap_count - 1; + size_t wave_index = slew_index * cap_count + cap_index; + const Table1 *voltage_currents = voltage_currents_[wave_index]; + FloatSeq *voltages = voltage_currents->axis1()->values(); + FloatSeq *currents = voltage_currents->values(); + size_t idx_last1 = voltages->size() - 2; + return (vdd_ - (*voltages)[idx_last1]) / abs((*currents)[idx_last1]); } //////////////////////////////////////////////////////////////// diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 29be2abf..89219382 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -21,6 +21,8 @@ #include "TimingRole.hh" #include "Liberty.hh" #include "TimingArc.hh" +#include "DcalcAnalysisPt.hh" +#include "TableModel.hh" namespace sta { @@ -316,13 +318,13 @@ TimingArcSet::sense() const return attrs_->timingSense(); } -RiseFall * +const RiseFall * TimingArcSet::isRisingFallingEdge() const { int arc_count = arcs_.size(); if (arc_count == 2) { - RiseFall *from_rf1 = arcs_[0]->fromEdge()->asRiseFall(); - RiseFall *from_rf2 = arcs_[1]->fromEdge()->asRiseFall(); + const RiseFall *from_rf1 = arcs_[0]->fromEdge()->asRiseFall(); + const RiseFall *from_rf2 = arcs_[1]->fromEdge()->asRiseFall(); if (from_rf1 == from_rf2) return from_rf1; } @@ -545,18 +547,42 @@ TimingArc::~TimingArc() delete scaled_models_; } -TimingModel * -TimingArc::model(const OperatingConditions *op_cond) const +GateTimingModel * +TimingArc::gateModel(const DcalcAnalysisPt *dcalc_ap) const { - if (scaled_models_) { - TimingModel *model = scaled_models_->findKey(op_cond); - if (model) - return model; - else - return model_; + return dynamic_cast(model(dcalc_ap)); +} + +GateTableModel * +TimingArc::gateTableModel() const +{ + return dynamic_cast(model_); +} + +GateTableModel * +TimingArc::gateTableModel(const DcalcAnalysisPt *dcalc_ap) const +{ + return dynamic_cast(model(dcalc_ap)); +} + +CheckTimingModel * +TimingArc::checkModel(const DcalcAnalysisPt *dcalc_ap) const +{ + return dynamic_cast(model(dcalc_ap)); +} + +TimingModel * +TimingArc::model(const DcalcAnalysisPt *dcalc_ap) const +{ + const TimingArc *corner_arc = cornerArc(dcalc_ap->libertyIndex()); + ScaledTimingModelMap *scaled_models = corner_arc->scaled_models_; + if (scaled_models) { + const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); + TimingModel *scaled_model = scaled_models->findKey(op_cond); + if (scaled_model) + return scaled_model; } - else - return model_; + return corner_arc->model(); } void diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc index e6cceeef..820f9206 100644 --- a/network/ConcreteLibrary.cc +++ b/network/ConcreteLibrary.cc @@ -320,8 +320,9 @@ public: int from() const { return from_; } int to() const { return to_; } ConcretePortSeq &members() { return members_; } + const ConcretePortSeq &members() const { return members_; } void setDirection(PortDirection *direction); - PortDirection *direction() { return direction_; } + PortDirection *direction() const { return direction_; } private: int from_; @@ -385,9 +386,7 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left, } // Make the bus ports. - for (auto name_bus : bus_map) { - string bus_name = name_bus.first; - BusPort &bus_port = name_bus.second; + for (const auto& [bus_name, bus_port] : bus_map) { int from = bus_port.from(); int to = bus_port.to(); size_t size = to - from + 1; diff --git a/tcl/Link.tcl b/network/Link.tcl similarity index 79% rename from tcl/Link.tcl rename to network/Link.tcl index 39912ddf..a278c710 100644 --- a/tcl/Link.tcl +++ b/network/Link.tcl @@ -18,11 +18,13 @@ namespace eval sta { -define_cmd_args "link_design" {[top_cell_name]} +define_cmd_args "link_design" {[-no_black_boxes]\ + [top_cell_name]} proc_redirect link_design { variable current_design_name + parse_key_args "link_design" args keys {} flags {-no_black_boxes} check_argc_eq0or1 "link_design" $args if { $args == "" } { set top_cell_name "" @@ -37,7 +39,11 @@ proc_redirect link_design { set top_cell_name $current_design_name } } - link_design_cmd $top_cell_name + set make_black_boxes 1 + if { [info exists flags(-no_black_boxes)] } { + set make_black_boxes 0 + } + link_design_cmd $top_cell_name $make_black_boxes } # sta namespace end diff --git a/network/Network.i b/network/Network.i new file mode 100644 index 00000000..8ccc7a7a --- /dev/null +++ b/network/Network.i @@ -0,0 +1,818 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + +%module network + +%{ +#include "Network.hh" +%} + +//////////////////////////////////////////////////////////////// +// +// Empty class definitions to make swig happy. +// Private constructor/destructor so swig doesn't emit them. +// +//////////////////////////////////////////////////////////////// + +class Library +{ +private: + Library(); + ~Library(); +}; + +class LibraryIterator +{ +private: + LibraryIterator(); + ~LibraryIterator(); +}; + +class Cell +{ +private: + Cell(); + ~Cell(); +}; + +class CellPortIterator +{ +private: + CellPortIterator(); + ~CellPortIterator(); +}; + +class Port +{ +private: + Port(); + ~Port(); +}; + +class PortMemberIterator +{ +private: + PortMemberIterator(); + ~PortMemberIterator(); +}; + +class Instance +{ +private: + Instance(); + ~Instance(); +}; + +class Pin +{ +private: + Pin(); + ~Pin(); +}; + +class Term +{ +private: + Term(); + ~Term(); +}; + +class InstanceChildIterator +{ +private: + InstanceChildIterator(); + ~InstanceChildIterator(); +}; + +class InstancePinIterator +{ +private: + InstancePinIterator(); + ~InstancePinIterator(); +}; + +class InstanceNetIterator +{ +private: + InstanceNetIterator(); + ~InstanceNetIterator(); +}; + +class LeafInstanceIterator +{ +private: + LeafInstanceIterator(); + ~LeafInstanceIterator(); +}; + +class Net +{ +private: + Net(); + ~Net(); +}; + +class NetPinIterator +{ +private: + NetPinIterator(); + ~NetPinIterator(); +}; + +class NetTermIterator +{ +private: + NetTermIterator(); + ~NetTermIterator(); +}; + +class NetConnectedPinIterator +{ +private: + NetConnectedPinIterator(); + ~NetConnectedPinIterator(); +}; + +class PinConnectedPinIterator +{ +private: + PinConnectedPinIterator(); + ~PinConnectedPinIterator(); +}; + +%inline %{ + +bool +network_is_linked() +{ + return Sta::sta()->cmdNetwork()->isLinked(); +} + +void +set_path_divider(char divider) +{ + cmdNetwork()->setPathDivider(divider); +} + +void +set_current_instance(Instance *inst) +{ + Sta::sta()->setCurrentInstance(inst); +} + +// Includes top instance. +int +network_instance_count() +{ + Network *network = cmdNetwork(); + return network->instanceCount(); +} + +int +network_pin_count() +{ + Network *network = cmdNetwork(); + return network->pinCount(); +} + +int +network_net_count() +{ + Network *network = cmdNetwork(); + return network->netCount(); +} + +int +network_leaf_instance_count() +{ + Network *network = cmdNetwork(); + return network->leafInstanceCount(); +} + +int +network_leaf_pin_count() +{ + Network *network = cmdNetwork(); + return network->leafPinCount(); +} + +Library * +find_library(const char *name) +{ + return cmdNetwork()->findLibrary(name); +} + +LibraryIterator * +library_iterator() +{ + return cmdNetwork()->libraryIterator(); +} + +CellSeq +find_cells_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdNetwork(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + CellSeq matches; + LibraryIterator *lib_iter = network->libraryIterator(); + while (lib_iter->hasNext()) { + Library *lib = lib_iter->next(); + CellSeq lib_matches = network->findCellsMatching(lib, &matcher); + for (Cell *match : lib_matches) + matches.push_back(match); + } + delete lib_iter; + return matches; +} + +void +set_cmd_namespace_cmd(const char *namespc) +{ + Sta *sta = Sta::sta(); + if (stringEq(namespc, "sdc")) + sta->setCmdNamespace(CmdNamespace::sdc); + else if (stringEq(namespc, "sta")) + sta->setCmdNamespace(CmdNamespace::sta); + else + sta->report()->warn(2120, "unknown namespace"); +} + +bool +link_design_cmd(const char *top_cell_name, + bool make_black_boxes) +{ + return Sta::sta()->linkDesign(top_cell_name, make_black_boxes); +} + +Instance * +top_instance() +{ + return cmdLinkedNetwork()->topInstance(); +} + +LeafInstanceIterator * +leaf_instance_iterator() +{ + return cmdLinkedNetwork()->leafInstanceIterator(); +} + +const char * +port_direction(const Port *port) +{ + return cmdLinkedNetwork()->direction(port)->name(); +} + +const char * +pin_direction(const Pin *pin) +{ + return cmdLinkedNetwork()->direction(pin)->name(); +} + +PortSeq +find_ports_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdLinkedNetwork(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + Cell *top_cell = network->cell(network->topInstance()); + PortSeq matches1 = network->findPortsMatching(top_cell, &matcher); + // Expand bus/bundle ports. + PortSeq matches; + for (const Port *port : matches1) { + if (network->isBus(port) + || network->isBundle(port)) { + PortMemberIterator *member_iter = network->memberIterator(port); + while (member_iter->hasNext()) { + Port *member = member_iter->next(); + matches.push_back(member); + } + delete member_iter; + } + else + matches.push_back(port); + } + return matches; +} + +PinSeq +find_port_pins_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdLinkedNetwork(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + Instance *top_inst = network->topInstance(); + Cell *top_cell = network->cell(top_inst); + PortSeq ports = network->findPortsMatching(top_cell, &matcher); + PinSeq pins; + for (const Port *port : ports) { + if (network->isBus(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); + } + delete member_iter; + } + else { + Pin *pin = network->findPin(top_inst, port); + if (pin) + pins.push_back(pin); + } + } + return pins; +} + +Pin * +find_pin(const char *path_name) +{ + return cmdLinkedNetwork()->findPin(path_name); +} + +Pin * +get_port_pin(const Port *port) +{ + Network *network = cmdLinkedNetwork(); + return network->findPin(network->topInstance(), port); +} + +PinSeq +find_pins_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Sta *sta = Sta::sta(); + Network *network = cmdLinkedNetwork(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + Instance *current_instance = sta->currentInstance(); + PinSeq matches = network->findPinsMatching(current_instance, &matcher); + return matches; +} + +PinSeq +find_pins_hier_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Sta *sta = Sta::sta(); + Network *network = cmdLinkedNetwork(); + Instance *current_instance = sta->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + PinSeq matches = network->findPinsHierMatching(current_instance, &matcher); + return matches; +} + +Instance * +find_instance(char *path_name) +{ + return cmdLinkedNetwork()->findInstance(path_name); +} + +InstanceSeq +network_leaf_instances() +{ + return cmdLinkedNetwork()->leafInstances(); +} + +InstanceSeq +find_instances_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Sta *sta = Sta::sta(); + Instance *current_instance = sta->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); + Network *network = cmdLinkedNetwork(); + InstanceSeq matches = network->findInstancesMatching(current_instance, &matcher); + return matches; +} + +InstanceSeq +find_instances_hier_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Sta *sta = Sta::sta(); + Network *network = cmdLinkedNetwork(); + Instance *current_instance = sta->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); + InstanceSeq matches = network->findInstancesHierMatching(current_instance, &matcher); + return matches; +} + +InstanceSet +find_register_instances(ClockSet *clks, + const RiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + cmdLinkedNetwork(); + InstanceSet insts = Sta::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) +{ + cmdLinkedNetwork(); + PinSet pins = Sta::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) +{ + cmdLinkedNetwork(); + PinSet pins = Sta::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) +{ + cmdLinkedNetwork(); + PinSet pins = Sta::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) +{ + cmdLinkedNetwork(); + PinSet pins = Sta::sta()->findRegisterOutputPins(clks, clk_tr, + edge_triggered, latches); + delete clks; + return pins; +} + +Net * +find_net(char *path_name) +{ + return cmdLinkedNetwork()->findNet(path_name); +} + +NetSeq +find_nets_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdLinkedNetwork(); + Instance *current_instance = Sta::sta()->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + NetSeq matches = network->findNetsMatching(current_instance, &matcher); + return matches; +} + +NetSeq +find_nets_hier_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdLinkedNetwork(); + Instance *current_instance = Sta::sta()->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + NetSeq matches = network->findNetsHierMatching(current_instance, &matcher); + return matches; +} + +PinSet +net_driver_pins(Net *net) +{ + Network *network = cmdLinkedNetwork(); + PinSet pins(network); + NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + if (network->isDriver(pin)) + pins.insert(pin); + } + delete pin_iter; + return pins; +} + +PinSet +net_load_pins(Net *net) +{ + Network *network = cmdLinkedNetwork(); + PinSet pins(network); + NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + if (network->isLoad(pin)) + pins.insert(pin); + } + delete pin_iter; + return pins; +} + +const char * +pin_location(const Pin *pin) +{ + Network *network = cmdNetwork(); + double x, y; + bool exists; + network->location(pin, x, y, exists); + // return x/y as tcl list + if (exists) + return sta::stringPrintTmp("%f %f", x, y); + else + return ""; +} + +const char * +port_location(const Port *port) +{ + Network *network = cmdNetwork(); + const Pin *pin = network->findPin(network->topInstance(), port); + return pin_location(pin); +} + +%} // inline + +//////////////////////////////////////////////////////////////// +// +// Object Methods +// +//////////////////////////////////////////////////////////////// + +%extend Library { +const char *name() +{ + return cmdNetwork()->name(self); +} + +Cell * +find_cell(const char *name) +{ + return cmdNetwork()->findCell(self, name); +} + +CellSeq +find_cells_matching(const char *pattern, + bool regexp, + bool nocase) +{ + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + CellSeq matches = cmdNetwork()->findCellsMatching(self, &matcher); + return matches; +} + +} // Library methods + +%extend LibraryIterator { +bool has_next() { return self->hasNext(); } +Library *next() { return self->next(); } +void finish() { delete self; } +} // LibraryIterator methods + +%extend Cell { +const char *name() { return cmdNetwork()->name(self); } +Library *library() { return cmdNetwork()->library(self); } +LibertyCell *liberty_cell() { return cmdNetwork()->libertyCell(self); } +bool is_leaf() { return cmdNetwork()->isLeaf(self); } +CellPortIterator * +port_iterator() { return cmdNetwork()->portIterator(self); } +string get_attribute(const char *key) { return cmdNetwork()->getAttribute(self, key); } + +Port * +find_port(const char *name) +{ + return cmdNetwork()->findPort(self, name); +} + +PortSeq +find_ports_matching(const char *pattern, + bool regexp, + bool nocase) +{ + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + return cmdNetwork()->findPortsMatching(self, &matcher); +} + +} // Cell methods + +%extend CellPortIterator { +bool has_next() { return self->hasNext(); } +Port *next() { return self->next(); } +void finish() { delete self; } +} // CellPortIterator methods + +%extend Port { +const char *bus_name() { return cmdNetwork()->busName(self); } +Cell *cell() { return cmdNetwork()->cell(self); } +LibertyPort *liberty_port() { return cmdNetwork()->libertyPort(self); } +bool is_bus() { return cmdNetwork()->isBus(self); } +PortMemberIterator * +member_iterator() { return cmdNetwork()->memberIterator(self); } + +} // Port methods + +%extend PortMemberIterator { +bool has_next() { return self->hasNext(); } +Port *next() { return self->next(); } +void finish() { delete self; } +} // PortMemberIterator methods + +%extend Instance { +Instance *parent() { return cmdLinkedNetwork()->parent(self); } +Cell *cell() { return cmdLinkedNetwork()->cell(self); } +LibertyCell *liberty_cell() { return cmdLinkedNetwork()->libertyCell(self); } +bool is_leaf() { return cmdLinkedNetwork()->isLeaf(self); } +InstanceChildIterator * +child_iterator() { return cmdLinkedNetwork()->childIterator(self); } +InstancePinIterator * +pin_iterator() { return cmdLinkedNetwork()->pinIterator(self); } +InstanceNetIterator * +net_iterator() { return cmdLinkedNetwork()->netIterator(self); } +Pin * +find_pin(const char *name) +{ + return cmdLinkedNetwork()->findPin(self, name); +} +string get_attribute(const char *key) { return cmdNetwork()->getAttribute(self, key); } +} // Instance methods + +%extend InstanceChildIterator { +bool has_next() { return self->hasNext(); } +Instance *next() { return self->next(); } +void finish() { delete self; } +} // InstanceChildIterator methods + +%extend LeafInstanceIterator { +bool has_next() { return self->hasNext(); } +Instance *next() { return self->next(); } +void finish() { delete self; } +} // LeafInstanceIterator methods + +%extend InstancePinIterator { +bool has_next() { return self->hasNext(); } +Pin *next() { return self->next(); } +void finish() { delete self; } +} // InstancePinIterator methods + +%extend InstanceNetIterator { +bool has_next() { return self->hasNext(); } +Net *next() { return self->next(); } +void finish() { delete self; } +} // InstanceNetIterator methods + +%extend Pin { +const char *port_name() { return cmdLinkedNetwork()->portName(self); } +Instance *instance() { return cmdLinkedNetwork()->instance(self); } +Net *net() { return cmdLinkedNetwork()->net(self); } +Port *port() { return cmdLinkedNetwork()->port(self); } +Term *term() { return cmdLinkedNetwork()->term(self); } +LibertyPort *liberty_port() { return cmdLinkedNetwork()->libertyPort(self); } +bool is_driver() { return cmdLinkedNetwork()->isDriver(self); } +bool is_load() { return cmdLinkedNetwork()->isLoad(self); } +bool is_leaf() { return cmdLinkedNetwork()->isLeaf(self); } +bool is_hierarchical() { return cmdLinkedNetwork()->isHierarchical(self); } +bool is_top_level_port() { return cmdLinkedNetwork()->isTopLevelPort(self); } +PinConnectedPinIterator *connected_pin_iterator() +{ return cmdLinkedNetwork()->connectedPinIterator(self); } + +Vertex ** +vertices() +{ + Vertex *vertex, *vertex_bidirect_drvr; + static Vertex *vertices[3]; + + cmdGraph()->pinVertices(self, vertex, vertex_bidirect_drvr); + vertices[0] = vertex; + vertices[1] = vertex_bidirect_drvr; + vertices[2] = nullptr; + return vertices; +} + +} // Pin methods + +%extend PinConnectedPinIterator { +bool has_next() { return self->hasNext(); } +const Pin *next() { return self->next(); } +void finish() { delete self; } +} // PinConnectedPinIterator methods + +%extend Term { +Net *net() { return cmdLinkedNetwork()->net(self); } +Pin *pin() { return cmdLinkedNetwork()->pin(self); } +} // Term methods + +%extend Net { +Instance *instance() { return cmdLinkedNetwork()->instance(self); } +const Net *highest_connected_net() +{ return cmdLinkedNetwork()->highestConnectedNet(self); } +NetPinIterator *pin_iterator() { return cmdLinkedNetwork()->pinIterator(self);} +NetTermIterator *term_iterator() {return cmdLinkedNetwork()->termIterator(self);} +NetConnectedPinIterator *connected_pin_iterator() +{ return cmdLinkedNetwork()->connectedPinIterator(self); } +bool is_power() { return cmdLinkedNetwork()->isPower(self);} +bool is_ground() { return cmdLinkedNetwork()->isGround(self);} + +float +capacitance(Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + Sta::sta()->connectedCap(self, corner, min_max, pin_cap, wire_cap); + return pin_cap + wire_cap; +} + +float +pin_capacitance(Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + Sta::sta()->connectedCap(self, corner, min_max, pin_cap, wire_cap); + return pin_cap; +} + +float +wire_capacitance(Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + Sta::sta()->connectedCap(self, corner, min_max, pin_cap, wire_cap); + return wire_cap; +} + +// get_ports -of_objects net +PortSeq +ports() +{ + Network *network = cmdLinkedNetwork(); + PortSeq ports; + if (network->isTopInstance(network->instance(self))) { + NetTermIterator *term_iter = network->termIterator(self); + while (term_iter->hasNext()) { + Term *term = term_iter->next(); + Port *port = network->port(network->pin(term)); + ports.push_back(port); + } + delete term_iter; + } + return ports; +} + +} // Net methods + +%extend NetPinIterator { +bool has_next() { return self->hasNext(); } +const Pin *next() { return self->next(); } +void finish() { delete self; } +} // NetPinIterator methods + +%extend NetTermIterator { +bool has_next() { return self->hasNext(); } +const Term *next() { return self->next(); } +void finish() { delete self; } +} // NetTermIterator methods + +%extend NetConnectedPinIterator { +bool has_next() { return self->hasNext(); } +const Pin *next() { return self->next(); } +void finish() { delete self; } +} // NetConnectedPinIterator methods + diff --git a/tcl/Network.tcl b/network/Network.tcl similarity index 98% rename from tcl/Network.tcl rename to network/Network.tcl index b6f31337..7fc7c1c3 100644 --- a/tcl/Network.tcl +++ b/network/Network.tcl @@ -26,9 +26,11 @@ proc_redirect report_instance { check_argc_eq1 "report_instance" $args if { [info exists flags(-connections)] } { + # deprecated 2024-01-17 sta_warn 233 "report_instance -connections is deprecated." } if { [info exists flags(-verbose)] } { + # deprecated 2024-01-17 sta_warn 234 "report_instance -verbose is deprecated." } set instance_path [lindex $args 0] @@ -183,12 +185,15 @@ proc_redirect report_net { check_argc_eq1 "report_net" $args if { [info exists flags(-connections)] } { + # deprecated 2024-01-17 sta_warn 235 "report_net -connections is deprecated." } if { [info exists flags(-verbose)] } { + # deprecated 2024-01-17 sta_warn 236 "report_net -verbose is deprecated." } if { [info exists flags(-hier_pins)] } { + # deprecated 2024-01-17 sta_warn 237 "report_net -hier_pins is deprecated." } diff --git a/tcl/NetworkEdit.i b/network/NetworkEdit.i similarity index 89% rename from tcl/NetworkEdit.i rename to network/NetworkEdit.i index 1b3e6673..ec385446 100644 --- a/tcl/NetworkEdit.i +++ b/network/NetworkEdit.i @@ -1,7 +1,3 @@ -%module NetworkEdit - -%{ - // OpenSTA, Static Timing Analyzer // Copyright (c) 2024, Parallax Software, Inc. // @@ -18,23 +14,23 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +%module NetworkEdit + +%{ using sta::Cell; using sta::Instance; using sta::Net; using sta::Port; using sta::Pin; using sta::NetworkEdit; -using sta::cmdEditNetwork; + +namespace sta { +NetworkEdit * +cmdEditNetwork(); +} %} -//////////////////////////////////////////////////////////////// -// -// SWIG type definitions. -// -//////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////// // // C++ functions visible as TCL functions. @@ -68,7 +64,7 @@ Net * make_net_cmd(const char *name, Instance *parent) { - Net *net = cmdEditNetwork()->makeNet(name, parent); + Net *net = sta::cmdEditNetwork()->makeNet(name, parent); // Sta notification unnecessary. return net; } diff --git a/tcl/NetworkEdit.tcl b/network/NetworkEdit.tcl similarity index 88% rename from tcl/NetworkEdit.tcl rename to network/NetworkEdit.tcl index 4256aaf9..38a11555 100644 --- a/tcl/NetworkEdit.tcl +++ b/network/NetworkEdit.tcl @@ -133,38 +133,6 @@ proc parse_connect_pin { arg } { return [list $inst $port] } -proc connect_pins { net pins } { - sta_warn 251 "connect_pins is deprecated. Use connect_pin." - # Visit the pins to make sure command will succeed. - set insts_ports [parse_connect_pins $pins] - if { $insts_ports == 0 } { - return 0 - } - set net [get_net_arg "net" $net] - if { $net == "NULL" } { - return 0 - } - foreach {inst port} $insts_ports { - connect_pin_cmd $inst $port $net - } - return 1 -} - -proc parse_connect_pins { arg } { - set path_regexp [path_regexp] - set inst_ports {} - # Copy backslashes that will be removed by foreach. - set arg [string map {\\ \\\\} $arg] - foreach obj $arg { - set inst_port [parse_connect_pin $obj] - if { $inst_port == 0 } { - return 0 - } - set inst_ports [concat $inst_ports $inst_port] - } - return $inst_ports -} - ################################################################ define_cmd_args "disconnect_pin" {net -all|pin} diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index f29af721..ec76fdcb 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -246,10 +246,8 @@ ConcretePiElmore::unannotatedLoads(const Pin *drvr_pin, const Parasitics *parasitics) const { PinSet loads = parasitics->loads(drvr_pin); - for (auto pin_elmore : loads_) { - const Pin *load = pin_elmore.first; + for (const auto [load, elmore] : loads_) loads.erase(load); - } return loads; } @@ -371,10 +369,8 @@ ConcretePiPoleResidue::unannotatedLoads(const Pin *drvr_pin, const Parasitics *parasitics) const { PinSet loads = parasitics->loads(drvr_pin); - for (auto pin_pole_residue : load_pole_residue_) { - const Pin *load = pin_pole_residue.first; + for (const auto& [load, pole_residue] : load_pole_residue_) loads.erase(load); - } return loads; } @@ -395,6 +391,7 @@ ConcreteParasiticNode::ConcreteParasiticNode(const Pin *pin, bool is_external) : is_net_(false), is_external_(is_external), + id_(0), cap_(0.0) { net_pin_.pin_ = pin; @@ -506,14 +503,10 @@ ConcreteParasiticNetwork::~ConcreteParasiticNetwork() void ConcreteParasiticNetwork::deleteNodes() { - for (auto id_node : sub_nodes_) { - ConcreteParasiticNode *node = id_node.second; + for (const auto& [id, node] : sub_nodes_) delete node; - } - for (auto pin_node : pin_nodes_) { - ConcreteParasiticNode *node = pin_node.second; + for (const auto& [pin, node] : pin_nodes_) delete node; - } } void @@ -547,14 +540,10 @@ ParasiticNodeSeq ConcreteParasiticNetwork::nodes() const { ParasiticNodeSeq nodes; - for (auto pin_node : pin_nodes_) { - ParasiticNode *node = pin_node.second; + for (const auto [pin, node] : pin_nodes_) nodes.push_back(node); - } - for (auto id_node : sub_nodes_) { - ParasiticNode *node = id_node.second; + for (const auto& [id, node] : sub_nodes_) nodes.push_back(node); - } return nodes; } @@ -562,14 +551,12 @@ float ConcreteParasiticNetwork::capacitance() const { float cap = 0.0; - for (auto id_node : sub_nodes_) { - ConcreteParasiticNode *node = id_node.second; + for (const auto& [id, node] : sub_nodes_) { if (!node->isExternal()) cap += node->capacitance(); } - for (auto pin_node : pin_nodes_) { - ConcreteParasiticNode *node = pin_node.second; + for (const auto [pin, node] : pin_nodes_) { if (!node->isExternal()) cap += node->capacitance(); } @@ -617,7 +604,8 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, if (id_node == sub_nodes_.end()) { node = new ConcreteParasiticNode(net, id, net != net_); sub_nodes_[net_id] = node; - max_node_id_ = max((int) max_node_id_, id); + if (net == net_) + max_node_id_ = max((int) max_node_id_, id); } else node = id_node->second; @@ -790,8 +778,7 @@ ConcreteParasitics::deleteParasitics() { int ap_count = corners_->parasiticAnalysisPtCount(); int ap_rf_count = ap_count * RiseFall::index_count; - for (auto drvr_parasitics : drvr_parasitic_map_) { - ConcreteParasitic **parasitics = drvr_parasitics.second; + for (const auto [drvr, parasitics] : drvr_parasitic_map_) { if (parasitics) { for (int i = 0; i < ap_rf_count; i++) delete parasitics[i]; @@ -800,8 +787,7 @@ ConcreteParasitics::deleteParasitics() } drvr_parasitic_map_.clear(); - for (auto net_parasitics : parasitic_network_map_) { - ConcreteParasiticNetwork **parasitics = net_parasitics.second; + for (const auto [net, parasitics] : parasitic_network_map_) { if (parasitics) { for (int i = 0; i < ap_count; i++) delete parasitics[i]; @@ -817,8 +803,8 @@ ConcreteParasitics::deleteParasitics(const Pin *drvr_pin, { ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; if (parasitics) { - for (auto tr : RiseFall::range()) { - int ap_rf_index = parasiticAnalysisPtIndex(ap, tr); + for (auto rf : RiseFall::range()) { + int ap_rf_index = parasiticAnalysisPtIndex(ap, rf); delete parasitics[ap_rf_index]; parasitics[ap_rf_index] = nullptr; } @@ -1247,8 +1233,10 @@ ConcreteParasitics::makeParasiticNetwork(const Net *net, ConcreteParasiticNetwork *parasitic = parasitics[ap_index]; if (parasitic) { delete parasitic; - for (const Pin *drvr_pin : *network_->drivers(net)) - deleteParasitics(drvr_pin, ap); + if (net) { + for (const Pin *drvr_pin : *network_->drivers(net)) + deleteParasitics(drvr_pin, ap); + } } parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_); parasitics[ap_index] = parasitic; @@ -1446,6 +1434,14 @@ ConcreteParasitics::net(const ParasiticNode *node, return cnode->net(network); } +unsigned +ConcreteParasitics::netId(const ParasiticNode *node) const +{ + const ConcreteParasiticNode *cnode = + static_cast(node); + return cnode->id(); +} + bool ConcreteParasitics::isExternal(const ParasiticNode *node) const { diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index 04014fda..fbb1b3d4 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -133,6 +133,7 @@ public: const Pin *pin(const ParasiticNode *node) const override; const Net *net(const ParasiticNode *node, const Network *network) const override; + unsigned netId(const ParasiticNode *node) const override; bool isExternal(const ParasiticNode *node) const override; float nodeGndCap(const ParasiticNode *node) const override; diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh index 4979d63f..b4503b70 100644 --- a/parasitics/ConcreteParasiticsPvt.hh +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -43,7 +43,7 @@ typedef std::map ConcreteElmoreLoadMap; typedef std::map ConcretePoleResidueMap; typedef std::map ConcreteParasiticSubNodeMap; -typedef std::map ConcreteParasiticPinNodeMap; +typedef std::map ConcreteParasiticPinNodeMap; typedef std::set ParasiticNodeSet; typedef std::set ParasiticResistorSet; typedef std::vector ParasiticResistorSeq; @@ -262,6 +262,7 @@ public: float capacitance() const { return cap_; } const char *name(const Network *network) const; const Net *net(const Network *network) const; + unsigned id() const { return id_; } bool isExternal() const { return is_external_; } const Pin *pin() const; void incrCapacitance(float cap); diff --git a/parasitics/EstimateParasitics.cc b/parasitics/EstimateParasitics.cc index fa6d0cbc..eeff7430 100644 --- a/parasitics/EstimateParasitics.cc +++ b/parasitics/EstimateParasitics.cc @@ -18,6 +18,7 @@ #include "Wireload.hh" #include "Liberty.hh" +#include "PortDirection.hh" #include "Network.hh" #include "Sdc.hh" #include "Parasitics.hh" @@ -147,7 +148,7 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, bool &elmore_use_load_cap) { if (wireload_res == 0.0 - || fanout == 0) { + || fanout == 0.0) { // No resistance, so load is capacitance only. c2 = wireload_cap + net_pin_cap; rpi = 0.0; @@ -168,22 +169,19 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, network_->connectedPinIterator(drvr_pin); while (load_iter->hasNext()) { const Pin *load_pin = load_iter->next(); + Port *port = network_->port(load_pin); + double cap = 0.0; // Bidirects don't count themselves as loads. - if (load_pin != drvr_pin && network_->isLoad(load_pin)) { - Port *port = network_->port(load_pin); - double load_cap = 0.0; - if (network_->isLeaf(load_pin)) - load_cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max); - else if (network_->isTopLevelPort(load_pin)) - load_cap = sdc_->portExtCap(port, rf, corner, min_max); - else - report_->critical(1050, "load pin not leaf or top level"); - double cap = load_cap + cap_fanout; - double y2_ = res_fanout * cap * cap; - y1 += cap; - y2 += -y2_; - y3 += y2_ * res_fanout * cap; - } + if (load_pin == drvr_pin) + cap = sdc_->portExtCap(port, rf, corner, min_max); + else if (network_->isLeaf(load_pin)) + cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max) + cap_fanout; + else if (network_->isTopLevelPort(load_pin)) + cap = sdc_->portExtCap(port, rf, corner, min_max) + cap_fanout; + double y2_ = res_fanout * cap * cap; + y1 += cap; + y2 += -y2_; + y3 += y2_ * res_fanout * cap; } delete load_iter; @@ -196,6 +194,8 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin, else { c1 = static_cast(y2 * y2 / y3); c2 = static_cast(y1 - y2 * y2 / y3); + if (c2 < 0.0) + c2 = 0.0; rpi = static_cast(-y3 * y3 / (y2 * y2 * y2)); } elmore_res = static_cast(res_fanout); diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index 0ab184bb..65b62a1d 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -203,29 +203,32 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin, const MinMax *min_max, const ParasiticAnalysisPt *ap) { + Parasitic *parasitic = nullptr; const Net *net = findParasiticNet(drvr_pin); - Parasitic *parasitic = makeParasiticNetwork(net, false, ap); - const OperatingConditions *op_cond = sdc_->operatingConditions(min_max); - float wireload_cap, wireload_res; - wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res); + if (net) { + parasitic = makeParasiticNetwork(net, false, ap); + const OperatingConditions *op_cond = sdc_->operatingConditions(min_max); + float wireload_cap, wireload_res; + wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res); - WireloadTree tree = WireloadTree::balanced; - if (op_cond) - tree = op_cond->wireloadTree(); - switch (tree) { - case WireloadTree::worst_case: - makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap, - wireload_res, fanout); - break; - case WireloadTree::balanced: - makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap, - wireload_res, fanout); - break; - case WireloadTree::best_case: - case WireloadTree::unknown: - makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap, - wireload_res, fanout); - break; + WireloadTree tree = WireloadTree::balanced; + if (op_cond) + tree = op_cond->wireloadTree(); + switch (tree) { + case WireloadTree::worst_case: + makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap, + wireload_res, fanout); + break; + case WireloadTree::balanced: + makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap, + wireload_res, fanout); + break; + case WireloadTree::best_case: + case WireloadTree::unknown: + makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap, + wireload_res, fanout); + break; + } } return parasitic; } @@ -324,4 +327,38 @@ ParasiticAnalysisPt::setCouplingCapFactor(float factor) coupling_cap_factor_ = factor; } +//////////////////////////////////////////////////////////////// + +ParasiticNodeLess::ParasiticNodeLess(const Parasitics *parasitics, + const Network *network) : + parasitics_(parasitics), + network_(network) +{ +} + +ParasiticNodeLess::ParasiticNodeLess(const ParasiticNodeLess &less) : + parasitics_(less.parasitics_), + network_(less.network_) +{ +} + +bool +ParasiticNodeLess::operator()(const ParasiticNode *node1, + const ParasiticNode *node2) const +{ + const Pin *pin1 = parasitics_->pin(node1); + const Pin *pin2 = parasitics_->pin(node2); + const Net *net1 = parasitics_->net(node1, network_); + const Net *net2 = parasitics_->net(node2, network_); + unsigned id1 = parasitics_->netId(node1); + unsigned id2 = parasitics_->netId(node2); + return (pin1 == nullptr && pin2) + || (pin1 && pin2 + && network_->id(pin1) < network_->id(pin2)) + || (pin1 == nullptr && pin2 == nullptr + && (network_->id(net1) < network_->id(net2) + || (net1 == net2 + && id1 < id2))); +} + } // namespace diff --git a/parasitics/Parasitics.i b/parasitics/Parasitics.i index 64d33e51..14989428 100644 --- a/parasitics/Parasitics.i +++ b/parasitics/Parasitics.i @@ -1,7 +1,3 @@ -%module parasitics - -%{ - // OpenSTA, Static Timing Analyzer // Copyright (c) 2024, Parallax Software, Inc. // @@ -18,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +%module parasitics + +%{ #include "Sta.hh" using sta::Sta; diff --git a/parasitics/Parasitics.tcl b/parasitics/Parasitics.tcl index 4932c5a9..0aca7d82 100644 --- a/parasitics/Parasitics.tcl +++ b/parasitics/Parasitics.tcl @@ -37,16 +37,20 @@ proc_redirect read_spef { set reduce [info exists flags(-reduce)] if { [info exists flags(-quiet)] } { + # deprecated 2024-02-08 sta_warn 272 "read_spef -quiet is deprecated." } if { [info exists keys(-reduce_to)] } { + # deprecated 2024-02-08 sta_warn 273 "read_spef -reduce_to is deprecated. Use -reduce instead." set reduce 1 } if { [info exists flags(-delete_after_reduce)] } { + # deprecated 2024-02-08 sta_warn 274 "read_spef -delete_after_reduce is deprecated." } if { [info exists flags(-save)] } { + # deprecated 2024-02-08 sta_warn 275 "read_spef -save is deprecated." } diff --git a/parasitics/ReduceParasitics.cc b/parasitics/ReduceParasitics.cc index 4d8f3f75..07c5b733 100644 --- a/parasitics/ReduceParasitics.cc +++ b/parasitics/ReduceParasitics.cc @@ -183,7 +183,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin, && resistor != from_res) { if (isVisited(onode)) { // Resistor loop. - debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor %lu", + debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor %zu", parasitics_->id(resistor)); markLoopResistor(resistor); } diff --git a/parasitics/ReportParasiticAnnotation.cc b/parasitics/ReportParasiticAnnotation.cc index bc6c9b6e..41eb3081 100644 --- a/parasitics/ReportParasiticAnnotation.cc +++ b/parasitics/ReportParasiticAnnotation.cc @@ -80,14 +80,14 @@ ReportParasiticAnnotation::report() void ReportParasiticAnnotation::reportAnnotationCounts() { - report_->reportLine("Found %lu unannotated drivers.", unannotated_.size()); + report_->reportLine("Found %zu unannotated drivers.", unannotated_.size()); if (report_unannotated_) { sort(unannotated_, PinPathNameLess(network_)); for (const Pin *drvr_pin : unannotated_) report_->reportLine(" %s", network_->pathName(drvr_pin)); } - report_->reportLine("Found %lu partially unannotated drivers.", + report_->reportLine("Found %zu partially unannotated drivers.", partially_annotated_.size()); if (report_unannotated_) { sort(partially_annotated_, PinPathNameLess(network_)); diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 8767986a..2c9c05a6 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -55,6 +55,12 @@ readSpefFile(const char *filename, StaState *sta) { bool success = false; + const ArcDelayCalc *arc_delay_calc = sta->arcDelayCalc(); + if (reduce && !arc_delay_calc->reduceSupported()) { + sta->report()->warn(1658, "Delay calculator %s does not support reduction.", + arc_delay_calc->name()); + reduce = false; + } // Use zlib to uncompress gzip'd files automagically. gzFile stream = gzopen(filename, "rb"); if (stream) { @@ -120,10 +126,8 @@ SpefReader::~SpefReader() design_flow_ = nullptr; } - for (auto index_name : name_map_) { - char *name = index_name.second; + for (const auto [index, name] : name_map_) stringDelete(name); - } } void @@ -496,7 +500,9 @@ SpefReader::findParasiticNode(char *name, int id = atoi(id_str); if (local_only && !network_->isConnected(net, net_)) - warn(1653, "%s not connected to net %s.", name, network_->pathName(net_)); + warn(1653, "%s not connected to net %s.", + name, + network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, net, id, network_); } else diff --git a/power/Power.cc b/power/Power.cc index 112af13d..93f4cd59 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -19,6 +19,7 @@ #include // max #include // abs +#include "cudd.h" #include "Debug.hh" #include "EnumNameMap.hh" #include "Hash.hh" @@ -502,8 +503,6 @@ Power::clockGatePins(const Instance *inst, //////////////////////////////////////////////////////////////// -#if CUDD - PwrActivity Power::evalActivity(FuncExpr *expr, const Instance *inst) @@ -586,12 +585,10 @@ Power::evalBddActivity(DdNode *bdd, const Instance *inst) { float activity = 0.0; - for (auto port_var : bdd_.portVarMap()) { - const LibertyPort *port = port_var.first; + for (const auto [port, var_node] : bdd_.portVarMap()) { const Pin *pin = findLinkPin(inst, port); if (pin) { PwrActivity var_activity = findActivity(pin); - DdNode *var_node = port_var.second; unsigned int var_index = Cudd_NodeReadIndex(var_node); DdNode *diff = Cudd_bddBooleanDiff(bdd_.cuddMgr(), bdd, var_index); Cudd_Ref(diff); @@ -611,112 +608,6 @@ Power::evalBddActivity(DdNode *bdd, return activity; } -#else - -PwrActivity -Power::evalActivity(FuncExpr *expr, - const Instance *inst) -{ - return evalActivity(expr, inst, nullptr, true); -} - -// Eval activity thru expr. -// With cofactor_port eval the positive/negative cofactor of expr wrt cofactor_port. -PwrActivity -Power::evalActivity(FuncExpr *expr, - const Instance *inst, - const LibertyPort *cofactor_port, - bool cofactor_positive) -{ - switch (expr->op()) { - case FuncExpr::op_port: { - LibertyPort *port = expr->port(); - if (port == cofactor_port) { - if (cofactor_positive) - return PwrActivity(0.0, 1.0, PwrActivityOrigin::constant); - else - return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant); - } - if (port->direction()->isInternal()) - return findSeqActivity(inst, port); - else { - Pin *pin = findLinkPin(inst, port); - if (pin) { - PwrActivity activity = findActivity(pin); - activity.setOrigin(PwrActivityOrigin::propagated); - return activity; - } - } - return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant); - } - case FuncExpr::op_not: { - PwrActivity activity1 = evalActivity(expr->left(), inst, - cofactor_port, cofactor_positive); - return PwrActivity(activity1.activity(), - 1.0 - activity1.duty(), - PwrActivityOrigin::propagated); - } - case FuncExpr::op_or: { - PwrActivity activity1 = evalActivity(expr->left(), inst, - cofactor_port, cofactor_positive); - PwrActivity activity2 = evalActivity(expr->right(), inst, - cofactor_port, cofactor_positive); - float p1 = 1.0 - activity1.duty(); - float p2 = 1.0 - activity2.duty(); - return PwrActivity(activity1.activity() * p2 + activity2.activity() * p1, - // d1 + d2 - d1 * d2 - 1.0 - p1 * p2, - PwrActivityOrigin::propagated); - } - case FuncExpr::op_and: { - PwrActivity activity1 = evalActivity(expr->left(), inst, - cofactor_port, cofactor_positive); - PwrActivity activity2 = evalActivity(expr->right(), inst, - cofactor_port, cofactor_positive); - float p1 = activity1.duty(); - float p2 = activity2.duty(); - return PwrActivity(activity1.activity() * p2 + activity2.activity() * p1, - p1 * p2, - PwrActivityOrigin::propagated); - } - case FuncExpr::op_xor: { - PwrActivity activity1 = evalActivity(expr->left(), inst, - cofactor_port, cofactor_positive); - PwrActivity activity2 = evalActivity(expr->right(), inst, - cofactor_port, cofactor_positive); - float d1 = activity1.duty(); - float d2 = activity2.duty(); - float p1 = d1 * (1.0 - d2); - float p2 = (1.0 - d1) * d2; - return PwrActivity(activity1.activity() + activity2.activity(), - p1 + p2, - PwrActivityOrigin::propagated); - } - case FuncExpr::op_one: - return PwrActivity(0.0, 1.0, PwrActivityOrigin::constant); - case FuncExpr::op_zero: - return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant); - } - return PwrActivity(); -} - -// Eval activity of difference(expr) wrt cofactor port. -float -Power::evalDiffDuty(FuncExpr *expr, - LibertyPort *cofactor_port, - const Instance *inst) -{ - // Activity of positive/negative cofactors. - PwrActivity pos = evalActivity(expr, inst, cofactor_port, true); - PwrActivity neg = evalActivity(expr, inst, cofactor_port, false); - // difference = xor(pos, neg). - float p1 = pos.duty() * (1.0 - neg.duty()); - float p2 = neg.duty() * (1.0 - pos.duty()); - return p1 + p2; -} - -#endif // CUDD - //////////////////////////////////////////////////////////////// void diff --git a/power/Power.i b/power/Power.i index aaf3ff23..61f6dd2b 100644 --- a/power/Power.i +++ b/power/Power.i @@ -1,7 +1,3 @@ -%module power - -%{ - // OpenSTA, Static Timing Analyzer // Copyright (c) 2024, Parallax Software, Inc. // @@ -18,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +%module power + +%{ #include "Sta.hh" #include "power/Power.hh" #include "power/VcdReader.hh" @@ -60,7 +59,8 @@ instance_power(Instance *inst, const Corner *corner) { cmdLinkedNetwork(); - PowerResult power = Sta::sta()->power(inst, corner); + Sta *sta = Sta::sta(); + PowerResult power = sta->power(inst, corner); FloatSeq powers; powers.push_back(power.internal()); powers.push_back(power.switching()); @@ -73,14 +73,16 @@ void set_power_global_activity(float activity, float duty) { - Sta::sta()->power()->setGlobalActivity(activity, duty); + Power *power = Sta::sta()->power(); + power->setGlobalActivity(activity, duty); } void set_power_input_activity(float activity, float duty) { - return Sta::sta()->power()->setInputActivity(activity, duty); + Power *power = Sta::sta()->power(); + return power->setInputActivity(activity, duty); } void @@ -88,7 +90,8 @@ set_power_input_port_activity(const Port *input_port, float activity, float duty) { - return Sta::sta()->power()->setInputPortActivity(input_port, activity, duty); + Power *power = Sta::sta()->power(); + return power->setInputPortActivity(input_port, activity, duty); } void @@ -96,21 +99,24 @@ set_power_pin_activity(const Pin *pin, float activity, float duty) { - return Sta::sta()->power()->setUserActivity(pin, activity, duty, - PwrActivityOrigin::user); + Power *power = Sta::sta()->power(); + return power->setUserActivity(pin, activity, duty, PwrActivityOrigin::user); } void read_vcd_activities(const char *filename, const char *scope) { - readVcdActivities(filename, scope, Sta::sta()); + Sta *sta = Sta::sta(); + cmdLinkedNetwork(); + readVcdActivities(filename, scope, sta); } void report_vcd_waveforms(const char *filename) { - reportVcdWaveforms(filename, Sta::sta()); + Sta *sta = Sta::sta(); + reportVcdWaveforms(filename, sta); } // debugging @@ -118,7 +124,8 @@ void report_vcd_var_values(const char *filename, const char *var_name) { - reportVcdVarValues(filename, var_name, Sta::sta()); + Sta *sta = Sta::sta(); + reportVcdVarValues(filename, var_name, sta); } %} // inline diff --git a/power/Power.tcl b/power/Power.tcl index c5122bc6..903ddff5 100644 --- a/power/Power.tcl +++ b/power/Power.tcl @@ -35,6 +35,10 @@ proc_redirect report_power { keys {-instances -corner -digits} flags {} check_argc_eq0 "report_power" $args + + if { ![liberty_libraries_exist] } { + sta_error 304 "No liberty libraries have been read." + } if { [info exists keys(-digits)] } { set digits $keys(-digits) check_positive_integer "-digits" $digits @@ -51,6 +55,16 @@ proc_redirect report_power { } } +proc liberty_libraries_exist {} { + set lib_iter [liberty_library_iterator] + set have_liberty 0 + if { [$lib_iter has_next] } { + set have_liberty 1 + } + $lib_iter finish + return $have_liberty +} + proc report_power_design { corner digits } { set power_result [design_power $corner] set totals [lrange $power_result 0 3] diff --git a/power/ReadVcdActivities.cc b/power/ReadVcdActivities.cc index 396dab63..fec6b2d3 100644 --- a/power/ReadVcdActivities.cc +++ b/power/ReadVcdActivities.cc @@ -105,7 +105,7 @@ ReadVcdActivities::readActivities() setActivities(); else report_->warn(1450, "VCD max time is zero."); - report_->reportLine("Annotated %lu pin activities.", annotated_pins_.size()); + report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size()); } void diff --git a/sdc/DeratingFactors.cc b/sdc/DeratingFactors.cc index 843f5655..a7499f75 100644 --- a/sdc/DeratingFactors.cc +++ b/sdc/DeratingFactors.cc @@ -41,8 +41,8 @@ DeratingFactors::setFactor(PathClkOrData clk_data, const EarlyLate *early_late, float factor) { - for (auto tr1 : rf->range()) - factors_[int(clk_data)].setValue(tr1, early_late, factor); + for (auto rf1 : rf->range()) + factors_[int(clk_data)].setValue(rf1, early_late, factor); } void diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc index 204a75a3..b04fb5a8 100644 --- a/sdc/ExceptionPath.cc +++ b/sdc/ExceptionPath.cc @@ -90,7 +90,8 @@ ExceptionPath::ExceptionPath(ExceptionFrom *from, to_(to), min_max_(min_max), own_pts_(own_pts), - priority_(priority) + priority_(priority), + id_(0) { makeStates(); } @@ -125,6 +126,12 @@ ExceptionPath::asString(const Network *network) const return result; } +void +ExceptionPath::setId(size_t id) +{ + id_ = id; +} + ExceptionPt * ExceptionPath::firstPt() { @@ -2247,19 +2254,14 @@ ExceptionState::hash() const return hashSum(exception_->hash(), index_); } -ExceptionStateLess::ExceptionStateLess(const Network *network) : - network_(network) -{ -} - bool ExceptionStateLess::operator()(const ExceptionState *state1, const ExceptionState *state2) const { const ExceptionPath *except1 = state1->exception(); const ExceptionPath *except2 = state2->exception(); - ExceptionPathLess except_less(network_); - return except_less(except1, except2) + return except1->id() < except2->id() + //return except1 < except2 || (except1 == except2 && state1->index() < state2->index()); } diff --git a/sdc/InputDrive.cc b/sdc/InputDrive.cc index f17f8c00..606df5f5 100644 --- a/sdc/InputDrive.cc +++ b/sdc/InputDrive.cc @@ -20,17 +20,17 @@ namespace sta { InputDrive::InputDrive() { - for (auto tr_index : RiseFall::rangeIndex()) { + for (auto rf_index : RiseFall::rangeIndex()) { for (auto mm_index : MinMax::rangeIndex()) - drive_cells_[tr_index][mm_index] = nullptr; + drive_cells_[rf_index][mm_index] = nullptr; } } InputDrive::~InputDrive() { - for (auto tr_index : RiseFall::rangeIndex()) { + for (auto rf_index : RiseFall::rangeIndex()) { for (auto mm_index : MinMax::rangeIndex()) { - InputDriveCell *drive_cell = drive_cells_[tr_index][mm_index]; + InputDriveCell *drive_cell = drive_cells_[rf_index][mm_index]; delete drive_cell; } } @@ -210,8 +210,8 @@ InputDriveCell::setToPort(const LibertyPort *to_port) void InputDriveCell::setFromSlews(float *from_slews) { - for (auto tr_index : RiseFall::rangeIndex()) - from_slews_[tr_index] = from_slews[tr_index]; + for (auto rf_index : RiseFall::rangeIndex()) + from_slews_[rf_index] = from_slews[rf_index]; } bool diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index b063a8c4..7925f048 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -110,6 +110,7 @@ Sdc::Sdc(StaState *sta) : disabled_wire_edges_(network_), disabled_clk_gating_checks_inst_(network_), disabled_clk_gating_checks_pin_(network_), + exception_id_(0), have_thru_hpin_exceptions_(false), first_thru_edge_exceptions_(0, PinPairHash(network_), PinPairEqual()), path_delay_internal_startpoints_(network_), @@ -271,15 +272,12 @@ Sdc::deleteConstraints() inst_min_pulse_width_map_.deleteContentsClear(); clk_min_pulse_width_map_.deleteContentsClear(); - for (auto pin_data_check : data_checks_from_map_) { - DataCheckSet *checks = pin_data_check.second; + for (auto [pin, checks] : data_checks_from_map_) { checks->deleteContents(); delete checks; } - for (auto pin_data_check : data_checks_to_map_) { - DataCheckSet *checks = pin_data_check.second; + for (auto [pin, checks] : data_checks_to_map_) delete checks; - } input_delays_.deleteContents(); input_delay_pin_map_.deleteContents(); @@ -323,9 +321,7 @@ Sdc::removeNetLoadCaps() void Sdc::removeLibertyAnnotations() { - for (auto cell_port : disabled_cell_ports_) { - DisabledCellPorts *disable = cell_port.second; - LibertyCell *cell = disable->cell(); + for (auto [cell, disable] : disabled_cell_ports_) { if (disable->all()) cell->setIsDisabledConstraint(false); @@ -444,6 +440,57 @@ Sdc::isConstrained(const Net *net) const //////////////////////////////////////////////////////////////// +PortSeq +Sdc::allInputs(bool no_clks) +{ + PortSeq ports; + Instance *top_inst = network_->topInstance(); + InstancePinIterator *pin_iter = network_->pinIterator(top_inst); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + const Port *port = network_->port(pin); + PortDirection *dir = network_->direction(port); + if (dir->isAnyInput() + && !(no_clks && isClock(pin))) + portMembers(port, ports); + } + delete pin_iter; + return ports; +} + +PortSeq +Sdc::allOutputs() +{ + PortSeq ports; + Instance *top_inst = network_->topInstance(); + InstancePinIterator *pin_iter = network_->pinIterator(top_inst); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + const Port *port = network_->port(pin); + PortDirection *dir = network_->direction(port); + if (dir->isAnyOutput()) + portMembers(port, ports); + } + delete pin_iter; + return ports; +} + +void +Sdc::portMembers(const Port *port, + PortSeq &ports) +{ + if (network_->isBus(port)) { + PortMemberIterator *member_iter = network_->memberIterator(port); + while (member_iter->hasNext()) { + Port *member = member_iter->next(); + ports.push_back(member); + } + delete member_iter; + } + else + ports.push_back(port); +} + void Sdc::setAnalysisType(AnalysisType analysis_type) { @@ -1906,8 +1953,8 @@ void Sdc::ensureClkGroupExclusions() { if (clk_group_exclusions_.empty()) { - for (auto name_clk_groups : clk_groups_name_map_) - makeClkGroupExclusions(name_clk_groups.second); + for (const auto [name, clk_groups] : clk_groups_name_map_) + makeClkGroupExclusions(clk_groups); } } @@ -2020,8 +2067,7 @@ Sdc::removeClockGroupsLogicallyExclusive(const char *name) removeClockGroups(groups); } else { - for (auto name_group : clk_groups_name_map_) { - ClockGroups *groups = name_group.second; + for (const auto [name, groups] : clk_groups_name_map_) { if (groups->logicallyExclusive()) removeClockGroups(groups); } @@ -2037,8 +2083,7 @@ Sdc::removeClockGroupsPhysicallyExclusive(const char *name) removeClockGroups(groups); } else { - for (auto name_group : clk_groups_name_map_) { - ClockGroups *groups = name_group.second; + for (const auto [name, groups] : clk_groups_name_map_) { if (groups->physicallyExclusive()) removeClockGroups(groups); } @@ -2054,8 +2099,7 @@ Sdc::removeClockGroupsAsynchronous(const char *name) removeClockGroups(groups); } else { - for (auto name_group : clk_groups_name_map_) { - ClockGroups *groups = name_group.second; + for (const auto [name, groups] : clk_groups_name_map_) { if (groups->asynchronous()) removeClockGroups(groups); } @@ -2075,10 +2119,8 @@ Sdc::removeClockGroups(ClockGroups *groups) void Sdc::clockGroupsDeleteClkRefs(Clock *clk) { - for (auto name_group : clk_groups_name_map_) { - ClockGroups *groups = name_group.second; + for (const auto [name, groups] : clk_groups_name_map_) groups->removeClock(clk); - } clearClkGroupExclusions(); } @@ -3983,9 +4025,7 @@ Sdc::clearGroupPathMap() { // GroupPath exceptions are deleted with other exceptions. // Delete group_path name strings. - for (auto name_groups : group_path_map_) { - const char *name = name_groups.first; - GroupPathSet *groups = name_groups.second; + for (auto [name, groups] : group_path_map_) { stringDelete(name); groups->deleteContents(); delete groups; @@ -4513,6 +4553,7 @@ void Sdc::recordException(ExceptionPath *exception) { exceptions_.insert(exception); + exception->setId(++exception_id_); recordMergeHashes(exception); recordExceptionFirstPts(exception); checkForThruHpins(exception); @@ -4774,6 +4815,7 @@ void Sdc::deleteExceptions() { exceptions_.deleteContentsClear(); + exception_id_ = 0; first_from_pin_exceptions_.deleteContentsClear(); first_from_clk_exceptions_.deleteContentsClear(); @@ -5164,7 +5206,7 @@ Sdc::exceptionFromStates(const ExceptionPathSet *exceptions, // but flush all other exception states because they are lower // priority. if (states == nullptr) - states = new ExceptionStateSet(network_); + states = new ExceptionStateSet(); states->clear(); states->insert(state); // No need to examine other exceptions from this @@ -5172,7 +5214,7 @@ Sdc::exceptionFromStates(const ExceptionPathSet *exceptions, return false; } if (states == nullptr) - states = new ExceptionStateSet(network_); + states = new ExceptionStateSet(); states->insert(state); } } @@ -5219,7 +5261,7 @@ Sdc::filterRegQStates(const Pin *to_pin, && exception->matchesFirstPt(to_rf, min_max)) { ExceptionState *state = exception->firstState(); if (states == nullptr) - states = new ExceptionStateSet(network_); + states = new ExceptionStateSet(); states->insert(state); } } @@ -5263,7 +5305,7 @@ Sdc::exceptionThruStates(const ExceptionPathSet *exceptions, if (exception->matchesFirstPt(to_rf, min_max)) { ExceptionState *state = exception->firstState(); if (states == nullptr) - states = new ExceptionStateSet(network_); + states = new ExceptionStateSet(); states->insert(state); } } diff --git a/sdc/Sdc.i b/sdc/Sdc.i new file mode 100644 index 00000000..909bb927 --- /dev/null +++ b/sdc/Sdc.i @@ -0,0 +1,1479 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + +%module sdc + +%{ +#include "Sdc.hh" +#include "Wireload.hh" +#include "Clock.hh" +#include "PortDelay.hh" +#include "Property.hh" +#include "Sta.hh" + +using namespace sta; + +%} + +//////////////////////////////////////////////////////////////// +// +// Empty class definitions to make swig happy. +// Private constructor/destructor so swig doesn't emit them. +// +//////////////////////////////////////////////////////////////// + +class Clock +{ +private: + Clock(); + ~Clock(); +}; + +class ClockEdge +{ +private: + ClockEdge(); + ~ClockEdge(); +}; + +class ExceptionFrom +{ +private: + ExceptionFrom(); + ~ExceptionFrom(); +}; + +class ExceptionThru +{ +private: + ExceptionThru(); + ~ExceptionThru(); +}; + +class ExceptionTo +{ +private: + ExceptionTo(); + ~ExceptionTo(); +}; + +class OperatingConditions +{ +private: + OperatingConditions(); + ~OperatingConditions(); +}; + +%inline %{ + +void +write_sdc_cmd(const char *filename, + bool leaf, + bool compatible, + int digits, + bool gzip, + bool no_timestamp) +{ + cmdLinkedNetwork(); + Sta::sta()->writeSdc(filename, leaf, compatible, digits, gzip, no_timestamp); +} + +void +set_analysis_type_cmd(const char *analysis_type) +{ + AnalysisType type; + if (stringEq(analysis_type, "single")) + type = AnalysisType::single; + else if (stringEq(analysis_type, "bc_wc")) + type = AnalysisType::bc_wc; + else if (stringEq(analysis_type, "on_chip_variation")) + type = AnalysisType::ocv; + else { + Sta::sta()->report()->warn(2121, "unknown analysis type"); + type = AnalysisType::single; + } + Sta::sta()->setAnalysisType(type); +} + +OperatingConditions * +operating_conditions(const MinMax *min_max) +{ + return Sta::sta()->operatingConditions(min_max); +} + +void +set_operating_conditions_cmd(OperatingConditions *op_cond, + const MinMaxAll *min_max) +{ + Sta::sta()->setOperatingConditions(op_cond, min_max); +} + +const char * +operating_condition_analysis_type() +{ + switch (Sta::sta()->sdc()->analysisType()){ + case AnalysisType::single: + return "single"; + case AnalysisType::bc_wc: + return "bc_wc"; + case AnalysisType::ocv: + return "on_chip_variation"; + } + // Prevent warnings from lame compilers. + return "?"; +} + +void +set_instance_pvt(Instance *inst, + const MinMaxAll *min_max, + float process, + float voltage, + float temperature) +{ + cmdLinkedNetwork(); + Pvt pvt(process, voltage, temperature); + Sta::sta()->setPvt(inst, min_max, pvt); +} + +float +port_ext_pin_cap(const Port *port, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + int fanout; + Sta::sta()->portExtCaps(port, corner, min_max, 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); +} + +float +port_ext_wire_cap(const Port *port, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + int fanout; + Sta::sta()->portExtCaps(port, corner, min_max, 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); +} + +void +set_port_ext_fanout_cmd(const Port *port, + int fanout, + const Corner *corner, + const MinMaxAll *min_max) +{ + Sta::sta()->setPortExtFanout(port, fanout, corner, min_max); +} + +float +port_ext_fanout(const Port *port, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + int fanout; + Sta::sta()->portExtCaps(port, corner, min_max, 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) +{ + Sta::sta()->setNetWireCap(net, subtract_pin_cap, corner, min_max, cap); +} + +void +set_wire_load_mode_cmd(const char *mode_name) +{ + WireloadMode mode = stringWireloadMode(mode_name); + if (mode == WireloadMode::unknown) + Sta::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); +} + +void +set_wire_load_cmd(Wireload *wireload, + const MinMaxAll *min_max) +{ + Sta::sta()->setWireload(wireload, min_max); +} + +void +set_wire_load_selection_group_cmd(WireloadSelection *selection, + const MinMaxAll *min_max) +{ + Sta::sta()->setWireloadSelection(selection, min_max); +} + +void +make_clock(const char *name, + PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, + char *comment) +{ + cmdLinkedNetwork(); + Sta::sta()->makeClock(name, pins, add_to_pins, period, waveform, comment); +} + +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) +{ + cmdLinkedNetwork(); + Sta::sta()->makeGeneratedClock(name, pins, add_to_pins, + src_pin, master_clk, + divide_by, multiply_by, duty_cycle, invert, + combinational, edges, edge_shifts, + comment); +} + +void +remove_clock_cmd(Clock *clk) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClock(clk); +} + +void +set_propagated_clock_cmd(Clock *clk) +{ + cmdLinkedNetwork(); + Sta::sta()->setPropagatedClock(clk); +} + +void +set_propagated_clock_pin_cmd(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->setPropagatedClock(pin); +} + +void +unset_propagated_clock_cmd(Clock *clk) +{ + cmdLinkedNetwork(); + Sta::sta()->removePropagatedClock(clk); +} + +void +unset_propagated_clock_pin_cmd(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removePropagatedClock(pin); +} + +void +set_clock_slew_cmd(Clock *clk, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockSlew(clk, rf, min_max, slew); +} + +void +unset_clock_slew_cmd(Clock *clk) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockSlew(clk); +} + +void +set_clock_latency_cmd(Clock *clk, + Pin *pin, + const RiseFallBoth *rf, + MinMaxAll *min_max, float delay) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockLatency(clk, pin, rf, min_max, delay); +} + +void +set_clock_insertion_cmd(Clock *clk, + Pin *pin, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockInsertion(clk, pin, rf, min_max, early_late, delay); +} + +void +unset_clock_latency_cmd(Clock *clk, + Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockLatency(clk, pin); +} + +void +unset_clock_insertion_cmd(Clock *clk, + Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockInsertion(clk, pin); +} + +void +set_clock_uncertainty_clk(Clock *clk, + const SetupHoldAll *setup_hold, + float uncertainty) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockUncertainty(clk, setup_hold, uncertainty); +} + +void +unset_clock_uncertainty_clk(Clock *clk, + const SetupHoldAll *setup_hold) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockUncertainty(clk, setup_hold); +} + +void +set_clock_uncertainty_pin(Pin *pin, + const MinMaxAll *min_max, + float uncertainty) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockUncertainty(pin, min_max, uncertainty); +} + +void +unset_clock_uncertainty_pin(Pin *pin, + const MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockUncertainty(pin, min_max); +} + +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) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, + uncertainty); +} + +void +unset_inter_clock_uncertainty(Clock *from_clk, + const RiseFallBoth *from_tr, + Clock *to_clk, + const RiseFallBoth *to_tr, + const MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max); +} + +void +set_clock_gating_check_cmd(const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin) +{ + Sta::sta()->setClockGatingCheck(rf, setup_hold, margin); +} + +void +set_clock_gating_check_clk_cmd(Clock *clk, + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin) +{ + Sta::sta()->setClockGatingCheck(clk, rf, setup_hold, margin); +} + +void +set_clock_gating_check_pin_cmd(Pin *pin, + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) +{ + Sta::sta()->setClockGatingCheck(pin, rf, setup_hold, margin, active_value); +} + +void +set_clock_gating_check_instance_cmd(Instance *inst, + const RiseFallBoth *rf, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) +{ + Sta::sta()->setClockGatingCheck(inst, rf, setup_hold, margin, active_value); +} + +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) +{ + Sta::sta()->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold, margin); +} + +void +unset_data_check_cmd(Pin *from, + 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); +} + +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) +{ + cmdLinkedNetwork(); + Sta::sta()->setInputDelay(pin, rf, clk, clk_rf, ref_pin, + source_latency_included, network_latency_included, + min_max, add, delay); +} + +void +unset_input_delay_cmd(Pin *pin, + RiseFallBoth *rf, + Clock *clk, + RiseFall *clk_rf, + MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->removeInputDelay(pin, rf, clk, clk_rf, min_max); +} + +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) +{ + cmdLinkedNetwork(); + Sta::sta()->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, + source_latency_included, network_latency_included, + min_max, add, delay); +} + +void +unset_output_delay_cmd(Pin *pin, + RiseFallBoth *rf, + Clock *clk, + RiseFall *clk_rf, + MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->removeOutputDelay(pin, rf, clk, clk_rf, min_max); +} + +void +disable_cell(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(cell, from, to); +} + +void +unset_disable_cell(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(cell, from, to); +} + +void +disable_lib_port(LibertyPort *port) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(port); +} + +void +unset_disable_lib_port(LibertyPort *port) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(port); +} + +void +disable_port(Port *port) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(port); +} + +void +unset_disable_port(Port *port) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(port); +} + +void +disable_instance(Instance *instance, + LibertyPort *from, + LibertyPort *to) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(instance, from, to); +} + +void +unset_disable_instance(Instance *instance, + LibertyPort *from, + LibertyPort *to) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(instance, from, to); +} + +void +disable_pin(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(pin); +} + +void +unset_disable_pin(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(pin); +} + +void +disable_edge(Edge *edge) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(edge); +} + +void +unset_disable_edge(Edge *edge) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(edge); +} + +void +disable_timing_arc_set(TimingArcSet *arc_set) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(arc_set); +} + +void +unset_disable_timing_arc_set(TimingArcSet *arc_set) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(arc_set); +} + +void +disable_clock_gating_check_inst(Instance *inst) +{ + cmdLinkedNetwork(); + Sta::sta()->disableClockGatingCheck(inst); +} + +void +disable_clock_gating_check_pin(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->disableClockGatingCheck(pin); +} + +void +unset_disable_clock_gating_check_inst(Instance *inst) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisableClockGatingCheck(inst); +} + +void +unset_disable_clock_gating_check_pin(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisableClockGatingCheck(pin); +} + +EdgeSeq +disabled_edges_sorted() +{ + cmdLinkedNetwork(); + return Sta::sta()->disabledEdgesSorted(); +} + +bool +timing_arc_disabled(Edge *edge, + TimingArc *arc) +{ + Graph *graph = Sta::sta()->graph(); + return !searchThru(edge, arc, graph); +} + +void +make_false_path(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const char *comment) +{ + cmdLinkedNetwork(); + Sta::sta()->makeFalsePath(from, thrus, to, min_max, comment); +} + +void +make_multicycle_path(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + const char *comment) +{ + cmdLinkedNetwork(); + Sta::sta()->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, + path_multiplier, comment); +} + +void +make_path_delay(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, + float delay, + const char *comment) +{ + cmdLinkedNetwork(); + Sta::sta()->makePathDelay(from, thrus, to, min_max, + ignore_clk_latency, delay, comment); +} + +void +reset_path_cmd(ExceptionFrom * + from, ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->resetPath(from, thrus, to, min_max); + // 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. + delete thrus; +} + +void +make_group_path(const char *name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const char *comment) +{ + cmdLinkedNetwork(); + if (name[0] == '\0') + name = nullptr; + Sta::sta()->makeGroupPath(name, is_default, from, thrus, to, comment); +} + +bool +is_path_group_name(const char *name) +{ + cmdLinkedNetwork(); + return Sta::sta()->isGroupPathName(name); +} + +ExceptionFrom * +make_exception_from(PinSet *from_pins, + ClockSet *from_clks, + InstanceSet *from_insts, + const RiseFallBoth *from_tr) +{ + cmdLinkedNetwork(); + return Sta::sta()->makeExceptionFrom(from_pins, from_clks, from_insts, + from_tr); +} + +void +delete_exception_from(ExceptionFrom *from) +{ + Sta::sta()->deleteExceptionFrom(from); +} + +void +check_exception_from_pins(ExceptionFrom *from, + const char *file, + int line) +{ + Sta::sta()->checkExceptionFromPins(from, file, line); +} + +ExceptionThru * +make_exception_thru(PinSet *pins, + NetSet *nets, + InstanceSet *insts, + const RiseFallBoth *rf) +{ + cmdLinkedNetwork(); + return Sta::sta()->makeExceptionThru(pins, nets, insts, rf); +} + +void +delete_exception_thru(ExceptionThru *thru) +{ + Sta::sta()->deleteExceptionThru(thru); +} + +ExceptionTo * +make_exception_to(PinSet *to_pins, + ClockSet *to_clks, + InstanceSet *to_insts, + const RiseFallBoth *rf, + RiseFallBoth *end_rf) +{ + cmdLinkedNetwork(); + return Sta::sta()->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf); +} + +void +delete_exception_to(ExceptionTo *to) +{ + Sta::sta()->deleteExceptionTo(to); +} + +void +check_exception_to_pins(ExceptionTo *to, + const char *file, + int line) +{ + Sta::sta()->checkExceptionToPins(to, file, line); +} + +ClockGroups * +make_clock_groups(const char *name, + 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); +} + +void +clock_groups_make_group(ClockGroups *clk_groups, + ClockSet *clks) +{ + Sta::sta()->makeClockGroup(clk_groups, clks); +} + +void +unset_clock_groups_logically_exclusive(const char *name) +{ + Sta::sta()->removeClockGroupsLogicallyExclusive(name); +} + +void +unset_clock_groups_physically_exclusive(const char *name) +{ + Sta::sta()->removeClockGroupsPhysicallyExclusive(name); +} + +void +unset_clock_groups_asynchronous(const char *name) +{ + Sta::sta()->removeClockGroupsAsynchronous(name); +} + +// Debugging function. +bool +same_clk_group(Clock *clk1, + Clock *clk2) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + return sdc->sameClockGroupExplicit(clk1, clk2); +} + +void +set_clock_sense_cmd(PinSet *pins, + ClockSet *clks, + bool positive, + bool negative, + bool stop_propagation) +{ + Sta *sta = Sta::sta(); + if (positive) + sta->setClockSense(pins, clks, ClockSense::positive); + else if (negative) + sta->setClockSense(pins, clks, ClockSense::negative); + else if (stop_propagation) + sta->setClockSense(pins, clks, ClockSense::stop); + else + sta->report()->critical(1577, "unknown clock sense"); +} + +void +set_input_slew_cmd(Port *port, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setInputSlew(port, rf, min_max, slew); +} + +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) +{ + 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); +} + +void +set_drive_resistance_cmd(Port *port, + const RiseFallBoth *rf, + const MinMaxAll *min_max, + float res) +{ + cmdLinkedNetwork(); + Sta::sta()->setDriveResistance(port, rf, min_max, res); +} + +void +set_slew_limit_clk(Clock *clk, + const RiseFallBoth *rf, + PathClkOrData clk_data, + const MinMax *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setSlewLimit(clk, rf, clk_data, min_max, slew); +} + +void +set_slew_limit_port(Port *port, + const MinMax *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setSlewLimit(port, min_max, slew); +} + +void +set_slew_limit_cell(Cell *cell, + const MinMax *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setSlewLimit(cell, min_max, slew); +} + +void +set_port_capacitance_limit(Port *port, + const MinMax *min_max, + float cap) +{ + Sta::sta()->setCapacitanceLimit(port, min_max, cap); +} + +void +set_pin_capacitance_limit(Pin *pin, + const MinMax *min_max, + float cap) +{ + Sta::sta()->setCapacitanceLimit(pin, min_max, cap); +} + +void +set_cell_capacitance_limit(Cell *cell, + const MinMax *min_max, + float cap) +{ + Sta::sta()->setCapacitanceLimit(cell, min_max, cap); +} + +void +set_latch_borrow_limit_pin(Pin *pin, + float limit) +{ + Sta::sta()->setLatchBorrowLimit(pin, limit); +} + +void +set_latch_borrow_limit_inst(Instance *inst, + float limit) +{ + Sta::sta()->setLatchBorrowLimit(inst, limit); +} + +void +set_latch_borrow_limit_clk(Clock *clk, float limit) +{ + Sta::sta()->setLatchBorrowLimit(clk, limit); +} + +void +set_min_pulse_width_global(const RiseFallBoth *rf, + float min_width) +{ + Sta::sta()->setMinPulseWidth(rf, min_width); +} + +void +set_min_pulse_width_pin(Pin *pin, + const RiseFallBoth *rf, + float min_width) +{ + Sta::sta()->setMinPulseWidth(pin, rf, min_width); +} + +void +set_min_pulse_width_clk(Clock *clk, + const RiseFallBoth *rf, + float min_width) +{ + Sta::sta()->setMinPulseWidth(clk, rf, min_width); +} + +void +set_min_pulse_width_inst(Instance *inst, + const RiseFallBoth *rf, + float min_width) +{ + Sta::sta()->setMinPulseWidth(inst, rf, min_width); +} + +void +set_max_area_cmd(float area) +{ + Sta::sta()->setMaxArea(area); +} + +void +set_port_fanout_limit(Port *port, + const MinMax *min_max, + float fanout) +{ + Sta::sta()->setFanoutLimit(port, min_max, fanout); +} + +void +set_cell_fanout_limit(Cell *cell, + const MinMax *min_max, + float fanout) +{ + Sta::sta()->setFanoutLimit(cell, min_max, fanout); +} + +void +set_logic_value_cmd(Pin *pin, + LogicValue value) +{ + Sta::sta()->setLogicValue(pin, value); +} + +void +set_case_analysis_cmd(Pin *pin, + LogicValue value) +{ + Sta::sta()->setCaseAnalysis(pin, value); +} + +void +unset_case_analysis_cmd(Pin *pin) +{ + Sta::sta()->removeCaseAnalysis(pin); +} + +void +set_timing_derate_cmd(TimingDerateType type, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) +{ + Sta::sta()->setTimingDerate(type, clk_data, rf, early_late, derate); +} + +void +set_timing_derate_net_cmd(const Net *net, + PathClkOrData clk_data, + const RiseFallBoth *rf, + const EarlyLate *early_late, + float derate) +{ + Sta::sta()->setTimingDerate(net, clk_data, rf, early_late, derate); +} + +void +set_timing_derate_inst_cmd(const Instance *inst, + 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); +} + +void +set_timing_derate_cell_cmd(const LibertyCell *cell, + 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); +} + +void +unset_timing_derate_cmd() +{ + Sta::sta()->unsetTimingDerate(); +} + +Clock * +find_clock(const char *name) +{ + cmdLinkedNetwork(); + return Sta::sta()->sdc()->findClock(name); +} + +bool +is_clock_src(const Pin *pin) +{ + return Sta::sta()->isClockSrc(pin); +} + +Clock * +default_arrival_clock() +{ + return Sta::sta()->sdc()->defaultArrivalClock(); +} + +ClockSeq +find_clocks_matching(const char *pattern, + bool regexp, + bool nocase) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); + return sdc->findClocksMatching(&matcher); +} + +void +update_generated_clks() +{ + cmdLinkedNetwork(); + Sta::sta()->updateGeneratedClks(); +} + +bool +is_clock(Pin *pin) +{ + Sta *sta = Sta::sta(); + return sta->isClock(pin); +} + +bool +is_ideal_clock(Pin *pin) +{ + Sta *sta = Sta::sta(); + return 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); +} + +bool +clk_thru_tristate_enabled() +{ + return Sta::sta()->clkThruTristateEnabled(); +} + +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(); + cmdLinkedNetwork(); + return sta->sdc()->allInputs(no_clocks); +} + +PortSeq +all_outputs_cmd() +{ + Sta *sta = Sta::sta(); + cmdLinkedNetwork(); + return sta->sdc()->allOutputs(); +} + +PortSeq +filter_ports(const char *property, + const char *op, + const char *pattern, + PortSeq *ports) +{ + PortSeq filtered_ports; + if (ports) { + Sta *sta = Sta::sta(); + bool exact_match = stringEq(op, "=="); + bool pattern_match = stringEq(op, "=~"); + bool not_match = stringEq(op, "!="); + for (const Port *port : *ports) { + PropertyValue value(getProperty(port, property, sta)); + const char *prop = value.stringValue(); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (not_match && !stringEq(prop, pattern)) + || (pattern_match && patternMatch(pattern, prop)))) + filtered_ports.push_back(port); + } + delete ports; + } + return filtered_ports; +} + +InstanceSeq +filter_insts(const char *property, + const char *op, + const char *pattern, + InstanceSeq *insts) +{ + InstanceSeq filtered_insts; + if (insts) { + Sta *sta = Sta::sta(); + cmdLinkedNetwork(); + bool exact_match = stringEq(op, "=="); + bool pattern_match = stringEq(op, "=~"); + bool not_match = stringEq(op, "!="); + for (const Instance *inst : *insts) { + PropertyValue value(getProperty(inst, property, sta)); + const char *prop = value.stringValue(); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (not_match && !stringEq(prop, pattern)) + || (pattern_match && patternMatch(pattern, prop)))) + filtered_insts.push_back(inst); + } + delete insts; + } + return filtered_insts; +} + +PinSeq +filter_pins(const char *property, + const char *op, + const char *pattern, + PinSeq *pins) +{ + PinSeq filtered_pins; + if (pins) { + Sta *sta = Sta::sta(); + bool exact_match = stringEq(op, "=="); + bool pattern_match = stringEq(op, "=~"); + bool not_match = stringEq(op, "!="); + for (const Pin *pin : *pins) { + PropertyValue value(getProperty(pin, property, sta)); + const char *prop = value.asString(sta->sdcNetwork()); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (not_match && !stringEq(prop, pattern)) + || (pattern_match && patternMatch(pattern, prop)))) + filtered_pins.push_back(pin); + } + delete pins; + } + return filtered_pins; +} + +EdgeSeq +filter_timing_arcs(const char *property, + const char *op, + const char *pattern, + EdgeSeq *edges) +{ + Sta *sta = Sta::sta(); + EdgeSeq filtered_edges; + bool exact_match = stringEq(op, "=="); + bool pattern_match = stringEq(op, "=~"); + bool not_match = stringEq(op, "!="); + for (Edge *edge : *edges) { + PropertyValue value(getProperty(edge, property, sta)); + const char *prop = value.stringValue(); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (not_match && !stringEq(prop, pattern)) + || (pattern_match && patternMatch(pattern, prop)))) + filtered_edges.push_back(edge); + } + delete edges; + return filtered_edges; +} + +//////////////////////////////////////////////////////////////// + +void +set_voltage_global(const MinMax *min_max, + float voltage) +{ + Sta::sta()->setVoltage(min_max, voltage); +} + +void +set_voltage_net(const Net *net, + const MinMax *min_max, + float voltage) +{ + Sta::sta()->setVoltage(net, min_max, voltage); +} + +//////////////////////////////////////////////////////////////// + +PinSet +group_path_pins(const char *group_path_name) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + if (sdc->isGroupPathName(group_path_name)) + return sta->findGroupPathPins(group_path_name); + else + return PinSet(sta->network()); +} + +//////////////////////////////////////////////////////////////// + +char +pin_case_logic_value(const Pin *pin) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + LogicValue value = LogicValue::unknown; + bool exists; + sdc->caseLogicValue(pin, value, exists); + return logicValueString(value); +} + +char +pin_logic_value(const Pin *pin) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + LogicValue value = LogicValue::unknown; + bool exists; + sdc->logicValue(pin, value, exists); + return logicValueString(value); +} + +//////////////////////////////////////////////////////////////// +// +// Variables +// +//////////////////////////////////////////////////////////////// + +bool +propagate_all_clocks() +{ + return Sta::sta()->propagateAllClocks(); +} + +void +set_propagate_all_clocks(bool prop) +{ + Sta::sta()->setPropagateAllClocks(prop); +} + +%} // inline + +//////////////////////////////////////////////////////////////// +// +// Object Methods +// +//////////////////////////////////////////////////////////////// + +%extend Clock { +float period() { return self->period(); } +FloatSeq *waveform() { return self->waveform(); } +float time(RiseFall *rf) { return self->edge(rf)->time(); } +bool is_generated() { return self->isGenerated(); } +bool waveform_valid() { return self->waveformValid(); } +bool is_virtual() { return self->isVirtual(); } +bool is_propagated() { return self->isPropagated(); } +const PinSet &sources() { return self->pins(); } + +float +slew(const RiseFall *rf, + const MinMax *min_max) +{ + return self->slew(rf, min_max); +} + +} + +%extend ClockEdge { +Clock *clock() { return self->clock(); } +RiseFall *transition() { return self->transition(); } +float time() { return self->time(); } +} diff --git a/tcl/Sdc.tcl b/sdc/Sdc.tcl similarity index 95% rename from tcl/Sdc.tcl rename to sdc/Sdc.tcl index ba5dd8e6..e840545d 100644 --- a/tcl/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -296,50 +296,21 @@ proc all_clocks { } { ################################################################ -define_cmd_args "all_inputs" {} +define_cmd_args "all_inputs" {[-no_clocks]} -proc all_inputs { } { - return [all_ports_for_direction "input"] +proc all_inputs { args } { + parse_key_args "all_inputs" args keys {} flags {-no_clocks} + set no_clks [info exists flags(-no_clocks)] + return [all_inputs_cmd $no_clks] } ################################################################ define_cmd_args "all_outputs" {} -proc all_outputs { } { - return [all_ports_for_direction "output"] -} - -proc all_ports_for_direction { direction } { - set top_instance [top_instance] - set top_cell [$top_instance cell] - set ports {} - set iter [$top_cell port_iterator] - while {[$iter has_next]} { - set port [$iter next] - set port_dir [port_direction $port] - if { $port_dir == $direction || $port_dir == "bidirect" } { - set ports [concat $ports [port_members $port]] - } - } - $iter finish - return $ports -} - -proc port_members { port } { - if [$port is_bus] { - # Expand bus ports. - set ports {} - set member_iter [$port member_iterator] - while {[$member_iter has_next]} { - set bit_port [$member_iter next] - lappend ports $bit_port - } - $member_iter finish - return $ports - } else { - return $port - } +proc all_outputs { args } { + check_argc_eq0 "all_outputs" $args + return [all_outputs_cmd] } ################################################################ @@ -1329,7 +1300,7 @@ proc set_clock_gating_check { args } { flags {-rise -fall -high -low} check_argc_eq0or1 "set_clock_gating_check" $args - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set active_value "" if {[info exists flags(-high)] && [info exists flags(-low)]} { @@ -1344,20 +1315,20 @@ proc set_clock_gating_check { args } { sta_error 397 "missing -setup or -hold argument." } if [info exists keys(-hold)] { - set_clock_gating_check1 $args $tr "min" $keys(-hold) $active_value + set_clock_gating_check1 $args $rf "min" $keys(-hold) $active_value } if [info exists keys(-setup)] { - set_clock_gating_check1 $args $tr "max" $keys(-setup) $active_value + set_clock_gating_check1 $args $rf "max" $keys(-setup) $active_value } } -proc set_clock_gating_check1 { args tr setup_hold margin active_value } { +proc set_clock_gating_check1 { args rf setup_hold margin active_value } { set margin [time_ui_sta $margin] if { [llength $args] == 0 } { if { $active_value != "" } { sta_error 398 "-high and -low only permitted for pins and instances." } - set_clock_gating_check_cmd $tr $setup_hold $margin + set_clock_gating_check_cmd $rf $setup_hold $margin } elseif { [llength $args] == 1 } { parse_clk_inst_port_pin_arg [lindex $args 0] clks insts pins @@ -1365,18 +1336,18 @@ proc set_clock_gating_check1 { args tr setup_hold margin active_value } { sta_error 399 "-high and -low only permitted for pins and instances." } foreach clk $clks { - set_clock_gating_check_clk_cmd $clk $tr $setup_hold $margin + set_clock_gating_check_clk_cmd $clk $rf $setup_hold $margin } if { $active_value == "" } { set active_value "X" } foreach pin $pins { - set_clock_gating_check_pin_cmd $pin $tr $setup_hold \ + set_clock_gating_check_pin_cmd $pin $rf $setup_hold \ $margin $active_value } foreach inst $insts { - set_clock_gating_check_instance_cmd $inst $tr $setup_hold \ + set_clock_gating_check_instance_cmd $inst $rf $setup_hold \ $margin $active_value } } @@ -1515,7 +1486,7 @@ proc set_clock_latency { args } { parse_clk_port_pin_arg $objects clks pins - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set min_max [parse_min_max_all_flags flags] set pin_clk "NULL" @@ -1531,14 +1502,14 @@ proc set_clock_latency { args } { set early_late [parse_early_late_all_flags flags] foreach clk $clks { - set_clock_insertion_cmd $clk "NULL" $tr $min_max $early_late $delay + set_clock_insertion_cmd $clk "NULL" $rf $min_max $early_late $delay } 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." } - set_clock_insertion_cmd $pin_clk $pin $tr $min_max $early_late $delay + set_clock_insertion_cmd $pin_clk $pin $rf $min_max $early_late $delay } } else { # Latency. @@ -1547,10 +1518,10 @@ proc set_clock_latency { args } { } foreach clk $clks { - set_clock_latency_cmd $clk "NULL" $tr $min_max $delay + set_clock_latency_cmd $clk "NULL" $rf $min_max $delay } foreach pin $pins { - set_clock_latency_cmd $pin_clk $pin $tr $min_max $delay + set_clock_latency_cmd $pin_clk $pin $rf $min_max $delay } } } @@ -1676,7 +1647,7 @@ proc set_clock_transition { args } { parse_key_args "set_clock_transition" args keys {} \ flags {-rise -fall -max -min} - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set min_max [parse_min_max_all_flags flags] check_argc_eq2 "set_clock_transition" $args @@ -1687,7 +1658,7 @@ proc set_clock_transition { args } { if { [$clk is_virtual] } { sta_warn 419 "transition time can not be specified for virtual clocks." } else { - set_clock_slew_cmd $clk $tr $min_max [time_ui_sta $slew] + set_clock_slew_cmd $clk $rf $min_max [time_ui_sta $slew] } } } @@ -2098,6 +2069,22 @@ proc parse_disable_inst_ports { inst port_name } { return $ports } +proc port_members { port } { + if [$port is_bus] { + # Expand bus ports. + set ports {} + set member_iter [$port member_iterator] + while {[$member_iter has_next]} { + set bit_port [$member_iter next] + lappend ports $bit_port + } + $member_iter finish + return $ports + } else { + return $port + } +} + proc set_disable_timing_cell { cell from to } { set from_ports [parse_disable_cell_ports $cell $from] set to_ports [parse_disable_cell_ports $cell $to] @@ -2114,7 +2101,7 @@ proc set_disable_timing_cell { cell from to } { } else { foreach from_port $from_ports { foreach to_port $to_ports { - disable_cell $cell $from_port $to_port + disable_cell $cell $from_port $to_port } } } @@ -2150,7 +2137,7 @@ proc unset_disable_timing { args } { proc unset_disable_cmd { cmd cmd_args } { parse_key_args $cmd cmd_args keys {-from -to} flags {} check_argc_eq1 $cmd $cmd_args - + set from "" if { [info exists keys(-from)] } { set from $keys(-from) @@ -2161,12 +2148,12 @@ proc unset_disable_cmd { cmd cmd_args } { } parse_libcell_libport_inst_port_pin_edge_timing_arc_set_arg $cmd_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 434 "-from/-to keywords ignored for lib_pin, port and pin arguments." } - + foreach libcell $libcells { unset_disable_timing_cell $libcell $from $to } @@ -2206,7 +2193,7 @@ proc unset_disable_timing_cell { cell from to } { } else { foreach from_port $from_ports { foreach to_port $to_ports { - unset_disable_cell $cell $from_port $to_port + unset_disable_cell $cell $from_port $to_port } } } @@ -2231,7 +2218,7 @@ proc unset_disable_timing_instance { inst from to } { } else { foreach from_port $from_ports { foreach to_port $to_ports { - unset_disable_instance $inst $from_port $to_port + unset_disable_instance $inst $from_port $to_port } } } @@ -2277,7 +2264,7 @@ proc set_false_path { args } { sta_warn 437 "-from, -through or -to required." } else { if [info exists flags(-reset_path)] { - reset_path_cmd $from $thrus $to $min_max + reset_path_cmd $from $thrus $to $min_max } set comment [parse_comment_key keys] @@ -2336,7 +2323,7 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } { parse_key_args $cmd cmd_args \ keys {-clock -reference_pin} \ flags {-rise -fall -max -min -clock_fall -add_delay \ - -source_latency_included -network_latency_included} + -source_latency_included -network_latency_included} check_argc_eq2 $cmd $cmd_args set delay_arg [lindex $cmd_args 0] @@ -2366,7 +2353,7 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } { set clk_rf "rise" } - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set min_max [parse_min_max_all_flags flags] set add [info exists flags(-add_delay)] set source_latency_included [info exists flags(-source_latency_included)] @@ -2374,14 +2361,14 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } { foreach pin $pins { if { [$pin is_top_level_port] \ - && [lsearch $port_dirs [pin_direction $pin]] == -1 } { + && [lsearch $port_dirs [pin_direction $pin]] == -1 } { sta_warn 440 "$cmd not allowed on [pin_direction $pin] port '[get_full_name $pin]'." } elseif { $clk != "NULL" && [lsearch [$clk sources] $pin] != -1 } { sta_warn 441 "$cmd relative to a clock defined on the same port/pin not allowed." } else { - $sta_cmd $pin $tr $clk $clk_rf $ref_pin\ - $source_latency_included $network_latency_included \ - $min_max $add $delay + $sta_cmd $pin $rf $clk $clk_rf $ref_pin\ + $source_latency_included $network_latency_included \ + $min_max $add $delay } } } @@ -2603,7 +2590,7 @@ proc unset_path_exceptions_cmd { cmd cmd_args } { parse_key_args $cmd cmd_args \ keys {-from -rise_from -fall_from -to -rise_to -fall_to} \ flags {-setup -hold -rise -fall} 0 - + set min_max "min_max" if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { set min_max "max" @@ -2611,7 +2598,7 @@ proc unset_path_exceptions_cmd { cmd cmd_args } { if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { set min_max "min" } - + set arg_error 0 set from [parse_from_arg keys arg_error] set thrus [parse_thrus_arg cmd_args arg_error] @@ -2621,7 +2608,7 @@ proc unset_path_exceptions_cmd { cmd cmd_args } { sta_error 447 "$cmd command failed." return 0 } - + check_for_key_args $cmd cmd_args if { $cmd_args != {} } { delete_from_thrus_to $from $thrus $to @@ -2631,7 +2618,7 @@ proc unset_path_exceptions_cmd { cmd cmd_args } { delete_from_thrus_to $from $thrus $to sta_error 449 "-from, -through or -to required." } - + reset_path_cmd $from $thrus $to $min_max delete_from_thrus_to $from $thrus $to } @@ -2680,11 +2667,11 @@ proc unset_port_delay { cmd swig_cmd cmd_args } { set clk_rf "rise" } - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set min_max [parse_min_max_all_flags flags] - + foreach pin $pins { - $swig_cmd $pin $tr $clk $clk_rf $min_max + $swig_cmd $pin $rf $clk $clk_rf $min_max } } @@ -2731,13 +2718,13 @@ define_cmd_args "set_case_analysis" \ proc set_case_analysis { value pins } { if { !($value == "0" \ - || $value == "1" \ - || $value == "zero" \ - || $value == "one" \ - || $value == "rise" \ - || $value == "rising" \ - || $value == "fall" \ - || $value == "falling") } { + || $value == "1" \ + || $value == "zero" \ + || $value == "one" \ + || $value == "rise" \ + || $value == "rising" \ + || $value == "fall" \ + || $value == "falling") } { sta_error 451 "value must be 0, zero, 1, one, rise, rising, fall, or falling." } set pins1 [get_port_pins_error "pins" $pins] @@ -2760,11 +2747,11 @@ proc unset_case_analysis { pins } { ################################################################ define_cmd_args "set_drive" {[-rise] [-fall] [-min] [-max] \ - resistance ports} + resistance ports} proc set_drive { args } { parse_key_args "set_drive" args keys {} flags {-rise -fall -min -max} - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set min_max [parse_min_max_all_check_flags flags] check_argc_eq2 "set_drive" $args @@ -2774,7 +2761,7 @@ proc set_drive { args } { set res [resistance_ui_sta $res] set ports [get_ports_error "ports" [lindex $args 1]] foreach port $ports { - set_drive_resistance_cmd $port $tr $min_max $res + set_drive_resistance_cmd $port $rf $min_max $res } } @@ -2790,10 +2777,10 @@ define_cmd_args "set_driving_cell" \ proc set_driving_cell { args } { parse_key_args "set_driving_cell" args \ keys {-lib_cell -cell -library -pin -from_pin -multiply_by \ - -input_transition_rise -input_transition_fall} \ + -input_transition_rise -input_transition_fall} \ flags {-rise -fall -min -max -dont_scale -no_design_rule} - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set min_max [parse_min_max_all_flags flags] # -cell is an undocumented non-sdc alias for -lib_cell. @@ -2807,13 +2794,13 @@ proc set_driving_cell { args } { set library [get_liberty_error "library" $keys(-library)] set cell [$library find_liberty_cell $cell_name] if { $cell == "NULL" } { - sta_error 452 "cell '$lib_name:$cell_name' not found." + sta_error 452 "cell '$lib_name:$cell_name' not found." } } else { set library "NULL" set cell [find_liberty_cell $cell_name] if { $cell == "NULL" } { - sta_error 453 "'$cell_name' not found." + sta_error 453 "'$cell_name' not found." } } } else { @@ -2834,14 +2821,14 @@ proc set_driving_cell { args } { set port [$port_iter next] set dir [liberty_port_direction $port] if { [port_direction_any_output $dir] } { - incr output_count - if { $output_count > 1 } { - $port_iter finish - sta_error 456 "-pin argument required for cells with multiple outputs." - } - set to_port $port - # No break. Keep looking for output ports to make sure there - # is only one. + incr output_count + if { $output_count > 1 } { + $port_iter finish + sta_error 456 "-pin argument required for cells with multiple outputs." + } + set to_port $port + # No break. Keep looking for output ports to make sure there + # is only one. } } $port_iter finish @@ -2884,14 +2871,14 @@ proc set_driving_cell { args } { set ports [get_ports_error "ports" [lindex $args 0]] foreach port $ports { set_drive_cell_cmd $library $cell $port $from_port \ - $from_slew_rise $from_slew_fall $to_port $tr $min_max + $from_slew_rise $from_slew_fall $to_port $rf $min_max } } proc port_direction_any_output { dir } { return [expr { $dir == "output" \ - || $dir == "bidirect" \ - || $dir == "tristate" } ] + || $dir == "bidirect" \ + || $dir == "tristate" } ] } ################################################################ @@ -2911,7 +2898,7 @@ proc set_input_transition { args } { parse_key_args "set_input_transition" args keys {-clock} \ flags {-rise -fall -max -min -clock_fall} - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set min_max [parse_min_max_all_flags flags] @@ -2930,7 +2917,7 @@ proc set_input_transition { args } { } foreach port $ports { - set_input_slew_cmd $port $tr $min_max $slew + set_input_slew_cmd $port $rf $min_max $slew } } @@ -2966,11 +2953,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 $corner $min_max $cap } } elseif { $wire_load } { foreach port $ports { - set_port_ext_wire_cap $port $subtract_pin_load $rf $corner $min_max $cap + set_port_ext_wire_cap $port $subtract_pin_load $rf $corner $min_max $cap } } } @@ -3095,11 +3082,11 @@ proc set_max_transition { args } { set objects [lindex $args 1] parse_clk_cell_port_args $objects clks cells ports - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set path_types {} if { ![info exists flags(-clock_path)] \ - && ![info exists flags(-data_path)] } { + && ![info exists flags(-data_path)] } { # Derate clk and data if neither -clock_path or -data_path. set path_types {"clk" "data"} } @@ -3111,17 +3098,17 @@ proc set_max_transition { args } { } if { ($ports != {} || $cells != {}) \ - && ([info exists flags(-clock_path)] \ - || [info exists flags(-data_path)] - || [info exists flags(-rise)] - || [info exists flags(-fall)]) } { + && ([info exists flags(-clock_path)] \ + || [info exists flags(-data_path)] + || [info exists flags(-rise)] + || [info exists flags(-fall)]) } { sta_warn 468 "-data_path, -clock_path, -rise, -fall ignored for ports and designs." } # -clock_path/-data_path and transition only apply to clock objects. foreach path_type $path_types { foreach clk $clks { - set_slew_limit_clk $clk $tr $path_type "max" $slew + set_slew_limit_clk $clk $rf $path_type "max" $slew } } foreach cell $cells { @@ -3180,7 +3167,7 @@ define_cmd_args "set_timing_derate" \ proc set_timing_derate { args } { parse_key_args "set_timing_derate" args keys {} \ flags {-rise -fall -early -late -clock -data \ - -net_delay -cell_delay -cell_check} + -net_delay -cell_delay -cell_check} check_argc_eq1or2 "set_timing_derate" $args set derate [lindex $args 0] @@ -3189,12 +3176,12 @@ proc set_timing_derate { args } { sta_warn 469 "derating factor greater than 2.0." } - set tr [parse_rise_fall_flags flags] + set rf [parse_rise_fall_flags flags] set early_late [parse_early_late_flags flags] set path_types {} if { ![info exists flags(-clock)] \ - && ![info exists flags(-data)] } { + && ![info exists flags(-data)] } { # Derate clk and data if neither -clock or -data. lappend path_types "clk" lappend path_types "data" @@ -3222,42 +3209,42 @@ proc set_timing_derate { args } { parse_libcell_inst_net_arg $objects libcells insts nets if { $nets != {} } { if { [info exists flags(-cell_delay)] \ - || [info exists flags(-cell_check)] } { - sta_warn 470 "-cell_delay and -cell_check flags ignored for net objects." + || [info exists flags(-cell_check)] } { + sta_warn 470 "-cell_delay and -cell_check flags ignored for net objects." } foreach net $nets { - foreach path_type $path_types { - set_timing_derate_net_cmd $net $path_type $tr $early_late $derate - } + foreach path_type $path_types { + set_timing_derate_net_cmd $net $path_type $rf $early_late $derate + } } } if { ![info exists flags(-cell_delay)] \ - && ![info exists flags(-cell_check)] } { + && ![info exists flags(-cell_check)] } { # Cell checks are not derated if no flags are specified. set derate_types {cell_delay} } foreach derate_type $derate_types { foreach path_type $path_types { - foreach inst $insts { - set_timing_derate_inst_cmd $inst $derate_type $path_type \ - $tr $early_late $derate - } - foreach libcell $libcells { - set_timing_derate_cell_cmd $libcell $derate_type $path_type \ - $tr $early_late $derate - } + foreach inst $insts { + set_timing_derate_inst_cmd $inst $derate_type $path_type \ + $rf $early_late $derate + } + foreach libcell $libcells { + set_timing_derate_cell_cmd $libcell $derate_type $path_type \ + $rf $early_late $derate + } } } } else { if { ![info exists flags(-net_delay)] \ - && ![info exists flags(-cell_delay)] \ - && ![info exists flags(-cell_check)] } { + && ![info exists flags(-cell_delay)] \ + && ![info exists flags(-cell_check)] } { # Cell checks are not derated if no flags are specified. set derate_types {net_delay cell_delay} } foreach derate_type $derate_types { foreach path_type $path_types { - set_timing_derate_cmd $derate_type $path_type $tr $early_late $derate + set_timing_derate_cmd $derate_type $path_type $rf $early_late $derate } } } @@ -3279,13 +3266,13 @@ proc parse_from_arg { keys_var arg_error_var } { if [info exists keys(-from)] { set key "-from" - set tr "rise_fall" + set rf "rise_fall" } elseif [info exists keys(-rise_from)] { set key "-rise_from" - set tr "rise" + set rf "rise" } elseif [info exists keys(-fall_from)] { set key "-fall_from" - set tr "fall" + set rf "fall" } else { return "NULL" } @@ -3296,7 +3283,7 @@ proc parse_from_arg { keys_var arg_error_var } { sta_warn 471 "no valid objects specified for $key." return "NULL" } - return [make_exception_from $from_pins $from_clks $from_insts $tr] + return [make_exception_from $from_pins $from_clks $from_insts $rf] } # "arg_error" is set to notify the caller to cleanup and post error. @@ -3307,29 +3294,29 @@ proc parse_thrus_arg { args_var arg_error_var } { set args_rtn {} while { $args != {} } { set arg [lindex $args 0] - set tr "" + set rf "" if { $arg == "-through" } { - set tr "rise_fall" + set rf "rise_fall" set key "-through" } elseif { $arg == "-rise_through" } { - set tr "rise" + set rf "rise" set key "-rise_through" } elseif { $arg == "-fall_through" } { - set tr "fall" + set rf "fall" set key "-fall_through" } - if { $tr != "" } { + if { $rf != "" } { if { [llength $args] > 1 } { - set args [lrange $args 1 end] - set arg [lindex $args 0] - parse_inst_port_pin_net_arg $arg insts pins nets - if {$pins == {} && $insts == {} && $nets == {}} { - upvar 1 $arg_error_var arg_error - set arg_error 1 - sta_warn 472 "no valid objects specified for $key" - } else { - lappend thrus [make_exception_thru $pins $nets $insts $tr] - } + set args [lrange $args 1 end] + set arg [lindex $args 0] + parse_inst_port_pin_net_arg $arg insts pins nets + if {$pins == {} && $insts == {} && $nets == {}} { + upvar 1 $arg_error_var arg_error + set arg_error 1 + sta_warn 472 "no valid objects specified for $key" + } else { + lappend thrus [make_exception_thru $pins $nets $insts $rf] + } } } else { lappend args_rtn $arg @@ -3454,9 +3441,9 @@ proc parse_op_cond { op_cond_name lib_key min_max key_var } { set lib [$lib_iter next] set op_cond [$lib find_operating_conditions $op_cond_name] if { $op_cond != "NULL" } { - set_operating_conditions_cmd $op_cond $min_max - set found 1 - break + set_operating_conditions_cmd $op_cond $min_max + set found 1 + break } } $lib_iter finish @@ -3471,8 +3458,8 @@ proc parse_op_cond_analysis_type { key_var } { if [info exists keys(-analysis_type)] { set analysis_type $keys(-analysis_type) if { $analysis_type == "single" \ - || $analysis_type == "bc_wc" \ - || $analysis_type == "on_chip_variation" } { + || $analysis_type == "bc_wc" \ + || $analysis_type == "on_chip_variation" } { set_analysis_type_cmd $analysis_type } else { sta_error 476 "-analysis_type must be single, bc_wc or on_chip_variation." @@ -3496,8 +3483,8 @@ define_cmd_args "set_wire_load_mode" "top|enclosed|segmented" proc set_wire_load_mode { mode } { if { $mode == "top" \ - || $mode == "enclosed" \ - || $mode == "segmented" } { + || $mode == "enclosed" \ + || $mode == "segmented" } { set_wire_load_mode_cmd $mode } else { sta_error 478 "mode must be top, enclosed or segmented." @@ -3531,7 +3518,7 @@ proc set_wire_load_model { args } { set lib [$lib_iter next] set wireload [$lib find_wireload $model_name] if {$wireload != "NULL"} { - break; + break; } } $lib_iter finish @@ -3570,7 +3557,7 @@ proc set_wire_load_selection_group { args } { set lib [$lib_iter next] set selection [$lib find_wireload_selection $selection_name] if {$selection != "NULL"} { - break; + break; } } $lib_iter finish @@ -3595,7 +3582,7 @@ proc set_voltage { args } { check_argc_eq1 "set_voltage" $args set max_case_voltage [lindex $args 0] check_float "max_case_voltage" $max_case_voltage - + set nets {} if { [info exists keys(-object_list)] } { set nets [get_nets_arg "-object_list" $keys(-object_list)] @@ -3686,7 +3673,7 @@ proc set_pvt { args } { check_argc_eq1 "set_pvt" $args set insts [get_instances_error "instances" [lindex $args 0]] - if { $min_max == "all" } { + if { $min_max == "min_max" } { set_pvt_min_max $insts "min" keys set_pvt_min_max $insts "max" keys } else { diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index 5fd36b28..5808f5c3 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -382,7 +382,7 @@ WriteSdc::writeClocks() const { // Write clocks in the order they were defined because generated // clocks depend on master clocks having been previously defined. - for (auto clk : sdc_->clocks_) { + for (const auto clk : sdc_->clocks_) { if (clk->isGenerated()) writeGeneratedClock(clk); else @@ -852,8 +852,8 @@ void WriteSdc::writeClockSenses() const { Vector pin_clks; - for (auto iter : sdc_->clk_sense_map_) - pin_clks.push_back(iter.first); + for (const auto& [pin_clk, sense] : sdc_->clk_sense_map_) + pin_clks.push_back(pin_clk); // Sort by pin/clk pair so regressions results are stable. sort(pin_clks, PinClockPairNameLess(sdc_network_)); @@ -937,10 +937,8 @@ ClockGroupLess::operator()(const ClockGroup *clk_group1, void WriteSdc::writeClockGroups() const { - for (auto &name_groups : sdc_->clk_groups_name_map_) { - ClockGroups *clk_groups = name_groups.second; + for (const auto [name, clk_groups] : sdc_->clk_groups_name_map_) writeClockGroups(clk_groups); - } } void @@ -1408,8 +1406,7 @@ void WriteSdc::writeDataChecks() const { Vector checks; - for (auto pin_checks : sdc_->data_checks_to_map_) { - DataCheckSet *checks1 = pin_checks.second; + for (const auto [pin, checks1] : sdc_->data_checks_to_map_) { for (DataCheck *check : *checks1) checks.push_back(check); } @@ -1512,9 +1509,7 @@ void WriteSdc::writeNetLoads() const { int corner_index = 0; // missing corner arg - for (auto net_cap : sdc_->net_wire_cap_maps_[corner_index]) { - const Net *net = net_cap.first; - MinMaxFloatValues &caps = net_cap.second; + for (const auto [net, caps] : sdc_->net_wire_cap_maps_[corner_index]) { float min_cap, max_cap; bool min_exists, max_exists; caps.value(MinMax::min(), min_cap, min_exists); @@ -1725,10 +1720,8 @@ void WriteSdc::writeNetResistances() const { NetSeq nets; - for (auto net_res : sdc_->netResistances()) { - const Net *net = net_res.first; + for (const auto [net, res] : sdc_->netResistances()) nets.push_back(net); - } sort(nets, NetPathNameLess(sdc_network_)); for (const Net *net : nets) { float min_res, max_res; @@ -1843,10 +1836,8 @@ void WriteSdc::sortedLogicValuePins(LogicValueMap &value_map, PinSeq &pins) const { - for (auto pin_value : value_map) { - const Pin *pin = pin_value.first; + for (const auto [pin, value] : value_map) pins.push_back(pin); - } // Sort pins. sort(pins, PinPathNameLess(sdc_network_)); } @@ -1860,9 +1851,7 @@ WriteSdc::writeDeratings() const if (factors) writeDerating(factors); - for (auto net_derating : sdc_->net_derating_factors_) { - const Net *net = net_derating.first; - DeratingFactorsNet *factors = net_derating.second; + for (const auto [net, factors] : sdc_->net_derating_factors_) { WriteGetNet write_net(net, this); for (auto early_late : EarlyLate::range()) { writeDerating(factors, TimingDerateType::net_delay, early_late, @@ -1870,16 +1859,12 @@ WriteSdc::writeDeratings() const } } - for (auto inst_derating : sdc_->inst_derating_factors_) { - const Instance *inst = inst_derating.first; - DeratingFactorsCell *factors = inst_derating.second; + for (const auto [inst, factors] : sdc_->inst_derating_factors_) { WriteGetInstance write_inst(inst, this); writeDerating(factors, &write_inst); } - for (auto cell_derating : sdc_->cell_derating_factors_) { - const LibertyCell *cell = cell_derating.first; - DeratingFactorsCell *factors = cell_derating.second; + for (const auto [cell, factors] : sdc_->cell_derating_factors_) { WriteGetLibCell write_cell(cell, this); writeDerating(factors, &write_cell); } @@ -2029,12 +2014,10 @@ WriteSdc::writeVoltages() const gzprintf(stream_, "set_voltage %.3f\n", voltage_max); } - for (auto net_volt : sdc_->net_voltage_map_) { - const Net *net = net_volt.first; - MinMaxFloatValues &values = net_volt.second; - values.value(MinMax::max(), voltage_max, exists_max); + for (const auto& [net, volts] : sdc_->net_voltage_map_) { + volts.value(MinMax::max(), voltage_max, exists_max); if (exists_max) { - values.value(MinMax::min(), voltage_min, exists_min); + volts.value(MinMax::min(), voltage_min, exists_min); if (exists_min) gzprintf(stream_, "set_voltage -object_list %s -min %.3f %.3f\n", sdc_network_->pathName(net), @@ -2065,23 +2048,17 @@ WriteSdc::writeDesignRules() const void WriteSdc::writeMinPulseWidths() const { - for (auto pin_widths : sdc_->pin_min_pulse_width_map_) { - const Pin *pin = pin_widths.first; - RiseFallValues *min_widths = pin_widths.second; + for (const auto [pin, min_widths] : sdc_->pin_min_pulse_width_map_) { WriteGetPin write_obj(pin, false, this); writeMinPulseWidths(min_widths, write_obj); } - for (auto inst_widths : sdc_->inst_min_pulse_width_map_) { - const Instance *inst = inst_widths.first; - RiseFallValues *min_widths = inst_widths.second; + for (const auto [inst, min_widths] : sdc_->inst_min_pulse_width_map_) { WriteGetInstance write_obj(inst, this); writeMinPulseWidths(min_widths, write_obj); } - for (auto clk_widths : sdc_->clk_min_pulse_width_map_) { - const Clock *clk = clk_widths.first; - RiseFallValues *min_widths = clk_widths.second; + for (const auto [clk, min_widths] : sdc_->clk_min_pulse_width_map_) { WriteGetClock write_obj(clk, this); writeMinPulseWidths(min_widths, write_obj); } @@ -2123,9 +2100,7 @@ WriteSdc::writeMinPulseWidth(const char *hi_low, void WriteSdc::writeLatchBorowLimits() const { - for (auto pin_borrow : sdc_->pin_latch_borrow_limit_map_) { - const Pin *pin = pin_borrow.first; - float limit = pin_borrow.second; + for (const auto [pin, limit] : sdc_->pin_latch_borrow_limit_map_) { gzprintf(stream_, "set_max_time_borrow "); writeTime(limit); gzprintf(stream_, " "); @@ -2133,9 +2108,7 @@ WriteSdc::writeLatchBorowLimits() const gzprintf(stream_, "\n"); } - for (auto inst_borrow : sdc_->inst_latch_borrow_limit_map_) { - const Instance *inst = inst_borrow.first; - float limit = inst_borrow.second; + for (const auto [inst, limit] : sdc_->inst_latch_borrow_limit_map_) { gzprintf(stream_, "set_max_time_borrow "); writeTime(limit); gzprintf(stream_, " "); @@ -2143,9 +2116,7 @@ WriteSdc::writeLatchBorowLimits() const gzprintf(stream_, "\n"); } - for (auto clk_borrow : sdc_->clk_latch_borrow_limit_map_) { - const Clock *clk = clk_borrow.first; - float limit = clk_borrow.second; + for (const auto [clk, limit] : sdc_->clk_latch_borrow_limit_map_) { gzprintf(stream_, "set_max_time_borrow "); writeTime(limit); gzprintf(stream_, " "); @@ -2266,12 +2237,10 @@ WriteSdc::writeCapLimits(const MinMax *min_max, gzprintf(stream_, " [current_design]\n"); } - for (auto port_limit : sdc_->port_cap_limit_map_) { - const Port *port = port_limit.first; - MinMaxFloatValues values = port_limit.second; + for (const auto [port, limits] : sdc_->port_cap_limit_map_) { float cap; bool exists; - values.value(min_max, cap, exists); + limits.value(min_max, cap, exists); if (exists) { gzprintf(stream_, "%s ", cmd); writeCapacitance(cap); @@ -2281,12 +2250,10 @@ WriteSdc::writeCapLimits(const MinMax *min_max, } } - for (auto pin_limit : sdc_->pin_cap_limit_map_) { - const Pin *pin = pin_limit.first; - MinMaxFloatValues values = pin_limit.second; + for (const auto [pin, limits] : sdc_->pin_cap_limit_map_) { float cap; bool exists; - values.value(min_max, cap, exists); + limits.value(min_max, cap, exists); if (exists) { gzprintf(stream_, "%s ", cmd); writeCapacitance(cap); diff --git a/sdf/Sdf.i b/sdf/Sdf.i index d2fa8905..8a0971be 100644 --- a/sdf/Sdf.i +++ b/sdf/Sdf.i @@ -1,7 +1,3 @@ -%module sdf - -%{ - // OpenSTA, Static Timing Analyzer // Copyright (c) 2024, Parallax Software, Inc. // @@ -18,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +%module sdf + +%{ #include "sdf/SdfReader.hh" #include "sdf/ReportAnnotation.hh" #include "sdf/SdfWriter.hh" diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 195f51be..05515d27 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -422,16 +422,20 @@ SdfReader::findPort(const Cell *cell, } void -SdfReader::timingCheck(TimingRole *role, SdfPortSpec *data_edge, - SdfPortSpec *clk_edge, SdfTriple *triple) +SdfReader::timingCheck(TimingRole *role, + SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *triple) { - const char *data_port_name = data_edge->port(); - const char *clk_port_name = clk_edge->port(); - Cell *cell = network_->cell(instance_); - Port *data_port = findPort(cell, data_port_name); - Port *clk_port = findPort(cell, clk_port_name); - if (data_port && clk_port) - timingCheck1(role, data_port, data_edge, clk_port, clk_edge, triple); + if (instance_) { + const char *data_port_name = data_edge->port(); + const char *clk_port_name = clk_edge->port(); + Cell *cell = network_->cell(instance_); + Port *data_port = findPort(cell, data_port_name); + Port *clk_port = findPort(cell, clk_port_name); + if (data_port && clk_port) + timingCheck1(role, data_port, data_edge, clk_port, clk_edge, triple); + } deletePortSpec(data_edge); deletePortSpec(clk_edge); deleteTriple(triple); diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 22d2e24e..5e0fbe0b 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -45,7 +45,7 @@ public: SdfWriter(StaState *sta); ~SdfWriter(); void write(const char *filename, - Corner *corner, + const Corner *corner, char sdf_divider, bool include_typ, int digits, @@ -122,7 +122,7 @@ private: void writeSdf(const char *filename, - Corner *corner, + const Corner *corner, char sdf_divider, bool include_typ, int digits, @@ -151,7 +151,7 @@ SdfWriter::~SdfWriter() void SdfWriter::write(const char *filename, - Corner *corner, + const Corner *corner, char sdf_divider, bool include_typ, int digits, diff --git a/sdf/SdfWriter.hh b/sdf/SdfWriter.hh index f3d23c08..acd2fff3 100644 --- a/sdf/SdfWriter.hh +++ b/sdf/SdfWriter.hh @@ -23,7 +23,7 @@ class Corner; void writeSdf(const char *filename, - Corner *corner, + const Corner *corner, char divider, bool include_typ, int digits, diff --git a/search/Bdd.cc b/search/Bdd.cc index 88eda5a6..43e55adc 100644 --- a/search/Bdd.cc +++ b/search/Bdd.cc @@ -16,30 +16,11 @@ #include "Bdd.hh" +#include "cudd.h" #include "StaConfig.hh" #include "Report.hh" #include "FuncExpr.hh" -#if CUDD -#include "cudd.h" -#else -#include -#define CUDD_UNIQUE_SLOTS 0 -#define CUDD_CACHE_SLOTS 0 -DdManager *Cudd_Init(int, int, int, int, int) { return nullptr; } -void Cudd_Quit(void *) {} -DdNode *Cudd_Not(void *) { return nullptr; } -DdNode *Cudd_bddOr(void *, void *, void *) { return nullptr; } -DdNode *Cudd_bddAnd(void *, void *, void *) { return nullptr; } -DdNode *Cudd_bddXor(void *, void *, void *) { return nullptr; } -DdNode *Cudd_ReadOne(void *) { return nullptr; } -DdNode *Cudd_ReadLogicZero(void *) { return nullptr; } -DdNode *Cudd_bddNewVar(void *) { return nullptr; } -int Cudd_NodeReadIndex(void *) { return 0;} -void Cudd_Ref(void *) {} -void Cudd_RecursiveDeref(void *, void *) {} -#endif - namespace sta { Bdd::Bdd(const StaState *sta) : @@ -168,10 +149,8 @@ Bdd::varIndexPort(int var_index) void Bdd::clearVarMap() { - for (auto port_node : bdd_port_var_map_) { - DdNode *var_node = port_node.second; + for (const auto [port, var_node] : bdd_port_var_map_) Cudd_RecursiveDeref(cudd_mgr_, var_node); - } bdd_port_var_map_.clear(); bdd_var_idx_port_map_.clear(); } diff --git a/search/Bfs.cc b/search/Bfs.cc index 95f0c0e7..f5d8a429 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -70,7 +70,7 @@ BfsIterator::clear() Level level = first_level_; while (levelLessOrEqual(level, last_level_)) { VertexSeq &level_vertices = queue_[level]; - for (auto vertex : level_vertices) { + for (Vertex *vertex : level_vertices) { if (vertex) vertex->setBfsInQueue(bfs_index_, false); } @@ -88,7 +88,7 @@ BfsIterator::reportEntries(const Network *network) VertexSeq &level_vertices = queue_[level]; if (!level_vertices.empty()) { report_->reportLine("Level %d", level); - for (auto vertex : level_vertices) { + for (Vertex *vertex : level_vertices) { if (vertex) report_->reportLine(" %s", vertex->name(network)); } @@ -101,7 +101,7 @@ void BfsIterator::deleteEntries(Level level) { VertexSeq &level_vertices = queue_[level]; - for (auto vertex : level_vertices) { + for (Vertex *vertex : level_vertices) { if (vertex) vertex->setBfsInQueue(bfs_index_, false); } @@ -281,7 +281,7 @@ BfsIterator::checkInQueue(Vertex *vertex) { Level level = vertex->level(); if (static_cast(queue_.size()) > level) { - for (auto v : queue_[level]) { + for (Vertex *v : queue_[level]) { if (v == vertex) { if (vertex->bfsInQueue(bfs_index_)) return; @@ -308,7 +308,7 @@ BfsIterator::remove(Vertex *vertex) Level level = vertex->level(); if (vertex->bfsInQueue(bfs_index_) && static_cast(queue_.size()) > level) { - for (auto &v : queue_[level]) { + for (Vertex *v : queue_[level]) { if (v == vertex) { v = nullptr; vertex->setBfsInQueue(bfs_index_, false); diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc index 6c1e35a4..f1835b39 100644 --- a/search/CheckMinPulseWidths.cc +++ b/search/CheckMinPulseWidths.cc @@ -411,7 +411,7 @@ MinPulseWidthCheck::width(const StaState *sta) const { return closeArrival(sta) + closeOffset(sta) - open_path_.arrival(sta) - + commonClkPessimism(sta); + + checkCrpr(sta); } float @@ -458,7 +458,7 @@ minPulseWidth(const Path *path, } Crpr -MinPulseWidthCheck::commonClkPessimism(const StaState *sta) const +MinPulseWidthCheck::checkCrpr(const StaState *sta) const { CheckCrpr *check_crpr = sta->search()->checkCrpr(); PathVertex close; diff --git a/search/CheckMinPulseWidths.hh b/search/CheckMinPulseWidths.hh index c2433b5b..d493929f 100644 --- a/search/CheckMinPulseWidths.hh +++ b/search/CheckMinPulseWidths.hh @@ -80,7 +80,7 @@ public: float closeOffset(const StaState *sta) const; const ClockEdge *openClkEdge(const StaState *sta) const; const ClockEdge *closeClkEdge(const StaState *sta) const; - Crpr commonClkPessimism(const StaState *sta) const; + Crpr checkCrpr(const StaState *sta) const; protected: // Open path of the pulse. diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 099bca62..bc6c45f2 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -281,8 +281,7 @@ ClkSkews::findWorstClkSkew(const Corner *corner, clks.push_back(clk); ClkSkewMap skews = findClkSkew(clks, corner, setup_hold, include_internal_latency); float worst_skew = 0.0; - for (auto clk_skew_itr : skews) { - ClkSkew &clk_skew = clk_skew_itr.second; + for (const auto& [clk, clk_skew] : skews) { float skew = clk_skew.skew(); if (abs(skew) > abs(worst_skew)) worst_skew = skew; @@ -318,9 +317,7 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, // Reduce skews from each register source. for (size_t i = 0; i < partial_skews.size(); i++) { - for (auto clk_skew_itr : partial_skews[i]) { - const Clock *clk = clk_skew_itr.first; - auto partial_skew = clk_skew_itr.second; + for (auto& [clk, partial_skew] : partial_skews[i]) { auto ins = skews.insert(std::make_pair(clk, partial_skew)); if (!ins.second) { ClkSkew &final_skew = ins.first->second; diff --git a/search/Genclks.cc b/search/Genclks.cc index f93a5b1d..7d264770 100644 --- a/search/Genclks.cc +++ b/search/Genclks.cc @@ -676,10 +676,10 @@ Genclks::seedSrcPins(Clock *gclk, for (auto path_ap : corners_->pathAnalysisPts()) { const MinMax *min_max = path_ap->pathMinMax(); const EarlyLate *early_late = min_max; - for (auto tr : RiseFall::range()) { - Tag *tag = makeTag(gclk, master_clk, master_pin, tr, src_filter, + for (auto rf : RiseFall::range()) { + Tag *tag = makeTag(gclk, master_clk, master_pin, rf, src_filter, path_ap); - Arrival insert = search_->clockInsertion(master_clk, master_pin, tr, + Arrival insert = search_->clockInsertion(master_clk, master_pin, rf, min_max, early_late, path_ap); tag_bldr.setArrival(tag, insert, nullptr); } @@ -703,7 +703,7 @@ Genclks::makeTag(const Clock *gclk, // from the get go. if (master_pin == gclk->srcPin()) state = state->nextState(); - ExceptionStateSet *states = new ExceptionStateSet(network_); + ExceptionStateSet *states = new ExceptionStateSet(); states->insert(state); ClkInfo *clk_info = search_->findClkInfo(master_clk->edge(master_rf), master_pin, true, nullptr, true, diff --git a/search/Latches.cc b/search/Latches.cc index bf1a8260..3ec2dfe8 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -269,7 +269,7 @@ Latches::latchEnablePath(Path *q_path, const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); const Instance *latch = network_->instance(q_path->pin(this)); Vertex *en_vertex; - RiseFall *en_rf; + const RiseFall *en_rf; LatchEnableState state; latchDtoQEnable(d_q_edge, latch, en_vertex, en_rf, state); if (state == LatchEnableState::enabled) { @@ -303,7 +303,7 @@ Latches::latchOutArrival(Path *data_path, Vertex *data_vertex = d_q_edge->from(graph_); const Instance *inst = network_->instance(data_vertex->pin()); Vertex *enable_vertex; - RiseFall *enable_rf; + const RiseFall *enable_rf; LatchEnableState state; latchDtoQEnable(d_q_edge, inst, enable_vertex, enable_rf, state); // Latch enable may be missing if library is malformed. @@ -466,16 +466,16 @@ Latches::latchDtoQEnable(Edge *d_q_edge, const Instance *inst, // Return values. Vertex *&enable_vertex, - RiseFall *&enable_rf, + const RiseFall *&enable_rf, LatchEnableState &state) const { enable_vertex = nullptr; state = LatchEnableState::open; LibertyCell *cell = network_->libertyCell(inst); if (cell) { - TimingArcSet *d_q_set = d_q_edge->timingArcSet(); - LibertyPort *enable_port; - FuncExpr *enable_func; + const TimingArcSet *d_q_set = d_q_edge->timingArcSet(); + const LibertyPort *enable_port; + const FuncExpr *enable_func; cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf); if (enable_port) { Pin *enable_pin = network_->findPin(inst, enable_port); @@ -515,7 +515,7 @@ Latches::latchDtoQState(Edge *edge) const const Pin *from_pin = from_vertex->pin(); const Instance *inst = network_->instance(from_pin); Vertex *enable_vertex; - RiseFall *enable_rf; + const RiseFall *enable_rf; LatchEnableState state; latchDtoQEnable(edge, inst, enable_vertex, enable_rf, state); return state; diff --git a/search/Latches.hh b/search/Latches.hh index 69dacaa9..827b182f 100644 --- a/search/Latches.hh +++ b/search/Latches.hh @@ -77,7 +77,7 @@ public: const Instance *inst, // Return values. Vertex *&enable_vertex, - RiseFall *&enable_rf, + const RiseFall *&enable_rf, LatchEnableState &state) const; LatchEnableState latchDtoQState(Edge *d_q_edge) const; void latchEnableOtherPath(Path *path, diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index d8252e3c..235b8920 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -395,9 +395,7 @@ void MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, const ClockEdgeDelays &clk_margins) { - for (auto clk_edge_margins : clk_margins) { - const ClockEdge *clk_edge = clk_edge_margins.first; - RiseFallMinMax &margins = clk_edge_margins.second; + for (const auto& [clk_edge, margins] : clk_margins) { for (MinMax *min_max : MinMax::range()) { bool setup = (min_max == MinMax::max()); TimingArcAttrsPtr attrs = nullptr; @@ -442,9 +440,7 @@ void MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin, OutputPinDelays &output_pin_delays) { - for (auto out_pin_delay : output_pin_delays) { - const Pin *output_pin = out_pin_delay.first; - OutputDelays &output_delays = out_pin_delay.second; + for (const auto& [output_pin, output_delays] : output_pin_delays) { TimingArcAttrsPtr attrs = nullptr; for (RiseFall *output_rf : RiseFall::range()) { MinMax *min_max = MinMax::max(); @@ -499,9 +495,7 @@ MakeTimingModel::findClkedOutputPaths() delayAsFloat(delay, min_max, sta_)); } } - for (auto clk_edge_delay : clk_delays) { - const ClockEdge *clk_edge = clk_edge_delay.first; - RiseFallMinMax &delays = clk_edge_delay.second; + for (const auto& [clk_edge, delays] : clk_delays) { for (const Pin *clk_pin : clk_edge->clock()->pins()) { LibertyPort *clk_port = modelPort(clk_pin); if (clk_port) { @@ -659,9 +653,6 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, { const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_); const Pvt *pvt = dcalc_ap->operatingConditions(); - const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); - int lib_index = dcalc_ap->libertyIndex(); - PinSet *drvrs = network_->drivers(network_->net(network_->term(output_pin))); const Pin *drvr_pin = *drvrs->begin(); const LibertyPort *drvr_port = network_->libertyPort(drvr_pin); @@ -680,8 +671,7 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin, drvr_arc->fromEdge()->asRiseFall(), dcalc_ap->index()); float in_slew1 = delayAsFloat(in_slew); - TimingModel *drvr_model = drvr_arc->cornerArc(lib_index)->model(op_cond); - GateTableModel *drvr_gate_model = dynamic_cast(drvr_model); + GateTableModel *drvr_gate_model = drvr_arc->gateTableModel(dcalc_ap); if (drvr_gate_model) { float output_load_cap = graph_delay_calc_->loadCap(output_pin, dcalc_ap); ArcDelay drvr_self_delay; diff --git a/search/PathEnd.cc b/search/PathEnd.cc index 6c67ad40..9f8503d2 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -259,7 +259,16 @@ PathEnd::borrow(const StaState *) const } Crpr -PathEnd::commonClkPessimism(const StaState *) const +PathEnd::checkCrpr(const StaState *sta) const +{ + if (checkRole(sta)->genericRole() == TimingRole::hold()) + return -crpr(sta); + else + return crpr(sta);; +} + +Crpr +PathEnd::crpr(const StaState *) const { return 0.0; } @@ -618,7 +627,7 @@ Arrival PathEndClkConstrained::targetClkArrival(const StaState *sta) const { return targetClkArrivalNoCrpr(sta) - + commonClkPessimism(sta); + + checkCrpr(sta); } Arrival @@ -689,24 +698,21 @@ PathEndClkConstrained::targetClkUncertainty(const StaState *sta) const } Crpr -PathEndClkConstrained::commonClkPessimism(const StaState *sta) const +PathEndClkConstrained::crpr(const StaState *sta) const { if (!crpr_valid_) { CheckCrpr *check_crpr = sta->search()->checkCrpr(); crpr_ = check_crpr->checkCrpr(path_.path(), targetClkPath()); crpr_valid_ = true; } - if (checkRole(sta)->genericRole() == TimingRole::hold()) - return -crpr_; - else - return crpr_; + return crpr_; } Required PathEndClkConstrained::requiredTime(const StaState *sta) const { return requiredTimeNoCrpr(sta) - + commonClkPessimism(sta); + + checkCrpr(sta); } Required @@ -1014,8 +1020,7 @@ PathEndCheck::exceptPathCmp(const PathEnd *path_end, Delay PathEndCheck::clkSkew(const StaState *sta) { - commonClkPessimism(sta); - return sourceClkDelay(sta) - targetClkDelay(sta) - crpr_ + return sourceClkDelay(sta) - targetClkDelay(sta) - crpr(sta) // Uncertainty decreases slack, but increases skew. - checkTgtClkUncertainty(&clk_path_, clk_path_.clkEdge(sta), checkRole(sta), sta); } @@ -1373,13 +1378,12 @@ PathEndOutputDelay::targetClkArrivalNoCrpr(const StaState *sta) const } Crpr -PathEndOutputDelay::commonClkPessimism(const StaState *sta) const +PathEndOutputDelay::crpr(const StaState *sta) const { if (!crpr_valid_) { CheckCrpr *check_crpr = sta->search()->checkCrpr(); crpr_ = check_crpr->outputDelayCrpr(path_.path(), targetClkEdge(sta)); - if (checkRole(sta)->genericRole() == TimingRole::hold()) - crpr_ = -crpr_; + crpr_valid_ = true; } return crpr_; } diff --git a/search/PathEnum.cc b/search/PathEnum.cc index d9560f41..69e6df77 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -399,7 +399,7 @@ PathEnumFaninVisitor::reportDiversion(TimingArc *div_arc, report_->reportLine("path_enum: from %s -> %s", div_prev.name(this), before_div_.name(this)); - report_->reportLine("path_enum: to %s ->e", + report_->reportLine("path_enum: to %s", after_div->name(this)); } } @@ -502,9 +502,6 @@ PathEnum::makeDiversions(PathEnd *path_end, path.prevPath(this, prev_path, prev_arc); PathEnumFaninVisitor fanin_visitor(path_end, path, unique_pins_, this); while (prev_arc - // Do not enumerate beyond latch D to Q edges. - // This breaks latch loop paths. - && prev_arc->role() != TimingRole::latchDtoQ() // Do not enumerate paths in the clk network. && !path.isClock(this)) { // Fanin visitor does all the work. @@ -512,6 +509,10 @@ PathEnum::makeDiversions(PathEnd *path_end, // previous path and arc as well as diversions. fanin_visitor.visitFaninPathsThru(path.vertex(this), prev_path.vertex(this), prev_arc); + // Do not enumerate beyond latch D to Q edges. + // This breaks latch loop paths. + if (prev_arc->role() == TimingRole::latchDtoQ()) + break; path.init(prev_path); path.prevPath(this, prev_path, prev_arc); } @@ -574,7 +575,7 @@ PathEnum::updatePathHeadDelays(PathEnumedSeq &paths, Path *after_div) { Tag *prev_tag = after_div->tag(this); - ClkInfo *clk_info = prev_tag->clkInfo(); + ClkInfo *prev_clk_info = prev_tag->clkInfo(); Arrival prev_arrival = search_->clkPathArrival(after_div); for (int i = paths.size() - 1; i >= 0; i--) { PathEnumed *path = paths[i]; @@ -585,19 +586,22 @@ PathEnum::updatePathHeadDelays(PathEnumedSeq &paths, ArcDelay arc_delay = search_->deratedDelay(edge->from(graph_), arc, edge, false, path_ap); Arrival arrival = prev_arrival + arc_delay; - debugPrint(debug_, "path_enum", 3, "update arrival %s %s -> %s", - path->name(this), + debugPrint(debug_, "path_enum", 3, "update arrival %s %s %s -> %s", + path->vertex(this)->name(network_), + path->tag(this)->asString(this), delayAsString(path->arrival(this), this), delayAsString(arrival, this)); path->setArrival(arrival, this); prev_arrival = arrival; - if (sdc_->crprActive()) { + if (sdc_->crprActive() + // 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 *tag = path->tag(this); Tag *updated_tag = search_->findTag(path->transition(this), path_ap, - clk_info, + prev_clk_info, tag->isClock(), tag->inputDelay(), tag->isSegmentStart(), diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 40d793fe..0b61358e 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -255,8 +255,7 @@ PathGroups::makeGroups(int group_count, { int mm_index = min_max->index(); if (setup_hold) { - for (auto name_group : sdc_->groupPaths()) { - const char *name = name_group.first; + for (const auto [name, group] : sdc_->groupPaths()) { if (reportGroup(name, group_names)) { PathGroup *group = PathGroup::makePathGroupSlack(name, group_count, endpoint_count, unique_pins, @@ -840,7 +839,7 @@ public: virtual void visit(Vertex *vertex); private: - VisitPathEnds *visit_path_ends_; + VisitPathEnds visit_path_ends_; PathEndVisitor *path_end_visitor_; const Corner *corner_; const MinMaxAll *min_max_; @@ -851,7 +850,7 @@ MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, const Corner *corner, const MinMaxAll *min_max, const StaState *sta) : - visit_path_ends_(new VisitPathEnds(sta)), + visit_path_ends_(sta), path_end_visitor_(path_end_visitor->copy()), corner_(corner), min_max_(min_max), @@ -860,7 +859,7 @@ MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, } MakeEndpointPathEnds::MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends) : - visit_path_ends_(new VisitPathEnds(make_path_ends.sta_)), + visit_path_ends_(make_path_ends.sta_), path_end_visitor_(make_path_ends.path_end_visitor_->copy()), corner_(make_path_ends.corner_), min_max_(make_path_ends.min_max_), @@ -870,7 +869,6 @@ MakeEndpointPathEnds::MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path MakeEndpointPathEnds::~MakeEndpointPathEnds() { - delete visit_path_ends_; delete path_end_visitor_; } @@ -883,8 +881,7 @@ MakeEndpointPathEnds::copy() const void MakeEndpointPathEnds::visit(Vertex *vertex) { - visit_path_ends_->visitPathEnds(vertex, corner_, min_max_, true, - path_end_visitor_); + visit_path_ends_.visitPathEnds(vertex, corner_, min_max_, true, path_end_visitor_); } //////////////////////////////////////////////////////////////// @@ -904,7 +901,7 @@ PathGroups::makeGroupPathEnds(VertexSet *endpoints, Vector visitors(thread_count_, MakeEndpointPathEnds(visitor, corner, min_max, this)); - for (auto endpoint : *endpoints) { + for (const auto endpoint : *endpoints) { dispatch_queue_->dispatch( [endpoint, &visitors](int i) { visitors[i].visit(endpoint); } ); } diff --git a/search/PathVertex.cc b/search/PathVertex.cc index 85b0f186..799a9715 100644 --- a/search/PathVertex.cc +++ b/search/PathVertex.cc @@ -428,7 +428,7 @@ PrevPathVisitor::unfilteredTag(const Tag *tag) const ExceptionPath *except = state->exception(); if (!except->isFilter()) { if (unfiltered_states == nullptr) - unfiltered_states = new ExceptionStateSet(network_); + unfiltered_states = new ExceptionStateSet(); unfiltered_states->insert(state); } } diff --git a/search/Property.cc b/search/Property.cc index 1ec9616d..d51b5303 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -889,6 +889,7 @@ getProperty(const Instance *inst, Sta *sta) { auto network = sta->cmdNetwork(); + LibertyCell *liberty_cell = network->libertyCell(inst); if (stringEqual(property, "name")) return PropertyValue(network->name(inst)); else if (stringEqual(property, "full_name")) @@ -899,6 +900,18 @@ getProperty(const Instance *inst, return PropertyValue(network->libertyCell(inst)); else if (stringEqual(property, "cell")) return PropertyValue(network->cell(inst)); + else if (stringEqual(property, "is_hierarchical")) + return PropertyValue(network->isHierarchical(inst)); + else if (stringEqual(property, "is_buffer")) + return PropertyValue(liberty_cell && liberty_cell->isBuffer()); + else if (stringEqual(property, "is_clock_gate")) + return PropertyValue(liberty_cell && liberty_cell->isClockGate()); + else if (stringEqual(property, "is_inverter")) + return PropertyValue(liberty_cell && liberty_cell->isInverter()); + else if (stringEqual(property, "is_macro")) + return PropertyValue(liberty_cell && liberty_cell->isMacro()); + else if (stringEqual(property, "is_memory_cell")) + return PropertyValue(liberty_cell && liberty_cell->isMemory()); else throw PropertyUnknown("instance", property); } @@ -918,6 +931,10 @@ getProperty(const Pin *pin, return PropertyValue(network->pathName(pin)); else if (stringEqual(property, "direction")) return PropertyValue(network->direction(pin)->name()); + else if (stringEqual(property, "is_hierarchical")) + return PropertyValue(network->isHierarchical(pin)); + else if (stringEqual(property, "is_port")) + return PropertyValue(network->isTopLevelPort(pin)); else if (stringEqual(property, "is_register_clock")) { const LibertyPort *port = network->libertyPort(pin); return PropertyValue(port && port->isRegClk()); @@ -1163,10 +1180,12 @@ getProperty(Clock *clk, return PropertyValue(clk->period(), sta->units()->timeUnit()); else if (stringEqual(property, "sources")) return PropertyValue(clk->pins()); - else if (stringEqual(property, "propagated")) - return PropertyValue(clk->isPropagated()); else if (stringEqual(property, "is_generated")) return PropertyValue(clk->isGenerated()); + else if (stringEqual(property, "is_virtual")) + return PropertyValue(clk->isVirtual()); + else if (stringEqual(property, "is_propagated")) + return PropertyValue(clk->isPropagated()); else throw PropertyUnknown("clock", property); } diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 89b31d28..662a1dba 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -580,14 +580,9 @@ ReportPath::reportEndpoint(const PathEndLatchCheck *end) const char * ReportPath::latchDesc(const PathEndLatchCheck *end) { - // Liberty latch descriptions can have timing checks to the - // wrong edge of the enable, so look up the EN->Q arcs and use - // them to characterize the latch as positive/negative. TimingArc *check_arc = end->checkArc(); - TimingArcSet *check_set = check_arc->set(); - LibertyCell *cell = check_set->from()->libertyCell(); - RiseFall *enable_rf = cell->latchCheckEnableEdge(check_set); - return latchDesc(enable_rf); + const RiseFall *en_rf = check_arc->fromEdge()->asRiseFall()->opposite(); + return latchDesc(en_rf); } void @@ -1197,7 +1192,7 @@ ReportPath::reportVerbose(MinPulseWidthCheck *check) reportLine(pin_name, delay_zero, close_arrival, close_el); if (sdc_->crprEnabled()) { - Crpr pessimism = check->commonClkPessimism(this); + Crpr pessimism = check->checkCrpr(this); close_arrival += pessimism; reportLine("clock reconvergence pessimism", pessimism, close_arrival, close_el); } @@ -1808,7 +1803,7 @@ ReportPath::clkRegLatchDesc(const PathEnd *end) TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ()) { - RiseFall *arc_rf = arc_set->isRisingFallingEdge(); + const RiseFall *arc_rf = arc_set->isRisingFallingEdge(); clk_set = arc_set; if (arc_rf == check_clk_rf) clk_rf_set = arc_set; @@ -2325,7 +2320,7 @@ ReportPath::reportCommonClkPessimism(const PathEnd *end, Arrival &clk_arrival) { if (sdc_->crprEnabled()) { - Crpr pessimism = end->commonClkPessimism(this); + Crpr pessimism = end->checkCrpr(this); clk_arrival += pessimism; reportLine("clock reconvergence pessimism", pessimism, clk_arrival, end->clkEarlyLate(this)); @@ -3255,9 +3250,9 @@ ReportPath::edgeRegLatchDesc(Edge *first_edge, Instance *inst = network_->instance(first_edge->to(graph_)->pin()); LibertyCell *cell = network_->libertyCell(inst); if (cell) { - LibertyPort *enable_port; - FuncExpr *enable_func; - RiseFall *enable_rf; + const LibertyPort *enable_port; + const FuncExpr *enable_func; + const RiseFall *enable_rf; cell->latchEnable(first_edge->timingArcSet(), enable_port, enable_func, enable_rf); return latchDesc(enable_rf); diff --git a/search/Search.cc b/search/Search.cc index 92d1335a..f8c0d0c1 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -905,8 +905,7 @@ Search::visitStartpoints(VertexVisitor *visitor) } delete pin_iter; - for (auto iter : sdc_->inputDelayPinMap()) { - const Pin *pin = iter.first; + for (const auto [pin, input_delays] : sdc_->inputDelayPinMap()) { // Already hit these. if (!network_->isTopLevelPort(pin)) { Vertex *vertex = graph_->pinDrvrVertex(pin); @@ -1674,8 +1673,7 @@ 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 (auto iter : sdc_->inputDelayPinMap()) { - const Pin *pin = iter.first; + for (const auto [pin, input_delays] : sdc_->inputDelayPinMap()) { if (!sdc_->isLeafPinClock(pin)) { Vertex *vertex = graph_->pinDrvrVertex(pin); seedInputArrival(pin, vertex, clks); @@ -2565,7 +2563,7 @@ Search::mutateTag(Tag *from_tag, // Second pass to apply state changes and add updated existing // states to new states. if (new_states == nullptr) - new_states = new ExceptionStateSet(network_); + new_states = new ExceptionStateSet(); for (auto state : *from_states) { ExceptionPath *exception = state->exception(); // One edge may traverse multiple hierarchical thru pins. @@ -2928,7 +2926,7 @@ Search::reportClkInfos() const sort(clk_infos, ClkInfoLess(this)); for (ClkInfo *clk_info : clk_infos) report_->reportLine("ClkInfo %s", clk_info->asString(this)); - report_->reportLine("%lu clk infos", clk_info_set_->size()); + report_->reportLine("%zu clk infos", clk_info_set_->size()); } ClkInfo * diff --git a/search/Search.i b/search/Search.i new file mode 100644 index 00000000..a5c83a20 --- /dev/null +++ b/search/Search.i @@ -0,0 +1,1483 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + +%module search + +%{ + +#include "Units.hh" +#include "PathGroup.hh" +#include "Search.hh" +#include "search/Levelize.hh" +#include "search/ReportPath.hh" +#include "Sta.hh" + +namespace sta { + +//////////////////////////////////////////////////////////////// +// +// C++ helper functions used by the interface functions. +// These are not visible in the TCL API. +// +//////////////////////////////////////////////////////////////// + +// Get the network for commands. +Network * +cmdNetwork() +{ + return Sta::sta()->cmdNetwork(); +} + +// Make sure the network has been read and linked. +// Throwing an error means the caller doesn't have to check the result. +Network * +cmdLinkedNetwork() +{ + Network *network = cmdNetwork(); + if (network->isLinked()) + return network; + else { + Report *report = Sta::sta()->report(); + report->error(1570, "no network has been linked."); + return nullptr; + } +} + +// Make sure an editable network has been read and linked. +NetworkEdit * +cmdEditNetwork() +{ + Network *network = cmdLinkedNetwork(); + if (network->isEditable()) + return dynamic_cast(network); + else { + Report *report = Sta::sta()->report(); + report->error(1571, "network does not support edits."); + return nullptr; + } +} + +// Get the graph for commands. +// Throw to cmd level on failure. +Graph * +cmdGraph() +{ + cmdLinkedNetwork(); + return Sta::sta()->ensureGraph(); +} + +} // namespace + +using namespace sta; + +%} + +//////////////////////////////////////////////////////////////// +// +// Empty class definitions to make swig happy. +// Private constructor/destructor so swig doesn't emit them. +// +//////////////////////////////////////////////////////////////// + +class VertexPathIterator +{ +private: + VertexPathIterator(); + ~VertexPathIterator(); +}; + +class PathRef +{ +private: + PathRef(); + ~PathRef(); +}; + +class PathEnd +{ +private: + PathEnd(); + ~PathEnd(); +}; + +class MinPulseWidthCheck +{ +private: + MinPulseWidthCheck(); + ~MinPulseWidthCheck(); +}; + +class MinPulseWidthCheckSeq +{ +private: + MinPulseWidthCheckSeq(); + ~MinPulseWidthCheckSeq(); +}; + +class MinPulseWidthCheckSeqIterator +{ +private: + MinPulseWidthCheckSeqIterator(); + ~MinPulseWidthCheckSeqIterator(); +}; + +class Corner +{ +private: + Corner(); + ~Corner(); +}; + +%inline %{ + +int group_count_max = PathGroup::group_count_max; + +//////////////////////////////////////////////////////////////// + +// Initialize sta after delete_all_memory. +void +init_sta() +{ + initSta(); +} + +void +clear_sta() +{ + Sta::sta()->clear(); +} + +void +make_sta(Tcl_Interp *interp) +{ + Sta *sta = new Sta; + Sta::setSta(sta); + sta->makeComponents(); + sta->setTclInterp(interp); +} + +Tcl_Interp * +tcl_interp() +{ + return Sta::sta()->tclInterp(); +} + +void +clear_network() +{ + Sta *sta = Sta::sta(); + sta->network()->clear(); +} + +void +delete_all_memory() +{ + deleteAllMemory(); +} + +//////////////////////////////////////////////////////////////// + +void +find_timing_cmd(bool full) +{ + cmdLinkedNetwork(); + Sta::sta()->updateTiming(full); +} + +void +arrivals_invalid() +{ + Sta *sta = Sta::sta(); + sta->arrivalsInvalid(); +} + +PinSet +startpoints() +{ + return Sta::sta()->startpointPins(); +} + +PinSet +endpoints() +{ + return Sta::sta()->endpointPins(); +} + +size_t +endpoint_count() +{ + return Sta::sta()->endpointPins().size(); +} + +void +find_requireds() +{ + cmdLinkedNetwork(); + Sta::sta()->findRequireds(); +} + +Slack +total_negative_slack_cmd(const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + return sta->totalNegativeSlack(min_max); +} + +Slack +total_negative_slack_corner_cmd(const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + return sta->totalNegativeSlack(corner, min_max); +} + +Slack +worst_slack_cmd(const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + Slack worst_slack; + Vertex *worst_vertex; + sta->worstSlack(min_max, worst_slack, worst_vertex); + return worst_slack; +} + +Vertex * +worst_slack_vertex(const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + Slack worst_slack; + Vertex *worst_vertex; + sta->worstSlack(min_max, worst_slack, worst_vertex); + return worst_vertex;; +} + +Slack +worst_slack_corner(const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + Slack worst_slack; + Vertex *worst_vertex; + sta->worstSlack(corner, min_max, worst_slack, worst_vertex); + return worst_slack; +} + +PathRef * +vertex_worst_arrival_path(Vertex *vertex, + const MinMax *min_max) +{ + Sta *sta = Sta::sta(); + PathRef path = sta->vertexWorstArrivalPath(vertex, min_max); + if (!path.isNull()) + return new PathRef(path); + else + return nullptr; +} + +PathRef * +vertex_worst_arrival_path_rf(Vertex *vertex, + const RiseFall *rf, + MinMax *min_max) +{ + Sta *sta = Sta::sta(); + PathRef path = sta->vertexWorstArrivalPath(vertex, rf, min_max); + if (!path.isNull()) + return new PathRef(path); + else + return nullptr; +} + +PathRef * +vertex_worst_slack_path(Vertex *vertex, + const MinMax *min_max) +{ + Sta *sta = Sta::sta(); + PathRef path = sta->vertexWorstSlackPath(vertex, min_max); + if (!path.isNull()) + return new PathRef(path); + else + return nullptr; +} + +int +tag_group_count() +{ + return Sta::sta()->tagGroupCount(); +} + +void +report_tag_groups() +{ + Sta::sta()->search()->reportTagGroups(); +} + +void +report_tag_arrivals_cmd(Vertex *vertex) +{ + Sta::sta()->search()->reportArrivals(vertex); +} + +void +report_arrival_count_histogram() +{ + Sta::sta()->search()->reportArrivalCountHistogram(); +} + +int +tag_count() +{ + return Sta::sta()->tagCount(); +} + +void +report_tags() +{ + Sta::sta()->search()->reportTags(); +} + +void +report_clk_infos() +{ + Sta::sta()->search()->reportClkInfos(); +} + +int +clk_info_count() +{ + return Sta::sta()->clkInfoCount(); +} + +int +arrival_count() +{ + return Sta::sta()->arrivalCount(); +} + +int +required_count() +{ + return Sta::sta()->requiredCount(); +} + +int +graph_arrival_count() +{ + return Sta::sta()->graph()->arrivalCount(); +} + +int +graph_required_count() +{ + return Sta::sta()->graph()->requiredCount(); +} + +int +endpoint_violation_count(const MinMax *min_max) +{ + return Sta::sta()->endpointViolationCount(min_max); +} + +void +report_loops() +{ + Sta *sta = Sta::sta(); + Network *network = cmdLinkedNetwork(); + Graph *graph = cmdGraph(); + Report *report = sta->report(); + for (GraphLoop *loop : *sta->graphLoops()) { + loop->report(report, network, graph); + report->reportLineString(""); + } +} + +char +pin_sim_logic_value(const Pin *pin) +{ + return logicValueString(Sta::sta()->simLogicValue(pin)); +} + +InstanceSeq +slow_drivers(int count) +{ + return Sta::sta()->slowDrivers(count); +} + +//////////////////////////////////////////////////////////////// + +PathEndSeq +find_path_ends(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + Corner *corner, + const MinMaxAll *delay_min_max, + int group_count, + int endpoint_count, + bool unique_pins, + 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) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + PathEndSeq ends = sta->findPathEnds(from, thrus, to, unconstrained, + corner, delay_min_max, + group_count, endpoint_count, unique_pins, + slack_min, slack_max, + sort_by_slack, + groups->size() ? groups : nullptr, + setup, hold, + recovery, removal, + clk_gating_setup, clk_gating_hold); + delete groups; + return ends; +} + +//////////////////////////////////////////////////////////////// + +void +report_path_end_header() +{ + Sta::sta()->reportPathEndHeader(); +} + +void +report_path_end_footer() +{ + Sta::sta()->reportPathEndFooter(); +} + +void +report_path_end(PathEnd *end) +{ + Sta::sta()->reportPathEnd(end); +} + +void +report_path_end2(PathEnd *end, + PathEnd *prev_end) +{ + Sta::sta()->reportPathEnd(end, prev_end); +} + +void +set_report_path_format(ReportPathFormat format) +{ + Sta::sta()->setReportPathFormat(format); +} + +void +set_report_path_field_order(StringSeq *field_names) +{ + Sta::sta()->setReportPathFieldOrder(field_names); + delete field_names; +} + +void +set_report_path_fields(bool report_input_pin, + bool report_net, + bool report_cap, + bool report_slew, + bool report_fanout) +{ + Sta::sta()->setReportPathFields(report_input_pin, + report_net, + report_cap, + report_slew, + report_fanout); +} + +void +set_report_path_field_properties(const char *field_name, + const char *title, + int width, + bool left_justify) +{ + Sta *sta = Sta::sta(); + ReportField *field = sta->findReportPathField(field_name); + if (field) + field->setProperties(title, width, left_justify); + else + sta->report()->error(1575, "unknown report path field %s", field_name); +} + +void +set_report_path_field_width(const char *field_name, + int width) +{ + Sta *sta = Sta::sta(); + ReportField *field = sta->findReportPathField(field_name); + if (field) + field->setWidth(width); + else + sta->report()->error(1576, "unknown report path field %s", field_name); +} + +void +set_report_path_digits(int digits) +{ + Sta::sta()->setReportPathDigits(digits); +} + +void +set_report_path_no_split(bool no_split) +{ + Sta::sta()->setReportPathNoSplit(no_split); +} + +void +set_report_path_sigmas(bool report_sigmas) +{ + Sta::sta()->setReportPathSigmas(report_sigmas); +} + +void +delete_path_ref(PathRef *path) +{ + delete path; +} + +void +report_path_cmd(PathRef *path) +{ + Sta::sta()->reportPath(path); +} + +//////////////////////////////////////////////////////////////// + +void +report_clk_skew(ConstClockSeq clks, + const Corner *corner, + const SetupHold *setup_hold, + bool include_internal_latency, + int digits) +{ + cmdLinkedNetwork(); + Sta::sta()->reportClkSkew(clks, corner, setup_hold, + include_internal_latency, digits); +} + +void +report_clk_latency(ConstClockSeq clks, + const Corner *corner, + bool include_internal_latency, + int digits) +{ + cmdLinkedNetwork(); + Sta::sta()->reportClkLatency(clks, corner, include_internal_latency, digits); +} + +float +worst_clk_skew_cmd(const SetupHold *setup_hold, + bool include_internal_latency) +{ + cmdLinkedNetwork(); + return Sta::sta()->findWorstClkSkew(setup_hold, include_internal_latency); +} + +//////////////////////////////////////////////////////////////// + +MinPulseWidthCheckSeq & +min_pulse_width_violations(const Corner *corner) +{ + cmdLinkedNetwork(); + return Sta::sta()->minPulseWidthViolations(corner); +} + +MinPulseWidthCheckSeq & +min_pulse_width_check_pins(PinSeq *pins, + const Corner *corner) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + MinPulseWidthCheckSeq &checks = sta->minPulseWidthChecks(pins, corner); + delete pins; + return checks; +} + +MinPulseWidthCheckSeq & +min_pulse_width_checks(const Corner *corner) +{ + cmdLinkedNetwork(); + return Sta::sta()->minPulseWidthChecks(corner); +} + +MinPulseWidthCheck * +min_pulse_width_check_slack(const Corner *corner) +{ + cmdLinkedNetwork(); + return Sta::sta()->minPulseWidthSlack(corner); +} + +void +report_mpw_checks(MinPulseWidthCheckSeq *checks, + bool verbose) +{ + Sta::sta()->reportMpwChecks(checks, verbose); +} + +void +report_mpw_check(MinPulseWidthCheck *check, + bool verbose) +{ + Sta::sta()->reportMpwCheck(check, verbose); +} + +//////////////////////////////////////////////////////////////// + +MinPeriodCheckSeq & +min_period_violations() +{ + cmdLinkedNetwork(); + return Sta::sta()->minPeriodViolations(); +} + +MinPeriodCheck * +min_period_check_slack() +{ + cmdLinkedNetwork(); + return Sta::sta()->minPeriodSlack(); +} + +void +report_min_period_checks(MinPeriodCheckSeq *checks, + bool verbose) +{ + Sta::sta()->reportChecks(checks, verbose); +} + +void +report_min_period_check(MinPeriodCheck *check, + bool verbose) +{ + Sta::sta()->reportCheck(check, verbose); +} + +//////////////////////////////////////////////////////////////// + +MaxSkewCheckSeq & +max_skew_violations() +{ + cmdLinkedNetwork(); + return Sta::sta()->maxSkewViolations(); +} + +MaxSkewCheck * +max_skew_check_slack() +{ + cmdLinkedNetwork(); + return Sta::sta()->maxSkewSlack(); +} + +void +report_max_skew_checks(MaxSkewCheckSeq *checks, + bool verbose) +{ + Sta::sta()->reportChecks(checks, verbose); +} + +void +report_max_skew_check(MaxSkewCheck *check, + bool verbose) +{ + Sta::sta()->reportCheck(check, verbose); +} + +//////////////////////////////////////////////////////////////// + +Slack +find_clk_min_period(const Clock *clk, + bool ignore_port_paths) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + return sta->findClkMinPeriod(clk, ignore_port_paths); +} + +//////////////////////////////////////////////////////////////// + +PinSeq +check_slew_limits(Net *net, + bool violators, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->checkSlewLimits(net, violators, corner, min_max); +} + +size_t +max_slew_violation_count() +{ + cmdLinkedNetwork(); + return Sta::sta()->checkSlewLimits(nullptr, true, nullptr, MinMax::max()).size(); +} + +float +max_slew_check_slack() +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + const Pin *pin; + Slew slew; + float slack; + float limit; + sta->maxSlewCheck(pin, slew, slack, limit); + return sta->units()->timeUnit()->staToUser(slack); +} + +float +max_slew_check_limit() +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + const Pin *pin; + Slew slew; + float slack; + float limit; + sta->maxSlewCheck(pin, slew, slack, 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) +{ + cmdLinkedNetwork(); + return Sta::sta()->checkFanoutLimits(net, violators, min_max); +} + +size_t +max_fanout_violation_count() +{ + cmdLinkedNetwork(); + return Sta::sta()->checkFanoutLimits(nullptr, true, MinMax::max()).size(); +} + +float +max_fanout_check_slack() +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + const Pin *pin; + float fanout; + float slack; + float limit; + sta->maxFanoutCheck(pin, fanout, slack, limit); + return slack;; +} + +float +max_fanout_check_limit() +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + const Pin *pin; + float fanout; + float slack; + float limit; + sta->maxFanoutCheck(pin, fanout, slack, limit); + return limit;; +} + +void +report_fanout_limit_short_header() +{ + 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); +} + +//////////////////////////////////////////////////////////////// + +PinSeq +check_capacitance_limits(Net *net, + bool violators, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->checkCapacitanceLimits(net, violators, corner, min_max); +} + +size_t +max_capacitance_violation_count() +{ + cmdLinkedNetwork(); + return Sta::sta()->checkCapacitanceLimits(nullptr, true,nullptr,MinMax::max()).size(); +} + +float +max_capacitance_check_slack() +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + const Pin *pin; + float capacitance; + float slack; + float limit; + sta->maxCapacitanceCheck(pin, capacitance, slack, limit); + return sta->units()->capacitanceUnit()->staToUser(slack); +} + +float +max_capacitance_check_limit() +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + const Pin *pin; + float capacitance; + float slack; + float limit; + sta->maxCapacitanceCheck(pin, capacitance, slack, 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) +{ + Sta::sta()->writeTimingModel(lib_name, cell_name, filename, corner); +} + +//////////////////////////////////////////////////////////////// + +void +define_corners_cmd(StringSet *corner_names) +{ + Sta *sta = Sta::sta(); + sta->makeCorners(corner_names); + delete corner_names; +} + +Corner * +cmd_corner() +{ + return Sta::sta()->cmdCorner(); +} + +void +set_cmd_corner(Corner *corner) +{ + Sta::sta()->setCmdCorner(corner); +} + +Corner * +find_corner(const char *corner_name) +{ + return Sta::sta()->findCorner(corner_name); +} + +Corners * +corners() +{ + return Sta::sta()->corners(); +} + +bool +multi_corner() +{ + return Sta::sta()->multiCorner(); +} + +//////////////////////////////////////////////////////////////// + +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) +{ + cmdLinkedNetwork(); + return Sta::sta()->checkTiming(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) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + PinSet fanin = sta->findFaninPins(to, flat, startpoints_only, + inst_levels, pin_levels, + thru_disabled, thru_constants); + 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) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + InstanceSet fanin = sta->findFaninInstances(to, flat, startpoints_only, + inst_levels, pin_levels, + thru_disabled, thru_constants); + 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) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + PinSet fanout = sta->findFanoutPins(from, flat, endpoints_only, + inst_levels, pin_levels, + thru_disabled, thru_constants); + 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) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + InstanceSet fanout = sta->findFanoutInstances(from, flat, endpoints_only, + inst_levels, pin_levels, + thru_disabled, thru_constants); + delete from; + return fanout; +} + +//////////////////////////////////////////////////////////////// +// +// Variables +// +//////////////////////////////////////////////////////////////// + +bool +crpr_enabled() +{ + return Sta::sta()->crprEnabled(); +} + +void +set_crpr_enabled(bool enabled) +{ + return Sta::sta()->setCrprEnabled(enabled); +} + +const char * +crpr_mode() +{ + switch (Sta::sta()->crprMode()) { + case CrprMode::same_transition: + return "same_transition"; + case CrprMode::same_pin: + return "same_pin"; + default: + return ""; + } +} + +void +set_crpr_mode(const char *mode) +{ + Sta *sta = Sta::sta(); + if (stringEq(mode, "same_pin")) + Sta::sta()->setCrprMode(CrprMode::same_pin); + else if (stringEq(mode, "same_transition")) + Sta::sta()->setCrprMode(CrprMode::same_transition); + else + sta->report()->critical(1573, "unknown common clk pessimism mode."); +} + +bool +pocv_enabled() +{ + return Sta::sta()->pocvEnabled(); +} + +void +set_pocv_enabled(bool enabled) +{ +#if !SSTA + if (enabled) + Sta::sta()->report()->error(1574, "POCV support requires compilation with SSTA=1."); +#endif + return Sta::sta()->setPocvEnabled(enabled); +} + +float +pocv_sigma_factor() +{ + return Sta::sta()->sigmaFactor(); +} + +void +set_pocv_sigma_factor(float factor) +{ + Sta::sta()->setSigmaFactor(factor); +} + +bool +propagate_gated_clock_enable() +{ + return Sta::sta()->propagateGatedClockEnable(); +} + +void +set_propagate_gated_clock_enable(bool enable) +{ + Sta::sta()->setPropagateGatedClockEnable(enable); +} + +bool +preset_clr_arcs_enabled() +{ + return Sta::sta()->presetClrArcsEnabled(); +} + +void +set_preset_clr_arcs_enabled(bool enable) +{ + Sta::sta()->setPresetClrArcsEnabled(enable); +} + +bool +cond_default_arcs_enabled() +{ + return Sta::sta()->condDefaultArcsEnabled(); +} + +void +set_cond_default_arcs_enabled(bool enabled) +{ + Sta::sta()->setCondDefaultArcsEnabled(enabled); +} + +bool +bidirect_inst_paths_enabled() +{ + return Sta::sta()->bidirectInstPathsEnabled(); +} + +void +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() +{ + return Sta::sta()->recoveryRemovalChecksEnabled(); +} + +void +set_recovery_removal_checks_enabled(bool enabled) +{ + Sta::sta()->setRecoveryRemovalChecksEnabled(enabled); +} + +bool +gated_clk_checks_enabled() +{ + return Sta::sta()->gatedClkChecksEnabled(); +} + +void +set_gated_clk_checks_enabled(bool enabled) +{ + Sta::sta()->setGatedClkChecksEnabled(enabled); +} + +bool +dynamic_loop_breaking() +{ + return Sta::sta()->dynamicLoopBreaking(); +} + +void +set_dynamic_loop_breaking(bool enable) +{ + Sta::sta()->setDynamicLoopBreaking(enable); +} + +bool +use_default_arrival_clock() +{ + return Sta::sta()->useDefaultArrivalClock(); +} + +void +set_use_default_arrival_clock(bool enable) +{ + return Sta::sta()->setUseDefaultArrivalClock(enable); +} + +//////////////////////////////////////////////////////////////// +// +// Properties +// +//////////////////////////////////////////////////////////////// + +PropertyValue +pin_property(const Pin *pin, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(pin, property, Sta::sta()); +} + +PropertyValue +instance_property(const Instance *inst, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(inst, property, Sta::sta()); +} + +PropertyValue +net_property(const Net *net, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(net, property, Sta::sta()); +} + +PropertyValue +port_property(const Port *port, + const char *property) +{ + return getProperty(port, property, Sta::sta()); +} + + +PropertyValue +liberty_cell_property(const LibertyCell *cell, + const char *property) +{ + return getProperty(cell, property, Sta::sta()); +} + +PropertyValue +cell_property(const Cell *cell, + const char *property) +{ + return getProperty(cell, property, Sta::sta()); +} + +PropertyValue +liberty_port_property(const LibertyPort *port, + const char *property) +{ + return getProperty(port, property, Sta::sta()); +} + +PropertyValue +library_property(const Library *lib, + const char *property) +{ + return getProperty(lib, property, Sta::sta()); +} + +PropertyValue +liberty_library_property(const LibertyLibrary *lib, + const char *property) +{ + return getProperty(lib, property, Sta::sta()); +} + +PropertyValue +edge_property(Edge *edge, + const char *property) +{ + cmdGraph(); + return getProperty(edge, property, Sta::sta()); +} + +PropertyValue +clock_property(Clock *clk, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(clk, property, Sta::sta()); +} + +PropertyValue +path_end_property(PathEnd *end, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(end, property, Sta::sta()); +} + +PropertyValue +path_ref_property(PathRef *path, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(path, property, Sta::sta()); +} + +PropertyValue +timing_arc_set_property(TimingArcSet *arc_set, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(arc_set, property, Sta::sta()); +} + +%} // inline + +//////////////////////////////////////////////////////////////// +// +// Object Methods +// +//////////////////////////////////////////////////////////////// + +%extend PathEnd { +bool is_unconstrained() { return self->isUnconstrained(); } +bool is_check() { return self->isCheck(); } +bool is_latch_check() { return self->isLatchCheck(); } +bool is_data_check() { return self->isDataCheck(); } +bool is_output_delay() { return self->isOutputDelay(); } +bool is_path_delay() { return self->isPathDelay(); } +bool is_gated_clock() { return self->isGatedClock(); } +Vertex *vertex() { return self->vertex(Sta::sta()); } +PathRef *path() { return &self->pathRef(); } +RiseFall *end_transition() +{ return const_cast(self->path()->transition(Sta::sta())); } +Slack slack() { return self->slack(Sta::sta()); } +ArcDelay margin() { return self->margin(Sta::sta()); } +Required data_required_time() { return self->requiredTimeOffset(Sta::sta()); } +Arrival data_arrival_time() { return self->dataArrivalTimeOffset(Sta::sta()); } +TimingRole *check_role() { return self->checkRole(Sta::sta()); } +MinMax *min_max() { return const_cast(self->minMax(Sta::sta())); } +float source_clk_offset() { return self->sourceClkOffset(Sta::sta()); } +Arrival source_clk_latency() { return self->sourceClkLatency(Sta::sta()); } +Arrival source_clk_insertion_delay() +{ return self->sourceClkInsertionDelay(Sta::sta()); } +const Clock *target_clk() { return self->targetClk(Sta::sta()); } +const ClockEdge *target_clk_edge() { return self->targetClkEdge(Sta::sta()); } +Path *target_clk_path() { return self->targetClkPath(); } +float target_clk_time() { return self->targetClkTime(Sta::sta()); } +float target_clk_offset() { return self->targetClkOffset(Sta::sta()); } +float target_clk_mcp_adjustment() +{ return self->targetClkMcpAdjustment(Sta::sta()); } +Arrival target_clk_delay() { return self->targetClkDelay(Sta::sta()); } +Arrival target_clk_insertion_delay() +{ return self->targetClkInsertionDelay(Sta::sta()); } +float target_clk_uncertainty() +{ return self->targetNonInterClkUncertainty(Sta::sta()); } +float inter_clk_uncertainty() +{ return self->interClkUncertainty(Sta::sta()); } +Arrival target_clk_arrival() { return self->targetClkArrival(Sta::sta()); } +bool path_delay_margin_is_external() +{ return self->pathDelayMarginIsExternal();} +Crpr check_crpr() { return self->checkCrpr(Sta::sta()); } +RiseFall *target_clk_end_trans() +{ return const_cast(self->targetClkEndTrans(Sta::sta())); } +Delay clk_skew() { return self->clkSkew(Sta::sta()); } + +} + +%extend PathRef { +float +arrival() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->arrival(sta)); +} + +float +required() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->required(sta)); +} + +float +slack() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->slack(sta)); +} + +const Pin * +pin() +{ + Sta *sta = Sta::sta(); + return self->pin(sta); +} + +const char * +tag() +{ + Sta *sta = Sta::sta(); + return self->tag(sta)->asString(sta); +} + +// mea_opt3 +PinSeq +pins() +{ + Sta *sta = Sta::sta(); + PinSeq pins; + PathRef path1(self); + while (!path1.isNull()) { + pins.push_back(path1.vertex(sta)->pin()); + PathRef prev_path; + path1.prevPath(sta, prev_path); + path1.init(prev_path); + } + return pins; +} + +} + +%extend VertexPathIterator { +bool has_next() { return self->hasNext(); } +PathRef * +next() +{ + Path *path = self->next(); + return new PathRef(path); +} + +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/tcl/Search.tcl b/search/Search.tcl similarity index 100% rename from tcl/Search.tcl rename to search/Search.tcl diff --git a/search/Sim.cc b/search/Sim.cc index 6d67611a..720a0811 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -16,7 +16,9 @@ #include "Sim.hh" -#include "StaConfig.hh" // CUDD +// https://davidkebo.com/cudd +#include "cudd.h" + #include "Error.hh" #include "Mutex.hh" #include "Debug.hh" @@ -32,14 +34,6 @@ #include "Sdc.hh" #include "Graph.hh" -#if CUDD -// https://davidkebo.com/cudd -#include "cudd.h" -#else -#define Cudd_Init(ignore1, ignore2, ignore3, ignore4, ignore5) nullptr -#define Cudd_Quit(ignore1) -#endif - namespace sta { static LogicValue @@ -69,8 +63,6 @@ Sim::~Sim() delete observer_; } -#if CUDD - TimingSense Sim::functionSense(const FuncExpr *expr, const Pin *input_pin, @@ -159,279 +151,6 @@ Sim::funcBddSim(const FuncExpr *expr, return bdd; } -#else -// No CUDD. - -static LogicValue -logicOr(LogicValue value1, - LogicValue value2) -{ - static LogicValue logic_or[5][5] = - {{LogicValue::zero, LogicValue::one, LogicValue::unknown, LogicValue::unknown, LogicValue::unknown}, - {LogicValue::one, LogicValue::one, LogicValue::one, LogicValue::one, LogicValue::one}, - {LogicValue::unknown,LogicValue::one, LogicValue::unknown, LogicValue::unknown, LogicValue::unknown}, - {LogicValue::unknown,LogicValue::one, LogicValue::unknown, LogicValue::unknown, LogicValue::unknown}, - {LogicValue::unknown,LogicValue::one, LogicValue::unknown, LogicValue::unknown, LogicValue::unknown}}; - return logic_or[int(value1)][int(value2)]; -} - -static LogicValue -logicAnd(LogicValue value1, - LogicValue value2) -{ - static LogicValue logic_and[5][5] = - {{LogicValue::zero,LogicValue::zero, LogicValue::zero, LogicValue::zero, LogicValue::zero}, - {LogicValue::zero,LogicValue::one, LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, - {LogicValue::zero,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, - {LogicValue::zero,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, - {LogicValue::zero,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}}; - return logic_and[int(value1)][int(value2)]; -} - -static LogicValue -logicXor(LogicValue value1, - LogicValue value2) -{ - static LogicValue logic_xor[5][5]= - {{LogicValue::zero, LogicValue::one, LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, - {LogicValue::one, LogicValue::zero, LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, - {LogicValue::unknown,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, - {LogicValue::unknown,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, - {LogicValue::unknown,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}}; - return logic_xor[int(value1)][int(value2)]; -} - -static TimingSense -senseNot(TimingSense sense) -{ - static TimingSense sense_not[5] = {TimingSense::negative_unate, - TimingSense::positive_unate, - TimingSense::non_unate, - TimingSense::none, - TimingSense::unknown}; - return sense_not[int(sense)]; -} - -static TimingSense -senseAndOr(TimingSense sense1, - TimingSense sense2) -{ - static TimingSense sense_and_or[5][5] = - {{TimingSense::positive_unate, TimingSense::non_unate, - TimingSense::non_unate, TimingSense::positive_unate, TimingSense::unknown}, - {TimingSense::non_unate, TimingSense::negative_unate, - TimingSense::non_unate, TimingSense::negative_unate, TimingSense::unknown}, - {TimingSense::non_unate, TimingSense::non_unate, TimingSense::non_unate, - TimingSense::non_unate, TimingSense::unknown}, - {TimingSense::positive_unate, TimingSense::negative_unate, - TimingSense::non_unate, TimingSense::none, TimingSense::unknown}, - {TimingSense::unknown, TimingSense::unknown, - TimingSense::unknown, TimingSense::non_unate, TimingSense::unknown}}; - return sense_and_or[int(sense1)][int(sense2)]; -} - -static TimingSense -senseXor(TimingSense sense1, - TimingSense sense2) -{ - static TimingSense xor_sense[5][5] = - {{TimingSense::non_unate, TimingSense::non_unate, - TimingSense::non_unate, TimingSense::non_unate, TimingSense::unknown}, - {TimingSense::non_unate, TimingSense::non_unate, - TimingSense::non_unate, TimingSense::non_unate, TimingSense::unknown}, - {TimingSense::non_unate, TimingSense::non_unate, - TimingSense::non_unate, TimingSense::non_unate, TimingSense::unknown}, - {TimingSense::non_unate, TimingSense::non_unate, - TimingSense::non_unate, TimingSense::none, TimingSense::unknown}, - {TimingSense::unknown, TimingSense::unknown, - TimingSense::unknown, TimingSense::unknown, TimingSense::unknown}}; - return xor_sense[int(sense1)][int(sense2)]; -} - -TimingSense -Sim::functionSense(const FuncExpr *expr, - const Pin *input_pin, - const Instance *inst) -{ - TimingSense sense = TimingSense::none; - LogicValue value = LogicValue::unknown; - functionSense(expr, input_pin, inst, sense, value); - return sense; -} - -void -Sim::functionSense(const FuncExpr *expr, - const Pin *input_pin, - const Instance *inst, - // return values - TimingSense &sense, - LogicValue &value) const -{ - switch (expr->op()) { - case FuncExpr::op_port: { - Pin *pin = network_->findPin(inst, expr->port()); - if (pin) { - if (pin == input_pin) - sense = TimingSense::positive_unate; - else - sense = TimingSense::none; - value = logicValue(pin); - } - else { - sense = TimingSense::none; - value = LogicValue::unknown; - } - break; - } - case FuncExpr::op_not: { - TimingSense sense1; - LogicValue value1; - functionSense(expr->left(), input_pin, inst, sense1, value1); - if (value1 == LogicValue::zero) { - sense = TimingSense::none; - value = LogicValue::one; - } - else if (value1 == LogicValue::one) { - sense = TimingSense::none; - value = LogicValue::zero; - } - else { - sense = senseNot(sense1); - value = LogicValue::unknown; - } - break; - } - case FuncExpr::op_or: { - TimingSense sense1, sense2; - LogicValue value1, value2; - functionSense(expr->left(), input_pin, inst, sense1, value1); - functionSense(expr->right(), input_pin, inst, sense2, value2); - if (value1 == LogicValue::one || value2 == LogicValue::one) { - sense = TimingSense::none; - value = LogicValue::one; - } - else if (value1 == LogicValue::zero) { - sense = sense2; - value = value2; - } - else if (value2 == LogicValue::zero) { - sense = sense1; - value = value1; - } - else { - sense = senseAndOr(sense1, sense2); - value = LogicValue::unknown; - } - break; - } - case FuncExpr::op_and: { - TimingSense sense1, sense2; - LogicValue value1, value2; - functionSense(expr->left(), input_pin, inst, sense1, value1); - functionSense(expr->right(), input_pin, inst, sense2, value2); - if (value1 == LogicValue::zero || value2 == LogicValue::zero) { - sense = TimingSense::none; - value = LogicValue::zero; - } - else if (value1 == LogicValue::one) { - sense = sense2; - value = value2; - } - else if (value2 == LogicValue::one) { - sense = sense1; - value = value1; - } - else { - sense = senseAndOr(sense1, sense2); - value = LogicValue::unknown; - } - break; - } - case FuncExpr::op_xor: { - TimingSense sense1, sense2; - LogicValue value1, value2; - functionSense(expr->left(), input_pin, inst, sense1, value1); - functionSense(expr->right(), input_pin, inst, sense2, value2); - if ((value1 == LogicValue::zero && value2 == LogicValue::zero) - || (value1 == LogicValue::one && value2 == LogicValue::one)) { - sense = TimingSense::none; - value = LogicValue::zero; - } - else if ((value1 == LogicValue::zero && value2 == LogicValue::one) - || (value1 == LogicValue::one && value2 == LogicValue::zero)) { - sense = TimingSense::none; - value = LogicValue::one; - } - else if (value1 == LogicValue::zero) { - sense = sense2; - value = value2; - } - else if (value1 == LogicValue::one) { - sense = senseNot(sense2); - value = logicNot(value2); - } - else if (value2 == LogicValue::zero) { - sense = sense1; - value = value1; - } - else if (value2 == LogicValue::one) { - sense = senseNot(sense1); - value = logicNot(value1); - } - else { - sense = senseXor(sense1, sense2); - value = logicXor(value1, value2); - } - break; - } - case FuncExpr::op_one: - sense = TimingSense::none; - value = LogicValue::one; - break; - case FuncExpr::op_zero: - sense = TimingSense::none; - value = LogicValue::zero; - break; - } -} - -LogicValue -Sim::evalExpr(const FuncExpr *expr, - const Instance *inst) -{ - switch (expr->op()) { - case FuncExpr::op_port: { - LibertyPort *port = expr->port(); - if (port) { - Pin *pin = network_->findPin(inst, port->name()); - if (pin) - return logicValue(pin); - } - // Internal ports don't have instance pins. - return LogicValue::unknown; - } - case FuncExpr::op_not: - return logicNot(evalExpr(expr->left(), inst)); - case FuncExpr::op_or: - return logicOr(evalExpr(expr->left(),inst), - evalExpr(expr->right(),inst)); - case FuncExpr::op_and: - return logicAnd(evalExpr(expr->left(),inst), - evalExpr(expr->right(),inst)); - case FuncExpr::op_xor: - return logicXor(evalExpr(expr->left(),inst), - evalExpr(expr->right(),inst)); - case FuncExpr::op_one: - return LogicValue::one; - case FuncExpr::op_zero: - return LogicValue::zero; - } - // Prevent warnings from lame compilers. - return LogicValue::zero; -} - -#endif // CUDD - static LogicValue logicNot(LogicValue value) { @@ -682,9 +401,7 @@ Sim::propagateConstants(bool thru_sequentials) void Sim::setConstraintConstPins(LogicValueMap &value_map) { - for (auto pin_value : value_map) { - const Pin *pin = pin_value.first; - LogicValue value = pin_value.second; + for (const auto [pin, value] : value_map) { debugPrint(debug_, "sim", 2, "case pin %s = %c", network_->pathName(pin), logicValueString(value)); @@ -1219,10 +936,9 @@ isModeDisabled(Edge *edge, if (cond_value == LogicValue::zero) { // For a mode value to be disabled by having a value of // logic zero one mode value must logic one. - for (auto name_mode : *mode_def->values()) { - ModeValueDef *value_def1 = name_mode.second; - if (value_def1) { - FuncExpr *cond1 = value_def1->cond(); + 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) { diff --git a/search/Sta.cc b/search/Sta.cc index fc1a6777..06a7429f 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -268,7 +268,6 @@ Sta::Sta() : clk_skews_(nullptr), report_path_(nullptr), power_(nullptr), - link_make_black_boxes_(true), update_genclks_(false), equiv_cells_(nullptr), graph_sdc_annotated_(false), @@ -349,8 +348,7 @@ Sta::updateComponentsState() corners_->copyState(this); levelize_->copyState(this); parasitics_->copyState(this); - if (arc_delay_calc_) - arc_delay_calc_->copyState(this); + arc_delay_calc_->copyState(this); sim_->copyState(this); search_->copyState(this); latches_->copyState(this); @@ -531,8 +529,7 @@ Sta::~Sta() delete search_; delete latches_; delete parasitics_; - if (arc_delay_calc_) - delete arc_delay_calc_; + delete arc_delay_calc_; delete graph_delay_calc_; delete sim_; delete levelize_; @@ -737,29 +734,18 @@ Sta::readNetlistBefore() } bool -Sta::linkDesign(const char *top_cell_name) +Sta::linkDesign(const char *top_cell_name, + bool make_black_boxes) { clear(); Stats stats(debug_, report_); bool status = network_->linkNetwork(top_cell_name, - link_make_black_boxes_, + make_black_boxes, report_); stats.report("Link"); return status; } -bool -Sta::linkMakeBlackBoxes() const -{ - return link_make_black_boxes_; -} - -void -Sta::setLinkMakeBlackBoxes(bool make) -{ - link_make_black_boxes_ = make; -} - //////////////////////////////////////////////////////////////// void @@ -776,8 +762,7 @@ Sta::setAnalysisType(AnalysisType analysis_type) { if (analysis_type != sdc_->analysisType()) { sdc_->setAnalysisType(analysis_type); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); search_->deletePathGroups(); corners_->analysisTypeChanged(); if (graph_) @@ -797,8 +782,7 @@ Sta::setOperatingConditions(OperatingConditions *op_cond, { sdc_->setOperatingConditions(op_cond, min_max); corners_->operatingConditionsChanged(); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } const Pvt * @@ -1000,8 +984,7 @@ void Sta::setWireloadMode(WireloadMode mode) { sdc_->setWireloadMode(mode); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } void @@ -1009,8 +992,7 @@ Sta::setWireload(Wireload *wireload, const MinMaxAll *min_max) { sdc_->setWireload(wireload, min_max); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } void @@ -1018,8 +1000,7 @@ Sta::setWireloadSelection(WireloadSelection *selection, const MinMaxAll *min_max) { sdc_->setWireloadSelection(selection, min_max); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } void @@ -1148,8 +1129,7 @@ void Sta::setPropagatedClock(Clock *clk) { sdc_->setPropagatedClock(clk); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); clkPinsInvalid(); } @@ -1157,8 +1137,7 @@ void Sta::removePropagatedClock(Clock *clk) { sdc_->removePropagatedClock(clk); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); clkPinsInvalid(); } @@ -1166,8 +1145,7 @@ void Sta::setPropagatedClock(Pin *pin) { sdc_->setPropagatedClock(pin); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); clkPinsInvalid(); } @@ -1175,8 +1153,7 @@ void Sta::removePropagatedClock(Pin *pin) { sdc_->removePropagatedClock(pin); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); clkPinsInvalid(); } @@ -1609,8 +1586,7 @@ Sta::disableAfter() { // Levelization respects disabled edges. levelize_->invalid(); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } //////////////////////////////////////////////////////////////// @@ -1837,8 +1813,7 @@ Sta::setLogicValue(Pin *pin, // fails. This could be more incremental if the graph delay // calculator searched thru disabled edges but ignored their // results. - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } void @@ -1854,8 +1829,7 @@ Sta::setCaseAnalysis(Pin *pin, // 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. - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } void @@ -1870,8 +1844,7 @@ Sta::removeCaseAnalysis(Pin *pin) // 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. - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } void @@ -2131,8 +2104,7 @@ void Sta::constraintsChanged() { levelize_->invalid(); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); sim_->constantsInvalid(); } @@ -2215,10 +2187,8 @@ Sta::pocvEnabled() const void Sta::setPocvEnabled(bool enabled) { - if (enabled != pocv_enabled_) { - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); - } + if (enabled != pocv_enabled_) + delaysInvalid(); pocv_enabled_ = enabled; updateComponentsState(); } @@ -2258,8 +2228,7 @@ Sta::setPresetClrArcsEnabled(bool enable) { if (sdc_->presetClrArcsEnabled() != enable) { levelize_->invalid(); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } sdc_->setPresetClrArcsEnabled(enable); } @@ -2274,8 +2243,7 @@ void Sta::setCondDefaultArcsEnabled(bool enabled) { if (sdc_->condDefaultArcsEnabled() != enabled) { - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); sdc_->setCondDefaultArcsEnabled(enabled); } } @@ -2291,8 +2259,7 @@ Sta::setBidirectInstPathsEnabled(bool enabled) { if (sdc_->bidirectInstPathsEnabled() != enabled) { levelize_->invalid(); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); sdc_->setBidirectInstPathsEnabled(enabled); } } @@ -2307,8 +2274,7 @@ void Sta::setBidirectNetPathsEnabled(bool enabled) { if (sdc_->bidirectNetPathsEnabled() != enabled) { - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); sdc_->setBidirectNetPathsEnabled(enabled); } } @@ -2656,7 +2622,7 @@ Sta::findClkDelays(const Clock *clk, //////////////////////////////////////////////////////////////// void -Sta::delaysInvalid() +Sta::delaysInvalid() const { graph_delay_calc_->delaysInvalid(); search_->arrivalsInvalid(); @@ -3106,8 +3072,8 @@ Sta::vertexSlacks(Vertex *vertex, Slack (&slacks)[RiseFall::index_count][MinMax::index_count]) { findRequired(vertex); - for(int rf_index : RiseFall::rangeIndex()) { - for(MinMax *min_max : MinMax::range()) { + for (int rf_index : RiseFall::rangeIndex()) { + for (const MinMax *min_max : MinMax::range()) { slacks[rf_index][min_max->index()] = MinMax::min()->initValue(); } } @@ -3327,8 +3293,7 @@ Sta::setArcDelayCalc(const char *delay_calc_name) arc_delay_calc_ = makeDelayCalc(delay_calc_name, sta_); // Update pointers to arc_delay_calc. updateComponentsState(); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } void @@ -3639,7 +3604,7 @@ Sta::setAnnotatedSlew(Vertex *vertex, void Sta::writeSdf(const char *filename, - Corner *corner, + const Corner *corner, char divider, bool include_typ, int digits, @@ -3656,7 +3621,7 @@ void Sta::removeDelaySlewAnnotations() { graph_->removeDelaySlewAnnotations(); - graph_delay_calc_->delaysInvalid(); + delaysInvalid(); } LogicValue @@ -3768,7 +3733,7 @@ void Sta::removeNetLoadCaps() const { sdc_->removeNetLoadCaps(); - graph_delay_calc_->delaysInvalid(); + delaysInvalid(); } void @@ -3920,8 +3885,7 @@ Sta::readSpef(const char *filename, pin_cap_included, keep_coupling_caps, coupling_cap_factor, reduce, corner, min_max, this); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); return success; } @@ -4023,8 +3987,7 @@ void Sta::deleteParasitics() { parasitics_->deleteParasitics(); - graph_delay_calc_->delaysInvalid(); - search_->arrivalsInvalid(); + delaysInvalid(); } Parasitic * diff --git a/search/Tag.cc b/search/Tag.cc index b50837f4..81942b88 100644 --- a/search/Tag.cc +++ b/search/Tag.cc @@ -97,11 +97,12 @@ Tag::asString(bool report_index, string result; if (report_index) - result += std::to_string(index_) + " "; + result += std::to_string(index_); if (report_rf_min_max) { const RiseFall *rf = transition(); PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index_); + result += " "; result += rf->asString(); result += " "; result += path_ap->pathMinMax()->asString(); @@ -109,6 +110,7 @@ Tag::asString(bool report_index, result += std::to_string(path_ap_index_); } + result += " "; const ClockEdge *clk_edge = clkEdge(); if (clk_edge) result += clk_edge->name(); diff --git a/search/TagGroup.cc b/search/TagGroup.cc index e922d05c..bb0b2a6f 100644 --- a/search/TagGroup.cc +++ b/search/TagGroup.cc @@ -90,7 +90,7 @@ void TagGroup::report(const StaState *sta) const { Report *report = sta->report(); - report->reportLine("Group %u hash = %lu", index_, hash_); + report->reportLine("Group %u hash = %zu", index_, hash_); arrivalMapReport(arrival_map_, sta); } diff --git a/search/WritePathSpice.cc b/spice/WritePathSpice.cc similarity index 94% rename from search/WritePathSpice.cc rename to spice/WritePathSpice.cc index 93b0970e..ab6509fe 100644 --- a/search/WritePathSpice.cc +++ b/spice/WritePathSpice.cc @@ -40,7 +40,7 @@ #include "PathRef.hh" #include "PathExpanded.hh" #include "StaState.hh" -#include "Sim.hh" +#include "search/Sim.hh" #include "WriteSpice.hh" namespace sta { @@ -137,6 +137,8 @@ private: // Input clock waveform cycles. int clk_cycle_count_; + InstanceSet written_insts_; + using WriteSpice::writeHeader; using WriteSpice::writePrintStmt; using WriteSpice::writeSubckts; @@ -177,12 +179,14 @@ WritePathSpice::WritePathSpice(Path *path, CircuitSim ckt_sim, const StaState *sta) : WriteSpice(spice_filename, subckt_filename, lib_subckt_filename, - model_filename, power_name, gnd_name, ckt_sim, sta), + model_filename, power_name, gnd_name, ckt_sim, + path->dcalcAnalysisPt(sta), sta), path_(path), path_expanded_(sta), - clk_cycle_count_(3) + clk_cycle_count_(3), + written_insts_(network_) { - initPowerGnd( path_->dcalcAnalysisPt(this)); + initPowerGnd(); } void @@ -253,18 +257,17 @@ float WritePathSpice::pathMaxTime() { float max_time = 0.0; - DcalcAPIndex dcalc_ap_index = path_->dcalcAnalysisPt(this)->index(); for (size_t i = 0; i < path_expanded_.size(); i++) { PathRef *path = path_expanded_.path(i); const RiseFall *rf = path->transition(this); Vertex *vertex = path->vertex(this); - float path_max_slew = railToRailSlew(findSlew(vertex,rf,nullptr,dcalc_ap_index),rf); + float path_max_slew = railToRailSlew(findSlew(vertex,rf,nullptr), rf); if (vertex->isDriver(network_)) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *load = edge->to(graph_); - float load_slew = railToRailSlew(findSlew(load, rf, nullptr, dcalc_ap_index),rf); + float load_slew = railToRailSlew(findSlew(load, rf, nullptr), rf); if (load_slew > path_max_slew) path_max_slew = load_slew; } @@ -387,9 +390,8 @@ float WritePathSpice::findSlew(Path *path) { Vertex *vertex = path->vertex(this); - DcalcAPIndex dcalc_ap_index = path->dcalcAnalysisPt(this)->index(); const RiseFall *rf = path->transition(this); - return findSlew(vertex, rf, nullptr, dcalc_ap_index); + return findSlew(vertex, rf, nullptr); } float @@ -398,8 +400,7 @@ WritePathSpice::findSlew(Path *path, TimingArc *next_arc) { Vertex *vertex = path->vertex(this); - DcalcAPIndex dcalc_ap_index = path->dcalcAnalysisPt(this)->index(); - return findSlew(vertex, rf, next_arc, dcalc_ap_index); + return findSlew(vertex, rf, next_arc); } //////////////////////////////////////////////////////////////// @@ -496,7 +497,7 @@ WritePathSpice::writeGateStage(Stage stage) const char *load_pin_name = stageLoadPinName(stage); string subckt_name = "stage" + std::to_string(stage); - Instance *inst = stageInstance(stage); + const Instance *inst = stageInstance(stage); LibertyPort *input_port = stageGateInputPort(stage); LibertyPort *drvr_port = stageDrvrPort(stage); @@ -511,10 +512,9 @@ WritePathSpice::writeGateStage(Stage stage) network_->pathName(inst), input_port->name(), drvr_port->name()); - writeSubcktInst(input_pin); + writeSubcktInst(inst); PathRef *drvr_path = stageDrvrPath(stage); - DcalcAPIndex dcalc_ap_index = drvr_path->dcalcAnalysisPt(this)->index(); const RiseFall *drvr_rf = drvr_path->transition(this); Edge *gate_edge = stageGateEdge(stage); @@ -523,11 +523,20 @@ WritePathSpice::writeGateStage(Stage stage) gatePortValues(input_pin, drvr_pin, drvr_rf, gate_edge, port_values, is_clked); - const Clock *clk = (is_clked) ? stageDrvrPath(stage)->clock(this) : nullptr; - writeSubcktInstVoltSrcs(input_pin, port_values, clk, dcalc_ap_index); + PinSet inputs(network_); + inputs.insert(input_pin); + writeSubcktInstVoltSrcs(inst, port_values, inputs); streamPrint(spice_stream_, "\n"); - writeSubcktInstLoads(drvr_pin, load_pin); + PinSet drvr_loads(network_); + PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + const Pin *load_pin = pin_iter->next(); + drvr_loads.insert(load_pin); + } + delete pin_iter; + + writeSubcktInstLoads(drvr_pin, load_pin, drvr_loads, written_insts_); writeStageParasitics(stage); streamPrint(spice_stream_, ".ends\n\n"); } @@ -538,9 +547,14 @@ WritePathSpice::writeStageParasitics(Stage stage) PathRef *drvr_path = stageDrvrPath(stage); DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(this); ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + const Pin *drvr_pin = stageDrvrPin(stage); + const Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + if (parasitic == nullptr) { + const RiseFall *drvr_rf = drvr_path->transition(this); + parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, parasitic_ap); + } NetSet coupling_nets; - writeDrvrParasitics(stageDrvrPin(stage), drvr_path->transition(this), - coupling_nets, parasitic_ap); + writeDrvrParasitics(drvr_pin, parasitic, coupling_nets); } //////////////////////////////////////////////////////////////// diff --git a/include/sta/WritePathSpice.hh b/spice/WritePathSpice.hh similarity index 100% rename from include/sta/WritePathSpice.hh rename to spice/WritePathSpice.hh diff --git a/search/WriteSpice.cc b/spice/WriteSpice.cc similarity index 91% rename from search/WriteSpice.cc rename to spice/WriteSpice.cc index 94dc99f9..84d7b656 100644 --- a/search/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "WriteSpice.hh" +#include "spice/WriteSpice.hh" + +#include // swap #include "Debug.hh" #include "Units.hh" @@ -27,7 +29,7 @@ #include "Liberty.hh" #include "Network.hh" #include "Graph.hh" -#include "Sim.hh" +#include "search/Sim.hh" #include "Clock.hh" #include "PathVertex.hh" #include "DcalcAnalysisPt.hh" @@ -36,6 +38,8 @@ namespace sta { using std::ifstream; +using std::swap; +using std::set; Net * pinNet(const Pin *pin, @@ -77,6 +81,7 @@ WriteSpice::WriteSpice(const char *spice_filename, const char *power_name, const char *gnd_name, CircuitSim ckt_sim, + const DcalcAnalysisPt *dcalc_ap, const StaState *sta) : StaState(sta), spice_filename_(spice_filename), @@ -86,23 +91,23 @@ WriteSpice::WriteSpice(const char *spice_filename, power_name_(power_name), gnd_name_(gnd_name), ckt_sim_(ckt_sim), + dcalc_ap_(dcalc_ap), default_library_(network_->defaultLibertyLibrary()), short_ckt_resistance_(.0001), cap_index_(1), res_index_(1), volt_index_(1), - next_node_index_(1), bdd_(sta) { } void -WriteSpice::initPowerGnd(const DcalcAnalysisPt *dcalc_ap) +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 = dcalc_ap_->operatingConditions(); if (op_cond == nullptr) op_cond = network_->defaultLibertyLibrary()->defaultOperatingConditions(); power_voltage_ = op_cond->voltage(); @@ -182,7 +187,7 @@ WriteSpice::writeGnuplotFile(StdStringSeq &node_nanes) csv_filename.c_str()); for (size_t i = 3; i <= node_nanes.size() + 1; i++) { streamPrint(gnuplot_stream, ",\\\n"); - streamPrint(gnuplot_stream, "'' using 1:%lu with lines", i); + streamPrint(gnuplot_stream, "'' using 1:%zu with lines", i); } streamPrint(gnuplot_stream, "\n"); streamPrint(gnuplot_stream, "pause mouse close\n"); @@ -312,9 +317,8 @@ WriteSpice::findCellSubckts(StdStringSet &cell_names) //////////////////////////////////////////////////////////////// void -WriteSpice::writeSubcktInst(const Pin *input_pin) +WriteSpice::writeSubcktInst(const Instance *inst) { - const Instance *inst = network_->instance(input_pin); const char *inst_name = network_->pathName(inst); LibertyCell *cell = network_->libertyCell(inst); const char *cell_name = cell->name(); @@ -340,23 +344,20 @@ WriteSpice::writeSubcktInst(const Pin *input_pin) // Power/ground and input voltage sources. void -WriteSpice::writeSubcktInstVoltSrcs(const Pin *input_pin, +WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst, LibertyPortLogicValues &port_values, - const Clock *clk, - DcalcAPIndex dcalc_ap_index) + const PinSet &excluded_input_pins) { - const Instance *inst = network_->instance(input_pin); LibertyCell *cell = network_->libertyCell(inst); const char *cell_name = cell->name(); StringVector &spice_port_names = cell_spice_port_names_[cell_name]; - - const LibertyPort *input_port = network_->libertyPort(input_pin); const char *inst_name = network_->pathName(inst); debugPrint(debug_, "write_spice", 2, "subckt %s", cell->name()); for (string subckt_port_sname : spice_port_names) { const char *subckt_port_name = subckt_port_sname.c_str(); LibertyPort *port = cell->findLibertyPort(subckt_port_name); + const Pin *pin = port ? network_->findPin(inst, port) : nullptr; LibertyPgPort *pg_port = cell->findPgPort(subckt_port_name); debugPrint(debug_, "write_spice", 2, " port %s%s", subckt_port_name, @@ -369,10 +370,9 @@ WriteSpice::writeSubcktInstVoltSrcs(const Pin *input_pin, else if (stringEq(subckt_port_name, gnd_name_)) writeVoltageSource(inst_name, subckt_port_name, gnd_voltage_); else if (port - && port != input_port + && excluded_input_pins.find(pin) == excluded_input_pins.end() && port->direction()->isAnyInput()) { // Input voltage to sensitize path from gate input to output. - const Pin *pin = network_->findPin(inst, port); // Look for tie high/low or propagated constant values. LogicValue port_value = sim_->logicValue(pin); if (port_value == LogicValue::unknown) { @@ -395,11 +395,7 @@ WriteSpice::writeSubcktInstVoltSrcs(const Pin *input_pin, power_voltage_); break; case LogicValue::rise: - writeClkedStepSource(pin, RiseFall::rise(), clk, dcalc_ap_index); - - break; case LogicValue::fall: - writeClkedStepSource(pin, RiseFall::fall(), clk, dcalc_ap_index); break; } } @@ -465,34 +461,14 @@ WriteSpice::pgPortVoltage(LibertyPgPort *pg_port) return voltage; } -// PWL voltage source that rises half way into the first clock cycle. -void -WriteSpice::writeClkedStepSource(const Pin *pin, - const RiseFall *rf, - const Clock *clk, - DcalcAPIndex dcalc_ap_index) -{ - Vertex *vertex = graph_->pinLoadVertex(pin); - float slew = findSlew(vertex, rf, nullptr, dcalc_ap_index); - float time = clkWaveformTimeOffset(clk) + clk->period() / 2.0; - writeRampVoltSource(pin, rf, time, slew); -} - -float -WriteSpice::clkWaveformTimeOffset(const Clock *clk) -{ - return clk->period() / 10; -} - //////////////////////////////////////////////////////////////// float WriteSpice::findSlew(Vertex *vertex, const RiseFall *rf, - TimingArc *next_arc, - DcalcAPIndex dcalc_ap_index) + TimingArc *next_arc) { - float slew = delayAsFloat(graph_->slew(vertex, rf, dcalc_ap_index)); + float slew = delayAsFloat(graph_->slew(vertex, rf, dcalc_ap_->index())); if (slew == 0.0 && next_arc) slew = slewAxisMinValue(next_arc); if (slew == 0.0) @@ -504,7 +480,7 @@ WriteSpice::findSlew(Vertex *vertex, float WriteSpice::slewAxisMinValue(TimingArc *arc) { - GateTableModel *gate_model = dynamic_cast(arc->model()); + GateTableModel *gate_model = arc->gateTableModel(dcalc_ap_); if (gate_model) { const TableModel *model = gate_model->delayModel(); const TableAxis *axis1 = model->axis1(); @@ -532,27 +508,20 @@ WriteSpice::slewAxisMinValue(TimingArc *arc) void WriteSpice::writeDrvrParasitics(const Pin *drvr_pin, - const RiseFall *drvr_rf, - const NetSet &aggressor_nets, - const ParasiticAnalysisPt *parasitic_ap) + const Parasitic *parasitic, + const NetSet &coupling_nets) { Net *net = network_->net(drvr_pin); const char *net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin); streamPrint(spice_stream_, "* Net %s\n", net_name); - Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); - node_map_.clear(); - next_node_index_ = 1; - if (parasitic) - writeParasiticNetwork(drvr_pin, parasitic, aggressor_nets); + if (parasitics_->isParasiticNetwork(parasitic)) + writeParasiticNetwork(drvr_pin, parasitic, coupling_nets); + else if (parasitics_->isPiElmore(parasitic)) + writePiElmore(drvr_pin, parasitic); else { - parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, parasitic_ap); - if (parasitic) - writePiElmore(drvr_pin, parasitic); - else { - streamPrint(spice_stream_, "* Net has no parasitics.\n"); - writeNullParasitic(drvr_pin); - } + streamPrint(spice_stream_, "* Net has no parasitics.\n"); + writeNullParasitic(drvr_pin); } } @@ -561,7 +530,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, const Parasitic *parasitic, const NetSet &coupling_nets) { - Set reachable_pins; + set reachable_pins; // Sort resistors for consistent regression results. ParasiticResistorSeq resistors = parasitics_->resistors(parasitic); sort(resistors.begin(), resistors.end(), @@ -575,8 +544,8 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, ParasiticNode *node2 = parasitics_->node2(resistor); streamPrint(spice_stream_, "R%d %s %s %.3e\n", res_index_++, - nodeName(node1), - nodeName(node2), + parasitics_->name(node1), + parasitics_->name(node2), resistance); // Necessary but not sufficient. Need a DFS. @@ -595,7 +564,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, if (pin != drvr_pin && network_->isLoad(pin) && !network_->isHierarchical(pin) - && !reachable_pins.hasKey(pin)) { + && reachable_pins.find(pin) == reachable_pins.end()) { streamPrint(spice_stream_, "R%d %s %s %.3e\n", res_index_++, network_->pathName(drvr_pin), @@ -605,6 +574,7 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, } delete pin_iter; + // Grounded node capacitors. // Sort nodes for consistent regression results. ParasiticNodeSeq nodes = parasitics_->nodes(parasitic); sort(nodes.begin(), nodes.end(), @@ -621,12 +591,12 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, if (cap > 0.0) { streamPrint(spice_stream_, "C%d %s 0 %.3e\n", cap_index_++, - nodeName(node), + parasitics_->name(node), cap); } } - // Sort coupling capacitors consistent regression results. + // Sort coupling capacitors for consistent regression results. ParasiticCapacitorSeq capacitors = parasitics_->capacitors(parasitic); sort(capacitors.begin(), capacitors.end(), [=] (const ParasiticCapacitor *c1, @@ -640,21 +610,21 @@ WriteSpice::writeParasiticNetwork(const Pin *drvr_pin, float cap = parasitics_->value(capacitor); const Net *net1 = node1 ? parasitics_->net(node1, network_) : nullptr; const Net *net2 = node2 ? parasitics_->net(node2, network_) : nullptr; - const ParasiticNode *net_node = nullptr; - const char *coupling_name; - if (net1 == net) { - net_node = node1; - coupling_name = net2 && coupling_nets.hasKey(net2) ? nodeName(node2) : "0"; + if (net2 == net) { + swap(net1, net2); + swap(node1, node2); } - else if (net2 == net) { - net_node = node2; - coupling_name = net1 && coupling_nets.hasKey(net1) ? nodeName(node1) : "0"; - } - if (net_node) + if (net2 && coupling_nets.hasKey(net2)) + // Write half the capacitance because the coupled net will do the same. streamPrint(spice_stream_, "C%d %s %s %.3e\n", cap_index_++, - nodeName(net_node), - coupling_name, + parasitics_->name(node1), + parasitics_->name(node2), + cap * .5); + else + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + cap_index_++, + parasitics_->name(node1), cap); } } @@ -674,27 +644,6 @@ pinNet(const Pin *pin, return net; } -const char * -WriteSpice::nodeName(const ParasiticNode *node) -{ - const Pin *pin = parasitics_->pin(node); - if (pin) - return parasitics_->name(node); - else { - int node_index; - auto index_itr = node_map_.find(node); - if (index_itr == node_map_.end()) { - node_index = next_node_index_++; - node_map_[node] = node_index; - } - else - node_index = index_itr->second; - const Net *net = parasitics_->net(node, network_); - const char *net_name = network_->pathName(net); - return stringPrintTmp("%s:%d", net_name, node_index); - } -} - void WriteSpice::writePiElmore(const Pin *drvr_pin, const Parasitic *parasitic) @@ -1081,21 +1030,23 @@ WriteSpice::seqPortValues(Sequential *seq, LibertyPortLogicValues &port_values) { FuncExpr *data = seq->data(); + // SHOULD choose values for all ports of data to make output rise/fall + // matching rf. LibertyPort *port = onePort(data); if (port) { TimingSense sense = data->portTimingSense(port); switch (sense) { case TimingSense::positive_unate: if (rf == RiseFall::rise()) - port_values[port] = LogicValue::rise; + port_values[port] = LogicValue::one; else - port_values[port] = LogicValue::fall; + port_values[port] = LogicValue::zero; break; case TimingSense::negative_unate: if (rf == RiseFall::rise()) - port_values[port] = LogicValue::fall; + port_values[port] = LogicValue::zero; else - port_values[port] = LogicValue::rise; + port_values[port] = LogicValue::one; break; case TimingSense::non_unate: case TimingSense::none: @@ -1153,20 +1104,25 @@ WriteSpice::drvrLoads(const Pin *drvr_pin) void WriteSpice::writeSubcktInstLoads(const Pin *drvr_pin, - const Pin *exclude) + const Pin *path_load, + const PinSet &excluded_input_pins, + InstanceSet &written_insts) { streamPrint(spice_stream_, "* Load pins\n"); PinSeq drvr_loads = drvrLoads(drvr_pin); // Do not sensitize side load gates. LibertyPortLogicValues port_values; for (const Pin *load_pin : drvr_loads) { - if (load_pin != exclude + const Instance *load_inst = network_->instance(load_pin); + if (load_pin != path_load && network_->direction(load_pin)->isAnyInput() && !network_->isHierarchical(load_pin) - && !network_->isTopLevelPort(load_pin)) { - writeSubcktInst(load_pin); - writeSubcktInstVoltSrcs(load_pin, port_values, nullptr, 0); + && !network_->isTopLevelPort(load_pin) + && !written_insts.hasKey(load_inst)) { + writeSubcktInst(load_inst); + writeSubcktInstVoltSrcs(load_inst, port_values, excluded_input_pins); streamPrint(spice_stream_, "\n"); + written_insts.insert(load_inst); } } } @@ -1264,4 +1220,25 @@ streamPrint(ofstream &stream, va_end(args); } +//////////////////////////////////////////////////////////////// + +// Unused +// PWL voltage source that rises half way into the first clock cycle. +void +WriteSpice::writeClkedStepSource(const Pin *pin, + const RiseFall *rf, + const Clock *clk) +{ + Vertex *vertex = graph_->pinLoadVertex(pin); + float slew = findSlew(vertex, rf, nullptr); + float time = clkWaveformTimeOffset(clk) + clk->period() / 2.0; + writeRampVoltSource(pin, rf, time, slew); } + +float +WriteSpice::clkWaveformTimeOffset(const Clock *clk) +{ + return clk->period() / 10; +} + +} // namespace diff --git a/search/WriteSpice.hh b/spice/WriteSpice.hh similarity index 90% rename from search/WriteSpice.hh rename to spice/WriteSpice.hh index 9d45d55f..5a5f8137 100644 --- a/search/WriteSpice.hh +++ b/spice/WriteSpice.hh @@ -50,10 +50,11 @@ public: const char *power_name, const char *gnd_name, CircuitSim ckt_sim, + const DcalcAnalysisPt *dcalc_ap, const StaState *sta); protected: - void initPowerGnd(const DcalcAnalysisPt *dcalc_ap); + void initPowerGnd(); void writeHeader(string &title, float max_time, float time_step); @@ -63,11 +64,10 @@ protected: void findCellSubckts(StdStringSet &cell_names); void recordSpicePortNames(const char *cell_name, StringVector &tokens); - void writeSubcktInst(const Pin *input_pin); - void writeSubcktInstVoltSrcs(const Pin *input_pin, + void writeSubcktInst(const Instance *inst); + void writeSubcktInstVoltSrcs(const Instance *inst, LibertyPortLogicValues &port_values, - const Clock *clk, - DcalcAPIndex dcalc_ap_index); + const PinSet &excluded_input_pins); float pgPortVoltage(LibertyPgPort *pg_port); void writeVoltageSource(const char *inst_name, const char *port_name, @@ -79,20 +79,21 @@ protected: float voltage); void writeClkedStepSource(const Pin *pin, const RiseFall *rf, - const Clock *clk, - DcalcAPIndex dcalc_ap_index); + 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); + void writeDrvrParasitics(const Pin *drvr_pin, + const Parasitic *parasitic, + const NetSet &coupling_nets); void writeParasiticNetwork(const Pin *drvr_pin, const Parasitic *parasitic, const NetSet &aggressor_nets); void writePiElmore(const Pin *drvr_pin, const Parasitic *parasitic); void writeNullParasitic(const Pin *drvr_pin); - const char *nodeName(const ParasiticNode *node); void writeVoltageSource(const char *node_name, float voltage); @@ -126,8 +127,7 @@ protected: const char *spiceTrans(const RiseFall *rf); float findSlew(Vertex *vertex, const RiseFall *rf, - TimingArc *next_arc, - DcalcAPIndex dcalc_ap_index); + TimingArc *next_arc); float slewAxisMinValue(TimingArc *arc); float clkWaveformTimeOffset(const Clock *clk); @@ -151,7 +151,9 @@ protected: // Return values. LibertyPortLogicValues &port_values); void writeSubcktInstLoads(const Pin *drvr_pin, - const Pin *exclude); + const Pin *path_load, + const PinSet &excluded_input_pins, + InstanceSet &written_insts); PinSeq drvrLoads(const Pin *drvr_pin); void writeSubcktInstVoltSrcs(); string replaceFileExt(string filename, @@ -164,6 +166,7 @@ protected: const char *power_name_; const char *gnd_name_; CircuitSim ckt_sim_; + const DcalcAnalysisPt *dcalc_ap_; ofstream spice_stream_; LibertyLibrary *default_library_; @@ -177,8 +180,6 @@ protected: int cap_index_; int res_index_; int volt_index_; - ParasiticNodeMap node_map_; - int next_node_index_; CellSpicePortNames cell_spice_port_names_; Bdd bdd_; }; diff --git a/spice/WriteSpice.i b/spice/WriteSpice.i new file mode 100644 index 00000000..9115a9bf --- /dev/null +++ b/spice/WriteSpice.i @@ -0,0 +1,43 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + +%module write_gate_spice + +%{ + +#include "spice/WritePathSpice.hh" + +%} + +%inline %{ + +void +write_path_spice_cmd(PathRef *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, + CircuitSim ckt_sim) +{ + Sta *sta = Sta::sta(); + writePathSpice(path, spice_filename, subckt_filename, + lib_subckt_filename, model_filename, + power_name, gnd_name, ckt_sim, sta); +} + +%} // inline diff --git a/spice/WriteSpice.tcl b/spice/WriteSpice.tcl new file mode 100644 index 00000000..200df1e8 --- /dev/null +++ b/spice/WriteSpice.tcl @@ -0,0 +1,279 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (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 . + +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\ + [-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} \ + flags {} + + if { [info exists keys(-spice_directory)] } { + set spice_dir [file nativename $keys(-spice_directory)] + if { ![file exists $spice_dir] } { + sta_error 1920 "Directory $spice_dir not found." + } + if { ![file isdirectory $spice_dir] } { + sta_error 1921 "$spice_dir is not a directory." + } + if { ![file writable $spice_dir] } { + sta_error 1922 "Cannot write in $spice_dir." + } + } else { + sta_error 1923 "No -spice_directory specified." + } + + if { [info exists keys(-lib_subckt_file)] } { + set lib_subckt_file [file nativename $keys(-lib_subckt_file)] + if { ![file readable $lib_subckt_file] } { + sta_error 1924 "-lib_subckt_file $lib_subckt_file is not readable." + } + } else { + sta_error 1925 "No -lib_subckt_file specified." + } + + if { [info exists keys(-model_file)] } { + set model_file [file nativename $keys(-model_file)] + if { ![file readable $model_file] } { + sta_error 1926 "-model_file $model_file is not readable." + } + } else { + sta_error 1927 "No -model_file specified." + } + + if { [info exists keys(-power)] } { + set power $keys(-power) + } else { + sta_error 1928 "No -power specified." + } + + if { [info exists keys(-ground)] } { + set ground $keys(-ground) + } else { + sta_error 1929 "No -ground specified." + } + + set ckt_sim [parse_ckt_sim_key keys] + + if { ![info exists keys(-path_args)] } { + sta_error 1930 "No -path_args specified." + } + set path_args $keys(-path_args) + set path_ends [eval [concat find_timing_paths $path_args]] + if { $path_ends == {} } { + sta_error 1931 "No paths found for -path_args $path_args." + } else { + set path_index 1 + foreach path_end $path_ends { + set path [$path_end path] + set path_name "path_$path_index" + set spice_file [file join $spice_dir "$path_name.sp"] + set subckt_file [file join $spice_dir "$path_name.subckt"] + write_path_spice_cmd $path $spice_file $subckt_file \ + $lib_subckt_file $model_file $power $ground $ckt_sim + incr path_index + } + } +} + +set ::ckt_sims {hspice ngspice xyce} + +proc parse_ckt_sim_key { keys_var } { + upvar 1 $keys_var keys + global ckt_sims + + set ckt_sim "ngspice" + if { [info exists keys(-simulator)] } { + set ckt_sim [file nativename $keys(-simulator)] + if { [lsearch $ckt_sims $ckt_sim] == -1 } { + sta_error 1910 "Unknown circuit simulator" + } + } + return $ckt_sim +} + +################################################################ + +define_cmd_args "write_gate_spice" \ + { -gates {{instance input_port driver_port edge [delay]}...}\ + -spice_filename spice_filename\ + -lib_subckt_file lib_subckts_file\ + -model_file model_file\ + -power power\ + -ground ground\ + [-simulator hspice|ngspice|xyce]\ + [-corner corner]\ + [-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}\ + flags {-measure_stmts -min -max} + + if { [info exists keys(-gates)] } { + set gates $keys(-gates) + } else { + sta_error 1932 "Missing -gates argument." + } + if { [info exists keys(-spice_filename)] } { + set spice_file [file nativename $keys(-spice_filename)] + set spice_dir [file dirname $spice_file] + if { ![file writable $spice_dir] } { + sta_error 1903 "Cannot write $spice_dir." + } + } else { + sta_error 1904 "No -spice_filename specified." + } + + if { [info exists keys(-lib_subckt_file)] } { + set lib_subckt_file [file nativename $keys(-lib_subckt_file)] + if { ![file readable $lib_subckt_file] } { + sta_error 1905 "-lib_subckt_file $lib_subckt_file is not readable." + } + } else { + sta_error 1906 "No -lib_subckt_file specified." + } + + if { [info exists keys(-model_file)] } { + set model_file [file nativename $keys(-model_file)] + if { ![file readable $model_file] } { + sta_error 1907 "-model_file $model_file is not readable." + } + } else { + sta_error 1908 "No -model_file specified." + } + + if { [info exists keys(-power)] } { + set power $keys(-power) + } else { + sta_error 1909 "No -power specified." + } + + if { [info exists keys(-ground)] } { + set ground $keys(-ground) + } else { + sta_error 1915 "No -ground specified." + } + + set ckt_sim [parse_ckt_sim_key keys] + + set corner [parse_corner keys] + set min_max [parse_min_max_flags flags] + check_argc_eq0 "write_gate_spice" $args + + set spice_dir [file dirname $spice_file] + set spice_root [file rootname [file tail $spice_file]] + 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 +} + +################################################################ + +# plot_pins defaults to input_pin, driver_pina and load pins for each driver. +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]} + +proc write_gate_gnuplot { args } { + parse_key_args "write_gate_gnuplot" args \ + keys {-gates -plot_pins -plot_basename -spice_waveforms -corner} \ + flags {-min -max} + + if { [info exists keys(-gates)] } { + set gates $keys(-gates) + } else { + sta_error 1933 "Missing -gates argument." + } + if { [info exists keys(-plot_pins)] } { + set plot_pins [get_port_pins_error "-plot_pins" $keys(-plot_pins)] + } else { + set plot_pins {} + set plot_all_loads 0 + set gate_idx 0 + foreach gate $gates { + set in_pin [parse_gate_in_pin $gate] + set drvr_pin [parse_gate_drvr_pin $gate] + lappend plot_pins $in_pin + lappend plot_pins $drvr_pin + # Only plot driver loads. + if { $plot_all_loads || $gate_idx == 0 } { + set pin_iter [$drvr_pin connected_pin_iterator] + while { [$pin_iter has_next] } { + set pin [$pin_iter next] + if { [$pin is_load] } { + lappend plot_pins $pin + } + } + $pin_iter finish + } + incr gate_idx + } + } + + if { [info exists keys(-plot_basename)] } { + set plot_base [file nativename $keys(-plot_basename)] + set plot_dir [file dirname $plot_base] + if { ![file writable $plot_dir] } { + sta_error 1913 "Cannot write $plot_dir." + } + } else { + sta_error 1914 "No -plot_basename specified." + } + set gnuplot_filename "${plot_base}.gnuplot" + set csv_filename "${plot_base}.csv" + + set sim_wave_filename "" + if { [info exists keys(-spice_waveforms)] } { + set sim_wave_filename $keys(-spice_waveforms) + } + + set corner [parse_corner 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 +} + +proc parse_gate_drvr_pin { gate_arg } { + lassign $gate_arg inst_name in_port_name in_rf drvr_port_name drvr_rf + set inst [get_instance_error "instance" $inst_name] + set drvr_pin [$inst find_pin $drvr_port_name] + return $drvr_pin +} + +proc parse_gate_in_pin { gate_arg } { + lassign $gate_arg inst_name in_port_name in_rf drvr_port_name drvr_rf + set inst [get_instance_error "instance" $inst_name] + set in_pin [$inst find_pin $in_port_name] + return $in_pin +} + +# sta namespace end. +} diff --git a/spice/Xyce.cc b/spice/Xyce.cc new file mode 100644 index 00000000..7635fe6a --- /dev/null +++ b/spice/Xyce.cc @@ -0,0 +1,77 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + + +#include "Xyce.hh" + +#include +#include +#include + +#include "Error.hh" + +namespace sta { + +using std::string; +using std::ifstream; +using std::getline; +using std::stringstream; +using std::vector; +using std::make_shared; + +void +readXyceCsv(const char *csv_filename, + // Return values. + StdStringSeq &titles, + WaveformSeq &waveforms) +{ + ifstream file(csv_filename); + if (file.is_open()) { + string line; + + // Read the header line. + getline(file, line); + stringstream ss(line); + string field; + size_t col = 0; + while (getline(ss, field, ',')) { + // Skip TIME title. + if (col > 0) + titles.push_back(field); + col++; + } + + vector values(titles.size() + 1); + while (getline(file, line)) { + stringstream ss(line); + size_t col = 0; + while (getline(ss, field, ',')) { + float value = std::stof(field); + values[col].push_back(value); + col++; + } + } + file.close(); + TableAxisPtr time_axis = make_shared(TableAxisVariable::time, + new FloatSeq(values[0])); + for (size_t var = 1; var < values.size(); var++) + waveforms.emplace_back(new FloatSeq(values[var]), time_axis); + } + else + throw FileNotReadable(csv_filename); +} + +} // namespace diff --git a/spice/Xyce.hh b/spice/Xyce.hh new file mode 100644 index 00000000..94b84693 --- /dev/null +++ b/spice/Xyce.hh @@ -0,0 +1,35 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + +#pragma once + +#include +#include + +#include "TableModel.hh" + +namespace sta { + +typedef std::vector StdStringSeq; +typedef std::vector WaveformSeq; + +void +readXyceCsv(const char *csv_filename, + // Return values. + StdStringSeq &titles, + WaveformSeq &waveforms); + +} // namespace diff --git a/tcl/CmdArgs.tcl b/tcl/CmdArgs.tcl index 8fed92ee..24f4da20 100644 --- a/tcl/CmdArgs.tcl +++ b/tcl/CmdArgs.tcl @@ -400,12 +400,21 @@ proc parse_corner { keys_var } { upvar 1 $keys_var keys if { [info exists keys(-corner)] } { - set corner_name $keys(-corner) - set corner [find_corner $corner_name] - if { $corner == "NULL" } { - sta_error 102 "$corner_name is not the name of process 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 + } else { + sta_error 144 "corner object type '$object_type' is not a corner." + } } else { - return $corner + set corner [find_corner $corner_arg] + if { $corner == "NULL" } { + sta_error 102 "$corner_arg is not the name of process corner." + } else { + return $corner + } } } elseif { [multi_corner] } { sta_error 103 "-corner keyword required with multi-corner analysis." @@ -510,7 +519,7 @@ proc parse_min_max_all_flags { flags_var } { } elseif { [info exists flags(-max)] && ![info exists flags(-min)] } { return "max" } else { - return "all" + return "min_max" } } @@ -518,13 +527,13 @@ proc parse_min_max_all_flags { flags_var } { proc parse_min_max_all_check_flags { flags_var } { upvar 1 $flags_var flags if { [info exists flags(-min)] && [info exists flags(-max)] } { - return "all" + return "min_max" } elseif { [info exists flags(-min)] && ![info exists flags(-max)] } { return "min" } elseif { [info exists flags(-max)] && ![info exists flags(-min)] } { return "max" } else { - return "all" + return "min_max" } } @@ -550,7 +559,7 @@ proc parse_early_late_all_flags { flags_var } { } elseif { [info exists flags(-late)] && ![info exists flags(-early)] } { return "max" } else { - return "all" + return "min_max" } } diff --git a/tcl/CmdUtil.tcl b/tcl/CmdUtil.tcl index 70ebff61..34f50db1 100644 --- a/tcl/CmdUtil.tcl +++ b/tcl/CmdUtil.tcl @@ -118,6 +118,17 @@ proc report_units { args } { } } +proc write_units_json { jsonfile } { + set f [open $jsonfile w] + puts $f "{" + foreach unit {"time" "capacitance" "resistance" "voltage" "current" "power"} { + puts $f " \"$unit\": \"[unit_scaled_suffix $unit]\"," + } + puts $f " \"distance\": \"[unit_scaled_suffix distance]\"" + puts $f "}" + close $f +} + ################################################################ define_cmd_args "set_cmd_units" \ @@ -156,10 +167,10 @@ proc set_unit_values { unit key suffix key_var } { set scale [unit_prefix_scale $unit $prefix ] set_cmd_unit_scale $unit $scale } else { - sta_error 343 "unknown unit $unit prefix '${arg_prefix}'." + sta_error 166 "unknown unit $unit prefix '${arg_prefix}'." } } else { - sta_error 501 "incorrect unit suffix '$arg_suffix'." + sta_error 167 "incorrect unit suffix '$arg_suffix'." } if [info exists keys(-digits)] { set_cmd_unit_digits $unit $keys(-digits) diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i deleted file mode 100644 index 26b9ac97..00000000 --- a/tcl/StaTcl.i +++ /dev/null @@ -1,5039 +0,0 @@ -%module sta - -%{ - -// OpenSTA, Static Timing Analyzer -// Copyright (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 . - -//////////////////////////////////////////////////////////////// -// -// Most of the TCL SWIG interface code is in this file. This and any -// optional interface code is %included into a final interface file -// used by the application. -// -// Define TCL methods for each network object. This works despite the -// fact that the underlying implementation does not have class methods -// corresponding to the TCL methods defined here. -// -// Note the function name changes from sta naming convention -// (lower/capitalize) to TCL naming convention (lower/underscore). -// -//////////////////////////////////////////////////////////////// - -#include "Machine.hh" -#include "StaConfig.hh" // STA_VERSION -#include "Stats.hh" -#include "Report.hh" -#include "Error.hh" -#include "StringUtil.hh" -#include "PatternMatch.hh" -#include "MinMax.hh" -#include "Fuzzy.hh" -#include "FuncExpr.hh" -#include "Units.hh" -#include "Transition.hh" -#include "TimingRole.hh" -#include "TimingArc.hh" -#include "TableModel.hh" -#include "Liberty.hh" -#include "LibertyWriter.hh" -#include "EquivCells.hh" -#include "Wireload.hh" -#include "PortDirection.hh" -#include "Network.hh" -#include "Clock.hh" -#include "PortDelay.hh" -#include "ExceptionPath.hh" -#include "Sdc.hh" -#include "Graph.hh" -#include "DelayCalc.hh" -#include "DcalcAnalysisPt.hh" -#include "Corner.hh" -#include "PathVertex.hh" -#include "PathRef.hh" -#include "PathExpanded.hh" -#include "PathEnd.hh" -#include "PathGroup.hh" -#include "PathAnalysisPt.hh" -#include "Property.hh" -#include "WritePathSpice.hh" -#include "Search.hh" -#include "Sta.hh" -#include "search/Tag.hh" -#include "search/CheckTiming.hh" -#include "search/CheckMinPulseWidths.hh" -#include "search/Levelize.hh" -#include "search/ReportPath.hh" - -namespace sta { - -//////////////////////////////////////////////////////////////// -// -// C++ helper functions used by the interface functions. -// These are not visible in the TCL API. -// -//////////////////////////////////////////////////////////////// - -typedef MinPulseWidthCheckSeq::Iterator MinPulseWidthCheckSeqIterator; - -// Get the network for commands. -Network * -cmdNetwork() -{ - return Sta::sta()->cmdNetwork(); -} - -// Make sure the network has been read and linked. -// Throwing an error means the caller doesn't have to check the result. -Network * -cmdLinkedNetwork() -{ - Network *network = cmdNetwork(); - if (network->isLinked()) - return network; - else { - Report *report = Sta::sta()->report(); - report->error(1570, "no network has been linked."); - return nullptr; - } -} - -// Make sure an editable network has been read and linked. -NetworkEdit * -cmdEditNetwork() -{ - Network *network = cmdLinkedNetwork(); - if (network->isEditable()) - return dynamic_cast(network); - else { - Report *report = Sta::sta()->report(); - report->error(1571, "network does not support edits."); - return nullptr; - } -} - -// Get the graph for commands. -// Throw to cmd level on failure. -Graph * -cmdGraph() -{ - cmdLinkedNetwork(); - return Sta::sta()->ensureGraph(); -} - -} // namespace - -using namespace sta; - -%} - -//////////////////////////////////////////////////////////////// -// -// Empty class definitions to make swig happy. -// Private constructor/destructor so swig doesn't emit them. -// -//////////////////////////////////////////////////////////////// - -class Library -{ -private: - Library(); - ~Library(); -}; - -class LibraryIterator -{ -private: - LibraryIterator(); - ~LibraryIterator(); -}; - -class Cell -{ -private: - Cell(); - ~Cell(); -}; - -class CellPortIterator -{ -private: - CellPortIterator(); - ~CellPortIterator(); -}; - -class LibertyCellPortIterator -{ -private: - LibertyCellPortIterator(); - ~LibertyCellPortIterator(); -}; - -class Port -{ -private: - Port(); - ~Port(); -}; - -class PortMemberIterator -{ -private: - PortMemberIterator(); - ~PortMemberIterator(); -}; - -class LibertyLibrary -{ -private: - LibertyLibrary(); - ~LibertyLibrary(); -}; - -class LibertyLibraryIterator -{ -private: - LibertyLibraryIterator(); - ~LibertyLibraryIterator(); -}; - -class LibertyCell -{ -private: - LibertyCell(); - ~LibertyCell(); -}; - -class LibertyPort -{ -private: - LibertyPort(); - ~LibertyPort(); -}; - -class LibertyPortMemberIterator -{ -private: - LibertyPortMemberIterator(); - ~LibertyPortMemberIterator(); -}; - -class TimingArcSet -{ -private: - TimingArcSet(); - ~TimingArcSet(); -}; - -class TimingArc -{ -private: - TimingArc(); - ~TimingArc(); -}; - -class Wireload -{ -private: - Wireload(); - ~Wireload(); -}; - -class WireloadSelection -{ -private: - WireloadSelection(); - ~WireloadSelection(); -}; - -class Transition -{ -private: - Transition(); - ~Transition(); -}; - -class Instance -{ -private: - Instance(); - ~Instance(); -}; - -class Pin -{ -private: - Pin(); - ~Pin(); -}; - -class Term -{ -private: - Term(); - ~Term(); -}; - -class InstanceChildIterator -{ -private: - InstanceChildIterator(); - ~InstanceChildIterator(); -}; - -class InstancePinIterator -{ -private: - InstancePinIterator(); - ~InstancePinIterator(); -}; - -class InstanceNetIterator -{ -private: - InstanceNetIterator(); - ~InstanceNetIterator(); -}; - -class LeafInstanceIterator -{ -private: - LeafInstanceIterator(); - ~LeafInstanceIterator(); -}; - -class Net -{ -private: - Net(); - ~Net(); -}; - -class NetPinIterator -{ -private: - NetPinIterator(); - ~NetPinIterator(); -}; - -class NetTermIterator -{ -private: - NetTermIterator(); - ~NetTermIterator(); -}; - -class NetConnectedPinIterator -{ -private: - NetConnectedPinIterator(); - ~NetConnectedPinIterator(); -}; - -class PinConnectedPinIterator -{ -private: - PinConnectedPinIterator(); - ~PinConnectedPinIterator(); -}; - -class Clock -{ -private: - Clock(); - ~Clock(); -}; - -class ClockEdge -{ -private: - ClockEdge(); - ~ClockEdge(); -}; - -class Vertex -{ -private: - Vertex(); - ~Vertex(); -}; - -class Edge -{ -private: - Edge(); - ~Edge(); -}; - -class VertexIterator -{ -private: - VertexIterator(); - ~VertexIterator(); -}; - -class VertexInEdgeIterator -{ -private: - VertexInEdgeIterator(); - ~VertexInEdgeIterator(); -}; - -class VertexOutEdgeIterator -{ -private: - VertexOutEdgeIterator(); - ~VertexOutEdgeIterator(); -}; - -class PathRef -{ -private: - PathRef(); - ~PathRef(); -}; - -class PathEnd -{ -private: - PathEnd(); - ~PathEnd(); -}; - -class MinPulseWidthCheck -{ -private: - MinPulseWidthCheck(); - ~MinPulseWidthCheck(); -}; - -class MinPulseWidthCheckSeq -{ -private: - MinPulseWidthCheckSeq(); - ~MinPulseWidthCheckSeq(); -}; - -class MinPulseWidthCheckSeqIterator -{ -private: - MinPulseWidthCheckSeqIterator(); - ~MinPulseWidthCheckSeqIterator(); -}; - -class VertexPathIterator -{ -private: - VertexPathIterator(); - ~VertexPathIterator(); -}; - -class ExceptionFrom -{ -private: - ExceptionFrom(); - ~ExceptionFrom(); -}; - -class ExceptionThru -{ -private: - ExceptionThru(); - ~ExceptionThru(); -}; - -class ExceptionTo -{ -private: - ExceptionTo(); - ~ExceptionTo(); -}; - -class OperatingConditions -{ -private: - OperatingConditions(); - ~OperatingConditions(); -}; - -class Corner -{ -private: - Corner(); - ~Corner(); -}; - -//////////////////////////////////////////////////////////////// -// -// C++ functions visible as TCL functions. -// -//////////////////////////////////////////////////////////////// - -%inline %{ - -float float_inf = INF; -int group_count_max = PathGroup::group_count_max; - -const char * -version() -{ - return STA_VERSION; -} - -const char * -git_sha1() -{ - return STA_GIT_SHA1; -} - -void -report_error(int id, - const char *msg) -{ - Report *report = Sta::sta()->report(); - report->error(id, "%s", msg); -} - -void -report_file_error(int id, - const char *filename, - int line, - const char *msg) -{ - Report *report = Sta::sta()->report(); - report->error(id, filename, line, "%s", msg); -} - -void -report_warn(int id, - const char *msg) -{ - Report *report = Sta::sta()->report(); - report->warn(id, "%s", msg); -} - -void -report_file_warn(int id, - const char *filename, - int line, - const char *msg) -{ - Report *report = Sta::sta()->report(); - report->fileWarn(id, filename, line, "%s", msg); -} - -void -report_line(const char *msg) -{ - Sta *sta = Sta::sta(); - if (sta) - sta->report()->reportLineString(msg); - else - // After sta::delete_all_memory souce -echo prints the cmd file line - printf("%s\n", msg); -} - -void -fflush() -{ - fflush(stdout); - fflush(stderr); -} - -void -redirect_file_begin(const char *filename) -{ - Sta::sta()->report()->redirectFileBegin(filename); -} - -void -redirect_file_append_begin(const char *filename) -{ - Sta::sta()->report()->redirectFileAppendBegin(filename); -} - -void -redirect_file_end() -{ - Sta::sta()->report()->redirectFileEnd(); -} - -void -redirect_string_begin() -{ - Sta::sta()->report()->redirectStringBegin(); -} - -const char * -redirect_string_end() -{ - return Sta::sta()->report()->redirectStringEnd(); -} - -void -log_begin_cmd(const char *filename) -{ - Sta::sta()->report()->logBegin(filename); -} - -void -log_end() -{ - Sta::sta()->report()->logEnd(); -} - -void -set_debug(const char *what, - int level) -{ - Sta::sta()->setDebugLevel(what, level); -} - -bool -is_object(const char *obj) -{ - // _hexaddress_p_type - const char *s = obj; - char ch = *s++; - if (ch != '_') - return false; - while (*s && isxdigit(*s)) - s++; - if ((s - obj - 1) == sizeof(void*) * 2 - && *s && *s++ == '_' - && *s && *s++ == 'p' - && *s && *s++ == '_') { - while (*s && *s != ' ') - s++; - return *s == '\0'; - } - else - return false; -} - -// Assumes is_object is true. -const char * -object_type(const char *obj) -{ - return &obj[1 + sizeof(void*) * 2 + 3]; -} - -bool -is_object_list(const char *list, - const char *type) -{ - const char *s = list; - while (s) { - bool type_match; - const char *next; - objectListNext(s, type, type_match, next); - if (type_match) - s = next; - else - return false; - } - return true; -} - -const char * -rise_short_name() -{ - return RiseFall::rise()->shortName(); -} - -const char * -fall_short_name() -{ - return RiseFall::fall()->shortName(); -} - -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); -} - -bool -clk_thru_tristate_enabled() -{ - return Sta::sta()->clkThruTristateEnabled(); -} - -void -set_clk_thru_tristate_enabled(bool enabled) -{ - Sta::sta()->setClkThruTristateEnabled(enabled); -} - -bool -network_is_linked() -{ - return Sta::sta()->cmdNetwork()->isLinked(); -} - -void -set_path_divider(char divider) -{ - cmdNetwork()->setPathDivider(divider); -} - -void -set_current_instance(Instance *inst) -{ - Sta::sta()->setCurrentInstance(inst); -} - -bool -read_liberty_cmd(char *filename, - Corner *corner, - const MinMaxAll *min_max, - bool infer_latches) -{ - LibertyLibrary *lib = Sta::sta()->readLiberty(filename, corner, min_max, - infer_latches); - return (lib != nullptr); -} - -bool -set_min_library_cmd(char *min_filename, - char *max_filename) -{ - return Sta::sta()->setMinLibrary(min_filename, max_filename); -} - -void -write_liberty_cmd(LibertyLibrary *library, - char *filename) -{ - writeLiberty(library, filename, Sta::sta()); -} - -Library * -find_library(const char *name) -{ - return cmdNetwork()->findLibrary(name); -} - -LibraryIterator * -library_iterator() -{ - return cmdNetwork()->libraryIterator(); -} - -LibertyLibrary * -find_liberty(const char *name) -{ - return cmdNetwork()->findLiberty(name); -} - -LibertyLibraryIterator * -liberty_library_iterator() -{ - return cmdNetwork()->libertyLibraryIterator(); -} - -LibertyCell * -find_liberty_cell(const char *name) -{ - return cmdNetwork()->findLibertyCell(name); -} - -CellSeq -find_cells_matching(const char *pattern, - bool regexp, - bool nocase) -{ - Network *network = cmdNetwork(); - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - CellSeq matches; - LibraryIterator *lib_iter = network->libraryIterator(); - while (lib_iter->hasNext()) { - Library *lib = lib_iter->next(); - CellSeq lib_matches = network->findCellsMatching(lib, &matcher); - for (Cell *match : lib_matches) - matches.push_back(match); - } - delete lib_iter; - return matches; -} - -LibertyCellSeq * -find_library_buffers(LibertyLibrary *library) -{ - return library->buffers(); -} - -void -make_equiv_cells(LibertyLibrary *lib) -{ - LibertyLibrarySeq libs; - libs.push_back(lib); - Sta::sta()->makeEquivCells(&libs, nullptr); -} - -LibertyCellSeq * -find_equiv_cells(LibertyCell *cell) -{ - return Sta::sta()->equivCells(cell); -} - -bool -equiv_cells(LibertyCell *cell1, - LibertyCell *cell2) -{ - return sta::equivCells(cell1, cell2); -} - -bool -equiv_cell_ports(LibertyCell *cell1, - LibertyCell *cell2) -{ - return equivCellPorts(cell1, cell2); -} - -bool -equiv_cell_timing_arcs(LibertyCell *cell1, - LibertyCell *cell2) -{ - return equivCellTimingArcSets(cell1, cell2); -} - -void -set_cmd_namespace_cmd(const char *namespc) -{ - if (stringEq(namespc, "sdc")) - Sta::sta()->setCmdNamespace(CmdNamespace::sdc); - else if (stringEq(namespc, "sta")) - Sta::sta()->setCmdNamespace(CmdNamespace::sta); - else - criticalError(269, "unknown namespace"); -} - -bool -link_design_cmd(const char *top_cell_name) -{ - return Sta::sta()->linkDesign(top_cell_name); -} - -bool -link_make_black_boxes() -{ - return Sta::sta()->linkMakeBlackBoxes(); -} - -void -set_link_make_black_boxes(bool make) -{ - Sta::sta()->setLinkMakeBlackBoxes(make); -} - -Instance * -top_instance() -{ - return cmdLinkedNetwork()->topInstance(); -} - -const char * -liberty_port_direction(const LibertyPort *port) -{ - return port->direction()->name(); -} - -const char * -port_direction(const Port *port) -{ - return cmdLinkedNetwork()->direction(port)->name(); -} - -const char * -pin_direction(const Pin *pin) -{ - return cmdLinkedNetwork()->direction(pin)->name(); -} - -PortSeq -find_ports_matching(const char *pattern, - bool regexp, - bool nocase) -{ - Network *network = cmdLinkedNetwork(); - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - Cell *top_cell = network->cell(network->topInstance()); - PortSeq matches1 = network->findPortsMatching(top_cell, &matcher); - // Expand bus/bundle ports. - PortSeq matches; - for (const Port *port : matches1) { - if (network->isBus(port) - || network->isBundle(port)) { - PortMemberIterator *member_iter = network->memberIterator(port); - while (member_iter->hasNext()) { - Port *member = member_iter->next(); - matches.push_back(member); - } - delete member_iter; - } - else - matches.push_back(port); - } - return matches; -} - -PinSeq -find_port_pins_matching(const char *pattern, - bool regexp, - bool nocase) -{ - Network *network = cmdLinkedNetwork(); - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - Instance *top_inst = network->topInstance(); - Cell *top_cell = network->cell(top_inst); - PortSeq ports = network->findPortsMatching(top_cell, &matcher); - PinSeq pins; - for (const Port *port : ports) { - if (network->isBus(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); - } - delete member_iter; - } - else { - Pin *pin = network->findPin(top_inst, port); - if (pin) - pins.push_back(pin); - } - } - return pins; -} - -Pin * -find_pin(const char *path_name) -{ - return cmdLinkedNetwork()->findPin(path_name); -} - -Pin * -get_port_pin(const Port *port) -{ - Network *network = cmdLinkedNetwork(); - return network->findPin(network->topInstance(), port); -} - -PinSeq -find_pins_matching(const char *pattern, - bool regexp, - bool nocase) -{ - Sta *sta = Sta::sta(); - Network *network = cmdLinkedNetwork(); - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - Instance *current_instance = sta->currentInstance(); - PinSeq matches = network->findPinsMatching(current_instance, &matcher); - return matches; -} - -PinSeq -find_pins_hier_matching(const char *pattern, - bool regexp, - bool nocase) -{ - Sta *sta = Sta::sta(); - Network *network = cmdLinkedNetwork(); - Instance *current_instance = sta->currentInstance(); - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - PinSeq matches = network->findPinsHierMatching(current_instance, &matcher); - return matches; -} - -Instance * -find_instance(char *path_name) -{ - return cmdLinkedNetwork()->findInstance(path_name); -} - -InstanceSeq -network_leaf_instances() -{ - return cmdLinkedNetwork()->leafInstances(); -} - -InstanceSeq -find_instances_matching(const char *pattern, - bool regexp, - bool nocase) -{ - Sta *sta = Sta::sta(); - Instance *current_instance = sta->currentInstance(); - PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); - Network *network = cmdLinkedNetwork(); - InstanceSeq matches = network->findInstancesMatching(current_instance, &matcher); - return matches; -} - -InstanceSeq -find_instances_hier_matching(const char *pattern, - bool regexp, - bool nocase) -{ - Sta *sta = Sta::sta(); - Network *network = cmdLinkedNetwork(); - Instance *current_instance = sta->currentInstance(); - PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); - InstanceSeq matches = network->findInstancesHierMatching(current_instance, &matcher); - return matches; -} - -InstanceSet -find_register_instances(ClockSet *clks, - const RiseFallBoth *clk_tr, - bool edge_triggered, - bool latches) -{ - cmdLinkedNetwork(); - InstanceSet insts = Sta::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) -{ - cmdLinkedNetwork(); - PinSet pins = Sta::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) -{ - cmdLinkedNetwork(); - PinSet pins = Sta::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) -{ - cmdLinkedNetwork(); - PinSet pins = Sta::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) -{ - cmdLinkedNetwork(); - PinSet pins = Sta::sta()->findRegisterOutputPins(clks, clk_tr, - edge_triggered, latches); - delete clks; - return pins; -} - -Net * -find_net(char *path_name) -{ - return cmdLinkedNetwork()->findNet(path_name); -} - -NetSeq -find_nets_matching(const char *pattern, - bool regexp, - bool nocase) -{ - Network *network = cmdLinkedNetwork(); - Instance *current_instance = Sta::sta()->currentInstance(); - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - NetSeq matches = network->findNetsMatching(current_instance, &matcher); - return matches; -} - -NetSeq -find_nets_hier_matching(const char *pattern, - bool regexp, - bool nocase) -{ - Network *network = cmdLinkedNetwork(); - Instance *current_instance = Sta::sta()->currentInstance(); - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - NetSeq matches = network->findNetsHierMatching(current_instance, &matcher); - return matches; -} - -PortSeq -filter_ports(const char *property, - const char *op, - const char *pattern, - PortSeq *ports) -{ - PortSeq filtered_ports; - if (ports) { - Sta *sta = Sta::sta(); - bool exact_match = stringEq(op, "=="); - bool pattern_match = stringEq(op, "=~"); - bool not_match = stringEq(op, "!="); - for (const Port *port : *ports) { - PropertyValue value(getProperty(port, property, sta)); - const char *prop = value.stringValue(); - if (prop && - ((exact_match && stringEq(prop, pattern)) - || (not_match && !stringEq(prop, pattern)) - || (pattern_match && patternMatch(pattern, prop)))) - filtered_ports.push_back(port); - } - delete ports; - } - return filtered_ports; -} - -InstanceSeq -filter_insts(const char *property, - const char *op, - const char *pattern, - InstanceSeq *insts) -{ - InstanceSeq filtered_insts; - if (insts) { - Sta *sta = Sta::sta(); - cmdLinkedNetwork(); - bool exact_match = stringEq(op, "=="); - bool pattern_match = stringEq(op, "=~"); - bool not_match = stringEq(op, "!="); - for (const Instance *inst : *insts) { - PropertyValue value(getProperty(inst, property, sta)); - const char *prop = value.stringValue(); - if (prop && - ((exact_match && stringEq(prop, pattern)) - || (not_match && !stringEq(prop, pattern)) - || (pattern_match && patternMatch(pattern, prop)))) - filtered_insts.push_back(inst); - } - delete insts; - } - return filtered_insts; -} - -PinSeq -filter_pins(const char *property, - const char *op, - const char *pattern, - PinSeq *pins) -{ - PinSeq filtered_pins; - if (pins) { - Sta *sta = Sta::sta(); - bool exact_match = stringEq(op, "=="); - bool pattern_match = stringEq(op, "=~"); - bool not_match = stringEq(op, "!="); - for (const Pin *pin : *pins) { - PropertyValue value(getProperty(pin, property, sta)); - const char *prop = value.asString(sta->sdcNetwork()); - if (prop && - ((exact_match && stringEq(prop, pattern)) - || (not_match && !stringEq(prop, pattern)) - || (pattern_match && patternMatch(pattern, prop)))) - filtered_pins.push_back(pin); - } - delete pins; - } - return filtered_pins; -} - -PropertyValue -pin_property(const Pin *pin, - const char *property) -{ - cmdLinkedNetwork(); - return getProperty(pin, property, Sta::sta()); -} - -PropertyValue -instance_property(const Instance *inst, - const char *property) -{ - cmdLinkedNetwork(); - return getProperty(inst, property, Sta::sta()); -} - -PropertyValue -net_property(const Net *net, - const char *property) -{ - cmdLinkedNetwork(); - return getProperty(net, property, Sta::sta()); -} - -PropertyValue -port_property(const Port *port, - const char *property) -{ - return getProperty(port, property, Sta::sta()); -} - - -PropertyValue -liberty_cell_property(const LibertyCell *cell, - const char *property) -{ - return getProperty(cell, property, Sta::sta()); -} - -PropertyValue -cell_property(const Cell *cell, - const char *property) -{ - return getProperty(cell, property, Sta::sta()); -} - -PropertyValue -liberty_port_property(const LibertyPort *port, - const char *property) -{ - return getProperty(port, property, Sta::sta()); -} - -PropertyValue -library_property(const Library *lib, - const char *property) -{ - return getProperty(lib, property, Sta::sta()); -} - -PropertyValue -liberty_library_property(const LibertyLibrary *lib, - const char *property) -{ - return getProperty(lib, property, Sta::sta()); -} - -PropertyValue -edge_property(Edge *edge, - const char *property) -{ - cmdGraph(); - return getProperty(edge, property, Sta::sta()); -} - -PropertyValue -clock_property(Clock *clk, - const char *property) -{ - cmdLinkedNetwork(); - return getProperty(clk, property, Sta::sta()); -} - -PropertyValue -path_end_property(PathEnd *end, - const char *property) -{ - cmdLinkedNetwork(); - return getProperty(end, property, Sta::sta()); -} - -PropertyValue -path_ref_property(PathRef *path, - const char *property) -{ - cmdLinkedNetwork(); - return getProperty(path, property, Sta::sta()); -} - -PropertyValue -timing_arc_set_property(TimingArcSet *arc_set, - const char *property) -{ - cmdLinkedNetwork(); - return getProperty(arc_set, property, Sta::sta()); -} - -LeafInstanceIterator * -leaf_instance_iterator() -{ - return cmdLinkedNetwork()->leafInstanceIterator(); -} - -//////////////////////////////////////////////////////////////// - -void -define_corners_cmd(StringSet *corner_names) -{ - Sta *sta = Sta::sta(); - sta->makeCorners(corner_names); - delete corner_names; -} - -Corner * -cmd_corner() -{ - return Sta::sta()->cmdCorner(); -} - -void -set_cmd_corner(Corner *corner) -{ - Sta::sta()->setCmdCorner(corner); -} - -Corner * -find_corner(const char *corner_name) -{ - return Sta::sta()->findCorner(corner_name); -} - -Corners * -corners() -{ - return Sta::sta()->corners(); -} - -bool -multi_corner() -{ - return Sta::sta()->multiCorner(); -} - -//////////////////////////////////////////////////////////////// - -void -set_analysis_type_cmd(const char *analysis_type) -{ - AnalysisType type; - if (stringEq(analysis_type, "single")) - type = AnalysisType::single; - else if (stringEq(analysis_type, "bc_wc")) - type = AnalysisType::bc_wc; - else if (stringEq(analysis_type, "on_chip_variation")) - type = AnalysisType::ocv; - else { - criticalError(270, "unknown analysis type"); - type = AnalysisType::single; - } - Sta::sta()->setAnalysisType(type); -} - -OperatingConditions * -operating_conditions(const MinMax *min_max) -{ - return Sta::sta()->operatingConditions(min_max); -} - -void -set_operating_conditions_cmd(OperatingConditions *op_cond, - const MinMaxAll *min_max) -{ - Sta::sta()->setOperatingConditions(op_cond, min_max); -} - -EdgeSeq -filter_timing_arcs(const char *property, - const char *op, - const char *pattern, - EdgeSeq *edges) -{ - Sta *sta = Sta::sta(); - EdgeSeq filtered_edges; - bool exact_match = stringEq(op, "=="); - bool pattern_match = stringEq(op, "=~"); - bool not_match = stringEq(op, "!="); - for (Edge *edge : *edges) { - PropertyValue value(getProperty(edge, property, sta)); - const char *prop = value.stringValue(); - if (prop && - ((exact_match && stringEq(prop, pattern)) - || (not_match && !stringEq(prop, pattern)) - || (pattern_match && patternMatch(pattern, prop)))) - filtered_edges.push_back(edge); - } - delete edges; - return filtered_edges; -} - -const char * -operating_condition_analysis_type() -{ - switch (Sta::sta()->sdc()->analysisType()){ - case AnalysisType::single: - return "single"; - case AnalysisType::bc_wc: - return "bc_wc"; - case AnalysisType::ocv: - return "on_chip_variation"; - } - // Prevent warnings from lame compilers. - return "?"; -} - -void -set_instance_pvt(Instance *inst, - const MinMaxAll *min_max, - float process, - float voltage, - float temperature) -{ - cmdLinkedNetwork(); - Pvt pvt(process, voltage, temperature); - Sta::sta()->setPvt(inst, min_max, pvt); -} - -float -port_ext_pin_cap(const Port *port, - const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - float pin_cap, wire_cap; - int fanout; - Sta::sta()->portExtCaps(port, corner, min_max, 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); -} - -float -port_ext_wire_cap(const Port *port, - const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - float pin_cap, wire_cap; - int fanout; - Sta::sta()->portExtCaps(port, corner, min_max, 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); -} - -void -set_port_ext_fanout_cmd(const Port *port, - int fanout, - const Corner *corner, - const MinMaxAll *min_max) -{ - Sta::sta()->setPortExtFanout(port, fanout, corner, min_max); -} - -float -port_ext_fanout(const Port *port, - const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - float pin_cap, wire_cap; - int fanout; - Sta::sta()->portExtCaps(port, corner, min_max, 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) -{ - Sta::sta()->setNetWireCap(net, subtract_pin_cap, corner, min_max, cap); -} - -void -set_wire_load_mode_cmd(const char *mode_name) -{ - WireloadMode mode = stringWireloadMode(mode_name); - if (mode == WireloadMode::unknown) - criticalError(271, "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); -} - -void -set_wire_load_cmd(Wireload *wireload, - const MinMaxAll *min_max) -{ - Sta::sta()->setWireload(wireload, min_max); -} - -void -set_wire_load_selection_group_cmd(WireloadSelection *selection, - const MinMaxAll *min_max) -{ - Sta::sta()->setWireloadSelection(selection, min_max); -} - -void -make_clock(const char *name, - PinSet *pins, - bool add_to_pins, - float period, - FloatSeq *waveform, - char *comment) -{ - cmdLinkedNetwork(); - Sta::sta()->makeClock(name, pins, add_to_pins, period, waveform, comment); -} - -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) -{ - cmdLinkedNetwork(); - Sta::sta()->makeGeneratedClock(name, pins, add_to_pins, - src_pin, master_clk, - divide_by, multiply_by, duty_cycle, invert, - combinational, edges, edge_shifts, - comment); -} - -void -remove_clock_cmd(Clock *clk) -{ - cmdLinkedNetwork(); - Sta::sta()->removeClock(clk); -} - -void -set_propagated_clock_cmd(Clock *clk) -{ - cmdLinkedNetwork(); - Sta::sta()->setPropagatedClock(clk); -} - -void -set_propagated_clock_pin_cmd(Pin *pin) -{ - cmdLinkedNetwork(); - Sta::sta()->setPropagatedClock(pin); -} - -void -unset_propagated_clock_cmd(Clock *clk) -{ - cmdLinkedNetwork(); - Sta::sta()->removePropagatedClock(clk); -} - -void -unset_propagated_clock_pin_cmd(Pin *pin) -{ - cmdLinkedNetwork(); - Sta::sta()->removePropagatedClock(pin); -} - -void -set_clock_slew_cmd(Clock *clk, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) -{ - cmdLinkedNetwork(); - Sta::sta()->setClockSlew(clk, rf, min_max, slew); -} - -void -unset_clock_slew_cmd(Clock *clk) -{ - cmdLinkedNetwork(); - Sta::sta()->removeClockSlew(clk); -} - -void -set_clock_latency_cmd(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - MinMaxAll *min_max, float delay) -{ - cmdLinkedNetwork(); - Sta::sta()->setClockLatency(clk, pin, rf, min_max, delay); -} - -void -set_clock_insertion_cmd(Clock *clk, - Pin *pin, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - const EarlyLateAll *early_late, - float delay) -{ - cmdLinkedNetwork(); - Sta::sta()->setClockInsertion(clk, pin, rf, min_max, early_late, delay); -} - -void -unset_clock_latency_cmd(Clock *clk, - Pin *pin) -{ - cmdLinkedNetwork(); - Sta::sta()->removeClockLatency(clk, pin); -} - -void -unset_clock_insertion_cmd(Clock *clk, - Pin *pin) -{ - cmdLinkedNetwork(); - Sta::sta()->removeClockInsertion(clk, pin); -} - -void -set_clock_uncertainty_clk(Clock *clk, - const SetupHoldAll *setup_hold, - float uncertainty) -{ - cmdLinkedNetwork(); - Sta::sta()->setClockUncertainty(clk, setup_hold, uncertainty); -} - -void -unset_clock_uncertainty_clk(Clock *clk, - const SetupHoldAll *setup_hold) -{ - cmdLinkedNetwork(); - Sta::sta()->removeClockUncertainty(clk, setup_hold); -} - -void -set_clock_uncertainty_pin(Pin *pin, - const MinMaxAll *min_max, - float uncertainty) -{ - cmdLinkedNetwork(); - Sta::sta()->setClockUncertainty(pin, min_max, uncertainty); -} - -void -unset_clock_uncertainty_pin(Pin *pin, - const MinMaxAll *min_max) -{ - cmdLinkedNetwork(); - Sta::sta()->removeClockUncertainty(pin, min_max); -} - -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) -{ - cmdLinkedNetwork(); - Sta::sta()->setClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, - uncertainty); -} - -void -unset_inter_clock_uncertainty(Clock *from_clk, - const RiseFallBoth *from_tr, - Clock *to_clk, - const RiseFallBoth *to_tr, - const MinMaxAll *min_max) -{ - cmdLinkedNetwork(); - Sta::sta()->removeClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max); -} - -void -set_clock_gating_check_cmd(const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) -{ - Sta::sta()->setClockGatingCheck(rf, setup_hold, margin); -} - -void -set_clock_gating_check_clk_cmd(Clock *clk, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin) -{ - Sta::sta()->setClockGatingCheck(clk, rf, setup_hold, margin); -} - -void -set_clock_gating_check_pin_cmd(Pin *pin, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) -{ - Sta::sta()->setClockGatingCheck(pin, rf, setup_hold, margin, active_value); -} - -void -set_clock_gating_check_instance_cmd(Instance *inst, - const RiseFallBoth *rf, - const SetupHold *setup_hold, - float margin, - LogicValue active_value) -{ - Sta::sta()->setClockGatingCheck(inst, rf, setup_hold, margin, active_value); -} - -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) -{ - Sta::sta()->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold, margin); -} - -void -unset_data_check_cmd(Pin *from, - 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); -} - -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) -{ - cmdLinkedNetwork(); - Sta::sta()->setInputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included, network_latency_included, - min_max, add, delay); -} - -void -unset_input_delay_cmd(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - MinMaxAll *min_max) -{ - cmdLinkedNetwork(); - Sta::sta()->removeInputDelay(pin, rf, clk, clk_rf, min_max); -} - -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) -{ - cmdLinkedNetwork(); - Sta::sta()->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, - source_latency_included, network_latency_included, - min_max, add, delay); -} - -void -unset_output_delay_cmd(Pin *pin, - RiseFallBoth *rf, - Clock *clk, - RiseFall *clk_rf, - MinMaxAll *min_max) -{ - cmdLinkedNetwork(); - Sta::sta()->removeOutputDelay(pin, rf, clk, clk_rf, min_max); -} - -void -disable_cell(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) -{ - cmdLinkedNetwork(); - Sta::sta()->disable(cell, from, to); -} - -void -unset_disable_cell(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to) -{ - cmdLinkedNetwork(); - Sta::sta()->removeDisable(cell, from, to); -} - -void -disable_lib_port(LibertyPort *port) -{ - cmdLinkedNetwork(); - Sta::sta()->disable(port); -} - -void -unset_disable_lib_port(LibertyPort *port) -{ - cmdLinkedNetwork(); - Sta::sta()->removeDisable(port); -} - -void -disable_port(Port *port) -{ - cmdLinkedNetwork(); - Sta::sta()->disable(port); -} - -void -unset_disable_port(Port *port) -{ - cmdLinkedNetwork(); - Sta::sta()->removeDisable(port); -} - -void -disable_instance(Instance *instance, - LibertyPort *from, - LibertyPort *to) -{ - cmdLinkedNetwork(); - Sta::sta()->disable(instance, from, to); -} - -void -unset_disable_instance(Instance *instance, - LibertyPort *from, - LibertyPort *to) -{ - cmdLinkedNetwork(); - Sta::sta()->removeDisable(instance, from, to); -} - -void -disable_pin(Pin *pin) -{ - cmdLinkedNetwork(); - Sta::sta()->disable(pin); -} - -void -unset_disable_pin(Pin *pin) -{ - cmdLinkedNetwork(); - Sta::sta()->removeDisable(pin); -} - -void -disable_edge(Edge *edge) -{ - cmdLinkedNetwork(); - Sta::sta()->disable(edge); -} - -void -unset_disable_edge(Edge *edge) -{ - cmdLinkedNetwork(); - Sta::sta()->removeDisable(edge); -} - -void -disable_timing_arc_set(TimingArcSet *arc_set) -{ - cmdLinkedNetwork(); - Sta::sta()->disable(arc_set); -} - -void -unset_disable_timing_arc_set(TimingArcSet *arc_set) -{ - cmdLinkedNetwork(); - Sta::sta()->removeDisable(arc_set); -} - -void -disable_clock_gating_check_inst(Instance *inst) -{ - cmdLinkedNetwork(); - Sta::sta()->disableClockGatingCheck(inst); -} - -void -disable_clock_gating_check_pin(Pin *pin) -{ - cmdLinkedNetwork(); - Sta::sta()->disableClockGatingCheck(pin); -} - -void -unset_disable_clock_gating_check_inst(Instance *inst) -{ - cmdLinkedNetwork(); - Sta::sta()->removeDisableClockGatingCheck(inst); -} - -void -unset_disable_clock_gating_check_pin(Pin *pin) -{ - cmdLinkedNetwork(); - Sta::sta()->removeDisableClockGatingCheck(pin); -} - -void -make_false_path(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - const char *comment) -{ - cmdLinkedNetwork(); - Sta::sta()->makeFalsePath(from, thrus, to, min_max, comment); -} - -void -make_multicycle_path(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max, - bool use_end_clk, - int path_multiplier, - const char *comment) -{ - cmdLinkedNetwork(); - Sta::sta()->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, - path_multiplier, comment); -} - -void -make_path_delay(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMax *min_max, - bool ignore_clk_latency, - float delay, - const char *comment) -{ - cmdLinkedNetwork(); - Sta::sta()->makePathDelay(from, thrus, to, min_max, - ignore_clk_latency, delay, comment); -} - -void -reset_path_cmd(ExceptionFrom * - from, ExceptionThruSeq *thrus, - ExceptionTo *to, - const MinMaxAll *min_max) -{ - cmdLinkedNetwork(); - Sta::sta()->resetPath(from, thrus, to, min_max); - // 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. - delete thrus; -} - -void -make_group_path(const char *name, - bool is_default, - ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - const char *comment) -{ - cmdLinkedNetwork(); - if (name[0] == '\0') - name = nullptr; - Sta::sta()->makeGroupPath(name, is_default, from, thrus, to, comment); -} - -bool -is_path_group_name(const char *name) -{ - cmdLinkedNetwork(); - return Sta::sta()->isGroupPathName(name); -} - -ExceptionFrom * -make_exception_from(PinSet *from_pins, - ClockSet *from_clks, - InstanceSet *from_insts, - const RiseFallBoth *from_tr) -{ - cmdLinkedNetwork(); - return Sta::sta()->makeExceptionFrom(from_pins, from_clks, from_insts, - from_tr); -} - -void -delete_exception_from(ExceptionFrom *from) -{ - Sta::sta()->deleteExceptionFrom(from); -} - -void -check_exception_from_pins(ExceptionFrom *from, - const char *file, - int line) -{ - Sta::sta()->checkExceptionFromPins(from, file, line); -} - -ExceptionThru * -make_exception_thru(PinSet *pins, - NetSet *nets, - InstanceSet *insts, - const RiseFallBoth *rf) -{ - cmdLinkedNetwork(); - return Sta::sta()->makeExceptionThru(pins, nets, insts, rf); -} - -void -delete_exception_thru(ExceptionThru *thru) -{ - Sta::sta()->deleteExceptionThru(thru); -} - -ExceptionTo * -make_exception_to(PinSet *to_pins, - ClockSet *to_clks, - InstanceSet *to_insts, - const RiseFallBoth *rf, - RiseFallBoth *end_rf) -{ - cmdLinkedNetwork(); - return Sta::sta()->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf); -} - -void -delete_exception_to(ExceptionTo *to) -{ - Sta::sta()->deleteExceptionTo(to); -} - -void -check_exception_to_pins(ExceptionTo *to, - const char *file, - int line) -{ - Sta::sta()->checkExceptionToPins(to, file, line); -} - -void -set_input_slew_cmd(Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float slew) -{ - cmdLinkedNetwork(); - Sta::sta()->setInputSlew(port, rf, min_max, slew); -} - -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) -{ - 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); -} - -void -set_drive_resistance_cmd(Port *port, - const RiseFallBoth *rf, - const MinMaxAll *min_max, - float res) -{ - cmdLinkedNetwork(); - Sta::sta()->setDriveResistance(port, rf, min_max, res); -} - -void -set_slew_limit_clk(Clock *clk, - const RiseFallBoth *rf, - PathClkOrData clk_data, - const MinMax *min_max, - float slew) -{ - cmdLinkedNetwork(); - Sta::sta()->setSlewLimit(clk, rf, clk_data, min_max, slew); -} - -void -set_slew_limit_port(Port *port, - const MinMax *min_max, - float slew) -{ - cmdLinkedNetwork(); - Sta::sta()->setSlewLimit(port, min_max, slew); -} - -void -set_slew_limit_cell(Cell *cell, - const MinMax *min_max, - float slew) -{ - cmdLinkedNetwork(); - Sta::sta()->setSlewLimit(cell, min_max, slew); -} - -void -set_port_capacitance_limit(Port *port, - const MinMax *min_max, - float cap) -{ - Sta::sta()->setCapacitanceLimit(port, min_max, cap); -} - -void -set_pin_capacitance_limit(Pin *pin, - const MinMax *min_max, - float cap) -{ - Sta::sta()->setCapacitanceLimit(pin, min_max, cap); -} - -void -set_cell_capacitance_limit(Cell *cell, - const MinMax *min_max, - float cap) -{ - Sta::sta()->setCapacitanceLimit(cell, min_max, cap); -} - -void -set_latch_borrow_limit_pin(Pin *pin, - float limit) -{ - Sta::sta()->setLatchBorrowLimit(pin, limit); -} - -void -set_latch_borrow_limit_inst(Instance *inst, - float limit) -{ - Sta::sta()->setLatchBorrowLimit(inst, limit); -} - -void -set_latch_borrow_limit_clk(Clock *clk, float limit) -{ - Sta::sta()->setLatchBorrowLimit(clk, limit); -} - -void -set_min_pulse_width_global(const RiseFallBoth *rf, - float min_width) -{ - Sta::sta()->setMinPulseWidth(rf, min_width); -} - -void -set_min_pulse_width_pin(Pin *pin, - const RiseFallBoth *rf, - float min_width) -{ - Sta::sta()->setMinPulseWidth(pin, rf, min_width); -} - -void -set_min_pulse_width_clk(Clock *clk, - const RiseFallBoth *rf, - float min_width) -{ - Sta::sta()->setMinPulseWidth(clk, rf, min_width); -} - -void -set_min_pulse_width_inst(Instance *inst, - const RiseFallBoth *rf, - float min_width) -{ - Sta::sta()->setMinPulseWidth(inst, rf, min_width); -} - -void -set_max_area_cmd(float area) -{ - Sta::sta()->setMaxArea(area); -} - -void -set_port_fanout_limit(Port *port, - const MinMax *min_max, - float fanout) -{ - Sta::sta()->setFanoutLimit(port, min_max, fanout); -} - -void -set_cell_fanout_limit(Cell *cell, - const MinMax *min_max, - float fanout) -{ - Sta::sta()->setFanoutLimit(cell, min_max, fanout); -} - -void -set_logic_value_cmd(Pin *pin, - LogicValue value) -{ - Sta::sta()->setLogicValue(pin, value); -} - -void -set_case_analysis_cmd(Pin *pin, - LogicValue value) -{ - Sta::sta()->setCaseAnalysis(pin, value); -} - -void -unset_case_analysis_cmd(Pin *pin) -{ - Sta::sta()->removeCaseAnalysis(pin); -} - -void -set_timing_derate_cmd(TimingDerateType type, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) -{ - Sta::sta()->setTimingDerate(type, clk_data, rf, early_late, derate); -} - -void -set_timing_derate_net_cmd(const Net *net, - PathClkOrData clk_data, - const RiseFallBoth *rf, - const EarlyLate *early_late, - float derate) -{ - Sta::sta()->setTimingDerate(net, clk_data, rf, early_late, derate); -} - -void -set_timing_derate_inst_cmd(const Instance *inst, - 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); -} - -void -set_timing_derate_cell_cmd(const LibertyCell *cell, - 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); -} - -void -unset_timing_derate_cmd() -{ - Sta::sta()->unsetTimingDerate(); -} - -Clock * -find_clock(const char *name) -{ - cmdLinkedNetwork(); - return Sta::sta()->sdc()->findClock(name); -} - -bool -is_clock_src(const Pin *pin) -{ - return Sta::sta()->isClockSrc(pin); -} - -Clock * -default_arrival_clock() -{ - return Sta::sta()->sdc()->defaultArrivalClock(); -} - -ClockSeq -find_clocks_matching(const char *pattern, - bool regexp, - bool nocase) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); - PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); - return sdc->findClocksMatching(&matcher); -} - -void -update_generated_clks() -{ - cmdLinkedNetwork(); - Sta::sta()->updateGeneratedClks(); -} - -bool -is_clock(Pin *pin) -{ - Sta *sta = Sta::sta(); - return sta->isClock(pin); -} - -bool -is_ideal_clock(Pin *pin) -{ - Sta *sta = Sta::sta(); - return 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); -} - -// format_unit functions print with fixed digits and suffix. -// Pass value arg as string to support NaNs. -const char * -format_time(const char *value, - int digits) -{ - float value1 = strtof(value, nullptr); - return Sta::sta()->units()->timeUnit()->asString(value1, digits); -} - -const char * -format_capacitance(const char *value, - int digits) -{ - float value1 = strtof(value, nullptr); - return Sta::sta()->units()->capacitanceUnit()->asString(value1, digits); -} - -const char * -format_resistance(const char *value, - int digits) -{ - float value1 = strtof(value, nullptr); - return Sta::sta()->units()->resistanceUnit()->asString(value1, digits); -} - -const char * -format_voltage(const char *value, - int digits) -{ - float value1 = strtof(value, nullptr); - return Sta::sta()->units()->voltageUnit()->asString(value1, digits); -} - -const char * -format_current(const char *value, - int digits) -{ - float value1 = strtof(value, nullptr); - return Sta::sta()->units()->currentUnit()->asString(value1, digits); -} - -const char * -format_power(const char *value, - int digits) -{ - float value1 = strtof(value, nullptr); - return Sta::sta()->units()->powerUnit()->asString(value1, digits); -} - -const char * -format_distance(const char *value, - int digits) -{ - float value1 = strtof(value, nullptr); - Unit *dist_unit = Sta::sta()->units()->distanceUnit(); - return dist_unit->asString(value1, digits); -} - -const char * -format_area(const char *value, - int digits) -{ - float value1 = strtof(value, nullptr); - Unit *dist_unit = Sta::sta()->units()->distanceUnit(); - return dist_unit->asString(value1 / dist_unit->scale(), digits); -} - -//////////////////////////////////////////////////////////////// - -// _sta_ui conversion from sta units to user interface units. -// _ui_sta conversion from user interface units to sta units. - -double -time_ui_sta(double value) -{ - return Sta::sta()->units()->timeUnit()->userToSta(value); -} - -double -time_sta_ui(double value) -{ - return Sta::sta()->units()->timeUnit()->staToUser(value); -} - -double -capacitance_ui_sta(double value) -{ - return Sta::sta()->units()->capacitanceUnit()->userToSta(value); -} - -double -capacitance_sta_ui(double value) -{ - return Sta::sta()->units()->capacitanceUnit()->staToUser(value); -} - -double -resistance_ui_sta(double value) -{ - return Sta::sta()->units()->resistanceUnit()->userToSta(value); -} - -double -resistance_sta_ui(double value) -{ - return Sta::sta()->units()->resistanceUnit()->staToUser(value); -} - -double -voltage_ui_sta(double value) -{ - return Sta::sta()->units()->voltageUnit()->userToSta(value); -} - -double -voltage_sta_ui(double value) -{ - return Sta::sta()->units()->voltageUnit()->staToUser(value); -} - -double -current_ui_sta(double value) -{ - return Sta::sta()->units()->currentUnit()->userToSta(value); -} - -double -current_sta_ui(double value) -{ - return Sta::sta()->units()->currentUnit()->staToUser(value); -} - -double -power_ui_sta(double value) -{ - return Sta::sta()->units()->powerUnit()->userToSta(value); -} - -double -power_sta_ui(double value) -{ - return Sta::sta()->units()->powerUnit()->staToUser(value); -} - -double -distance_ui_sta(double value) -{ - return Sta::sta()->units()->distanceUnit()->userToSta(value); -} - -double -distance_sta_ui(double value) -{ - return Sta::sta()->units()->distanceUnit()->staToUser(value); -} - -double -area_ui_sta(double value) -{ - double scale = Sta::sta()->units()->distanceUnit()->scale(); - return value * scale * scale; -} - -double -area_sta_ui(double value) -{ - double scale = Sta::sta()->units()->distanceUnit()->scale(); - return value / (scale * scale); -} - -//////////////////////////////////////////////////////////////// - -void -set_cmd_unit_scale(const char *unit_name, - float scale) -{ - Unit *unit = Sta::sta()->units()->find(unit_name); - if (unit) - unit->setScale(scale); -} - -void -set_cmd_unit_digits(const char *unit_name, - int digits) -{ - Unit *unit = Sta::sta()->units()->find(unit_name); - if (unit) - unit->setDigits(digits); -} - -void -set_cmd_unit_suffix(const char *unit_name, - const char *suffix) -{ - Unit *unit = Sta::sta()->units()->find(unit_name); - if (unit) { - unit->setSuffix(suffix); - } -} - -const char * -unit_scale_abbreviation (const char *unit_name) -{ - Unit *unit = Sta::sta()->units()->find(unit_name); - if (unit) - return unit->scaleAbbreviation(); - else - return ""; -} - -const char * -unit_suffix(const char *unit_name) -{ - Unit *unit = Sta::sta()->units()->find(unit_name); - if (unit) - return unit->suffix(); - else - return ""; -} - -const char * -unit_scaled_suffix(const char *unit_name) -{ - Unit *unit = Sta::sta()->units()->find(unit_name); - if (unit) - return unit->scaledSuffix(); - else - return ""; -} - -//////////////////////////////////////////////////////////////// - -VertexIterator * -vertex_iterator() -{ - return new VertexIterator(cmdGraph()); -} - -void -set_arc_delay(Edge *edge, - TimingArc *arc, - const Corner *corner, - const MinMaxAll *min_max, - float delay) -{ - cmdGraph(); - Sta::sta()->setArcDelay(edge, arc, corner, min_max, delay); -} - -void -set_annotated_slew(Vertex *vertex, - const Corner *corner, - const MinMaxAll *min_max, - const RiseFallBoth *rf, - float slew) -{ - cmdGraph(); - Sta::sta()->setAnnotatedSlew(vertex, corner, min_max, rf, slew); -} - -// Remove all delay and slew annotations. -void -remove_delay_slew_annotations() -{ - cmdGraph(); - Sta::sta()->removeDelaySlewAnnotations(); -} - -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) -{ - cmdLinkedNetwork(); - return Sta::sta()->checkTiming(no_input_delay, no_output_delay, - reg_multiple_clks, reg_no_clks, - unconstrained_endpoints, - loops, generated_clks); -} - -bool -crpr_enabled() -{ - return Sta::sta()->crprEnabled(); -} - -void -set_crpr_enabled(bool enabled) -{ - return Sta::sta()->setCrprEnabled(enabled); -} - -const char * -crpr_mode() -{ - switch (Sta::sta()->crprMode()) { - case CrprMode::same_transition: - return "same_transition"; - case CrprMode::same_pin: - return "same_pin"; - default: - return ""; - } -} - -void -set_crpr_mode(const char *mode) -{ - Sta *sta = Sta::sta(); - if (stringEq(mode, "same_pin")) - Sta::sta()->setCrprMode(CrprMode::same_pin); - else if (stringEq(mode, "same_transition")) - Sta::sta()->setCrprMode(CrprMode::same_transition); - else - sta->report()->critical(1573, "unknown common clk pessimism mode."); -} - -bool -pocv_enabled() -{ - return Sta::sta()->pocvEnabled(); -} - -void -set_pocv_enabled(bool enabled) -{ -#if !SSTA - if (enabled) - Sta::sta()->report()->error(1574, "POCV support requires compilation with SSTA=1."); -#endif - return Sta::sta()->setPocvEnabled(enabled); -} - -float -pocv_sigma_factor() -{ - return Sta::sta()->sigmaFactor(); -} - -void -set_pocv_sigma_factor(float factor) -{ - Sta::sta()->setSigmaFactor(factor); -} - -bool -propagate_gated_clock_enable() -{ - return Sta::sta()->propagateGatedClockEnable(); -} - -void -set_propagate_gated_clock_enable(bool enable) -{ - Sta::sta()->setPropagateGatedClockEnable(enable); -} - -bool -preset_clr_arcs_enabled() -{ - return Sta::sta()->presetClrArcsEnabled(); -} - -void -set_preset_clr_arcs_enabled(bool enable) -{ - Sta::sta()->setPresetClrArcsEnabled(enable); -} - -bool -cond_default_arcs_enabled() -{ - return Sta::sta()->condDefaultArcsEnabled(); -} - -void -set_cond_default_arcs_enabled(bool enabled) -{ - Sta::sta()->setCondDefaultArcsEnabled(enabled); -} - -bool -bidirect_inst_paths_enabled() -{ - return Sta::sta()->bidirectInstPathsEnabled(); -} - -void -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() -{ - return Sta::sta()->recoveryRemovalChecksEnabled(); -} - -void -set_recovery_removal_checks_enabled(bool enabled) -{ - Sta::sta()->setRecoveryRemovalChecksEnabled(enabled); -} - -bool -gated_clk_checks_enabled() -{ - return Sta::sta()->gatedClkChecksEnabled(); -} - -void -set_gated_clk_checks_enabled(bool enabled) -{ - Sta::sta()->setGatedClkChecksEnabled(enabled); -} - -bool -dynamic_loop_breaking() -{ - return Sta::sta()->dynamicLoopBreaking(); -} - -void -set_dynamic_loop_breaking(bool enable) -{ - Sta::sta()->setDynamicLoopBreaking(enable); -} - -bool -use_default_arrival_clock() -{ - return Sta::sta()->useDefaultArrivalClock(); -} - -void -set_use_default_arrival_clock(bool enable) -{ - return Sta::sta()->setUseDefaultArrivalClock(enable); -} - -bool -propagate_all_clocks() -{ - return Sta::sta()->propagateAllClocks(); -} - -void -set_propagate_all_clocks(bool prop) -{ - Sta::sta()->setPropagateAllClocks(prop); -} - -//////////////////////////////////////////////////////////////// - -PathEndSeq -find_path_ends(ExceptionFrom *from, - ExceptionThruSeq *thrus, - ExceptionTo *to, - bool unconstrained, - Corner *corner, - const MinMaxAll *delay_min_max, - int group_count, - int endpoint_count, - bool unique_pins, - 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) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - PathEndSeq ends = sta->findPathEnds(from, thrus, to, unconstrained, - corner, delay_min_max, - group_count, endpoint_count, unique_pins, - slack_min, slack_max, - sort_by_slack, - groups->size() ? groups : nullptr, - setup, hold, - recovery, removal, - clk_gating_setup, clk_gating_hold); - delete groups; - return ends; -} - -void -report_path_end_header() -{ - Sta::sta()->reportPathEndHeader(); -} - -void -report_path_end_footer() -{ - Sta::sta()->reportPathEndFooter(); -} - -void -report_path_end(PathEnd *end) -{ - Sta::sta()->reportPathEnd(end); -} - -void -report_path_end2(PathEnd *end, - PathEnd *prev_end) -{ - Sta::sta()->reportPathEnd(end, prev_end); -} - -void -set_report_path_format(ReportPathFormat format) -{ - Sta::sta()->setReportPathFormat(format); -} - -void -set_report_path_field_order(StringSeq *field_names) -{ - Sta::sta()->setReportPathFieldOrder(field_names); - delete field_names; -} - -void -set_report_path_fields(bool report_input_pin, - bool report_net, - bool report_cap, - bool report_slew, - bool report_fanout) -{ - Sta::sta()->setReportPathFields(report_input_pin, - report_net, - report_cap, - report_slew, - report_fanout); -} - -void -set_report_path_field_properties(const char *field_name, - const char *title, - int width, - bool left_justify) -{ - Sta *sta = Sta::sta(); - ReportField *field = sta->findReportPathField(field_name); - if (field) - field->setProperties(title, width, left_justify); - else - sta->report()->error(1575, "unknown report path field %s", field_name); -} - -void -set_report_path_field_width(const char *field_name, - int width) -{ - Sta *sta = Sta::sta(); - ReportField *field = sta->findReportPathField(field_name); - if (field) - field->setWidth(width); - else - sta->report()->error(1576, "unknown report path field %s", field_name); -} - -void -set_report_path_digits(int digits) -{ - Sta::sta()->setReportPathDigits(digits); -} - -void -set_report_path_no_split(bool no_split) -{ - Sta::sta()->setReportPathNoSplit(no_split); -} - -void -set_report_path_sigmas(bool report_sigmas) -{ - Sta::sta()->setReportPathSigmas(report_sigmas); -} - -void -delete_path_ref(PathRef *path) -{ - delete path; -} - -void -remove_constraints() -{ - Sta::sta()->removeConstraints(); -} - -void -report_path_cmd(PathRef *path) -{ - Sta::sta()->reportPath(path); -} - -//////////////////////////////////////////////////////////////// - -void -report_clk_skew(ConstClockSeq clks, - const Corner *corner, - const SetupHold *setup_hold, - bool include_internal_latency, - int digits) -{ - cmdLinkedNetwork(); - Sta::sta()->reportClkSkew(clks, corner, setup_hold, - include_internal_latency, digits); -} - -void -report_clk_latency(ConstClockSeq clks, - const Corner *corner, - bool include_internal_latency, - int digits) -{ - cmdLinkedNetwork(); - Sta::sta()->reportClkLatency(clks, corner, include_internal_latency, digits); -} - -float -worst_clk_skew_cmd(const SetupHold *setup_hold, - bool include_internal_latency) -{ - cmdLinkedNetwork(); - return Sta::sta()->findWorstClkSkew(setup_hold, include_internal_latency); -} - -//////////////////////////////////////////////////////////////// - -PinSet -startpoints() -{ - return Sta::sta()->startpointPins(); -} - -PinSet -endpoints() -{ - return Sta::sta()->endpointPins(); -} - -size_t -endpoint_count() -{ - return Sta::sta()->endpointPins().size(); -} - -PinSet -group_path_pins(const char *group_path_name) -{ - Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); - if (sdc->isGroupPathName(group_path_name)) - return sta->findGroupPathPins(group_path_name); - else - return PinSet(sta->network()); -} - -//////////////////////////////////////////////////////////////// - -MinPulseWidthCheckSeq & -min_pulse_width_violations(const Corner *corner) -{ - cmdLinkedNetwork(); - return Sta::sta()->minPulseWidthViolations(corner); -} - -MinPulseWidthCheckSeq & -min_pulse_width_check_pins(PinSeq *pins, - const Corner *corner) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - MinPulseWidthCheckSeq &checks = sta->minPulseWidthChecks(pins, corner); - delete pins; - return checks; -} - -MinPulseWidthCheckSeq & -min_pulse_width_checks(const Corner *corner) -{ - cmdLinkedNetwork(); - return Sta::sta()->minPulseWidthChecks(corner); -} - -MinPulseWidthCheck * -min_pulse_width_check_slack(const Corner *corner) -{ - cmdLinkedNetwork(); - return Sta::sta()->minPulseWidthSlack(corner); -} - -void -report_mpw_checks(MinPulseWidthCheckSeq *checks, - bool verbose) -{ - Sta::sta()->reportMpwChecks(checks, verbose); -} - -void -report_mpw_check(MinPulseWidthCheck *check, - bool verbose) -{ - Sta::sta()->reportMpwCheck(check, verbose); -} - -//////////////////////////////////////////////////////////////// - -MinPeriodCheckSeq & -min_period_violations() -{ - cmdLinkedNetwork(); - return Sta::sta()->minPeriodViolations(); -} - -MinPeriodCheck * -min_period_check_slack() -{ - cmdLinkedNetwork(); - return Sta::sta()->minPeriodSlack(); -} - -void -report_min_period_checks(MinPeriodCheckSeq *checks, - bool verbose) -{ - Sta::sta()->reportChecks(checks, verbose); -} - -void -report_min_period_check(MinPeriodCheck *check, - bool verbose) -{ - Sta::sta()->reportCheck(check, verbose); -} - -//////////////////////////////////////////////////////////////// - -MaxSkewCheckSeq & -max_skew_violations() -{ - cmdLinkedNetwork(); - return Sta::sta()->maxSkewViolations(); -} - -MaxSkewCheck * -max_skew_check_slack() -{ - cmdLinkedNetwork(); - return Sta::sta()->maxSkewSlack(); -} - -void -report_max_skew_checks(MaxSkewCheckSeq *checks, - bool verbose) -{ - Sta::sta()->reportChecks(checks, verbose); -} - -void -report_max_skew_check(MaxSkewCheck *check, - bool verbose) -{ - Sta::sta()->reportCheck(check, verbose); -} - -//////////////////////////////////////////////////////////////// - -void -find_timing_cmd(bool full) -{ - cmdLinkedNetwork(); - Sta::sta()->updateTiming(full); -} - -void -find_requireds() -{ - cmdLinkedNetwork(); - Sta::sta()->findRequireds(); -} - -void -find_delays() -{ - cmdLinkedNetwork(); - Sta::sta()->findDelays(); -} - -Slack -total_negative_slack_cmd(const MinMax *min_max) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - return sta->totalNegativeSlack(min_max); -} - -Slack -total_negative_slack_corner_cmd(const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - return sta->totalNegativeSlack(corner, min_max); -} - -Slack -worst_slack_cmd(const MinMax *min_max) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - Slack worst_slack; - Vertex *worst_vertex; - sta->worstSlack(min_max, worst_slack, worst_vertex); - return worst_slack; -} - -Vertex * -worst_slack_vertex(const MinMax *min_max) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - Slack worst_slack; - Vertex *worst_vertex; - sta->worstSlack(min_max, worst_slack, worst_vertex); - return worst_vertex;; -} - -Slack -worst_slack_corner(const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - Slack worst_slack; - Vertex *worst_vertex; - sta->worstSlack(corner, min_max, worst_slack, worst_vertex); - return worst_slack; -} - -PathRef * -vertex_worst_arrival_path(Vertex *vertex, - const MinMax *min_max) -{ - Sta *sta = Sta::sta(); - PathRef path = sta->vertexWorstArrivalPath(vertex, min_max); - if (!path.isNull()) - return new PathRef(path); - else - return nullptr; -} - -PathRef * -vertex_worst_arrival_path_rf(Vertex *vertex, - const RiseFall *rf, - MinMax *min_max) -{ - Sta *sta = Sta::sta(); - PathRef path = sta->vertexWorstArrivalPath(vertex, rf, min_max); - if (!path.isNull()) - return new PathRef(path); - else - return nullptr; -} - -PathRef * -vertex_worst_slack_path(Vertex *vertex, - const MinMax *min_max) -{ - Sta *sta = Sta::sta(); - PathRef path = sta->vertexWorstSlackPath(vertex, min_max); - if (!path.isNull()) - return new PathRef(path); - else - return nullptr; -} - -Slack -find_clk_min_period(const Clock *clk, - bool ignore_port_paths) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - return sta->findClkMinPeriod(clk, ignore_port_paths); -} - -//////////////////////////////////////////////////////////////// - -PinSeq -check_slew_limits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - return Sta::sta()->checkSlewLimits(net, violators, corner, min_max); -} - -size_t -max_slew_violation_count() -{ - cmdLinkedNetwork(); - return Sta::sta()->checkSlewLimits(nullptr, true, nullptr, MinMax::max()).size(); -} - -float -max_slew_check_slack() -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - const Pin *pin; - Slew slew; - float slack; - float limit; - sta->maxSlewCheck(pin, slew, slack, limit); - return sta->units()->timeUnit()->staToUser(slack); -} - -float -max_slew_check_limit() -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - const Pin *pin; - Slew slew; - float slack; - float limit; - sta->maxSlewCheck(pin, slew, slack, 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) -{ - cmdLinkedNetwork(); - return Sta::sta()->checkFanoutLimits(net, violators, min_max); -} - -size_t -max_fanout_violation_count() -{ - cmdLinkedNetwork(); - return Sta::sta()->checkFanoutLimits(nullptr, true, MinMax::max()).size(); -} - -float -max_fanout_check_slack() -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - const Pin *pin; - float fanout; - float slack; - float limit; - sta->maxFanoutCheck(pin, fanout, slack, limit); - return slack;; -} - -float -max_fanout_check_limit() -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - const Pin *pin; - float fanout; - float slack; - float limit; - sta->maxFanoutCheck(pin, fanout, slack, limit); - return limit;; -} - -void -report_fanout_limit_short_header() -{ - 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); -} - -//////////////////////////////////////////////////////////////// - -PinSeq -check_capacitance_limits(Net *net, - bool violators, - const Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - return Sta::sta()->checkCapacitanceLimits(net, violators, corner, min_max); -} - -size_t -max_capacitance_violation_count() -{ - cmdLinkedNetwork(); - return Sta::sta()->checkCapacitanceLimits(nullptr, true,nullptr,MinMax::max()).size(); -} - -float -max_capacitance_check_slack() -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - const Pin *pin; - float capacitance; - float slack; - float limit; - sta->maxCapacitanceCheck(pin, capacitance, slack, limit); - return sta->units()->capacitanceUnit()->staToUser(slack); -} - -float -max_capacitance_check_limit() -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - const Pin *pin; - float capacitance; - float slack; - float limit; - sta->maxCapacitanceCheck(pin, capacitance, slack, 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); -} - -//////////////////////////////////////////////////////////////// - -EdgeSeq -disabled_edges_sorted() -{ - cmdLinkedNetwork(); - return Sta::sta()->disabledEdgesSorted(); -} - -void -write_sdc_cmd(const char *filename, - bool leaf, - bool compatible, - int digits, - bool gzip, - bool no_timestamp) -{ - cmdLinkedNetwork(); - Sta::sta()->writeSdc(filename, leaf, compatible, digits, gzip, no_timestamp); -} - -void -write_path_spice_cmd(PathRef *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, - CircuitSim ckt_sim) -{ - Sta *sta = Sta::sta(); - writePathSpice(path, spice_filename, subckt_filename, - lib_subckt_filename, model_filename, - power_name, gnd_name, ckt_sim, sta); -} - -void -write_timing_model_cmd(const char *lib_name, - const char *cell_name, - const char *filename, - const Corner *corner) -{ - Sta::sta()->writeTimingModel(lib_name, cell_name, filename, corner); -} - -//////////////////////////////////////////////////////////////// - -bool -liberty_supply_exists(const char *supply_name) -{ - auto network = Sta::sta()->network(); - auto lib = network->defaultLibertyLibrary(); - return lib && lib->supplyExists(supply_name); -} - -float -unit_scale(const char *unit_name) -{ - Unit *unit = Sta::sta()->units()->find(unit_name); - if (unit) - return unit->scale(); - else - return 1.0F; -} - -bool -fuzzy_equal(float value1, - float value2) -{ - return fuzzyEqual(value1, value2); -} - -char -pin_sim_logic_value(const Pin *pin) -{ - return logicValueString(Sta::sta()->simLogicValue(pin)); -} - -char -pin_case_logic_value(const Pin *pin) -{ - Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); - LogicValue value = LogicValue::unknown; - bool exists; - sdc->caseLogicValue(pin, value, exists); - return logicValueString(value); -} - -char -pin_logic_value(const Pin *pin) -{ - Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); - LogicValue value = LogicValue::unknown; - bool exists; - sdc->logicValue(pin, value, exists); - return logicValueString(value); -} - -InstanceSeq -slow_drivers(int count) -{ - return Sta::sta()->slowDrivers(count); -} - -bool -timing_arc_disabled(Edge *edge, - TimingArc *arc) -{ - Graph *graph = Sta::sta()->graph(); - return !searchThru(edge, arc, graph); -} - -ClockGroups * -make_clock_groups(const char *name, - 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); -} - -void -clock_groups_make_group(ClockGroups *clk_groups, - ClockSet *clks) -{ - Sta::sta()->makeClockGroup(clk_groups, clks); -} - -void -unset_clock_groups_logically_exclusive(const char *name) -{ - Sta::sta()->removeClockGroupsLogicallyExclusive(name); -} - -void -unset_clock_groups_physically_exclusive(const char *name) -{ - Sta::sta()->removeClockGroupsPhysicallyExclusive(name); -} - -void -unset_clock_groups_asynchronous(const char *name) -{ - Sta::sta()->removeClockGroupsAsynchronous(name); -} - -// Debugging function. -bool -same_clk_group(Clock *clk1, - Clock *clk2) -{ - Sta *sta = Sta::sta(); - Sdc *sdc = sta->sdc(); - return sdc->sameClockGroupExplicit(clk1, clk2); -} - -void -set_clock_sense_cmd(PinSet *pins, - ClockSet *clks, - bool positive, - bool negative, - bool stop_propagation) -{ - Sta *sta = Sta::sta(); - if (positive) - sta->setClockSense(pins, clks, ClockSense::positive); - else if (negative) - sta->setClockSense(pins, clks, ClockSense::negative); - else if (stop_propagation) - sta->setClockSense(pins, clks, ClockSense::stop); - else - sta->report()->critical(1577, "unknown clock sense"); -} - -bool -timing_role_is_check(TimingRole *role) -{ - return role->isTimingCheck(); -} - -//////////////////////////////////////////////////////////////// - -PinSet -find_fanin_pins(PinSeq *to, - bool flat, - bool startpoints_only, - int inst_levels, - int pin_levels, - bool thru_disabled, - bool thru_constants) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - PinSet fanin = sta->findFaninPins(to, flat, startpoints_only, - inst_levels, pin_levels, - thru_disabled, thru_constants); - 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) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - InstanceSet fanin = sta->findFaninInstances(to, flat, startpoints_only, - inst_levels, pin_levels, - thru_disabled, thru_constants); - 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) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - PinSet fanout = sta->findFanoutPins(from, flat, endpoints_only, - inst_levels, pin_levels, - thru_disabled, thru_constants); - 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) -{ - cmdLinkedNetwork(); - Sta *sta = Sta::sta(); - InstanceSet fanout = sta->findFanoutInstances(from, flat, endpoints_only, - inst_levels, pin_levels, - thru_disabled, thru_constants); - delete from; - return fanout; -} - -PinSet -net_load_pins(Net *net) -{ - Network *network = cmdLinkedNetwork(); - PinSet pins(network); - NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - if (network->isLoad(pin)) - pins.insert(pin); - } - delete pin_iter; - return pins; -} - -PinSet -net_driver_pins(Net *net) -{ - Network *network = cmdLinkedNetwork(); - PinSet pins(network); - NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net); - while (pin_iter->hasNext()) { - const Pin *pin = pin_iter->next(); - if (network->isDriver(pin)) - pins.insert(pin); - } - delete pin_iter; - return pins; -} - -//////////////////////////////////////////////////////////////// - -void -report_loops() -{ - Sta *sta = Sta::sta(); - Network *network = cmdLinkedNetwork(); - Graph *graph = cmdGraph(); - Report *report = sta->report(); - for (GraphLoop *loop : *sta->graphLoops()) { - loop->report(report, network, graph); - report->reportLineString(""); - } -} - -// Includes top instance. -int -network_instance_count() -{ - Network *network = cmdNetwork(); - return network->instanceCount(); -} - -int -network_pin_count() -{ - Network *network = cmdNetwork(); - return network->pinCount(); -} - -int -network_net_count() -{ - Network *network = cmdNetwork(); - return network->netCount(); -} - -int -network_leaf_instance_count() -{ - Network *network = cmdNetwork(); - return network->leafInstanceCount(); -} - -int -network_leaf_pin_count() -{ - Network *network = cmdNetwork(); - return network->leafPinCount(); -} - -int -graph_vertex_count() -{ - return cmdGraph()->vertexCount(); -} - -int -graph_edge_count() -{ - return cmdGraph()->edgeCount(); -} - -int -graph_arc_count() -{ - return cmdGraph()->arcCount(); -} - -int -tag_group_count() -{ - return Sta::sta()->tagGroupCount(); -} - -void -report_tag_groups() -{ - Sta::sta()->search()->reportTagGroups(); -} - -void -report_tag_arrivals_cmd(Vertex *vertex) -{ - Sta::sta()->search()->reportArrivals(vertex); -} - -void -report_arrival_count_histogram() -{ - Sta::sta()->search()->reportArrivalCountHistogram(); -} - -int -tag_count() -{ - return Sta::sta()->tagCount(); -} - -void -report_tags() -{ - Sta::sta()->search()->reportTags(); -} - -void -report_clk_infos() -{ - Sta::sta()->search()->reportClkInfos(); -} - -int -clk_info_count() -{ - return Sta::sta()->clkInfoCount(); -} - -int -arrival_count() -{ - return Sta::sta()->arrivalCount(); -} - -int -required_count() -{ - return Sta::sta()->requiredCount(); -} - -int -graph_arrival_count() -{ - return Sta::sta()->graph()->arrivalCount(); -} - -int -graph_required_count() -{ - return Sta::sta()->graph()->requiredCount(); -} - -void -delete_all_memory() -{ - deleteAllMemory(); -} - -Tcl_Interp * -tcl_interp() -{ - return Sta::sta()->tclInterp(); -} - -// Initialize sta after delete_all_memory. -void -init_sta() -{ - initSta(); -} - -void -clear_sta() -{ - Sta::sta()->clear(); -} - -void -make_sta(Tcl_Interp *interp) -{ - Sta *sta = new Sta; - Sta::setSta(sta); - sta->makeComponents(); - sta->setTclInterp(interp); -} - -void -clear_network() -{ - Sta *sta = Sta::sta(); - sta->network()->clear(); -} - -// Elapsed run time (in seconds). -double -elapsed_run_time() -{ - return elapsedRunTime(); -} - -// User run time (in seconds). -double -user_run_time() -{ - return userRunTime(); -} - -// User run time (in seconds). -unsigned long -cputime() -{ - return static_cast(userRunTime() + .5); -} - -// Peak memory usage in bytes. -unsigned long -memory_usage() -{ - return memoryUsage(); -} - -int -processor_count() -{ - return processorCount(); -} - -int -thread_count() -{ - return Sta::sta()->threadCount(); -} - -void -set_thread_count(int count) -{ - Sta::sta()->setThreadCount(count); -} - -void -arrivals_invalid() -{ - Sta *sta = Sta::sta(); - sta->arrivalsInvalid(); -} - -void -delays_invalid() -{ - Sta *sta = Sta::sta(); - sta->delaysInvalid(); -} - -const char * -pin_location(const Pin *pin) -{ - Network *network = cmdNetwork(); - double x, y; - bool exists; - network->location(pin, x, y, exists); - // return x/y as tcl list - if (exists) - return sta::stringPrintTmp("%f %f", x, y); - else - return ""; -} - -const char * -port_location(const Port *port) -{ - Network *network = cmdNetwork(); - const Pin *pin = network->findPin(network->topInstance(), port); - return pin_location(pin); -} - -int -endpoint_violation_count(const MinMax *min_max) -{ - return Sta::sta()->endpointViolationCount(min_max); -} - -void -set_voltage_global(const MinMax *min_max, - float voltage) -{ - Sta::sta()->setVoltage(min_max, voltage); -} - -void -set_voltage_net(const Net *net, - const MinMax *min_max, - float voltage) -{ - Sta::sta()->setVoltage(net, min_max, voltage); -} - -%} // inline - -//////////////////////////////////////////////////////////////// -// -// Object Methods -// -//////////////////////////////////////////////////////////////// - -%extend Library { -const char *name() -{ - return cmdNetwork()->name(self); -} - -Cell * -find_cell(const char *name) -{ - return cmdNetwork()->findCell(self, name); -} - -CellSeq -find_cells_matching(const char *pattern, - bool regexp, - bool nocase) -{ - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - CellSeq matches = cmdNetwork()->findCellsMatching(self, &matcher); - return matches; -} - -} // Library methods - -%extend LibertyLibrary { -const char *name() { return self->name(); } - -LibertyCell * -find_liberty_cell(const char *name) -{ - return self->findLibertyCell(name); -} - -LibertyCellSeq -find_liberty_cells_matching(const char *pattern, - bool regexp, - bool nocase) -{ - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - return self->findLibertyCellsMatching(&matcher); -} - -Wireload * -find_wireload(const char *model_name) -{ - return self->findWireload(model_name); -} - -WireloadSelection * -find_wireload_selection(const char *selection_name) -{ - return self->findWireloadSelection(selection_name); -} - -OperatingConditions * -find_operating_conditions(const char *op_cond_name) -{ - return self->findOperatingConditions(op_cond_name); -} - -OperatingConditions * -default_operating_conditions() -{ - return self->defaultOperatingConditions(); -} - -} // LibertyLibrary methods - -%extend LibraryIterator { -bool has_next() { return self->hasNext(); } -Library *next() { return self->next(); } -void finish() { delete self; } -} // LibraryIterator methods - -%extend LibertyLibraryIterator { -bool has_next() { return self->hasNext(); } -LibertyLibrary *next() { return self->next(); } -void finish() { delete self; } -} // LibertyLibraryIterator methods - -%extend Cell { -const char *name() { return cmdNetwork()->name(self); } -Library *library() { return cmdNetwork()->library(self); } -LibertyCell *liberty_cell() { return cmdNetwork()->libertyCell(self); } -bool is_leaf() { return cmdNetwork()->isLeaf(self); } -CellPortIterator * -port_iterator() { return cmdNetwork()->portIterator(self); } -string get_attribute(const char *key) { return cmdNetwork()->getAttribute(self, key); } - -Port * -find_port(const char *name) -{ - return cmdNetwork()->findPort(self, name); -} - -PortSeq -find_ports_matching(const char *pattern, - bool regexp, - bool nocase) -{ - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - return cmdNetwork()->findPortsMatching(self, &matcher); -} - -} // Cell methods - -%extend LibertyCell { -const char *name() { return self->name(); } -bool is_leaf() { return self->isLeaf(); } -bool is_buffer() { return self->isBuffer(); } -bool is_inverter() { return self->isInverter(); } -LibertyLibrary *liberty_library() { return self->libertyLibrary(); } -Cell *cell() { return reinterpret_cast(self); } -LibertyPort * -find_liberty_port(const char *name) -{ - return self->findLibertyPort(name); -} - -LibertyPortSeq -find_liberty_ports_matching(const char *pattern, - bool regexp, - bool nocase) -{ - PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); - return self->findLibertyPortsMatching(&matcher); -} - -LibertyCellPortIterator * -liberty_port_iterator() { return new LibertyCellPortIterator(self); } - -const TimingArcSetSeq & -timing_arc_sets() -{ - return self->timingArcSets(); -} - -} // LibertyCell methods - -%extend CellPortIterator { -bool has_next() { return self->hasNext(); } -Port *next() { return self->next(); } -void finish() { delete self; } -} // CellPortIterator methods - -%extend LibertyCellPortIterator { -bool has_next() { return self->hasNext(); } -LibertyPort *next() { return self->next(); } -void finish() { delete self; } -} // LibertyCellPortIterator methods - -%extend Port { -const char *bus_name() { return cmdNetwork()->busName(self); } -Cell *cell() { return cmdNetwork()->cell(self); } -LibertyPort *liberty_port() { return cmdNetwork()->libertyPort(self); } -bool is_bus() { return cmdNetwork()->isBus(self); } -PortMemberIterator * -member_iterator() { return cmdNetwork()->memberIterator(self); } - -} // Port methods - -%extend LibertyPort { -const char *bus_name() { return self->busName(); } -Cell *cell() { return self->cell(); } -bool is_bus() { return self->isBus(); } -LibertyPortMemberIterator * -member_iterator() { return new LibertyPortMemberIterator(self); } - -const char * -function() -{ - FuncExpr *func = self->function(); - if (func) - return func->asString(); - else - return nullptr; -} - -const char * -tristate_enable() -{ - FuncExpr *enable = self->tristateEnable(); - if (enable) - return enable->asString(); - else - return nullptr; -} - -float -capacitance(Corner *corner, - const MinMax *min_max) -{ - Sta *sta = Sta::sta(); - return sta->capacitance(self, corner, min_max); -} - -void -set_direction(const char *dir) -{ - self->setDirection(PortDirection::find(dir)); -} - -} // LibertyPort methods - -%extend OperatingConditions { -float process() { return self->process(); } -float voltage() { return self->voltage(); } -float temperature() { return self->temperature(); } -} - -%extend PortMemberIterator { -bool has_next() { return self->hasNext(); } -Port *next() { return self->next(); } -void finish() { delete self; } -} // PortMemberIterator methods - -%extend LibertyPortMemberIterator { -bool has_next() { return self->hasNext(); } -LibertyPort *next() { return self->next(); } -void finish() { delete self; } -} // LibertyPortMemberIterator methods - -%extend TimingArcSet { -LibertyPort *from() { return self->from(); } -LibertyPort *to() { return self->to(); } -TimingRole *role() { return self->role(); } -const char *sdf_cond() { return self->sdfCond(); } - -const char * -full_name() -{ - const char *from = self->from()->name(); - const char *to = self->to()->name(); - const char *cell_name = self->libertyCell()->name(); - return stringPrintTmp("%s %s -> %s", - cell_name, - from, - to); -} - -TimingArcSeq & -timing_arcs() { return self->arcs(); } - -} // TimingArcSet methods - -%extend TimingArc { -LibertyPort *from() { return self->from(); } -LibertyPort *to() { return self->to(); } -Transition *from_edge() { return self->fromEdge(); } -const char *from_edge_name() { return self->fromEdge()->asRiseFall()->name(); } -Transition *to_edge() { return self->toEdge(); } -const char *to_edge_name() { return self->toEdge()->asRiseFall()->name(); } -TimingRole *role() { return self->role(); } - -Table1 -voltage_waveform(float in_slew, - float load_cap) -{ - GateTableModel *gate_model = dynamic_cast(self->model()); - if (gate_model) { - OutputWaveforms *waveforms = gate_model->outputWaveforms(); - if (waveforms) { - Table1 waveform = waveforms->voltageWaveform(in_slew, load_cap); - return waveform; - } - } - return Table1(); -} - -const Table1 * -current_waveform(float in_slew, - float load_cap) -{ - GateTableModel *gate_model = dynamic_cast(self->model()); - if (gate_model) { - OutputWaveforms *waveforms = gate_model->outputWaveforms(); - if (waveforms) { - const Table1 *waveform = waveforms->currentWaveform(in_slew, load_cap); - return waveform; - } - } - return nullptr; -} - -float -voltage_current(float in_slew, - float load_cap, - float voltage) -{ - GateTableModel *gate_model = dynamic_cast(self->model()); - if (gate_model) { - OutputWaveforms *waveforms = gate_model->outputWaveforms(); - if (waveforms) { - float current = waveforms->voltageCurrent(in_slew, load_cap, voltage); - return current; - } - } - return 0.0; -} - -} // TimingArc methods - -%extend Instance { -Instance *parent() { return cmdLinkedNetwork()->parent(self); } -Cell *cell() { return cmdLinkedNetwork()->cell(self); } -LibertyCell *liberty_cell() { return cmdLinkedNetwork()->libertyCell(self); } -bool is_leaf() { return cmdLinkedNetwork()->isLeaf(self); } -InstanceChildIterator * -child_iterator() { return cmdLinkedNetwork()->childIterator(self); } -InstancePinIterator * -pin_iterator() { return cmdLinkedNetwork()->pinIterator(self); } -InstanceNetIterator * -net_iterator() { return cmdLinkedNetwork()->netIterator(self); } -Pin * -find_pin(const char *name) -{ - return cmdLinkedNetwork()->findPin(self, name); -} -string get_attribute(const char *key) { return cmdNetwork()->getAttribute(self, key); } -} // Instance methods - -%extend InstanceChildIterator { -bool has_next() { return self->hasNext(); } -Instance *next() { return self->next(); } -void finish() { delete self; } -} // InstanceChildIterator methods - -%extend LeafInstanceIterator { -bool has_next() { return self->hasNext(); } -Instance *next() { return self->next(); } -void finish() { delete self; } -} // LeafInstanceIterator methods - -%extend InstancePinIterator { -bool has_next() { return self->hasNext(); } -Pin *next() { return self->next(); } -void finish() { delete self; } -} // InstancePinIterator methods - -%extend InstanceNetIterator { -bool has_next() { return self->hasNext(); } -Net *next() { return self->next(); } -void finish() { delete self; } -} // InstanceNetIterator methods - -%extend Pin { -const char *port_name() { return cmdLinkedNetwork()->portName(self); } -Instance *instance() { return cmdLinkedNetwork()->instance(self); } -Net *net() { return cmdLinkedNetwork()->net(self); } -Port *port() { return cmdLinkedNetwork()->port(self); } -Term *term() { return cmdLinkedNetwork()->term(self); } -LibertyPort *liberty_port() { return cmdLinkedNetwork()->libertyPort(self); } -bool is_driver() { return cmdLinkedNetwork()->isDriver(self); } -bool is_load() { return cmdLinkedNetwork()->isLoad(self); } -bool is_leaf() { return cmdLinkedNetwork()->isLeaf(self); } -bool is_hierarchical() { return cmdLinkedNetwork()->isHierarchical(self); } -bool is_top_level_port() { return cmdLinkedNetwork()->isTopLevelPort(self); } -PinConnectedPinIterator *connected_pin_iterator() -{ return cmdLinkedNetwork()->connectedPinIterator(self); } - -Vertex ** -vertices() -{ - Vertex *vertex, *vertex_bidirect_drvr; - static Vertex *vertices[3]; - - cmdGraph()->pinVertices(self, vertex, vertex_bidirect_drvr); - vertices[0] = vertex; - vertices[1] = vertex_bidirect_drvr; - vertices[2] = nullptr; - return vertices; -} - -} // Pin methods - -%extend PinConnectedPinIterator { -bool has_next() { return self->hasNext(); } -const Pin *next() { return self->next(); } -void finish() { delete self; } -} // PinConnectedPinIterator methods - -%extend Term { -Net *net() { return cmdLinkedNetwork()->net(self); } -Pin *pin() { return cmdLinkedNetwork()->pin(self); } -} // Term methods - -%extend Net { -Instance *instance() { return cmdLinkedNetwork()->instance(self); } -const Net *highest_connected_net() -{ return cmdLinkedNetwork()->highestConnectedNet(self); } -NetPinIterator *pin_iterator() { return cmdLinkedNetwork()->pinIterator(self);} -NetTermIterator *term_iterator() {return cmdLinkedNetwork()->termIterator(self);} -NetConnectedPinIterator *connected_pin_iterator() -{ return cmdLinkedNetwork()->connectedPinIterator(self); } -bool is_power() { return cmdLinkedNetwork()->isPower(self);} -bool is_ground() { return cmdLinkedNetwork()->isGround(self);} - -float -capacitance(Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - float pin_cap, wire_cap; - Sta::sta()->connectedCap(self, corner, min_max, pin_cap, wire_cap); - return pin_cap + wire_cap; -} - -float -pin_capacitance(Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - float pin_cap, wire_cap; - Sta::sta()->connectedCap(self, corner, min_max, pin_cap, wire_cap); - return pin_cap; -} - -float -wire_capacitance(Corner *corner, - const MinMax *min_max) -{ - cmdLinkedNetwork(); - float pin_cap, wire_cap; - Sta::sta()->connectedCap(self, corner, min_max, pin_cap, wire_cap); - return wire_cap; -} - -// get_ports -of_objects net -PortSeq -ports() -{ - Network *network = cmdLinkedNetwork(); - PortSeq ports; - if (network->isTopInstance(network->instance(self))) { - NetTermIterator *term_iter = network->termIterator(self); - while (term_iter->hasNext()) { - Term *term = term_iter->next(); - Port *port = network->port(network->pin(term)); - ports.push_back(port); - } - delete term_iter; - } - return ports; -} - -} // Net methods - -%extend NetPinIterator { -bool has_next() { return self->hasNext(); } -const Pin *next() { return self->next(); } -void finish() { delete self; } -} // NetPinIterator methods - -%extend NetTermIterator { -bool has_next() { return self->hasNext(); } -const Term *next() { return self->next(); } -void finish() { delete self; } -} // NetTermIterator methods - -%extend NetConnectedPinIterator { -bool has_next() { return self->hasNext(); } -const Pin *next() { return self->next(); } -void finish() { delete self; } -} // NetConnectedPinIterator methods - -%extend Clock { -float period() { return self->period(); } -FloatSeq *waveform() { return self->waveform(); } -float time(RiseFall *rf) { return self->edge(rf)->time(); } -bool is_generated() { return self->isGenerated(); } -bool waveform_valid() { return self->waveformValid(); } -bool is_virtual() { return self->isVirtual(); } -bool is_propagated() { return self->isPropagated(); } -const PinSet &sources() { return self->pins(); } - -float -slew(const RiseFall *rf, - const MinMax *min_max) -{ - return self->slew(rf, min_max); -} - -} - -%extend ClockEdge { -Clock *clock() { return self->clock(); } -RiseFall *transition() { return self->transition(); } -float time() { return self->time(); } -} - -%extend Vertex { -Pin *pin() { return self->pin(); } -bool is_bidirect_driver() { return self->isBidirectDriver(); } -int level() { return Sta::sta()->vertexLevel(self); } -int tag_group_index() { return self->tagGroupIndex(); } - -Slew -slew(const RiseFall *rf, - const MinMax *min_max) -{ - Sta *sta = Sta::sta(); - return sta->vertexSlew(self, rf, min_max); -} - -Slew -slew_corner(const RiseFall *rf, - const Corner *corner, - const MinMax *min_max) -{ - Sta *sta = Sta::sta(); - return sta->vertexSlew(self, rf, corner, min_max); -} - -VertexOutEdgeIterator * -out_edge_iterator() -{ - return new VertexOutEdgeIterator(self, Sta::sta()->graph()); -} - -VertexInEdgeIterator * -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; -} - -StringSeq -arrivals_clk_delays(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf, - int digits) -{ - Sta *sta = Sta::sta(); - StringSeq arrivals; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - arrivals.push_back(delayAsString(sta->vertexArrival(self, rf, clk_edge, - path_ap, nullptr), - sta, digits)); - } - return arrivals; -} - -FloatSeq -requireds_clk(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf) -{ - Sta *sta = Sta::sta(); - FloatSeq reqs; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - reqs.push_back(delayAsFloat(sta->vertexRequired(self, rf, clk_edge, - path_ap))); - } - return reqs; -} - -StringSeq -requireds_clk_delays(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf, - int digits) -{ - Sta *sta = Sta::sta(); - StringSeq reqs; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - reqs.push_back(delayAsString(sta->vertexRequired(self, rf, clk_edge, path_ap), - sta, digits)); - } - return reqs; -} - -Slack -slack(MinMax *min_max) -{ - Sta *sta = Sta::sta(); - return sta->vertexSlack(self, min_max); -} - -FloatSeq -slacks(RiseFall *rf) -{ - Sta *sta = Sta::sta(); - FloatSeq slacks; - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, path_ap))); - } - return slacks; -} - -// Slack with respect to a clock rise/fall edge. -FloatSeq -slacks_clk(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf) -{ - Sta *sta = Sta::sta(); - FloatSeq slacks; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, clk_edge, - path_ap))); - } - return slacks; -} - -StringSeq -slacks_clk_delays(const RiseFall *rf, - Clock *clk, - const RiseFall *clk_rf, - int digits) -{ - Sta *sta = Sta::sta(); - StringSeq slacks; - const ClockEdge *clk_edge = nullptr; - if (clk) - clk_edge = clk->edge(clk_rf); - for (auto path_ap : sta->corners()->pathAnalysisPts()) { - slacks.push_back(delayAsString(sta->vertexSlack(self, rf, clk_edge, - path_ap), - sta, digits)); - } - return slacks; -} - -VertexPathIterator * -path_iterator(const RiseFall *rf, - const MinMax *min_max) -{ - return Sta::sta()->vertexPathIterator(self, rf, min_max); -} - -bool -has_downstream_clk_pin() -{ - 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 { -Vertex *from() { return self->from(Sta::sta()->graph()); } -Vertex *to() { return self->to(Sta::sta()->graph()); } -Pin *from_pin() { return self->from(Sta::sta()->graph())->pin(); } -Pin *to_pin() { return self->to(Sta::sta()->graph())->pin(); } -TimingRole *role() { return self->role(); } -const char *sense() { return timingSenseString(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_cond_default() -{ return Sta::sta()->isDisabledCondDefault(self); } -PinSet -disabled_constant_pins() { return Sta::sta()->disabledConstantPins(self); } -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 timingSenseString(Sta::sta()->simTimingSense(self));} - -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))); - return delays; -} - -StringSeq -arc_delay_strings(TimingArc *arc, - 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), - sta, digits)); - return delays; -} - -bool -delay_annotated(TimingArc *arc, - const Corner *corner, - const MinMax *min_max) -{ - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - return Sta::sta()->arcDelayAnnotated(self, arc, dcalc_ap); -} - -float -arc_delay(TimingArc *arc, - const Corner *corner, - const MinMax *min_max) -{ - DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); - return delayAsFloat(Sta::sta()->arcDelay(self, arc, dcalc_ap)); -} - -const char * -cond() -{ - FuncExpr *cond = self->timingArcSet()->cond(); - if (cond) - return cond->asString(); - else - return nullptr; -} - -const char * -mode_name() -{ - return self->timingArcSet()->modeName(); -} - -const char * -mode_value() -{ - return self->timingArcSet()->modeValue(); -} - -const char * -latch_d_to_q_en() -{ - if (self->role() == TimingRole::latchDtoQ()) { - Sta *sta = Sta::sta(); - const Network *network = sta->cmdNetwork(); - const Graph *graph = sta->graph(); - Pin *from_pin = self->from(graph)->pin(); - Instance *inst = network->instance(from_pin); - LibertyCell *lib_cell = network->libertyCell(inst); - TimingArcSet *d_q_set = self->timingArcSet(); - LibertyPort *enable_port; - FuncExpr *enable_func; - RiseFall *enable_rf; - lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf); - const char *en_name = enable_port->name(); - return stringPrintTmp("%s %s", en_name, enable_rf->asString()); - - } - return ""; -} - -} // Edge methods - -%extend VertexIterator { -bool has_next() { return self->hasNext(); } -Vertex *next() { return self->next(); } -void finish() { delete self; } -} - -%extend VertexInEdgeIterator { -bool has_next() { return self->hasNext(); } -Edge *next() { return self->next(); } -void finish() { delete self; } -} - -%extend VertexOutEdgeIterator { -bool has_next() { return self->hasNext(); } -Edge *next() { return self->next(); } -void finish() { delete self; } -} - -%extend PathEnd { -bool is_unconstrained() { return self->isUnconstrained(); } -bool is_check() { return self->isCheck(); } -bool is_latch_check() { return self->isLatchCheck(); } -bool is_data_check() { return self->isDataCheck(); } -bool is_output_delay() { return self->isOutputDelay(); } -bool is_path_delay() { return self->isPathDelay(); } -bool is_gated_clock() { return self->isGatedClock(); } -Vertex *vertex() { return self->vertex(Sta::sta()); } -PathRef *path() { return &self->pathRef(); } -RiseFall *end_transition() -{ return const_cast(self->path()->transition(Sta::sta())); } -Slack slack() { return self->slack(Sta::sta()); } -ArcDelay margin() { return self->margin(Sta::sta()); } -Required data_required_time() { return self->requiredTimeOffset(Sta::sta()); } -Arrival data_arrival_time() { return self->dataArrivalTimeOffset(Sta::sta()); } -TimingRole *check_role() { return self->checkRole(Sta::sta()); } -MinMax *min_max() { return const_cast(self->minMax(Sta::sta())); } -float source_clk_offset() { return self->sourceClkOffset(Sta::sta()); } -Arrival source_clk_latency() { return self->sourceClkLatency(Sta::sta()); } -Arrival source_clk_insertion_delay() -{ return self->sourceClkInsertionDelay(Sta::sta()); } -const Clock *target_clk() { return self->targetClk(Sta::sta()); } -const ClockEdge *target_clk_edge() { return self->targetClkEdge(Sta::sta()); } -Path *target_clk_path() { return self->targetClkPath(); } -float target_clk_time() { return self->targetClkTime(Sta::sta()); } -float target_clk_offset() { return self->targetClkOffset(Sta::sta()); } -float target_clk_mcp_adjustment() -{ return self->targetClkMcpAdjustment(Sta::sta()); } -Arrival target_clk_delay() { return self->targetClkDelay(Sta::sta()); } -Arrival target_clk_insertion_delay() -{ return self->targetClkInsertionDelay(Sta::sta()); } -float target_clk_uncertainty() -{ return self->targetNonInterClkUncertainty(Sta::sta()); } -float inter_clk_uncertainty() -{ return self->interClkUncertainty(Sta::sta()); } -Arrival target_clk_arrival() { return self->targetClkArrival(Sta::sta()); } -bool path_delay_margin_is_external() -{ return self->pathDelayMarginIsExternal();} -Crpr common_clk_pessimism() { return self->commonClkPessimism(Sta::sta()); } -RiseFall *target_clk_end_trans() -{ return const_cast(self->targetClkEndTrans(Sta::sta())); } -Delay clk_skew() { return self->clkSkew(Sta::sta()); } - -} - -%extend MinPulseWidthCheckSeqIterator { -bool has_next() { return self->hasNext(); } -MinPulseWidthCheck *next() { return self->next(); } -void finish() { delete self; } -} // MinPulseWidthCheckSeqIterator methods - -%extend PathRef { -float -arrival() -{ - Sta *sta = Sta::sta(); - return delayAsFloat(self->arrival(sta)); -} - -float -required() -{ - Sta *sta = Sta::sta(); - return delayAsFloat(self->required(sta)); -} - -float -slack() -{ - Sta *sta = Sta::sta(); - return delayAsFloat(self->slack(sta)); -} - -const Pin * -pin() -{ - Sta *sta = Sta::sta(); - return self->pin(sta); -} - -const char * -tag() -{ - Sta *sta = Sta::sta(); - return self->tag(sta)->asString(sta); -} - -// mea_opt3 -PinSeq -pins() -{ - Sta *sta = Sta::sta(); - PinSeq pins; - PathRef path1(self); - while (!path1.isNull()) { - pins.push_back(path1.vertex(sta)->pin()); - PathRef prev_path; - path1.prevPath(sta, prev_path); - path1.init(prev_path); - } - return pins; -} - -} - -%extend VertexPathIterator { -bool has_next() { return self->hasNext(); } -PathRef * -next() -{ - Path *path = self->next(); - return new PathRef(path); -} - -void finish() { delete self; } -} - -%extend Corner { -const char *name() { return self->name(); } -} - -// Local Variables: -// mode:c++ -// End: diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index 6127dea9..648f7961 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -1,3 +1,19 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + // Swig TCL input/output type parsers. %{ @@ -154,6 +170,28 @@ tclListNetworkSet(Tcl_Obj *const source, return nullptr; } +template +SET_TYPE +tclListNetworkSet1(Tcl_Obj *const source, + swig_type_info *swig_type, + Tcl_Interp *interp, + const Network *network) +{ + int argc; + Tcl_Obj **argv; + SET_TYPE set(network); + if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK + && argc > 0) { + for (int i = 0; i < argc; i++) { + void *obj; + // Ignore returned TCL_ERROR because can't get swig_type_info. + SWIG_ConvertPtr(argv[i], &obj, swig_type, false); + set.insert(reinterpret_cast(obj)); + } + } + return set; +} + StringSet * tclListSetConstChar(Tcl_Obj *const source, Tcl_Interp *interp) @@ -328,6 +366,72 @@ objectListNext(const char *list, } } +Tcl_Obj * +tclArcDcalcArg(ArcDcalcArg &gate, + Tcl_Interp *interp) +{ + Sta *sta = Sta::sta(); + const Network *network = sta->network(); + const Instance *drvr = network->instance(gate.drvrPin()); + const TimingArc *arc = gate.arc(); + + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + Tcl_Obj *obj; + + const char *inst_name = network->pathName(drvr); + obj = Tcl_NewStringObj(inst_name, strlen(inst_name)); + Tcl_ListObjAppendElement(interp, list, obj); + + const char *from_name = arc->from()->name(); + obj = Tcl_NewStringObj(from_name, strlen(from_name)); + Tcl_ListObjAppendElement(interp, list, obj); + + const char *from_edge = arc->fromEdge()->asString(); + obj = Tcl_NewStringObj(from_edge, strlen(from_edge)); + Tcl_ListObjAppendElement(interp, list, obj); + + const char *to_name = arc->to()->name(); + obj = Tcl_NewStringObj(to_name, strlen(to_name)); + Tcl_ListObjAppendElement(interp, list, obj); + + const char *to_edge = arc->toEdge()->asString(); + obj = Tcl_NewStringObj(to_edge, strlen(to_edge)); + Tcl_ListObjAppendElement(interp, list, obj); + + const char *input_delay = delayAsString(gate.inputDelay(), sta, 3); + obj = Tcl_NewStringObj(input_delay, strlen(input_delay)); + Tcl_ListObjAppendElement(interp, list, obj); + + return list; +} + +ArcDcalcArg +arcDcalcArgTcl(Tcl_Obj *obj, + Tcl_Interp *interp) +{ + Sta *sta = Sta::sta(); + sta->ensureGraph(); + int list_argc; + Tcl_Obj **list_argv; + if (Tcl_ListObjGetElements(interp, obj, &list_argc, &list_argv) == TCL_OK) { + const char *input_delay = "0.0"; + int length; + if (list_argc == 6) + input_delay = Tcl_GetStringFromObj(list_argv[5], &length); + if (list_argc == 5 || list_argc == 6) { + return makeArcDcalcArg(Tcl_GetStringFromObj(list_argv[0], &length), + Tcl_GetStringFromObj(list_argv[1], &length), + Tcl_GetStringFromObj(list_argv[2], &length), + Tcl_GetStringFromObj(list_argv[3], &length), + Tcl_GetStringFromObj(list_argv[4], &length), + input_delay, sta); + } + else + sta->report()->warn(2140, "Delay calc arg requires 5 or 6 args."); + } + return ArcDcalcArg(); +} + } // namespace using namespace sta; @@ -718,6 +822,11 @@ using namespace sta; $1 = tclListSeqPtr($input, SWIGTYPE_p_Pin, interp); } +%typemap(in) PinSet { + Network *network = cmdNetwork(); + $1 = tclListNetworkSet1($input, SWIGTYPE_p_Pin, interp, network); +} + %typemap(in) PinSet* { Network *network = cmdNetwork(); $1 = tclListNetworkSet($input, SWIGTYPE_p_Pin, interp, network); @@ -1431,6 +1540,39 @@ using namespace sta; } } -%typemap(in) ArcDcalcArgPtrSeq { - $1 = tclListSeq($input, SWIGTYPE_p_ArcDcalcArg, interp); +%typemap(in) ArcDcalcArg { + Tcl_Obj *const source = $input; + $1 = arcDcalcArgTcl(source, interp); +} + +%typemap(out) ArcDcalcArg { + Tcl_Obj *tcl_obj = tclArcDcalcArg($1, interp); + Tcl_SetObjResult(interp, tcl_obj); +} + +%typemap(in) ArcDcalcArgSeq { + Tcl_Obj *const source = $input; + int argc; + Tcl_Obj **argv; + + Sta *sta = Sta::sta(); + ArcDcalcArgSeq seq; + if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK + && argc > 0) { + for (int i = 0; i < argc; i++) { + ArcDcalcArg gate = arcDcalcArgTcl(argv[i], interp); + if (gate.drvrPin()) + seq.push_back(gate); + } + } + $1 = seq; +} + +%typemap(out) ArcDcalcArgSeq { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + for (ArcDcalcArg &gate : $1) { + Tcl_Obj *tcl_obj = tclArcDcalcArg(gate, interp); + Tcl_ListObjAppendElement(interp, list, tcl_obj); + } + Tcl_SetObjResult(interp, list); } diff --git a/tcl/Variables.tcl b/tcl/Variables.tcl index 0d054dbf..01e45d0e 100644 --- a/tcl/Variables.tcl +++ b/tcl/Variables.tcl @@ -22,14 +22,6 @@ namespace eval sta { # ################################################################ -trace variable ::link_make_black_boxes "rw" \ - sta::trace_link_make_black_boxes - -proc trace_link_make_black_boxes { name1 name2 op } { - trace_boolean_var $op ::link_make_black_boxes \ - link_make_black_boxes set_link_make_black_boxes -} - # Default digits to print after decimal point for reporting commands. set ::sta_report_default_digits 2 diff --git a/tcl/WritePathSpice.tcl b/tcl/WritePathSpice.tcl deleted file mode 100644 index 19541b72..00000000 --- a/tcl/WritePathSpice.tcl +++ /dev/null @@ -1,118 +0,0 @@ -# OpenSTA, Static Timing Analyzer -# Copyright (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 . - -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\ - [-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} \ - flags {} - - if { [info exists keys(-spice_directory)] } { - set spice_dir [file nativename $keys(-spice_directory)] - if { ![file exists $spice_dir] } { - sta_error 600 "Directory $spice_dir not found." - } - if { ![file isdirectory $spice_dir] } { - sta_error 601 "$spice_dir is not a directory." - } - if { ![file writable $spice_dir] } { - sta_error 602 "Cannot write in $spice_dir." - } - } else { - sta_error 603 "No -spice_directory specified." - } - - if { [info exists keys(-lib_subckt_file)] } { - set lib_subckt_file [file nativename $keys(-lib_subckt_file)] - if { ![file readable $lib_subckt_file] } { - sta_error 604 "-lib_subckt_file $lib_subckt_file is not readable." - } - } else { - sta_error 605 "No -lib_subckt_file specified." - } - - if { [info exists keys(-model_file)] } { - set model_file [file nativename $keys(-model_file)] - if { ![file readable $model_file] } { - sta_error 606 "-model_file $model_file is not readable." - } - } else { - sta_error 607 "No -model_file specified." - } - - if { [info exists keys(-power)] } { - set power $keys(-power) - } else { - sta_error 608 "No -power specified." - } - - if { [info exists keys(-ground)] } { - set ground $keys(-ground) - } else { - sta_error 609 "No -ground specified." - } - - set ckt_sim [parse_ckt_sim_key keys] - - if { ![info exists keys(-path_args)] } { - sta_error 610 "No -path_args specified." - } - set path_args $keys(-path_args) - set path_ends [eval [concat find_timing_paths $path_args]] - if { $path_ends == {} } { - sta_error 611 "No paths found for -path_args $path_args." - } else { - set path_index 1 - foreach path_end $path_ends { - set path [$path_end path] - set path_name "path_$path_index" - set spice_file [file join $spice_dir "$path_name.sp"] - set subckt_file [file join $spice_dir "$path_name.subckt"] - write_path_spice_cmd $path $spice_file $subckt_file \ - $lib_subckt_file $model_file $power $ground $ckt_sim - incr path_index - } - } -} - -set ::ckt_sims {hspice ngspice xyce} - -proc parse_ckt_sim_key { keys_var } { - upvar 1 $keys_var keys - global ckt_sims - - set ckt_sim "ngspice" - if { [info exists keys(-simulator)] } { - set ckt_sim [file nativename $keys(-simulator)] - if { [lsearch $ckt_sims $ckt_sim] == -1 } { - sta_error 1710 "Unknown circuit simulator" - } - } - return $ckt_sim -} - -# sta namespace end. -} diff --git a/test/ccs_sim1.ok b/test/ccs_sim1.ok deleted file mode 100644 index 01adfab6..00000000 --- a/test/ccs_sim1.ok +++ /dev/null @@ -1,17 +0,0 @@ -Startpoint: in1 (input port) -Endpoint: out1 (output port) -Path Group: unconstrained -Path Type: max - - Slew Delay Time Description -------------------------------------------------------------------- - 0.000 0.000 v input external delay - 20.000 0.000 0.000 v in1 (in) - 20.000 0.000 0.000 v u1/A (INVx8_ASAP7_75t_R) - 49.139 20.018 20.018 ^ u1/Y (INVx8_ASAP7_75t_R) - 139.668 44.168 64.186 ^ out1 (out) - 64.186 data arrival time -------------------------------------------------------------------- -(Path is unconstrained) - - diff --git a/test/ccs_sim1.spef b/test/ccs_sim1.spef deleted file mode 100644 index f4b4594f..00000000 --- a/test/ccs_sim1.spef +++ /dev/null @@ -1,37 +0,0 @@ -*SPEF "IEEE 1481-1998" -*DESIGN "top" -*DATE "Tue Sep 25 11:51:50 2012" -*VENDOR "handjob" -*PROGRAM "handjob" -*VERSION "0.0" -*DESIGN_FLOW "NETLIST_TYPE_VERILOG" -*DIVIDER / -*DELIMITER : -*BUS_DELIMITER [ ] -*T_UNIT 1 PS -*C_UNIT 1 FF -*R_UNIT 1 KOHM -*L_UNIT 1 UH - -*POWER_NETS VDD -*GROUND_NETS VSS - -*PORTS -in1 I -out1 O - -*D_NET out1 16.6 -*CONN -*I u1:Y O -*I u2:Y O -*I out1 O -*CAP -1 u1:Y 10 -2 u2:Y 10 -3 out1:1 10 -4 out1 50 -*RES -1 u1:Y out1:1 0.1 -2 u2:Y out1:1 0.1 -3 out1:1 out1 1.0 -*END diff --git a/test/ccs_sim1.tcl b/test/ccs_sim1.tcl deleted file mode 100644 index b3b14ea4..00000000 --- a/test/ccs_sim1.tcl +++ /dev/null @@ -1,8 +0,0 @@ -# ccs_sim parallel inverters -read_liberty asap7_invbuf.lib.gz -read_verilog ccs_sim1.v -link_design top -read_spef ccs_sim1.spef -set_input_transition 20 in1 -sta::set_delay_calculator ccs_sim -report_checks -fields {input_pins slew} -unconstrained -digits 3 diff --git a/test/ccs_sim1.v b/test/ccs_sim1.v deleted file mode 100644 index 9fc0ae6b..00000000 --- a/test/ccs_sim1.v +++ /dev/null @@ -1,7 +0,0 @@ -module top (in1, out1); - input in1; - output out1; - - INVx8_ASAP7_75t_R u1 (.A(in1), .Y(out1)); - INVx8_ASAP7_75t_R u2 (.A(in1), .Y(out1)); -endmodule // top diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index 55482d62..8fde713d 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -122,7 +122,6 @@ record_example_tests { } record_sta_tests { - ccs_sim1 prima3 verilog_attribute } diff --git a/util/RiseFallValues.cc b/util/RiseFallValues.cc index 8f15462c..a9ce0af2 100644 --- a/util/RiseFallValues.cc +++ b/util/RiseFallValues.cc @@ -26,15 +26,15 @@ RiseFallValues::RiseFallValues() void RiseFallValues::clear() { - for (auto tr_index : RiseFall::rangeIndex()) - exists_[tr_index] = false; + for (auto rf_index : RiseFall::rangeIndex()) + exists_[rf_index] = false; } RiseFallValues::RiseFallValues(float init_value) { - for (auto tr_index : RiseFall::rangeIndex()) { - values_[tr_index] = init_value; - exists_[tr_index] = true; + for (auto rf_index : RiseFall::rangeIndex()) { + values_[rf_index] = init_value; + exists_[rf_index] = true; } } diff --git a/util/StaConfig.hh.cmake b/util/StaConfig.hh.cmake index 137c35ef..572588af 100644 --- a/util/StaConfig.hh.cmake +++ b/util/StaConfig.hh.cmake @@ -4,8 +4,6 @@ #cmakedefine ZLIB_FOUND -#define CUDD ${CUDD} - #define SSTA ${SSTA} #define TCL_READLINE ${TCL_READLINE} diff --git a/util/Util.i b/util/Util.i new file mode 100644 index 00000000..e149c2f6 --- /dev/null +++ b/util/Util.i @@ -0,0 +1,533 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (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 . + +%module util + +%{ + +#include "Sta.hh" +#include "StaConfig.hh" // STA_VERSION +#include "Stats.hh" +#include "Report.hh" +#include "Error.hh" +#include "Fuzzy.hh" +#include "Units.hh" + +using namespace sta; + +%} + +//////////////////////////////////////////////////////////////// +// +// Empty class definitions to make swig happy. +// Private constructor/destructor so swig doesn't emit them. +// +//////////////////////////////////////////////////////////////// + +%inline %{ + +float float_inf = INF; + +const char * +version() +{ + return STA_VERSION; +} + +const char * +git_sha1() +{ + return STA_GIT_SHA1; +} + +// Elapsed run time (in seconds). +double +elapsed_run_time() +{ + return elapsedRunTime(); +} + +// User run time (in seconds). +double +user_run_time() +{ + return userRunTime(); +} + +// User run time (in seconds). +unsigned long +cputime() +{ + return static_cast(userRunTime() + .5); +} + +// Peak memory usage in bytes. +unsigned long +memory_usage() +{ + return memoryUsage(); +} + +int +processor_count() +{ + return processorCount(); +} + +int +thread_count() +{ + return Sta::sta()->threadCount(); +} + +void +set_thread_count(int count) +{ + Sta::sta()->setThreadCount(count); +} + +//////////////////////////////////////////////////////////////// + +void +report_error(int id, + const char *msg) +{ + Report *report = Sta::sta()->report(); + report->error(id, "%s", msg); +} + +void +report_file_error(int id, + const char *filename, + int line, + const char *msg) +{ + Report *report = Sta::sta()->report(); + report->error(id, filename, line, "%s", msg); +} + +void +report_warn(int id, + const char *msg) +{ + Report *report = Sta::sta()->report(); + report->warn(id, "%s", msg); +} + +void +report_file_warn(int id, + const char *filename, + int line, + const char *msg) +{ + Report *report = Sta::sta()->report(); + report->fileWarn(id, filename, line, "%s", msg); +} + +void +report_line(const char *msg) +{ + Sta *sta = Sta::sta(); + if (sta) + sta->report()->reportLineString(msg); + else + // After sta::delete_all_memory souce -echo prints the cmd file line + printf("%s\n", msg); +} + +void +fflush() +{ + fflush(stdout); + fflush(stderr); +} + +void +redirect_file_begin(const char *filename) +{ + Sta::sta()->report()->redirectFileBegin(filename); +} + +void +redirect_file_append_begin(const char *filename) +{ + Sta::sta()->report()->redirectFileAppendBegin(filename); +} + +void +redirect_file_end() +{ + Sta::sta()->report()->redirectFileEnd(); +} + +void +redirect_string_begin() +{ + Sta::sta()->report()->redirectStringBegin(); +} + +const char * +redirect_string_end() +{ + return Sta::sta()->report()->redirectStringEnd(); +} + +void +log_begin_cmd(const char *filename) +{ + Sta::sta()->report()->logBegin(filename); +} + +void +log_end() +{ + Sta::sta()->report()->logEnd(); +} + +void +set_debug(const char *what, + int level) +{ + Sta::sta()->setDebugLevel(what, level); +} + +//////////////////////////////////////////////////////////////// + +bool +is_object(const char *obj) +{ + // _hexaddress_p_type + const char *s = obj; + char ch = *s++; + if (ch != '_') + return false; + while (*s && isxdigit(*s)) + s++; + if ((s - obj - 1) == sizeof(void*) * 2 + && *s && *s++ == '_' + && *s && *s++ == 'p' + && *s && *s++ == '_') { + while (*s && *s != ' ') + s++; + return *s == '\0'; + } + else + return false; +} + +// Assumes is_object is true. +const char * +object_type(const char *obj) +{ + return &obj[1 + sizeof(void*) * 2 + 3]; +} + +bool +is_object_list(const char *list, + const char *type) +{ + const char *s = list; + while (s) { + bool type_match; + const char *next; + objectListNext(s, type, type_match, next); + if (type_match) + s = next; + else + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////// +// +// Units +// +//////////////////////////////////////////////////////////////// + +// _sta_ui conversion from sta units to user interface units. +// _ui_sta conversion from user interface units to sta units. + +double +time_ui_sta(double value) +{ + return Sta::sta()->units()->timeUnit()->userToSta(value); +} + +double +time_sta_ui(double value) +{ + return Sta::sta()->units()->timeUnit()->staToUser(value); +} + +double +capacitance_ui_sta(double value) +{ + return Sta::sta()->units()->capacitanceUnit()->userToSta(value); +} + +double +capacitance_sta_ui(double value) +{ + return Sta::sta()->units()->capacitanceUnit()->staToUser(value); +} + +double +resistance_ui_sta(double value) +{ + return Sta::sta()->units()->resistanceUnit()->userToSta(value); +} + +double +resistance_sta_ui(double value) +{ + return Sta::sta()->units()->resistanceUnit()->staToUser(value); +} + +double +voltage_ui_sta(double value) +{ + return Sta::sta()->units()->voltageUnit()->userToSta(value); +} + +double +voltage_sta_ui(double value) +{ + return Sta::sta()->units()->voltageUnit()->staToUser(value); +} + +double +current_ui_sta(double value) +{ + return Sta::sta()->units()->currentUnit()->userToSta(value); +} + +double +current_sta_ui(double value) +{ + return Sta::sta()->units()->currentUnit()->staToUser(value); +} + +double +power_ui_sta(double value) +{ + return Sta::sta()->units()->powerUnit()->userToSta(value); +} + +double +power_sta_ui(double value) +{ + return Sta::sta()->units()->powerUnit()->staToUser(value); +} + +double +distance_ui_sta(double value) +{ + return Sta::sta()->units()->distanceUnit()->userToSta(value); +} + +double +distance_sta_ui(double value) +{ + return Sta::sta()->units()->distanceUnit()->staToUser(value); +} + +double +area_ui_sta(double value) +{ + double scale = Sta::sta()->units()->distanceUnit()->scale(); + return value * scale * scale; +} + +double +area_sta_ui(double value) +{ + double scale = Sta::sta()->units()->distanceUnit()->scale(); + return value / (scale * scale); +} + +//////////////////////////////////////////////////////////////// + +void +set_cmd_unit_scale(const char *unit_name, + float scale) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + unit->setScale(scale); +} + +void +set_cmd_unit_digits(const char *unit_name, + int digits) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + unit->setDigits(digits); +} + +void +set_cmd_unit_suffix(const char *unit_name, + const char *suffix) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) { + unit->setSuffix(suffix); + } +} + +const char * +unit_scale_abbreviation (const char *unit_name) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + return unit->scaleAbbreviation(); + else + return ""; +} + +const char * +unit_suffix(const char *unit_name) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + return unit->suffix(); + else + return ""; +} + +const char * +unit_scaled_suffix(const char *unit_name) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + return unit->scaledSuffix(); + else + return ""; +} + +float +unit_scale(const char *unit_name) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + return unit->scale(); + else + return 1.0F; +} + +//////////////////////////////////////////////////////////////// + +// format_unit functions print with fixed digits and suffix. +// Pass value arg as string to support NaNs. +const char * +format_time(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->timeUnit()->asString(value1, digits); +} + +const char * +format_capacitance(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->capacitanceUnit()->asString(value1, digits); +} + +const char * +format_resistance(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->resistanceUnit()->asString(value1, digits); +} + +const char * +format_voltage(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->voltageUnit()->asString(value1, digits); +} + +const char * +format_current(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->currentUnit()->asString(value1, digits); +} + +const char * +format_power(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->powerUnit()->asString(value1, digits); +} + +const char * +format_distance(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + Unit *dist_unit = Sta::sta()->units()->distanceUnit(); + return dist_unit->asString(value1, digits); +} + +const char * +format_area(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + Unit *dist_unit = Sta::sta()->units()->distanceUnit(); + return dist_unit->asString(value1 / dist_unit->scale(), digits); +} + +//////////////////////////////////////////////////////////////// + +const char * +rise_short_name() +{ + return RiseFall::rise()->shortName(); +} + +const char * +fall_short_name() +{ + return RiseFall::fall()->shortName(); +} + +//////////////////////////////////////////////////////////////// + +bool +fuzzy_equal(float value1, + float value2) +{ + return fuzzyEqual(value1, value2); +} + +%} // inline + +//////////////////////////////////////////////////////////////// +// +// Object Methods +// +//////////////////////////////////////////////////////////////// + + diff --git a/verilog/Verilog.i b/verilog/Verilog.i index ac077dba..e250545c 100644 --- a/verilog/Verilog.i +++ b/verilog/Verilog.i @@ -1,7 +1,3 @@ -%module verilog - -%{ - // OpenSTA, Static Timing Analyzer // Copyright (c) 2024, Parallax Software, Inc. // @@ -18,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +%module verilog + +%{ #include "VerilogReader.hh" #include "VerilogWriter.hh" #include "Sta.hh" diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 8980c7a0..f5ff7615 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -157,8 +157,7 @@ void VerilogReader::deleteModules() { StringSet filenames; - for (auto module_iter : module_map_) { - VerilogModule *module = module_iter.second; + for (const auto [name, module] : module_map_) { filenames.insert(module->filename()); delete module; } diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index 1e105076..e8d193cd 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -258,9 +258,8 @@ VerilogWriter::writeWireDcls(Instance *inst) } delete net_iter; - for (auto name_range : bus_ranges) { - const char *bus_name = name_range.first.c_str(); - const BusIndexRange &range = name_range.second; + for (const auto& [bus_name1, range] : bus_ranges) { + const char *bus_name = bus_name1.c_str(); string net_vname = netVerilogName(bus_name, network_->pathEscape()); fprintf(stream_, " wire [%d:%d] %s;\n", range.first,